← Område 6 Guide · JavaScript och DOM Felsökning

Webbutveckling · område 6

Guide – JavaScript och DOM

Här står allt som prov 4 kan fråga om, och allt du behöver för Projekt 5. Guiden fungerar utan att du varit på lektionen. Avsnitt 1 till 6 följer områdets första sex pass i tur och ordning.

Exemplen är hämtade ur referensprojektet Fyrar längs Bohuskusten – samma sajt som i område 5, nu med tre skript och en ljudfil. Öppna referensprojektet i webbläsaren och klicka på knappen Mörkt läge i sidhuvudet. Ha sedan mappen skript/ öppen i din kodeditor bredvid guiden. Kommentarerna i filerna är märkta med passets nummer, Pass 47 och Pass 48, där något hör till det här området.

Här handlar allt om koden

Vilka ljud sajten ska ha, hur långa rörelserna är och varför bestäms i Gränssnittsdesign. Guiden visar koden som spelar ljudet och sätter klassen, och säger i en mening var resten står.

Två filer får du kopiera, resten skriver du av

Ljudmodulen ljud.js och morkt-lage.js står i sin helhet i guiden, och de två går att markera och kopiera. De är färdiga moduler, och E-nivån i Projekt 5 är att koppla in dem, inte att skriva dem. All annan kod i guiden skriver du av. Det är när du skriver den själv som du märker var parenteserna sitter.

Vad du kan efter guiden

1. Vad JavaScript gör

Hittills har din kod varit något: HTML säger vad som finns, CSS hur det ser ut. JavaScript är webbens programmeringsspråk, och det gör något när någon använder sajten. Det körs i webbläsaren, på besökarens dator, och det reagerar på det som händer på sidan: ett klick, en tangent, ett fält som fylls i.

Klicka på lägesknappen i referensprojektet. Det låter, och färgerna byts. Öppna sedan sidkällan med Ctrl + U och leta upp knappen. Det enda på knappen som har med ljudet att göra är ett attribut, data-ljud="vaxla". HTML kan inte spela ett ljud när någon klickar, och CSS kan det inte heller. Det gör en tredje sorts fil.

Tre språk, tre filer

SpråkSvarar påI fyrsajten
HTMLVad finns på sidan?Knappen med texten Mörkt läge
CSSHur ser det ut?Knappens färg, storlek och hover
JavaScriptVad händer när någon gör något?Ljudet när knappen klickas

De tre ligger i var sin fil, och det är ett krav i Projekt 5, inte en stilfråga. En JavaScript-fil slutar på .js och ligger i mappen skript/, precis som bilderna ligger i bilder/. Referensprojektet ser ut så här:

index.html
fyrarna.html
vinga.html
rapportera.html
tack.html
stil.css
dokumentation.md
bilder/
ljud/
  vaxla.mp3
skript/
  ljud.js
  falt.js
  morkt-lage.js

En modul är en färdig bit kod som gör en sak och går att koppla in i en sajt som inte byggdes för den. Ljudmodulen spelar ljud. Den vet ingenting om fyrar.

JavaScript har ingenting med språket Java att göra, bara namnet. Det valdes för att Java var populärt 1995.

Ljudmodulen

Hela filen, skript/ljud.js. Läs den en gång utan att försöka förstå allt. Lägg märke till att den är kort. Resten av avsnittet går igenom den, ett begrepp i taget.

// Ljudmodulen. Spelar ett ljud när något händer med ett element som har
// attributet data-ljud. Värdet är filnamnet utan .mp3:
//   <button data-ljud="vaxla">
// Utan något annat är det ett klick. data-ljud-nar väljer en annan händelse:
//   <input data-ljud="klart" data-ljud-nar="change">
// Ändra inställningarna nedan. Resten behöver du inte röra.

// Inställningar
const LJUDMAPP = "ljud/";  // var ljudfilerna ligger, räknat från sidan
const VOLYM = 0.5;         // 0 är tyst, 1 är fullt

// Spelar filen som heter namn. Gör ingenting om ljudet är avstängt.
function spela(namn) {
  if (document.documentElement.classList.contains("ljud-av")) {
    return;
  }
  const ljud = new Audio(LJUDMAPP + namn + ".mp3");
  ljud.volume = VOLYM;
  ljud.play();
}

// Hitta alla element med data-ljud och lyssna efter händelsen på vart och ett.
const ljudelement = document.querySelectorAll("[data-ljud]");

for (const element of ljudelement) {
  let handelse = "click";
  if (element.dataset.ljudNar) {
    handelse = element.dataset.ljudNar;
  }
  element.addEventListener(handelse, function () {
    spela(element.dataset.ljud);
  });
}

Kommentarer

Rader som börjar med // är till för människor. Webbläsaren hoppar över dem. Det är samma sak som /* */ i CSS och <!-- --> i HTML, fast för en rad i taget. Läs de sex första raderna: de säger vad modulen gör, hur den styrs från HTML:en och vad du får ändra.

Variabel: const och let

En variabel är ett namn på ett värde.

const LJUDMAPP = "ljud/";  // var ljudfilerna ligger, räknat från sidan
const VOLYM = 0.5;         // 0 är tyst, 1 är fullt
DelVad den gör
constHär kommer en variabel vars värde inte byts ut
LJUDMAPPNamnet. Versaler betyder att det är en inställning
=Sätter värdet. Inte är lika med som i matten
"ljud/"Värdet: en text. Text står alltid inom citattecken
;Raden är slut

En text inom citattecken kallas en sträng, engelska string. VOLYM är ett tal, och tal står utan citattecken. Skriver du "0.5" är det en sträng som råkar innehålla siffror.

Längre ned i filen står let. Det är en variabel vars värde får bytas. Använd const när värdet inte ska bytas och let när det ska. Det gamla ordet var används inte i kursen; det står i uppgiftens tekniska krav.

En variabel är som en variabel i CSS. --accent är ett namn på en färg, och ändrar du värdet på ett ställe slår det igenom överallt. Här är det mappen och volymen.

Funktion, parameter och anrop

En funktion är några rader med ett namn, som körs när någon anropar namnet.

function spela(namn) {
  if (document.documentElement.classList.contains("ljud-av")) {
    return;
  }
  const ljud = new Audio(LJUDMAPP + namn + ".mp3");
  ljud.volume = VOLYM;
  ljud.play();
}

Att skriva en funktion och att anropa den är två olika saker. Raderna ovan spelar ingenting när sidan laddas. De väntar tills någon skriver namnet med parenteser efter, ett anrop:

spela("vaxla");

Just då är namn lika med "vaxla", och funktionen spelar ljud/vaxla.mp3.

Villkor

De tre första raderna i funktionen är ett villkor:

  if (document.documentElement.classList.contains("ljud-av")) {
    return;
  }

Läs det som en mening: om html-elementet har klassen ljud-av, sluta här.

Ingenting i referensprojektet sätter ljud-av

Villkoret väntar på en ljudknapp som sätter klassen ljud-av på html. Den knappen skriver du själv. Det är ett bör-krav, nivå C, i Projekt 5. Vad den behöver står i avsnitt 4.

Loop och händelse

Sista delen hittar elementen och lyssnar på dem.

const ljudelement = document.querySelectorAll("[data-ljud]");

querySelectorAll hittar alla element som matchar en selektor, och selektorn skrivs som i CSS: [data-ljud] är alla element som har attributet data-ljud. Variabeln ljudelement blir en lista över dem. Hur det går till står i avsnitt 2.

for (const element of ljudelement) {
  let handelse = "click";
  if (element.dataset.ljudNar) {
    handelse = element.dataset.ljudNar;
  }
  element.addEventListener(handelse, function () {
    spela(element.dataset.ljud);
  });
}

