如何检查是否存在动态附加的事件侦听器?


102

这是我的问题:是否可以通过某种方式检查动态附加事件侦听器的存在?或者如何检查DOM中“ onclick”(?)属性的状态?我已经像Stack Overflow一样在互联网上搜索解决方案,但是没有运气。这是我的html:

<a id="link1" onclick="linkclick(event)"> link 1 </a>
<a id="link2"> link 2 </a> <!-- without inline onclick handler -->

然后在Javascript中,将动态创建的事件侦听器附加到第二个链接:

document.getElementById('link2').addEventListener('click', linkclick, false);

该代码运行良好,但是我所有检测该附加侦听器的尝试均失败:

// test for #link2 - dynamically created eventlistener
alert(elem.onclick); // null
alert(elem.hasAttribute('onclick')); // false
alert(elem.click); // function click(){[native code]} // btw, what's this?

jsFiddle在这里。如果单击“为2添加onclick”,然后单击“ [链接2]”,则事件触发良好,但“测试链接2”始终报告为false。有人可以帮忙吗?


1
我很抱歉地说,但不可能使用当前的方法来获取事件绑定:stackoverflow.com/questions/5296858/...
伊万

1
您可以使用chrome开发工具来做到这一点:stackoverflow.com/a/41137585/863115
arufian

您可以通过创建自己的存储来跟踪侦听器的存储来实现。看到我的答案更多。
天使Politis

如果目的是防止已经添加的事件再次添加,那么正确的答案就在这里。基本上,如果您使用命名函数而不是匿名函数,则重复的相同事件侦听器将被丢弃,您不必担心它们。
Syknapse

如果您担心有重复的侦听器,请先删除当前事件侦听器,然后再添加它。它并不完美,但很简单。
杰克逊(Jacksonkr)

Answers:


49

无法检查是否存在动态附加的事件侦听器。

查看是否附加了事件侦听器的唯一方法是通过附加事件侦听器,如下所示:

elem.onclick = function () { console.log (1) }

然后,您可以onclick通过返回!!elem.onclick(或类似方法)测试事件监听器是否已附加。


33

我做了这样的事情:

const element = document.getElementById('div');

if (element.getAttribute('listener') !== 'true') {
     element.addEventListener('click', function (e) {
         const elementClicked = e.target;
         elementClicked.setAttribute('listener', 'true');
         console.log('event has been attached');
    });
}

在附加侦听器时为元素创建特殊属性,然后检查其是否存在。


4
这是我过去使用的方法。我的建议是使用非常具体的语法结构。IE而不是“侦听器”,请使用事件本身。因此,“数据事件单击”。这在您要执行多个事件时提供了一定的灵活性,并使事情更具可读性。
conrad10781

加上@ conrad10781似乎是最可靠和最简单的方法。如果出于任何原因重新渲染了该元素,并且事件侦听器断开了连接,则此属性也会被重置。
Arye Eidelman

23

我要做的是在函数外部创建一个布尔值,该布尔值以FALSE开头,并在附加事件时设置为TRUE。在您再次附加事件之前,这将为您提供某种标记。这是一个想法的例子。

// initial load
var attached = false;

// this will only execute code once
doSomething = function()
{
 if (!attached)
 {
  attached = true;
  //code
 }
} 

//attach your function with change event
window.onload = function()
{
 var txtbox = document.getElementById('textboxID');

 if (window.addEventListener)
 {
  txtbox.addEventListener('change', doSomething, false);
 }
 else if(window.attachEvent)
 {
  txtbox.attachEvent('onchange', doSomething);
 }
}

这要求您更改附加事件的代码以跟踪其是否已附加(类似于thisthis answer)。如果您不控制代码,它将无法正常工作。
user202729 '18

6

tl; dr:不,您不能以任何本机支持的方式执行此操作。


我知道实现此目标的唯一方法是创建一个自定义存储对象,在其中保存添加的侦听器的记录。遵循以下内容:

/* Create a storage object. */
var CustomEventStorage = [];

步骤1:首先,您将需要一个可以遍历存储对象并返回给定元素(或false)的元素记录的函数。

/* The function that finds a record in the storage by a given element. */
function findRecordByElement (element) {
    /* Iterate over every entry in the storage object. */
    for (var index = 0, length = CustomEventStorage.length; index < length; index++) {
        /* Cache the record. */
        var record = CustomEventStorage[index];

        /* Check whether the given element exists. */
        if (element == record.element) {
            /* Return the record. */
            return record;
        }
    }

    /* Return false by default. */
    return false;
}

步骤2:然后,您将需要一个函数,该函数可以添加事件侦听器,也可以将侦听器插入存储对象。

