Введение в библиотеку jquery. Часть 2

August 28, 2007

JQuery: Быстрый старт в мире javascript. Часть 2



Сегодня мы продолжаем знакомство с jquery – библиотекой позволяющей автоматизировать и ускорить разработку javascript-решений. В прошлый раз мы разобрали одну из ключевых особенностей jquery – Selectors – механизм позволяющий адресовать элементы дерева DOM с помощью css/xpath подобной нотации. Сегодня мы рассмотрим специальные функции traversal, механизмы определения возможностей браузера, события.

Начнем мы с traversal. Что это такое и зачем оно нам нужно? Traversal – это общее название задачи поиска в дереве DOM нужной информации/узлов. Ранее мы научились записывать условие поиска узлов с помощью css/xpath нотаций, но это еще не все. Мы можем использовать комбинации специальных функций, которые будут уточнять сформированный результат поиска. Использовать эти функции удобно также и потому что мы можем записывать цепочку вызовов в сокращенной форме, например, так: первое_условие(параметры_1) . второе_условие(параметры_2) . третье_условие(параметры_3)

В таблице далее я привожу перечисление наиболее полезных функций Traversal. Эти функции достаточно гибки: почти каждая из них может принимать входной параметр, который служит для уточнения условия поиска. Если вы выстраиваете цепочку из вызовов функций, то следует помнить, что результаты – найденные узлы – которые вернула первая функция в цепочке, будут поданы на вход второй функции, и будут играть для нее роль базиса. Например, такая запись:
  1. $('div').add('h1').not('#siteSub2')
будет означать, что вы нашли все блоки div, затем к ним добавили все заголовки первого уровня h1 и выкинули из итогового набора узлов узел с идентификатором id=”siteSub2”.
Функция Описание
Add(условие) Служит для добавления в набор искомых узлов еще одного набора задаваемого параметром - условием или явно перечисленных узлов.
Children(условие) Находим дочерние узлы с заданным условием отбора.
Contains(шаблон) Выполняет поиск среди узлов тех, которые содержат внутри себя некоторую строку текста заданную параметром функции.
End() Необычная функция. Ее назначение – вернуть отобранный набор узлов в предшествующее состояние (до последнего уточняющего запроса отбора).
Filter(условие) Filter(функция) Из заданного набора узлов выбрасываются все те узлы, которые не подходят под параметр-условие. Можно задать параметр не только строкой-фильтром, но и передать ссылку на некоторую функцию, которая будет принимать решение подходит или нет каждый из узлов.
Next(условие) Функция выполняет поиск для некоторого узла его “брата” (sibling-а), находящегося после узла, от которого идет поиск.
Not(элемент) Not(условие) Выполняет удаление из списка найденных узлов того, кто задан как входной параметр-элемент или же подходит под условие удаления.
Parents(условие) Для заданного узла выполняет поиск его родителя (на любом уровне). Также требуется, чтобы для этого узла (ancestor) выполнялось условие отбора.
Parent(условие) Функция похожа на предыдущую, но поиск узлов идет только среди непосредственных родителей базисных узлов.
Prev(условие) Функция выполняет поиск для некоторого узла узел sibling-а, находящегося до узла от которого идет поиск.
Siblings(условие) Выполняется поиск всех узлов братьев (sibling) для базисных. Также требуется, чтобы для них выполнялось некоторое условие отбора, заданное параметром функции. Здесь, а также в функиях prev. Next поиск идет только среди непосредственных узлов sibling-ов, но не их потомков.
Вот парочка примеров использования описанных выше функций:
  1. var elts = $('p').add('b').contains ('bold');
Находим все параграфы, затем добавляем к ним набор узлов “b” и отбрасываем все те элементы, текстовое содержимое которых не содержит слова bold.
  1. var elts = $('em').next('#bar');
Находим все элементы “em”, затем ищем на одном с ними уровне вложенности элемент с идентификатором равным “bar”.
  1. var elts = $('li').filter ("[@id]");
Здесь мы находим все элементы списка – “li”, а затем отбираем среди них только те, для которых задано значение свойства “id”.
  1. var elts = $('*');
Находим абсолютно все узлы дерева документа.
  1. var elts = $('*').filter (
  2.     function (pos){
  3.       return this.className == 'apple' || this.id == 'item_1' || pos % 2 == 0; 
  4.     }
  5.  );
