034.4 Lezione 1
Certificazione: |
Web Development Essentials |
---|---|
Versione: |
1.0 |
Argomento: |
034 Programmazione JavaScript |
Obiettivo: |
034.4 Manipolazione con JavaScript del Contenuto e dello Stile di un Sito Web |
Lezione: |
1 di 1 |
Introduzione
HTML, CSS e JavaScript sono tre tecnologie distinte che si incontrano sul Web. Per fare pagine veramente dinamiche e interattive, il programmatore JavaScript deve combinare componenti HTML e CSS in esecuzione. Un compito che è notevolmente facilitato dall’uso del Document Object Model (DOM).
Interagire con il DOM
Il DOM è una struttura di dati che funziona come un’interfaccia di programmazione al documento, dove ogni aspetto di quest’ultimo è rappresentato come un nodo nel DOM e ogni cambiamento fatto al DOM si riverbera immediatamente nel documento. Per mostrare come usare il DOM in JavaScript, salva il seguente codice HTML in un file chiamato example.html
:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first">
<p>The dynamic content goes here</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section</p>
</div><!-- #content_second -->
</body>
</html>
Il DOM è disponibile solo dopo che l’HTML è stato caricato, quindi scrivi il seguente JavaScript alla fine del corpo della pagina (prima del tag finale </body>
):
<script>
let body = document.getElementsByTagName("body")[0];
console.log(body.innerHTML);
</script>
L’oggetto document
è l’elemento superiore del DOM, tutti gli altri elementi si diramano da esso. Il metodo getElementsByTagName()
elenca tutti gli elementi discendenti da document
che hanno il nome del tag dato. Anche se il tag body
è usato solo una volta nel documento, il metodo getElementsByTagName()
restituisce sempre una collezione simile a un array di elementi trovati, da cui l’uso dell’indice [0]
per restituire il primo (e unico) elemento trovato.
Contenuto HTML
Come mostrato nell’esempio precedente, l’elemento DOM restituito dal document.getElementsByTagName("body")[0]
è stato assegnato alla variabile body
. La variabile body
può quindi essere usata per manipolare l’elemento body della pagina, perché eredita tutti i metodi e gli attributi DOM da quell’elemento. Per esempio, la proprietà innerHTML
contiene l’intero codice di markup HTML scritto all’interno dell’elemento corrispondente, quindi può essere usata per leggere il markup interno. La nostra chiamata console.log(body.innerHTML)
stampa il contenuto all’interno di <body></body>
nella console web. La variabile può anche essere usata per sostituire quel contenuto, come in body.innerHTML = "<p>Content erased</p>"
.
Piuttosto che modificare intere porzioni di marcatura HTML, è più pratico mantenere inalterata la struttura del documento e interagire semplicemente con i suoi elementi. Una volta che il documento è stato renderizzato dal browser, tutti gli elementi sono accessibili con i metodi DOM. È possibile, ad esempio, elencare e accedere a tutti gli elementi HTML utilizzando la stringa speciale *
nel metodo getElementsByTagName()
dell’oggetto document
:
let elements = document.getElementsByTagName("*");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Questo codice metterà tutti gli elementi trovati in document
nella variabile elements
. La variabile elements
è un oggetto simile a un array, quindi possiamo iterare attraverso ciascuno dei suoi elementi con un ciclo for
. Se la pagina HTML dove viene eseguito questo codice ha un elemento con un attributo id
impostato su content_first
(vedi la pagina HTML di esempio mostrata all’inizio della lezione), l’istruzione if
corrisponde a quell’elemento e il suo contenuto di markup sarà cambiato in <p>New content</p>
. Nota che gli attributi di un elemento HTML nel DOM sono accessibili usando la notazione puntata delle proprietà degli oggetti JavaScript: quindi, element.id
si riferisce all’attributo id dell’elemento corrente del ciclo for
. Si potrebbe anche usare il metodo getAttribute()
, come in element.getAttribute("id")
.
Non è necessario iterare attraverso tutti gli elementi se si vuole ispezionare solo un sottoinsieme di essi. Per esempio, il metodo document.getElementsByClassName()
limita gli elementi trovati a quelli che hanno una classe specifica:
let elements = document.getElementsByClassName("content");
for ( element of elements )
{
if ( element.id == "content_first" )
{
element.innerHTML = "<p>New content</p>";
}
}
Tuttavia, iterare attraverso molti elementi del documento usando un ciclo non è la migliore strategia quando si deve cambiare un elemento specifico nella pagina.
Selezionare Elementi Specifici
JavaScript fornisce metodi ottimizzati per selezionare l’esatto elemento su cui si vuole lavorare. Il ciclo precedente potrebbe essere interamente sostituito dal metodo document.getElementById()
:
let element = document.getElementById("content_first");
element.innerHTML = "<p>New content</p>";
Ogni attributo id
nel documento deve essere unico, quindi il metodo document.getElementById()
restituisce solo un singolo oggetto DOM. Anche la dichiarazione della variabile element
può essere omessa, perché JavaScript ci permette di concatenare direttamente i metodi:
document.getElementById("content_first").innerHTML = "<p>New content</p>";
Il metodo getElementById()
è il metodo preferibile per localizzare elementi nel DOM, perché le sue prestazioni sono molto migliori dei metodi iterativi quando si lavora con documenti complessi. Tuttavia, non tutti gli elementi hanno un ID esplicito, e il metodo restituisce un valore null se nessun elemento corrisponde all’ID fornito (questo impedisce anche l’uso di attributi o funzioni concatenate, come la innerHTML
usata nell’esempio precedente). Inoltre, è più pratico assegnare gli attributi ID solo ai componenti principali della pagina e poi usare i selettori CSS per localizzare i loro elementi figli.
I selettori, introdotti in una precedente lezione sui CSS, sono schemi che corrispondono a elementi nel DOM. Il metodo querySelector()
restituisce il primo elemento corrispondente nell’albero del DOM, mentre querySelectorAll()
restituisce tutti gli elementi che corrispondono al selettore specificato.
Nell’esempio precedente, il metodo getElementById()
recupera l’elemento con l’ID content_first
. Il metodo querySelector()
può eseguire lo stesso compito:
document.querySelector("#content_first").innerHTML = "<p>New content</p>";
Poiché il metodo querySelector()
usa la sintassi del selettore, l’ID fornito deve iniziare con un carattere hash. Se non viene trovato nessun elemento corrispondente, il metodo querySelector()
restituisce null.
Nell’esempio precedente, l’intero contenuto del div content_first
è sostituito dalla stringa di testo fornita. La stringa ha del codice HTML al suo interno, il che non è considerato una buona pratica. Devi stare attento quando aggiungi codice HTML hard-coded al codice JavaScript, perché il tracciamento degli elementi può diventare difficile quando sono richiesti cambiamenti alla struttura generale del documento.
I selettori non sono limitati all’ID dell’elemento. L’elemento interno p
può essere indirizzato direttamente:
document.querySelector("#content_first p").innerHTML = "New content";
Il selettore #content_first p
corrisponderà solo al primo elemento p
all’interno del div #content_first
. Funziona bene se vogliamo manipolare il primo elemento. Tuttavia, potremmo voler cambiare il secondo paragrafo:
<div class="content" id="content_first">
<p>Don't change this paragraph.</p>
<p>The dynamic content goes here.</p>
</div><!-- #content_first -->
In questo caso, possiamo usare la pseudo-classe :nth-child(2)
per abbinare il secondo elemento p
:
document.querySelector("#content_first p:nth-child(2)").innerHTML = "New content";
Il numero 2
in p:nth-child(2)
indica il secondo paragrafo che corrisponde al selettore. Vedi la lezione sui selettori CSS per saperne di più sui selettori e su come usarli.
Lavorare con gli Attributi
La capacità di JavaScript di interagire con il DOM non è limitata alla manipolazione del contenuto. Infatti, l’uso più pervasivo di JavaScript nel browser è quello di modificare gli attributi degli elementi HTML esistenti.
Diciamo che la nostra pagina originale HTML di esempio ha ora tre sezioni di contenuto:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>HTML Manipulation with JavaScript</title>
</head>
<body>
<div class="content" id="content_first" hidden>
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second" hidden>
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third" hidden>
<p>Third section.</p>
</div><!-- #content_third -->
</body>
</html>
Potresti voler rendere visibile solo uno di essi alla volta, da qui l’attributo hidden
in tutti i tag div
. Questo è utile, per esempio, per mostrare solo un’immagine da una galleria di immagini. Per renderne visibile una al caricamento della pagina, aggiungi il seguente codice JavaScript alla pagina:
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).removeAttribute("hidden");
L’espressione valutata dall’istruzione switch
restituisce casualmente il numero 0, 1 o 2. Il selettore ID corrispondente viene quindi assegnato alla variabile content_visible
, che è usata dal metodo querySelector(content_visible)
. La chiamata concatenata removeAttribute("hidden")
rimuove l’attributo hidden
dall’elemento.
È possibile anche l’approccio opposto: Tutte le sezioni potrebbero essere inizialmente visibili (senza l’attributo hidden
) e il programma JavaScript può poi assegnare l’attributo hidden
a ogni sezione tranne quella in content_visible
. Per fare ciò, è necessario iterare attraverso tutti gli elementi div
del contenuto che sono diversi da quello scelto, il che può essere fatto usando il metodo querySelectorAll()
:
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
// Hide all content divs, except content_visible
for ( element of document.querySelectorAll(".content:not("+content_visible+")") )
{
// Hidden is a boolean attribute, so any value will enable it
element.setAttribute("hidden", "");
}
Se la variabile content_visible
è stata impostata a #content_first
, il selettore sarà .content:not(#content_first)
, che si legge come tutti gli elementi che hanno la classe content
tranne quelli che hanno l’ID content_first
. Il metodo setAttribute()
aggiunge o cambia gli attributi degli elementi HTML. Il suo primo parametro è il nome dell’attributo e il secondo è il valore dell’attributo.
Tuttavia, il modo corretto per cambiare l’aspetto degli elementi è con i CSS. In questo caso, possiamo impostare la proprietà CSS display
su hidden
e poi cambiarla in block
usando JavaScript:
<style>
div.content { display: none }
</style>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
<script>
// Which content to show
let content_visible;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_visible = "#content_first";
break;
case 1:
content_visible = "#content_second";
break;
case 2:
content_visible = "#content_third";
break;
}
document.querySelector(content_visible).style.display = "block";
</script>
Le stesse buone pratiche che si applicano a “mescolare” tag HTML con JavaScript si applicano anche ai CSS: per questo motivo scrivere le proprietà CSS direttamente nel codice JavaScript non è raccomandato. Invece, le regole CSS dovrebbero essere scritte separatamente dal codice JavaScript. Il modo corretto per alternare lo stile visivo è quello di selezionare una classe CSS predefinita per l’elemento.
Lavorare con le Classi
Gli elementi possono avere più di una classe associata, rendendo più facile scrivere stili che possono essere aggiunti o rimossi quando necessario. Sarebbe estenuante cambiare molti attributi CSS direttamente in JavaScript, così si può creare una nuova classe CSS con quegli attributi e poi aggiungere la classe all’elemento. Gli elementi DOM hanno la proprietà classList
, che può essere usata per visualizzare e manipolare le classi assegnate all’elemento corrispondente.
Per esempio, invece di cambiare la visibilità dell’elemento, possiamo creare una classe CSS aggiuntiva per evidenziare il nostro div content
:
div.content {
border: 1px solid black;
opacity: 0.25;
}
div.content.highlight {
border: 1px solid red;
opacity: 1;
}
Questo foglio di stile aggiungerà un sottile bordo nero e una semi-trasparenza a tutti gli elementi che hanno la classe content
. Solo gli elementi che hanno anche la classe highlight
saranno completamente opachi e avranno un sottile bordo rosso. Quindi, invece di cambiare direttamente le proprietà CSS come abbiamo fatto prima, possiamo usare il metodo classList.add("highlight")
nell’elemento selezionato:
// Which content to highlight
let content_highlight;
switch ( Math.floor(Math.random() * 3) )
{
case 0:
content_highlight = "#content_first";
break;
case 1:
content_highlight = "#content_second";
break;
case 2:
content_highlight = "#content_third";
break;
}
// Highlight the selected div
document.querySelector(content_highlight).classList.add("highlight");
Tutte le tecniche e gli esempi che abbiamo visto finora sono stati eseguiti alla fine del processo di caricamento della pagina, ma non sono limitati a questa fase. Ciò che rende JavaScript così utile agli sviluppatori Web è la sua capacità di reagire agli eventi sulla pagina, cosa che vedremo in seguito.
Gestori di eventi
Tutti gli elementi visibili della pagina sono suscettibili di eventi interattivi, come il clic o il movimento del mouse stesso. Possiamo associare azioni personalizzate a questi eventi, il che espande notevolmente ciò che un documento HTML può fare.
Probabilmente l’elemento HTML più ovvio che beneficia di un’azione associata è l’elemento button
. Per mostrare come funziona, aggiungi tre pulsanti sopra il primo elemento div
della pagina di esempio:
<p>
<button>First</button>
<button>Second</button>
<button>Third</button>
</p>
<div class="content" id="content_first">
<p>First section.</p>
</div><!-- #content_first -->
<div class="content" id="content_second">
<p>Second section.</p>
</div><!-- #content_second -->
<div class="content" id="content_third">
<p>Third section.</p>
</div><!-- #content_third -->
I pulsanti non fanno nulla da soli, ma supponiamo di voler evidenziare il div
corrispondente al pulsante premuto. Possiamo usare l’attributo onClick
per associare un’azione a ogni pulsante:
<p>
<button onClick="document.getElementById('content_first').classList.toggle('highlight')">First</button>
<button onClick="document.getElementById('content_second').classList.toggle('highlight')">Second</button>
<button onClick="document.getElementById('content_third').classList.toggle('highlight')">Third</button>
</p>
Il metodo classList.toggle()
aggiunge la classe specificata all’elemento se non è presente, e la rimuove se già lo è. Se esegui l’esempio, noterai che più di un div
può essere evidenziato allo stesso tempo. Per evidenziare solo il div
corrispondente al pulsante premuto, è necessario rimuovere la classe highlight
dagli altri elementi div
. Tuttavia, se l’azione personalizzata è troppo lunga o comporta più di una linea di codice, è più pratico scrivere una funzione separata dall’elemento tag:
function highlight(id)
{
// Remove the "highlight" class from all content elements
for ( element of document.querySelectorAll(".content") )
{
element.classList.remove('highlight');
}
// Add the "highlight" class to the corresponding element
document.getElementById(id).classList.add('highlight');
}
Come gli esempi precedenti, questa funzione può essere posta all’interno di un tag <script>
o in un file JavaScript esterno associato al documento. La funzione highlight
rimuove prima la classe highlight
da tutti gli elementi div
associati alla classe content
, poi aggiunge la classe highlight
all’elemento scelto. Ogni pulsante dovrebbe poi chiamare questa funzione dal suo attributo onClick
, usando l’ID corrispondente come argomento della funzione:
<p>
<button onClick="highlight('content_first')">First</button>
<button onClick="highlight('content_second')">Second</button>
<button onClick="highlight('content_third')">Third</button>
</p>
Oltre all’attributo onClick
, potremmo usare l’attributo onMouseOver
(attivato quando il dispositivo di puntamento viene usato per spostare il cursore sull’elemento), l’attributo onMouseOut
(attivato quando il dispositivo di puntamento non è più contenuto nell’elemento), ecc. Inoltre, i gestori di eventi non sono limitati ai pulsanti, quindi puoi assegnare azioni personalizzate a questi gestori di eventi per tutti gli elementi HTML visibili.
Esercizi Guidati
-
Usando il metodo
document.getElementById()
, come potresti inserire la frase “Dynamic content” nel contenuto interno dell’elemento il cui ID èmessage
? -
Qual è la differenza tra referenziare un elemento attraverso il suo ID usando il metodo
document.querySelector()
e farlo tramite il metododocument.getElementById()
? -
Qual è lo scopo del metodo
classList.remove()
? -
Qual è il risultato dell’uso del metodo
myelement.classList.toggle("active")
semyelement
non ha la classeactive
assegnata a esso?
Esercizi Esplorativi
-
Quale argomento al metodo
document.querySelectorAll()
gli farà imitare il metododocument.getElementsByTagName("input")
? -
Come si può usare la proprietà
classList
per elencare tutte le classi associate a un dato elemento?
Sommario
Questa lezione spiega come usare JavaScript per cambiare i contenuti HTML e le loro proprietà CSS usando il DOM (Document Object Model). Questi cambiamenti possono essere innescati da eventi utente, utile per creare interfacce dinamiche. La lezione passa attraverso i seguenti concetti e procedure:
-
Come ispezionare la struttura del documento usando metodi come
document.getElementById()
,document.getElementsByClassName()
,document.getElementsByTagName()
,document.querySelector()
edocument.querySelectorAll()
. -
Come cambiare il contenuto del documento con la proprietà
innerHTML
. -
Come aggiungere e modificare gli attributi degli elementi della pagina con i metodi
setAttribute()
eremoveAttribute()
. -
Il modo corretto di manipolare le classi degli elementi usando la proprietà
classList
e la sua relazione con gli stili CSS. -
Come legare funzioni a eventi del mouse in elementi specifici.
Risposte agli Esercizi Guidati
-
Usando il metodo
document.getElementById()
, come potresti inserire la frase “Dynamic content” nel contenuto interno dell’elemento il cui ID èmessage
?Può essere inserita con la proprietà
innerHTML
:document.getElementById("message").innerHTML = "Dynamic content"
-
Qual è la differenza tra referenziare un elemento attraverso il suo ID usando il metodo
document.querySelector()
e farlo tramite il metododocument.getElementById()
?L’ID deve essere accompagnato dal carattere hash (
#
) nelle funzioni che usano i selettori, comedocument.querySelector()
. -
Qual è lo scopo del metodo
classList.remove()
?Rimuove la classe (il cui nome è dato come argomento della funzione) dall’attributo
class
dell’elemento corrispondente. -
Qual è il risultato dell’uso del metodo
myelement.classList.toggle("active")
semyelement
non ha la classeactive
assegnata a esso?Il metodo assegnerà la classe
active
amyelement
.
Risposte agli Esercizi Esplorativi
-
Quale argomento al metodo
document.querySelectorAll()
gli farà imitare il metododocument.getElementsByTagName("input")
?L’uso di
document.querySelectorAll("input")
corrisponderà a tutti gli elementiinput
nella pagina, proprio comedocument.getElementsByTagName("input")
. -
Come si può usare la proprietà
classList
per elencare tutte le classi associate a un dato elemento?La proprietà
classList
è un oggetto simile a un array, quindi un ciclofor
può essere usato per iterare tutte le classi che contiene.