/* The function that adds an event listener, while storing it in the storage object. */
function insertListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found. */
    if (record) {
        /* Normalise the event of the listeners object, in case it doesn't exist. */
        record.listeners[event] = record.listeners[event] || [];
    }
    else {
        /* Create an object to insert into the storage object. */
        record = {
            element: element,
            listeners: {}
        };

        /* Create an array for event in the record. */
        record.listeners[event] = [];

        /* Insert the record in the storage. */
        CustomEventStorage.push(record);
    }

    /* Insert the listener to the event array. */
    record.listeners[event].push(listener);

    /* Add the event listener to the element. */
    element.addEventListener(event, listener, options);
}

步骤3:关于问题的实际要求,您将需要以下函数来检查是否已为元素添加了用于指定事件的事件侦听器。

/* The function that checks whether an event listener is set for a given event. */
function listenerExists (element, event, listener) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether a record was found & if an event array exists for the given event. */
    if (record && event in record.listeners) {
        /* Return whether the given listener exists. */
        return !!~record.listeners[event].indexOf(listener);
    }

    /* Return false by default. */
    return false;
}

步骤4:最后,您将需要一个可以从存储对象中删除侦听器的函数。

/* The function that removes a listener from a given element & its storage record. */
function removeListener (element, event, listener, options) {
    /* Use the element given to retrieve the record. */
    var record = findRecordByElement(element);

    /* Check whether any record was found and, if found, whether the event exists. */
    if (record && event in record.listeners) {
        /* Cache the index of the listener inside the event array. */
        var index = record.listeners[event].indexOf(listener);

        /* Check whether listener is not -1. */
        if (~index) {
            /* Delete the listener from the event array. */
            record.listeners[event].splice(index, 1);
        }

        /* Check whether the event array is empty or not. */
        if (!record.listeners[event].length) {
            /* Delete the event array. */
            delete record.listeners[event];
        }
    }

    /* Add the event listener to the element. */
    element.removeEventListener(event, listener, options);
}

片段:


尽管自OP发布该问题以来已经过去了5年多,但我相信将来迷失于此问题的人们将从该答案中受益,因此随时可以提出建议或对其进行改进。😊


谢谢您的解决方案,它既简单又坚固。不过,有一点是错误的。函数“ removeListener”检查事件数组是否为空,但三进制不正确。它应该说“ if(record.listeners [event] .length!=-1)”。
JPA


5

例如,您始终可以使用Chrome检查器手动检查EventListener是否存在。在“元素”选项卡中,您具有传统的“样式”子选项卡,并且靠近该子选项卡:“事件监听器”。这将为您提供所有EventListener及其链接元素的列表。


5

这种方法不存在似乎很奇怪。现在是时候添加它了吗?

如果您愿意,可以执行以下操作:

var _addEventListener = EventTarget.prototype.addEventListener;
var _removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.events = {};
EventTarget.prototype.addEventListener = function(name, listener, etc) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    events[name] = [];
  }

  if (events[name].indexOf(listener) == -1) {
    events[name].push(listener);
  }

  _addEventListener(name, listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
  var events = EventTarget.prototype.events;

  if (events[name] != null && events[name].indexOf(listener) != -1) {
    events[name].splice(events[name].indexOf(listener), 1);
  }

  _removeEventListener(name, listener);
};
EventTarget.prototype.hasEventListener = function(name) {
  var events = EventTarget.prototype.events;
  if (events[name] == null) {
    return false;
  }

  return events[name].length;
};

3

这是我用来检查是否存在动态附加的事件侦听器的脚本。我使用jQuery将事件处理程序附加到元素,然后触发该事件(在本例中为'click'事件)。这样,我可以检索和捕获仅在附加了事件处理程序后才存在的事件属性。

var eventHandlerType;

$('#contentDiv').on('click', clickEventHandler).triggerHandler('click');

function clickEventHandler(e) {
    eventHandlerType = e.type;
}

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}

2
您还可以为元素分配一个属性,基本上将其标记为具有事件的元素。
贾斯汀

2

似乎没有跨浏览器功能来搜索在给定元素下注册的事件。

但是,可以使用某些浏览器的开发工具查看元素的回调函数。在尝试确定网页的功能或调试代码时,这很有用。

火狐浏览器

首先,在开发人员工具的“ 检查器”选项卡中查看元素。可以这样做:

  • 在页面上,右键单击要检查的网页上的项目,然后从菜单中选择“检查元素”。
  • 在控制台中,通过使用函数来选择元素,例如document.querySelector,然后单击元素旁边的图标以在“ 检查器”选项卡中进行查看。