Здесь мы снова находим все узлы документа, но затем выполняем отбор среди них тех, которые имеют значение имени класса стилей css равным “apple”, также ищем тот узел, у которого значение идентификатора равно “item_1”. И, напоследок, отбираем еще элементы, порядковые номера которых в дереве DOM являются четными. Номер элемента задается как параметр функции, а ссылка на сам элемент доступна через ссылку “this”.
  1. $("input[@type='checkbox']").parent().is("form")
Функцию “is” использованную в этом примере я не описывал в таблице выше, поскольку она решает не задачу отбора узлов, а задачу проверки некоторого условия для узла. Так здесь находится элемент “input” со значением атрибута “type” равным “checkbox”. Затем находим родительский узел (обратите внимание, что условие отбора родителя не задано), и, наконец, проверяем, чтобы искомый узел являлся тегом form.
  1. var elts = $('#item_1').parents ('p[@bar]');
Здесь я выполняю поиск узла с идентификатором “item_1”, а затем начинаю восхождение от этого узла вверх по дереву DOM в поисках такого родительского тега “p”, который содержит атрибут “bar” (с любым значением).
  1. var elts = $('#item_1').parents ('p[@bar$=test]');


Аналогично предыдущему примеру, но теперь тег “p” должен содержать атрибут “bar” значение, которого заканчивается на слово “test”.

Надеюсь, вы уже заметили, что строки-условия отбора аналогичны тем конструкциям, про которые я рассказывал в прошлый раз, когда мы искали элементы DOM с помощью смешанной нотации css/xpath.

Теперь, когда мы разобрались с отбором узлов, остается только научиться их обрабатывать. Самый простой способ - это воспользоваться классическим циклом, например, так:
  1. var elts = $('span').add('h3').add ('#');
  2. for (var i=0; i< elts.length; i++)
  3.  alert (elts [i].id + " -- " + elts[i].innerHTML);
Возможен же и другой прием – применить специальную функцию итератор – each. В примере ниже я выполняю перебор всех элементов массива чисел от 1 до 10 и динамически создаю в конце страницы html набор тегов p содержащих заданную цифру.
  1. $.each( [0,1,2,3,4,5,6,7,8,9], function(i, n){
  2.    var p = document.createElement('p');
  3.    p.innerHTML = 'p # ' + i;
  4.    $('body')[0].appendChild (p);
  5.    });
Здесь я вызываю функцию each, передав ей в качестве параметра два объекта. Первый из них – массив элементов, которые будут перебраны в цикле и для каждого элемента массива будет вызвана функция – второй параметр.

Вот схожий пример, когда итерация будет выполнена не по обычному массиву, а по ассоциативному:
  1. $.each( { name: "John", lang: "JS" }, function(i, n){
  2.    alert( "Name: " + i + ", Value: " + n );
  3.  });
А в этом примере я покажу как можно всем найденным элементам списка “li” установить значение класса css равным имени “bar”. Откровенно говоря, это гораздо проще было бы сделать с помощью вызова
  1. $('li').addClass(“bar”), но все же это ведь учебный пример.
  2. $.each( $('li'), function(i, n){
  3.   n.className = 'bar';
  4.  }
  5. );
Есть похожая на each функция extend, получающая в качестве входных данных два объекта и выполняющая “апгрейд” первого из них свойствами второго объекта. В документации jquery по адресу http://docs.jquery.com/JavaScript был приведен идеальный пример, который я повторю и здесь, разве что, добавив парочку своих комментариев.
  1. <!-- предполагая, что у нас есть документ html, фрагмент которого выглядит так: -->
  2. <a id="zbang" /> 
  3. /* теперь пример кода javascript. Здесь выполняется привязка набора функций, 
  4. также массива свойств к элементу “a” с заданным id=”zbang” */
  5. // создаем массив свойств-расширений
  6. var extension = { foo: function() { alert(this.anarray); },
  7.  anarray: ['one', 'two', 'three', 'start your engines']};
  8. // выполняем апгред тега “a”
  9. $.extend($("#zbang").get(0), extension);
  10. // и вот пример вызова функции foo от имени тега “a”, и, внимание, 
  11. функция может оперировать массивом свойств “anarray”.
  12. $("#zbang").get(0).foo();
