Печать

Запутанные места jQuery

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

1. .parent() vs. .parents() vs. .closest()

Все эти 3 метода связаны с навигацией вверх по DOM, выше элемента возвращаемого селектором, с указанным предком, с указанным родителем

Все три функции похожи, но каждая обладает свом уникальным функционалом.

parent(selector)

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

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

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

Совет: возможность ограничить дейчствие методов как во второй строке примера — это одна из фишек jQuery.

parents(selector)

Этот метод дейстует почти так же как и предыдущий, кроме того, что он не ограничен одним уровнем. Это значит, что он возвращает множество предков:

$('li.nav').parents('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>, которые находятся на 3 уровне вложенности, в оранжевый цвет.

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

Этот код можно интерпретировать так: для каждого <li> найденного в #nav, прямой потомок или нет все равно, смотрим, сколько элементов родителей <li> находится выше #nav. Если их 2, то значит этот <li> — элемент третьего уровня и его нужно раскрасить.

closest(selector)

Сейчас я открою вам маленький, но очень полезный секрет. Метод closest работает почти как parents(), кроме того, что он возвращет только одного родителя. Судя по моему опыту, чаще приходится проверять наличие одного элемента-родителя, а не всех вышестоящих.Потому я предпочитаю использовать этот метод, а не метод parent(). Скажем, мы хотим проверить, является ли элемент потомком другого:

if ($('#element1').closest('#element2').length == 1)
 alert("yes - #element1 is a descendent of #element2!");
 else
 alert("No - #element1 is not a descendent of #element2");

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

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

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

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

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

2. .position() vs. .offset()

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

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

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

3. .css(‘width’) and .css(‘height’) vs. .width() and .height()

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

Главное различие в функциях — они возвращают данные разных типов: css('width') и css('height') возвращают размеры как строку,с добаввленными px в конце, а результат width() и height() -- это целое число ( тип integer) .

На самом деле еще один малоизвестное различие, связанное с IE (сюрприз!), И именно поэтому вы должны избегать использования css('width') и css('height'). Это связано с тем, что IE, когда его просят прочитать «вычисленные» (т.е. не заданные явно) размеры, возвращает auto. В ядре jQuery , width() и height() реализованы с помощью .OffsetWidth и .OffsetHeight, которые присутствуют у каждого элемента, и которые IE делает считывает правильно.
Но если вы хотите прочитать ширину одного элемента и установить ее у другого элемента, вы можете использовать css('width'), потому что установка значения в CSS требует указания единиуы измерения.
Если вы хотите узнать ширину элемента, чтобы произвести какие-либо расчеты, то лучше использовать width().
Помните, что вы можете замеять эти функции другн другом, с помощью одной строки кода. Например:

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

Ну и наконец, width() и height() есть еще одна хитрость: они не могут вернуть размеры окна и документа.
Если вы захотите сделать это, используя css(), вы получите сообщение об ошибке.

4. .click() (etc) vs. .bind() vs. .live() vs. .delegate

Эти функции связаны с привязкой событий к элементам. Разница заключается в том, какие элементы они могут связывать и то, насколько мы можем влиять на обработчик события (“callback”).

click() (etc)

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

Такие псевдонимы позволяют быстро связать функцию-обработчик с определенным типом события. Они принимают всего один элемент: функцию, которая выполнится когда событие произойдет.Пример:
$('#table td ').click(function() {
alert(«The TD you clicked contains '»+$(this).text()+"'");
});

Всякий раз, когда на <div> внутри #table произойдет клик, alert() выведет его содержимое.

bind()

То же самое можно сделать с bind():

$('#table td ').bind('click', function() {
 alert("The TD you clicked contains '"+$(this).text()+"'");
 });

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

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

$('#table td').bind('click contextmenu', function() {
 alert("The TD you clicked contains '"+$(this).text()+"'");
 });

Сейчас функция-обработчик срабатывает при клике на <td> правой или левой кнопкой. Я уже говорил, что bind() дает больший контроль над обработчиками событий. Он это делает, передавая три аргумента вместо двух. Еще один аргумент — это данные, которые будут доступны внутри функции обработчика:

$('#table td').bind('click contextmenu', {message: 'hello!'}, function(e) {
 alert(e.data.message);
 });

Как вы видите, мы можем передавать в сallback переменные, в примере это сообщение для вывода.

Вот пример того, как выводить различные сообщения при клике правой и левой кнопкой мыши, без объявления лишних переменных

$('#table td').bind('click', {message: 'You left clicked a TD'}, function(e) {
 alert(e.data.message);
 });
 $('#table td').bind('contextmenu', {message: 'You right clicked a TD'},
 function(e) {
 alert(e.data.message);
 });

Обработчики, связанные с помощью bind() и методами-псевдонимами(.mouseover(), etc) можно освободить с помощью unbind().

live()

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

delegate()

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

$('#myDiv a').live('mouseover', function() {
 alert('hello');
});

Но не…

$('#myDiv').children('a').live('mouseover', function() {
 alert('hello');
});

… которые не будет работать, так же как не будет работать вызов live прямо для элемента DOM, напрмимер $(document.body).

delegate()

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

$('#myDiv').delegate('a', 'mouseover', function() {
 alert('hello');
});

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

Пример из жизни

Давайте представим приложение для заказа авиабилетов. Пользователю предлагается ввести имена летящих пассажиров. Введенные имена появляются в новых строках в таблице, #passengersTable, с двумя столбцами: «Name» и «Delete» (содержит кнопку, чтобы удалить строку с пассажиром).
Чтобы добавить нового пассажира (т.е.строку), пользователь должен кликнуть на кнопку #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('delete');
 $(td2).append(button);
 $(tr).append(td1);
 $(tr).append(td2);
 $('#passengersTable tbody').append(tr);
});

Обработчик назначется с помощью click(), а не live('click'), потому что известно, что кнопка #addPassenger уже существует.
А как насчет кнопки “Delete”?

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

Здесь мы используем live() потому что кнопка далить не существует в начале выпонения скрипта.

Обработчики назначенные методом live() можно отменить методом die().

У live() есть недостаток: вы не можете назначить несколько обработчиков событий. Можно назначить только один обработчик.

Интересная статья? Поделись ей с другими: