Несколько обработчиков событий js на одном элементе

Я работаю с существующим веб-приложением, в приложении есть множество кнопок отправки в разных формах, некоторые используют обычный пост http, некоторые определяют функцию onClick, а некоторые привязывают обработчик события js к кнопке, используя класс на элементе.

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

Пример сценария: я хочу добавить класс к этим кнопкам, который сводит их все к общей js-функции, которая просто записывает использование в некоторый api. Есть ли риск того, что функция ведения журнала не вызывается, потому что отправитель формы прошел навигацию от страницы?

Я не выполнял множество js-разработок, и я мог проверить это 100 раз и просто повезло с ним.


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

$(document).ready(function(){
    $('.testingBtn').click(function() {
        window.location.replace("http://stackoverflow.com");
    });
    $( ".testingBtn" ).click(function(){
        alert('submitting!');
    });
});


<input class="testingBtn" type="submit" id="submitform" value="Complete Signup" />

Как видно выше, я могу связать несколько событий, и в этом примере просто перенаправлен на другой URL-адрес, но это может быть form.submit() и т.д. В моем тестировании предупреждение всегда срабатывало первым, но я просто получаю повезло с условиями гонки?

Ответы

Ответ 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 прослушивателями событий добавьте их на другом уровне, чтобы отсортировать их так, как вам нравится.

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

Когда у вас есть два слушателя, прикрепленные к одному и тому же элементу/объекту для одного и того же события, я никогда не сталкивался с ситуацией, когда первый слушатель, который был прикреплен первым, также не назывался первым.

Что он, я уйду спать, надеясь, что я имел смысл

Ответ 2

jQuery делает это легко.

$(document).on('click', '.someclass', function() {
  doStuff();
});

$(document).on('click', '.someclass', function() {
  doMoreStuff();
});

Затем обработчики будут стрелять по клику. jQuery хранит для вас очередь ручных инструментов. И обрабатывает клики документов, которые соответствуют выбранному вами селектору, чтобы они могли запускаться независимо от того, когда ваши кнопки созданы.

Ответ 3

У меня есть аналогичная проблема. Однако я не могу повлиять на порядок/делегировать ранее существовавшие события 'click' (добавленные картой Wicket). Но мне все равно нужно выполнить новое настраиваемое событие before любого из событий 'click' или 'change', обрабатываемых каркасом.

К счастью, есть несколько событий, которые фактически выполняются по порядку. "Mousedown" и "mouseup" происходят до 'click'.
http://en.wikipedia.org/wiki/DOM_events

$(document).on('mousedown', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  console.log(target + ' before default event'); // Hold mouse button down to see this message in console before any action is executed
});

ИЛИ

$(document).on('mouseup', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  alert(target + ' before default event'); // You may not notice this event fires when the page changes unless this is an alert
});

Это позволит вести регистрацию (например, через ajax) до того, как будет выполнено фактическое событие, например. изменение страницы через (ajax) ссылку.

Конечно, вам может потребоваться более сложное средство для определения того, для чего необходимо выполнить дополнительную обработку событий, но для этого вы можете использовать, например, "целевую" информацию. = > Этот script контролирует все на странице, так как мне это нужно сделать.