Hoppa till innehåll

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.

export class MyComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.append(template.content.cloneNode(true))
const paragraphs = document.querySelectorAll('p')
// ...
}
}
  1. Shadow DOM ska alltid ha mode: 'closed'.
  2. Komponenten har ett beroende till dokumentet utanför sig själv.
  3. append() fungerar inte på en shadow-rot.
  4. En komponent ska ärva från HTMLComponent.

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().

Givet två, av varandra oberoende, element: my-timer och en knapp. När användaren klickar på knappen ska timern stoppas.

Index.js innehållandes två av varandra oberoende komponenter. En klockkomponent och en knapp döpt button

Vilken konstruktion är lämpligast?

  1. Knappen bäddas in i my-timer-komponenten.
  2. Knappen anropar timerns publika metod stop().
  3. Timern lyssnar på knappens click-händelse och anropar sin egen metod stop().
  4. index.js lyssnar på knappens click-händelse och anropar timerns publika metod stop().

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.

Ett spel innehåller brickor. Vid spelets slut ska samtliga brickors framsida visas.

Index.js innehållande komponenten "the-game". Denna komponent innehåller i sin tur sex spelbrickor, döpta "game-tile"

Vilket är det lämpligaste sättet att åstadkomma detta?

  1. Brickorna lyssnar på spelets händelse gameover och vänder sig själva.
  2. Spelet anropar respektive brickas metod flip() för att vända på brickan.
  3. Spelet vänder brickorna genom att sätta attributet flipped.
  4. Spelet utlöser händelsen gameover, varpå index.js fångar händelsen, söker ut samtliga brickor och ser till att de vänds.

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.

<my-timer seconds="20"></my-timer>

Inuti my-timer står:

const total = this.getAttribute('seconds') + 5

Vad blir total?

  1. 25
  2. '205'
  3. NaN
  4. Ett TypeError kastas.

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.

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?

  1. this.querySelectorAll('img')
  2. this.shadowRoot.querySelectorAll('img')
  3. document.querySelectorAll('img')
  4. Både 1 och 2.

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.

todo-list lyssnar på slotchange på sin slot, där dess todo-item visas. Vilka av de här ändringarna utlöser slotchange?

  1. En ny todo-item läggs till med list.append(item).
  2. En todo-item bockas av, och får attributet done.
  3. Texten i en todo-item ändras med item.textContent = 'Buy oat milk'.
  4. En todo-item tas bort med item.remove().

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.

CCBY