Методы attachEvent/addEventListener имеют ряд общих недостатков.
Во-первых, ни W3C ни Microsoft не определяют порядок срабатывания обработчиков. Несколько обработчиков одного события на элементе могут сработать в любом порядке.
На текущий момент (март 2010) addEventListener сохраняет порядок назначения обработчиков, а attachEvent в IE - нет.
elem.attachEvent("onclick", handler);
elem.attachEvent("onclick", handler2);
// может быть так, что handler2 сработает раньше handler.
// так, судя по демке ниже, ведет себя IE.
// а может быть, handler сработает раньше handler2.
// так в демо ниже ведет себя Opera
// ... вообще, порядок неопределен
Есть еще одна проблема, с которой можно столкнуться при управлении событиями: нельзя точно сказать установлен ли определённый обработчик, или нет, и какие обработчики установлены на данный момент.
В спецификации DOM 3 существует объект eventListenerList, но он слишком новый и на данный момент не поддерживается ни одним из браузеров.
Основной недостаток attachEvent заключается в том, что функция-обработчик не получает текущий элемент, на котором сработало событие, ни в каком виде.
Значение this указывает на window, а свойство event.currentTarget отсутствует.
И это достаточно важная особенность!
Например, пусть мы хотим подсвечивать divElem при клике.
Элемент divElem с разным текстом и различными ссылками, и даже с жирным текстом внутри тага <b>
Конечно, же мы повесим обработчик на divElem:
divElem.attachEvent("onclick", handler)
Но при клике из объекта события event в IE можно получить только srcElement, то есть самый глубокий кликнутый элемент. Он может быть ссылкой <a> или элементом <b>, но нам-то нужны не они, а сам divElem, чтобы его подсветить.
Обработчик, добавленный при помощи attachEvent никак не может выяснить объект, на который подвешен.
Впрочем, это легко обойти при помощи небольшого замыкания, корректно передающего указатель this:
Но этот код порождает утечку памяти в Internet Explorer 6, на который не установлено исправляющее обновление, вышедшее в июне 2007 года, из-за круговой ссылки DOM <-> JS.
Можно повесить несколько обработчиков на одно событие
Вот я только одного не могу понять:
какой толк в attachEvent и addEventListener, если все всеравно все они выполняются по одному событию? Ну напишу я 10 раз attachEvent, перечислив 10 разных функций. И что? Где это пригодится на практике? Проще объявить onclick, адрес функции и в ней уже перечислить список тех 10 функций. Разве так не проще?
На практике одно и тоже. Я не вижу ВООБЩЕ никаких плючов у attachEvent, за исключением примера с iframe
разовый обработчик не всегда есть возможность написать. Ибо элемент может не существовать, а появляться в результете каких-либо действий пользователя. Тут и поможет создание такого обработчика.
Исходники страниц браузер делает сам, поэтому не верь ему.
Если на сайте при входе будет работать сценарий, который выводит "Привет (Ваш ник(Допустим proVIDec))!", то при просмотре исходного кода браузер даст не php скрипт, а текст "Превет proVIDec".
Этот параметр объяснялся в главе введение в события:
Установка по стандарту W3C
Решение W3C работает во всех современных браузерах, кроме Internet Explorer.
Установка обработчика:element.addEventListener( имя_события, обработчик, фаза)
Удаление обработчика:element.removeEventListener( имя_события, обработчик, фаза)
Еще одно отличие от решения Microsoft это третий параметр – фаза.
Если он установлен в true, то при срабатывании события во вложенном элементе, обработчик будет вызван на фазе "перехвата", а если значение будет false, то - на фазе "всплывания". Подробнее об этом будет написано далее, в разделе этой статьи "Порядок срабатывания событий".
Немного дополню данный пост. Зачастую в функцию требуется передать некоторые параметры. Как вариант, можно расплодить глобальные переменные или создать один глобальный массив, из которого и вытягивать нужные значения. А можно решить данный вопрос и другим способом. Приведу свою функцию:
В данной функции генерится код двух других функций с подстановкой значений необходимых параметров в текстовом виде, после чего он прогоняется через eval - в результате мы имеем две функции select_region и select_city со всеми параметрами, которые им требовалось передать.
Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены. Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
А как удалить свой пост?
Никак ! Мучайся теперь всю жизнь !
Вот я только одного не могу понять:
какой толк в attachEvent и addEventListener, если все всеравно все они выполняются по одному событию? Ну напишу я 10 раз attachEvent, перечислив 10 разных функций. И что? Где это пригодится на практике? Проще объявить onclick, адрес функции и в ней уже перечислить список тех 10 функций. Разве так не проще?
На практике одно и тоже. Я не вижу ВООБЩЕ никаких плючов у attachEvent, за исключением примера с iframe
Скачал ты чей-то скрипт, а он ставит обработчик через elem.on(событие). И убил все твои обработчики на это событие.
разовый обработчик не всегда есть возможность написать. Ибо элемент может не существовать, а появляться в результете каких-либо действий пользователя. Тут и поможет создание такого обработчика.
у меня по кнопке addEventListener выдаются алерты с 1 по 7 на FF.
это чей-то глюк или опечатка?
Видимо это можно отнести к недостутку этого способа - когда алертишь 5 раз он алертится 7 раз :-))))
ps: если открыть исходник страницы, то можно увидить
var b = document.getElementById('2ff') if (b.addEventListener) { b.addEventListener('click', function() { alert(1) }, false) b.addEventListener('click', function() { alert(2) }, false) b.addEventListener('click', function() { alert(3) }, false) b.addEventListener('click', function() { alert(4) }, false) b.addEventListener('click', function() { alert(5) }, false) b.addEventListener('click', function() { alert(6) }, false) b.addEventListener('click', function() { alert(7) }, false) }Исходники страниц браузер делает сам, поэтому не верь ему.
Если на сайте при входе будет работать сценарий, который выводит "Привет (Ваш ник(Допустим proVIDec))!", то при просмотре исходного кода браузер даст не php скрипт, а текст "Превет proVIDec".
Браузер ничего с исходниками страниц не делает. Он рендерит то, что отдаст сервер.
А обработкой php скриптов (как и любых других server-side'овых) как раз занимается сервер.
Впрочем, это легко обойти при помощи небольшого замыкания, корректно передающего указатель this:
divElem.attachEvent("onclick", function() { handler.call(divElem) })А вот так
divElem.attachEvent("onclick", handler.call(divElem) )передать получится?
Третий параметр в addEventListener совсем не понятен
Что за "фаза погружения"?
Этот параметр объяснялся в главе введение в события:
Установка по стандарту W3C
Решение W3C работает во всех современных браузерах, кроме Internet Explorer.
Установка обработчика:element.addEventListener( имя_события, обработчик, фаза)
Удаление обработчика:element.removeEventListener( имя_события, обработчик, фаза)
Еще одно отличие от решения Microsoft это третий параметр – фаза.
Если он установлен в true, то при срабатывании события во вложенном элементе, обработчик будет вызван на фазе "перехвата", а если значение будет false, то - на фазе "всплывания". Подробнее об этом будет написано далее, в разделе этой статьи "Порядок срабатывания событий".
Немного дополню данный пост. Зачастую в функцию требуется передать некоторые параметры. Как вариант, можно расплодить глобальные переменные или создать один глобальный массив, из которого и вытягивать нужные значения. А можно решить данный вопрос и другим способом. Приведу свою функцию:
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 со всеми параметрами, которые им требовалось передать.
Плохое решение. Лучше сделать это с использованием замыкания.
Отправить комментарий
Приветствуются комментарии:- Полезные.
- Дополняющие прочитанное.
- Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.Для остальных вопросов и обсуждений есть форум.