无论事件添加的顺序如何,如何强制Javascript事件先运行?
How do you force your Javascript event to run first, regardless of the order in which the events were added?
我有Javascript,人们包括在他们的页面。在我的Javascript中,我有一个版本的jQuery(为了方便参考,是1.8),它被分割到自己的命名空间中,并通过一个全局变量(但不是"$"的两个默认变量之一)引用。或"jQuery"。这允许用户在他们的页面中使用jQuery,并且不会干扰我在函数中内部做的事情。
所以我们有一个页面已经有jQuery(1.4),一切都很好,除了用户和我的代码都在听"点击"元素上的事件,而它们的事件首先发生,所以对于它们所做的少数返回false的事件,jQuery停止传播,我的事件永远不会被触发。我得先办我的活动。用户希望我的onClick
功能仍然工作。
现在我知道jQuery通过_data()
对象内部保持自己的事件顺序,并且通过这个可以取消绑定现有事件,绑定我的事件,然后重新绑定现有事件,但这只适用于通过jQuery实例绑定的对象。我宁愿不盲目地寻找jQuery对象,希望冲突是由用户自己的jQuery版本引入的。毕竟,当用户不通过jQuery绑定事件时会发生什么?试图在页面中操作现有的jQuery对象并不是一个好的解决方案。
我知道,根据浏览器,他们正在使用addEventListener
/removeEventListener
或attachEvent
/detachEvent
。如果我能得到已经添加的事件的列表就好了,我就可以按照我想要的顺序重新绑定它们,但是我不知道怎么做。通过DOM通过Chrome检查我没有看到onclick
绑定在任何地方(不在对象上,不在窗口或文档上)。
我正在努力弄清楚jQuery绑定侦听的确切位置。为了能够控制自己事件的顺序,jQuery必须在某处监听,然后触发自己的函数,对吗?如果我能弄清楚这是在哪里发生的,我可能会对如何确保我的活动总是排在第一位有所了解。或者可能有一些Javascript API我没能在谷歌上找到。
有什么建议吗?
我们通过添加一个小的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());
}
};
然后,绑定你的事件:
$(".foo").bindFirst("click", function() { /* Your handler */ });
容易peasy !
正如Bergi和Chris Heald在评论中所说,事实证明没有办法从DOM中获得现有的事件,也没有方法"首先"插入事件。它们是按照设计插入的顺序触发的,也是按照设计隐藏的。正如一些海报所提到的,你可以通过jQuery的数据访问通过jQuery的相同实例添加的内容,但仅此而已。
还有另外一种情况,你可以在代码运行之前绑定的事件之前运行,那就是他们使用了"onclick" HTML属性。在这种情况下,您可以编写一个包装器函数,正如下面一个相当夸张的注释所指出的那样。虽然这在我问的原始问题的实例中没有帮助,而且现在非常少见事件以这种方式绑定(大多数人和框架现在在下面使用addEvent或attachEventListener),但这是一个可以解决"先运行"问题的场景,并且由于很多人现在访问这个问题寻找答案,我想我会确保答案是完整的。
我遇到了一个相反的情况,我被要求在我们的网站上包含一个使用event.stopImmediatePropagation()
元素的库。因此,跳过了一些事件处理程序。以下是我所做的(在这里回答):
<span onclick="yourEventHandler(event)">Button</span>
警告:这是不是绑定事件的推荐方式,其他开发人员可能会因此而杀了你。
<div>
<div id="target"></div>
</div>
target.parentNode.addEventListener('click',()=>{console.log('parent capture phase handler')},true)
addEventListener的第三个参数表示:
- true -捕获阶段
- 假气泡相
有用的链接:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
https://javascript.info/bubbling-and-capturing
Aliaksei Pavlenkos关于useCapture的建议可以使用。他断言它必须附加到父节点是错误的:MDN
"捕获"阶段的事件侦听器在任何非捕获阶段的事件侦听器之前被调用
target.addEventListener(type, listener, useCapture);
发现最容易将addListener
和removeListener
方法添加到document
(因为这只是我需要它们的地方-我想你可以使用Element.prototype
和this
代替)。只有一个"真正的"。每个类型都添加侦听器,它只是一个按顺序调用实际侦听器的函数。eventListeners
字典被添加到文档(因此可以与处理程序或顺序混淆)。
[编辑]我认为大多数情况下的正确答案是使用addEventListener
的第三个参数:https://stackoverflow.com/a/29923421。下面的答案忽略了参数(故意)。
[edit]更新代码,只添加一个额外属性:document.eventHandlers
+修改命名。
// Storage.
document.eventListeners = {}; // { type: [ handlerFunc, listenerFuncs ] }
// Add event listener - returns index.
document.addListener = (type, listener, atIndex) => {
// Get info.
const listening = document.eventListeners[type];
// Add to existing.
if (listening) {
// Clean up.
atIndex = atIndex || 0;
const listeners = listening[1]; // Array of funcs.
// Already has.
const iExists = listeners.indexOf(listener);
if (iExists !== -1) {
// Nothing to do.
if (iExists === atIndex)
return atIndex;
// Remove from old position.
listeners.splice(atIndex, 1);
}
// Add (supporting one cycle of negatives).
const nListeners = listeners.length;
if (atIndex > nListeners)
atIndex = nListeners;
else if (atIndex < 0)
atIndex = Math.max(0, atIndex + nListeners + 1);
listeners.splice(atIndex, 0, listener);
}
// New one.
else {
// Handler func.
const handler = (...args) => {
const listening = document.eventListeners[type];
if (listening) {
const listeners = listening[1]; // Array of funcs.
for (const listener of listeners)
listener(...args);
}
};
// Update dictionary.
document.eventListeners[type] = [ handler, [ listener ] ];
// Add listener.
document.addEventListener(type, handler);
// First one.
atIndex = 0;
}
// Return index.
return atIndex;
};
// Remove event listener - returns index (-1 if not found).
document.removeListener = (type, listener) => {
// Get info.
const listening = document.eventListeners[type];
if (!listening)
return -1;
// Check if exists.
const listeners = listening[1];
const iExists = listeners.indexOf(listener);
if (iExists !== -1) {
// Remove listener.
listeners.splice(iExists, 1);
// If last one.
if (!listeners.length) {
// Remove listener.
const handlerFunc = listening[0];
document.removeEventListener(type, handlerFunc);
// Update dictionary.
delete document.eventListeners[type];
}
}
// Return index.
return iExists;
}
正如所说,我认为如果您覆盖这些函数的本机实现,这可能是可能的。当开发一个库来替代现有实现时,这是一个非常糟糕的做法,因为它很容易与其他库发生冲突。
然而,为了完整起见,这里有一种可能性(完全未经测试,只是演示一般概念):
// 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);
}
}
// ...
在这方面需要做更多的工作来真正锁定它,并且,最好不要修改本机库。但这是一个有用的心理练习,有助于展示JavaScript在解决此类问题时提供的灵活性。
- 召回窗口加载事件 - javascript
- 动态创建OnClick事件Javascript
- 全局屏幕span onclick触发一个事件javascript
- 使用react js在跨域上执行事件javascript
- 在change事件javascript上动态添加行
- 在粘贴事件Javascript的输入字段中删除所有非数字字符
- 如何在多表单的输入框中使用输入事件javascript
- 无法查看特定信息.单击事件.Javascript
- 两个文本框事件Javascript
- 阻止鼠标滚轮滚动,但不阻止滚动条事件.JavaScript
- 从 onkeydown 事件 (Javascript) 获取当前用户输入
- “稍后提醒我”/“快速事件”JavaScript解决方案
- 更改我正在侦听的事件Javascript的对象的CSS
- setInterval 不允许同时单击事件 - javascript
- 对每种情况使用一个事件(JavaScript 开关)
- 仅创建第二次点击事件 Javascript
- 每 60 秒仅启动一次事件 (JavaScript)
- 如何连接来自不同文件的事件?Javascript/Backbone
- URL 更改事件 - JavaScript
- 无法在无头模式下使用 watir Webdriver 执行按钮元素的 onclick 事件 javascript