无论事件添加的顺序如何,您如何强制您的 javascript 事件首先运行?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13979961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:19:45  来源:igfitidea点击:

How do you force your javascript event to run first, regardless of the order in which the events were added?

javascriptjqueryjavascript-events

提问by Scott

I have javascript that people are including in their page. In my javascript I have a version of jQuery (1.8 for sake of easy reference) that is sectioned off into its own namespace, and referenced via a global variable (but not one of the two default vars of "$" or "jQuery"). This allows users to have jQuery in their page and have it not interfere with the stuff I'm doing internally in my functions.

我有人们在他们的页面中包含的 javascript。在我的 javascript 中,我有一个 jQuery 版本(为了便于参考,为 1.8),它被划分到自己的命名空间中,并通过全局变量引用(但不是“$”或“jQuery”的两个默认变量之一) . 这允许用户在他们的页面中使用 jQuery,并且它不会干扰我在我的函数内部所做的事情。

So we have one page that has jQuery already (1.4), and everything works fine, except that the user and my code are both listening to "click" events on elements, and theirs is going first, so on the few events they do that return false, jQuery stops propagation and my event never gets triggered. I need my event to go first. The user is expecting my onClick functionality to still work.

所以我们有一个已经有 jQuery 的页面(1.4),一切正常,除了用户和我的代码都在监听元素上的“点击”事件,他们的事件最先发生,所以他们这样做的几个事件返回 false,jQuery 停止传播,我的事件永远不会被触发。我需要先举办我的活动。用户希望我的 onClick 功能仍然有效。

Now I know that jQuery keeps its own order of events internally through the _data() object, and through this it is possible to unbind existing events, bind my event, then rebind the existing events, but that only applies to objects bound through that instance of jQuery. I'd rather not just blindly look for the jQuery object in hopes that the conflict was introduced by a user's own version of jQuery. After all what happens when a user binds the event not through jQuery? Trying to manipulate the existing jQuery object in the page isn't a good solution.

现在我知道 jQuery 通过 _data() 对象在内部保持自己的事件顺序,通过这个可以解除现有事件的绑定,绑定我的事件,然后重新绑定现有的事件,但这仅适用于通过该实例绑定的对象的 jQuery。我宁愿不只是盲目地寻找 jQuery 对象,希望冲突是由用户自己的 jQuery 版本引入的。毕竟,当用户不通过 jQuery 绑定事件时会发生什么?尝试操作页面中现有的 jQuery 对象并不是一个好的解决方案。

I know that, depending on browser, they are using addEventListener/removeEventListener or attachEvent/detachEvent. If only I could get a listing of the already added events, I could rebind them in the order I wanted, but I can't find out how. Looking through the DOM via chrome inspect I don't see onclick bound anywhere (not on the object, not on window or document either).

我知道,根据浏览器,他们使用 addEventListener/removeEventListener 或 attachEvent/detachEvent。如果我能得到已添加事件的列表,我就可以按照我想要的顺序重新绑定它们,但我不知道如何。通过 chrome inspect 查看 DOM 我没有看到任何地方的 onclick 绑定(不在对象上,也不在窗口或文档上)。

I'm having the darndest time trying to figure out just exactly where jQuery binds its listening. To be able to control the order of its own events, jQuery must blanketly listen somewhere and then fire off its own functions right? If I could figure out where that's done I might get some insight into how to ensure my event is always first. Or maybe there's some javascript API I haven't been able to find on google.

我有最糟糕的时间试图找出 jQuery 绑定其监听的确切位置。为了能够控制自己事件的顺序,jQuery 必须在某处全面监听,然后触发自己的函数,对吗?如果我能弄清楚这是在哪里完成的,我可能会深入了解如何确保我的活动始终是第一位的。或者也许有一些我在谷歌上找不到的 javascript API。

Any suggestions?

有什么建议?

采纳答案by Scott

As Bergi and Chris Heald said in the comments, it turns out there's no way to get at the existing events from the DOM, and no method to insert events "first". They are fired in the order they were inserted by design, and hidden by design. As a few posters mentioned you have access to the ones added through the same instance of jQuery that you're using via jQuery's data, but that's it.

正如 Bergi 和 Chris Heald 在评论中所说,事实证明无法从 DOM 获取现有事件,也没有方法“首先”插入事件。它们按设计插入的顺序触发,并按设计隐藏。正如一些海报提到的,您可以访问通过 jQuery 数据使用的同一个 jQuery 实例添加的那些,但仅此而已。