dataset läser attribut som börjar med data-. Prefixet tas bort, bindestrecken försvinner och bokstaven efter ett bindestreck blir stor:

I HTML:enI JavaScript
data-ljudelement.dataset.ljud
data-ljud-narelement.dataset.ljudNar

Följ ett klick

Lägesknappen har data-ljud="vaxla" och inget data-ljud-nar. Så här går ett klick, med ljudmodulen bredvid dig:

  1. När sidan laddas går loopen ett varv för knappen. handelse blir "click", och en lyssnare läggs på knappen.
  2. Någon klickar. Lyssnarens funktion körs.
  3. Den anropar spela("vaxla").
  4. Villkoret: har html klassen ljud-av? Nej, så funktionen fortsätter.
  5. new Audio skapar ett ljud ur "ljud/vaxla.mp3", volymen sätts till 0.5, och play() spelar det.
Loopen körs en gång, lyssnaren varje gång

Loopen körs en gång, när sidan laddas. Den sätter upp lyssnarna. Det som står inuti lyssnaren körs först när någon klickar, och sedan varje gång någon klickar. Det är områdets svåraste tanke. Koden läses inte bara uppifrån och ned en gång.

Hela modulen styrs alltså från HTML:en. Vilket ljud står i data-ljud, när i data-ljud-nar. Filen behöver du inte öppna för att använda den.

Prova själv

Läsövningen från passet. Svara med egna ord, inte med kodens ord, och med ljudmodulen framför dig.

  1. Vad säger kommentarraderna överst? Vem är de skrivna för?
  2. En knapp har data-ljud="skicka-klart". Vilken fil försöker modulen spela? Skriv hela sökvägen.
  3. Vad händer om VOLYM ändras till 0? Spelas ljudet?
  4. html-elementet har klassen ljud-av. Någon klickar på lägesknappen. Vilka rader körs, och vilka körs inte?
  5. Ett fält har data-ljud="klart" och data-ljud-nar="change". Vilket värde får handelse? När spelas ljudet, och vilken fil?
  6. Varför står det let framför handelse, när nästan allt annat har const?
  7. En sida har tre element med data-ljud. Hur många gånger körs raden med addEventListener, och när?
  8. En sida har inget element med data-ljud alls. Vad händer? Blir det ett fel?
  9. Ljudfilerna flyttas till mappen media/ljud/. Vilken rad ändrar du, och till vad?
  10. Beskriv med tre meningar, utan kodord, vad modulen gör.

Fråga 7 är den som oftast blir fel. Peka på var loopen börjar och slutar, och fråga dig när loopen körs. Fråga 8 har ett lugnande svar: listan är tom, loopen går noll varv, och ingenting händer. Inget fel. Modulen går att koppla in på varje sida, också på sidor utan ljud.

Projekt 5 är en trappa

Uppdraget i Projekt 5 är att bygga in ljuden och mikrointeraktionerna från Gränssnittsdesign i din egen sajt, med JavaScript.

NivåVad du gör med JavaScript
EKopplar in och anpassar en färdig modul från referensprojektet, efter överlämningen
CSkriver egen, kort händelsekod för en mikrointeraktion och en ljudknapp
ASkriver en egen liten modul som fungerar på element den inte byggdes för

Ljudmodulen är E-nivåns modul. Du behöver inte ha skrivit den, men du behöver förstå den så väl att du kan koppla in den och ändra det som får ändras. Två principer gäller hela uppgiften, och de kommer tillbaka i varje avsnitt:

2. DOM och konsolen

Ljudmodulen använder querySelectorAll och classList. Båda arbetar med något som har ett namn: DOM, sidan som webbläsaren håller i minnet medan den visas. Det är den JavaScript läser och ändrar, aldrig HTML-filen.

Filen och trädet

Öppna Elements-fliken i DevTools, F12, dubbelklicka på texten i sidans h1 och skriv något annat. Rubriken ändras direkt. Ladda om, och den är tillbaka. Du ändrade inte filen, du ändrade webbläsarens kopia av sidan.

DOM står för Document Object Model, dokumentobjektsmodellen. När webbläsaren läser HTML-filen bygger den upp sidan som ett träd av element: html överst, head och body under det, header, main och footer under body, och så vidare nedåt. Ett element är en del av trädet, till exempel en knapp eller en rubrik.

Jämför lägesknappen på två ställen. Ctrl + U visar filen, som den ligger på disken, och där står hidden på knappen. Elements-fliken visar trädet, och där står inget hidden. Ett skript har ändrat trädet efter att filen lästes; vilket står i avsnitt 4. Elements-fliken visar alltså DOM som den ser ut just nu. Sidkällan visar hur sidan såg ut när den kom.

Konsolen

Konsolen, engelska console, är en flik i DevTools där du skriver JavaScript och får svaret direkt. Det du skriver gäller sidan som är öppen, och ingenting sparas i filerna.

TangenterGör
Ctrl + Shift + JÖppnar konsolen i Chrome och Edge
Ctrl + Shift + KÖppnar konsolen i Firefox
Pil uppHämtar förra raden du skrev
TabFyller i resten av ett ord konsolen känner igen
Ctrl + LTömmer konsolen

querySelector och querySelectorAll

Skriv i konsolen på referensprojektet, en rad i taget, och tryck Enter efter varje:

document.querySelector("h1");

document är hela sidan. querySelector hittar det första elementet som matchar selektorn, och selektorn skrivs precis som i CSS, inom citattecken. Konsolen svarar med elementet. Håll musen över svaret, så lyser elementet upp på sidan.

document.querySelectorAll("nav a");

querySelectorAll hittar alla. Svaret är en lista, och siffran visar hur många. Det är den raden ljudmodulen använder, med "[data-ljud]".

document.querySelector(".finns-inte");

Svaret är null, som betyder ingenting. Ingen varning, inget fel, bara tomt. Ordet kommer tillbaka i ett felmeddelande i avsnitt 3.

Ändra texten

document.querySelector("h1").textContent = "Fyrar i hela Sverige";

textContent är elementets text. Med = byts den ut, samma = som sätter en variabel.

Ändra klassen

Det här är ändringen som betyder mest i området.

document.documentElement.classList.add("morkt");

Hela sajten blir mörk. classList är elementets klasser, och add lägger till en. JavaScript ändrade inga färger. Det satte en klass, och färgerna fanns redan i CSS:en och väntade på den.

Prova de andra tre, en i taget:

document.documentElement.classList.remove("morkt");
document.documentElement.classList.toggle("morkt");
document.documentElement.classList.contains("morkt");
MetodGör
addLägger till klassen
removeTar bort klassen
toggleVäxlar: finns klassen tas den bort, annars läggs den till
containsFrågar om klassen finns. Svarar true eller false

contains är frågan i ljudmodulens villkor. Prova den på riktigt:

document.documentElement.classList.add("ljud-av");

Klicka på lägesknappen. Färgerna byts, men det är tyst. Villkoret i spela hittade klassen.

Ändra attribut

document.querySelector(".lage-knapp").hidden = true;

Knappen försvinner. hidden är ett HTML-attribut som döljer ett element, och i JavaScript är det true eller false. Med false kommer knappen tillbaka.

document.querySelector("nav a").setAttribute("href", "vinga.html");

setAttribute sätter vilket attribut som helst: namnet först, värdet sedan. Klicka på Start i menyn, så hamnar du på Vinga-sidan. Titta i Elements-fliken: href är ändrat. Ladda om, och allt är som förut.

