Manipolazione del DOM con JavaScript
Impara la manipolazione del DOM con JavaScript: modifica contenuti, attributi e classi, crea, inserisci, clona e rimuovi elementi con esempi pratici.
La manipolazione del DOM (Document Object Model) con JavaScript è una competenza fondamentale per gli sviluppatori web. Il DOM è una rappresentazione live a forma di albero della pagina che il browser costruisce a partire dall'HTML; manipolarlo significa usare JavaScript per leggere e modificare quell'albero a runtime, in modo che la pagina si aggiorni senza ricaricarsi.
Questa guida copre le quattro operazioni più comuni:
- Lettura e modifica del contenuto con
textContenteinnerHTML. - Lettura e modifica degli attributi con
getAttribute,setAttributeeclassList. - Creazione e inserimento di elementi con
createElement,append,insertBeforeeinsertAdjacentHTML. - Rimozione e sostituzione di elementi con
remove()ereplaceWith().
Prima di poter manipolare un elemento devi prima trovarlo. Se non hai ancora familiarità con getElementById, querySelector e simili, leggi prima Selezionare elementi del DOM e Ricerca: getElement, querySelector — tutti gli esempi seguenti presuppongono che tu abbia già un riferimento al nodo.
Modificare il contenuto e gli attributi degli elementi
Manipolare il contenuto e gli attributi degli elementi DOM è un aspetto cruciale dello sviluppo web dinamico. Cambiando il contenuto, possiamo aggiornare il testo o l'HTML all'interno di un elemento. Modificando gli attributi, possiamo cambiare proprietà come class, id o src. JavaScript fornisce potenti metodi per eseguire queste operazioni, consentendoci di creare applicazioni web responsive e interattive. Vediamo come usare efficacemente innerHTML, textContent, setAttribute e getAttribute.
Modificare il contenuto degli elementi
Possiamo modificare il contenuto di un elemento usando le proprietà innerHTML o textContent.
innerHTML vs textContent
innerHTML ci permette di impostare o ottenere il contenuto HTML di un elemento, inclusi eventuali tag HTML.
<!DOCTYPE html>
<html>
<head>
<title>innerHTML vs textContent</title>
<style>
.content-container {
margin: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.html-content {
color: red;
}
</style>
</head>
<body>
<div class="content-container">
<p id="content">Original paragraph content.</p>
<button id="change-innerHTML">Change using innerHTML</button>
<button id="change-textContent">Change using textContent</button>
</div>
<script>
const content = document.getElementById('content');
const innerHTMLButton = document.getElementById('change-innerHTML');
const textContentButton = document.getElementById('change-textContent');
innerHTMLButton.addEventListener('click', () => {
content.innerHTML = 'New content with <strong class="html-content">HTML</strong> tags.';
});
textContentButton.addEventListener('click', () => {
content.textContent = 'Updated paragraph content without HTML tags.';
});
</script>
</body>
</html>Spiegazione:
innerHTMLci permette di impostare o ottenere il contenuto HTML di un elemento, inclusi eventuali tag HTML. Nell'esempio, facendo clic sul pulsante "Change using innerHTML" il contenuto del paragrafo viene sostituito con un nuovo contenuto che include tag HTML, modificando così l'aspetto del testo.textContentimposta o ottiene il contenuto testuale di un elemento senza interpretare i tag HTML. Facendo clic sul pulsante "Change using textContent" il contenuto del paragrafo viene sostituito con testo semplice, ignorando qualsiasi tag HTML.
Usa textContent quando inserisci contenuti generati dagli utenti per evitare rischi di sicurezza come XSS (Cross-Site Scripting). Assegnare una string grezza proveniente da un utente a innerHTML consente a un malintenzionato di iniettare tag <script> o attributi event-handler che vengono eseguiti nella tua pagina. textContent non interpreta mai l'HTML, quindi la string viene mostrata letteralmente ed è sempre sicura.
Esiste anche una terza proprietà, outerHTML, che rappresenta l'elemento e il suo stesso tag. Assegnarle un valore sostituisce l'elemento interamente:
// Replace the whole <p> with an <h2>, keeping it in the same position
const p = document.querySelector('p');
p.outerHTML = '<h2>A heading instead</h2>';
// After this, `p` still points at the old, detached node — re-query if you need the new one.Modificare gli attributi degli elementi
Possiamo modificare gli attributi di un elemento usando il metodo setAttribute e recuperarli con il metodo getAttribute. Questi metodi sono utili per modificare gli elementi dinamicamente in base alle interazioni dell'utente o ad altri eventi.
<!DOCTYPE html>
<html>
<head>
<title>setAttribute and getAttribute</title>
</head>
<body>
<div id="container" class="initial-class">Container content</div>
<button id="change-attribute">Change Attribute</button>
<button id="get-attribute">Get Attribute</button>
<script>
const container = document.getElementById('container');
const changeAttributeButton = document.getElementById('change-attribute');
const getAttributeButton = document.getElementById('get-attribute');
changeAttributeButton.addEventListener('click', () => {
container.setAttribute('class', 'new-class');
alert('Class attribute changed to "new-class"');
});
getAttributeButton.addEventListener('click', () => {
const className = container.getAttribute('class');
alert(`Current class attribute: ${className}`);
});
</script>
</body>
</html>Spiegazione:
setAttribute('attributeName', 'value'): questo metodo ci consente di impostare un nuovo valore per un attributo specificato di un elemento. Nell'esempio, facendo clic sul pulsante "Change Attribute" l'attributo class del<div>viene modificato da "initial-class" a "new-class".getAttribute('attributeName'): questo metodo recupera il valore corrente dell'attributo specificato. Facendo clic sul pulsante "Get Attribute" viene mostrato un avviso con il valore corrente dell'attributo class del<div>.
Usa sempre setAttribute e getAttribute per attributi personalizzati e attributi generati dinamicamente. Per la gestione delle classi, preferisci l'API classList descritta di seguito — modifica le classi singolarmente invece di sovrascrivere l'intera string class.
Attributi vs. proprietà
Una fonte comune di confusione: un attributo HTML (quello scritto nel markup) non è sempre uguale alla proprietà DOM (il valore live sull'object JavaScript). getAttribute('value') legge il valore originale del markup, mentre input.value legge ciò che l'utente ha attualmente digitato. Per gli attributi boolean il divario è più netto — checkbox.getAttribute('checked') riflette il markup, mentre checkbox.checked riflette lo stato live. Come regola generale: usa le proprietà (el.id, el.value, el.checked) per i valori standard che cambiano frequentemente, e setAttribute/getAttribute per attributi data- personalizzati o attributi di markup occasionali.
Per leggere e scrivere attributi data-* esiste un'API dedicata ed ergonomica — la proprietà dataset:
// <div id="card" data-user-id="42" data-role="admin"></div>
const card = document.getElementById('card');
console.log(card.dataset.userId); // "42" (note: data-user-id → userId)
card.dataset.role = 'editor'; // writes data-role="editor"Gestire le classi con classList
classList attiva/disattiva singole classi senza disturbare quelle già presenti:
element.classList.add('new-class'); // add one (or several) classes
element.classList.remove('old-class'); // remove a class
element.classList.toggle('active'); // add if absent, remove if present
element.classList.replace('open', 'shut'); // swap one class for another
console.log(element.classList.contains('active')); // true / falsetoggle accetta un secondo argomento opzionale per forzare uno stato, il che è utile per sincronizzare una classe con una condizione: el.classList.toggle('valid', isValid). Per un lavoro più approfondito sullo stile — inclusa la lettura e la scrittura della proprietà style — consulta Lavorare con gli stili nel DOM.
Aggiungere e rimuovere elementi
Possiamo aggiungere nuovi elementi al DOM o rimuovere quelli esistenti.
Aggiungere elementi
Per aggiungere nuovi elementi al DOM, li creiamo prima con il metodo createElement, poi li aggiungiamo a un elemento esistente usando appendChild oppure li inseriamo in una posizione specifica usando insertBefore.
createElement(), appendChild(), insertBefore()
<!DOCTYPE html>
<html>
<head>
<title>Adding Elements</title>
</head>
<body>
<div id="task-list">
<h2>Task List</h2>
<ul id="tasks">
<li>Initial task</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
<button id="insert-before">Insert Before First Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
const insertBeforeButton = document.getElementById('insert-before');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
insertBeforeButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
const firstTask = taskList.firstElementChild;
taskList.insertBefore(newTask, firstTask);
}
});
</script>
</body>
</html>Spiegazione:
createElement('tagName'): questo metodo crea un nuovo elemento specificato datagName. Per esempio,document.createElement('li')crea un nuovo elemento<li>.appendChild(newElement): questo metodo aggiunge un nuovo elemento figlio a un elemento genitore specificato. Nell'esempio, facendo clic sul pulsante "Add Task" viene creato un nuovo elemento di lista (<li>) e aggiunto all'elenco dei task (<ul>).insertBefore(newElement, referenceElement): questo metodo inserisce un nuovo elemento prima di un elemento di riferimento specificato all'interno dello stesso genitore. Facendo clic sul pulsante "Insert Before First Task" viene creato un nuovo elemento di lista (<li>) e inserito prima del primo task nell'elenco.
Per inserire string HTML direttamente, considera insertAdjacentHTML(). Per sostituire un elemento esistente, element.replaceWith(newElement) è un'alternativa moderna alla combinazione di remove() e appendChild().
insertAdjacentHTML e i metodi di inserimento moderni
Quando hai già una string HTML (invece di un nodo costruito con createElement), insertAdjacentHTML(position, html) la analizza e la inserisce in una sola chiamata. L'argomento position è una delle quattro parole chiave relative all'elemento:
// Given: <div id="box">content</div>
const box = document.getElementById('box');
box.insertAdjacentHTML('beforebegin', '<p>before the box</p>'); // before <div>
box.insertAdjacentHTML('afterbegin', '<p>first child</p>'); // inside, at the start
box.insertAdjacentHTML('beforeend', '<p>last child</p>'); // inside, at the end
box.insertAdjacentHTML('afterend', '<p>after the box</p>'); // after </div>I browser moderni forniscono anche append(), prepend(), before() e after(), che accettano sia nodi che string semplici e possono ricevere più argomenti contemporaneamente — sono generalmente più chiari di appendChild/insertBefore:
const list = document.getElementById('tasks');
const item = document.createElement('li');
item.textContent = 'New task';
list.append(item, 'some trailing text'); // append node + string together
list.prepend('Top of the list'); // insert at the startClonare elementi
Per duplicare un nodo esistente invece di costruirne uno da zero, usa cloneNode(deep). Passa true per copiare l'elemento insieme a tutti i suoi discendenti; passa false (o nulla) per copiare solo l'elemento stesso:
const template = document.getElementById('card');
const copy = template.cloneNode(true); // deep clone, including children
copy.id = 'card-2'; // ids must stay unique
document.body.append(copy);Per markup ripetuto e complesso, l'elemento <template> è lo strumento appositamente progettato — il suo contenuto rimane inerte finché non lo cloni.
Rimuovere elementi
Per rimuovere un elemento, possiamo usare il moderno metodo element.remove().
<!DOCTYPE html>
<html>
<head>
<title>Removing Elements</title>
</head>
<body>
<div id="container">
<p id="paragraph">This is a paragraph.</p>
<button id="remove-paragraph">Remove Paragraph</button>
</div>
<script>
const container = document.getElementById('container');
const paragraph = document.getElementById('paragraph');
const removeParagraphButton = document.getElementById('remove-paragraph');
removeParagraphButton.addEventListener('click', () => {
paragraph.remove();
});
</script>
</body>
</html>Spiegazione:
element.remove(): questo metodo moderno rimuove direttamente dal DOM un elemento specificato. Nell'esempio, facendo clic sul pulsante "Remove Paragraph" l'elemento<p>viene rimosso dalla pagina.
Usare element.remove() è l'approccio consigliato per le modifiche dinamiche al contenuto, come la rimozione di articoli da un carrello della spesa, poiché offre una sintassi più pulita rispetto al metodo legacy parent.removeChild(child) (che rimane utile quando hai bisogno di un riferimento al nodo rimosso).
Inserire molti elementi in modo efficiente
Ogni volta che inserisci un nodo nel documento live, il browser potrebbe dover ricalcolare il layout e ridisegnare la pagina. Farlo all'interno di un ciclo — una volta per ogni elemento — è uno spreco. Un DocumentFragment ti permette di assemblare i nodi fuori schermo e di inserirli tutti in una singola operazione:
const list = document.getElementById('tasks');
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 1000; i++) {
const li = document.createElement('li');
li.textContent = `Task ${i}`;
fragment.appendChild(li); // no layout work — fragment is not in the document
}
list.appendChild(fragment); // one insertion, one reflowDue regole pratiche correlate mantengono il codice DOM-intensive veloce: raggruppa le letture e le scritture (non alternare la lettura di offsetHeight con l'impostazione degli stili, altrimenti forzi reflow ripetuti) e preferisci costruire una string e assegnarla a innerHTML una sola volta rispetto a molte chiamate appendChild quando non stai collegando event listener a ogni nodo. Per un trattamento approfondito, consulta Ottimizzazione delle prestazioni del DOM.
Esempio: lista To-Do dinamica
Creiamo una semplice applicazione to-do list per dimostrare i concetti illustrati sopra.
<!DOCTYPE html>
<html>
<head>
<title>To-Do List</title>
</head>
<body>
<div id="todo-list">
<h2>My To-Do List</h2>
<ul id="tasks">
<li>Learn JavaScript</li>
</ul>
<input type="text" id="new-task" placeholder="New task">
<button id="add-task">Add Task</button>
</div>
<script>
const taskList = document.getElementById('tasks');
const newTaskInput = document.getElementById('new-task');
const addTaskButton = document.getElementById('add-task');
addTaskButton.addEventListener('click', () => {
const newTaskText = newTaskInput.value;
if (newTaskText.trim()) {
const newTask = document.createElement('li');
newTask.textContent = newTaskText;
taskList.appendChild(newTask);
newTaskInput.value = '';
}
});
taskList.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
event.target.remove();
}
});
</script>
</body>
</html>Spiegazione:
createElementcrea un nuovo elemento di lista per il task, eappendChildlo aggiunge all'elenco.- Un unico listener di click viene collegato al
<ul>, non a ogni<li>. Poiché i click si propagano verso l'alto dall'elemento cliccato ai suoi antenati, il listener esaminaevent.targetper decidere quale task è stato cliccato e lo rimuove. Questo schema si chiama event delegation, ed è la ragione per cui il gestore continua a funzionare per i task che non esistevano al caricamento della pagina.
L'event delegation è il motivo per cui non aggiungiamo un listener separato ogni volta che viene creato un task — un unico listener sul genitore gestisce i figli presenti e futuri. Per approfondire il bubbling, event.target vs event.currentTarget e la delegation, leggi Gestione degli eventi nel DOM.
Quando un'interfaccia cresce fino ad avere molti elementi di stato indipendenti e aggiornati frequentemente, mantenere il DOM sincronizzato manualmente diventa soggetto a errori. Framework come React, Vue o Svelte ti consentono di descrivere come dovrebbe apparire l'interfaccia per un dato stato e gestiscono per te gli aggiornamenti del DOM. Le tecniche scritte a mano in questo capitolo rimangono le fondamenta su cui sono costruiti quei framework.
Conclusione
Padroneggiare la manipolazione del DOM è essenziale per creare applicazioni web dinamiche e interattive. Ora sai come modificare il contenuto (textContent, innerHTML, outerHTML), lavorare con attributi e classi (setAttribute, dataset, classList), creare e inserire nodi (createElement, append, insertAdjacentHTML, cloneNode), rimuoverli e sostituirli (remove(), replaceWith()), e raggruppare gli inserimenti in modo efficiente con un DocumentFragment.
Per continuare a costruire su queste basi:
- Selezionare elementi del DOM — trova i nodi che vuoi modificare.
- Attraversare il DOM — muoviti tra genitori, figli e fratelli.
- Lavorare con gli stili nel DOM — leggi e scrivi CSS da JavaScript.
- Gestione degli eventi nel DOM — rispondi all'interazione dell'utente.