Пользователь
0,0
рейтинг
1 декабря 2013 в 00:31

Разработка → 60 FPS? Легко! pointer-events:none! перевод tutorial



Вы, наверное, уже читали интересную статью о том, как можно отключать эффекты :hover при скроле – это позволяет здорово сохранить отзывчивость сайта, но имеет один недостаток – вам приходится опираться на один общий класс, и это плохо.

.hover .element:hover {
  box-shadow: 1px 1px 1px #000;
}

Плохо, потому что данный подход ограничивает вашу расширяемость, и вносит лишнюю спецификацию в, и без того нелегкие, селекторы CSS.

Фишка тут в том, что при скроле вы просто удаляете класс .hover с тега body, тем самым отключая все ваши селекторы с :hover-ом. После окончания события, класс возвращается, и эффекты :hover опять в деле.

Круто. Но не очень – привычное переключение глобального класса запускает немалый пересчет стилей, и это не есть гуд. Гениальное решение предложил Christian Schaefer:



О да, pointer-events – наше все!


Свойство pointer-events позволяет управлять условиями, при которых элементы вашей страницы будут реагировать на события мыши. Смотрите сами:



Ошеломительная разница, не так ли? И все это без лишней сложности селекторов:

.disable-hover {
  pointer-events: none;
}

Просто добавляем этот класс к нашему body по началу скрола, и все – мышь «пролетает»!

var body = document.body,
    timer;

window.addEventListener('scroll', function() {
  clearTimeout(timer);
  if(!body.classList.contains('disable-hover')) {
    body.classList.add('disable-hover')
  }
  
  timer = setTimeout(function(){
    body.classList.remove('disable-hover')
  },500);
}, false);

Код, как видите, довольно простой. Вешаем обработчик на событие скрола, в котором сперва сбрасываем предыдущий таймер, проверяем наличие класса на нашем body, и, если его нет – добавляем. Затем просто добавляем новый таймер, который, через пол секунды после окончания скрола, сбросит наш класс. Все!

Почти


Если где-то в странице будут элементы со стилем pointer-events: auto, они перетрут глобальный класс, и будут все еще тормозить. Мы же не хотим этого, верно?

.disable-hover,
.disable-hover * {
  pointer-events: none !important;
}

Как вы догадались, решение, так же, простое. Супер-селектором * с флагом !important мы научим наши элементы вести себя хорошо.

