Когда я вчера начинал писать эту статью я хотел написать что-то типа «селекторы для продвинутых» небольшое руководство по сложным выборкам, но как то так получилось, что я отклонился от темы в сторону обьяснения внутрених механизмов 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
Конечно есть механизмы оптимизации, но есть и много другого кода поэтому от него отстранимся и будем решать нашу задачу. Задача очень простая надо посчитать общее количество циклов и трудозатраты на каждом цыкле.
Итак, считаем:
Отсюда вывод, что в примере селекторы лучше всего поменять местами в противоположном порядке — :disabled.myClass:visible
.
Конечно пример про :visible
натянут тут они все видимы но в реальной ситуации когда выбираются все абзацы текста, все может быть по другомую.
В общем это я все клоню к тому, что для того чтобы селекторы быстро работали надо вначале писать самые простые, отсекающие как можно больше элементов, а потом сложные чтобы они фильтровали как можно меньше элементов.