220

Here is my problem: is it somehow possible to check for the existence of a dynamically attached event listener? Or how can I check the status of the "onclick" (?) property in the DOM? I have searched the internet just like Stack Overflow for a solution, but no luck. Here is my html:

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

Then in Javascript I attach a dynamically created event listener to the 2nd link:

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

The code runs well, but all my attempts to detect that attached listener fail:

// 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 is here. If you click "Add onclick for 2" and then "[link 2]", the event fires well, but the "Test link 2" always reports false. Can somebody help?

Radllaufer
  • 187
  • 1
  • 2
  • 10
Stano
  • 8,749
  • 6
  • 30
  • 44
  • 2
    I'm sorry to say, but it is impossible to get the event bindings using your current method: http://stackoverflow.com/questions/5296858/is-there-a-way-to-get-all-event-listener-bindings-in-javascript – Ivan Jul 12 '12 at 15:53
  • 3
    you can do it using chrome dev tool: http://stackoverflow.com/a/41137585/863115 – Alfian Busyro Feb 02 '17 at 15:47
  • 1
    You can do it by creating your own storage that keeps track of the listeners. See [my answer](https://stackoverflow.com/a/47414605/6313073) for more. – Angel Politis Nov 28 '17 at 09:13
  • 1
    If the objective is to prevent an event that has already been added from being added again then the right [answer is here](https://stackoverflow.com/a/47337711/8182578). Basically, if you use a named function instead of an anonymous one, duplicate identical event listeners will be discarded and you don't need to worry about them. – Syknapse Oct 23 '19 at 09:00
  • 1
    If you're concerned about having duplicate listeners then remove the current event listener before you add it again. It's not perfect but it's simple. – Jacksonkr Nov 13 '19 at 18:21

23 Answers23

121

I did something like that:

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

Creating a special attribute for an element when the listener is attached and then checking if it exists.

Radllaufer
  • 187
  • 1
  • 2
  • 10
Karol Grabowski
  • 1,321
  • 1
  • 9
  • 4
  • 15
    This is the approach I have used in the past. My recommendation would be to use a very specific syntax structure. IE instead of "listener", use the event itself. So "data-event-click". This provides some flexibility in the event you're looking to do more than one event, and keeps things a bit more readable. – conrad10781 Jan 25 '18 at 15:38
  • 2
    This with @conrad10781 's addition seems like the most reliable and simplest way. If for any reason the element is rerenderd and the event listener gets disconnected, this attribute will be reset as well. – Arye Eidelman Jun 25 '19 at 17:15
  • 24
    I may be going daft (74 upvotes on this) but I'm thinking that `elementClicked.setAttribute('listener', 'true');` should be added outside the `addEventListender` function - otherwise it is only added when the event is triggered and therefore doesn't actually allow you to check the event listener is attached reliably. – Dazed Nov 15 '21 at 15:03
  • At time of posting this is top voted, but (as above comment) the logic is a little off. A better alternative is below (https://stackoverflow.com/a/69189193/1265200) – Dazed Nov 23 '21 at 10:53
77

There is no way to check whether dynamically attached event listeners exist or not.

The only way you can see if an event listener is attached is by attaching event listeners like this:

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

You can then test if an event listener was attached to onclick by returning !!elem.onclick (or something similar).

Ivan
  • 10,052
  • 12
  • 47
  • 78
33

What I would do is create a Boolean outside your function that starts out as FALSE and gets set to TRUE when you attach the event. This would serve as some sort of flag for you before you attach the event again. Here's an example of the idea.

// 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);
 }
}
John
  • 1
  • 13
  • 98
  • 177
Cesar
  • 2,229
  • 1
  • 22
  • 20
  • 1
    This requires you to change the code of the attached event to keep track of whether it is attached (similar to [this](https://stackoverflow.com/a/47330239/5267751) or [this answer](https://stackoverflow.com/a/47414605/5267751)). It won't work if you don't control the code. – user202729 Nov 20 '18 at 05:49
  • 1
    In my usecase the user might have the same page open with different tabs. This only works if the event will only ever be attached in one place in one tab. The boolean can't really be kept in sync with whether or not the event is attached or not because we're just guessing that it's not when this loads – ceckenrode Apr 16 '21 at 21:54
16

Possible duplicate: Check if an element has event listener on it. No jQuery Please find my answer there.

Basically here is the trick for Chromium (Chrome) browser:

getEventListeners(document.querySelector('your-element-selector'));
Alfian Busyro
  • 2,295
  • 2
  • 17
  • 32
8

tl;dr: No, you cannot do this in any natively supported way.


The only way I know to achieve this would be to create a custom storage object where you keep a record of the listeners added. Something along the following lines:

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

Step 1: First, you will need a function that can traverse the storage object and return the record of an element given the element (or 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;
}

Step 2: Then, you will need a function that can add an event listener but also insert the listener to the storage object.

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

Step 3: As regards the actual requirement of your question, you will need the following function to check whether an element has been added an event listener for a specified event.

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

Step 4: Finally, you will need a function that can delete a listener from the storage object.

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

Snippet:

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>

Although more than 5 years have passed since the OP posted the question, I believe people who stumble upon it in the future will benefit from this answer, so feel free to make suggestions or improvements to it.

Angel Politis
  • 10,955
  • 14
  • 48
  • 66
  • 1
    Thank you for this solution, it's straightforward and sturdy. One remark though, there is one little mistake. The function "removeListener" checks if the event array is empty or not but the ternary is incorrect. It should say "if (record.listeners[event].length!=-1)". – JPA Jan 23 '20 at 14:27
8

It seems odd that this method doesn't exist. Is it time to add it finally?

If you wanted to you could something like the following:

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;
};
Caleb Taylor
  • 2,670
  • 2
  • 21
  • 31
1.21 gigawatts
  • 16,517
  • 32
  • 123
  • 231
5

You could always check manually if your EventListener exist using Chrome inspector for example. In Element tab you have the traditional "Styles" subtab and close to it another one : "Event Listeners". Which will give you the list of all EventListeners with their linked elements.

Paul Leclerc
  • 1,117
  • 1
  • 14
  • 18
4

There doesn't appear to be a cross browser function that searches for events registered under a given element.

However, it is possible to view the call back functions for elements in some browsers using their development tools. This can be useful when attempting to determine how a web page functions or for debugging code.

Firefox

First, view the element in the Inspector tab within the developer tools. This can be done:

  • On the page by right clicking on the item on the web page that you want to inspect and selecting "Inspect Element" from the menu.
  • Within the console by using a function to select the element, such as document.querySelector, and then clicking the icon beside the element to view it in the Inspector tab.

If any events were registered to the element, you will see a button containing the word Event beside the element. Clicking it will allow you to see the events that have been registered with the element. Clicking the arrow beside an event allows you to view the callback function for it.

Chrome

First, view the element in the Elements tab within the developer tools. This can be done:

  • On the page by right clicking on the item on the web page that you want to inspect and selecting "Inspect" from the menu
  • Within the console by using a function to select the element, such as document.querySelector, right clicking the the element, and selecting "Reveal in Elements panel" to view it in the Inspector tab.

Near the section of the window that shows the tree containing the web page elements, there should be another section with a tab entitled "Event Listeners". Select it to see events that were registered to the element. To see the code for a given event, click the link to the right of it.

In Chrome, events for an element can also be found using the getEventListeners function. However, based on my tests, the getEventListeners function doesn't list events when multiple elements are passed to it. If you want to find all of the elements on the page that have listeners and view the callback functions for those listeners, you can use the following code in the console to do this:

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

Please edit this answer if you know of ways to do this in the given browsers or in other browsers.

Dave F
  • 1,837
  • 15
  • 20
4

2022 update:

I wrote utility methods in TypeScript to attach and detach events based on this answer but a corrected one. Hope someone finds it helpful.

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

Import it & Use it anywhere like this

  attachEvent(domElement, 'click', this.myfunction.bind(this));
  detachEvent(domElement, 'click', this.myfunction);
minigeek
  • 2,766
  • 1
  • 25
  • 35
  • 2
    this prevents adding listeners of different type, e.g., mouseenter/leave, so you might need to set a 'listener'+eventName attribute instead of just 'listener' – Evgeny Apr 20 '23 at 11:08
3

Here's a script I used to check for the existence of a dynamically attached event listener. I used jQuery to attach an event handler to an element, then trigger that event (in this case the 'click' event). This way I can retrieve and capture event properties that will only exist if the event handler is attached.

var eventHandlerType;

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

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

if (eventHandlerType === 'click') {
    console.log('EventHandler "click" has been applied');
}
dsmith63
  • 199
  • 4
  • 3
    You could also assign a property to the element your element basically marking it as element that has a event attached to it; – Justin Sep 27 '14 at 16:29
3

// Just take this and assign the attribute outside of the anonymous function

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');
denkdirnix
  • 39
  • 1
2

Just remove the event before adding it :

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoize
  • 2,124
  • 3
  • 23
  • 44
  • 3
    That's a good trick, but I assume it works only if you have a ref to the function that is one of the currently "added" event-listeners. I think this is clearly a shortcoming of the standard API, if we can add any event-listener, it should also be possible to check what event-listeners have been so far added. It is almost like event-listeners currently are "write only variables" which seems like an obscure concept. – Panu Logic Jan 15 '20 at 20:33
1

I just found this out by trying to see if my event was attached....

if you do :

item.onclick

it will return "null"

but if you do:

item.hasOwnProperty('onclick')

then it is "TRUE"

so I think that when you use "addEventListener" to add event handlers, the only way to access it is through "hasOwnProperty". I wish I knew why or how but alas, after researching, I haven't found an explanation.

carinlynchin
  • 389
  • 1
  • 3
  • 13
1

If I understand well you can only check if a listener has been checked but not which listener is presenter specifically.

So some ad hoc code would fill the gap to handle your coding flow. A practical method would be to create a state using variables. For example, attach a listener's checker as following:

var listenerPresent=false

then if you set a listener just change the value:

listenerPresent=true

then inside your eventListener 's callback you can assign specific functionalities inside and in this same way, distribute the access to functionalities depending of some state as variable for example:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
Webwoman
  • 10,196
  • 12
  • 43
  • 87
1

I wrote a Chrome extension that needed to determine which elements on the page responded to clicks. Here's how I did it:

(1) In manifest.json, set the "run_at" attribute to "document_start". (We need to inject a script before the page begins running.)

(2) In your content script, add a little code to inject a script into the page that will override EventTarget.prototype.addEventListener to flag all the elements that are dynamically assigned click listeners:

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) Add "webNavigation" to your "permissions" list in manifest.json

(4) Add some code to your background script to notify the content script when the page is done loading:

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

chrome.webNavigation.onCompleted.addListener(onPageDoneLoading);

(5) When the page finishes loading make your content script inject another script into the page that scans all the elements on the page for old-style "onclick" handlers:

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) Finally, you can test an element in your content script to see if it has a click handler like this:

if (el.getAttribute('data-has_click_handler'))
    console.log('yep, this element has a click handler');
Mike Gashler
  • 609
  • 7
  • 12
1
var listenerPaste = pasteAreaElm.hasOwnProperty('listenerPaste');
if (!listenerPaste) {
    pasteAreaElm.addEventListener("paste", onPasteEvent, false);
    pasteAreaElm.listenerPaste = true;
}
zaerymoghaddam
  • 3,037
  • 1
  • 27
  • 33
rom m
  • 91
  • 1
  • 3
1

It is possible to list all event listeners in JavaScript. You just have to hack the prototype's method of the HTML elements (before adding the listeners).

This solution will work only on Google Chrome or Chromium-based browsers

  1. Right-click on the element and choose “inspect” to open the Chrome developer tools.
  2. Once the dev tools are open, switch to the “Event Listeners” tab and you will see all the event listeners bound to the element.

enter image description here

Hanna Rose
  • 412
  • 2
  • 9
0

I just wrote a script that lets you achieve this. It gives you two global functions: hasEvent(Node elm, String event) and getEvents(Node elm) which you can utilize. Be aware that it modifies the EventTarget prototype method add/RemoveEventListener, and does not work for events added through HTML markup or javascript syntax of elm.on_event = ...

More info at GitHub

