Översikt
Nu är det dags att programmera mot ett av webbläsarens äldsta, men samtidigt, viktigaste API; DOM, Document Object Model. Information om veckans upplägg annonseras löpande i kursens Zulip-kanal.
Stödjande inspelningar
Section titled “Stödjande inspelningar”HTML Element and the Document Object Model
Section titled “HTML Element and the Document Object Model”DOM-API:et
Section titled “DOM-API:et”Document Object Model (DOM) är det API som exponerar webbläsarens interna renderingsmotor mot JavaScript-motorn – vägen in för att läsa och ändra sidans innehåll efter att den laddats. document är ingången till API:et: allt arbete mot DOM:en börjar med document. något.
DOM:en är en trädstruktur av noder. Element (<h1>, <article> osv.) är en typ av nod, men inte den enda: text mellan taggar blir en egen textnod, kommentarer blir en kommentarsnod, och dokumentet i sig är en dokumentnod. Alla element är noder, men långt ifrån alla noder är element. Även whitespace – radbrytningar och indenteringar mellan taggar, skrivna för läsbarhetens skull – blir enligt standarden egna textnoder, vilket historiskt orsakade skillnader mellan webbläsare (Internet Explorer hoppade förbi dem, andra webbläsare gjorde det inte). Praktiskt spelar det knappt någon roll längre, men det förklarar varför ett träd kan innehålla fler noder än man förväntar sig.
Att välja ut noder
Section titled “Att välja ut noder”Ett äldre gäng metoder finns direkt på document: getElementById (ett element), getElementsByTagName och getElementsByClassName (en HTMLCollection vardera). Dessa är standardiserade av W3C med långa, explicita namn, och getElementById dyker upp i praktiskt taget alla exempel som finns – värt att känna igen även om den används mer sällan i praktiken idag.
Modernare och att föredra är Selectors API: querySelector (returnerar ett element, det första som matchar) och querySelectorAll (returnerar en NodeList), båda med vanliga CSS-selektorer som argument – samma selektorsyntax som redan används i CSS, istället för en egen uppsättning metoder per sökkriterium.
const container = document.querySelector('#container')const posts = document.querySelectorAll('.post')En viktig skillnad mellan de två returtyperna: en HTMLCollection (från de äldre metoderna) är live – ändras DOM:en efteråt uppdateras samlingen automatiskt. En NodeList från querySelectorAll är statisk, ett snapshot av hur DOM:en såg ut vid anropet. Ingen av dem är en riktig array (NodeList.prototype !== Array.prototype): forEach fungerar på en NodeList, men inte map/filter/reduce. Behövs de, konverteras samlingen till en array med Array.from(nodeList) eller spridningsoperatorn.
Navigera i trädet
Section titled “Navigera i trädet”Utifrån en känd nod kan man ta sig till närliggande noder: parentNode (alltid ett element, eftersom bara element kan ha barn), firstChild/lastChild (kan vara en textnod), firstElementChild/lastElementChild (garanterat ett element), samt previousElementSibling/nextElementSibling för att röra sig mellan syskon. childNodes ger alla barnnoder inklusive text, medan children bara ger elementnoder.
Historiskt skiljde sig firstChild mellan webbläsare (Microsoft implementerade den så att den hoppade direkt till första elementet, andra följde standarden och gav den första noden oavsett typ) – ännu ett skäl att hålla isär “nod” och “element” som begrepp, även om skillnaden i praktiken är utjämnad sedan länge.
Skapa och manipulera
Section titled “Skapa och manipulera”Nya noder skapas med document.createElement('p') (tomt element) och document.createTextNode('text') (textnod) – de måste därefter kopplas ihop och in i trädet för att synas:
const link = document.createElement('a')const text = document.createTextNode('lnu.se')link.appendChild(text)container.appendChild(link)appendChild lägger till sist bland ett elements barn; körs den på en nod som redan finns i trädet flyttas den istället till den nya platsen (ingen manuell borttagning behövs). Övriga byggstenar: insertBefore(newNode, referenceNode), replaceChild(newNode, oldNode), removeChild(oldNode) och cloneNode(deep) – där deep (true/false) avgör om hela undernodträdet kopieras med eller bara den översta noden.
Genvägar – och en säkerhetsfälla
Section titled “Genvägar – och en säkerhetsfälla”Att bygga upp text manuellt med createTextNode + appendChild för varje liten ändring blir fort omständligt. Två genvägar täcker det vanligaste fallet:
element.textContent = 'Ny text' // ersätter allt innehåll med en textnodelement.innerHTML = '<p>Text</p>' // parsar strängen till element + textnoderinnerHTML känns bekvämt eftersom den tolkar HTML-taggar i strängen – men det är samtidigt dess fara. Kommer strängen från en användare eller annan extern källa och innehåller <script>-taggar, exekveras den koden på sidan (XSS). innerHTML ska bara användas när innehållet är fullt kontrollerat, till exempel en hårdkodad sträng i källkoden – aldrig på text som kommer utifrån. Standardvalet ska vara textContent, som bara någonsin skapar text, aldrig kod. Ett vanligt undantag där innerHTML ändå är okej: tömma ett element genom att sätta det till en tom sträng.
Attribut
Section titled “Attribut”getAttribute('namn'), setAttribute('namn', värde) och removeAttribute('namn') läser, sätter respektive tar bort HTML-attribut. Ett exempel som visar nyttan: skapa ett <img>-element och sätta src via setAttribute – webbläsaren börjar hämta bilden i bakgrunden så fort attributet sätts, redan innan elementet lagts in i DOM-trädet, och renderar den så fort både bilden är hämtad och elementet infogats.
De flesta inbyggda HTML-attribut har också en motsvarande egenskap direkt på elementobjektet (element.href, element.className osv.) – Microsofts genväg till samma resultat som setAttribute. Den fungerar, men lämnar mindre kontroll: skillnaden blir tydlig med className kontra classList (se nedan).
Håll isär HTML, CSS och JavaScript
Section titled “Håll isär HTML, CSS och JavaScript”Att bygga upp komplex HTML-struktur genom en lång kedja av createElement/appendChild-anrop blir snabbt oläsligt – samma princip som att dela upp kod i funktioner och moduler gäller nu även uppdelningen mellan HTML, CSS och JavaScript. Att blanda dem (till exempel bygga hela strukturer i JavaScript istället för att skriva HTML som HTML) gör koden svår att underhålla trots att den gör något enkelt.
Ett konkret exempel på varför disciplinen kring det här spelar roll: att sätta en CSS-klass fel kan orsaka riktiga buggar. element.className = 'danger' ersätter hela klasslistan (det är i grunden ett set), medan element.classList.add('danger') lägger till utan att röra befintliga klasser. Ett element med klassen important som får en ny klass satt via className tappar important helt – en bugg som är lätt att missa eftersom koden ser rimlig ut. classList har också remove, toggle och contains för att slippa den typen av misstag.
Sätts stilar direkt via JavaScript (element.style.color = 'red') skrivs CSS-egenskapsnamn i camelCase istället för med bindestreck (fontSize, marginLeft), eftersom bindestreck inte är giltigt i JavaScript-identifierare. float är dessutom ett reserverat ord i JavaScript, så CSS-egenskapen float heter style.cssFloat. Bättre än att sätta enskilda stilar via style är oftast att lägga stilreglerna i CSS och bara växla class via classList – exakt samma princip som att hålla HTML utanför JavaScript.
Templates
Section titled “Templates”<template>-elementet löser problemet med att bygga upp återkommande HTML-strukturer i JavaScript: innehållet i en <template>-tagg parsas av webbläsaren men renderas aldrig och hämtar inte externa resurser (bilder m.m.) förrän det faktiskt klonas ut i dokumentet.
const template = document.querySelector('#post-template')const clone = template.content.cloneNode(true)document.body.appendChild(clone)template.content.cloneNode(true) är närmast pannplattekod – lätt att glömma .content. Går det inte att ha en <template> i själva HTML-dokumentet (till exempel för att den behöver skapas dynamiskt i en komponents JavaScript-fil), kan samma HTML skrivas som en template-sträng direkt i JavaScript-koden istället; det finns (2021) inget standardiserat, snyggt sätt att importera en fristående .html-fil in i en JavaScript-modul. <template> har funnits i webbläsaren i över tio år och föregår de externa mallmotorer (Mustache, Handlebars, Pug m.fl.) som fortfarande används mycket på serversidan.
Kursen väljer genomgående att undervisa webbstandarder snarare än bibliotek som jQuery eller ramverk som React/Angular – standarderna är det som håller över tid, medan enskilda bibliotek och ramverksversioner kommer och går.
Läsanvisningar
Section titled “Läsanvisningar”Under läsanvisningar hittar du veckans kapitel som ska läsas i kurslitteraturen. Planera upp och dela upp läsningen under veckan.
Övningar
Section titled “Övningar”Under Övningsuppgifter hittar du övningsuppgifter som det är lämpligt att du arbetar med under veckan.
Handledning
Section titled “Handledning”Skicka gärna in frågor på förhand via formuläret som du når här:
Examinationsuppgift A3
Section titled “Examinationsuppgift A3”Nu bör du verkligen vara igång och arbeta med examinationsuppgiften. Kanske bör du faktiskt ha som mål att färdigställa denna under denna kursvecka.
Läs mer om “Examinationsuppgift A3 - Tjugoett” här
Utmaningar
Section titled “Utmaningar”Veckans utmaningar hjälper dig att stämma av hur bra du kan DOM och hur JavaScript integreras i webbläsaren.
Studiefrågor
Section titled “Studiefrågor”Här är ett antal frågor som du kan försöka besvara och diskutera tillsammans med din AI-assistent. Tveka inte att lyfta någon av frågorna till handledningen för att fortsätta diskussionen med lärare och studiekamrater.
- Ge en kort förklaring av vad DOM är och hur det fungerar i webbläsaren.
- Kan jag från JavaScript modifiera hela webbsidans innehåll och style utifrån JavaScript?
- Om jag vill lägga till en ul/li lista med JavaScript, hur gör man det?
- Kan jag skapa en bild i form av ett img element och lägga in i webbsidan med js?
- Vill du förklara begreppet
classlistoch hur man kan använda det för att sätta eller ta bort klasser på ett html-element? - Förklara vad HTML templates är.
- Hur förhåller sig html templates när man pratar om webbkomponenter?
Här är en utskrift från hur det såg ut när jag diskuterade ovan frågor med min AI-assistent Ajj.
Här följer tips till hur du kan prioritera bland kursmaterialet för att komma igång med veckan.
- Det viktiga är att slutföra A3. Vänta inte in i det sista. Satsa på att göra din MR redan nu på fredag så har du extra tid, om det behövs.
- Försök prioritera så du också hinner med att titta på föreläsningen. Du behöver inte komma ihåg allt som händer i föreläsningen men titta lite extra noga på sista delen som handlar om HTML templates.
- När du tittat på föreläsningarna så finns det en (blå) övningsuppgift där du får möjlighet att testa att programmera mot DOM och med HTML templates i webbläsaren.
