jQuery: Увеличивает ли специфичность селектора производительность при делегировании .on()?

Есть ли прирост производительности при увеличении специфичности привязки делегированных событий с помощью .on()?

Как это проверить?

Например, является ли второй оператор более эффективным, чем первый:

$('.foo').on('mouseenter', ':has(.other)', function (e) {
    console.log(e);
});

$('.bar').on('mouseenter', 'button:has(.other)', function (e) {
    console.log(e);
});

$('.foo').on('mouseenter', ':has(.other)', function (e) {
  console.log(e);
});

$('.bar').on('mouseenter', 'button:has(.other)', function (e) {
  console.log(e);
});
.foo, .bar {
  margin: 2em auto;
  text-align: center;
}
<div class="foo">
  <button type="button">Hello
    <span class="other">World</span>
  </button>
</div>

<div class="bar">
  <button type="button">Hello
    <span class="other">World</span>
  </button>
</div>

Меня больше всего беспокоит то, что прослушивание событий mouseenter с делегированием снижает производительность, и есть ли способ проверить производительность обработчика событий этого типа.

Обновить

Я должен уточнить, что я не пытаюсь понять последствия использования делегирования в целом для производительности. Я надеюсь понять влияние на производительность использования делегирования с помощью mouseenter во время взаимодействия с пользователем (поскольку мышь пользователя вводит как делегированные, так и неделегированные элементы в связанный элемент), и если есть прирост производительности за счет использования более конкретного селектора для делегирования .

Я склонен предположить, что это не так, потому что каждое событие должно всплывать до связанного элемента, прежде чем оно будет проверено делегированным селектором.

Но есть ли способ проверить это?


person gfullam    schedule 12.02.2015    source источник
comment
Пробовали запускать тесты jsperf?   -  person j08691    schedule 13.02.2015
comment
Во всяком случае, я думаю, что вторая версия будет незначительно менее производительной. Они оба в любом случае перейдут к предку, а вторая версия требует проверки типа узла, а также наличия класса.   -  person JMM    schedule 13.02.2015
comment
Как я могу запустить тест jsperf на этом конкретном примере? Разве jsperf не проверяет производительность во время выполнения, например, сами операции привязки, а не обработку событий при взаимодействии с пользователем?   -  person gfullam    schedule 13.02.2015
comment
c2.com/cgi/wiki?PrematureOptimization   -  person undefined    schedule 13.02.2015
comment
РЖУ НЕ МОГУ. Это не преждевременно. Я работаю над корпоративным приложением, которое может иметь тысячи элементов DOM для прохождения. Это парный пример, чтобы быть уверенным.   -  person gfullam    schedule 13.02.2015
comment
Я должен уточнить, что я не пытаюсь понять последствия использования делегирования для производительности. Я надеюсь понять влияние на производительность использования делегирования с mouseenter и получить ли прирост производительности за счет использования более конкретного селектора для делегирования. Я склонен предположить, что это не так, потому что каждое событие должно всплывать до связанного элемента, прежде чем оно будет проверено делегированным селектором. Но есть ли способ проверить это?   -  person gfullam    schedule 13.02.2015
comment
Я бы предположил, что в общих чертах использование :has в вызове closest (как on делает за кулисами) будет иметь ужасную производительность. Я бы проверил button в селекторе, а затем использовал метод .has() в обратном вызове.   -  person lonesomeday    schedule 13.02.2015
comment
@lonesomeday Это имеет смысл, но, к сожалению, я могу напрямую привязываться только к элементу контейнера, который получит кнопки позже. Содержимое этого контейнера может быть повторно отображено любое количество раз, поэтому делегирование является обязательным.   -  person gfullam    schedule 13.02.2015
comment
@gfullam Я уверен, что делегирование - правильный вариант. Однако я приложил бы огромные усилия, чтобы убедиться, что :has selector не находится в вашем строка селектора. Это селектор jQuery, а не собственный браузер, поэтому он очень медленный. Вы используете его для каждого элемента дерева. Если вы сделаете еще один тест, например button, а затем используйте .has функцию для проверки этого элемента, он будет иметь гораздо более высокая производительность, поскольку он может лучше использовать встроенные возможности браузера.   -  person lonesomeday    schedule 13.02.2015
comment
@lonesomeday Изначально я приложил немало усилий, чтобы избежать делегирования, потому что мне нужно привязаться к родителю известного селектора дочернего класса, к сожалению, единственный способ сделать это — с помощью :has, потому что вы не можете связать .has с делегированными селекторами — он может только цепочка к существующим элементам DOM, и во время привязки элементов нет. Именно из-за того, что я использую :has, я беспокоюсь о других преимуществах производительности. Мне также важно понять, что происходит и как протестировать производительность в этой ситуации.   -  person gfullam    schedule 13.02.2015
comment
@gfullam Я понимаю это. Я говорю, что вы должны попытаться убрать :has из селектора и выполнить тест в обратном вызове события; у него будет гораздо лучшая производительность. См. эти два примера: они функционально эквивалентны, но первый имеет гораздо лучшую производительность.   -  person lonesomeday    schedule 13.02.2015
comment
@lonesomeday Мне нравится этот пример. И это приближается к ответу на мой вопрос. Можете ли вы опубликовать это как ответ с объяснением того, «почему» одна функция более эффективна, чем другая? Как определить разницу в производительности между ними?   -  person gfullam    schedule 13.02.2015
comment
В конце концов нашел способ сформулировать тест jsperf, который показывает, что использование :has в селекторе чаще (в повторных тестах) более эффективно, чем использование .has в обработчике; это также показывает, что увеличение специфичности, как в button:has в селекторе, постоянно повышает производительность. См.: jsperf.com/has-filter-in-delegated-event-selector< /а>   -  person gfullam    schedule 03.05.2015


