On

.on()

Устанавливает обработчики событий на выбранные элементы страницы. Имеет два варианта использования:

.on(events, [selector], [data], handler):jQuery1.7

events — тип(ы) обрабатываемых событий. Например "click", "resize" и.т.д. (список всех событий см. ниже). Если необходимо привязать обработчик сразу на несколько типов событий, нужно перечислить их через пробел: "click resize ..."
selectorселектор по которому будут фильтроваться элементы, лежащие внутри уже найденных. В итоге, обработчик будет срабатывать только в том случае, если событие «поднялось» от одного из отфильтрованных элементов.
data — данные, передаваемые обработчику событий. В обработчике будут доступны в переменной event.data.
handler — функция, которая будет установлена в качестве обработчика. Вместо функции, можно указать значение false, это будет эквивалентно установке такой функции: function(){return false;}.

.on(events-map, [selector], [data]):jQuery1.7

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

events-map — объект, в котором нужно перечислить типы обрабатываемых событий и соответствующие им обработчики. Задается в формате {events-1:handler-1, events-2:handler-2, ...}, где events-i и handler-i соответствуют параметрам events и handler в первом варианте метода (описанном выше).
selector — см. выше.
data — см. выше..

Простой пример:

// Установим обработчик нажатия кнопкой мыши, элементу с идентификатором foo
$('#foo').on('click', function(){
  alert('Вы нажали на элемент "foo"');
});
 
// Теперь, при нажатии на элемент foo, будет выведено сообщение

Метод on() введен в jQuery-1.7, чтобы объединить три метода библиотеки, устанавливающие обработчики событий на элементы страницы: .bind(), .delegate(), .live(). Сами эти методы считаются теперь устаревшими, хотя еще поддерживаться. Установить обработчик, срабатывающий только один раз, по прежнему можно только с помощью отдельного метода .one().

Убрать установленный обработчик можно с помощью метода off().

Содержание

Типы обрабатываемых событий (параметр events)

В качестве первого параметра метода (тип события) может быть использовано любое строковое значение. Если необходимо обрабатывать одно из стандартных javascript-событий, то следует использовать следующие значения: blur, focus, focusin, focusout, load, resize, scroll, unload, click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, mouseenter, mouseleave, change, select, submit, keydown, keypress, keyup, error. Каждый из этих типов имеет одноименный метод, являющейся краткой формой метода on() . К примеру, использование click(handler) равнозначно on("click", handler).


Если тип события не совпадет ни с одним из значений представленных выше, то будет воспринято jQuery как пользовательское событие. Такие события организуют сами пользователи (в смысле, пользователи библиотеки — программисты), генерируя их (события) с помощью методов trigger() и triggerHandler().


Пространство имен

Тип событий может быть задан с указанием пространства имен, например: on('click.name', handler). Здесь name является пространством имен, а click — типом события. Пространство имен позволяет разделить обработчики одних и тех же событий на подгруппы, которые, в последствии, будет легко отдельно вызывать (методом trigger()) и удалять (методом off()).

// установим на все div'ы обработчик события click
$('div').on('click', someHandler);
 
// а затем другой обработчик события click,
// но уже с указанным пространством имен - box1
$('div').on('click.box1', someHandler2);
 
// вызовем все обработчики события click
$('div').trigger('click'); 
// - в результате, на div'ах будут вызваны и someHandler
// и someHandler2
 
// вызвать обработчики только с пространством имен box1
// можно так:
$('div').trigger('click.box1');
 
// С удалением то же самое. Удалим обработчики
// с пространством имен box1:
$('div').off('click.box1');

Можно указывать сразу несколько пространств имен: on('click.name1.name2', handler). Чтобы обратиться к такому событию, достаточно указать любое из них (или не указывать вовсе). Таким образом, пространства имен похожи по своей сути на классы в css.

Прямая и делегированная обработка (параметр selector)

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


Однако, прямая обработка событий имеет свои недостатки. Допустим на странице есть список (ul), на элементах которого необходимо обрабатывать событие click. Если после установки соответствующих обработчиков, в список будут добавлены новые элементы, то они уже не будут реагировать на «клики», поскольку непосредственно к новым элементам, обработчики прикреплены не были. Эта особенность может породить много проблем и коварных логических ошибок.


Для таких случаев jQuery имеет возможность организовывать так называемую делегированную обработку событий. В этом случае, jQuery вместо заданного обработчика (в параметре handler) устанавливает собственный специальный обработчик. Причем он не устанавливается непосредственно на элементы, на которых необходимо обрабатывать события (назовем их inner-элементами), а на содержащие их элементы (назовем их outer-элементами. Получается, что inner находятся внутри outer). После того, как событие происходит на одном из элементов inner, оно начинает подниматься вверх по иерархии DOM. В какой то момент оно оказывается на одном из элементов outer, на котором срабатывает специально установленный обработчик, который проверяет, поднялось ли событие от одного из элементов inner. Если это так — запускается обработчик handler. Image:DelegateOn.gif

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

