Проблемы с обработкой событий (Javascript) - PullRequest
3 голосов
/ 23 января 2010

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

Насколько я понимаю, правильно настроенный документ будет выглядеть примерно так:

<html>
<head>
<title>Title</title>
<script src="jsfile.js" type="text/javascript></script>
</head>
<body>

//Body content, like some form elements in my case

</body>
</html>

Jsfile.js будет выглядеть примерно так:

function a() {
//code;
}
function b()...

window.addEventListener('load', a, false);
document.getElementById("id").addEventListener('click', b, false);
document.myForm.typeSel.addEventListener('change', c, false);

//or to use better browser-compatible code...

function addEvent(obj,evt,fn) {
 if (obj.addEventListener)
   obj.addEventListener(evt,fn,false);
 else if (obj.attachEvent)
   obj.attachEvent('on'+evt,fn);
}
addEvent(window, 'load', a);
addEvent(document.getElementById('id'), 'click', b);
addEvent(document.myForm.typeSel, 'change', c);

Насколько я понимаю, браузер загружает этот код javascript, добавляя каждый из этих обработчиков событий в соответствующие элементы. ОДНАКО ... Хотя обработчик window добавлен правильно, ни один из них не добавлен. Но если поместить в функцию, (например) метод getElementById для доступа к элементу работает просто отлично, и добавляется обработчик события. Таким образом, я мог бы создать функцию loadEvents (), которая вызывается через окно загрузки, которая содержит все функции addEvent () для других элементов документа, для которых мне нужны обработчики событий. Но, как я все понимаю, я не должен был этого делать.

Кроме того, если бы я вставил код addEvent в тело вместе с элементом, к которому он обращается, например ...

<input type="checkbox" id="test" />
<script type="text/javascript>
document.getElementById("test").onclick = func;
</script>

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

Итак, вопрос в том, чтобы использовать " element .addEventListener ('click', func, false)", "addEvent ( element , 'click', func)" или даже " element .onclick = func" - как я могу успешно ссылаться на элемент в конце файла сценария в голове, не вставляя его в другую функцию? Почему getElementById и другие подобные методы не работают вне функции в голове?

Или есть какой-то недостаток в моем глубоком понимании?

Ответы [ 3 ]

2 голосов
/ 23 января 2010

Помещение <script> в <head> раньше было мудростью. Но в наше время, с тяжелыми страницами Ajax, <script> все чаще и чаще, но в теле, как можно ниже. Идея состоит в том, что загрузка и синтаксический анализ <script> не позволяют загрузить остальную часть страницы, поэтому пользователь будет смотреть на пустую страницу. Убедившись, что тело загружается максимально быстро, вы даете пользователю возможность на что-то взглянуть. См. Лучшие практики YAHOO для лучшего объяснения этого вопроса: http://developer.yahoo.com/performance/rules.html

Теперь, независимо от этой проблемы, код, который вы сейчас настроили, не может работать - по крайней мере, когда элементы, к которым вы пытаетесь присоединить обработчики, еще не созданы. Например, в этой строке:

document.getElementById("id").addEventListener('click', b, false);

вы получите ошибку времени выполнения, если элемент с id="id" находится внутри тела. Теперь, если вы поместите <script> в теле, чуть ниже, после содержимого (включая элемент с id="id", оно будет работать, так как скрипт выполняется после того, как html-код для этих элементов будет проанализирован и добавлен в ДОМ.

Если вы хотите, чтобы скрипт был в заголовке, вы можете это сделать, но вам нужно будет синхронизировать добавление обработчиков событий с отображением содержимого страницы. Вы можете сделать это, добавив их все в обработчик загрузки документа или окна. Итак, если вы напишите:

//cross browser add event handler 
function addEventHandler(obj,evt,fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evt,fn,false);
    } else if (obj.attachEvent) {
        obj.attachEvent('on'+evt,fn);
    }
}
addEventHandler(document, 'load', function(){
    //set up all handlers after loading the document
    addEventHandler(document.getElementById('id'), 'click', b);
    addEventHandler(document.myForm.typeSel, 'change', c);
});

это работает.

1 голос
/ 23 января 2010

Причина, по которой window.addEventListener работает, а document.getEle...().addEventListener не работает, проста: window объект существует, когда вы выполняете этот код, в то время как элемент с id="abc" все еще не загружен.

Когда ваш браузер загружает исходные тексты страницы, исходный код анализируется и выполняется как можно скорее. Поэтому, если вы поместите script в элемент head - в самом начале источника - он будет выполнен еще до того, как какая-то <div id="abc">...</div> будет загружена. Поэтому я думаю, теперь вы знаете, почему

<div id="test">Blah</div>
<script type="text/javascript">document.getElementById("test").style.color = "red";</script>

работает, а вот это:

<script type="text/javascript">document.getElementById("test").style.color = "red";</script>
<div id="test">Blah</div>

нет.

Вы можете решить эту проблему разными способами. Самые популярные из них:

  • размещение сценариев в конце документа (прямо перед </body>)
  • использование событий для задержки выполнения скриптов

Первый способ должен быть ясен прямо сейчас, но лично я предпочитаю последний (даже если он немного хуже).

Так как бороться с событиями? Когда браузер наконец загрузит и проанализирует весь источник, будет выполнено событие DOMContentLoaded. Это событие означает, что источник готов, и вы можете манипулировать DOM с помощью JavaScript.

window.addEventListener("DOMContentLoaded", function() {
    //here you can safely use document.getElementById("...") etc.
}, false);

К сожалению, не все браузеры поддерживают событие DOMContentLoaded, но, как всегда, ... Google является ответом . Но это не конец плохих новостей. Как вы заметили, addEventListener не очень хорошо поддерживается IE. Ну ... этот браузер действительно усложняет жизнь, и вам придется взломать еще одну вещь ... Да ... еще раз - Google . Но это IE, так что это еще не все. Обычные браузеры (например, Opera или Firefox) поддерживают Модель событий W3C , в то время как IE поддерживает свою собственную - так что еще раз - Google для кросс-браузерного решения.

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

PS. Я заметил, что вы рассматриваете возможность использования события Load для выполнения ваших скриптов. Не делай этого. Load Событие выполнено слишком поздно. Вы должны ждать, пока каждое изображение или файл загружен. Вы должны использовать DOMContentLoaded событие. ;)

EDIT: Я забыл ... работать с кросс-браузерной моделью событий намного проще, когда вы используете какой-то фреймворк, например очень популярный jQuery . Но хорошо знать, как работают браузеры.

0 голосов
/ 23 января 2010

вы знакомы с jQuery ?
это библиотека javascript, содержащая некоторые действительно классные инструменты.
например, если вы хотите, чтобы какое-то действие js выполнялось сразу после вашей страницы, если она полностью загружена и все элементы DOM созданы (чтобы избежать этих раздражающих исключений), вы можете просто использовать метод ready () . также я вижу, что вы хотите прикрепить click \ change события jQuery об этом тоже позаботится :) и вам не нужно беспокоиться обо всех этих проблемах с кросс-браузерностью.
взгляните на jQuery селекторы , чтобы упростить вашу жизнь при попытке получить элемент.

Хорошо, просто попробуй, у него очень интуитивный API и хорошая документация.

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