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

时间:2022-10-03 04:15:54

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),并且一切正常,除了用户和我的代码都在元素上听“click”事件,而他们的第一个,所以在他们做的那几个事件上返回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检查来查看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?

4 个解决方案

#1


19  

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!

#2


3  

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 very rare 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),这是一个你可以解决的场景“先跑”的问题,既然很多人现在都在寻找答案,我想我会确保答案是完整的。

#3


1  

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 not the recommended way to bind events, other developers may murder you for this.

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

#4


0  

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在解决此类问题时提供的灵活性。

#1


19  

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!

#2


3  

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 very rare 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),这是一个你可以解决的场景“先跑”的问题,既然很多人现在都在寻找答案,我想我会确保答案是完整的。

#3


1  

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 not the recommended way to bind events, other developers may murder you for this.

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

#4


0  

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在解决此类问题时提供的灵活性。