Как оценивается селектор jQuery $('# foo a')?
В качестве примера кода jQuery (https://coderwall.com/p/7uchvg) я прочитал, что выражение $('#foo a');
ведет себя следующим образом:
Найдите каждый a
на странице, а затем отфильтруйте a
внутри #foo
.
И это выглядит неэффективно.
Это правильно? И если да, то как нам это сделать лучше?
Ответы
Ответ 1
Это правильно - Sizzle (механизм выбора jQuery) ведет себя так же, как и селектор CSS. Селекторы CSS и Sizzle оцениваются справа налево, и поэтому #foo a
найдет все узлы a
, а затем отфильтровывает те узлы, которые спускаются с #foo
.
Вы улучшаете это, гарантируя, что ваши селекторы листьев имеют высокую специфичность, обычно, предоставляя им класс или идентификатор.
Ответ 2
как нам это сделать лучше?
Используйте параметр контекста из jQuery.
$('a', '#foo');
Теперь jQuery будет искать все якоря в контексте элемента с id: foo.
В вашем запросе контекст по умолчанию считается документом при пропуске:
$('#foo a'); == $('#foo a', document);
В этом случае ваш запрос действительно неэффективен.
Вы можете посмотреть в этой статье.
Ответ 3
Хотя верно, что Sizzle является движком справа налево (это то же самое, что интерпретируется css), неверно, что конкретный селектор в вашем примере будет выбирать все элементы привязки на странице, а затем фильтровать их родители соответствуют идентификатору "foo". Sizzle фактически оптимизирует любой селектор, который начинается с идентификатора, и использует его как контекст для всего выделения, а не для использования документа. Другими словами, выбранный вами селектор в основном переводится на:
document.getElementById("foo").getElementsByTagName("a")
Действительно, это не плохой селектор вообще.
Однако, учитывая другие вещи, которые должен выполнить jQuery (который включает в себя цикл над элементами для их объединения на экземпляр jQuery), jQuery ( "# foo" ). find ( "a" ) всегда будет самым быстрым, поскольку jQuery реализует ярлык создания объекта jQuery для селекторов только для идентификаторов, а затем он находит корневой путь из #foo.
Другими словами, сам Sizzle не сильно отличается при выполнении Sizzle("#foo a")
и Sizzle("a", document.getElementById("foo"))
, но jQuery("#foo").find...
будет быстрее из-за ярлыка собственного идентификатора jQuery.
Кстати, мои замечания о Sizzle предполагают, что querySelectorAll не используется. Если это так, Sizzle просто передает его на qsa, что все еще не так быстро, как использование ярлыка ID jQuery.
Ответ 4
Вы можете использовать функцию find() для более детального контроля над порядком выбора:
$('#foo').find('a');
Это, конечно, будет более впечатляющим с более сложными селекторами, где вы можете цепью find() и filter().
Для записи $('#foo').find('a') === $('a','#foo')
[Update] ok, позже я понял, что это именно то, что говорит ваша ссылка...
Механизм селектора jQuery (Sizzle) был реорганизован в прошлом году, здесь вы найдете подробные объяснения:
http://www.wordsbyf.at/2011/11/23/selectors-selectoring/
Ответ 5
Вместо фильтрации с помощью a
внутри элементов #foo
просто присоедините класс к элементам a
и получите элементы a
с классом типа $("a.class");
. Это было бы более эффективно.
Ответ 6
Еще один "попробуй это для себя":
Похоже, что это не так сильно отличается от "плоской" DOM (1 и 2), но производительность значительно отличается от вложенной DOM.
Также обратите внимание, что некоторые из тестовых примеров не выбирают правильные элементы (т.е. $('.a')
vs $('.a', context)
), но я оставил их из исходных тестов только для сравнения.
Ответ 7
В этом примере будут извлекаться все элементы привязок a
в элементе с именем foo
, чтобы найти все a на странице, а затем отфильтровать внутри #foo, как вы хотите, и вы должны выбрать a #foo
$("a #foo");
это приведет к извлечению всех элементов foo
внутри элементов a
.