Классическая система кроссбраузерного добавления событий, описанная в предыдущей статье, неустойчива к ошибкам выполнения обработчиков.
Если какой-нибудь обработчик содержит ошибку, то, генерируя исключение, он ломает цикл вызова остальных обработчиков текущего элемента/события.
Получается, что обработчики зависят друг от друга. Есть различные способы, как этого избежать.
Можно завернуть вызов каждого обработчика в try .. catch. Тогда исключение будет поймано.
Что делать дальше?
Есть варианты.. Например, библиотека Yahoo UI хранит последнее пойманное исключение в специальной переменной lastError. А фреймворк Mochikit собирает все пойманные исключения в errors.
В коде ниже проиллюстрирован подход из Mochikit:
function commonHandle(event) {
event = fixEvent(event)
handlers = this.events[event.type]
// (1)
var errors = []
for ( var g in handlers ) {
try {
var ret = handlers[g].call(this, event)
if ( ret === false ) {
event.preventDefault()
event.stopPropagation()
}
} catch(e) {
// (2)
errors.push(e)
}
}
// (3)
if (errors.length == 1) {
throw errors[0]
} else if (errors.length > 1) {
var e = new Error("Multiple errors thrown in handling 'sig', see errors property");
e.errors = errors
throw e
}
}
errors для исключенийError со списком ошибок в свойстве errors и кидаем его.setTimeoutКаждый вызов обработчика можно завернуть в setTimeout.
Пожалуй, это самый простой способ.
// вместо
handlers[g].call(this, event)
// поставить
setTimeout(function() { handlers[g].call(..) }, 0)
При этом - да, обработчики будут вызваны независимо. Но к моменту, когда сработает setTimeout - событие будет уже "мертво", нельзя будет ни вызвать preventDefault, ни отменить всплытие.
Кроме того, setTimeout не гарантирует последовательность исполнения и является асинхронным, в то время как реальная система обработчиков должна работать синхронно.
Поэтому, увы, этот способ никак не годится.
Этот подход описан Dean Edwards в статье Callbacks and Events
Он заключается в том, что каждое событие заворачивается не в setTimeout, а в dispatchEvent (fireEvent для IE).
Таким образом, события вызываются в независимых потоках выполнения и инициируют ошибки независимо.
Есть несколько вариантов, как сделать систему событий устойчивой к ошибкам.
Жизнеспособны - первый подход (YUI/Mochikit) и последний (Dean).
Их можно с успехом использовать на практике.