¿Cómo comprobar si existe o no un detector de eventos adjunto dinámicamente?

103

Aquí está mi problema: ¿es posible de alguna manera verificar la existencia de un detector de eventos adjunto dinámicamente? ¿O cómo puedo verificar el estado de la propiedad "onclick" (?) En DOM? He buscado una solución en Internet como Stack Overflow, pero no tuve suerte. Aquí está mi html:

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

Luego, en Javascript, adjunto un detector de eventos creado dinámicamente al segundo enlace:

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

El código funciona bien, pero todos mis intentos de detectar ese oyente adjunto fallan:

// 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 está aquí . Si hace clic en "Agregar al hacer clic para 2" y luego en "[vínculo 2]", el evento se dispara bien, pero el "vínculo de prueba 2" siempre informa falso. ¿Alguien puede ayudar?

Stano
fuente
1
Lamento decirlo, pero es imposible obtener los enlaces de eventos usando su método actual: stackoverflow.com/questions/5296858/…
Ivan
1
puede hacerlo usando la herramienta de desarrollo de Chrome: stackoverflow.com/a/41137585/863115
arufian
Puede hacerlo creando su propio almacenamiento que realiza un seguimiento de los oyentes. Vea mi respuesta para más.
Angel Politis
Si el objetivo es evitar que un evento que ya se ha agregado se vuelva a agregar, la respuesta correcta está aquí . Básicamente, si usa una función con nombre en lugar de una anónima, los oyentes de eventos idénticos duplicados se descartarán y no tendrá que preocuparse por ellos.
Syknapse
Si le preocupa tener oyentes duplicados, elimine el oyente de eventos actual antes de volver a agregarlo. No es perfecto pero es simple.
Jacksonkr

Respuestas:

49

No hay forma de comprobar si existen o no detectores de eventos adjuntos dinámicamente.

La única forma de ver si un detector de eventos está adjunto es adjuntando detectores de eventos como este:

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

Luego, puede probar si se adjuntó un detector de eventos al onclickregresar !!elem.onclick(o algo similar).

Iván
fuente
33

Hice algo como eso:

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

Crear un atributo especial para un elemento cuando se adjunta un oyente y luego verificar si existe.

Karol Grabowski
fuente
4
Este es el enfoque que he utilizado en el pasado. Mi recomendación sería utilizar una estructura de sintaxis muy específica. IE en lugar de "escucha", usa el evento en sí. Entonces "datos-evento-clic". Esto proporciona cierta flexibilidad en caso de que desee realizar más de un evento y mantiene las cosas un poco más legibles.
conrad10781
Esto con la adición de @ conrad10781 parece ser la forma más confiable y sencilla. Si por alguna razón el elemento se vuelve a renderizar y el detector de eventos se desconecta, este atributo también se restablecerá.
Arye Eidelman
23

Lo que haría es crear un booleano fuera de su función que comienza como FALSO y se establece en VERDADERO cuando adjunta el evento. Esto le servirá como una especie de bandera antes de adjuntar el evento nuevamente. He aquí un ejemplo de la 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);
 }
}
Cesar
fuente
Esto requiere que cambie el código del evento adjunto para realizar un seguimiento de si está adjunto (similar a esta o esta respuesta ). No funcionará si no controlas el código.
user202729
6

tl; dr : No, no puede hacer esto de ninguna manera compatible de forma nativa.


La única forma que conozco de lograr esto sería crear un objeto de almacenamiento personalizado donde mantenga un registro de los oyentes agregados. Algo en las siguientes líneas:

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

Paso 1: Primero, necesitará una función que pueda atravesar el objeto de almacenamiento y devolver el registro de un elemento dado el elemento (o falso).

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

Paso 2: Luego, necesitará una función que pueda agregar un detector de eventos pero también insertar el detector en el objeto de almacenamiento.

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

Paso 3: en lo que respecta al requisito real de su pregunta, necesitará la siguiente función para verificar si se ha agregado un elemento como detector de eventos para un evento específico.

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

Paso 4: Finalmente, necesitará una función que pueda eliminar un oyente del objeto de almacenamiento.

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

Retazo:


Aunque han pasado más de 5 años desde que el OP publicó la pregunta, creo que las personas que se encuentren con ella en el futuro se beneficiarán de esta respuesta, así que siéntase libre de hacer sugerencias o mejoras. 😊

Ángel Politis
fuente
Gracias por esta solución, es sencilla y robusta. Sin embargo, una observación, hay un pequeño error. La función "removeListener" comprueba si la matriz de eventos está vacía o no, pero el ternario es incorrecto. Debería decir "if (record.listeners [evento] .length! = - 1)".
JPA
5

Siempre puede verificar manualmente si su EventListener existe usando el inspector de Chrome, por ejemplo. En la pestaña Elemento tienes la subpestaña tradicional "Estilos" y cerca de ella otra: "Escuchadores de eventos". Lo que le dará la lista de todos los EventListeners con sus elementos vinculados.

Paul Leclerc
fuente
5

Parece extraño que este método no exista. ¿Es hora de agregarlo finalmente?

Si quisiera, podría hacer algo como lo siguiente:

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;
};
1,21 gigavatios
fuente
3

