• Главная»
  • Уроки»
  • jQuery»
  • Моменты использования jQuery, в которых чаще всего путаются начинающие разработчики

Моменты использования jQuery, в которых чаще всего путаются начинающие разработчики

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

Теперь скрипт, который находит все ссылки с определенным классом CSS в документе и привязывает к ним обработчик определенных событий, выполняется только в одну строчку кода, а не в 10. Для обеспечения таких функций jQuery приносит с собой часть собственного API, с функциями, методами, и синтаксическими конструкциями. Некоторые из них являются достаточно путанными или похожими на другие, но они все в действительности отличаются друг от друга. В данном уроке мы постараемся разъяснить несколько запутанных мест.

1. .parent() и .parents() и .closest()

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

parent(selector)

Данный вызов соответствует только непосредственному родителю элемента. Метод получает селектор, с помощью которого выделяется элемент с соответствующим родителем. Например:

$('span#mySpan').parent().css('background', '#f90');
$('p').parent('div.large').css('background', '#f90');

Первая строка изменяет предка для #mySpan. Вторая строка делает такие же изменения родителей для всех тегов <p>, при условии, что предки являются div и имеют класс large.

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

parents(selector)

Данный метод действует как и parent(), за исключением того, что он не ограничен одним уровнем вышестоящих соответствующих элементов. Он может возвращать нескольких предков. Например:

$('li.nav').parents('li'); //для каждого LI, который имеет класс nav, находим всех родителей/предков, которые также являются элементами LI

В данной строке определяется, что для каждого  <li>, который имеет класс nav, нужно вернуть всех родителей/предков, которые также являются элементами <li>. Такой код может быть полезным в многоуровневых деревьях навигации, например:

<ul id='nav'>
<li>Link 1
<ul>
<li>Sub link 1.1</li>
<li>Sub link 1.2</li>
<li>Sub link 1.3</li>
</ul>
<li>Link 2
<ul>
<li>Sub link 2.1
<li>Sub link 2.2
</ul>
</li>
</ul>

Представьте, что нужно задать для каждого элемента <li> третьего уровня в данном дереве оранжевый цвет. Делаем просто:

$('#nav li').each(function() {
	if ($(this).parents('#nav li').length == 2)
		$(this).css('color', '#f90');
});

Данный код можно перевести так: для каждого <li>, найденного в #nav (поэтому используется цикл each()), является ли он прямым потомком или нет, смотрим, сколько элементов <li> являются для него родителями/предками внутри #nav. Если их количество равно двум, то данный элемент <li> находится на нужном уровне и надо менять его цвет.

closest(selector)

Работает как  parents(), за исключением того, что он возвращает только одного родителя/предка. Обычно используется для проверки существования определенного элемента в списке предков, без того, чтобы получать полный список. Допустим, нужно проверить, является ли один элемент потомком другого, вне зависимости от глубины дерева DOM:

if ($('#element1').closest('#element2').length == 1)
	alert("Да - #element1 является потомком #element2!");
else
	alert("Нет - #element1 не является потомком #element2");

Совет: вы можете имитировать closest() с помощью parents(), ограничивая его одним возвращаемым элементом.

$($('#element1').parents('#element2').get(0)).css('background', '#f90');

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

$('div#div2').closest('div').css('background', '#f90');

Данная строка кода установит оранжевый цвет для самого элемента #div2, потому что closest() ищет <div>, а ближайщим <div> для #div2 является он сам.

2. .position() и .offset()

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

position() вычисляет положение относительно смещения родителя — или, в других словах, ближайшего родителя или предка данного элемента, который имеет свойство position: relative. Если такого родителя или предка не найдено, положение вычисляется относительно документа (то есть верхний-левый угол окна обзора).

offset()всегда вычисляет положение относительно документа, вне зависимости от атрибута position родителя или предков.

Рассмотрим следующих два элемента <div>:

Привет! Я #wrapperDiv. Мои свойства position: relative и left: 100px

Привет! Я #innerDiv. Мои свойства position: absolute, left: 50px и top: 80px.

Использование offset() и position() для #innerDiv вернет разные результаты.

var position = $('#innerDiv').position();
var offset = $('#innerDiv').offset();
alert("Position: left = "+position.left+", top = "+position.top+"\n"+
      "Offset: left = "+offset.left+" and top = "+offset.top
)

 

Попробуйте сами. Чтобы увидеть результат, нажмите здесь.