Vad du ändrarMedI Projekt 5
TextentextContentSällan. Innehållet ska stå i HTML:en, se avsnitt 5
KlassenclassListNästan alltid. Så byter JavaScript läge
Attributhidden, setAttribute Knappar som visas av skriptet, aria-pressed
Skriv inte const knapp i konsolen på fyrsajten

Referensprojektets skript för mörkt läge har redan en variabel som heter knapp, och const knapp i konsolen ger ett fel om att namnet redan finns. Skriv hela document.querySelector(…) i stället, som i exemplen. Varför står i avsnitt 4.

Prova själv

Öppna din egen sajt och konsolen. Gör det här, en sak i taget:

  1. Byt texten i din h1.
  2. Räkna länkarna i din navigering med querySelectorAll. Stämmer siffran?
  3. Lägg till en klass som redan finns i din stil.css på ett element som inte har den. Ser elementet ut som de andra med den klassen?
  4. Dölj ett element med hidden, och ta tillbaka det.
  5. Ändra ett attribut: href på en länk, eller alt på en bild. Kontrollera ändringen i Elements-fliken.
  6. Leta efter något som inte finns. Skriv en selektor med ett stavfel. Vad svarar konsolen?
  7. Ladda om. Vad finns kvar?

Tre saker som brukar gå fel: querySelector("knapp") letar efter ett element som heter knapp, inte en klass, och svarar null. Punkten saknas. queryselector med litet s ger ett rött fel, is not a function, eftersom JavaScript skiljer på stora och små bokstäver. Och kod som skrivs i Elements-fliken gör ingenting; den fliken tar HTML, inte JavaScript.

3. Händelser

I konsolen ändrade du sidan för hand. Nu händer samma ändring när någon gör något: klickar, trycker på en tangent, lämnar ett fält. Det är en händelse, och koden som väntar på den skrivs med addEventListener. Kod som lyssnar gör ingenting förrän något händer.

addEventListener

Raderna ur ljudmodulen igen:

  element.addEventListener(handelse, function () {
    spela(element.dataset.ljud);
  });
DelVad den säger
elementVem som ska lyssnas på
.addEventListener(Lägg till en lyssnare
handelseVilken händelse. Här en variabel som är "click" om inte data-ljud-nar säger annat
function () { … }Vad som ska hända när den kommer
});Funktionens klammer, sedan parentesen, sedan raden slut

Oftast står händelsens namn direkt, inom citattecken: "click". Ljudmodulen har det i en variabel för att HTML:en ska kunna välja. Tre händelser räcker för området:

HändelseNär den kommer
clickNågon klickar, eller trycker Enter eller mellanslag på en knapp
changeEtt fält har ändrats och lämnats
keydownEn tangent trycks ned

Ett element kan ha flera lyssnare. Skriv i konsolen på fyrsajten:

document.querySelector(".lage-knapp").addEventListener("click", function () {
  console.log("klick");
});

console.log skriver i konsolen. Det syns inte på sidan, bara för den som har DevTools öppet. Klicka på knappen: ljudmodulens lyssnare spelar ljudet, en annan byter färgerna, och din skriver klick. Tre lyssnare på samma knapp, som inte vet om varandra.

I Gränssnittsdesign den 22 februari hette det här mikrointeraktionens utlösare. I koden är utlösaren en händelse.

Varför inget onclick i HTML:en

Det finns ett äldre sätt, och du kommer att hitta det när du söker på nätet:

<!-- Fungerar, men blandar beteende och struktur -->
<button type="button" onclick="spela('vaxla')">Mörkt läge</button>

Det fungerar, och det är ändå förbjudet i Projekt 5, som ska-krav. Tre skäl:

data-ljud="vaxla" är information: vilket ljud. onclick är beteende. Informationen får stå i HTML:en, beteendet hör hemma i .js-filen. Samma sak gäller onload, onchange och alla andra attribut som börjar på on.

Fältet: JavaScript byter klass, CSS rör

Öppna rapportera.html i referensprojektet. Skriv Vinga i fältet Fyrens namn. Ingenting händer medan du skriver. Tryck Tab. Fältets kant blir grön, och en stapel syns till vänster. Töm fältet och tabba ut igen, så försvinner stapeln. Det hände när fältet lämnades, och bara för att det var rätt ifyllt.

Koden är skript/falt.js, inkopplad bara på den sidan:

// Ett obligatoriskt fält visar att det är rätt ifyllt när man lämnar det.
// Hur det ser ut står i stil.css under .godkant. Här byts bara klassen.
const obligatoriska = document.querySelectorAll("input[required]");

for (const falt of obligatoriska) {
  falt.addEventListener("change", function () {
    if (falt.checkValidity()) {
      falt.classList.add("godkant");
    } else {
      falt.classList.remove("godkant");
    }
  });
}

Hur klassen ser ut står i stil.css, avsnitt 10, inte i skriptet:

/* Fältet är rätt ifyllt. En stapel till vänster, inte bara färg, så att det syns
   också för den som inte skiljer färgerna åt. Värdena kommer ur överlämningen. */
input.godkant {
  border-color: var(--klar);
  box-shadow: inset 0.3rem 0 0 var(--klar);
  transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  input.godkant {
    transition: none;
  }
}

--klar är en variabel i :root, en grön. box-shadow med inset ritar stapeln innanför kanten. transition och prefers-reduced-motion skrev du i område 5, på knappen; syntaxen är densamma, bara med två egenskaper. Se guiden till område 5, avsnitt 3. Varför 150 ms och ease-out tog Gränssnittsdesign den 25 februari; här är det värden som står i en överlämning.

CSS rör, JavaScript byter läge

Skriptet vet inte att fältet blir grönt. Det vet bara att klassen heter godkant. Vill du att stapeln ska vara blå ändrar du CSS:en och rör inte skriptet. Så ska all rörelse och allt utseende i Projekt 5 byggas: utseendet i en klass i CSS, och JavaScript som sätter och tar bort klassen. Aldrig style direkt från skriptet.

Referensprojektet har också input:invalid, som ger en röd kant utan en rad JavaScript. Det nya är att JavaScript kan sätta ett tillstånd som CSS inte kan räkna ut själv, här rätt ifyllt och lämnat.

Ljudmodulen spelar ett ljud varje gång händelsen kommer. Ett ljud som bara ska spelas ibland, till exempel när ett fält är fel ifyllt, klarar den inte. Då är det egen händelsekod som anropar spela på rätt ställe i ett villkor som det här. Det är C-nivå, och mer står i avsnitt 6.

Klick och tangentbord

Tabba till lägesknappen och tryck Enter. Färgerna byts och det låter. Tryck mellanslag, samma sak. Lyssnaren säger "click", och ingen klickade. En button gör om Enter och mellanslag till ett klick av sig själv.

En div med samma lyssnare gör det inte. Den går inte ens att tabba till, som i guiden till område 5, avsnitt 5. Regeln: lyssna på klick på en button eller en a, aldrig på en div, så fungerar tangentbordet utan en rad extra kod.

Vill du lyssna på tangenterna själva finns keydown. Skriv i konsolen och tryck sedan på några tangenter:

document.addEventListener("keydown", function (event) {
  console.log(event.key);
});

Konsolen skriver a, Enter, Escape. event är en parameter, som namn i ljudmodulens funktion. Webbläsaren fyller den med uppgifter om händelsen, och event.key är vilken tangent det var. keydown behövs sällan i Projekt 5. Den som använder button har tangentbordet gratis.

Din första skriptfil

En knapp som gör något synligt, på en sida i din egen sajt. Fyra delar, i den här ordningen. Först knappen i HTML:en, någonstans i main:

<button class="prova-knapp" type="button">Markera innehållet</button>