Aquí hay un script que usé para verificar la existencia de un detector de eventos adjunto dinámicamente. Usé jQuery para adjuntar un controlador de eventos a un elemento y luego activar ese evento (en este caso, el evento 'clic'). De esta manera puedo recuperar y capturar propiedades de eventos que solo existirán si el controlador de eventos está adjunto.

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
fuente
2
También puede asignar una propiedad al elemento su elemento básicamente marcándolo como elemento que tiene un evento adjunto;
Justin
2

No parece haber una función de navegador cruzado que busque eventos registrados en un elemento determinado.

Sin embargo, es posible ver las funciones de devolución de llamada para elementos en algunos navegadores utilizando sus herramientas de desarrollo. Esto puede resultar útil al intentar determinar cómo funciona una página web o para depurar código.

Firefox

Primero, vea el elemento en la pestaña Inspector dentro de las herramientas de desarrollo. Esto puede hacerse:

  • En la página, haga clic derecho en el elemento de la página web que desea inspeccionar y seleccione "Inspeccionar elemento" en el menú.
  • Dentro de la consola, utilice una función para seleccionar el elemento, como document.querySelector , y luego haga clic en el icono junto al elemento para verlo en la pestaña Inspector .

Si se registró algún evento en el elemento, verá un botón que contiene la palabra Evento junto al elemento. Al hacer clic en él, podrá ver los eventos que se han registrado con el elemento. Hacer clic en la flecha al lado de un evento le permite ver la función de devolución de llamada correspondiente.

Cromo

Primero, vea el elemento en la pestaña Elementos dentro de las herramientas de desarrollo. Esto puede hacerse:

  • En la página, haga clic con el botón derecho en el elemento de la página web que desea inspeccionar y seleccione "Inspeccionar" en el menú.
  • Dentro de la consola mediante el uso de una función para seleccionar el elemento, como document.querySelector , haga clic derecho en el elemento y seleccione "Mostrar en el panel Elementos" para verlo en la pestaña Inspector .

Cerca de la sección de la ventana que muestra el árbol que contiene los elementos de la página web, debería haber otra sección con una pestaña titulada "Oyentes de eventos". Selecciónelo para ver los eventos que se registraron en el elemento. Para ver el código de un evento determinado, haga clic en el enlace a la derecha.

En Chrome, los eventos para un elemento también se pueden encontrar usando la función getEventListeners . Sin embargo, según mis pruebas, la función getEventListeners no enumera eventos cuando se le pasan varios elementos. Si desea encontrar todos los elementos en la página que tienen oyentes y ver las funciones de devolución de llamada para esos oyentes, puede usar el siguiente código en la consola para hacer esto:

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

Edite esta respuesta si conoce formas de hacerlo en los navegadores indicados o en otros navegadores.

Dave F
fuente
1

Descubrí esto tratando de ver si mi evento estaba adjunto ...

si lo haces :

item.onclick

devolverá "nulo"

pero si lo haces:

item.hasOwnProperty('onclick')

entonces es "VERDADERO"

así que creo que cuando usa "addEventListener" para agregar controladores de eventos, la única forma de acceder es a través de "hasOwnProperty". Ojalá supiera por qué o cómo, pero lamentablemente, después de investigar, no he encontrado una explicación.

carinlynchin
fuente
2
onclickestá separado de .addEventListener- afecta un atributo mientras .addEventListenerque no
Zach Saucier
1

Si lo entiendo bien, solo puede verificar si se ha verificado un oyente, pero no qué oyente es el presentador específicamente.

Entonces, algún código ad hoc llenaría el vacío para manejar su flujo de codificación. Un método práctico sería crear statevariables de uso. Por ejemplo, adjunte un verificador de escucha de la siguiente manera:

var listenerPresent=false

entonces, si establece un oyente, simplemente cambie el valor:

listenerPresent=true

luego dentro de la callback de tu eventListener puedes asignar funcionalidades específicas dentro y de esta misma manera, distribuir el acceso a funcionalidades dependiendo de algún estado como variable por ejemplo:

accessFirstFunctionality=false
accessSecondFunctionality=true
accessThirdFunctionality=true
Webwoman
fuente
1

Simplemente elimine el evento antes de agregarlo:

document.getElementById('link2').removeEventListener('click', linkclick, false);
document.getElementById('link2').addEventListener('click', linkclick, false);
Entretoizar
fuente
Es un buen truco, pero supongo que funciona sólo si tiene una referencia a la función que es uno de los oyentes de eventos "agregados" actualmente. Creo que esto es claramente una deficiencia de la API estándar, si podemos agregar cualquier detector de eventos, también debería ser posible verificar qué detectores de eventos se han agregado hasta ahora. Es casi como si los oyentes de eventos fueran "variables de solo escritura", lo que parece un concepto oscuro.
Panu Logic
0

Acabo de escribir un guión que te permite lograrlo. Le brinda dos funciones globales: hasEvent(Node elm, String event)y getEvents(Node elm)que puede utilizar. Tenga en cuenta que modifica el EventTargetmétodo de prototipo add/RemoveEventListenery no funciona para eventos agregados a través del marcado HTML o la sintaxis de JavaScript deelm.on_event = ...

Más información en GitHub

Demo en vivo

Guión:

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
fuente
-1

En teoría, podría usar addEventListener y removeEventListener para agregar eliminar una bandera al objeto 'this'. Feo y no he probado ...

kofifus
fuente