Odbiornik zdarzenia kliknięcia JavaScript w klasie

220

Obecnie próbuję napisać JavaScript, aby uzyskać atrybut klikniętej klasy. Wiem, że aby to zrobić poprawnie, powinienem użyć detektora zdarzeń. Mój kod jest następujący:

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

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

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

Spodziewałem się, że za każdym razem, gdy kliknę jedną z klas, pojawi się okno z ostrzeżeniem, aby podać mi atrybut, ale niestety to nie działa. Czy ktoś może pomóc?

( Uwaga - mogę dość łatwo to zrobić, jQueryale NIE chciałbym tego używać )

30secondstosam
źródło
2
Wystąpił problem z kodem, który dodaje detektor zdarzeń. addEventListener pobiera nazwę zdarzenia („kliknięcie”), odwołanie do funkcji (a nie wynik tej funkcji, jaka jest teraz poprzez wywołanie myFunction () ze znakami parens) i flagę wskazującą bąbelkowanie zdarzenia. Wywołanie addEventListener powinno wyglądać następująco: elem.addEventListener („click”, myFunction, false), a nazwa klasy jest typem NodeList. Musisz zapętlić wszystkie elementy i dołączyć detektor do każdego z listy.
Joey Guerra

Odpowiedzi:

373

To powinno działać. getElementsByClassNamezwraca tablicę Obiekt podobny do tablicy (patrz edycja) elementów spełniających kryteria.

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 wykonuje dla ciebie część zapętlającą, którą musisz zrobić w zwykłym JavaScript.

Jeśli masz obsługę ES6 , możesz zastąpić swój ostatni wiersz:

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

Uwaga: starsze przeglądarki (takie jak IE6, IE7, IE8) nie obsługują getElementsByClassNamei dlatego zwracają undefined.


EDYCJA: Korekta

getElementsByClassNamenie zwraca tablicy, ale w większości HTMLCollection lub NodeList w niektórych przeglądarkach ( ref . Mozilla ). Oba typy są podobne do macierzy (co oznacza, że ​​mają właściwość length, a do obiektów można uzyskać dostęp poprzez ich indeks), ale nie są ściśle tablicą ani nie są dziedziczone z macierzy. (co oznacza, że ​​innych metod, które można wykonać na tablicy, nie można wykonać na tych typach)

Dziękuję użytkownikowi @ Nemo za zwrócenie na to uwagi i zachęcenie mnie do pełnego zrozumienia.

Anudeep Bulla
źródło
6
To działa idealnie. Dziękuję Ci. Właściwie nie zdawałem sobie sprawy, że jQuery zrobił pętlę. Wielka pomoc Anudeep. Oto odpowiedź robocza: jsfiddle.net/LWda3/2
30secondstosam
2
document.getElementsByClassNamefaktycznie zawsze zwraca tablicę, nawet jeśli tylko jeden element spełnia kryteria
Guilherme Sehn
Ostrożnie jednak pierwszym elementem tablicy są wszystkie elementy dom. Zacznij więc pętlę for od 1
Vishal Sakaria,
11
stackoverflow.com/a/13258908/1333493 „document.getElementsByClassName nie zwraca tablicy. Zwraca listę węzłów, która jest przechodzona jak plik XML.”
Nemo
9
Array.from()ma drugi parametr, który jest funkcją mapy, więc powyższe (przy założeniu obsługi ES6) można zapisać Array.from(classname, c => c.addEventListener('click', myFunction));.
Kilmazing
12

* Zostało to zmodyfikowane, aby umożliwić dzieciom klasy docelowej wyzwalanie zdarzeń. Szczegóły znajdują się u dołu odpowiedzi. *

Alternatywna odpowiedź na dodanie detektora zdarzeń do klasy, w której elementy są często dodawane i usuwane. Jest to zainspirowane onfunkcją jQuery, w której można przekazać selektor elementu potomnego, którego słucha zdarzenie.

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/

Spowoduje to nasłuchiwanie kliknięć baseelementów podrzędnych elementu, a jeśli element docelowy kliknięcia ma element nadrzędny pasujący do selektora, zdarzenie klasy zostanie obsłużone. Możesz dodawać i usuwać elementy, jak chcesz, bez konieczności dodawania kolejnych detektorów kliknięć do poszczególnych elementów. Spowoduje to złapanie ich wszystkich nawet dla elementów dodanych po dodaniu tego detektora, podobnie jak funkcjonalność jQuery (która, jak sądzę, jest nieco podobna pod maską).

Zależy to od propagowania wydarzeń, więc jeśli zdarzy stopPropagationsię to w innym miejscu, może to nie działać. Ponadto closestfunkcja najwyraźniej ma pewne problemy ze zgodnością z IE (co nie?).

Można to zmienić w funkcję, jeśli trzeba wykonywać tego typu czynności wielokrotnie, np

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

=========================================
EDYCJA: Ten post pierwotnie używał matchesfunkcji Elementy DOM w celu zdarzenia, ale ograniczyło to cele zdarzenia tylko do klasy bezpośredniej. Zostało zaktualizowane, aby closestzamiast tego używało tej funkcji, umożliwiając również wywoływanie zdarzeń na dzieciach pożądanej klasy. Oryginalny matcheskod można znaleźć na oryginalnym skrzypcach: https://jsfiddle.net/u6oje7af/23/

obermillerk
źródło
3

Możesz użyć kodu poniżej:

document.body.addEventListener('click', function (evt) {
    if (evt.target.className === 'databox') {
        alert(this)
    }
}, false);
Rajat Kumar
źródło
Będę musiał spróbować, jest to unikalne podejście, które może być łatwiejsze w utrzymaniu i działać lepiej niż zapętlenie!
Cliff Helsel
4
jeśli element ma wiele klas, musisz sprawdzić poprawną wartość w tych polach, np. zajęcia i porządek. wolałbym wybraćevt.target.classList.contains('databox')
honk31,
8
Wydaje mi się to niezwykle nieefektywne. Każde zdarzenie na ciele przechodzi przez tę funkcję. Co jest tak trudnego do utrzymania w pętli?
JosefAssad
Jest to bardzo nieefektywne, przechodziłbyś przez każdy element, jak powiedział @JosefAssad.
nullwriter,
3

Dzięki nowoczesnemu JavaScriptowi można to zrobić w następujący sposób:

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. Pobiera wszystkie elementy według nazwy klasy
  2. Zapętla wszystkie elementy za pomocą forEach
  3. Dołącz detektor zdarzeń do każdego elementu
  4. Służy event.targetdo pobierania dodatkowych informacji dla określonego elementu
V. Sambor
źródło