There is one other case where you can run before an event that was bound before your code ran, and that's if they used the "onclick" HTML attribute. In that case you can write a wrapper function, as nothingisnecessary pointed out in a rather over-the-top toned comment below. While this wouldn't help in the instance of the original question I asked, and it's now veryrare for events to be bound this way (most people and frameworks use addEvent or attachEventListener underneath now), it is one scenario in which you can solve the issue of "running first", and since a lot of people visit this question looking for answers now, I thought I'd make sure the answer is complete.

还有另一种情况,您可以在代码运行之前绑定的事件之前运行,那就是如果他们使用了“onclick”HTML 属性。在这种情况下,您可以编写一个包装函数,正如下面的评论中所指出的那样,没有必要。虽然这在我提出的原始问题的实例中无济于事,而且现在以这种方式绑定事件的情况非常罕见(大多数人和框架现在都在下面使用 addEvent 或 attachEventListener ),但这是您可以解决的一种情况“先运行”的问题,由于现在很多人访问这个问题寻找答案,我想我会确保答案是完整的。

回答by Chris Heald

We solved this by just adding a little jQuery extension that inserts events at the head of the event chain:

我们通过添加一个小的 jQuery 扩展来解决这个问题,该扩展在事件链的头部插入事件:

$.fn.bindFirst = function(name, fn) {
  var elem, handlers, i, _len;
  this.bind(name, fn);
  for (i = 0, _len = this.length; i < _len; i++) {
    elem = this[i];
    handlers = jQuery._data(elem).events[name.split('.')[0]];
    handlers.unshift(handlers.pop());
  }
};

Then, to bind your event:

然后,绑定您的事件:

$(".foo").bindFirst("click", function() { /* Your handler */ });

Easy peasy!

十分简单!

回答by Linh Dam

I encounter an opposite situation where I was asked to include a library, which uses event.stopImmediatePropagation()on an element, to our website. So some of my event handlers are skipped. Here is what I do (as answered here):

我遇到了相反的情况,我被要求event.stopImmediatePropagation()在我们的网站上包含一个用于元素的库。所以我的一些事件处理程序被跳过了。这是我做什么(如回答这里):

<span onclick="yourEventHandler(event)">Button</span>

Warning: this is notthe recommended way to bind events, other developers may murder you for this.

警告:这不是绑定事件的推荐方式,其他开发人员可能会因此而谋杀您。

回答by opensourcejunkie

Just so it's said, I think this might be possible if you override the native implementations of these functions. This is BAD practice - very bad practice when developing a library to alter native implementations, because it can easily conflict with other libraries.

话虽如此,我认为如果您覆盖这些函数的本机实现,这可能是可能的。这是不好的做法 - 在开发库以更改本机实现时非常糟糕的做法,因为它很容易与其他库发生冲突。

However, for completeness, here's one possibility (completely untested, just demonstrating the general concept):

但是,为了完整性,这里有一种可能性(完全未经测试,仅演示一般概念):

// override createElement()
var temp = document.createElement;
document.createElement = function() {
    // create element
    var el = document.createElement.original.apply(document, arguments);

    // override addEventListener()
    el.addEventListenerOriginal = el.addEventListener;
    el._my_stored_events = [];

    // add custom functions
    el.addEventListener = addEventListenerCustom;
    el.addEventListenerFirst = addEventListenerFirst;
    // ...
};
document.createElement.original = temp;

// define main event listeners
function myMainEventListeners(type) {
    if (myMainEventListeners.all[type] === undefined) {
        myMainEventListeners.all[type] = function() {
            for (var i = 0; i < this._my_stored_events.length; i++) {
                var event = this._my_stored_events[i];
                if (event.type == type) {
                    event.listener.apply(this, arguments);
                }
            }
        }
    }
    return myMainEventListeners.all[type];
}
myMainEventListeners.all = {};

// define functions to mess with the event list
function addEventListenerCustom(type, listener, useCapture, wantsUntrusted) {
    // register handler in personal storage list
    this._my_stored_events.push({
        'type' : type,
        'listener' : listener
    });

    // register custom event handler
    if (this.type === undefined) {
        this.type = myMainEventListeners(type);
    }
}

function addEventListenerFirst(type, listener) {
    // register handler in personal storage list
    this._my_stored_events.push({
        'type' : type,
        'listener' : listener
    });

    // register custom event handler
    if (this.type === undefined) {
        this.type = myMainEventListeners(type);
    }
}

// ...

A lot more work would need to be done in this regard to truly lock this down, and again, it's best not to modify native libraries. But it's a useful mental exercise that helps to demonstrate the flexibility JavaScript provides in solving problems like this.

在这方面需要做更多的工作才能真正锁定它,而且最好不要修改本机库。但这是一个有用的心理练习,有助于展示 JavaScript 在解决此类问题时提供的灵活性。