Ответ 1
В JS у вас действительно нет контроля над тем, какой порядок обработчиков событий вызывается, но с тщательной делегией и хорошо размещенными слушателями это возможно.
Делегирование - одна из самых мощных функций модели событий. Как вы можете или не можете знать: в JS событие передается на вершину dom, откуда оно распространяется до элемента, на который должно быть применено событие. Поэтому разумно, что прослушиватель событий, прикрепленный к глобальному объекту, будет вызывать его обработчик до слушателя, который был присоединен к самому элементу.
window.addEventListener('click',function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
console.log('window noticed you clicked something');
console.log(target);//<-- this is the element that was clicked
}, false);//<-- we'll get to the false in a minute
Важно отметить, что мы действительно имеем доступ к объекту события в обработчиках. В этом случае мы оставили объект события нетронутым, поэтому он будет продолжать распространяться до цели, по пути вниз, он может встретить что-то вроде этого:
document.getElementById('container').addEventListener('click', function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() !== 'a' || target.className.match(/\bclickable\b/))
{
return e;//<return the event, unharmed
}
e.returnValue = false;
if (e.preventDefault)
{
e.preventDefault();
}
}, false);
Теперь этот обработчик будет вызван после того, как слушатель на уровне окна вызовет его помощник. На этот раз событие изменится, если элемент с щелчком не имеет класса clickable
, или элемент является ссылкой. Событие отменяется, но он продолжает жить. Событие по-прежнему свободно распространяется дальше по дому, поэтому мы можем встретить что-то вроде:
document.getElmentById('form3').addEventListener('click',function(e)
{
e = e || window.event;
if (e.returnValue === false || e.isDefaultPrevented)
{//this event has been changed already
//do stuff, like validation or something, then you could:
e.cancelBubble = true;
if (e.stopPropagation)
{
e.stopPropagation();
}
}
}, false);
Здесь, вызывая stopPropagation
, событие уничтожается. Он не может распространяться дальше вниз по дому до своей цели, если событие уже не было изменено. Если нет, объект события перемещается дальше DOM, как будто ничего не произошло.
Как только он достигает своей цели node, событие переходит в свою вторую фазу: фазу пузырьков. Вместо того, чтобы распространяться вниз в глубины DOM, он поднимается вверх, на верхний уровень (вплоть до глобального объекта, где он был отправлен... откуда он пришел и все такое).
В фазе пузырька действуют те же правила, что и в фазе распространения, только наоборот. Объект события встретит сначала элементы, наиболее близкие к целевому элементу, и глобальный объект.
Здесь есть много удобных и понятных диаграмм. Я не могу сказать, что это лучше, чем хороший "quirksmode", поэтому я предлагаю вам прочитать, что они там скажут.
В нижней строке: при работе с 2 прослушивателями событий добавьте их на другом уровне, чтобы отсортировать их так, как вам нравится.
Если вы хотите гарантировать, что оба вызываются, прекратите распространение события в этом обработчике, который будет называться последним.
Когда у вас есть два слушателя, прикрепленные к одному и тому же элементу/объекту для одного и того же события, я никогда не сталкивался с ситуацией, когда первый слушатель, который был прикреплен первым, также не назывался первым.
Что он, я уйду спать, надеясь, что я имел смысл