type="button" gör att knappen inte skickar något formulär den råkar stå i. Sedan läget i stil.css, alltså hur det ser ut när klassen är satt:

/* Övningen: så här ser main ut när knappen har markerat det */
main.markerad {
  outline: 0.25rem dashed currentColor;
}

Välj gärna något annat som syns: en bakgrund, en kant, en annan textfärg. Skapa sedan mappen skript/ bredvid index.html, och filen prova.js i den:

// Övning: knappen växlar en klass på main. Hur det ser ut står i stil.css.
const provknapp = document.querySelector(".prova-knapp");
const innehall = document.querySelector("main");

provknapp.addEventListener("click", function () {
  innehall.classList.toggle("markerad");
});

Sist kopplingen, i head på samma sida, efter link:

<script src="skript/prova.js" defer></script>

Vad defer gör står i avsnitt 4. Ladda om och klicka. Tabba sedan till knappen och tryck Enter. Båda ska fungera. Variabeln heter provknapp och inte knapp med flit; skälet står i avsnitt 4.

Läs ett felmeddelande

När knappen fungerar, framkalla ett fel med flit. Den som aldrig sett felet med flit blir rädd för det första gången det kommer på riktigt.

  1. Stava fel på klassen i skriptet: ".prova-knap". Spara, ladda om och öppna konsolen.
  2. Läs det röda. Chrome och Edge skriver det första nedan, Firefox det andra.
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')

Uncaught TypeError: provknapp is null

Läs det bakifrån:

Stavfelet står på rad 2, men felet står på rad 5. Felet står inte på raden där det gick fel, utan på raden där felet märktes. Det var på rad 5 som null skulle användas. Rätta stavningen och ladda om, så är felet borta.

Ta sedan bort defer på script-raden och ladda om. Samma fel, fast klassen är rätt stavad. Förklaringen står i avsnitt 4. Sätt tillbaka defer.

Två saker ger inget fel alls, bara tystnad. Ett felstavat händelsenamn, "onclick" eller "Click", och en klass som heter olika i CSS:en och i classList. Titta i Elements-fliken om klassen faktiskt sätts när du klickar. Syns den där men inte på sidan är det CSS:en.

Ett vanligt misstag till: att anropa funktionen i stället för att lämna över den.

// Fel: spelar ljudet en gång när sidan laddas, och sedan aldrig mer
provknapp.addEventListener("click", spela("vaxla"));

// Rätt: det som ska hända vid klicket ligger inuti function () { … }
provknapp.addEventListener("click", function () {
  spela("vaxla");
});

Ljudfilen som saknas

Ett fel till att känna igen. Klicka någonstans i texten på referensprojektets startsida, och skriv sedan i konsolen:

spela("finns-inte");

spela är ljudmodulens funktion, och den går att anropa från konsolen. Svaret är rött. Så här står det i Chrome och Edge; Firefox formulerar sig annorlunda:

Uncaught (in promise) NotSupportedError: Failed to load because no supported source was found.

NotSupportedError betyder att filen inte hittades. Ljudet har fel namn, fel versal eller ligger i fel mapp. Kontrollera namnet i data-ljud och LJUDMAPP innan något annat. Modulen tar inte hand om felet, och det är meningen: att få den att klara en saknad fil utan fel i konsolen är en utmaning på nivå A, se avsnitt 7.

4. Koppla in och anpassa en modul

Det här är E-nivån i Projekt 5: ta en modul någon annan skrivit, koppla in den och ändra det som får ändras. Tre frågor bär avsnittet. Var ska script stå? Hur hittar sidan filen? Vilka rader får jag röra?

Det mesta som går fel när en modul kopplas in är inte koden, utan kopplingen. En sida där skript/ljud.js är felstavat till skript/ljd.js byter färger men är tyst. Konsolen har en röd rad om en fil som inte gick att hämta, och ingenting annat klagar.

script och defer

Så här står skripten i referensprojektets head, efter link och i den här ordningen:

<script src="skript/ljud.js" defer></script>
<script src="skript/morkt-lage.js" defer></script>
DelVad den gör
<script>Här ska JavaScript köras
src="skript/ljud.js"Filen, samma sorts sökväg som href på stil.css
deferVänta med att köra skriptet tills hela HTML:en är läst
</script>Sluttaggen måste stå med, också när elementet är tomt

rapportera.html har en rad till, eftersom fälten bara finns där:

<script src="skript/falt.js" defer></script>

defer är engelska för skjuta upp. Webbläsaren läser HTML:en uppifrån. Utan defer körs skriptet i head innan body är läst. Då finns ingen knapp att hitta, querySelector ger null, och felet från avsnitt 3 kommer. Med defer hämtas skriptet medan sidan läses, men körs först när den är klar.

Ett annat sätt är att lägga script sist i body. Det fungerar också. Kursen använder defer i head för att alla kopplingar då står på ett ställe, som link till stilmallen, och för att skripten körs i den ordning de står. Ljudmodulen står först, eftersom andra skript kan vilja använda dess funktion spela.

Första kontrollen när ett skript kopplas in: en fil med en enda rad.

console.log("skriptet laddades");

Syns raden i konsolen på varje sida är sökvägen och defer rätt, innan du skrivit något som kan gå fel.

Sökvägar

Två sökvägar, och båda räknas från sidan:

Tre fel som kommer:

Vad som är säkert att ändra

Läs ljudmodulens kommentar överst en gång till. Modulen anpassas nästan helt utan att filen öppnas. Det mesta görs i HTML:en.

VarSäkert att ändraRör inte
HTML:endata-ljud: vilket ljud. data-ljud-nar: vilken händelse, om det inte är klick–
ljud.jsDe två inställningarna, LJUDMAPP och VOLYMLoopen, båda villkoren, funktionens namn spela

Ett fält som ska låta när det lämnats ändrat får båda attributen:

<label for="epost">Din e-post</label>
<input type="email" id="epost" name="epost" data-ljud="klart" data-ljud-nar="change">

Klick är förvalet. change passar för ett fält som lämnats ändrat. submit, när ett formulär skickas, passar sällan: sidan byts, och ljudet hinns inte höras. Attributnamnen skrivs med gemener och utan å, ä och ö: data-ljud-när hittas inte.

Inställningarna är gjorda för att ändras, och attributen är gjorda för att sättas. Loopen och villkoren är det som får modulen att fungera på vilken sajt som helst. Ändrar du dem har du skrivit en egen modul, och då är den din att felsöka.

Håll också fast vid namnen som binder ihop filerna. En klass som skriptet sätter och CSS:en läser måste heta samma sak på båda ställena. Byts ljud-av på ett ställe men inte på det andra slutar ljudknappen fungera, utan ett enda felmeddelande.

Det du ändrar i en modul skriver du i dokumentation.md, under rubriken Skript: vilken fil, vad den gör och vad du ändrat. Det är ett ska-krav. Hur avsnittet ser ut står i avsnitt 6.

Mörkt läge, steg för steg

Övningen från passet. Gör den i en kopia av fyrsajten från område 5, i en egen mapp utanför ditt projekt, inte i din egen sajt. Kopian ligger som zip-fil i itslearning, under område 6. Mörkt läge på din egen sajt är en utmaning, se avsnitt 7.

Steg 1. Modulerna på plats. Skapa mapparna skript/ och ljud/. Kopiera ljud.js, morkt-lage.js och vaxla.mp3 från referensprojektets mappar. Modulerna kopieras, de skrivs inte av; det är just det E-nivån är. Koppla in båda skripten i head på index.html, efter link, med raderna ovan.

Steg 2. Knappen i HTML:en, sist i header:

<button class="lage-knapp" type="button" data-ljud="vaxla" hidden>Mörkt läge</button>

type="button" som förut. data-ljud="vaxla" gör att ljudmodulen hittar knappen, utan en rad ny kod. hidden döljer knappen tills skriptet visar den; varför står i avsnitt 5. Ladda om. Knappen syns, fast den har hidden i HTML:en: morkt-lage.js tog bort det. Klicka. Det låter, men ingenting blir mörkt. Titta i Elements-fliken: skriptet satte klassen morkt på html, men CSS:en har ingen regel för den ännu.

Steg 3. Den mörka paletten i CSS:en, direkt efter :root i avsnitt 1 av stil.css. Formen är den här:

:root {
  /* ljusa värden som förut */
}

/* Systemet vill ha mörkt, och användaren har inte valt ljust på sajten */
@media (prefers-color-scheme: dark) {
  :root:not(.ljust) {
    /* mörka värden */
  }
}

/* Användaren har valt mörkt med knappen */
:root.morkt {
  /* samma mörka värden */
}

De mörka värdena skriver du av från referensprojektets stil.css, en ny rad för varje färgvariabel. Typografi och avstånd rörs inte. Så här står den andra av de två platserna där:

:root.morkt {
  color-scheme: dark;
  --text: #e3eaf1;
  --text-dov: #a3b3c3;
  --botten: #0f1a24;
  --yta: #172431;
  --linje: #2c3d4d;
  --linje-falt: #7b8ea1;
  --accent: #6cc3de;
  --accent-ljus: #16323d;
  --accent-hover: #96d6ea;
  --pa-accent: #0f1a24;
  --varning: #ff8f7a;
  --klar: #5ec98c;
}

Samma värden ska stå inuti media queryn också. Paletten står två gånger, en gång för systemets val och en gång för knappens. Ändras en mörk färg ska den ändras på båda ställena. color-scheme: dark gör att också fälten och rullningslisterna blir mörka. Ladda om och klicka. Nu blir det mörkt.

Steg 4. Läs vad modulen gör. Hela filen, skript/morkt-lage.js:

// Mörkt läge. Knappen växlar mellan ljust och mörkt och minns valet till nästa besök.
// Utan JavaScript följer sajten systemets inställning, med CSS. Knappen har hidden i
// HTML:en och visas först här, eftersom den inte gör något utan skriptet.

const knapp = document.querySelector(".lage-knapp");
const rot = document.documentElement;

// Är sidan mörk just nu? Ett eget val går före systemets inställning.
function arMork() {
  if (rot.classList.contains("morkt")) {
    return true;
  }
  if (rot.classList.contains("ljust")) {
    return false;
  }
  return matchMedia("(prefers-color-scheme: dark)").matches;
}

// Sätter läget och sparar valet. localStorage kan vara avstängt och kastar då ett
// fel. try gör att sidan fungerar ändå, fast utan minne.
function sattLage(lage) {
  rot.classList.remove("morkt", "ljust");
  rot.classList.add(lage);
  knapp.setAttribute("aria-pressed", lage === "morkt");
  try {
    localStorage.setItem("lage", lage);
  } catch (fel) {
    // Inget minne. Läget gäller tills sidan laddas om.
  }
}

// Ett sparat val från förra besöket
try {
  const sparat = localStorage.getItem("lage");
  if (sparat) {
    sattLage(sparat);
  }
} catch (fel) {
  // Inget sparat val. Systemets inställning gäller.
}

knapp.hidden = false;
knapp.setAttribute("aria-pressed", arMork());

knapp.addEventListener("click", function () {
  if (arMork()) {
    sattLage("ljust");
  } else {
    sattLage("morkt");
  }
});

Fyra ställen att förstå. Först två variabler, knappen och html-elementet:

const knapp = document.querySelector(".lage-knapp");
const rot = document.documentElement;

Sedan funktionen som sätter läget:

function sattLage(lage) {
  rot.classList.remove("morkt", "ljust");
  rot.classList.add(lage);
  knapp.setAttribute("aria-pressed", lage === "morkt");
  try {
    localStorage.setItem("lage", lage);
  } catch (fel) {
    // Inget minne. Läget gäller tills sidan laddas om.
  }
}

Här visas knappen, med samma hidden som du satte från konsolen i avsnitt 2:

knapp.hidden = false;
knapp.setAttribute("aria-pressed", arMork());

Och sist en lyssnare, som i avsnitt 3. Är sidan mörk, välj ljust. Annars mörkt. arMork är en funktion högre upp som svarar true eller false. Den tar hänsyn både till knappens val och till systemets inställning.

knapp.addEventListener("click", function () {
  if (arMork()) {
    sattLage("ljust");
  } else {
    sattLage("morkt");
  }
});

Steg 5. Prova. Tre prov:

  1. Välj mörkt och ladda om. Sidan är fortfarande mörk: valet är sparat.
  2. Tabba till knappen och tryck mellanslag. Det låter och växlar.
  3. Öppna Rendering-panelen i DevTools: Ctrl + Shift + P, skriv Rendering. Sätt prefers-color-scheme till dark. Välj ljust med knappen och ladda om. Knappens val går före systemets.

Steg 6. Ändra beteendet. Två ändringar, i tur och ordning:

  1. Byt knappens text till något eget. Den står i HTML:en, inte i skriptet. Ett skript som inte innehåller någon text behöver inte ändras när texten gör det.
  2. Låt valet glömmas. Kommentera bort blocket under // Ett sparat val från förra besöket, med // framför varje rad. Välj mörkt och ladda om. Sidan följer systemet igen. Valet sparas fortfarande, men det läses aldrig.
// Ett sparat val från förra besöket
// try {
//   const sparat = localStorage.getItem("lage");
//   if (sparat) {
//     sattLage(sparat);
//   }
// } catch (fel) {
//   // Inget sparat val. Systemets inställning gäller.
// }

Den andra ändringen rör något utanför inställningarna. Det går, eftersom blocket är helt och gör en sak. Skriv det i dokumentationen, så att nästa person vet varför modulen glömmer.

Kontrasten. Mät de mörka textfärgerna mot den mörka bakgrunden, som du mätte färgscheman i Gränssnittsdesign den 5 oktober. Samma verktyg, samma gräns. Ett mörkt läge som inte är mätt är inte klart.

Med defer kan sidan visas ljus en kort stund innan skriptet hunnit sätta mörkt. På fyrsajten syns det knappt, och det löses inte i den här kursen.

Ljudknappen följer samma mönster

Ljudknappen är ett bör-krav, nivå C, och den skriver du själv. Allt den behöver finns i lägesknappen:

DelI lägesknappenI ljudknappen
KnappenEn button med hidden, som skriptet visarLikadant
Klassen på htmlmorkt eller ljustljud-av, som ljudmodulen redan lyssnar på
Läget för skärmläsarenaria-pressed Likadant
MinnetlocalStorage i try Likadant, under ett eget namn

Ljudmodulen lyssnar redan på ljud-av. Du behöver bara sätta den. Att det ska gå att stänga av ljudet, och vad användaren väntar sig då, tog Gränssnittsdesign den 1 mars.

Skript på samma sida delar namn

Alla skript på en sida delar sina namn. morkt-lage.js har redan const knapp och const rot. Skriver du en egen ljudknapp med const knapp på en sida där morkt-lage.js också är inkopplad kommer det här, i Chrome och Edge och sedan i Firefox:

Uncaught SyntaxError: Identifier 'knapp' has already been declared

Uncaught SyntaxError: redeclaration of const knapp

Döp den egna till något eget, till exempel ljudknapp. Det är också därför övningsknappen i avsnitt 3 heter provknapp.

