, ad esempio, attraverso alert(document.getElementById("id").innerHTML), quindi funzionerà in IE.
Sintassi
elemento.innerHTML = "testo assegnato"
Esempio
Testare la proprietà innerHTML
Paragrafo da inserire
Il Document Object Model, o "DOM", è un'interfaccia di programmazione per l'accesso agli elementi delle pagine web. Fondamentalmente, è un'API di pagina che ti consente di leggere e manipolare il contenuto, la struttura e gli stili della pagina. Vediamo come funziona e come funziona.
Come si costruisce una pagina web?
Il processo di conversione di un documento HTML sorgente in una pagina renderizzabile, stilizzata e interattiva è chiamato "Percorso di rendering critico". Sebbene questo processo possa essere suddiviso in diversi passaggi, come ho descritto in Comprendere il percorso di rendering critico, questi passaggi possono essere raggruppati approssimativamente in due passaggi. Nel primo, il browser analizza il documento per determinare cosa verrà eventualmente visualizzato sulla pagina e nel secondo, il browser esegue il rendering.
Il risultato della prima fase è quello che viene chiamato "albero di rendering". albero di renderingè una rappresentazione degli elementi HTML che verranno visualizzati nella pagina e dei loro stili associati. Per costruire questo albero, il browser ha bisogno di due cose:
- CSSOM, Rappresentare gli stili associati agli elementi
- DOM, rappresentazione degli elementi
Di cosa è fatto il DOM?
DOM è una rappresentazione di oggetti del documento HTML originale. Presenta alcune differenze, come vedremo più avanti, ma essenzialmente è un tentativo di trasformare la struttura e il contenuto di un documento HTML in un modello a oggetti che può essere utilizzato da vari programmi.
La struttura degli oggetti DOM è rappresentata da quello che viene chiamato "albero dei nodi". È così chiamato perché può essere pensato come un albero con un unico genitore che si ramifica in più rami figli, ognuno dei quali può avere foglie. In questo caso, l'"elemento" genitore è l'elemento radice, i "rami" figli sono elementi nidificati e le "foglie" sono il contenuto all'interno degli elementi.
Prendiamo questo documento HTML come esempio:
La mia prima pagina web
Ciao mondo!
Come stai?
Questo documento può essere rappresentato come il seguente albero di nodi:
Cosa non è il DOM
Nell'esempio sopra, sembra che il DOM sia una mappatura 1:1 del documento HTML originale. Tuttavia, come ho detto, ci sono differenze. Per comprendere appieno cos'è il DOM, dobbiamo guardare a cosa non è.
DOM non è una copia dell'HTML originale
Sebbene il DOM venga creato da un documento HTML, non è sempre esattamente lo stesso. Esistono due casi in cui il DOM può differire dall'HTML originale.
1. Quando l'HTML contiene errori di markup
Il DOM è un'interfaccia per accedere agli elementi effettivi (cioè già renderizzati) di un documento HTML. Nel processo di creazione del DOM, il browser stesso può correggere alcuni errori nel codice HTML.
Considera questo documento HTML come esempio:
Ciao mondo!
Elementi mancanti dal documento
e , che è un requisito per HTML. Ma se osserviamo l'albero DOM risultante, possiamo vedere che questo è stato risolto:
- html
2. Quando il DOM viene modificato dal codice Javascript
Oltre ad essere l'interfaccia per la visualizzazione dei contenuti di un documento HTML, il DOM stesso può anche essere modificato.
Possiamo, ad esempio, creare nodi aggiuntivi per il DOM usando Javascript.
Var newParagraph = document.createElement("p"); var sectionContent = document.createTextNode("Sono nuovo!"); newParagraph.appendChild(paragraphContent); document.body.appendChild(newParagraph);
Questo codice cambierà il DOM, ma le modifiche non si rifletteranno nel documento HTML.
Il DOM non è quello che vedi nel browser (cioè l'albero di rendering)
Nella finestra del browser, vedi l'albero di rendering, che, come ho detto, è una combinazione di DOM e CSSOM. Ciò che rende il DOM diverso da un albero di rendering è che quest'ultimo consiste solo in ciò che verrà eventualmente visualizzato sullo schermo.
Poiché l'albero di rendering si occupa solo di ciò che viene visualizzato, esclude gli elementi che sono visivamente nascosti. Ad esempio, gli elementi che hanno stili con display: nessuno.
Ciao mondo!
Come stai?
DOM includerà l'elemento
Tuttavia, l'albero di rendering, e quindi ciò che è visibile nella finestra, non sarà incluso in questo elemento.
DOM non è ciò che viene visualizzato in DevTools
Questa differenza è leggermente inferiore perché DevTools Element Inspector fornisce l'approssimazione più vicina al DOM che abbiamo in un browser. Tuttavia, l'ispettore DevTools contiene informazioni aggiuntive che non sono nel DOM.
Il miglior esempio di ciò sono gli pseudo-elementi CSS. Pseudo-elementi creati usando i selettori ::prima e ::dopo, fanno parte del CSSOM e dell'albero di rendering, ma tecnicamente non fanno parte del DOM. Questo perché il DOM viene generato solo dal documento HTML originale, esclusi gli stili applicati all'elemento.
Anche se gli pseudo-elementi non fanno parte del DOM, sono nel nostro controllo degli elementi devtools.
Riepilogo
DOM è un'interfaccia per un documento HTML. Viene utilizzato dai browser come primo passaggio per determinare cosa visualizzare nel viewport e dal codice Javascript per modificare il contenuto, la struttura o lo stile di una pagina.
Il riferimento contiene una descrizione di tutte le proprietà e i metodi degli oggetti JavaScript incorporati standard.
Modello a oggetti del documento
Modello a oggetti del documento(Document Object Model, DOM) è un'API (Application Programming Interface) per XML che è stata estesa per funzionare anche con HTML.
Nel DOM, tutto il contenuto della pagina (elementi e testo) è rappresentato come una gerarchia di nodi. Considera il seguente codice:
pagina semplice
Ciao mondo!
Questo codice può essere rappresentato utilizzando il DOM come una gerarchia di nodi:
Rappresentando un documento come un albero di nodi, l'API DOM offre agli sviluppatori il controllo completo sul contenuto e sulla struttura di una pagina web.
Quando si descrive la struttura ad albero del DOM, viene utilizzata una terminologia presa in prestito dagli alberi genealogici.
Pertanto, un nodo situato direttamente al di sopra di un dato nodo è chiamato nodo padre di un dato nodo. I nodi situati un livello sotto questo nodo sono chiamati figli di questo nodo. I nodi che sono allo stesso livello e hanno lo stesso genitore sono chiamati fratelli o fratelli. I nodi situati un numero qualsiasi di livelli al di sotto di un dato nodo sono chiamati suoi discendenti. Genitori, nonni e qualsiasi altro nodo situato a un numero qualsiasi di livelli al di sopra di un dato nodo sono chiamati i suoi antenati.
Il riferimento DOM contiene una descrizione degli oggetti Document, Element, Event e NodeList, inclusa una descrizione dei loro metodi e proprietà:
Directory distinta base
BOM (Browser Object Model in translation from English - Browser Object Model) fornisce l'accesso alla finestra del browser e consente di manipolarla e i suoi elementi.
Gli oggetti BOM forniscono l'accesso alle funzionalità del browser indipendentemente dal contenuto della pagina Web. L'argomento della distinta base è allo stesso tempo interessante e complesso, perché a causa della lunga assenza delle specifiche, i produttori di browser hanno esteso liberamente la distinta base come meglio credevano. Molti elementi simili in diversi browser sono diventati standard de facto che vengono seguiti fino ad oggi per motivi di compatibilità reciproca. Per standardizzare questi aspetti fondamentali di JavaScript, il W3C ha definito gli elementi principali della distinta base nella specifica HTML5.
In genere, gli sviluppatori usano jQuery quando hanno bisogno di fare qualcosa con il DOM. Tuttavia, quasi tutte le manipolazioni DOM possono essere eseguite in puro JavaScript utilizzando la sua API DOM.
Diamo un'occhiata a questa API in modo più dettagliato:
Alla fine, scriverai la tua semplice libreria DOM che può essere utilizzata in qualsiasi progetto.
DOM query
Le query DOM vengono eseguite utilizzando il metodo .querySelector(), che accetta come argomento un selettore CSS arbitrario.
Const myElement = document.querySelector("#pippo > div.bar")
Restituirà il primo elemento corrispondente. Puoi fare il contrario: controlla se un elemento corrisponde a un selettore:
MyElement.matches("div.bar") === true
Se vuoi ottenere tutti gli elementi che corrispondono a un selettore, usa il seguente costrutto:
Const myElements = document.querySelectorAll(".bar")
Se sai a quale elemento padre fare riferimento, puoi semplicemente cercare tra i suoi figli, invece di cercare nell'intero codice:
Const myChildElemet = myElement.querySelector("input") // Invece di: // document.querySelector("#foo > div.bar input")
Sorge la domanda: perché allora usare altri metodi meno convenienti come.getElementsByTagName() ? C'è un piccolo problema: il risultato dell'output di .querySelector() non viene aggiornato e quando aggiungiamo un nuovo elemento (vedi ), non cambierà.
const elements1 = document.querySelectorAll("div") const elements2 = document.getElementsByTagName("div") const newElement = document.createElement("div") document.body.appendChild(newElement) elements1.length === elementi2.length // falso
Anche querySelectorAll() raccoglie tutto in un elenco, il che lo rende non molto efficiente.
Come lavorare con le liste?
Inoltre, .querySelectorAll() ha due piccole stranezze. Non puoi semplicemente chiamare metodi sui risultati e aspettarti che vengano applicati a ciascuno di essi (come potresti essere abituato a fare con jQuery). In ogni caso, dovrai scorrere tutti gli elementi nel ciclo. In secondo luogo, l'oggetto restituito è un elenco di elementi, non un array. Pertanto, i metodi dell'array non funzioneranno. Naturalmente, ci sono metodi per le liste, qualcosa come .forEach() , ma, purtroppo, non sono adatti a tutti i casi. Quindi è meglio convertire l'elenco in un array:
// Utilizzo di Array.from() Array.from(myElements).forEach(doSomethingWithEachElement) // O un prototipo di array (pre-ES6) Array.prototype.forEach.call(myElements, doSomethingWithEachElement) // Più semplice: .forEach.call (myElements , doSomethingWithEachElement)
Ogni elemento ha delle proprietà che fanno riferimento ad una "famiglia".
MyElement.children myElement.firstElementChild myElement.lastElementChild myElement.previousElementSibling myElement.nextElementSibling
Poiché l'interfaccia Element è ereditata dall'interfaccia Node, sono presenti anche le seguenti proprietà:
MyElement.childNodes myElement.firstChild myElement.lastChild myElement.previousSibling myElement.nextSibling myElement.parentNode myElement.parentElement
Le prime proprietà si riferiscono all'elemento, mentre le seconde (ad eccezione di .parentElement) possono essere elenchi di elementi di qualsiasi tipo. Di conseguenza, puoi controllare il tipo dell'elemento:
MyElement.firstChild.nodeType === 3 // questo elemento sarà un nodo di testo
Aggiunta di classi e attributi
Aggiungere una nuova classe è molto semplice:
myElement.classList.add("pippo") myElement.classList.remove("bar") myElement.classList.toggle("baz")
Aggiungere una proprietà a un elemento è esattamente come aggiungere una proprietà a qualsiasi oggetto:
// Ottieni il valore dell'attributo const value = myElement.value // Imposta l'attributo come proprietà dell'elemento myElement.value = "(!LANG:foo"
// Для установки нескольких свойств используйте.Object.assign()
Object.assign(myElement, {
value: "foo",
id: "bar"
})
// Удаление атрибута
myElement.value = null
!}
È possibile utilizzare i metodi .getAttibute() , .setAttribute() e .removeAttribute(). Modificheranno immediatamente gli attributi HTML dell'elemento (al contrario delle proprietà DOM), che attiveranno un ridisegno del browser (sarai in grado di vedere eventuali modifiche ispezionando l'elemento utilizzando gli strumenti di sviluppo nel browser). Tali ridisegni non solo richiedono più risorse rispetto all'impostazione delle proprietà DOM, ma possono anche portare a errori imprevisti.
In genere vengono utilizzati su elementi che non hanno proprietà DOM corrispondenti, come colspan . Oppure se il loro utilizzo è realmente necessario, ad esempio per le proprietà HTML durante l'ereditarietà (vedi).
Aggiunta di stili CSS
Vengono aggiunti allo stesso modo delle altre proprietà:
MyElement.style.marginLeft = "2em"
Alcune proprietà specifiche possono essere impostate usando .style , ma se vuoi ottenere valori dopo alcuni calcoli, è meglio usare window.getComputedStyle() . Questo metodo prende l'elemento e restituisce una CSSStyleDeclaration contenente gli stili sia dell'elemento stesso che del suo genitore:
Window.getComputedStyle(myElement).getPropertyValue("margine-sinistra")
Modifica del DOM
Puoi spostare gli elementi:
// Aggiunta di element1 come ultimo figlio di element2 element1.appendChild(element2) // Inserimento di element2 come figlio di element1 prima di element3 element1.insertBefore(element2, element3)
Se non vuoi spostarti, ma devi incollare una copia, usa:
// Crea un clone const myElementClone = myElement.cloneNode() myParentElement.appendChild(myElementClone)
Il metodo .cloneNode() accetta un valore booleano come argomento e, se true, vengono clonati anche gli elementi figlio.
Ovviamente puoi creare nuovi elementi:
const myNewElement = document.createElement("div") const myNewTextNode = document.createTextNode("alcuni testi")
E poi inserirli come mostrato sopra. Non puoi eliminare un elemento direttamente, ma puoi farlo tramite l'elemento genitore:
MyParentElement.removeChild(myElement)
Puoi anche fare riferimento indirettamente:
MyElement.parentNode.removeChild(myElement)
Metodi sugli elementi
Ogni elemento ha proprietà come .innerHTML e .textContent , contengono il codice HTML e, di conseguenza, il testo stesso. L'esempio seguente modifica il contenuto di un elemento:
// Modifica l'HTML myElement.innerHTML = `
nuovo contenuto
bip boop bip boop
` // Questo rimuove il contenuto myElement.innerHTML = null // Aggiungi all'HTML myElement.innerHTML += ` continua a leggere...
In effetti, modificare l'HTML è una cattiva idea, perché perde tutte le modifiche apportate in precedenza e sovraccarica anche i gestori di eventi. È meglio utilizzare questo metodo solo eliminando completamente tutto l'HTML e sostituendolo con una copia dal server. Come questo:
const link = document.createElement("a") const text = document.createTextNode("Continua a leggere...") const hr = document.createElement("hr") link.href = "foo.html" link.appendChild( testo) myElement.appendChild(link) myElement.appendChild(hr)
Tuttavia, ciò comporterà due ridisegni del browser, mentre .innerHTML ne risulterà solo uno. Puoi aggirare questo problema se prima aggiungi tutto a DocumentFragment , quindi aggiungi il frammento di cui hai bisogno:
Const fragment = document.createDocumentFragment() fragment.appendChild(testo) fragment.appendChild(hr) myElement.appendChild(frammento)
Gestori di eventi
Uno dei gestori più semplici:
MyElement.onclick = funzione onclick (evento) ( console.log(event.type + " got licenziato") )
Ma come regola generale, dovrebbe essere evitato. Qui, .onclick è una proprietà dell'elemento, e in teoria puoi cambiarla, ma non sarai in grado di aggiungere altri gestori usando ancora un'altra funzione che fa riferimento a quella precedente.
È meglio usare .addEventListener() per aggiungere gestori. Richiede tre argomenti: il tipo di evento, una funzione che verrà chiamata ogni volta che viene attivata e un oggetto di configurazione (ci arriveremo più avanti).
MyElement.addEventListener("click", function (event) ( console.log(event.type + " got licenziato") )) myElement.addEventListener("click", function (event) ( console.log(event.type + " fu licenziato di nuovo") ))
La proprietà event.target fa riferimento all'elemento a cui è collegato l'evento.
E così puoi accedere a tutte le proprietà:
// La proprietà `forms` è un array contenente collegamenti a tutti i moduli const myForm = document.forms const myInputElements = myForm.querySelectorAll("input") Array.from(myInputElements).forEach(el => ( el.addEventListener(" modifica ", funzione (evento) ( console.log(event.target.value) )) ))
Prevenire le azioni predefinite
Per questo viene utilizzato il metodo .preventDefault(), che blocca le azioni standard. Ad esempio, bloccherà l'invio del modulo se l'autorizzazione lato client non è andata a buon fine:
MyForm.addEventListener("submit", function (event) ( const name = this.querySelector("#name") if (name.value === "(!LANG:Donald Duck") {
alert("You gotta be kidding!")
event.preventDefault()
}
})
!}
Il metodo .stopPropagation() ti aiuterà se hai un gestore di eventi specifico collegato al figlio e un secondo gestore per lo stesso evento collegato al genitore.
Come accennato in precedenza, il metodo .addEventListener() accetta un terzo argomento facoltativo come oggetto di configurazione. Questo oggetto deve contenere una delle seguenti proprietà booleane (tutte impostate su false per impostazione predefinita):
- cattura: l'evento sarà allegato a questo elemento prima di qualsiasi altro elemento sottostante nel DOM;
- una volta: un evento può essere bloccato solo una volta.
- passivo: event.preventDefault() verrà ignorato (eccezione durante l'errore).
La proprietà più comune è .capture ed è così comune che c'è una scorciatoia per essa: invece di passarla in un oggetto di configurazione, basta specificarne il valore qui:
MyElement.addEventListener(tipo, listener, true)
I gestori vengono rimossi utilizzando il metodo .removeEventListener(), che accetta due argomenti: il tipo di evento e un riferimento al gestore da rimuovere. Ad esempio, la proprietà once può essere implementata in questo modo:
MyElement.addEventListener("change", funzione listener (evento) ( console.log(event.type + " è stato attivato su " + this) this.removeEventListener ("change", listener) ))
Eredità
Supponiamo che tu abbia un elemento e desideri aggiungere un gestore di eventi per tutti i suoi elementi figlio. Quindi dovresti scorrerli in loop usando il metodo myForm.querySelectorAll ("input") come mostrato sopra. Tuttavia, puoi semplicemente aggiungere elementi al modulo e verificarne il contenuto con event.target .
MyForm.addEventListener("change", function (event) ( const target = event.target if (target.matches("input")) ( console.log(target.value) ) ))
E un altro vantaggio di questo metodo è che il gestore verrà automaticamente allegato ai nuovi elementi figlio.
Animazione
Il modo più semplice per aggiungere animazioni è utilizzare CSS con la proprietà di transizione. Ma per una maggiore flessibilità (ad esempio, per un gioco), JavaScript è più adatto.
Chiamare il metodo window.setTimeout() fino al termine dell'animazione non è una buona idea, poiché l'applicazione potrebbe bloccarsi, specialmente sui dispositivi mobili. È meglio usare window.requestAnimationFrame() per salvare tutte le modifiche fino al prossimo ridisegno. Prende una funzione come argomento, che a sua volta riceve un timestamp:
const start = window.performance.now() const duration = 2000 window.requestAnimationFrame(function fadeIn (now)) ( const progress = now - start myElement.style.opacity = progress / duration if (progress< duration) {
window.requestAnimationFrame(fadeIn)
}
}
In questo modo si ottiene un'animazione molto fluida. Nel suo articolo, Mark Brown discute questo argomento.
Scrivere la nostra libreria
Il fatto che nel DOM devi ripetere continuamente gli elementi per fare qualcosa con loro può sembrare piuttosto noioso rispetto alla sintassi $(".foo").css((color: "red")) di jQuery. Ma perché non scrivere alcuni dei tuoi metodi per rendere più facile questo compito?
Const $ = funzione $(selettore, contesto = documento) ( elementi const = Array.from(context.querySelectorAll(selettore)) return ( elementi, html (nuovoHtml) ( this.elements.forEach(elemento => ( element.innerHTML = newHtml )) restituisce questo ), css (newCss) ( this.elements.forEach(element => ( Object.assign(element.style, newCss) )) restituisce questo ), on (evento, gestore, opzioni) ( this.elements .forEach(element => ( element.addEventListener(event, handler, options) )) return this ) ) )