3. .css(‘width’) + .css(‘height’) и .width() + .height()

Эти три метода вычисляют размеры элемента в пикселах. Они возвращают реальные размеры элемента не учитывая того, как его растягивает внутренний контент.

Они различаются типом возвращаемых значений: css('width') и css('height') возвращают строку, в конец которой добавляется px, а width() и height() возвращают целочисленные значения.

В действительности есть мало-известная разница, которая возникает при работе в IE (сюрприз!), и которая является причиной для того, чтобы избегать использования css('width') и css('height'). Дело в том, что IE при запросе “вычисленных” размеров (то есть тех, которые не установлены явно) возвращает  auto. В ядре jQuery  width() и height() используют свойства .offsetWidth и .offsetHeight, непременно имеющиеся у каждого элемента, которые IE читает правильно.

Но если вы работаете с элементами, для которых размеры устанавливаются явно, не стоит беспокоится. Если нужно определить ширину одного элемента и установить ее для другого, то следует использовать функцию css('width'), потому что она возвращает значение с уже добавленным суффиксом ‘px’.

Каждый из данных методов может быть имитирован с помощью другого и нескольких дополнительных строк кода JavaScript. Например:

var width = $('#someElement').width(); //получаем целое
width = width+'px'; //теперь это строка, наподобие той, которую возвращает css('width')
var width = $('#someElement').css('width'); //получаем строку
width = parseInt(width); //теперь это целое, наподобие того, которое возвращает width()

Кроме того, width() и height() могут возвращать размеры окна и документа. Если попробовать получить тоже самое с помощью метода css(), то попытка закончится ошибкой..

4. .click() (и так далее) и .bind() и .live() и .delegate

Данные методы предназначены для привязывания событий к элементам. Разница заключается в том, к каким элементам привязываются события, и как мы можем влиять на обработчик событий. Звучит несколько запутанно, но далее все разъясняется подробно.

click() (и так далее)

Важно понимать, что метод bind() является папочкой части API jQuery, которая связана с обработкой событий. Большинство уроков, посвященных обработке событий, связаны с просто выглядящими методами, такими как  click() и mouseover(), но за кулисами все они являются просто лейтенантами, которые подчиняются bind().

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

$('#table td ').click(function() {
	alert("TD на который вы нажали содержит '"+$(this).text()+"'");
});

Данный код просто выводит сообщение при нажатии кнопки мыши на элементе <td> внутри #table.

bind()

Можно сделать тоже самое с помощью bind:

$('#table td ').bind('click', function() {
	alert("TD на который вы нажали содержит '"+$(this).text()+"'");
});

Тип события передается как первый аргумент bind(), а в качестве второго аргумента выступает возвратная функция. Зачем использовать bind(), когда есть простые функции псевдонимы?

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

$('#table td').bind('click contextmenu', function() {
	alert("TD на который вы нажали содержит '"+$(this).text()+"'");
});

Теперь наша функция будет выполняться в случае если нажата левая или права кнопка мыши на элементе <td>.

Как bind() дает больший контроль над обработчиком событий?  С помщью передачи трех аргументов, а не двух. Аргумент номер два может быть объектом данных, который содержит свойства, доступные для обработчика. Например:

$('#table td').bind('click contextmenu', {message: 'Привет!'}, function(e) {
	alert(e.data.message);
});

Мы передаем в функцию обработчик набор переменных, которые становятся доступны внутри функции, в данном случае это переменная message.

Зачем так делать? Почему бы не определить какую-нибудь переменную вне функции обработчика и передавать данные через нее? По причине области видимости переменных. Когда мы читаем переменную, JavaScript начинает работать в текущей области видимости и работает с ней вне ее пределов (в этом заключается фундаментальное отличие от других языков программирования). Рассмотрим следующий пример:

var message = 'Вы нажали левую кнопку мыши на TD';
$('#table td').bind('click', function(e) {
	alert(message);
});
var message = 'Вы нажали правую кнопку мыши на TD';
$('#table td').bind('contextmenu', function(e) {
	alert(message);
});

Вне зависимости от того, какую кнопку мыши нажали на <td>, мы всегда будем видеть сообщение о нажатии правой кнопки. Потому что переменная message читается функцией alert() в момент совершения события, а не в момент привязывания события к элементу.

Если задать для каждого события свою версию message при привязке события, то проблема будет решена.