Vanliga fel i avsnittet

5. När JavaScript inte finns

Ett ska-krav i Projekt 5 skiljer sig från de andra: sajten ska fungera fullt ut med JavaScript avstängt. Allt innehåll går att läsa, alla länkar och formuläret fungerar. Det enda som saknas är ljudet och de tillstånd som bara JavaScript kan sätta.

Stäng av JavaScript

WebbläsareStäng avGäller
Chrome, EdgeDevTools öppet, Ctrl + Shift + P, skriv Disable JavaScript, Enter. Slå på igen med Enable JavaScript Bara så länge DevTools är öppet
FirefoxSkriv about:config i adressfältet, sök på javascript.enabled och dubbelklicka så att det blir falseTills du ändrar tillbaka

Ladda om sidan efter varje ändring. En sida som redan är laddad har redan kört sina skript.

Struktur, stil och beteende

LagerSpråkUtan det
BeteendeJavaScriptSidan gör ingenting när man använder den, men allt står kvar
StilCSSSidan är ful men läsbar, och länkarna fungerar
StrukturHTMLIngenting

Varje lager ska fungera utan lagret ovanför. HTML:en ensam är en läsbar sida med länkar och ett formulär. CSS:en gör den bra att använda. JavaScript lägger till ljud och tillstånd. På engelska heter tanken progressive enhancement: basen först, sedan förbättringar som får saknas.

Reglerna du redan följer finns av det skälet. Inget onclick i HTML:en, för då blir beteendet en del av strukturen. Rörelsen i CSS och inte med style, för då ligger utseendet i beteendet. Varje regel håller isär två lager.

Ett skript kan saknas av fler skäl än att någon stängt av det: nätet tappar filen, ett tillägg i webbläsaren blockerar den, eller ett fel på en rad stoppar resten av skriptet. För sajten är det samma sak.

Vad som håller i referensprojektet

Stäng av JavaScript och gå igenom referensprojektet. Rubrikerna, texten, bilderna och menyn är kvar, och alla länkar fungerar. Det enda som saknas på startsidan är lägesknappen.

Lägesknappen. Den står i HTML:en med hidden. Utan skriptet tas hidden aldrig bort, och knappen syns inte. Kommentaren överst i morkt-lage.js säger varför: knappen gör ingenting utan skriptet. En knapp som inte gör något när man trycker är sämre än ingen knapp, för den som trycker tror att sajten är trasig.

hidden fungerar för att webbläsaren döljer elementet med display: none. En egen regel som sätter display på knappen vinner över det:

/* Så här ska det inte stå: display vinner över hidden,
   och knappen syns också utan JavaScript */
.lage-knapp {
  display: inline-flex;
}

Därför har .lage-knapp i referensprojektets stil.css ingen display-regel, och kommentaren där säger att det är avsiktligt. Kontrollera i Elements-fliken vilken regel som gäller om din knapp syns fast den inte borde.

Det mörka läget. Sätt prefers-color-scheme till dark i Rendering-panelen. Sajten blir mörk, utan skript. Det är media queryn med :root:not(.ljust) från avsnitt 4. Den är CSS och bryr sig inte om JavaScript: utan skript finns ingen klass ljust, och villkoret stämmer alltid. Det som försvinner är valet. Att mörkt läge fungerar utan JavaScript är nivå A i uppgiften, och det är skälet till att paletten står två gånger.

Formuläret. Tryck Skicka rapporten på rapportera.html med tomma fält. Webbläsaren stoppar och säger att fältet ska fyllas i. Det är required i HTML:en, och det fungerar utan en rad JavaScript. Fyll i fälten och tabba ut: ingen grön stapel, eftersom falt.js inte körs. Skicka, och tack.html visas som vanligt. Stapeln var en extra bekräftelse. Kontrollen var aldrig beroende av den.

Ljudet. Inget ljud någonstans. Fyrsajtens enda ljud hör till lägesknappen, och den finns inte nu. Ett ljud får aldrig vara det enda som säger något, det tog Gränssnittsdesign den 1 mars, så ingen information försvinner med det.

Innehåll som bara JavaScript skapar

Nu motsatsen, ett påhittat exempel som inte finns i fyrsajten:

<h2>Öppettider</h2>
<p class="oppettider"></p>
// Så här ska det inte göras: texten finns bara om skriptet körs
document.querySelector(".oppettider").textContent = "Öppet alla dagar 10–16.";

Med JavaScript ser sidan rätt ut. Utan är stycket tomt, och rubriken står över ingenting. Samma sak för sökmotorn och för den vars nät tappade skriptfilen. Innehållet hör hemma i HTML:en. JavaScript får ändra det, visa det eller dölja det, men det får inte vara det enda stället där det finns.

Fler mönster som går sönder på samma sätt:

Din sajt utan JavaScript

  1. Stäng av JavaScript och ladda om startsidan.
  2. Gå igenom varje sida. Läs innehållet, klicka på varje länk, skicka formuläret med tomma fält och med rätt ifyllda.
  3. Lista vad som slutar fungera. Allt, också det som verkar litet: en knapp som inte gör något, ett tomt stycke, en bild som saknas.
  4. Sortera listan i två. Det som bör försvinna utan JavaScript, som ljud och tillstånd. Och det som inte får, som innehåll, länkar och formuläret.
  5. Rätta det som inte får försvinna. Oftast är det en knapp som ska få hidden, eller text som ska flyttas från skriptet till HTML:en.
  6. Skriv raderna i testprotokollet i dokumentation.md, i samma form som i guiden till område 5, avsnitt 6.

Så här står raderna i referensprojektets protokoll:

TestMed vadSidaResultatÅtgärd
Utan JavaScriptChrome NN, DevTools: Disable JavaScript alla sidorAllt innehåll, alla länkar och formuläret fungerar. Lägesknappen syns inte. Mörkt läge följer systemet. Fältet får ingen markering, men webbläsaren stoppar ett tomt obligatoriskt fältIngen
Utan JavaScript, andra webbläsarenFirefox NN, about:config, javascript.enabled av alla sidorSom i ChromeIngen

NN står för webbläsarens versionsnummer. Det hittar du i webbläsarens Om-meny. Testa varje sida, inte bara startsidan: formuläret och undersidorna är där det går sönder. Och en sida som visar en rubrik och ingenting under fungerar inte, även om inget ser trasigt ut.

Inför överlämningen

Överlämningen från Gränssnittsdesign kommer den 8 mars, och den 10 mars bygger du in den. Gör tre saker innan dess:

  1. Ljudfilerna i ljud/, packade ur zip-filen, med exakt de namn som står i tabellen. Döp inte om något.
  2. För varje ljudrad: vilket element i din HTML det gäller, och om händelsen är klick eller något annat. En rad per ljud, på papper.
  3. För varje rörelserad: vilken klass som ska sättas, och på vilket element.

Tabellen skrivs inte av till en kopia. Den finns på portföljsidan i Gränssnittsdesign och ska bara finnas där.

6. Från överlämning till kod

Överlämningen från Gränssnittsdesign har två tabeller: en för ljuden och en för rörelserna. Varje cell i dem svarar mot något bestämt i koden. Inget i avsnittet är nytt: övergången och prefers-reduced-motion kommer från område 5, klassbytet från avsnitt 3 och ljudmodulen från avsnitt 4. Det nya är att värdena kommer från någon annan, och att koden ska följa dem.

Varför ett ljud är så långt det är, vilken tidskurva som passar och vad som ska hända för den som valt bort rörelse är beslut som redan är fattade i Gränssnittsdesign. Här byggs de som de står.

Ljudtabellen