Ответы (1)


Конкретика помогает… немного.

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

В некоторых тестах разница была достаточно мала и находилась в пределах погрешности, чтобы считаться статистически эквивалентной.

Фильтрация селектора в обработчике причиняет боль

Но самым удивительным результатом является вспомогательный третий тест, предложенный одним из комментаторов выше, который убирает :has() из селектора и заменяет его проверкой .has() в функции обратного вызова, которая последовательно работает медленнее, чем использование :has() в селекторе — часто на 100 операций в секунду и более.

Это, возможно, менее незначительно и особенно примечательно, учитывая, что документация jQuery для :has() указывает, что вы должны ожидайте лучшей производительности здесь:

Поскольку :has() является расширением jQuery, а не частью спецификации CSS, запросы, использующие :has(), не могут воспользоваться преимуществом повышения производительности, обеспечиваемым собственным методом DOM querySelectorAll(). Для лучшей производительности в современных браузерах используйте вместо этого $( "your-pure-css-selector" ).has( selector/DOMElement ).

Я не сомневаюсь, что в операторе селектора, где .has() просто используется для сокращения набора совпадающих элементов, можно ожидать лучшей производительности; но я подозреваю, что потеря производительности, продемонстрированная в тесте, является результатом нарушения этого соглашения путем перемещения проверки .has() в обработчик событий, где данные события передаются, новый объект jQuery создается из this, и .length осуществляется доступ и проверено в операторе if.


Состав теста

Чтобы сформулировать тест, были настроены и применены к одной и той же HTML-разметке три контрольных показателя привязки событий, затем к каждому из три бенчмарка для сравнения.

Эталон 1:

    $('.foo').on('mouseenter', ':has(.other)', function(e) {
      console.log(e);
    });

Эталон 2:

    $('.foo').on('mouseenter', 'button:has(.other)', function(e) {
      console.log(e);
    });

Показатель 3:

    $('.foo').on('mouseenter', 'button', function(e) {
      if ($(this).has('.other').length) {
        console.log(e);
      }
    });

HTML:

<div class="foo">
    <button type="button" id="A">some text</button>
    <button type="button" id="B"><span class="other">some text</span></button>
    <div id="C">some text</div>
</div>

Тестовый пример:

$('#A').trigger('mouseenter');
$('#B').trigger('mouseenter');
$('#C').trigger('mouseenter');

Скриншот таблицы с результатами теста JSPerf

person gfullam    schedule 04.05.2015