Подтвердить что ты не робот

Почему этот 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 здесь?

4b9b3361

Ответ 1

Вы можете попробовать использовать textContent вместо innerText, я думаю, что это должно быть быстрее. Кроме того, синхронизация генерации списков и циклов отдельно указала бы, есть ли проблема в генерации списков.

Ответ 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,
        result;
    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 );