Для работы с массивами также удобно использовать функцию “$.grep”. Она служит для того, чтобы выбросить из массива ненужные элементы – это первый параметр функции. В качестве условия отбора используется пользовательская функция – второй параметр, и третий параметр позволяет инвертировать поведение функции.
  1. var elts = $.grep( $('li'), function(n,i){
  2.    return n.id != "item_2";
  3.    //функция должна вернуть true для тех элементов, 
  4.    //которые надо оставить
  5.   }
  6.  );
  7.  // результатом работы будут те теги li, значение идентификатора 
  8.  // которых не равно “item_2”
  9. var elts = $.grep( $('li'), function(n,i){
  10.    return n.id != "item_2";
  11.   },
  12.   true
  13. );
  14. // а здесь третий параметр равный true говорит, 
  15. // что наоборот нужно оставить только те элементы идентификатор которых 
  16. // равен “item_2”
В случае если вам необходимо выполнить модификацию массива так, чтобы каждый его элемент был заменен на нечто иное, то используйте функцию “$.map( array, fn )”. Первый параметр задает массив, а второй – пользовательскую функцию. Например, следующий код инвертирует знак чисел элементов массива.
  1. var elts = $.map( [1,2,-3] , function(n,i){
  2.     return -n; 
  3.    }
  4.   );
Задача слияния элементов массивов и удаления при этом дублей встречается не слишком часто, но и для нее в составе jquery найдется специальная функция.
  1. $.merge( [0,1,2], [2,3,4] ) //и результатом работы будет [0,1,2,3,4]
Последняя функция общего назначения выполняет удаление из пробелов по обоим краям строки:
  1. $.trim("  hello, how are you?  ");// и результатом работы будет "hello, how are you?"
В случае если вам необходимо реализовать различное поведение скрипта в зависимости от типа браузера, то можно использовать специальные булевы переменные.
 <strong>1. $.browser.msie</strong> – признак того, что используемый браузер – internet explorer.
 <strong>2. $.browser.safari</strong> – используется safari – самый популярный под mac браузер.
 <strong>3. $.browser.opera</strong> – используется opera.
 <strong>4. $.browser.mozilla</strong> – и, наконец, браузеры mozilla, firefox, seamonkey 
 или что-то еще построенное на базе движка gecko.

События



Теперь мы разберем обработку событий и научимся “привязывать” специальные функции обработчики, которые вызываются всякий раз, когда пользователь взаимодействует с некоторым элементом веб-страницы. Для того чтобы, например, для каждого элемента списка “li” привязать специальный обработчик события “click” который бы выделял активный элемент, скажем, красным цветом используйте следующий код:
  1. $("li").bind("click", function(){
  2.    this.style.color = 'red';
  3. });// ссылка на элемент, для которого произошло данное событие доступен через переменную this
Здесь я использовал только два параметра для функции “bind”: имя события и функцию обработчик. Также возможно было указать необязательный параметр – “пользовательские данные”, которые будут переданы функции обработчику. Это полезно если вы не передаете внутрь bind анонимную функцию, а используете один централизованный обработчик событий для нескольких элементов. Тогда основываясь на переданных пользовательских данных, функция будет знать, что ей следует делать в каждой конкретной ситуации.

Функция “bind” является родовой и может управлять всеми событиями, однако существует ряд специализированных функций (работающих не со всеми событиями, а только с одним определенным событием), но предоставляющих также возможность активации событий для целого множества отобранных узлов документа. В коде ниже я создаю обработчик события click для всех элементов списка, также создаю один обработчик события задан в старом стиле
  1. <em onclick="функция()">
и вызываю все эти функции-обработчики одной строкой кода:
  1. // это фрагмент html
  2. <em onclick="this.style.color = 'green';">EM block</em>
  3. // а это фрагмент javascript
  4. $("li").click ( function(){
  5.    this.style.color = 'red';
  6. });// и делаю вызов
  7. $("li").add("em").click();
Вуаля, и элементы списка “li” приняли красный цвет шрифта, а содержимое тега “em” стало зеленым. Естественно, что “click” не единственная специализированная функция, кроме нее есть также:

1. “blur” – служит для привязки/активации события потери фокуса для некоторого элемента. Это очень часто используемое событие при построении форм, например, по событию blur вы можете проверять, что введенные пользователем данные в текстовые поля корректны и сообщить ему, если это не так.

2. “focus” – событие вызываемое при получении некоторым элементом фокуса ввода. Например, если вы делает текстовое поле в форме и значение поля играет роль подсказки, то при получении фокуса неплохо было бы очистить поле и подготовить его для пользовательского ввода.

3. “change” – вызывается при изменении содержимого элемента. Также очень полезное событие при построении форм.

4. “dblclick” – как вы уже догадались обработка события двойного клика.

