Обход и манипуляция

jQuery обеспечивает мощные инструменты для нахождения элемента или последующих элементов и работы с ними для получения желаемого результата. С помощью методов обхода и манипуляций можно браться за такие задачи, выполнение которых через родную манипуляцию DOM будет весьма болезненно. jQuery делает подобные задачи довольно простыми и интуитивно понятными.

В этом разделе мы рассмотрим некоторые (но не все) методы обхода и манипуляции. Предварительно вы должны знать несколько важных терминов. Рассмотрим фрагмент HTML:

<ul>
  <li>
    <span>
      <i>Foo</i>
    </span>
  </li>
  <li>Bar</li>
</ul>
  • Первый элемент списка является дочерним для маркированного списка.
  • Маркированный список является родителем обоих элементов списка.
  • <span> является потомком маркированного списка.
  • Маркированный список является предком для всего внутри него.
  • Два элемента списка являются родственными.

Обход

jQuery позволяет нам делать «обход» или передвижение по HTML-элементам, которые составляют нашу страницу. Сперва мы делаем начальную выборку, а затем двигаемся через DOM относительно этой выборки. По мере продвижения через DOM мы меняем нашу оригинальную выборку; в некоторых случаях заменяем оригинальную выборку новой, в других случаях мы добавляем или убираем что-то из оригинальной выборки.

Фильтрация выборки

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

var listItems = $( 'li' );

// фильтровать выборку только для элементов с классом special
var special = listItems.filter( '.special' );

// фильтровать выборку только для элементов без класса special
var notSpecial = listItems.not( '.special' );

// фильтровать выборку только для элементов содержащих <span>
var hasSpans = listItems.has( 'span' );

Важно отметить, что .not() не является противоположностью .is(). Метод .is() возвращает логическое значение, в то время как метод .not() возвращает новый объект jQuery.

Поиск элементов относительно выборки

Начальная выборка может служить «базой» для создания дополнительных выборок. К примеру, у вас может быть имеющаяся выборка, которая содержит отдельный пункт списка, а затем необходимо работать с его родственными элементами или родительским маркированным списком. Вы можете легко сделать новую выборку относительно существующей:

// Получить первый пункт списка на странице
var listItem = $( 'li' ).first(); // и .last()
  
// Получить родственные элементы этого пункта списка
var siblings = listItem.siblings();
  
// Получить следующего родственника этого пункта списка
var nextSibling = listItem.next(); // и .prev()
  
// Получить родителя этого пункта списка
var list = listItem.parent();

// Получить пункты списка являющихся непосредственными потомками list
var listItems = list.children();

// Получить ВСЕ пункты списка из list, включая вложенные
var allListItems = list.find( 'li' );

// Найти всех предков этого пункта списка с классом module
var modules = listItem.parents( '.module' );

// Найти ближайшего предка этого пункта списка с классом module
var module = listItem.closest( '.module' );

Вы также можете добавить что-то в существующую выборку с помощью метода .add(). Можно передать селектор, массив элементов, строку HTML или объект jQuery.

var list = $( '#my-unordered-list' );

// Сделать нечто со списком, а затем...

var listAndListItems = list.add( '#my-unordered-list li' );

Возвращение к исходной выборке

Когда вы используете один из методов обхода для поиска некоторых элементов относительно исходной выборки, jQuery сохраняет на неё указатель для случая, если вы захотите вернуться к исходной выборке. Рассмотрим, например, ситуацию, когда вы выбираете маркированный список, вносите в его элементы некоторые изменения, а затем снова желаете работать со списком. Вы можете использовать метод jQuery .end(), чтобы вернуться к исходной выборке:

$( '#my-unordered-list' )
  .find('li')
  
  // Теперь мы работаем с элементами списка
  .addClass('special')

.end()

// Теперь мы вернулись к работе со списком
.addClass('super-special');

Метод .end() позволят легко делать множество изменений в одном выражении. Такая практика мало что даёт для понимания кода, зато это коротко, словно рассказать историю, не переводя дыхания. Поэтому вы должны использовать этот метод редко. Чаще всего это приводит к коду, который трудно читать, поддерживать и отлаживать.

Лучшее решение может выглядеть следующим образом:

var list = $( '#my-unordered-list' );
var listItems = list.find('li');
  
listItems.addClass( 'special' );
list.addClass( 'super-special' );

jQuery также предлагает метод .addBack(), если вы хотите добавить свою исходную выборку в текущую. К примеру:

$( 'li.special' )

  .siblings()

  // Теперь мы работаем с родственниками исходной выборки
  .removeClass( 'important' )

  .addBack()

  // Теперь мы работаем с исходным li И его родственниками
  .addClass( 'urgent' );

Запутались? Как и с .end() всё это может привести к коду, с которым тяжело разбираться. Метод .addBack(), несмотря на его пользу, может легко усложнить код. Вместо этого лучшим решением было бы использовать метод .add(), чтобы объединить две исходные выборки:

var specialListItems = $( 'li.special' );
var otherListItems = specialListItems.siblings();
  
otherListItems.removeClass( 'important' );
specialListItems.add( otherListItems ).addClass( 'urgent' );

Существуют и другие методы обхода, которые мы здесь не рассматриваем. Вы можете прочитать обо всех методах обхода в документации.

Манипуляция

Методы манипуляции jQuery позволяют вам менять DOM вашей страницы используя гораздо дружелюбный синтаксис, чем родные методы манипуляции DOM. Методы манипуляции возвращают объект jQuery, а значит вы можете соединять или объединять их с другими методами jQuery, которые рассматривались ранее.

Изменение элементов

Есть множество способов для изменения элементов с помощью jQuery. Мы рассмотрим как решать некоторые наиболее распространённые задачи.

Добавление и удаление классов

Атрибут class может быть использован для добавления правил CSS, а также может быть полезен для выбора элементов через jQuery. Например, к элементу может быть добавлен класс hidden, с соответствующим правилом CSS, который определяет, что для этого класса установить display как none. Используя jQuery мы можем добавлять и удалять классы и тем самым влиять на отображение элементов.

$( 'li' ).addClass( 'hidden' );
$( 'li' ).eq( 1 ).removeClass( 'hidden' );

Если в вашем случае постоянно требуется добавлять и удалять класс, jQuery предлагает метод .toggleClass(). Следующий код добавляет класс hidden если его нет и удаляет его, если он присутствует.

$( 'li' ).eq( 1 ).toggleClass( 'hidden' );

Изменение стиля

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

Если вы не можете получить результат через добавление и удаление классов, то jQuery предлагает метод .css(), который позволяет вам установить стиль элементов напрямую. Обычно это требуется, если вы хотите установить числовые значения вычисляемые на лету — например, информация о позиционировании. Метод .css() не должен применяться для задания простых стилей, таких как display: none — практически во всех случаях для этого предпочтительнее использовать классы и CSS.

Рассмотрим, к примеру, ситуацию, когда вы хотите задать стиль элемента на основе ширины родительского элемента. Для резинового макета заранее узнать ширину родительского элемента может быть трудно или невозможно. В этом случае мы можем обратиться к методу .css().

var list = $( '#my-unordered-list' );
var width = Math.floor( list.width() * 0.1 );
  
list.find('li').each(function( index, elem ) {
  var padding = width * index;
  $( elem ).css( 'padding-left', padding + 'px' );
});

Если вам нужно установить сразу несколько свойств, вы можете передать объект в метод .css() вместо имени свойства и его значения. Обратите внимание, что имена свойств с дефисом нужно брать в кавычки.

$( 'li' ).eq( 1 ).css({
  'font-size': '20px',
  'padding-left': '20px'
});

Изменение значений форм

jQuery предлагает метод .val() для изменения значения элементов формы, таких как <select> и <input>.

Для текстовых элементов <input> вы можете установить их содержание передавая строку в метод .val():

$( 'input[type="text"]' ).val( 'новое значение' );

Для элементов <select> вы также можете установить выбранный пункт с помощью .val():

$( 'select' ).val( '2' );

Для чекбоксов вы должны установить свойство checked для элемента используя метод .prop().

$( 'input[type="checkbox"]' ).prop( 'checked', 'checked' );

Метод .prop() был введён в jQuery 1.6; предыдущие версии jQuery использовали для этой цели метод .attr(). Он по-прежнему работает в более поздних версиях jQuery, но для свойства checked в конечном счёте просто вызывает метод .prop(). Если вы применяете версию jQuery позднее 1.6, то должны всегда использовать метод .prop() чтобы установить свойство checked и другие свойства элементов DOM. Смотрите документацию для подробного объяснения.

Изменение других атрибутов

Вы можете использовать метод .attr(), чтобы изменить другие атрибуты элементов. Например, вы можете с его помощью установить новый атрибут title для ссылки:

$( 'a' ).attr( 'title', 'Нажми меня!' );

При установке атрибута вы можете передать функцию в качестве второго аргумента. Подобно другим устанавливающим методам эта функция получает два аргумента: индекс элемента, с которым она работает, и исходное значение атрибута. Функция должна вернуть новое значение атрибута.

$( 'a' ).attr( 'href', function(index, value) {
  return value + '?special=true';
});

Вы также можете удалить атрибуты полностью через .removeAttr().

Получение информации из элементов

В разделе «Основы jQuery» мы обсудили понятия «устанавливающий метод» и «метод чтения». Все методы, которые могут быть использованы для изменения элементов, также могут применяться и для получения информации из элементов. Например, описанный выше метод .val() может быть как устанавливающим методом, так и методом чтения.

var input = $( 'input[type="text"]' );
input.val( 'новое значение' );
input.val(); // вернёт 'новое значение'

Кроме того, метод .css() может быть использован для получения значения отдельных свойств CSS, если для него указать только имя свойства, а не значение.