Можете убедиться в работе данного подхода. Попробуйте замерить производительность с hover-ом, и без. Результат впечатляет!
Перевод: Ryan Seddon
Азиз Газанчиян @creage
карма
12,0
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (52)

  • +2
    По-моему, это круто! По крайней мере имеет практическое применение.
  • +3
    Google Chrome 31.0.1650.57, OS X. Разницы в производительности нет.
    • +2
      Google Chrome Windows 7. Весьма ощутимая разница.
      • 0
        Странно, вот матрица браузеров, которые поддерживают pointer-events.
        • +3
          Тем не менее, тоже Win7+Chrome — прирост очень ощутимый. В разы плавнее скролится.
        • 0
          А что странно, никак в толк не возьму? Хром же поддерживает.
          • 0
            Извините, только сейчас заметил, что промахнулся с ответом. Хотел ответить уровнем выше.
    • 0
      Google Chrome 31.0.1650.57, OS X. Разница ощутимая.
      • 0
        Что прямо в отладчике видно разницу?
    • +6
      Надо было так:
      Mac Pro, 2х 6-core Xeon, 64 gb RAM, Google Chrome 31.0.1650.57, OS X. Разницы в производительности нет.
      • 0
        Версию OS X указать забыли
        • –1
          А что есть сомнения в версии OS X на Mac Pro?
    • 0
      Я думаю тут дело в мощности процессора или видеокарты. Я тоже разницы не заметил.
    • 0
      Все аналогично и разницы тоже нет. 60 fps.
  • +2
    Может я что-то не так делаю, но FF25 Linux тоже никакой разницы не вижу.
    • +6
      Аналогично. Что так тормозит, что так тормозит.
    • +3
      FF25 + Win7 — аналогично, совершенно никакой разницы в FPS. Судя по наблюдениям, в FF25 уже есть встроенный механизм отключения :hover во время скролинга.
      А вот на «хромовых» браузерах разница, хоть и небольшая, но есть. К тому же они даже с включенным pointer-events скролят в разы плавнее, чем FF25
  • 0
    Win8, новая Opera. Производительность выросла более чем в 2 раза.
  • 0
    Хром 30, Убунту, железо старенькое. Никакой разницы.
    • 0
      fps подрос, но субъективно особо разницы то и нету… проверю потом на всякий айфонах андроидах, глядишь там толк есть.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Android точно может быть с мышой… однако это достаточно экзотическая конфигурация…
        • +3
          при легком касании на iOS делается hover
          • +2
            На андроиде тоже происходит hover при касании, например на огромных списках с кнопками, если у вас есть для кнопки :active :hover — такие стили могут существенно понижать производительность, потому что рендер затыкается на элементе по которому случайно тапнули, чем заставили перерисовать стиль.
        • 0
          Мыши там может и нету, но события мыши эмулируются. И hover там так же есть, как есть и active. С другой стороны их редко для тач девайсов используют. Хотя и то что в примере так же редко встречается.
  • –1
    В итоге, я так понял, все ховеры на странице тормозят браузер.
  • –8
    В реализации на jquery ещё и сам код становится простым и коротким. Красота.
    • +2
      Если вы гонитесь за производительностью, зачем вам jQuery?
      • +2
        Иногда jQuery уже есть в проекте. Чем плохо использовать его и для подвешивания на scroll()?
        • +2
          jQuery сам по себе медленный. Перед тем, как оптимизировать мелочи типа прокрутки, желательно заниматься крупными оптимизациями более очевидных вещей.
          • 0
            Оптимизировать надо узкие места.
            Фреймворки позволяют экономить время, и их замена на самопальный код — огромное количество человекочасов, причем на развивающемся проекте оно почти всегда не укладывается в количество доступных ресурсов.

            Данный хак прост и с точки зрения «улучшение производительности / затраченное время» он однозначно выигрывает.
    • +1
      в реализации на underscore/lodash скорее, ибо вам нужен debounce. Добавлять обработчики событий нативной, классы и т.д. много кода вам не сократят. Да и то это так же лишнее…
    • +5

      да?
      • +3
        Уже осознал на собственной шкуре карме — хабражители не любят jQuery. Был не прав.
  • –1
    Opera 18.
    При отключении hover, и промотке страницы скролом, перестают подсвечиваться элементы пока не пошевелишь мышь, при включении — подсвечиваются.
  • +1
    А ведь можно добавить это в пользовательский скрипт и получить высокую производительность на всех сайтах!
    • 0
      Похоже, я сказал какую-то несусветную глупость, потому что совершенно не знаю JS. Кто-нибудь пояснит мне, в чем она состоит?
      • +1
        в том что эта штука помогает только в очень специфичных кейсах.
        • 0
          Но при этом она вроде бы ничего не ломает. Невелика беда, что во время прокрутки hover не работает.
          • 0
            В этом специально подготовленном примере fps выросло в полтора раза (у меня), а вот на всех моих текущих проектах толку вообще нету.
  • +1
    В Firefox тормозит как с хаком, так и без. Да ещё и включаются события заторможенно.
    • 0
      Возникает мрачное подозрение, что перед нами ещё один такой рецепт, который превосходно работает в Chrome, но дурно (или никак) в Файерфоксе. Тем более, что Paul Irish явно сослался на какой-то чисто хромовский нюанс.
  • +3
    В FF тормозит в обоих случаях.
    В chrome чуть быстрее, чем без
    В IE11 работает очень шустро, разницу не заметил
    /Win8.1 64 bit, intel core i5
  • +1
    Если навешивать на hover эффекты, вызывающие repaint, то будет разница. Если такого нет, то и разницы особой не заметите. Даже на графиках в Dev Tools.
  • –1
    Спасибо! Грядёт ускорение барона, и эта фишка туда тоже попадёт.
  • +1
    Очень полезно! Сделал расширение для хрома, который применяет данный твик на всех сайтах. Надеюсь, что пригодится кому-то еще.
    • 0
      «которое», конечно же. Уже нельзя исправить.
  • 0
    А разве pointer-events вообще применим к HTML? Когда я последний раз смотрел w3c, он был разрешён только для SVG, а из HTML убран из-за каких-то проблем с совместимостью.

    Некоторые браузеры применяют и к HTML вопреки спецификациям, но далеко не факт, что это будет работать везде и всегда.
  • 0
    один косяк: когда скролл остановился, то элемент остался не подсвеченным
  • 0
    А для IE 8 — 10 можно попробовать что-то такое (внимание, не тестировалось!):

    .no-hover:before {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 5000;
    width: 100%;
    height: 100%;
    display: block;
    content: '';
    }
    

    // Used to track the enabling of hover effects
    var enableTimer = 0;
    
    /*
     * Listen for a scroll and use that to remove
     * the possibility of hover effects
     */
    window.addEventListener('scroll', function() {
      clearTimeout(enableTimer);
      removeNoHoverClass();
    
      // enable after 1 second, choose your own value here!
      enableTimer = setTimeout(addNoHoverClass, 1000);
    }, false);
    
    /**
     * Removes the hover class from the body. Hover styles
     * are reliant on this class being present
     */
    function removeNoHoverClass() {
      document.body.classList.remove('no-hover');
    }
    
    /**
     * Adds the hover class to the body. Hover styles
     * are reliant on this class being present
     */
    function addNoHoverClass() {
      document.body.classList.add('no-hover');
    }
    
  • 0
    linux amd64, chromium
    разница есть, процентов 5 =)

    Однако у меня выключен плавный скролл (терпеть его не могу, возможно даже как раз изза того что тормозит). С ним разница больше, примерно как на видео в шапке. Однако без плавного скролла все быстрее всё равно. Кто придумал плавный скролл и как его отключить глобально у всего человечества знает кто? =))
  • 0
    По-моему при каждом скролле обращаться к элементу проверяя есть ли у него класс это жестоко. Вот так получше будет.

    var body = document.body,
        timer,
        hover_disabled = false;
    
    window.addEventListener('scroll', function() {
      clearTimeout(timer);
      if( ! hover_disabled && ! body.classList.contains('disable-hover')) {
        body.classList.add('disable-hover');
        hover_disabled = true;
      }
      
      timer = setTimeout(function(){
        body.classList.remove('disable-hover');
        hover_disabled = false;
      }, 500);
    }, false);
    

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.