Utmaningar
Nedan finns sex frågor om hur webbkomponenter byggs ihop och kommunicerar utan att bli beroende av varandra. Frågorna bygger på veckans artiklar Bra komponenter, Komponenter i komponenter och Kommunikation mellan komponenter.
Varje fråga har ett antal alternativ att välja bland. Klicka på “Visa lösning” för att se svaret och förklaringen.
Om du är osäker kan du alltid testa i webbläsaren själv eller ta upp frågan på en handledning.
Fråga 13-01: Vad är fel i koden?
Section titled “Fråga 13-01: Vad är fel i koden?”export class MyComponent extends HTMLElement { constructor() { super()
this.attachShadow({ mode: 'open' }) this.shadowRoot.append(template.content.cloneNode(true))
const paragraphs = document.querySelectorAll('p')
// ... }}- Shadow DOM ska alltid ha
mode: 'closed'. - Komponenten har ett beroende till dokumentet utanför sig själv.
append()fungerar inte på en shadow-rot.- En komponent ska ärva från
HTMLComponent.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 2. document.querySelectorAll('p') söker igenom hela sidan, inte bara
komponenten. Komponenten hittar då stycken som inte är dess egna, och fungerar bara på en sida som
ser ut på ett visst sätt. Sök från this eller this.shadowRoot i stället. Däremot är det i sin
ordning att använda document.createElement().
Fråga 13-02: Timern
Section titled “Fråga 13-02: Timern”Givet två, av varandra oberoende, element: my-timer och en knapp. När användaren klickar på
knappen ska timern stoppas.
Vilken konstruktion är lämpligast?
- Knappen bäddas in i
my-timer-komponenten. - Knappen anropar timerns publika metod
stop(). - Timern lyssnar på knappens
click-händelse och anropar sin egen metodstop(). index.jslyssnar på knappensclick-händelse och anropar timerns publika metodstop().
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 4.
Alternativ 1 skulle fungera, men faller på att komponenterna bör vara oberoende av varandra. Alternativ 2 är olämpligt även om det skulle fungera: knappen får ett beroende till timern och kan inte användas utan den. Alternativ 3 är samma sak åt andra hållet; nu kan timern inte fungera om inte knappen finns där.
Alternativ 4 är lämpligt. Knappen behöver inte känna till timern och timern behöver inte känna till
knappen. I stället “limmar” index.js, eller en omslutande komponent, ihop dem.
Fråga 13-03: Spelet
Section titled “Fråga 13-03: Spelet”Ett spel innehåller brickor. Vid spelets slut ska samtliga brickors framsida visas.
Vilket är det lämpligaste sättet att åstadkomma detta?
- Brickorna lyssnar på spelets händelse
gameoveroch vänder sig själva. - Spelet anropar respektive brickas metod
flip()för att vända på brickan. - Spelet vänder brickorna genom att sätta attributet
flipped. - Spelet utlöser händelsen
gameover, varpåindex.jsfångar händelsen, söker ut samtliga brickor och ser till att de vänds.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 3.
Alternativ 1 går att få att fungera, men nu uppstår återigen ett beroende åt fel håll: barnen ska inte behöva känna till sin förälder. Det är bättre att föräldern talar om för barnet vad det ska göra. Alternativ 2 är absolut en väg att gå, det fungerar, men att kommunicera ett tillstånd via ett attribut (alternativ 3) är lämpligare, eftersom det härmar hur de inbyggda elementen fungerar.
Alternativ 4 är inte säkert att det ens fungerar, eftersom det förutsätter att brickorna går att nå
från dokumentet och inte ligger i spelets eget shadow DOM. Och även om de gjorde det skulle
index.js börja bero på hur spelet är byggt inuti.
Fråga 13-04: Vad blir tiden?
Section titled “Fråga 13-04: Vad blir tiden?”<my-timer seconds="20"></my-timer>Inuti my-timer står:
const total = this.getAttribute('seconds') + 5Vad blir total?
25'205'NaN- Ett
TypeErrorkastas.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 2. Ett attributs värde är alltid en sträng, även när det ser ut som ett tal.
'20' + 5 slår ihop strängarna till '205'. Komponenten måste göra om värdet till ett tal själv,
gärna med ett standardvärde: Number(this.getAttribute('seconds') ?? 20) + 5.
Fråga 13-05: Var finns bilderna?
Section titled “Fråga 13-05: Var finns bilderna?”En komponent image-gallery används så här:
<image-gallery> <img src="cat.jpg" alt="A cat" /> <img src="dog.jpg" alt="A dog" /></image-gallery>Komponentens mall har ett <slot> där bilderna visas, och två egna knappar, Previous och
Next. Vilket anrop inuti komponenten hittar de två bilderna?
this.querySelectorAll('img')this.shadowRoot.querySelectorAll('img')document.querySelectorAll('img')- Både 1 och 2.
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 1. Bilderna är skrivna av den som använder komponenten och är vanliga barn
till image-gallery i dokumentets träd. Att de visas i komponentens slot flyttar dem inte in i
shadow-trädet, så this.shadowRoot.querySelectorAll('img') ger en tom lista. Knapparna däremot
ligger i shadow-trädet och nås från this.shadowRoot. Alternativ 3 hittar bilderna, men också
alla andra bilder på sidan.
Fråga 13-06: När utlöses slotchange?
Section titled “Fråga 13-06: När utlöses slotchange?”todo-list lyssnar på slotchange på sin slot, där dess todo-item visas. Vilka av de här
ändringarna utlöser slotchange?
- En ny
todo-itemläggs till medlist.append(item). - En
todo-itembockas av, och får attributetdone. - Texten i en
todo-itemändras meditem.textContent = 'Buy oat milk'. - En
todo-itemtas bort meditem.remove().
Lösningsförslag
Section titled “Lösningsförslag”Svaret är alternativ 1 och 4. slotchange utlöses när de element som är tilldelade slotten ändras,
alltså när ett barn kommer eller går.
Ett nytt attribut på ett barn ändrar inte vilka barn som finns. Inte heller ny text i en
todo-item; texten är barn till todo-item, och visas i dess egen slot, inte i listans. Att en
uppgift bockas av får listan veta på annat sätt, genom den händelse todo-item utlöser när
användaren klickar.