MyTetra Share
Делитесь знаниями!
Ускоряем селекторы в jQuery
Время создания: 08.12.2010 19:29
Раздел: Компьютер - Программирование - Java Script - Библиотека jQuery
Запись: xintrea/mytetra_syncro/master/base/0000003351/text.html на raw.github.com

Когда я вчера начинал писать эту статью я хотел написать что-то типа "селекторы для продвинутых" небольшое руководство по сложным выборкам, но как то так получилось, что я отклонился от темы в сторону обьяснения внутрених механизмов jQuery и получилось что-то средние между "селекторами для продвинутых" и "перфомансом селекторов", что тоже не плохо. Объяснять как работают селекторы я буду на простейшем примере, который лучше смотреть в FF3.1 или IE8:

<style>

.myClass{

color:red;

}

</style>

<select name="myName">

<option value="101">101</option>

<option value="102">102</option>

<option value="103">103</option>

<option value="104">104</option>

<option value="105">105</option>

<option value="106" class="myClass">106</option>

<option value="107" class="myClass">107</option>

<option value="108" disabled="disabled">108</option>

<option value="109" disabled="disabled">109</option>

<option value="110" disabled="disabled" class="myClass">110</option>

</select>

Теперь напишем селектор который выберет все элементы которые нам видны, которые имеют класс myClass и которые неактивны.

jQuery().ready(function($){

// выбираем все option

var o = $("select[name=myName] option");

// фильтруем

o.filter(":visible.myClass:disabled");

});

Работает, но это не оптимальный селектор, даже если он занимает минимум символов при написании, он работает медленно. Надо понимать как это все работает и какая операция быстрее. Итак попробуем разобрать: если бы все было прекрасно и браузер поддерживал функцию document.querySelectorAll, jQuery бы передал ей селектор, но у нас не та ситуация, я спецально создал такие условия чтобы querySelectorAll ничего не ускорил, ну или мы используем какойто старый браузер который не поддерживает этот метод. Итак у нас три части селесктора :visible + .myClass + :disabled , (и хотя querySelectorAll понимает второй селектор, jQuery всеравно придеться обрабатывать первый и третий) примерный код выглядел бы так, только намного сложнее.

// Этот код работает идентично тому как работает jQuery

// выбираем все option без jQuery

var o = document.querySelectorAll("select[name=myName] option"),

tmp1 = [], tmp2 = [], result = [];

function isVisible(obj){

if (obj == document)

return true;

if(!obj)

return false;

if(obj.style.display !== "none" && obj.style.visibility !== "hidden")

return isVisible(obj.parentNode);

}

// фильтруем

for (var i=0; i<o.length; i++){

if(isVisible(o[i]))

tmp1.push(o[i]);

} // 10

for (var i=0; i<tmp1.length; i++){

var cls = tmp1[i].className.split('/\s+/')

for (var c in cls)

if (cls[c] == "myClass")

tmp2.push(tmp1[i]);

} // 3

for (var i=0; i<tmp2.length; i++){

if(tmp2[i].getAttribute("disabled"))

result.push(tmp2[i]);

} // 1

alert(result.length); // 1

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

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

Итак, считаем:

  • первый цикл обойдет все 10 элементов и трудазатраты у него возрастают пропорционально величине DOM дерева (дадим ему модификатор 3)
  • второй менее трудозатратен и его величина пропорциональна количеству классов у элемента (модификатор 2)
  • и наконец третий цикл совсем простой с минимальным количеством трудозатрат (модификатор 1).

Посчитаем общую сложность 10*3+10*2+3*1=53. Попробуем поменять местами поставив самые простые на перед - 10*1+3*2+1*3=19, получается в 2,5 раза меньше. Отсюда вывод, что в примере селекторы лучше всего поменять местами в противоположном порядке - :disabled.myClass:visible.

Конечно пример про :visible натянут тут они все видимы но в реальной ситуации когда выбираются все абзацы текста, все может быть по другомую.

В общем это я все клоню к тому, что для того чтобы селекторы быстро работали надо вначале писать самые простые, отсекающие как можно больше элементов, а потом сложные чтобы они фильтровали как можно меньше элементов.

21.02 UPD: Несколько часов назад вышла jquery-1.3.2.js . В release note сказано что отремонтированы селекторы :visible/:hidden и действительно сейчас они полностью переписаны и не зависят от величины дом дерева.

vae element = document.getElementById("id")

if(!element.offsetWidth && !elem.offsetHeight)

// hidden

else

// visible

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

 
MyTetra Share v.0.59
Яндекс индекс цитирования