Referensprojektets egen överlämning står i dess dokumentation.md, i samma form som din:

filhändelselängdsynlig motsvarighet
vaxla.mp3klick på lägesknappen120 ms färgerna byts, knappen ser nedtryckt ut
KolumnBlir i kodenI referensprojektet
filFilen i ljud/, med exakt det namnet, och data-ljud med namnet utan .mp3. Modulen lägger till detljud/vaxla.mp3 och data-ljud="vaxla"
händelseVilket element som får data-ljud, och data-ljud-nar om händelsen inte är klick. Skriptet rörs inte Lägesknappen, inget data-ljud-nar
längdIngenting i koden. Längden är filen. Lyssna och kontrollera–
synlig motsvarighetNågot som syns vid samma händelse Klassen morkt och .lage-knapp[aria-pressed="true"]

Rörelsetabellen

elementutlösarevad som ändrasvaraktighet tidskurvautan rörelse
obligatoriskt fält i rapportera.htmllämnar fältet rätt ifylltkantens färg, stapel till vänster150 ms ease-outändringen sker direkt

Koden den blev står i avsnitt 3: falt.js och regeln input.godkant. Cell för cell:

KolumnBlir i kodenI referensprojektet
elementSelektorn i CSS:en, och det skriptet letar efter input.godkant, input[required]
utlösareHändelsen i skriptet, och villkoret om det finns ett"change" och checkValidity()
vad som ändrasEgenskaperna i regeln och i transitionborder-color och box-shadow
varaktighetTiden i transition 150ms
tidskurvaKurvan i transition ease-out
utan rörelseDet som står i @media (prefers-reduced-motion: reduce) transition: none

Tre tekniska saker

Så ser felet ut i Chrome och Edge när ett ljud spelas innan användaren gjort något:

Uncaught (in promise) NotAllowedError: play() failed because the user didn't interact with the document first.

Arbetsordningen

  1. Ljuden. Filerna i ljud/, data-ljud på elementen. Klicka och lyssna, ett ljud i taget, med hörlurar.
  2. Den synliga motsvarigheten för varje ljud.
  3. Mikrointeraktionerna i CSS först. Klassen och övergången, med överlämningens värden. Sätt klassen för hand i Elements-fliken eller i konsolen. Ser det rätt ut?
  4. Sedan JavaScript, som bara sätter och tar bort klassen. Skriver du skriptet först vet du inte om det är skriptet eller övergången som inte fungerar.
  5. Reducerad rörelse. Rendering-panelen, prefers-reduced-motion till reduce. Görs det överlämningen säger i kolumnen utan rörelse?

Avvikelser skrivs ner

Referensprojektets dokumentation.md har ett avsnitt Skript med en rad per fil, och samma avsnitt är ett ska-krav i Projekt 5. Så står ljudmodulens rad:

FilVad den görVarÄndrat i modulen
ljud.jsLjudmodulen. Spelar ljud/<namn>.mp3 vid klick, eller vid händelsen i data-ljud-nar, på element med data-ljud. Tyst om html har klassen ljud-avalla sidor Inget. Styrs från HTML:en: data-ljud="vaxla" på lägesknappen

Under det står varje ställe där koden inte gör som tabellen säger, med skäl och med tabellens rad angiven. Referensprojektet har en:

RadAvvikelseSkäl
Rörelse, fältetMarkeringen försvinner utan övergång när fältet töms igenTabellen beskriver bara vägen in. Övergången sitter på input.godkant, och när klassen tas bort finns ingen övergång kvar. Frågan är ställd till designern: ska vägen ut också ta 150 ms?

Andra avvikelser som kan komma:

En avvikelse med ett skäl är ett bör-krav, nivå C. En avvikelse utan skäl är ett fel. Bygg det som står, även när ett värde känns fel, och skriv ner det som inte fungerar. Frågan om värdet tas i Gränssnittsdesign. Tabellen skrivs inte av in i dokumentation.md: då finns två tabeller som säger olika saker. Ändras överlämningen efter den 8 mars tar du in det fram till den 16 mars.

7. Ta det längre

Utmaningarna i Projekt 5, nivå A. Ta dem när ska- och bör-kraven sitter. Avsnittet säger vad utmaningen går ut på och var du börjar leta. Lösningen är din.

En egen modul styrd med attribut

En modul som fungerar på element den inte byggdes för. Förebilden är ljudmodulen, och det som gör den till en modul är det här:

Pröva den på en kamrats sajt. Där ser du om den verkligen inte vet något om din. Skriv i dokumentation.md hur den används, så att någon annan kan koppla in den utan att fråga dig.

Tangentbord och skärmläsare

Alla mikrointeraktioner ska gå att utlösa med tangentbordet, och återkopplingen ska nå också den som använder skärmläsare.

<p class="status" aria-live="polite"></p>

Elementet ska finnas i HTML:en från början. Det är texten i det som ändras, med textContent som i avsnitt 2. Ett element som skapas samtidigt som texten läses ofta inte upp. Hur du startar skärmläsaren står i Gränssnittsdesigns guide, avsnitt 3. Skriv i testprotokollet vad du hörde.

Mörkt läge på din egen sajt

Övningen i avsnitt 4 gjordes i en kopia av fyrsajten, med fyrsajtens färger. Att flytta över den rakt av är inte samma sak som utmaningen. Det som skiljer:

När ljudfilen saknas

Du ska kunna visa vad som händer när en ljudfil saknas, och koden ska klara det utan fel i konsolen. Felet känner du igen från avsnitt 3: Uncaught (in promise) NotSupportedError.

Två ord i felet är ledtråden. play() spelar inte bara ljudet, den ger tillbaka ett löfte, engelska promise: ett besked som kommer senare om uppspelningen lyckades eller inte. Uncaught (in promise) betyder att löftet misslyckades och att ingen kod tog hand om det. MDN, webbens referensverk, beskriver vad play() ger tillbaka och vad som händer när den misslyckas:

https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play

Läs sidan, pröva i konsolen med spela("finns-inte"), och skriv i dokumentation.md vad du ändrade i modulen och varför. En ändring i loopen eller i funktionen betyder att modulen är din att felsöka.

8. Felsökning

30 rader