Чтобы организовать делегированную обработку, необходимо во втором параметре метода on задать селектор. В этом случае роль outer-элементов будут играть элементы к которым был применен метод, а роль элементов inner — элементы, находящиеся внутри outer и удовлетворяющие заданному селектору. То есть, если необходимо делегировано обрабатывать событие click на элементах списка, то следует делать это так:

$('ul').on('click', 'li', function(){ // обработчик ....

Прямые обработчики в этой же ситуации устанавливались бы так:

$('ul li').on('click', function(){ // обработчик ....


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

Небольшой пример. Так можно организовать делегированную обработку нажатия по элементам списка:

~lt~!DOCTYPE html~gt~
~lt~html~gt~
~lt~head~gt~
  ~lt~style~gt~
  ~lt~/style~gt~
  ~lt~script src="https://code.jquery.com/jquery-latest.min.js"~gt~~lt~/script~gt~
~lt~/head~gt~
~lt~body~gt~
  (нажмите на один из элементов из списка)
  ~lt~ul~gt~
    ~lt~li~gt~Быстрее~lt~/li~gt~
    ~lt~li~gt~Выше~lt~/li~gt~
    ~lt~li~gt~Сильнее~lt~/li~gt~
  ~lt~/ul~gt~
  ~lt~script~gt~
    $('ul').on('click', "li", function(){
      var txt = $(this).text(); // вытащим текст из нажатого элемента
      alert('Вы нажали на элемент с текстом - «' + txt + '»'); // выведем сообщение с текстом
    });
  ~lt~/script~gt~
~lt~/body~gt~
~lt~/html~gt~

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


По спецификации W3C, события focus и blur не имеют особенности подниматься вверх по иерархии DOM, однако в jQuery организованы их альтернативы — focusin и focusout, которые умеют «всплывать». Поэтому, если вы попытаетесь установить делегированные обработчики focus или blur, то jQuery по факту будет отслеживать focusin и focusout. Чтобы избежать возможных недоразумений, делегированную обработку приобретения и потери фокуса лучше осуществлять обработкой focusin и focusout.


Во всех браузерах, событие load, не всплывает вверх по иерархии. В IE 8 и младше, события paste и reset так же не обладают этим свойством. В связи с этим, не следует обрабатывать эти события делегированно. Привязывать обработчики этих событий следует непосредственно на элементы, на которых они происходят (без делегирования).

Обработчики событий (параметр handler)

В качестве параметра handler должна быть указана функция (или значение false, но об этом позже). Это может быть анонимная функция:

$('#foo').on('click', function(){
  alert('Вы нажали на элемент "foo"');
});

или именем заданной функции:

function myFunction(){
  alert('Вы нажали на элемент "foo"');
});
 
$('#foo').on('click', myFunction);

Когда происходит событие, jQuery передает в вызываемый обработчик объект event, в котором содержится вся вспомогательная информация по происходящему событию. Этот объект немного отличается от стандартных объектов событий, которые предоставляют браузеры (их получают обработчики, установленные обычными средствами javascript). Библиотека jQuery, изменяет некоторые поля для обеспечения кроссбраузерности. Так или иначе, всегда можно добраться и до нативного объекта события (тот, который предоставляет браузер), он лежит в поле event.originalEvent.


После возникновения, событие всегда всплывает по иерархии DOM вплоть до объекта document. Если при обработке события необходимо остановить всплытие, нужно вызвать метод event.stopPropagation() внутри обработчика. В результате, данное событие прекратит далнейшее всплытие по дереву DOM. Однако, если на текущем элементе были установлены другие невыполненные обработчики, они будут выполнены. Чтобы этого избежать нужно вместо stopPropagation() вызвать event.stopImmediatePropagation().

Многие события порождают выполнение связанных с ними действий. Например, сразу после того, как на ссылке происходит событие клик, происходит переход на другую страницу. Чтобы отменить выполнение этих действий, можно внутри обработчика события вызвать event.preventDefault(). Конечно, не все события порождают дополнительные действия, а так же, не все действия могут быть предотвращены (см. W3C спецификацию).

Если вместо функции в параметре handler задать false, то event.stopPropagation() и event.preventDefault() будут вызваны автоматически. Вообще, задание false идентично установке такого обработчика:

function(){ return false; }

То есть, например после кода:

$("a.disabled").on("click", false);

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


Переменная this внутри обработчика всегда содержит DOM-элемент, на котором отлавливается событие. При прямой обработке события это будет один из выбранных элементов, а при делегированной — элемент, лежащий внутри одного из выбранных элементов и удовлетворяющий параметру selector. Создать объект jQuery по this элементу можно так $(this).

Передача дополнительных данных в обработчик (параметр data)

Если параметр data задан, то он будет доступен в обработчике в even.data. В этот параметр можно передавать данные любого типа, однако, нужно быть осторожным при передаче строк — если при этом параметр selector не будет задан, то библиотека воспримет заданную строку в качестве этого параметра. Чтобы этого избежать нужно задать в параметре selector null. Удобнее всего задавать параметр data с помощью объектов. В этом случае, в обработчик можно передать сразу много данных.

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

function greet(event){
  alert("Hello "+event.data.name);
}
// установим на кнопки один обработчики с 2-мя разными данными
$("button").on("click", { name: "Karl" }, greet);
$("button").on("click", { name: "Addy" }, greet);

Помимо параметра data есть и другой способ передать параметры в обработчик — при ручном вызове событий с помощью методов .trigger() или .triggerHandler(), можно передавать дополнительные данные в их параметрах.

Дополнительные замечания

Большинство событий, как например нажатие мышью по элементу (click) происходят относительно редко и проблем с их обработкой практически не возникает. Однако такие события как mousemove и scroll могут происходить несколько раз в секунду. В этом случае, частое выполнение обработчиков может потребовать значительных вычислительных ресурсов компьютера и приводить к зависанию. Избежать этого можно, если грамотно организовать выполнение обработчиков. Например, стоит кэшировать вычисляемые величины, вместо того, чтобы считать их каждый раз заново. И в первую очередь следует кэшировать объекты jQuery, поскольку их создание зачастую требует весомых вычислительных затрат. Например, рассмотрим случай, когда нужно в элементе с идентификатором elm прописывать координаты мыши, когда она движется над элементом с классом mousemove-elm:

// неэффективное выполнение задачи - элемент с id=elm
// ищется заново при каждом выполнении обработчика
$('.mousemove-elm').on('mousemove', function(event){
  $("#elm").text(event.pageX + ", " + event.pageY);
});
 
 
// эффективное выполнение задачи - элемент с id=elm
// ищется только один раз
var elm = $("#elm");
$('.mousemove-elm').on('mousemove', function(event){
  elm.text(event.pageX + ", " + event.pageY);
});

Если обработчики часто выполняемых событий не удается оптимизировать в достаточной степени, то следует сократить количество их вызовов, используя setTimeout:

// ограничим выполнение обработчика не более чем двумя вызовами в секунду
var elm = $("#elm");
var canExe = true;
$('.mousemove-elm').on('mousemove', function(event){
  if(canExe == true){
    elm.text(event.pageX + ", " + event.pageY);
    canExe = false;
    setTimeout(function(){canExe = true}, 500); 
    //вызывать следующий обработчик можно будет только через 500 мл.секунд
  }
});


Чтобы привязывать события к элементам страницы, jQuery требуется возможность устанавливать на них данные, в качестве свойств. Элементы object, embed и applet не имеют таких особенностей, поэтому установленные на них обработчики работать не будут.


Событие error объекта window, обладает нестандартными входными параметрами и возвращаемым значением, поэтому jQuery не поддерживает его обработку. Установить обработчик на это событие можно только стандартным методом — присвоив функцию величине window.onerror.

Примеры

Выведем на экран сообщение с текстом параграфа, по которому был совершен клик мышью:

$("p").on("click", function(){
  alert( $(this).text() );
});


Передадим данные в обработчик, который задан отдельной функцией:

function myHandler(event) {
  alert(event.data.foo);
}
$("p").on("click", {foo: "bar"}, myHandler);


Предотвратим отправку данных формы, а так же дальнейшее всплытие события, если переменная flag не равна true:

$("form").on("submit", function(){
  if(flag != true)
    return false;
});

Предотвратим только отправку данных формы, если переменная flag не равна true. Таким образом несмотря на то, что данные не будут отправлены, а страница перезагружена, событие submit продолжит свой путь к вершине иерархии DOM - объекту document:

$("form").on("submit", function(){
  if(flag != true)
    event.preventDefault();
});

В примере показано, как можно осуществить обработку событий наведения, нажатия и двойного нажатия на элемент:

~lt~!DOCTYPE html~gt~
~lt~html~gt~
~lt~head~gt~
  ~lt~style~gt~
    p{background:yellow; font-weight:bold; cursor:pointer; padding:5px;}
    p.over{background: #ccc;}
    span{color:red;}
  ~lt~/style~gt~
  ~lt~script src="https://code.jquery.com/jquery-latest.min.js"~gt~~lt~/script~gt~
~lt~/head~gt~
~lt~body~gt~
  ~lt~p~gt~"Кликать" сюда.~lt~/p~gt~
  ~lt~span~gt~~lt~/span~gt~
  ~lt~script~gt~
    // при нажатии по элементу ~lt~p~gt~ выведем текст, содержащий координаты клика
    $("p").on("click", function(event){
      var str = "( " + event.pageX + ", " + event.pageY + " )";
      $("span").text("Клик, это звучит гордо! Особенно в координатах " + str);
    });

    // при двойном нажатии по элементу ~lt~p~gt~ выведем название тега нажатого элемента
    $("p").on("dblclick", function(){
      $("span").text("Клик - хорошо, а двойной лучше! Нажат элемент " + this.nodeName);
    });

    // при наведении и отведении курсора от элемента ~lt~p~gt~ будем "переключать" наличие класса over
    $("p").on("mouseenter mouseleave", function(){
      $(this).toggleClass("over");
    });

~lt~/script~gt~
~lt~/body~gt~
~lt~/html~gt~

Ссылки

Поисковые ключи:
  • обработчик событий
  • установка обработчика событий
  • обработка событий
  • .on()
  • on()