Escucha de eventos de clic de JavaScript en clase

220

Actualmente estoy tratando de escribir JavaScript para obtener el atributo de la clase en la que se ha hecho clic. Sé que para hacer esto de la manera correcta, debería usar un detector de eventos. Mi código es el siguiente:

var classname = document.getElementsByClassName("classname");

var myFunction = function() {
    var attribute = this.getAttribute("data-myattribute");
    alert(attribute);
};

classname.addEventListener('click', myFunction(), false);

Esperaba recibir un cuadro de alerta cada vez que hacía clic en una de las clases para decirme el atributo, pero desafortunadamente esto no funciona. ¿Alguien puede ayudar por favor?

( Nota : puedo hacer esto con bastante facilidad jQuerypero NO me gustaría usarlo )

30secondstosam
fuente
2
Hay un problema con el código que agrega el detector de eventos. addEventListener toma el nombre del evento ('clic'), la referencia a la función (no el resultado de la función como lo es ahora al llamar a myFunction () con parens) y una bandera para indicar el burbujeo del evento. La llamada addEventListener debería verse así: elem.addEventListener ('click', myFunction, false) y classname es un tipo NodeList. Necesita recorrer todos los elementos y adjuntar el oyente a cada uno de la lista.
Joey Guerra

Respuestas:

373

Esto debería funcionar. getElementsByClassNamedevuelve un objeto tipo matriz de matriz (ver edición) de los elementos que coinciden con los criterios.

var elements = document.getElementsByClassName("classname");

var myFunction = function() {
    var attribute = this.getAttribute("data-myattribute");
    alert(attribute);
};

for (var i = 0; i < elements.length; i++) {
    elements[i].addEventListener('click', myFunction, false);
}

jQuery hace la parte de bucle por usted, que debe hacer en JavaScript simple.

Si tiene soporte para ES6 , puede reemplazar su última línea con:

    Array.from(elements).forEach(function(element) {
      element.addEventListener('click', myFunction);
    });

Nota: los navegadores más antiguos (como IE6, IE7, IE8) no son compatibles, getElementsByClassNamepor lo que regresan undefined.


EDITAR: Corrección

getElementsByClassNameno devuelve una matriz, sino una colección HTMLC en la mayoría, o una NodeList en algunos navegadores ( referencia de Mozilla ). Ambos tipos son Array-Like, (lo que significa que tienen una propiedad de longitud y se puede acceder a los objetos a través de su índice), pero no son estrictamente un Array o se heredan de un Array. (lo que significa que otros métodos que se pueden realizar en una matriz no se pueden realizar en estos tipos)

Gracias al usuario @ Nemo por señalar esto y hacerme profundizar para comprenderlo completamente.

Anudeep Bulla
fuente
66
Esto funciona a la perfección. Gracias. En realidad no me di cuenta de que jQuery hizo el bucle. Gran ayuda Anudeep. Aquí está su respuesta de trabajo: jsfiddle.net/LWda3/2
30secondstosam
2
document.getElementsByClassNameen realidad siempre devuelve una matriz, incluso si solo un elemento coincide con los criterios
Guilherme Sehn
Cuidado, aunque el primer elemento de la matriz son todos los elementos dom. Así que comienza tu ciclo for con 1
Vishal Sakaria
11
stackoverflow.com/a/13258908/1333493 "document.getElementsByClassName no devuelve una matriz. Devuelve una lista de nodos que se atraviesa como un archivo XML".
Nemo
99
Array.from()tiene un segundo parámetro que es una función de mapa, por lo que se podría escribir lo anterior (suponiendo que es6 support) Array.from(classname, c => c.addEventListener('click', myFunction));.
Kilmazing
12

* Esto fue editado para permitir que los niños de la clase objetivo activen los eventos. Vea la parte inferior de la respuesta para más detalles. * *

