Ouvinte de evento de JavaScript JavaScript na classe

220

Atualmente, estou tentando escrever JavaScript para obter o atributo da classe que foi clicada. Eu sei que, para fazer isso da maneira correta, devo usar um ouvinte de evento. Meu código é o seguinte:

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

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

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

Eu esperava receber uma caixa de alerta toda vez que clicar em uma das classes para me informar o atributo, mas infelizmente isso não funciona. Alguém pode ajudar por favor?

( Observação - eu posso fazer isso facilmente, jQuerymas NÃO gostaria de usá-lo )

30secondstosam
fonte
2
Há um problema com o código que está adicionando o ouvinte de evento. addEventListener usa o nome do evento ('clique'), referência à função (não o resultado da função como é agora, chamando myFunction () com parens) e um sinalizador para indicar que o evento está piscando. A chamada addEventListener deve se parecer com: elem.addEventListener ('click', myFunction, false) e classname é um tipo NodeList. Precisa fazer um loop sobre todos os elementos e anexar o ouvinte a cada um na lista.
Joey Guerra

Respostas:

373

Isso deve funcionar. getElementsByClassNameretorna um objeto Array-like array (consulte editar) dos elementos que correspondem aos critérios.

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

O jQuery faz a parte em loop para você, o que você precisa fazer em JavaScript simples.

Se você tiver suporte para ES6, poderá substituir sua última linha por:

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

Nota: Navegadores mais antigos (como IE6, IE7, IE8) não suportam getElementsByClassNamee, portanto, retornam undefined.


EDIT: Correção

getElementsByClassNamenão retorna uma matriz, mas uma coleção HTMLC na maioria ou uma NodeList em alguns navegadores ( ref Mozilla ). Esses dois tipos são do tipo Array (o que significa que eles têm uma propriedade length e os objetos podem ser acessados ​​por meio de seu índice), mas não são estritamente um Array ou herdados de um Array. (ou seja, outros métodos que podem ser executados em uma matriz não podem ser executados nesses tipos)

Agradeço ao usuário @ Nemo por apontar isso e fazer com que eu entenda para entender completamente.

Anudeep Bulla
fonte
6
Isso funciona perfeitamente. Obrigado. Na verdade, eu não sabia que o jQuery fazia o loop. Grande ajuda Anudeep. Aqui está a sua resposta de trabalho: jsfiddle.net/LWda3/2
30secondstosam
2
document.getElementsByClassNamena verdade sempre retornar um array, mesmo que apenas um elemento corresponde aos critérios
Guilherme Sehn
Cuidado, embora o primeiro elemento da matriz seja todos os elementos dom. Então comece seu loop com 1
Vishal Sakaria
11
stackoverflow.com/a/13258908/1333493 "document.getElementsByClassName não retorna uma matriz. Retorna uma lista de nós que é atravessada como um arquivo XML."
Nemo
9
Array.from()possui um segundo parâmetro, que é uma função de mapa, para que o acima (assumindo suporte ao es6) possa ser gravado Array.from(classname, c => c.addEventListener('click', myFunction));.
precisa saber é o seguinte
12

* Isso foi editado para permitir que filhos da classe de destino acionem os eventos. Veja a parte inferior da resposta para obter detalhes. *

Uma resposta alternativa para adicionar um ouvinte de evento a uma classe em que itens são frequentemente adicionados e removidos. Isso é inspirado na onfunção do jQuery, na qual você pode passar um seletor para um elemento filho que o evento está ouvindo.

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

Fiddle: https://jsfiddle.net/u6oje7af/94/

Isso escutará cliques nos filhos do baseelemento e, se o destino de um clique tiver um pai correspondente ao seletor, o evento da classe será tratado. Você pode adicionar e remover elementos como desejar, sem precisar adicionar mais ouvintes de clique aos elementos individuais. Isso irá capturá-los todos, mesmo para os elementos adicionados após a adição deste ouvinte, assim como a funcionalidade do jQuery (que eu imagino ser um pouco semelhante ao fundo).

Isso depende da propagação dos eventos; portanto, se você estiver stopPropagationno evento em outro lugar, isso pode não funcionar. Além disso, a closestfunção tem alguns problemas de compatibilidade com o IE aparentemente (o que não acontece?).

Isso pode ser transformado em uma função se você precisar executar esse tipo de ação ouvindo 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);
    }
  });
}

=========================================
EDIT: Esta postagem originalmente usou a matchesfunção para Elementos DOM no destino do evento, mas isso restringiu os destinos dos eventos apenas à classe direta. Foi atualizada para usar a closestfunção, permitindo que eventos em filhos da classe desejada também acionem os eventos. O matchescódigo original pode ser encontrado no violino original: https://jsfiddle.net/u6oje7af/23/

obermillerk
fonte
3

Você pode usar o código abaixo:

document.body.addEventListener('click', function (evt) {
    if (evt.target.className === 'databox') {
        alert(this)
    }
}, false);
Rajat kumar
fonte
Vou ter que tentar, é uma abordagem única que pode ser mais fácil de manter e ter um desempenho melhor do que fazer um loop!
Cliff Helsel 02/02/19
4
se o elemento tiver várias classes, você teria que verificar o valor correto nesses campos, por exemplo. classes e ordem. i prefiro ir paraevt.target.classList.contains('databox')
honk31
8
Isso me parece extremamente ineficiente. Cada evento único no corpo passaria por essa função. O que é tão difícil de manter sobre um loop?
28719 JosefAssad
Isso é muito ineficiente, você analisaria cada elemento como o @JosefAssad disse.
Nullwriter 02/09/19
3

Com o JavaScript moderno, isso pode ser feito assim:

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. Obtém todos os elementos pelo nome da classe
  2. Loops em todos os elementos usando forEach
  3. Anexar um ouvinte de evento em cada elemento
  4. Usa event.targetpara recuperar mais informações para um elemento específico
V. Sambor
fonte