Manipulació del DOM
El DOM (Document Object Model) és una representació d'un document HTML com a una estructura d'arbre, on cada node és un objecte que representa una part del document. El DOM es crea quan es carrega una pàgina web, i és manipulable des de JavaScript gràcies a una sèrie d'operacions que permeten:
- Afegir, modificar i esborrar qualsevol element o atribut HTML.
- Canviar qualsevol estil CSS.
- Reaccionar a un esdeveniment.
- Crear nous esdeveniments.
Navegació
Imaginem que tenim aquest senzill document:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Simple DOM example</title>
</head>
<body>
<section>
<img
src="dinosaur.png"
alt="A red Tyrannosaurus Rex" />
<p>
A link to the
<a href="https://www.mozilla.org/">Mozilla homepage</a>
</p>
</section>
</body>
</html>
Per poder manipular el DOM primer cal obtenir una referència a l'element i desar-la a una variable. Podem utilitzar Document.querySelector(), que demana un selector CSS com a paràmetre:
Per exemple:
const link = document.querySelector("a");
També tenim Document.querySelectorAll() per a obtenir una llista d'elements en format NodeList
:
const paragraphs = document.querySelectorAll("p");
paragraphs.forEach((paragraph) => {
// do something with it
});
Tot i que amb aquests dos mètodes en tenim prou per fer queries, hi ha altres (menys flexibles) com Document.getElementById()
, Document.getElementsByTagName()
, Document.getElementsByClassName()
, etc. També podem veure els elements fill amb la propietat children
, que retorna una col.lecció HTMLCollection
.
Si volem moure'ns cap a dalt, podem utilitzar la propietat parentElement
o el mètode closest(selector)
. També ens podem moure cap als costats amb nextElementSibling
i previousElementSibling
.
Selectors CSS
Aquests són els selectors més habituals:
- Tots:
*
- Etiqueta:
head
- Classe:
.red
- ID:
#nav
- Etiqueta i classe:
div.row
- Valor d'atribut:
[aria-hidden="true"]
- Fills d'un altre element:
li a
- Fills directes:
li > a
- Tots dos selectors:
li, a
Pseudo-selectors:
- Primer fill:
:first-child
- Últim fill:
:last-child
- Element amb hover o focus:
:hover
,:focus
- Element clicat:
:active
- Enllaços no clicats o clicats:
:link
,:visited
Modificació
També podem afegir nous nodes al DOM. Si es volgués afegir un paràgraf a la secció seria així:
const sect = document.querySelector("section");
const para = document.createElement("p");
para.textContent = "We hope you enjoyed the ride.";
sect.appendChild(para);
També podem esborrar un element de diferents formes:
sect.removeChild(para);
para.remove();
para.parentNode.removeChild(para);
Una altra opció és utilitzar un template de l'html i clonar-lo al contingut.
<main class="container">
<h1>Template!</h1>
</main>
<template id="message">
<section>
<h2 class="heading"></h2>
<p class="text"></p>
</section>
</template>
const template = document.querySelector("#message");
const message = template.content.cloneNode(true);
message.querySelector('.heading').textContent = 'A title';
message.querySelector('.text').textContent = 'Some text here';
const main = document.querySelector('main');
main.appendChild(message);
Atributs i propietats
Els elements tenen atributs i propietats:
- Els atributs són les característiques dels elements que apareixen dins de l'etiqueta, i es defineixen a l'HTML. S'anomenen amb paraules separades per guions. Exemple:
src
,alt
. - Les propietats defineixen el comportament intern i la funcionalitat d'un element, i es manipulen des de JS. S'anomenen amb camel case. Exemple:
value
oinnerHTML
.
Tenim Element.getAttribute()
, Element.setAttribute()
, Element.removeAttribute()
i Element.hasAttribute()
per a gestionar els atributs d'un element. Les propietats, en canvi, es manipulen com a propietats de l'objecte.
Podem tenir atributs que tenen una representació com a propietats. Per exemple, id
és un atribut i propietat. Normalment es mantenen sincronitzats:
p.setAttribute("id", "one");
let id1 = p.getAttribute("id"); // one
let id2 = p.id; // one
En canvi, les propietats d'un formulari modificables per l'usuari (value
, checked
, selected
) no estan sincronitzades: l'atribut és el valor inicial de l'HTML i la propietat, l'actual. De fet, Document.setAttribute()
només canvia el valor si no ho ha fet l'usuari, però value
ho fa sempre.
La manipulació d'estils es realitza accedint a la propietat HTMLElement.style
. També és molt útil la propietat Element.classList
, que retorna una DOMTokenList
. Aquesta permet afegir i esborrar classes d'un element amb els mètodes add
i remove
, per exemple.
Atributs data
Val la pena parlar dels atributs data. Se solen utilitzar des de JS, en contraposició als atributs HTML, ja que no tenen significat a l'hora de visualitzar un element.
<div class="expand" data-expand>
<p>Some content that can be collapsed or expanded.</p>
<button data-click="sayHi">Say Hello!</button>
<button data-click="showMore">Show More</button>
</div>
let accordion = document.querySelector('[data-expand]');
let btnHi = document.querySelector('[data-click="sayHi"]');
let btnMore = document.querySelector('[data-click="showMore"]');
En general, si volem manipular amb JS els elements del DOM, és preferible utilitzar atributs data, que no condicionen la creació d'IDs o classes, que habitualment associem als estils CSS.
Esdeveniments
Podem afegir esdeveniments associats a un node del DOM utilitzant EventTarget.addEventListener()
. La sintaxi més habitual inclou dos paràmetes:
- El
type
indica quin tipus d'esdeveniment vol escoltar-se. - El listener sol ser una funció callback que rep un objecte Event. Aquest objecte té tres propietats interessants:
type
, el tipus d'esdeveniment;target
, l'element que ha generat l'esdeveniment; icurrentTarget
, el que té associat el listener. Aquestes dues últimes poden ser diferents perquè els esdeveniments són bombolles que pugen.
Exemple de l'efecte bombolla.
<main class="container">
<h1>Bubbling!</h1>
<button id="b1">First!</button>
<button id="b2">Second!</button>
</main>
const main = document.querySelector("main");
const btn1 = document.querySelector("#b1");
const btn2 = document.querySelector("#b2");
function handle(e) {
console.log(`${e.type} target: ${e.target.tagName}, current: ${e.currentTarget.tagName}`);
}
document.body.addEventListener("click", handle);
main.addEventListener("click", handle);
btn1.addEventListener("click", handle);