Una respuesta alternativa para agregar un detector de eventos a una clase donde los elementos se agregan y eliminan con frecuencia. Esto está inspirado en la onfunción de jQuery, donde puede pasar un selector para un elemento secundario que el evento está escuchando.

var base = document.querySelector('#base'); // the container for the variable content
var selector = '.card'; // any css selector for children

base.addEventListener('click', function(event) {
  // find the closest parent of the event target that
  // matches the selector
  var closest = event.target.closest(selector);
  if (closest && base.contains(closest)) {
    // handle class event
  }
});

Violín: https://jsfiddle.net/u6oje7af/94/

Esto escuchará los clics en los baseelementos secundarios del elemento y si el objetivo de un clic tiene un elemento primario que coincida con el selector, se manejará el evento de clase. Puede agregar y eliminar elementos como desee sin tener que agregar más oyentes de clics a los elementos individuales. Esto los atrapará a todos incluso para los elementos agregados después de que se agregó este oyente, al igual que la funcionalidad jQuery (que imagino que es algo similar bajo el capó).

Esto depende de los eventos que se propagan, por lo que si está stopPropagationen el evento en otro lugar, esto puede no funcionar. Además, la closestfunción tiene algunos problemas de compatibilidad con IE aparentemente (¿qué no?).

Esto podría convertirse en una función si necesita realizar este tipo de acción escuchando repetidamente, como

function addChildEventListener(base, eventName, selector, handler) {
  base.addEventListener(eventName, function(event) {
    var closest = event.target.closest(selector);
    if (closest && base.contains(closest)) {
      // passes the event to the handler and sets `this`
      // in the handler as the closest parent matching the
      // selector from the target element of the event
      handler.call(closest, event);
    }
  });
}

=========================================
EDITAR: Esta publicación originalmente utilizó la matchesfunción para Elementos DOM en el destino del evento, pero esto restringió los destinos de los eventos solo a la clase directa. Se ha actualizado para usar la closestfunción en su lugar, permitiendo que los eventos en los hijos de la clase deseada también activen los eventos. El matchescódigo original se puede encontrar en el violín original: https://jsfiddle.net/u6oje7af/23/

Obermillerk
fuente
3

Puedes usar el siguiente código:

document.body.addEventListener('click', function (evt) {
    if (evt.target.className === 'databox') {
        alert(this)
    }
}, false);
Rajat kumar
fuente
Tendré que probar esto, es un enfoque único que en realidad podría ser más fácil de mantener y realizar mejor que el bucle.
Cliff Helsel
44
si el elemento tiene varias clases, deberá verificar el valor correcto en esos campos, por ejemplo. clases y orden. Prefiero ir aevt.target.classList.contains('databox')
honk31
8
Esto me parece extremadamente ineficiente. Cada evento en el cuerpo pasaría por esta función. ¿Qué es tan difícil de mantener sobre un bucle?
JosefAssad
Esto es muy ineficiente, revisarías cada elemento como dijo @JosefAssad.
nullwriter
3

Con JavaScript moderno se puede hacer así:

const divs = document.querySelectorAll('.a');

divs.forEach(el => el.addEventListener('click', event => {
  console.log(event.target.classList[1]);
}));
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Example</title>
  <style>
    .a {
      background-color:red;
      height: 33px;
      display: flex;
      align-items: center;
      margin-bottom: 10px;
      cursor: pointer;
    }
    
    .b {
      background-color:#00AA00;
      height: 50px;
      display: flex;
      align-items: center;
      margin-bottom: 10px;
    }
  </style>
</head>
<body>
  <div class="a a-1">1</div>
  <div class="b">2</div>
  <div class="a a-2">11</div>
</body>
</html>

  1. Obtiene todos los elementos por nombre de clase
  2. Recorre todos los elementos con el uso de forEach
  3. Adjunte un detector de eventos en cada elemento
  4. Utiliza event.targetpara recuperar más información para un elemento específico
V. Sambor
fuente