如果向该元素注册了任何事件,您将在该元素旁边看到一个包含单词Event的按钮。单击它将允许您查看已向元素注册的事件。单击事件旁边的箭头,可以查看该事件的回调函数。

首先,在开发人员工具的“ 元素”选项卡中查看元素。可以这样做:

  • 在页面上,右键单击要检查的网页上的项目,然后从菜单中选择“检查”
  • 在控制台中,通过使用一个函数来选择元素,例如document.querySelector,右键单击该元素,然后选择“在元素面板中显示”以在“ 检查器”选项卡中查看它。

在显示包含网页元素的树的窗口部分附近,应该有另一部分带有标题为“事件监听器”的选项卡。选择它可以查看已注册到元素的事件。要查看给定事件的代码,请单击其右侧的链接。

在Chrome中,还可以使用getEventListeners函数找到元素的事件。但是,根据我的测试,将多个元素传递给它时,getEventListeners函数不会列出事件。如果要在页面上找到所有具有侦听器的元素并查看这些侦听器的回调函数,则可以在控制台中使用以下代码来执行此操作:

var elems = document.querySelectorAll('*');

for (var i=0; i <= elems.length; i++) {
    var listeners = getEventListeners(elems[i]);

    if (Object.keys(listeners).length < 1) {
        continue;
    }

    console.log(elems[i]);

    for (var j in listeners) {
        console.log('Event: '+j);

        for (var k=0; k < listeners[j].length; k++) {
            console.log(listeners[j][k].listener);
        }
    }
}

如果您知道在给定浏览器或其他浏览器中执行此操作的方法,请编辑此答案。


1

我只是通过尝试查看我的活动是否被发现而发现了这一点。

如果您这样做:

item.onclick

它将返回“ null”

但如果您这样做:

item.hasOwnProperty('onclick')

那就是“ TRUE”

所以我认为,当您使用“ addEventListener”添加事件处理程序时,访问它的唯一方法是通过“ hasOwnProperty”。我希望我知道为什么或如何,但是,经过研究,我还没有找到任何解释。


2
onclick是分开的.addEventListener-它,而影响到的属性.addEventListener
扎克索西耶

1

如果我理解得很好,则只能检查是否已检查了某个侦听器,而不能检查特定地是哪个侦听器。

因此,一些特定的代码将填补处理您的编码流程的空白。一种实用的方法是创建一个state使用变量。例如,附加一个侦听器的检查器,如下所示:

var listenerPresent=false

然后,如果您设置了监听器,则只需更改值即可:

listenerPresent=true

然后在eventListener的回调内部,您可以在内部分配特定功能,并以相同的方式,根据某些状态将对这些功能的访问分配为变量,例如:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true

1

只需在添加事件之前将其删除:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);

这是一个很好的技巧,但是我认为只有在您引用了当前“添加的”事件侦听器之一的功能时,它才能起作用。我认为这显然是标准API的缺点,如果我们可以添加任何事件侦听器,那么还应该可以检查到目前为止已添加了哪些事件侦听器。几乎就像事件监听器当前是“只写变量”一样,这似乎是一个晦涩的概念。
Panu Logic

0

我刚刚编写了一个脚本,可以让您实现这一目标。它为您提供了两个全局功能:hasEvent(Node elm, String event)以及getEvents(Node elm)可以使用的全局功能。请注意,它会修改EventTarget原型方法add/RemoveEventListener,并且不适用于通过HTML标记或Javascript语法添加的事件elm.on_event = ...

GitHub上的更多信息

现场演示

脚本:

var hasEvent,getEvents;!function(){function b(a,b,c){c?a.dataset.events+=","+b:a.dataset.events=a.dataset.events.replace(new RegExp(b),"")}function c(a,c){var d=EventTarget.prototype[a+"EventListener"];return function(a,e,f,g,h){this.dataset.events||(this.dataset.events="");var i=hasEvent(this,a);return c&&i||!c&&!i?(h&&h(),!1):(d.call(this,a,e,f),b(this,a,c),g&&g(),!0)}}hasEvent=function(a,b){var c=a.dataset.events;return c?new RegExp(b).test(c):!1},getEvents=function(a){return a.dataset.events.replace(/(^,+)|(,+$)/g,"").split(",").filter(function(a){return""!==a})},EventTarget.prototype.addEventListener=c("add",!0),EventTarget.prototype.removeEventListener=c("remove",!1)}();

-1

从理论上讲,您可以搭载addEventListener和removeEventListener来添加删除标记到'this'对象。丑陋,我还没有测试...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.