var listItemColor = $( 'li' ).css( 'color' );

Когда методы манипуляции используются в качестве установки, метод работает только с первым элементом в выборке, с одним исключением: метод .text(). В этом случае, если аргумент не передаётся в метод, то будет возвращён текст всех выбранных элементов.

Размещение элементов в документе

Если вы выбрали элемент или создали новый элемент, то можете взять вашу выборку и поместить её в документ. Для этого есть два основных способа: путём вызова метода для элемента или элементов, которые вы хотите разместить, или путём вызова метода для элемента, относительно которого вы хотите разместить.

Рассмотрим, к примеру, ситуацию, когда вы хотите переместить первый элемент списка в его конец. Есть несколько способов для достижения этой цели.

Вы можете добавить пункт в список, вызывая .appendTo() для элемента списка:

var listItem = $( '#my-unordered-list li' ).first();
listItem.appendTo( '#my-unordered-list' );

Вы можете добавить пункт в список, вызывая .append() для списка:

var listItem = $( '#my-unordered-list li' ).first();
$( '#my-unordered-list' ).append( listItem );

Вы можете вставить элемент списка после последнего элемента с помощью вызова .insertAfter() для того элемента списка, который вы хотите переместить:

var listItems = $( '#my-unordered-list li' );
listItems.first().insertAfter( listItems.last() );

Вы также можете вставить элемент списка после последнего элемента с помощью вызова .after() для последнего пункта списка:

var listItems = $( '#my-unordered-list li' );
listItems.last().after( listItems.first() );

Существует много других методов для размещения элементов — вы можете разместить их вокруг, внутри, снаружи других элементов, всё зависит от ваших конкретных потребностей.

Наиболее эффективный способ основан на элементах, к которым у вас уже есть доступ. В приведённом выше примере вы можете выбрать как добавить элемент в маркированный список, если список был уже выбран для какой-то другой цели; или, если у вас уже имеется выборка, содержащая все элементы списка, то будет проще разместить первый элемент списка относительно последнего.

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

Копирование элементов

Вы можете сделать копию элемента или набора элементов используя метод .clone(). Он позволяет делать копию элементов, но заметьте, что копия хранится только в памяти — вы должны самостоятельно поместить её в документ. Вы можете манипулировать клонированным элементом или элементами до их размещения в документе.

var clones = $( 'li' ).clone();

clones.html(function( index, oldHtml ) {
  return oldHtml + '!!!';
});
  
$( '#my-unordered-list' ).append( clones );

jQuery не помешает вам клонировать элемент с идентификатором, поэтому вы должны убедиться, что изменили или удалили атрибут id перед установкой элемента в документ, поскольку в документе не должно быть несколько элементов с одинаковым id.

Удаление элементов

Есть три способа для удаления элементов из документа: .remove(), .detach() и .replaceWith(). Каждый метод служит конкретной цели.

Метод .remove() следует использовать для удаления элементов навсегда, также он убирает любые обработчики событий связанных с элементами. Метод .remove() возвращает указатель на удалённые элементы, но если вы повторно их добавите, то у них больше не будет связанных с ними событий.

$( '#my-unordered-list li' ).click(function() {
  alert( $( this ).text() );
});

var removedListItem = $( '#my-unordered-list li' ).first().remove();

removedListItem.appendTo( '#my-unordered-list' );
removedListItem.trigger( 'click' ); // alert не работает!

Метод .detach() применяется для временного удаления элементов из документа. К примеру, если вы собираетесь внести множество изменений в структуру вашей страницы через jQuery, то будет более эффективно использовать .detach() чтобы удалить выбранные элементы, внести в них необходимые изменения, а затем снова прикрепить элемент, используя один из методов вставки. Элементы, удалённые через .detach(), сохраняют свои обработчики событий; вы можете повторно добавить их в документ с помощью .appendTo() или другим методом.

$( '#my-unordered-list li' ).click(function() {
  alert( $( this ).text() );
});
  
var detachedListItem = $( '#my-unordered-list li' ).first().detach();

// Сделать что-то комплексное с элементом списка

detachedListItem.appendTo( '#my-unordered-list' );
detachedListItem.trigger( 'click' ); // alert!

Наконец, метод .replaceWith() заменяет элемент или элементы на элемент или HTML, который передаётся в качестве аргумента .replaceWith(). Метод возвращает заменённые элементы, но как и с .remove(), убираются все обработчики событий связанные с ними.

$( '#my-unordered-list li' ).click(function() {
  alert( $( this ).text() );
});
  
var replacedListItem = $( '#my-unordered-list li' ).first()
.replaceWith( '<li>новое!</li>' );
  
replacedListItem.appendTo( '#my-unordered-list' );

replacedListItem.trigger( 'click' ); // alert не работает!

Резюме

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

Автор: Ребекка Мёрфи
Последнее изменение: 11.06.2016
Редакторы: Влад Мержевич, Клим Щербаков