# Навигация по jQuery-коллекции Мы умеем перебирать коллекцию, получать нужный элемент по номеру. Достаточно ли этого? Конечно нет! По коллекции нужно путешествовать. Получать детей, искать родителей, выбирать элементы по условию, и чтобы это было удобно. [cut] ## Шпаргалка по методам Методов для навигации по коллекции много. Их гораздо проще воспринять, если разбить на группы. Итак, на картинке ниже: [pre]
  parent parents parentsUntil
closest
 
prev prevAll prevUntil
siblings
filter not has
eq first last slice
is add map
next nextAll nextUntil
siblings
  find
children contents
 
[/pre] Так как методов много, то запомнить их все сложно, да и не нужно. Достаточно знать, какие есть, а при необходимости -- обратиться к этой странице или к [официальной документации](http://api.jquery.com/category/traversing/). У этих методов очень хорошие названия, они станут очевидны, как только мы их рассмотрим чуть подробнее. ## По текущей коллекции
filter(фильтр), not(фильтр), has(фильтр)
Возвращают отфильтрованную коллекцию, содержащую только элементы... ```html ``` В качестве фильтра может быть использована также функция, принимающая элемент как `this` и возвращающая `true/false`: ```html ```
eq(n), first, last, slice(start, end)
Возвращает новую коллекцию...
is(фильтр)
Возвращает `true`, если *какой-нибудь* элемент из коллекции подходит под фильтр. ```html ```
add(элементы)
Возвращает новую коллекцию из элементов текущей и новых, по селектору. ```html ``` Важно: эта функция не меняет текущую коллекцию, она создаёт новую из существующих и добавленных элементов.
map(функция)
Этот метод стоит особняком. Он не меняет коллекцию, не ищет в ней, а пропускает её через функцию и возвращает результаты. ```html ```
Заметим две приятные особенности: ## По родителям
[parent()](http://api.jquery.com/parent/), [parents(фильтр)](http://api.jquery.com/parents/), [parentsUntil(стоп, фильтр)](http://api.jquery.com/parentsUntil/)
Родители -- один `parent`, все `parents(фильтр)` (с фильтром по селектору) и до определённого `parentsUntil(где остановиться, селектор для элементов)`.
[closest(фильтр, элемент-контейнер)](http://api.jquery.com/closest/)
Ищет одного, ближайшего, родителя, подходящего под селектор. Второй, необязательный, аргумент `элемент-контейнер`, если он передан, ограничивает поиск. jQuery будет идти вверх до тех пор, пока не встретит этот DOM-элемент. ```html ```
## По потомкам
[find(селектор)](http://api.jquery.com/find/)
Ищет в потомках по селектору. ```js // для каждого LI искать CODE среди потомков, вернуть их коллекцию $('li').find('code'); ```
[children(селектор)](http://api.jquery.com/children/), [contents()](http://api.jquery.com/contents/)
Выбирает детей по селектору, без аргументов -- всех детей: ```html ``` Метод [contents()](http://api.jquery.com/contents/) -- также возвращает детей, но в отличие от `children` -- узлы всех типов, включая текстовые и комментарии, а не только узлы-элементы. ```html ```
## По соседям
prev(), prevAll(фильтр), prevUntil(элемент, фильтр)
Получить левого соседа `prev`, всех левых соседей `prevAll` или всех левых соседей `prevUntil` до указанного (`элемент`), подходящих под фильтр.
next(), nextAll(фильтр), nextUntil(элемент, фильтр)
То же самое, но правые соседи.
siblings()
Получить коллекцию всех соседей.
## Стек селекторов: методы end и addBack Все методы не влияют на текущую коллекцию, они создают и возвращают новую. При каждом новом поиске возвращается jQuery-объект с результатом. **Предыдущий jQuery-объект при этом не теряется, к нему всегда можно вернуться вызовом [end()](http://api.jquery.com/end/).** Посмотрим это на примере задачи. Допустим, мы нашли форму `$('form')` и хотим выбрать все чекбоксы в ней. Можно сделать это так: ```js //+ no-beautify $('form') .find(':checkbox') .each(function() { ... }); // сделать что-то с элементами коллекции ``` ...И теперь хотим поискать в этой же форме что-то ещё. Самый очевидный способ это сделать -- сохранить `$('form')` в переменной: ```js //+ no-beautify var form = $('form'); form .find(':checkbox') .each(function() { ... }); form .find(':radio') .each(function() { ... }); ``` ...Но на самом деле в этом нет необходимости. jQuery, при вызове `find` сохраняет предыдущую найденную коллекцию, к которой можно вернуться вызовом `end()`: ```js //+ no-beautify $('form') .find(':checkbox') // нашли чекбоксы внутри .each(function() { ... }) // обработали *!* .end() // вернулись обратно на форму */!* .find(':radio') // поискали другие элементы внутри.. .each(function() { ... }); // сделали с ними что-то ещё ``` **Метод [addBack(selector)](http://api.jquery.com/addBack/) добавляет элементы из предыдущей коллекции к текущей.** Если указать селектор, то он отфильтрует их. Этот метод бывает очень удобен, когда какую-то операцию нужно сделать как с детьми, так и с самим элементом, например: ```js //+ no-beautify $('ul') // коллекция: UL .children() // получить коллекцию LI .addBack() // добавить к ней сам UL .each(...) // теперь у нас коллекция LI и UL-родитель ``` Полный список методов вы найдёте в [разделе Traversing](http://api.jquery.com/category/traversing/) документации. При использовании jQuery, вы часто будете иметь с ними дело и отлично запомните. ## Неэффективность jQuery Для ряда задач jQuery-методы поиска по коллекции неэффективны. Например, нужно найти первого потомка. Некоторые способы: ```js elem.children(':first'); elem.children().first(); $(':first-child', elem); ``` Все эти способы неэффективны. Особенно первые два.
  1. Первый проходит всех детей, по псевдо-фильтру выбирая нужного.
  2. Второй копирует детей в коллекцию, а потом получает из неё первый элемент.
  3. Третий запускает `querySelectorAll(':first-child)` (на самом деле там чуть сложнее, но не суть важно) в контексте `elem`. Здесь, с первого взгляда, нет копирования всех детей, но внутренний алгоритм браузера для выполнения `querySelectorAll` всё равно работает полным перебором. Впрочем, это будет намного быстрее, чем предыдущие решения.
Обычный же вызов `elem[0].children[0]` на порядки обгонит все вышеприведённые способы, особенно если детей много. Какой вывод из этого? **Там, где потеря в производительности некритична, используем jQuery -- для удобства. Это большинство случаев. Там, где она важна -- обращаемся к обычному DOM.**