Live Demo

Script:

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)}();
Gaurang Tandon
  • 6,504
  • 11
  • 47
  • 84
0

I usually attach a class to the element then check if the class exist like this:

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

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

element.classList.add('attached-listener');
0

Somethings like this should help for document:

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

Or using selectors:

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

Hello I propose here my method which works in a certain way to avoid ending up with several even listen on the same element of the DOM. My method uses JQUERY but the principle is as follows:

After a particular treatment of your application where you must reproduce a listener on a DOM element such as the click, then we start by deleting the event to avoid having it several times and immediately afterwards we recreate it.

This principle is to be adapted according to the needs. This is just the principle. I show you how to do it in jquery.

line 1 : $(_ELTDOM_).unbind('click');
line 2 : $(_ELTDOM_).bind('click',myFct);

I hope this can help some people by adapting this solution. I insist on the fact that this solution is not necessarily applicable in all circumstances but it already makes it possible to solve problems associated with double, triple... simultaneous processing due to several listens on the same element for example

0

I wrote 3 functions to do this:

var addEvent = function(object, type, callback)
{
    if (object != null && typeof(object) != 'undefined' && object.addEventListener)
    {
        object.isAttached = typeof object.isAttached == "undefined" ? [] : object.isAttached;
        if (!object.isAttached[type])
        {
            object.isAttached[type] = true;
            object.addEventListener(type, callback, false);
        }
    }
};

var removeEvent = function(object, type, callback)
{
    if (object != null && typeof(object) != "undefined" && typeof object.isAttached != "undefined" && object.isAttached[type])
    {
        object.removeEventListener(type, callback, false);
        object.isAttached[type] = false;
    }
};

var hasEvent = function(object, type, callback)
{
    return object != null && typeof(object) != "undefined" && typeof object.isAttached != "undefined" && object.isAttached[type];
};

To use the functions is simple:

function mousemove(e)
{
    console.log("x:" + e.clientX + ", y:" + e.clientY);
}

if (hasEvent(window, "mousemove", mousemove))
    console.log('window has "mousemove" event with "mousemove" callback');
else
    console.log('window does not have "mousemove" event with "mousemove" callback');

addEvent(window, "mousemove", mousemove);

if (hasEvent(window, "mousemove", mousemove))
    console.log('window has "mousemove" event with "mousemove" callback');
else
    console.log('window does not have "mousemove" event with "mousemove" callback');
/*
Output
window does not have "mousemove" event with "mousemove"
window has "mousemove" event with "mousemove" callback
The x and y coordinates of mouse as you move it
*/
Dan Bray
  • 7,242
  • 3
  • 52
  • 70
0
document.addEventListener('CustomEvent', delegate_method, {once:true});
jmoerdyk
  • 5,544
  • 7
  • 38
  • 49
Fabio
  • 11
  • 1
  • 3
    While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Yunnosch Apr 17 '23 at 07:45