# Навигация по jQuery-коллекции
Мы умеем перебирать коллекцию, получать нужный элемент по номеру. Достаточно ли этого?
Конечно нет! По коллекции нужно путешествовать. Получать детей, искать родителей, выбирать элементы по условию, и чтобы это было удобно.
[cut]
## Шпаргалка по методам
Методов для навигации по коллекции много. Их гораздо проще воспринять, если разбить на группы.
Итак, на картинке ниже:
- По центру -- действия с текущей коллекцией: фильтрация, добавление, изменение и т.п.
- Сверху -- методы для доступа к родителям.
- Снизу -- для поиска среди потомков.
- Слева/справа -- для поиска соседей.
[pre]
[/pre]
Так как методов много, то запомнить их все сложно, да и не нужно. Достаточно знать, какие есть, а при необходимости -- обратиться к этой странице или к [официальной документации](http://api.jquery.com/category/traversing/).
У этих методов очень хорошие названия, они станут очевидны, как только мы их рассмотрим чуть подробнее.
## По текущей коллекции
- filter(фильтр), not(фильтр), has(фильтр)
- Возвращают отфильтрованную коллекцию, содержащую только элементы...
- для `filter` -- подходящие под фильтр,
- для `not` -- не подходящие под фильтр,
- для `has` -- содержащие потомка, подходящего под фильтр.
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
В качестве фильтра может быть использована также функция, принимающая элемент как `this` и возвращающая `true/false`:
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
- eq(n), first, last, slice(start, end)
- Возвращает новую коллекцию...
- `eq(n)` -- с одним элементом по его номеру,
- `first/last` -- из первого/последнего элемента,
- `slice(start[, end])` -- из элементов с номера `start` до `end`.
- is(фильтр)
- Возвращает `true`, если *какой-нибудь* элемент из коллекции подходит под фильтр.
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
- add(элементы)
- Возвращает новую коллекцию из элементов текущей и новых, по селектору.
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
Важно: эта функция не меняет текущую коллекцию, она создаёт новую из существующих и добавленных элементов.
- map(функция)
- Этот метод стоит особняком. Он не меняет коллекцию, не ищет в ней, а пропускает её через функцию и возвращает результаты.
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
Заметим две приятные особенности:
- Большинство методов, которые осуществляют фильтрацию, могут принимать как селектор так и фильтрующую функцию. jQuery любит функции.
- Большинство методов, которые принимают элементы, могут получать их в виде jQuery-коллекции или селектора. Главное, чтобы найти по этому можно было.
## По родителям
- [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
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
Метод [contents()](http://api.jquery.com/contents/) -- также возвращает детей, но в отличие от `children` -- узлы всех типов, включая текстовые и комментарии, а не только узлы-элементы.
```html
filter
: только подходящие.
not
: не подходящие.
has
: по потомкам.
- ...
```
## По соседям
- 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);
```
Все эти способы неэффективны. Особенно первые два.
- Первый проходит всех детей, по псевдо-фильтру выбирая нужного.
- Второй копирует детей в коллекцию, а потом получает из неё первый элемент.
- Третий запускает `querySelectorAll(':first-child)` (на самом деле там чуть сложнее, но не суть важно) в контексте `elem`. Здесь, с первого взгляда, нет копирования всех детей, но внутренний алгоритм браузера для выполнения `querySelectorAll` всё равно работает полным перебором. Впрочем, это будет намного быстрее, чем предыдущие решения.
Обычный же вызов `elem[0].children[0]` на порядки обгонит все вышеприведённые способы, особенно если детей много.
Какой вывод из этого?
**Там, где потеря в производительности некритична, используем jQuery -- для удобства. Это большинство случаев. Там, где она важна -- обращаемся к обычному DOM.**