#javascript #jquery #performance
#javascript #jquery #Производительность
Вопрос:
У меня есть HTML-список из примерно 500 элементов и поле «фильтр» над ним. Я начал с использования jQuery для фильтрации списка при вводе буквы (код синхронизации добавлен позже):
$('#filter').keyup( function() {
var jqStart = (new Date).getTime();
var search = $(this).val().toLowerCase();
var $list = $('ul.ablist > li');
$list.each( function() {
if ( $(this).text().toLowerCase().indexOf(search) === -1 )
$(this).hide();
else
$(this).show();
} );
console.log('Time: ' ((new Date).getTime() - jqStart));
} );
Однако после ввода каждой буквы (особенно первой буквы) была задержка на пару секунд. Поэтому я подумал, что это может быть немного быстрее, если я использую обычный Javascript (недавно я прочитал, что функция jQuery each
работает особенно медленно). Вот мой эквивалент JS:
document.getElementById('filter').addEventListener( 'keyup', function () {
var jsStart = (new Date).getTime();
var search = this.value.toLowerCase();
var list = document.querySelectorAll('ul.ablist > li');
for ( var i = 0; i < list.length; i )
{
if ( list[i].innerText.toLowerCase().indexOf(search) === -1 )
list[i].style.display = 'none';
else
list[i].style.display = 'block';
}
console.log('Time: ' ((new Date).getTime() - jsStart));
}, false );
Однако, к моему удивлению, простой Javascript работает в 10 раз медленнее, чем эквивалент jQuery. Для фильтрации каждой буквы в версии jQuery требуется около 2-3 секунд, в то время как версия Javascript занимает более 17 секунд! Я использую Google Chrome в Ubuntu Linux.
Это не для чего-то действительно важного, поэтому оно не должно быть суперэффективным. Но я делаю что-то действительно глупое с моим Javascript здесь?
Комментарии:
1. Я бы засек время двух селекторов и посмотрел, все ли там; Я знаю, что материал jQuery selector настроен до смерти.
2. Кэширование
list.length
— это одно, хотя это не имело бы такого большого значения. Также считается, что ваш метод синхронизации неточен. ejohn.org/blog/accuracy-of-javascript-time3. @Dave: jQuery стремится использовать как можно больше встроенной поддержки селекторов браузера, в основном любой селектор, который не зависит от jQuery, должен обрабатываться собственным интерфейсом браузера, потому что это будет значительно быстрее, чем делать это в JavaScript.
4. @DaveNewton querySelectorAll = 1 мс, $() = 3 мс. Определенно не селекторы!
5. Попробуйте заменить
textContent
наinnerText
, чтобы увидеть, имеет ли это значение. Кроме того, есть ли причина, по которой вы запускаете'ul.ablist > li'
событие on each keyup? Ожидается ли, что он изменится? Если это так, я бы использовал живой список узлов. В любом случае, я бы кэшировал его вне обработчика.
Ответ №1:
Вы могли бы попробовать использовать textContent
вместо innerText
, я думаю, это должно быть быстрее. Кроме того, синхронизация генерации списка и цикла отдельно покажет, есть ли проблема в генерации списка.
Комментарии:
1. Спасибо, использование
textContent
полностью решило проблему — фильтрация теперь мгновенная! 17 000 мс до ~ 8 мс: D2. 1 = 10. Это бронзовый значок 😉 @disappuntledgoat Дополнительная информация: внешние запятые в
((new Date).getTime - jsStart)
не являются строго необходимыми, потому что ваше выражение JavaScript вычисляется справа налево. Смотрите также: developer.mozilla.org/en/JavaScript/Reference/Operators /…3. @RobW получил значок. Приветствия !
Ответ №2:
Еще одна лучшая практика для повышения скорости javascript — это кэширование list.length
в переменной и вызов переменной, например:
l = list.length;
for (var i=0;i<l;i ):{ code here}
И, возможно, синхронизация с jsperf была бы лучше.
Ответ №3:
Здесь я немного переработал ваш код:
var filter = document.getElementById( 'filter' ),
ablist = document.querySelector( '.ablist' );
filter.addEventListener( 'keyup', function () {
var re, elems, i, len, elem;
re = RegExp( this.value, 'i' );
elems = ablist.children;
for ( i = 0, len = elems.length; i < len; i = 1 ) {
elem = elems[i];
elem.style.display =
elem.textContent.search( re ) > -1 ? 'list-item' : 'none';
}
}, false );
Живая демонстрация: http: //jsfiddle.net/MVFxn/
Изменения:
- с регулярным выражением и
i
флагом нет необходимостиtoLowerCase
, - если на странице есть только один
'.ablist'
элемент,querySelector
это должен быть самый быстрый способ его захвата (поскольку он прерывает запрос, как только находит первый такой элемент), - для элементов LI нет запроса, поскольку
children
свойство уже удобно ссылается на них.
Я хотел бы знать, как этот код работает на вашей странице…
Ответ №4:
Я использовал while
вместо for
и внес некоторые незначительные улучшения. Вот окончательный код.
var list = list = document.querySelectorAll('ul.ablist > li');
document.getElementById('javascriptFilter').addEventListener( 'keyup', function () {
var jsStart = (new Date).getTime(),
search = this.value.toLowerCase(),
i = list.length - 1,
listItem,
resu<
while( i >= 0 )
{
listItem = list[i];
if ( listItem.textContent.toLowerCase().indexOf(search) === -1 )
listItem.style.display = 'none';
else
listItem.style.display = 'block';
i--;
}
result = ((new Date).getTime() - jsStart);
console.log(['Time: ', result, '<br />'].join(''));
}, false );