$('#table td').bind('click', {message: 'Вы нажали левую кнопку мыши на TD'}, function(e) {
	alert(e.data.message);
});
$('#table td').bind('contextmenu', {message: 'Вы нажали правую кнопку мыши на TD'}, function(e) {
	alert(e.data.message);
});

События, привязанные с помощью метода bind() и псевдонимов (.mouseover(), и так далее), отсоединяются с помощью метода unbind().

live()

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

var newDiv = document.createElement('div');
newDiv.appendChild(document.createTextNode('Миру мир!'));
$(newDiv).css({width: 100, height: 100, background: '#f90'});
document.body.appendChild(newDiv);

delegate()

Недостатком метода live(), в отличие от большинства методов jQuery, является то, что его нельзя использовать в цепочках. То есть, его надо использовать непосредственно с селектором:

$('#myDiv a').live('mouseover', function() {
	alert('Привет!');
});

Но нельзя делать так:

$('#myDiv').children('a').live('mouseover', function() {
	alert('Привет!');
});

Такой вызов приведет к ошибке, также как и непосредственное использование элемента  DOM, например, с $(document.body).

delegate(), который появился в jQuery 1.4.2, создает метод решения данной проблемы за счет того, что первым аргументом является контекст в селекторе. Например:

$('#myDiv').delegate('a', 'mouseover', function() {
	alert('Привет!');
});

Как и live(), метод delegate() привязывает события и к существующим элементам и к будущим. Обработчик отсоединяется с помощью метода undelegate().

Пример использования

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

Допустим, что мы делаем приложение для заказа билетов на самолет. Запрашиваем у пользователя имена всех пассажиров. Введенные имена пассажиров появляются в новых строках таблицы #passengersTable с двумя колонками: “Имя” (содержит текстовое поле с именем пассажира) и “Удалить” (содержит кнопку для удаления строки с именем пассажира).

Чтобы добавить нового пассажира (то есть строку), пользователь нажимает кнопку #addPassenger:

$('#addPassenger').click(function() {
	var tr = document.createElement('tr');
	var td1 = document.createElement('td');
	var input = document.createElement('input');
	input.type = 'text';
	$(td1).append(input);
	var td2 = document.createElement('td');
	var button = document.createElement('button');
	button.type = 'button';
	$(button).text('Удалить');
	$(td2).append(button);
	$(tr).append(td1);
	$(tr).append(td2);
	$('#passengersTable tbody').append(tr);
});

Заметьте, что событие для #addPassenger привязывается с помощью click(), а не live('click'), так как нам известно, что данная кнопка существует с самого начала.

А что с кодом обработчика события для кнопки “Удалить”, которая удаляет имя пассажира?

$('#passengersTable td button').live('click', function() {
	if (confirm("Вы действительно хотите удалить из списка данного пассажира?"))
	$(this).closest('tr').remove();
});

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

Обработчик, который был привязан с помощью метода live(), отсоединяется с помощью метода die().

Удобство использования метода live() имеет свою цену: ему нельзя передать несколько обработчиков событий, только один.

5. .children() и .find()

children()

Данный метод возвращает непосредственного ребенка элемента (или элементов), возвращаемого селектором. Как и большинство методов jQuery для перемещения по структуре, в нем можно использовать фильтрацию с помощью селектора. Так, если вы хотите установить оранжевый цвет для всех <td> в таблице, которые содержат слово “dog”, то нужно использовать следующий код:

$('#table tr').children('td:contains(dog)').css('background', '#f90');

find()

Данный метод работает как и children(), только он просматирвает и ребенка элементов и более дальних родственников. К тому же он более безопасный.

Допустим, вам нужно спрятать все <tr>, которые имеют класс hideMe. Но где-нибудь пропустили <tbody> в разметке таблицы. Будет очень рисковано использовать указание <tr> следующим образом:

$('#table tbody tr.hideMe').hide();

... потому что произойдет ошибка, если где-то не хватает  <tbody>. Вместо этого лучше использовать find():

$('#table').find('tr.hideMe').hide();

Таким образом мы ищем <tr> в #table с .hideMe, и вне зависимости от других условий, прячем его.

6. .not() и .is() и :not()

Как и следовало ожидать из названия, функции с именами “not” и “is” имеют противоположное назначение. Но данные функции в действительности не эквивалентны.

.not()

not() возвращает элемент, который не соответствует заданному селектору. Например:

$('p').not('.someclass').css('color', '#f90');

В данной строке кода изменяются все элементы (для них устанавливается оранжевый цвет), которые не имеют класса someclass.

.is()

Если нужно указать параграфы, которые содержат класс someclass, то можно даже не думать об использовании подобной конструкции:

$('p').is('.someclass').css('color', '#f90');

Данная строка ошибочна, так как is() не возвращает элемент: она возвращает значение boolean. Это функция для проверки соответствует ли элемент цепочки селектору.

Когда может быть полезна функция is()? При запросе элемента о его свойствах (смотри пример использования ниже).

:not()

:not() - это псевдо-селектор, эквивалентный методу .not() .  Он выполняет туже работу, но с одним отличием - как и все псевдо-селекторы, он может быть использован в середине строки селектора, и парсер строк jQuery примет его и будет работать с ним. Следующий пример эквивалентен примеру с методом .not(), который приводился выше.

$('p:not(.someclass)').css('color', '#f90');

Пример использования

Функция .is() используется для проверки, а не для фильтрования, элементов. Допустим, нужно сделать следующую форму подписки. Обязательные для заполнения поля имеют класс required.

<form id='myform' method='post' action='somewhere.htm'>
<label>Имя *
<input type='text' class='required' />
<br />
<label>Фамилия *
<input type='text' class='required' />
<br />
<label>Номер телефона
<input type='text' />
<br />
<label>Желаемое имя пользователя *
<input type='text' class='required' />
<br />
<input type='submit' value='Отправить' />
</form>

При отправке наш скрипт должен проверить, что все обязательные поля заполнены. Если это не так, то пользователь получает сообщение и отправка прерывается.

$('#myform').submit(function() {
	if ($(this).find('input').is('.required[value=]')) {
		alert('Обязательные для заполнения поля должны быть заполнены!');
		return false; //прерываем отправку
	}
});

Здесь нам не нужен элемент для манипулирования им, но нужно проверить его существование. Часть цепочки is() просто проверяет существование полей внутри  #myform, которые соответствуют селектору. Она возвращает true, если такие элементы будут найдены, что означает присутствие незаполненных полей.

7. .filter() и .each()

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

.each()

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

$('p').each(function() {
	alert($(this).text());
});

В данном примере берутся все элементы <p> в документе и выводится сообщение с их содержимым.

Но each() является большим, чем просто метод для прохода по выделены селектором элементам. Он может быть использован для обработки массивов (подобно функции PHP foreach()). Его можно использовать как метод или как функцию ядра jQuery. Например …

var myarray = ['один', 'два'];
$.each(myarray, function(key, val) {
	alert('Значение ключа '+key+' : '+val);
});

То же самое можно сделать так:

var myarray = ['один', 'два'];
$(myarray).each(function(key, val) {
	alert('Значение ключа '+key+' : '+val);
});

Для каждого элемента в массиве myarray  с помощью переменных key и val возвратной функции становятся доступны элементы массива.

Вы также можете проходить по объектам, но только с помощью первого способа (то есть, используя $.each).

jQuery известна как библиотека для манипулирования элементами DOM и эффектами, немного отличаясь по назначению от других рабочих сред, таких как   MooTools, но each() расширяет ее функционал до API  JavaScript.

.filter()

filter(), как и each(), проходит по всем элементам в цепочке, но в дополнение он удаляет элементы из цепочки, если они не проходят определенной проверки.

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

$('p.someClass').css('color', '#f90');
$('p').filter('.someclass').css('color', '#f90');

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

var elements = $('#someElement div ul li a');
//через сотню строчек кода ниже...
elements.filter('.someclass').css('color', '#f90');

elements был определен где-то в начале нашего кода, а теперь не нужно изменять некоторые элементы в нем.

filter() в действительности находит все элементы, которые затем пропускаются через фильтрующую функцию, которая отбирает те, которые надо обрабатывать. Например:

$('p').filter(function() {
	return $(this).text().indexOf('привет') != -1;
}).css('color', '#f90')

В данном примере каждый <p>, найденный в документе, получит оранжевый цвет, если он содержит строку привет. В противном случае изменений не будет.

В нашем уроке выше было показано, что is(), несмотря на имя, не эквивалентен not(), как можно было бы ожидать. Используйте filter() или has() как положительный эквивалент для not().

В отличие от each(), filter() нельзя использовать для массивов и объектов.

Пример использования

