170

这是我的问题:是否有可能检查动态附加的事件侦听器是否存在?或者如何检查 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”总是报告错误。有人可以帮忙吗?

4

20 回答 20

89

我做了这样的事情:

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');
    });
}

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

于 2017-11-16T12:56:24.943 回答
65

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

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

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

onclick然后,您可以通过返回!!elem.onclick(或类似的东西)来测试是否附加了事件侦听器。

于 2012-07-12T15:58:14.463 回答
28

我要做的是在你的函数之外创建一个布尔值,它以 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);
 }
}
于 2013-01-16T18:10:37.327 回答
12

可能重复:检查元素上是否有事件监听器。没有 jQuery 请在那里找到我的答案。

基本上这里是 Chromium (Chrome) 浏览器的窍门:

getEventListeners(document.querySelector('your-element-selector'));
于 2018-07-04T06:52:26.677 回答
8

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);
}

第三步:根据您的问题的实际需求,您将需要以下函数来检查元素是否已添加指定事件的事件侦听器。

/* 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);
}

片段:

window.onload = function () {
  var
    /* Cache the test element. */
    element = document.getElementById("test"),

    /* Create an event listener. */
    listener = function (e) {
      console.log(e.type + "triggered!");
    };

  /* Insert the listener to the element. */
  insertListener(element, "mouseover", listener);

  /* Log whether the listener exists. */
  console.log(listenerExists(element, "mouseover", listener));

  /* Remove the listener from the element. */
  removeListener(element, "mouseover", listener);

  /* Log whether the listener exists. */
  console.log(listenerExists(element, "mouseover", listener));
};
<!-- Include the Custom Event Storage file -->
<script src = "https://cdn.rawgit.com/angelpolitis/custom-event-storage/master/main.js"></script>

<!-- A Test HTML element -->
<div id = "test" style = "background:#000; height:50px; width: 50px"></div>


尽管自 OP 发布问题以来已经过去了 5 年多,但我相信将来偶然发现它的人将从这个答案中受益,因此请随时提出建议或改进。

于 2017-11-21T13:42:48.240 回答
8

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

如果你想你可以像下面这样:

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;
};
于 2019-10-28T22:20:10.500 回答
5

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

于 2016-05-12T15:12:25.193 回答
3

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

var eventHandlerType;

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

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

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}
于 2014-05-16T16:10:03.470 回答
3

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

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

火狐

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

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

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

铬合金

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

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

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

在 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);
        }
    }
}

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

于 2018-09-29T17:40:46.117 回答
2

// 只需要这个并在匿名函数之外分配属性

const element = document.getElementById('div');
if (element && !element.hasAttribute('listenerOnClick')) {
    element.addEventListener('click', function () {
        const elementClicked = this;
        // fnDoAnything(this); // maybe call a function with the elementClicked...
        console.log('event has been attached');
    });
}
element.setAttribute('listenerOnClick', 'true');
于 2021-09-15T07:59:17.250 回答
1

我只是通过尝试查看我的活动是否已附加才发现这一点....

如果你这样做:

item.onclick

它将返回“空”

但如果你这样做:

item.hasOwnProperty('onclick')

那么它是“真”

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

于 2015-04-29T16:13:31.767 回答
1

如果我理解得很好,您只能检查是否已检查听众,但不能检查具体是哪个听众。

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

var listenerPresent=false

那么如果你设置了一个监听器,只需更改值:

listenerPresent=true

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

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
于 2019-05-29T21:00:14.267 回答
1

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

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
于 2019-10-15T09:06:25.917 回答
1

我编写了一个 Chrome 扩展程序,它需要确定页面上的哪些元素响应了点击。我是这样做的:

(1) 在 manifest.json 中,将“run_at”属性设置为“document_start”。(我们需要在页面开始运行之前注入一个脚本。)

(2) 在您的内容脚本中,添加一些代码以将脚本注入页面,该脚本将覆盖 EventTarget.prototype.addEventListener 以标记所有动态分配的点击侦听器的元素:

let flagClickHandledElements = function() {
    let oldEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(event_name, handler_func) {
        if (event_name === 'click') {
            if (this.setAttribute) {
                this.setAttribute('data-has_click_handler', true);
            }
        }
        if (oldEventListener)
            oldEventListener(event_name, handler_func);
    }
}

function injectScript(func) {
    let codeString = '(' + func + ')();';
    let script = document.createElement('script');
    script.textContent = codeString;
    (document.head||document.documentElement).appendChild(script);
}

injectScript(flagClickHandledElements);

(3) 在 manifest.json 中将“webNavigation”添加到您的“权限”列表中

(4) 在后台脚本中添加一些代码,以在页面加载完成时通知内容脚本:

function onPageDoneLoading(details)
{
    chrome.tabs.sendMessage(details.tabId, {"action": "doneloading"});
}

chrome.webNavigation.onCompleted.addListener(onPageDoneLoading);

(5) 当页面完成加载时,让您的内容脚本将另一个脚本注入页面,扫描页面上的所有元素以查找旧式“onclick”处理程序:

let gatherOldStyleClickHandledElements = function() {
    let all_elements = document.getElementsByTagName("*");
    for (let i = 0; i < all_elements.length; i++) {
        let el = all_elements[i];
        if (el.setAttribute && el.onclick) {
            el.setAttribute('data-has_click_handler', true);
        }
    }
}

function onReceiveMessage(request) {
    if (request.action === 'doneloading') {
        injectScript(gatherOldStyleClickHandledElements);
    } else {
        console.log('Unrecognized message');
    }

    return Promise.resolve("Dummy response to keep the console quiet");
}

(6) 最后,你可以在你的内容脚本中测试一个元素,看看它是否有这样的点击处理程序:

if (el.getAttribute('data-has_click_handler'))
    console.log('yep, this element has a click handler');
于 2021-04-15T21:16:35.530 回答
1
var listenerPaste = pasteAreaElm.hasOwnProperty('listenerPaste');
if (!listenerPaste) {
    pasteAreaElm.addEventListener("paste", onPasteEvent, false);
    pasteAreaElm.listenerPaste = true;
}
于 2021-07-21T16:37:24.930 回答
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)}();
于 2015-06-10T10:33:53.943 回答
0

我通常将一个类附加到元素然后检查该类是否存在,如下所示:

let element = document.getElementsById("someElement");

if(!element.classList.contains('attached-listener'))
   element.addEventListener("click", this.itemClicked);

element.classList.add('attached-listener');
于 2020-12-25T19:44:21.450 回答
0

像这样的东西应该有助于文件:

var listeners = window.getEventListeners(document);
Object.keys(listeners).forEach(event => {
    console.log(event, listeners[event]);
});

或使用选择器:

getAllEventListeners = function(el) {
 var allListeners = {}, listeners;

 while(el) {
  listeners = getEventListeners(el);

  for(event in listeners) {
   allListeners[event] = allListeners[event] || [];
   allListeners[event].push({listener: listeners[event], element: el});  
  }

  el = el.parentNode;
 }

 return allListeners;
}
于 2021-03-29T19:24:30.780 回答
0

2022年更新:

我在 TypeScript 中编写了实用程序方法来根据这个答案附加和分离事件,但是一个更正的答案。希望有人觉得它有帮助。

export const attachEvent = (
  element: Element,
  eventName: string,
  callback: () => void
) => {
  if (element && eventName && element.getAttribute('listener') !== 'true') {
    element.setAttribute('listener', 'true');
    element.addEventListener(eventName, () => {
      callback();
    });
  }
};

export const detachEvent = (
  element: Element,
  eventName: string,
  callback: () => void
) => {
  if (eventName && element) {
    element.removeEventListener(eventName, callback);
  }
};

像这样导入并在任何地方使用它

  attachEvent(domElement, 'click', this.myfunction.bind(this));
  detachEvent(domElement, 'click', this.myfunction);
于 2022-02-24T05:46:49.193 回答
-1

理论上,您可以搭载 addEventListener 和 removeEventListener 将删除标志添加到“this”对象。丑陋的,我没有测试...

于 2018-05-10T03:06:44.477 回答