Самый быстрый способ скрыть тысячи элементов <li>?

У меня есть форма автозаполнения, в которой пользователь может ввести термин, и он скрывает все элементы <li>, которые не содержат этот термин.

Я изначально переместил все <li> с помощью jQuery each и применил .hide() к тем, которые не содержали этот термин. Это был ПУТЬ слишком медленно.

Я обнаружил, что более быстрый способ - пропустить все <li> и применить класс .hidden ко всем, что нужно скрывать, а затем в конце цикла сделать $('.hidden').hide(). Это кажется хакером, хотя.

Возможно, более быстрый способ переписать правило CSS для класса .hidden, используя document.styleSheets. Может ли кто-нибудь подумать о еще лучшем способе?

EDIT: Позвольте мне уточнить, что я не уверен, что слишком много людей знают о. Если вы изменяете DOM в каждой итерации цикла, и это изменение заставляет страницу перерисовываться, это будет МНОГО медленнее, чем "подготовка" всех ваших изменений и применение их сразу сразу после завершения цикла.

Ответ 1

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

Гораздо лучше использовать ваши данные отдельно от DOM. Поиск по массиву строк JS на несколько порядков быстрее.

Это может означать загрузку вашего набора данных в качестве объекта JSON. Если это не вариант, вы можете выполнить цикл через <li> один раз (при загрузке страницы) и скопировать данные в массив.

Теперь, когда ваш набор данных не зависит от присутствующих элементов DOM, вы можете просто заменить все содержимое <ul> с помощью .html() каждый раз, когда пользователь набирает. (Это намного быстрее, чем манипуляция JS DOM, поскольку браузер может оптимизировать изменения DOM при простое изменение innerHTML.)

var dataset = ['term 1', 'term 2', 'something else', ... ];

$('input').keyup(function() {
    var i, o = '', q = $(this).val();
    for (i = 0; i < dataset.length; i++) {
        if (dataset[i].indexOf(q) >= 0) o+='<li>' + dataset[i] + '</li>';
    }
    $('ul').html(o);
});

Как вы можете видеть, это очень быстро.


Обратите внимание, что если вы до 10 000 элементов, производительность начинает страдать от первых нескольких нажатий клавиш. Это больше связано с количеством вставленных результатов в DOM, чем с необработанным количеством предметов, которые ищутся. (Поскольку вы печатаете больше и меньше результатов для отображения, производительность очень хорошая, даже если она все еще просматривает все 10 000 элементов.)

Чтобы этого избежать, я бы рассмотрел ограничение количества результатов, отображаемых на разумный номер. (1000 кажется таким же хорошим, как и любой другой.) Это автозаполнение; никто не действительно просматривает все результаты – они будут продолжать печатать до тех пор, пока набор результатов не будет доступен для человека.

Ответ 2

Вы можете напрямую выбрать все <li> s, а затем отфильтровать их: $("li").filter(function(){...}).hide() (см. здесь)

(извините, я ранее публиковал неправильно)

Ответ 3

Вы можете использовать селектор jQuery contains() для поиска всех элементов в списке с определенным текстом, а затем просто скрыть их, например:

HTML:

 <ul id="myList">
      <li>this</li>
      <li>that</li>
 <ul>​

JQuery

var term = 'this';    
$('li:contains("' + term + '")').hide();​

Ответ 4

Я знаю, что это вопрос старый, но я не удовлетворен ни одним из ответов. В настоящее время я работаю над проектом Youtube, который использует список jQuery Selectable, который содержит около 120 000 предметов. Эти списки можно отфильтровать по тексту и показать соответствующие элементы. Единственным приемлемым способом скрыть все несоответствующие элементы было скрыть элемент ul сначала, чем скрыть элементы li и снова показать элемент списка (ul).

Ответ 5

Как насчет:

<style>
    .hidden{ display: none; }
</style>

Таким образом, вам не нужно делать дополнительный запрос, используя $('. hidden'). hide()?

Ответ 6

Вместо того, чтобы переопределять правила таблиц стилей, вы можете напрямую определить 'hide' свойство класса "display: none;" перед рукой и на вашей странице вы можете просто примените класс, который вы определили после проверки состояния с помощью javascript, как показано ниже.

$("li").each(function(){if(condition){$(this).addClass('hide');}});

и позже, если вы хотите снова показать эти лики, вы можете просто удалить класс, как показано ниже

$("li").each(function(){if(condition){$(this).removeClass('hide');}});

Ответ 7

Вы можете использовать более уникальный метод, который технически не использует JavaScript для фактического скрытия, путем размещения копии данных в атрибуте и использования селектора атрибутов CSS.

Например, если термин secret, и вы поместили копию данных в атрибут data-term, вы можете использовать следующий CSS:

li[data-term*="secret"] {
    display: none;
}

Чтобы сделать это динамически, вам нужно добавить стиль в голову в javascript:

function hideTerm(term) {
    css = 'li[data-term*="'+term+'"]{display:none;}'
    style = $('<style type="text/css">').text(css)
    $('head').append(style);
}

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

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