Источник: http://learn.javascript.ru/introduction-browser-events
Для реакции на действия посетителя и внутреннего взаимодействия скриптов существуют события.
Событие – это сигнал от браузера о том, что что-то произошло.
Существует много видов событий.
click
происходит, когда кликнули на элемент;mouseover
— когда на элемент наводится мышь;focus
— когда посетитель фокусируется на элементе;keydown
— когда посетитель нажимает клавишу.resize
— когда изменяется размер окна.load
, readystatechange
, DOMContentLoaded
…События соединяют JavaScript-код с документом и посетителем, позволяя создавать динамические интерфейсы.
Есть несколько способов назначить событию обработчик. Сейчас мы их рассмотрим, начиная от самого простого.
Обработчик может быть назначен прямо в разметке, в атрибуте, который называется on<событие>
.
Например, чтобы прикрепить click-событие к input-кнопке, можно присвоить обработчик onclick
, вот так:
<input id="b1" value="Нажми меня" onclick="alert('Спасибо!')" type="button"/>
При клике мышкой на кнопке выполнится код, указанный в атрибуте onclick
.
В действии:
Обратите внимание, внутри alert используются одиночные кавычки, так как сам атрибут находится в двойных.
Запись вида onclick="alert("Клик")"
не будет работать. Если вам действительно нужно использовать именно двойные кавычки, то это можно сделать, заменив их на &quot;
: onclick="alert(&quot;Клик&quot;)"
.
Однако, обычно этого не требуется, так как в разметке пишутся только очень простые обработчики. Если нужно сделать что-то сложное, то имеет смысл описать это в функции, и в обработчике вызвать её.
Следующий пример по клику запускает функцию countRabbits()
.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script>
function countRabbits() {
for(var i = 1; i <= 3; i++) {
alert("Кролик номер " + i);
}
}
</script>
</head>
<body>
<input type="button" onclick="countRabbits()" value="Считать кроликов!"/>
</body>
</html>
Как мы помним, атрибут HTML-тега не чувствителен к регистру, поэтому ONCLICK
будет работать так же, как onClick
или onclick
… Но, как правило, атрибуты пишут в нижнем регистре: onclick
.
Можно назначать обработчик, используя свойство DOM-элемента on<событие>.
Пример установки обработчика click
элементу с id="myElement"
:
<input id="myElement" type="button" value="Нажми меня"/>
<script>
var elem = document.getElementById('myElement');
elem.onclick = function() {
alert('Спасибо');
}
</script>
В действии:
Если обработчик задан через атрибут, то соответствующее свойство появится у элемента автоматически. Браузер читает HTML-разметку, создаёт новую функцию из содержимого атрибута и записывает в свойство onclick
.
Первичным является именно свойство, а атрибут — лишь способ его инициализации.
Эти два примера кода работают одинаково:
<input type="button" onclick="alert('Клик!')" value="Кнопка"/>
<input type="button" id="button" value="Кнопка"/>
<script>
document.getElementById('button').onclick = function() {
alert('Клик!');
}
</script>
Так как свойство, в итоге, одно, то назначить по обработчику и там и там нельзя.
В примере ниже, назначение через JavaScript перезапишет обработчик из атрибута:
<input type="button" onclick="alert('До')" value="Нажми меня"/>
<script>
var elem = document.getElementsByTagName('input')[0];
elem.onclick = function() { // перезапишет существующий обработчик
alert('После');
}
</script>
Обработчиком можно назначить уже существующую функцию:
function sayThanks() {
alert('Спасибо!');
}
document.getElementById('button').onclick = sayThanks;
sayThanks
, а не sayThanks()
:document.getElementById('button').onclick = sayThanks;
Если добавить скобки, то sayThanks()
— будет уже результат выполнения функции (а так как в ней нет return
, то в onclick
попадёт undefined). Нам же нужна именно функция.
А вот в разметке как раз скобки нужны:
<input type="button" id="button" onclick="sayThanks()"/>
Это различие просто объяснить. При создании обработчика браузером по разметке, он автоматически создает функцию из его содержимого. Поэтому последний пример – фактически то же самое, что:
document.getElementById('button').onclick = function() {
sayThanks(); // содержимое атрибута
}
elem.setAttribute('onclick', func)
.Хотя, с другой стороны, если func
— строка, то такое присвоение будет успешным, например:
// сработает, будет при клике выдавать 1
document.body.setAttribute('onclick', 'alert(1)');
Браузер в этом случае сделает функцию-обработчик с телом из строки alert(1)
.
…А вот если func
— не строка, а функция (как и должно быть), то работать совсем не будет:
// при нажатии на body будут ошибки
document.body.setAttribute('onclick', function() { alert(1) });
Значением атрибута может быть только строка. Любое другое значение преобразуется в строку. Функция в строчном виде обычно даёт свой код: "function() { alert(1) }"
.
Запись elem.onclick = 'alert(1)'
будет работать, но не рекомендуется.
При использовании в такой функции-строке переменных из замыкания будут проблемы с JavaScript-минификаторами. Здесь мы не будем вдаваться в детали этих проблем, но общий принцип такой — функция должна быть function
.
on<событие>
должно быть написано в нижнем регистре.Свойство ONCLICK
работать не будет.
Внутри обработчика события this
ссылается на текущий элемент. Это можно использовать, чтобы получить свойства или изменить элемент.
В коде ниже button
выводит свое содержимое, используя this.innerHTML
:
<button onclick="alert(this.innerHTML)">Нажми меня</button>
В действии:
Фундаментальный недостаток описанных способов назначения обработчика — невозможность повесить несколько обработчиков на одно событие.
Например, одна часть кода хочет при клике на кнопку делать ее подсвеченной, а другая — выдавать сообщение. Нужно в разных местах два обработчика повесить.
При этом новый обработчик будет затирать предыдущий. Например, следующий код на самом деле назначает один обработчик — последний:
input.onclick = function() { alert(1); }
// ...
input.onclick = function() { alert(2); } // заменит предыдущий обработчик
Конечно, это можно обойти разными способами, в том числе написанием фреймворка вокруг обработчиков. Но существует и другой метод назначения обработчиков, который свободен от указанного недостатка.
Для назначения обработчиков существуют специальные методы. Как правило, в браузерах они стандартные, кроме IE<9, где они похожи, но немного другие.
Сначала посмотрим метод для старых IE, т.к. оно чуть проще.
Назначение обработчика осуществляется вызовом attachEvent
:
element.attachEvent( "on"+event, handler);
Удаление обработчика — вызовом detachEvent
:
element.detachEvent( "on"+event, handler);
Например:
var input = document.getElementById('button')
function handler() {
alert('спасибо!')
}
input.attachEvent( "onclick" , handler) // Назначение обработчика
// ....
input.detachEvent( "onclick", handler) // Удаление обработчика
Обычно, обработчики ставятся. Но бывают ситуации, когда их нужно удалять или менять.
В этом случае нужно передать в метод удаления именно функцию-обработчик. Такой вызов будет неправильным:
input.attachEvent( "onclick" ,
function() {alert('Спасибо!')}
)
// ....
input.detachEvent( "onclick",
function() {alert('Спасибо!')}
)
Несмотря на то, что функции работают одинаково, это две разных функции.
Использование attachEvent
позволяет добавлять несколько обработчиков на одно событие одного элемента.
Пример ниже будет работать только в IE и Opera:
<input id="myElement" type="button" value="Нажми меня"/>
<script>
var myElement = document.getElementById("myElement")
var handler = function() {
alert('Спасибо!')
}
var handler2 = function() {
alert('Спасибо еще раз!')
}
myElement.attachEvent("onclick", handler); // первый
myElement.attachEvent("onclick", handler2); // второй
</script>
У обработчиков, назначенных с attachEvent
, нет this
.
Это важная особенность и подводный камень старых IE.
Официальный способ назначения обработчиков из стандарта W3C работает во всех современных браузерах, включая IE9+.
Назначение обработчика:
element.addEventListener(event, handler, phase);
Удаление:
element.removeEventListener(event, handler, phase);
Как видите, похоже на attachEvent/detachEvent
, только название события пишется без префикса «on».
Еще одно отличие от синтаксиса Microsoft — это третий параметр: phase
, который обычно не используется и выставлен в false. Позже мы посмотрим, что он означает.
Использование этого метода — такое же, как и у attachEvent
:
function handler() { ... }
elem.addEventListener( "click" , handler, false) // назначение обработчика
elem.removeEventListener( "click", handler, false) // удаление обработчика
Можно объединить способы для IE<9 и современных браузеров, создав свои методы addEvent(elem, type, handler)
и removeEvent(elem, type, handler)
:
var addEvent, removeEvent;
if (document.addEventListener) { // проверка существования метода
addEvent = function(elem, type, handler) {
elem.addEventListener(type, handler, false);
};
removeEvent = function(elem, type, handler) {
elem.removeEventListener(type, handler, false);
};
} else {
addEvent = function(elem, type, handler) {
elem.attachEvent("on" + type, handler);
};
removeEvent = function(elem, type, handler) {
elem.detachEvent("on" + type, handler);
};
}
...
// использование:
addEvent(elem, "click", function() { alert("Привет"); });
Это хорошо работает в большинстве случаев, но у обработчика не будет this
в IE, потому что attachEvent
не поддерживает this
.
Кроме того, в IE<8 есть проблемы с утечками памяти… Но если вам не нужно this
, и вы не боитесь утечек (как вариант — не поддерживаете IE<8), то это решение может подойти.
Есть три способа назначения обработчиков событий:
onclick="..."
.elem.onclick = function
.elem.attachEvent(on+событие, handler)
(удаление через detachEvent
).elem.addEventListener(событие, handler, false)
(удаление через removeEventListener
).Все способы, кроме attachEvent
, обеспечивают доступ к элементу, на котором сработал обработчик, через this
.