addEventListener e il target di riferimento

MarcoGrazia

Utente Attivo
15 Dic 2009
853
21
28
63
Udine
www.stilisticamente.com
Ciao,
una domanda forse un po' da newbie ma necessaria.

Sto cercando di identificare (tramite javascript) tutti i click sulle ancore, ed ho iniziato con un semplice addEventListener() associato ad A.
Mi ha dato errore, ancora.addEventListener() non è una funzione.
Il codice:
JavaScript:
var ancora = document.getElementsByName('a');
ancora.addEventListener('click', function(event) {   //   <---- Punto di errore

});

Ora se ricordo bene, e la pagina https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener mi è d'aiuto, non mi pare di aver fatto errori, ma forse sbaglio. Forse, non devo utilizzare la getElementsByName() ma allora dovrei almeno identificare ogni ancora singolarmente e associare a tutte lo stesso evento.
Come risolvo?

Ah, la pagina ha ancore al suo interno.
 
Ciao, non sei molto lontano dalla soluzione ma ti manca qualche nozione in più da conoscere, qualche procedimento in più da applicare e sopratutto qualche granchio da evitare.

Nozione:
A differenza di getElementById(), il quale restituisce un unico elemento, il metodo getElementsByName(), come vari altri, restituisce una collection NodeList, cioè una sorta di vettore di tutti gli elementi selezionati.

Il metodo addEventListener() può essere applicato al singolo elemento, non ad una collection; da qui l'errore che ricevi, perché la collection NodeList non possiede di fatto tale funzione.

Procedimento:
A tal proposito dovrai eseguire un ciclo su tale vettore per poter "identificare" i singoli elementi e applicare a quel punto il listener. Più o meno come hai supposto tu stesso.

Puoi fare una cosa del genere con un semplice ciclo for:
JavaScript:
var ancore = document.getElementsByName('a');
for(let i = 0; i < ancore.length; i++){
   ancore[i].addEventListener('click', function(event) {
      //... resto del codice
   });
}

Granchio da evitare:
Occhio alle sviste. Tutto funzionerebbe se tu volessi selezionare degli elementi (che siano ancore o no*) ai quali è applicato l'attributo "name" col valore "a":

Esempio:
Codice:
<a href="#" name="a">A</a>
<br>
<input type="checkbox" name="a">
<br>
<span name="a">B</span>
<script>
  var ancore = document.getElementsByName('a');
  for (let i = 0; i < ancore.length; i++) {
    ancore[i].addEventListener('click', function(event) {
      event.preventDefault();
      console.log("Hai cliccato su ", this);
    });
  }
</script>
* generalmente il "name" è supportato solo per particolari elementi, come gli elementi di controllo nei form.

La documentazione, riguardo il metodo getElementsByName(), parla chiaro: "Collection of elements with a given name attribute"

Deduco invece che tu volessi utilizzare il metodo getElementsByTagName(), il che, avrebbe più senso. Anche in questo caso il discorso "collection" resta uguale:
Codice:
<a href="#A">A</a>
<br>
<a href="#B">B</a>
<br>
<a href="#C">C</a>
<script>
  var ancore = document.getElementsByTagName('a');
  for (let i = 0; i < ancore.length; i++) {
    ancore[i].addEventListener('click', function(event) {
      event.preventDefault();
      console.log("Hai cliccato su ", this);
    });
  }
</script>

Chiaramente si può risolvere in vari altri modi.

Spero di aver chiarito qualche perplessità.
Buon proseguimento.
 
  • Like
Reactions: MarcoGrazia
Ciao, in realtà non ho dovuto fare tutto quel che hai fatto tu, ma ho risolto in altro modo.

In realtà so bene la differenza tra getElementById e getElementsByName, solo speravo che addEventListener accettasse una lista di elementi e non il riferimento ad uno e solo uno.

non è così e pazienza, si trova un'altra strada come hai fatto tu :)
A proposito piccolo stupido consiglio, invece di usare name in span, che non è supportato (fonte whatwg https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-span-element) puoi utilizzare dataSet.

Ad ogni modo ho risolto dando document in pasto ad addEventListener e funziona, anzi, è proprio questo che mi aveva portato fuori strada, pensavo se accetta document che si porta con se tutto l'intero documento, strano che non accetta una porzione dello stesso.

Quindi:
JavaScript:
<!DOCTYPE html>
<html lang="it" dir="ltr">
    <head>
        <title>Prova</title>
    </head>
    <body>
        <textarea></textarea>
        <ul>
            <li><a href="#ciccio">Ciccio</a></li>
            <li><a href="#pluto">Pluto</a></li>
            <li><a href="#paperino">Paperino</a></li>
        </ul>
        <script>
            document.addEventListener('click', function(evt) {
                var hash = '';
                switch (evt.currentTarget.activeElement.localName) {
                    case 'a':
                        var hash = evt.currentTarget.activeElement.hash;
                        break;
                    default:
                        evt.preventDefault();
                }
            }, false);
        </script>
    </body>
</html>
In pratica , creato l'evento da associare all'intero document, una volta cliccato su un punto della pagina, se questo è l'ancora, posso estrarne l'hash (o l'url, o il testo o qualsiasi altra parte dell'ancora) oppure, gestire qualsiasi altro elemento della pagina ( per questo ho utilizzato uno switch anche se qui sembra inutile e bastava un semplice selettore if ).

Il preventDefault alla fine mi serve a cancellare l'evento quando non è sull'elemento cercato.

Funziona, non devo ciclare nulla e mi basta un solo gestore di evento.
 

Discussioni simili