5. “hover” – этой функции следует передать в качестве параметра две функции обработчика. Одна из которых вызывается в случае если мышь наведена на целевой элемент. Вторая же вызывается, когда мышь с целевого элемента уходит. Эта функция не является простой калькой с привычных уже вам onmouseover и onmouseout т.к. более “интеллектуальна”, например, событие onmouseout часто генерируется в случаях, когда мышь уходит с самого элемента на другой элемент, но (и это важно) находящийся внутри целевого элемента. Например, если вы делаете выпадающее меню сайта, то поставив обработчик события onmouseover для пункта меню высшего уровня вам придется быть очень острожным чтобы не получить сообщение onmouseout когда пользователь водит мышь по подпунктам меню. Хотя это и не страшная проблема, но все же довольно неприятно. Например, попробуйте следующий код:
  1. <b id="log">here is log</b>
  2. <br /> выше размещен специальный элемент играющий роль журнала событий
  3.  
  4. <div style="border: 2px solid black; background-color: green; padding: 20px;" id="block"
  5.  onmouseover="makeOver(this)" onmouseout="makeOut(this)">
  6.     <div style="border: 2px solid black; background-color: red; padding: 20px;" 
  7.        id="block2">
  8.          Hello User <br />
  9.          Bye, Bye
  10.     </div>
  11. </div>
  12.  
  13. var log = 0;// переменная счетчик событий 
  14. function makeOver (t){
  15.  $('#log').html ('over: '+ (log++));
  16. }
  17.  
  18. function makeOut (t){
  19.  $('#log').html ('out' + (log++)) ;
  20. }
В коде выше если бы я не ввел бы дополнительную переменную счетчик, то я бы не смог бы заметить момент, возникающий, когда мышь пересекает границу вложенного блока div - уж слишком быстро происходит чередование событий “out,over”. Так при переходе через границу номер переменной log увеличивается на 2, что говорит о двойной генерации события. А что будет, если мы воспользуемся встроенным в jquery методом hover?
  1. $('#block').hover (makeOver, makeOut);
Если вы запустите пример, то увеличения счетчика при перемещении мыши через границу вложенного

элемента не будет происходить – что и требовалось получить.

6. “keydown” – событие срабатывает, когда происходит нажатие на какую-либо клавишу для текущего элемента и также есть парные методы “keypress”, “keyup”.

7. “load” - функция срабатывает всякий раз когда загрузка целевого элемента была успешно завершена. Традиционно это применяется для обработки загрузки изображений. Например, пока некоторая достаточно крупная картинка грузится, вы желаете на ее месте выводить специальный логотип “мол, подождите, идет загрузка” и только с помощью события “load” вы можете отследить момент, когда все изображение было полностью загружено и вам следует спрятать тот самый символ “ожидания”.

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

Оставшиеся события я не привожу, их не так уж и много и они частично дублируются приведенными выше событиями. Гораздо интереснее рассмотреть еще одну особую универсальную функцию привязки обработчика событий к элементу. Функция “one” очень похожа на “bind” (даже входные параметры получает один к одному), но ее отличие в том, что целевое событие срабатывает только один раз, после чего jquery удалит отработавшую функцию из списка подписчиков события. Чем-то похожа на “one” и функция “toggle”. Ее назначение – чередовать две функции обработчика событий вызываемых по очереди при клике на некоторых элементах страницы.

Функцию “ready” мы уже рассматривали в прошлый раз, но напомню что ее назначение – отследить момент, когда дерево DOM будет полностью загружено и готово для использования. Вспомните сами, в чем ее ключевое отличие от привычного нам “window.onload”.
  1. $(document).ready(function(){ что-то делаем });
Последняя функция, которую мы рассмотрим, – это функция “unbind”. Ее назначение удалить обработчики событий с некоторого элемента. Можно указать при вызове функции необязательный параметр – имя события, для которого больше обработчики не будут вызываться. Если вы не укажите имя события, то отменены они будут отменены абсолютно все.

Внутри функции обработчика события вы можете управлять тем будет ли выполнено действие элемента управления по-умолчанию (для отмены используйте метод preventDefault), также вы можете отменить “bubbling” с помощью вызова stopPropagation. Эти функции следует вызывать от объекта event передаваемого как параметр функции обработчика события – словом, все как в классическом javascript.

В этой статье я ранее планировал рассказать и о том, как реализована поддержка ajax в библиотеке jquery. Но решил, что такая интересная тема требует отдельного материала и рассмотрения механизмов асинхронных вызовов не только со стороны клиента (javascript) но и на стороне сервера. Я расскажу об используемых мною в работе framework-ах для интеграции javascript-ajax-php.