单个元素上的多个JS事件处理程序

问题描述 投票:26回答:3

我正在使用现有的Web应用程序,在应用程序中有不同形式的各种提交按钮,一些使用常规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()等。在我的测试中,警报总是首先触发,但我只是对竞争条件感到幸运?

javascript jquery javascript-events jquery-events
3个回答
51
投票

在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类,或者元素是链接,则更改事件。该事件被取消,但它依旧存在。该事件仍然可以在dom中进一步传播,因此我们可能遇到类似的事情:

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进一步传播到其目标。如果没有,事件对象将沿着DOM进一步向下移动,就好像什么都没发生一样。

一旦到达目标节点,事件就进入第二阶段:泡沫阶段。它不是向下传播到DOM的深处,而是向上爬回到顶层(一直到全局对象,它被派遣到那里......从那里来的所有这一切)。

在泡沫阶段,所有相同的规则都适用于传播阶段,只是相反。事件对象将首先遇到最接近目标元素的元素,并且最后会遇到全局对象。

There's a lot of handy, and clear diagrams for this here。我不能把它比任何好的'ol quirksmode更好,所以我建议你阅读他们在那里说的话。

结论:当处理2个事件监听器时,将它们连接在不同的级别上,以便按照您喜欢的方式对它们进行排序。

如果要保证两者都被调用,只能阻止事件在最后调用的处理程序中传播。

当你有两个监听器,连接到同一个事件的同一个元素/对象时,我从来没有遇到过第一个被连接的监听器的情况。

就是这样,我上床睡觉,希望我有道理


9
投票

jQuery使这很容易。

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

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

然后处理程序都会点击。 jQuery为你保留了一个汉堡的队列。并处理与您选择的选择器匹配的文档点击,以便无论何时创建按钮都可以触发它们。


4
投票

我/我遇到了类似的问题。但是,我不能影响/委托预先存在的“点击”事件的顺序(由Wicket框架添加)。但是我仍然需要在框架处理的任何“点击”或“更改”事件之前执行新的自定义事件。

幸运的是,有几个事件实际按顺序执行。 '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)链接更改页面。

当然,您可能需要使用更复杂的方法来检测应该执行的其他事件处理,但是您可以使用例如“目标”信息。 =>此脚本监视页面上的所有内容,因为这是我需要完成的工作。

© www.soinside.com 2019 - 2024. All rights reserved.