Способы добавления обработчиков в сравнении
В статье приводится сравнение способов добавления обработчиков через on(click), addEventListener и attachEvent.
Рассмотрены особенности, плюсы и минусы разных способов.
Считается, что вы знакомы с основными свойствами объекта события и порядком их обработки.
Самый универсальный метод. У него есть, пожалуй, только один существенный недостаток: можно повесить лишь один обработчик.
Кроме того, есть глюк с iframe: в Firefox не будет работать обработчик, назначенный так:
iframeElement.onclick = function(event) { .... }
Но будет прекрасно работать addEventListener:
iframeElement.addEventListener( "click", ... )
- Полная кросс-браузерность
- Только один обработчик на событие
- Глюк с iframe в браузерах от Mozilla
Методы attachEvent/addEventListener имеют ряд общих недостатков.
Во-первых, ни W3C ни Microsoft не определяют порядок срабатывания обработчиков. Несколько обработчиков одного события на элементе могут сработать в любом порядке.
На текущий момент (март 2010) addEventListener сохраняет порядок назначения обработчиков, а attachEvent в IE - нет.
elem.attachEvent("onclick", handler);
elem.attachEvent("onclick", handler2);
// может быть так, что handler2 сработает раньше handler.
// так, судя по демке ниже, ведет себя IE.
// а может быть, handler сработает раньше handler2.
// так в демо ниже ведет себя Opera
// ... вообще, порядок неопределен
На одну и ту же кнопку вешаются пять обработчиков onclick: первый выдает "1", второй выдает "2" и т.п.
Есть еще одна проблема, с которой можно столкнуться при управлении событиями: нельзя точно сказать установлен ли определённый обработчик, или нет, и какие обработчики установлены на данный момент.
В спецификации DOM 3 существует объект eventListenerList, но он слишком новый и на данный момент не поддерживается ни одним из браузеров.
Обработчик ставится как:
element.attachEvent( "on"+имя_события, обработчик)
Основной недостаток attachEvent заключается в том, что функция-обработчик не получает текущий элемент, на котором сработало событие, ни в каком виде.
Значение this указывает на window, а свойство event.currentTarget отсутствует.
И это достаточно важная особенность!
Например, пусть мы хотим подсвечивать divElem при клике.
Элемент divElem с разным текстом и различными ссылками, и даже с жирным текстом внутри тага <b>
Конечно, же мы повесим обработчик на divElem:
divElem.attachEvent("onclick", handler)
Но при клике из объекта события event в IE можно получить только srcElement, то есть самый глубокий кликнутый элемент. Он может быть ссылкой <a> или элементом <b>, но нам-то нужны не они, а сам divElem, чтобы его подсветить.
Обработчик, добавленный при помощи attachEvent никак не может выяснить объект, на который подвешен.
Впрочем, это легко обойти при помощи небольшого замыкания, корректно передающего указатель this:
divElem.attachEvent("onclick", function() { handler.call(divElem) })
Но этот код порождает утечку памяти в Internet Explorer 6, на который не установлено исправляющее обновление, вышедшее в июне 2007 года, из-за круговой ссылки DOM <-> JS.
- Можно повесить несколько обработчиков на одно событие
- Не передается текущий элемент.
- Поддерживается только IE/Opera.
Решение W3C работает во всех современных браузерах, кроме Internet Explorer.
Установка обработчика:
element.addEventListener( имя_события, обработчик, фаза)
Пожалуй, особенных недостатков, кроме общих с attachEvent, у этого способа нет.
- Можно повесить несколько обработчиков на одно событие
- Умеет вешать обработчики на фазу погружения события
- Стандарт W3C
- Не поддерживается IE.
В следующей статье разобран способ, позволяющий добавлять обработчики кроссбраузерно и свободный от описанных недостатков.
|
Автор: airfly (не зарегистрирован), дата: 1 мая, 2010 - 18:19
#permalinkВот я только одного не могу понять:
какой толк в attachEvent и addEventListener, если все всеравно все они выполняются по одному событию? Ну напишу я 10 раз attachEvent, перечислив 10 разных функций. И что? Где это пригодится на практике? Проще объявить onclick, адрес функции и в ней уже перечислить список тех 10 функций. Разве так не проще?
На практике одно и тоже. Я не вижу ВООБЩЕ никаких плючов у attachEvent, за исключением примера с iframe
Автор: THERE (не зарегистрирован), дата: 3 июня, 2010 - 09:13
#permalinkу меня по кнопке addEventListener выдаются алерты с 1 по 7 на FF.
это чей-то глюк или опечатка?
Автор: Гость (не зарегистрирован), дата: 5 сентября, 2010 - 11:05
#permalinkИсходники страниц браузер делает сам, поэтому не верь ему.
Если на сайте при входе будет работать сценарий, который выводит "Привет (Ваш ник(Допустим proVIDec))!", то при просмотре исходного кода браузер даст не php скрипт, а текст "Превет proVIDec".
Автор: Гость (не зарегистрирован), дата: 9 января, 2011 - 01:02
#permalinkВпрочем, это легко обойти при помощи небольшого замыкания, корректно передающего указатель this:
divElem.attachEvent("onclick", function() { handler.call(divElem) })А вот так
divElem.attachEvent("onclick", handler.call(divElem) )передать получится?
Автор: Гость (не зарегистрирован), дата: 24 января, 2011 - 12:53
#permalinkТретий параметр в addEventListener совсем не понятен
Что за "фаза погружения"?
Автор: demoniqus, дата: 16 мая, 2012 - 18:17
#permalinkНемного дополню данный пост. Зачастую в функцию требуется передать некоторые параметры. Как вариант, можно расплодить глобальные переменные или создать один глобальный массив, из которого и вытягивать нужные значения. А можно решить данный вопрос и другим способом. Приведу свою функцию:
function select_country(country_id, step) { request = 'some request'; rslt = GenerateXMLHttpRequest(request, 'php/some_file.php'); rslt = rslt.match(/([^;]+);([^\|]+)\|(.+)/); /* Нулевой элемент - тип возвращенного ответа Первый элемент - текст надписи Второй - элементы списка (options'ы) */ document.getElementById('div_region').innerHTML = document.getElementById('div_city').innerHTML = ''; if (rslt[1] == 'regions') { rsl = "<select id='region'>" + rslt[3] + "</select>"; document.getElementById('div_region').innerHTML = rsl; select_city = ''; select_region = ''; eval("select_city = function () {step = " + step + ";if (document.getElementById(\"city\").value != \"\") {" + "document.getElementById(\"city\").setAttribute(\"correct\", 1);" + "} " + "else {" + "document.getElementById(\"city\").setAttribute(\"correct\", 0);" + "}" + "is_fields_correct(" + step + ")" + "}") eval('select_region = function () {step = ' + step + ';if (document.getElementById("region").value != "") {' + 'document.getElementById("region").setAttribute("correct", 1);' + '} ' + 'else {' + 'document.getElementById("region").setAttribute("correct", 0);' + '}' + 'is_fieldsvalue_correct(' + step + ');' + 'request = "lang=" + LANG + "&doit=is_select_region&id_country=" ' + '+ document.getElementById("country").value + "&id_region=" + ' + 'document.getElementById("region").value;' + 'rslt = GenerateXMLHttpRequest(request, "php/doit.php");' + 'rslt = rslt.match(/([^;]+);([^\\|]+)\\|(.+)/);' + 'document.getElementById("div_city").innerHTML = "";' + 'if (rslt[1] == "cities") {' + 'rsl = "<div id=\'caption_select_city\'>" + rslt[2] + "</div>";' + 'rsl += "<select id=\'city\'>" + rslt[3] + "</select>";' + 'document.getElementById("div_city").innerHTML = rsl;' + 'if (document.getElementById("city").addEventListener) {' + 'document.getElementById("city").addEventListener("change", ' + select_city + ', false);' + '}' + 'else if (document.getElementById("city").attachEvent) {' + 'document.getElementById("city").attachEvent("onChange", ' + select_city + ');' + '}' + '}' + '}') if (document.getElementById('region').addEventListener) { document.getElementById('region').addEventListener('change', select_region, false); } else if (document.getElementById('region').attachEvent) { document.getElementById('region').attachEvent('onChange', select_region); } } if (rslt[1] == 'cities') { rsl = "<div id='caption_select_city'>" + rslt[2] + "</div>"; rsl += "<select id='city'>" + rslt[3] + "</select>"; document.getElementById('div_city').innerHTML = rsl; } }В данной функции генерится код двух других функций с подстановкой значений необходимых параметров в текстовом виде, после чего он прогоняется через eval - в результате мы имеем две функции select_region и select_city со всеми параметрами, которые им требовалось передать.
Автор: Гость (не зарегистрирован), дата: 6 марта, 2013 - 17:46
#permalinkПридётся делать свой луна-парк.. Всем одна функция, в функции лист обработчиков. В принципе если сесть и подумать то легко, только как не дать убить никогда свой универсальный on<событие>?
Автор: Vladimir M (не зарегистрирован), дата: 16 марта, 2013 - 18:59
#permalinkВпрочем, это легко обойти при помощи небольшого замыкания, корректно передающего указатель this: divElem.attachEvent("onclick", function() { handler.call(divElem) })Вот только не всегда знаешь, какой это "divElem". А если есть список элементов, на который надо циклом назначить один обработчик, то здесь ваше решение не поможет.
Автор: koeshiro, дата: 14 июля, 2013 - 08:43
#permalinkГде можно найти имена всех событий?
Автор: Гость (не зарегистрирован), дата: 25 марта, 2014 - 12:30
#permalinkУзнал про Greasemonkey. Тренируюсь. Почему-то нижеследующий скрипт работает практически всюду, кроме "e.mail.ru", подскажите почему?
window.addEventListener('load',function(e){
alert('alert [10-33-12]');
},true);
Автор: iamxaoc (не зарегистрирован), дата: 29 декабря, 2014 - 10:25
#permalink"Особенности addEventListener
Решение W3C работает во всех современных браузерах, кроме Internet Explorer."
Работает в IE 9 >
Только что проверил в IE 11 - все ок
Автор: Мизар (не зарегистрирован), дата: 10 июля, 2015 - 16:19
#permalinkПодскажите пожалуста кто нибуть!
Имеем опредиленый код формы:
<div id="content"> <form action="game.php" onsubmit="return CheckTarget()" method="post"> <input type="hidden" name="token" value="miska"> <table class="table519"> <tr class="left top"> <td style="width:50%;margin:0;padding:0;"> <table border="0" cellpadding="0" cellspacing="0" width="259" style="margin:0;padding:0;"> <tr style="height:20px;"> <td class="transparent left"> <input id="radio_8" type="radio" name="mission" value="8" style="width:60px;"> <label for="radio_8">Перемешать</label> </td> </tr> </table> </td> <td class="top"> <table border="0" cellpadding="0" cellspacing="0" width="259"> <tr style="height:20px;"> <td class="transparent">Капуста</td> <td class="transparent"><a href="javascript:maxResource('metal');">все</a></th> <td class="transparent"><input name="metal" size="10" onchange="calculateCapacity();" type="text"></td> </tr> <tr style="height:20px;"> <td class="transparent">Моркофь</td> <td class="transparent"><a href="javascript:maxResource('crystal');">все</a></th> <td class="transparent"><input name="crystal" size="10" onchange="calculateCapacity();" type="text"></td> </tr> <tr style="height:20px;"> <td class="transparent">Огурец</td> <td class="transparent"><a href="javascript:maxResource('deuterium');">все</a></td> <td class="transparent"><input name="deuterium" size="10" onchange="calculateCapacity();" type="text"></td> </tr> </table> </td> </tr> <tr style="height:20px;"> <td colspan="2"><input class="submit" value="Дальше" type="submit"></input></td> </tr> </table> </form> <script type="text/javascript"> data = {"fleetroom":"5460000000","consumption":"4"}; </script> </div>Как правильно добавить обработчик (и вожможно ли такое) так чтоб при нажатии кнопки "Дальше" На сервер отправлялась информация не раз, а три раза с интервалом в одну секунду (с задержкой)!
Автор: Мизар (не зарегистрирован), дата: 10 июля, 2015 - 16:30
#permalinkИли скажу по проще:
Можна продублировать как-то отправку формы, от нажатия только одногораза на кнопку!?
Автор: Разработчик (не зарегистрирован), дата: 12 июня, 2018 - 21:23
#permalinkВот это то, что надо!
Автор: Slope play game (не зарегистрирован), дата: 18 ноября, 2024 - 11:09
#permalinkЭтот материал предоставляет полезное объяснение о слушателях событий в JavaScript и том, как их использовать. Мне понравилось.