Ricerca in JavaScript: getElement*, querySelector*
Esplora e padroneggia il Document Object Model (DOM) con i metodi getElement* e querySelector* per creare applicazioni web interattive e dinamiche.
Prima di poter modificare, spostare o leggere qualsiasi elemento di una pagina, devi trovare l'elemento desiderato. Questo è il primo passo di quasi ogni operazione sul DOM, e JavaScript mette a disposizione due famiglie di strumenti per farlo:
- Metodi legacy
getElement*—getElementById,getElementsByClassName,getElementsByTagName. Sono veloci e restituiscono collezioni live. - Metodi moderni
querySelector*—querySelector,querySelectorAll. Accettano qualsiasi selettore CSS e restituiscono risultati statici.
Questa guida tratta entrambe le famiglie, gli helper matches, closest e contains per verificare e navigare l'albero, e la differenza più importante da non dimenticare: quella tra una collezione live e una statica. Ogni esempio è eseguibile, così puoi vedere il risultato immediatamente.
Una volta trovato un elemento, i passi successivi sono di solito attraversare il DOM per raggiungere gli elementi adiacenti e manipolare il DOM per modificarlo. Per una panoramica più introduttiva, consulta la selezione degli elementi DOM.
Accesso efficiente agli elementi: getElementById
Il metodo getElementById è il modo più rapido e affidabile per accedere a un singolo elemento, poiché un ID deve essere unico all'interno di un documento e i browser indicizzano gli ID internamente. Restituisce l'elemento corrispondente, oppure null se non esiste alcun elemento con quell'ID — quindi controlla sempre che non sia null prima di usare il risultato. Nota che si passa l'ID senza il prefisso # (quello è riservato ai selettori CSS). Nell'esempio seguente, il testo iniziale "Default text" viene sostituito immediatamente.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<head>
<title>getElementById Example</title>
</head>
<body>
<div id="main-content">Default text</div>
<script>
const element = document.getElementById('main-content');
element.innerHTML = "Modified text!"
</script>
</body>
</html>Accesso a più elementi: getElementsByClassName e getElementsByTagName
Quando si selezionano elementi per nome della classe o per nome del tag, si ottiene un HTMLCollection. Questa è una collezione live: si aggiorna automaticamente al variare del DOM. È simile a un array — puoi leggere gli elementi tramite indice (els[0]) e controllare els.length — ma non è un vero array, quindi non dispone di forEach, map o filter. Per iterarla in modo sicuro, convertila prima con Array.from(...) (oppure con lo spread [...els]).
Esempio con getElementsByClassName
Accedi a più elementi con la stessa classe usando getElementsByClassName. In questo esempio abbiamo due elementi div con lo stesso nome di classe. Li modifichiamo entrambi selezionandoli tramite il nome della classe.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<head>
<title>getElementsByClassName Example</title>
</head>
<body>
<div class="info">First Info</div>
<div class="info">Second Info</div>
<script>
const infoElements = document.getElementsByClassName('info');
Array.from(infoElements).forEach(el => el.innerHTML = "MODIFIED!");
</script>
</body>
</html>Esempio con getElementsByTagName
Recupera elementi tramite il nome del tag con getElementsByTagName. È del tutto simile all'esempio precedente, ma questa volta selezioniamo in base al nome del tag anziché al nome della classe.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<head>
<title>getElementsByTagName Example</title>
</head>
<body>
<p>First Paragraph</p>
<p>Second Paragraph</p>
<script>
const paragraphs = document.getElementsByTagName('p');
Array.from(paragraphs).forEach(el => el.innerHTML = "MODIFIED!");
</script>
</body>
</html>Ricerche flessibili con querySelector e querySelectorAll
Selezione con querySelector
Usa querySelector per trovare il primo elemento che corrisponde a un selettore CSS. In questo esempio selezioniamo il primo elemento con la classe text che è figlio diretto dell'elemento con l'id main.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<head>
<title>QuerySelector Example</title>
</head>
<body>
<div id="main"><span class="text">This will be replaced</span></div>
<div id="other"><span class="text">This one doesn't change</span></div>
<script>
const spanInsideDiv = document.querySelector('#main > .text');
spanInsideDiv.innerHTML = "MODIFIED!";
</script>
</body>
</html>Recupero di più elementi con querySelectorAll
querySelectorAll restituisce tutti gli elementi che corrispondono a un selettore CSS, come una NodeList statica. Comodamente, una NodeList ha un forEach integrato, quindi puoi iterarla direttamente senza doverla convertire prima in un array.
La parola statica è importante: querySelectorAll scatta un'istantanea delle corrispondenze nel momento in cui viene chiamato. Se successivamente aggiungi o rimuovi elementi corrispondenti, quella istantanea non cambia. Questo è esattamente l'opposto della HTMLCollection live restituita dai metodi getElementsBy*.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<head>
<title>QuerySelectorAll Example</title>
</head>
<body>
<ul>
<li class="item">Item 1</li>
<li class="item">Item 2</li>
</ul>
<script>
const items = document.querySelectorAll('.item');
items.forEach(item => item.innerHTML = "MODIFIED!");
</script>
</body>
</html>Live vs. Statico: la trappola delle collezioni
Questa è la trappola in cui cadono i principianti. Una HTMLCollection live riflette lo stato attuale del DOM ad ogni lettura, mentre una NodeList statica è bloccata al momento della selezione. Il frammento di codice seguente mostra entrambe reagire a un elemento aggiunto di recente:
// Suppose the page has two <li class="item"> elements.
const live = document.getElementsByClassName('item'); // live HTMLCollection
const snapshot = document.querySelectorAll('.item'); // static NodeList
console.log(live.length); // 2
console.log(snapshot.length); // 2
// Now add a third matching element.
const li = document.createElement('li');
li.className = 'item';
document.querySelector('ul').appendChild(li);
console.log(live.length); // 3 — updated automatically
console.log(snapshot.length); // 2 — still the old snapshotPerché è importante: iterare su una collezione live mentre si rimuovono elementi corrispondenti è una classica causa di elementi saltati, perché la collezione si riduce durante il ciclo. Una NodeList statica ottenuta da querySelectorAll è più sicura in quel caso, poiché la lista non cambia a metà ciclo.
Verifica e navigazione: matches, closest e contains
La ricerca non consiste solo nel trovare elementi — spesso si ha già un elemento e si deve fare una domanda su di esso.
element.matches(selector)restituiscetruese l'elemento stesso corrisponde al selettore CSS. Ottimo per la delega degli eventi.element.closest(selector)risale l'albero a partire dall'elemento (includendo l'elemento stesso) e restituisce l'antenato più vicino che corrisponde, oppurenull.parent.contains(node)restituiscetruesenodeè il genitore stesso o un suo discendente.
<!-- snippet: html-result -->
<!DOCTYPE html>
<html>
<body>
<section class="card">
<button id="save" class="btn primary">Save</button>
</section>
<div id="out"></div>
<script>
const btn = document.getElementById('save');
const section = document.querySelector('.card');
const out = document.getElementById('out');
out.innerHTML =
'matches(".primary"): ' + btn.matches('.primary') + '<br>' +
'closest(".card") is section: ' + (btn.closest('.card') === section) + '<br>' +
'section.contains(btn): ' + section.contains(btn);
</script>
</body>
</html>Quale metodo devo usare?
- Hai bisogno di un solo elemento tramite ID? Usa
getElementById— è il più veloce e chiaro. - Hai bisogno di un selettore CSS (discendenti, combinatori, attributi,
:not())? UsaquerySelector/querySelectorAll. - Hai bisogno di una lista live che traccia le modifiche al DOM? Usa
getElementsByClassName/getElementsByTagName. - Hai un elemento e devi verificare o risalire l'albero? Usa
matches,closestocontains.
Conclusione
Trovare gli elementi è la base di ogni script DOM. Usa getElementById per ID singoli, querySelector* per la flessibilità dei selettori CSS, e i metodi getElementsBy* quando hai realmente bisogno di una collezione live — ricordando solo la differenza tra live e statico in modo che le collezioni non ti sorprendano a metà ciclo. Da qui, continua con l'attraversamento del DOM e la manipolazione del DOM.