32

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

4

3 回答 3

63

在 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 的深处,而是向上爬回顶层(一直到全局对象,它被调度的地方......它从哪里来等等)。

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

这里有很多方便且清晰的图表。我不能说它比好的 'ol quirksmode 更好,所以我建议你阅读他们在那里所说的内容。

底线:在处理 2 个事件侦听器时,将它们都附加到不同的级别,以便按照您喜欢的方式对它们进行排序。

如果您想保证两者都被调用,只需停止事件在最后调用的处理程序中传播。

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

就是这样,我要去睡觉了,希望我说得通

于 2013-06-27T20:51:27.923 回答
13

jQuery 让这一切变得简单。

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

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

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

于 2013-06-27T20:06:16.753 回答
4

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

幸运的是,有几个事件实际上是按顺序执行的。“mousedown”和“mouseup”恰好发生在“点击”之前。
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) 链接更改页面。

当然,您可能需要更复杂的方法来检测应该执行哪些附加事件处理,但是您可以使用例如“目标”信息来实现这一点。=> 这个脚本监视页面上的所有内容,因为这就是我需要这样做的方式。

于 2014-11-10T07:59:09.263 回答