Если посмотреть на пример в котором всем <p>, начинающимся с привет устанавливается оранжевый цвет, то можно увидеть, что есть вариант более простого выполнения:

$('p:contains(привет)').css('color', '#f90')

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

Допустим, есть таблица для CD, в которой имеется четыре колонки: исполнитель, название, жанр и цена. Используя элементы управления на странице пользователь устанавливает условие, что он хочет исключить из списка просмотра все CD с жанром “Country” или ценой выше $10. Это два условия для фильтрации, поэтому нам нужна фильтрующая функция:

$('#productsTable tbody tr').filter(function() {
	var genre = $(this).children('td:nth-child(3)').text();
	var price = $(this).children('td:last').text().replace(/[^\d\.]+/g, '');
	return genre.toLowerCase() == 'country' || parseInt(price) >= 10;
}).hide();

Для каждого <tr> внутри таблицы, определяются колонки 3 и 4 (жанр и цена соответственно). Известно, что таблица имеет четыре колонки, поэтому мы можем указать четвертую колонку с помощью псевдо-селектора :last. Для каждого CD мы храним жанр и цену в специальных переменных просто чтобы было удобнее работать с кодом.

Для цены, мы заменяем любой символ, который может помешать использованию математической функции для значения. Если столбец будет содержать значение $14.99, и мы попробуем использовать его в выражении сравнения, которое устанавливается ниже, то получим сообщение о том. что это не число, так как в нем присутствует символ $. Таким образом нужно удалить все, что не является числом или точкой.

Если хотя бы одно из условий выполняется, то мы возвращаем true (что означает скрытие строки).

8. .merge() и .extend()

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

.merge()

merge() позволяет объединять содержание двух массивов в первый указанный массив. Он выполняет перманентное изменение первого массива. При этом не создается нового массива, просто элементы второго массива добавляются к первому:

var arr1 = ['one', 'two'];
var arr2 = ['three', 'four'];
$.merge(arr1, arr2);

После выполнения кода  arr1 будет содержать четыре элемента one, two, three, four. Массив arr2 останется без изменений. (Данная фугкция эквивалентна функции PHP array_merge().)

.extend()

extend() выполняет такуюже операцию, но для объектов:

var obj1 = {one: 'un', two: 'deux'}
var obj2 = {three: 'trois', four: 'quatre'}
$.extend(obj1, obj2);

extend() имеет несколько преимуществ. Первое, можно объединять более чем два объекта — можно передавать столько объектов, сколько нужно. Второе, объединение может быть рекурсивным. То есть, если свойством объекта является сам объект, то оно может быть включено в объединенение. Чтобы выполнить такое объединение, надо передать true в качестве первого аргумента:

var obj1 = {one: 'un', two: 'deux'}
var obj2 = {three: 'trois', four: 'quatre', some_others: {five: 'cinq', six: 'six', seven: 'sept'}}
$.extend(true, obj1, obj2);

Заключение

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

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

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: www.smashingmagazine.com/2010/08/04/commonly-confused-bits-of-jquery/
Перевел: Сергей Фастунов
Урок создан: 3 Сентября 2010
Просмотров: 61499
Правила перепечатки


5 последних уроков рубрики "jQuery"

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 4 Сентября 2010 19:36
    Serega
    спасибо, полезный урок для новичков, как я.
  • 4 Сентября 2010 23:20
    Kesha_php
    Не плохая статья, довольно подробно о том о чём написано сложновато)Спасибо) Напишите пожалуйста урок про iframe и кроссбраузерному scroll) тож интересные темы
  • 5 Сентября 2010 10:42
    xsid
    перевод немножко хромает... А так - хорошая информация.
  • 5 Сентября 2010 12:38
    mikola
    Спасибо, очень хорошая статья.
  • 5 Сентября 2010 23:10
    Димон
    как раз, что искал.
  • 7 Сентября 2010 21:27
    mounta
    ужасно сложно
  • 9 Сентября 2010 22:13
    Андрей
    спасибо, побольше бы таких емких новостей
  • 11 Сентября 2010 19:53
    леха
    спасибо
  • 18 Октября 2010 15:09
    Nyukers
    Наверняка полезно. "jQuery не является языком программирования" - наверное єто его главный недостаток. Столько кода и все руками...
  • 24 Июля 2012 15:31
    markusila
    спасибо! очень помогло) даже оставлю в качестве шпаргалки)
^ Наверх ^