SymptomTrolig orsakÅtgärd
Det låter inte, och konsolen är tomElementet saknar data-ljud, ljud.js är inte inkopplad på sidan, eller html har klassen ljud-avElements-fliken: attributet på elementet och klassen på html. Network-fliken: laddades ljud.js?
NotSupportedError i konsolen när ljudet ska spelasFilen finns inte där modulen letar: fel namn, fel versal eller fel mappJämför data-ljud med filnamnet tecken för tecken, och kontrollera LJUDMAPP
Ljudet fungerar lokalt men inte på PagesVersaler i filnamnet eller i data-ljud. Pages skiljer på stora och små bokstäverGemener överallt, i filen och i attributet
Ljudet kommer vid klick fast det ska komma vid change data-ljud-nar saknas eller är felstavat, till exempel data-ljud-närElements-fliken: attributet tecken för tecken. Gemener, inga å, ä eller ö
NotAllowedError i konsolenLjudet spelas innan användaren gjort något: när sidan laddas eller vid hoverByt till en händelse där användaren gör något. Står det så i överlämningen är det en avvikelse
Ljudet på en länk hörs inteSidan byts innan ljudet hunnit spelasAvvikelse. Skriv ner den och föreslå en annan händelse
Ljudet spelas en gång när sidan laddas, och aldrig merFunktionen anropas i stället för att lämnas över: spela("…") direkt i addEventListenerLägg anropet inuti function () { … }
Det låter fast ljudknappen stängt av ljudetKlassen heter något annat än ljud-av, eller sätts på ett annat element än htmlSamma namn på båda ställena, på document.documentElement
Cannot read properties of null (reading 'addEventListener') Selektorn hittade inget: stavfel, skriptet är inkopplat på en sida utan elementet, eller defer saknasKlicka på radnumret. Kontrollera selektorn mot HTML:en, defer, och att elementet finns på sidan
provknapp is null i FirefoxSamma fel som ovan, med Firefox ordSom ovan
Identifier 'knapp' has already been declaredTvå skript på samma sida har en variabel med samma namnDöp om i ditt eget skript, till exempel ljudknapp
is not a functionFel stor eller liten bokstav: queryselector, addEventlistenerSkriv namnet exakt: querySelector, addEventListener
missing ) after argument list eller Unexpected end of input En parentes eller klammer saknas, oftast i });Räkna parenteser och klamrar från raden konsolen anger och uppåt
Konsolen säger att en fil inte gick att hämtaFel sökväg i src: stavfel, snedstreck först, omvänt snedstreck eller versalerSökvägen från sidan, med / och gemener
Knappen gör ingenting, och konsolen är tomHändelsens namn är fel: "onclick" eller "Click""click", med gemener och utan on
Klassen sätts, men ingenting synsKlassen heter olika i CSS:en och i skriptet, eller regeln saknasElements-fliken: sätts klassen? Jämför namnet tecken för tecken med selektorn i stil.css
Konsolen svarar nullSelektorn hittar inget, oftast för att punkten före klassnamnet saknasSkriv selektorn som i CSS: ".prova-knapp"
Det jag ändrade i konsolen är bortaKonsolen ändrar DOM, inte filenDet som ska finnas kvar skrivs i en fil
Koden jag skrev gör ingenting allsDen skrevs i Elements-fliken Byt till konsolen
Knappen fungerar med mus men inte med tangentbordLyssnaren sitter på en divByt till button
Övergången syns inte, fast klassen sättsEgenskapen i transition är inte den som ändras, eller så är det display som bytsSamma egenskap på båda ställena. display går inte att tona
Övergången syns fast reducerad rörelse är påMedia queryn står före regeln den ska ändraFlytta den direkt efter regeln
Rörelsen går inte att stänga avDen är satt med style i skriptetFlytta utseendet till en klass i CSS, och låt skriptet byta klass
Knappen syns fast JavaScript är avstängthidden saknas, eller en regel i CSS:en sätter display på knappen hidden i HTML:en, ingen display-regel för knappen
Ett stycke är tomt utan JavaScriptTexten skapas av skriptet Flytta texten till HTML:en
Mörkt läge fungerar med knappen men inte med systemets inställning, eller tvärtomDen mörka paletten står bara på ett av de två ställenaSamma värden i media queryn och under :root.morkt
Några färger stannar ljusa i mörkt lägeFärgen står direkt i regeln, inte som variabelSök efter # och hsl( utanför :root
Valet glöms när sidan laddas omWebbläsaren blockerar lagring för sajten, eller så är blocket som läser valet bortkommenterat Tillåt webbplatsdata i webbläsarens inställningar och prova igen. Läs blocket under Ett sparat val från förra besöket
Allt fungerar som vanligt fast jag stängt av JavaScriptDevTools stängdes i Chrome, eller sidan laddades inte omÖppna DevTools igen, stäng av, ladda om
Ingen sajt fungerar som vanligt i Firefoxjavascript.enabled står kvar på falseSätt tillbaka det i about:config

9. Snabbreferens: begreppen

Ordlistan från första passet. Använd den inför prov 4. Ordträningen nedan drar slumpvis ur tabellen.

Ordträning

57 rader

BegreppBetyder
JavaScriptWebbens programmeringsspråk. Säger vad som händer när någon använder sidan
modulFärdig kod som gör en sak och går att koppla in i en sajt den inte byggdes för
kommentarRad som börjar med //. Till för människor, webbläsaren hoppar över den
variabelEtt namn på ett värde
constVariabel vars värde inte byts ut
letVariabel vars värde får bytas
varGammalt sätt att skriva en variabel. Används inte i kursen
=Sätter ett värde. Betyder inte är lika med
===Jämför två värden. Svarar true eller false
strängText inom citattecken, till exempel "ljud/"
talEtt tal, utan citattecken, till exempel 0.5
true och falseSant och falskt. Svaret på en fråga i koden
funktionRader med ett namn, som körs när namnet anropas
parameterTom plats i en funktion som fylls i vid anropet, till exempel namn i spela(namn)
anropAtt köra en funktion: namnet med parenteser, till exempel spela("vaxla")
returnGå ut ur funktionen. Raderna under körs inte
villkorif: koden inom klamrarna körs bara om svaret är ja
elseAnnars. Körs när villkoret inte är uppfyllt
loopfor … of: gör samma sak en gång för varje element i en lista
händelseNågot som händer på sidan: ett klick, en tangent, ett fält som lämnas. Engelska event
event listenerHändelselyssnare. En funktion som väntar på en händelse och körs varje gång den kommer
addEventListenerLägger till en lyssnare: vem, vilken händelse, vad som ska hända
clickHändelsen när någon klickar, eller trycker Enter eller mellanslag på en knapp
changeHändelsen när ett fält har ändrats och lämnats
keydownHändelsen när en tangent trycks ned
event.keyVilken tangent som trycktes
elementEn del av sidan, till exempel en knapp eller en rubrik
DOMDocument Object Model. Sidan som ett träd av element, som webbläsaren håller i minnet
barn och förälderEtt element inuti ett annat är dess barn. Elementet utanför är förälder
documentHela sidan, i JavaScript
document.documentElementSidans html-element. Samma som :root i CSS
querySelectorHittar det första elementet som matchar en selektor
querySelectorAllHittar alla element som matchar en selektor. Svarar med en lista
nullIngenting. Svaret när querySelector inte hittar något
textContentElementets text. Kan läsas och bytas ut
classListElementets klasser, med add, remove, toggle och contains
toggleVäxlar en klass: finns den tas den bort, annars läggs den till
containsFrågar om elementet har en klass. Svarar true eller false
setAttributeSätter ett attribut: namnet först, värdet sedan
hiddenAttribut som döljer ett element. En display-regel i CSS slår ut det
data-attributEget attribut som börjar med data-, till exempel data-ljud. Information, inte beteende
datasetLäser data-attribut. data-ljud-nar blir dataset.ljudNar
konsolFlik i DevTools där du skriver JavaScript och får svaret direkt. Ctrl + Shift + J
console.logSkriver i konsolen. Syns inte på sidan
felmeddelandeRöd rad i konsolen. Står på raden där felet märktes, inte där det gjordes
onclickJavaScript i ett HTML-attribut. Används inte: beteendet hör hemma i .js-filen
scriptElementet som kopplar in en .js-fil, med src
deferSkjuter upp skriptet tills hela HTML:en är läst, så att elementen finns
localStorageWebbläsarens minne för sajten. Finns kvar till nästa besök
try och catchFörsök. Går det fel körs det i catch i stället, och sidan fungerar ändå
aria-pressedSäger till skärmläsaren om en knapp är nedtryckt eller inte
prefers-color-schemeMedia query: användaren har ställt in mörkt eller ljust i sitt system
progressive enhancementBasen först, sedan förbättringar som får saknas. JavaScript lägger till, det bär aldrig
new AudioSkapar ett ljud ur en fil. play() spelar det
NotSupportedErrorFelet när ljudfilen inte hittas
NotAllowedErrorFelet när ett ljud spelas innan användaren gjort något
avvikelseStälle där koden inte gör som överlämningen säger. Skrivs ner med skäl