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.
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.
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
- Säga vad HTML, CSS och JavaScript var för sig gör på en sida
- Läsa en färdig modul och peka ut variabler, funktioner, villkor, loopar och händelser
- Följa ett klick från knappen till ljudet, rad för rad, och säga vad som körs och när
- Hitta element i sidan med
querySelectoroch ändra deras text, klass och attribut från konsolen - Lyssna efter klick och tangenter med
addEventListener, utan JavaScript i HTML:en - Läsa ett felmeddelande och hitta raden det gäller
- Koppla in ett skript med
scriptochdefer, och veta vilka rader i en modul som är säkra att ändra - Bygga så att sajten fungerar med JavaScript avstängt
- Göra om en överlämningstabell till kod, cell för cell, och skriva ner var koden avviker
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åk | Svarar på | I fyrsajten |
|---|---|---|
| HTML | Vad finns på sidan? | Knappen med texten Mörkt läge |
| CSS | Hur ser det ut? | Knappens färg, storlek och hover |
| JavaScript | Vad 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
| Del | Vad den gör |
|---|---|
const | Här kommer en variabel vars värde inte byts ut |
LJUDMAPP | Namnet. 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();
}
function spela(namn)– en funktion som heterspela.namninom parentesen är en parameter: en tom plats som fylls i när funktionen anropas.- Klamrarna
{ }håller ihop det som hör till funktionen, som i CSS. new Audio(LJUDMAPP + namn + ".mp3")skapar ett ljud ur en fil.+sätter ihop strängar:"ljud/"och"vaxla"och".mp3"blir"ljud/vaxla.mp3".const ljud = …sparar ljudet i en variabel, så att nästa rad kan använda det.ljud.volume = VOLYM;ger ljudet volymen från inställningen.ljud.play();spelar det.
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.
ifbetyder om. Det som står inom parentesen är frågan, och det som står inom klamrarna körs bara om svaret är ja.document.documentElementär sidanshtml-element, samma som:rooti CSS.classList.contains("ljud-av")frågar om elementet har den klassen. Svaret ärtrueellerfalse, sant eller falskt.returnbetyder gå ut ur funktionen. Raderna under körs då aldrig, och det blir tyst.
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);
});
}
for (const element of ljudelement)är en loop: gör det som står inom klamrarna en gång för varje element i listan. Varje varv heter elementetelement.let handelse = "click";är en variabel medlet, eftersom värdet kanske byts på nästa rad. Varje element börjar med klick.- Det andra villkoret: om elementet har attributet
data-ljud-nar, byt händelse till det som står där. Saknas attributet är villkoret inte uppfyllt, och det blir klick. addEventListenerbetyder lägg till en lyssnare. En händelse, engelska event, är något som händer på sidan. En event listener, händelselyssnare, väntar på en händelse och kör en funktion när den kommer.function () { … }är funktionen som körs när händelsen kommer. Den har inget namn, eftersom den bara behövs här.element.dataset.ljudläser värdet idata-ljud. På lägesknappen är det"vaxla".
dataset läser attribut som börjar med data-. Prefixet tas
bort, bindestrecken försvinner och bokstaven efter ett bindestreck blir stor:
| I HTML:en | I JavaScript |
|---|---|
data-ljud | element.dataset.ljud |
data-ljud-nar | element.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:
- När sidan laddas går loopen ett varv för knappen.
handelseblir"click", och en lyssnare läggs på knappen. - Någon klickar. Lyssnarens funktion körs.
- Den anropar
spela("vaxla"). - Villkoret: har
htmlklassenljud-av? Nej, så funktionen fortsätter. new Audioskapar ett ljud ur"ljud/vaxla.mp3", volymen sätts till 0.5, ochplay()spelar det.
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.
Läsövningen från passet. Svara med egna ord, inte med kodens ord, och med ljudmodulen framför dig.
- Vad säger kommentarraderna överst? Vem är de skrivna för?
- En knapp har
data-ljud="skicka-klart". Vilken fil försöker modulen spela? Skriv hela sökvägen. - Vad händer om
VOLYMändras till0? Spelas ljudet? html-elementet har klassenljud-av. Någon klickar på lägesknappen. Vilka rader körs, och vilka körs inte?- Ett fält har
data-ljud="klart"ochdata-ljud-nar="change". Vilket värde fårhandelse? När spelas ljudet, och vilken fil? - Varför står det
letframförhandelse, när nästan allt annat harconst? - En sida har tre element med
data-ljud. Hur många gånger körs raden medaddEventListener, och när? - En sida har inget element med
data-ljudalls. Vad händer? Blir det ett fel? - Ljudfilerna flyttas till mappen
media/ljud/. Vilken rad ändrar du, och till vad? - 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 |
|---|---|
| E | Kopplar in och anpassar en färdig modul från referensprojektet, efter överlämningen |
| C | Skriver egen, kort händelsekod för en mikrointeraktion och en ljudknapp |
| A | Skriver 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:
- CSS rör, JavaScript byter läge. Rörelsen skrivs i CSS. JavaScript sätter och tar bort en klass. Se avsnitt 3.
- Sajten ska fungera med JavaScript avstängt. JavaScript lägger till, det bär aldrig. Se avsnitt 5. Det din sajt klarar innan den har någon JavaScript är din nollpunkt, och det ska fungera på deadlinedagen också.
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.
- Ett element som ligger inuti ett annat är dess barn.
navär barn tillheader. - Elementet utanför är förälder.
headerär förälder tillnav. - Samma ord som i CSS, där
header > pbetyder ettpsom är barn tillheader.
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.
| Tangenter | Gör |
|---|---|
| Ctrl + Shift + J | Öppnar konsolen i Chrome och Edge |
| Ctrl + Shift + K | Öppnar konsolen i Firefox |
| Pil upp | Hämtar förra raden du skrev |
| Tab | Fyller i resten av ett ord konsolen känner igen |
| Ctrl + L | Tö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");
| Metod | Gör |
|---|---|
add | Lägger till klassen |
remove | Tar bort klassen |
toggle | Växlar: finns klassen tas den bort, annars läggs den till |
contains | Frå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 ändrar | Med | I Projekt 5 |
|---|---|---|
| Texten | textContent | Sällan. Innehållet ska stå i HTML:en, se avsnitt 5 |
| Klassen | classList | Nästan alltid. Så byter JavaScript läge |
| Attribut | hidden, setAttribute |
Knappar som visas av skriptet, aria-pressed |
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.
Öppna din egen sajt och konsolen. Gör det här, en sak i taget:
- Byt texten i din
h1. - Räkna länkarna i din navigering med
querySelectorAll. Stämmer siffran? - Lägg till en klass som redan finns i din
stil.csspå ett element som inte har den. Ser elementet ut som de andra med den klassen? - Dölj ett element med
hidden, och ta tillbaka det. - Ändra ett attribut:
hrefpå en länk, elleraltpå en bild. Kontrollera ändringen i Elements-fliken. - Leta efter något som inte finns. Skriv en selektor med ett stavfel. Vad svarar konsolen?
- 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);
});
| Del | Vad den säger |
|---|---|
element | Vem som ska lyssnas på |
.addEventListener( | Lägg till en lyssnare |
handelse | Vilken 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ändelse | När den kommer |
|---|---|
click | Någon klickar, eller trycker Enter eller mellanslag på en knapp |
change | Ett fält har ändrats och lämnats |
keydown | En 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:
- Tre språk på ett ställe. HTML ska säga vad som finns. Här säger den också vad som händer, och JavaScript står i ett attribut där ingen letar efter det.
- Varje element för hand. Ska tio knappar låta står samma kod tio gånger.
Ljudmodulen hittar alla element med
data-ljudsjälv, i en loop. - Ett ställe att ändra. Byter du händelse eller volym ändrar du en rad i en fil, inte varje sida.
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");
}
});
}
querySelectorAll("input[required]")hittar alla fält med attributetrequired. På sidan är det två: namnet och e-posten.- Loopen sätter en lyssnare på vart och ett, som i ljudmodulen.
"change"i stället för"click": lyssnaren körs när fältet lämnas efter en ändring.falt.checkValidity()frågar webbläsaren om fältet uppfyller sina regler: att det är ifyllt, och för e-posten att adressen har rätt form. Svaret ärtrueellerfalse.if … else: om svaret är sant, lägg till klassen. Annars, ta bort den.elsebetyder just annars.
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.
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.
- Stava fel på klassen i skriptet:
".prova-knap". Spara, ladda om och öppna konsolen. - 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:
- null – något var ingenting. Samma
nullsom konsolen svarade i avsnitt 2. - reading 'addEventListener' – det var när koden försökte lägga till en lyssnare.
- Till höger står filnamnet och radnumret,
prova.js:5. Klicka på det, så visar konsolen raden.
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>
| Del | Vad 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 |
defer | Vä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:
src="skript/ljud.js"räknas från HTML-filen, somhref="stil.css"och bildernassrc.LJUDMAPPi ljudmodulen räknas också från sidan, inte från skriptfilen. Kommentaren säger det: var ljudfilerna ligger, räknat från sidan. Skriptet ligger iskript/, men ljuden hittas iljud/bredvidindex.html.
Tre fel som kommer:
- Snedstreck först.
/skript/ljud.jsbetyder roten, inte mappen bredvid. Det fungerar ibland lokalt och aldrig på Pages. Skriv utan snedstreck först. - Omvända snedstreck.
skript\ljud.jsär Windows sätt att skriva. Webben använder/. - Versaler.
Skript/ochskript/är samma mapp i Windows men inte på Pages. Gemener överallt, som i guiden till område 2, avsnitt 10.
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.
| Var | Säkert att ändra | Rör inte |
|---|---|---|
| HTML:en | data-ljud: vilket ljud.
data-ljud-nar: vilken händelse, om det inte är klick | – |
ljud.js | De två inställningarna, LJUDMAPP och
VOLYM | Loopen, 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 */
}
prefers-color-scheme: darkär en media query, somprefers-reduced-motion. Den frågar om användaren ställt in mörkt i sitt system.:root:not(.ljust)betyder html-elementet, om det inte har klassenljust. Den som valt ljust med knappen får ljust, även om systemet vill ha mörkt.:root.morktgäller när knappen valt mörkt, oavsett systemet.
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.
}
}
removetar bort båda klasserna,addsätter den som valdes. Aldrig båda samtidigt.lage === "morkt"jämför. Tre likhetstecken betyder är lika med, och svaret ärtrueellerfalse. Det är det ett enkelt=inte är.aria-pressedsäger till skärmläsaren att knappen är nedtryckt eller inte. Knappens text är densamma i båda lägena; det äraria-pressedoch utseendet som visar läget. Istil.cssstår utseendet under.lage-knapp[aria-pressed="true"].localStorageär webbläsarens minne för sajten.setItem("lage", lage)sparar valet under namnetlage, och det finns kvar nästa gång sidan öppnas.trybetyder försök. Går det fel, till exempel för att lagringen är avstängd, körs det som står icatchi stället, och sidan fungerar ändå.
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:
- Välj mörkt och ladda om. Sidan är fortfarande mörk: valet är sparat.
- Tabba till knappen och tryck mellanslag. Det låter och växlar.
- Ö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:
- 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.
- 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:
| Del | I lägesknappen | I ljudknappen |
|---|---|---|
| Knappen | En button med hidden, som skriptet
visar | Likadant |
Klassen på html | morkt eller
ljust | ljud-av, som ljudmodulen redan lyssnar
på |
| Läget för skärmläsaren | aria-pressed |
Likadant |
| Minnet | localStorage 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
- Knappen syns fast JavaScript är avstängt. En regel i
stil.csssom ger knappendisplayslår uthidden. Se avsnitt 5. - Paletten bara på ett ställe. Står den bara under
:root.morktfungerar knappen men inte systemets val. Står den bara i media queryn är det tvärtom. - Färger som inte följer med. Varje färg som står direkt i en regel, i stället
för som variabel, stannar kvar i ljust läge. Fyrsajtens
.knapphade två sådana, och de blev variabler i pass 48. Sök efter#ochhsl(utanför:root. - Skriptet på en sida utan knappen.
morkt-lage.jsutgår från att knappen finns. Saknas den ger det Cannot read properties of null. Knappen ska finnas iheaderpå varje sida som har skriptet.
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äsare | Stäng av | Gäller |
|---|---|---|
| Chrome, Edge | DevTools öppet, Ctrl + Shift + P, skriv Disable JavaScript, Enter. Slå på igen med Enable JavaScript | Bara så länge DevTools är öppet |
| Firefox | Skriv about:config i adressfältet, sök på
javascript.enabled och dubbelklicka så att det blir
false | Tills du ändrar tillbaka |
Ladda om sidan efter varje ändring. En sida som redan är laddad har redan kört sina skript.
- I Chrome slås JavaScript på igen när DevTools stängs. Bra när du glömmer, dåligt när du tror att du testar utan. Är du osäker, ladda om sidan: står raden skriptet laddades i konsolen är skripten igång. Att skriva något i konsolen visar ingenting – den svarar också när JavaScript är avstängt.
- I Firefox måste det slås på igen för hand. Den som glömmer har en webbläsare där ingen sajt fungerar som vanligt, i veckor.
Struktur, stil och beteende
| Lager | Språk | Utan det |
|---|---|---|
| Beteende | JavaScript | Sidan gör ingenting när man använder den, men allt står kvar |
| Stil | CSS | Sidan är ful men läsbar, och länkarna fungerar |
| Struktur | HTML | Ingenting |
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.
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:
- En meny som är dold med CSS och bara öppnas med JavaScript. Utan skript finns ingen meny. Det var därför menyn i område 5 fick radbryta i stället.
- En länk med
href="#"och en lyssnare. Utan skript leder den ingenstans. En länk ska ha en riktig adress. - En knapp som bara fungerar med skriptet, men som syns utan. Övningsknappen i
avsnitt 3 är en sådan. Antingen
hiddeni HTML:en och en rad i skriptet som tar bort det, eller bort.
Din sajt utan JavaScript
- Stäng av JavaScript och ladda om startsidan.
- 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.
- 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.
- 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.
- 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. - 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:
| Test | Med vad | Sida | Resultat | Åtgärd |
|---|---|---|---|---|
| Utan JavaScript | Chrome NN, DevTools: Disable JavaScript | alla sidor | Allt 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ält | Ingen |
| Utan JavaScript, andra webbläsaren | Firefox NN,
about:config, javascript.enabled av |
alla sidor | Som i Chrome | Ingen |
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:
- Ljudfilerna i
ljud/, packade ur zip-filen, med exakt de namn som står i tabellen. Döp inte om något. - 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.
- 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:
| fil | händelse | längd | synlig motsvarighet |
|---|---|---|---|
vaxla.mp3 | klick på lägesknappen | 120 ms | färgerna byts, knappen ser nedtryckt ut |
| Kolumn | Blir i koden | I referensprojektet |
|---|---|---|
| fil | Filen i ljud/, med exakt det namnet, och
data-ljud med namnet utan .mp3. Modulen lägger till
det | ljud/vaxla.mp3 och
data-ljud="vaxla" |
| händelse | Vilket 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ängd | Ingenting i koden. Längden är filen. Lyssna och kontrollera | – |
| synlig motsvarighet | Något som syns vid samma händelse | Klassen morkt och
.lage-knapp[aria-pressed="true"] |
Rörelsetabellen
| element | utlösare | vad som ändras | varaktighet | tidskurva | utan rörelse |
|---|---|---|---|---|---|
obligatoriskt fält i rapportera.html | lämnar fältet rätt ifyllt | kantens färg, stapel till vänster | 150 ms | ease-out | ändringen sker direkt |
Koden den blev står i avsnitt 3: falt.js och regeln
input.godkant. Cell för cell:
| Kolumn | Blir i koden | I referensprojektet |
|---|---|---|
| element | Selektorn i CSS:en, och det skriptet letar efter | input.godkant, input[required] |
| utlösare | Händelsen i skriptet, och villkoret om det finns ett | "change" och checkValidity() |
| vad som ändras | Egenskaperna i regeln och i
transition | border-color och
box-shadow |
| varaktighet | Tiden i transition |
150ms |
| tidskurva | Kurvan i transition |
ease-out |
| utan rörelse | Det som står i
@media (prefers-reduced-motion: reduce) |
transition: none |
Tre tekniska saker
- Webbläsaren spelar inte ljud förrän användaren gjort något på sidan. Ett ljud när sidan laddas, eller när musen bara passerar, stoppas och ger felet NotAllowedError i konsolen. Hela meddelandet står under listan.
- Ett ljud på en länk som byter sida hinns inte höras. Den nya sidan laddas och tar ljudet med sig. Vilken händelse som passar i stället avgörs i Gränssnittsdesign.
- Ett ljud som bara ska spelas ibland, till exempel när ett fält är fel ifyllt,
klarar modulen inte. Det kräver egen händelsekod som anropar
spelai ett villkor, i ett skript som står efterljud.jsihead. Funktionen finns på sidan eftersom ljudmodulen kopplades in först. Titta påelseifalt.js: där är ett ställe där ett sådant anrop kunde stå. Koden skriver du själv; det är C-nivå.
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
- Ljuden. Filerna i
ljud/,data-ljudpå elementen. Klicka och lyssna, ett ljud i taget, med hörlurar. - Den synliga motsvarigheten för varje ljud.
- 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?
- 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.
- 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:
| Fil | Vad den gör | Var | Ändrat i modulen |
|---|---|---|---|
ljud.js | Ljudmodulen. 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-av | alla 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:
| Rad | Avvikelse | Skäl |
|---|---|---|
| Rörelse, fältet | Markeringen försvinner utan övergång när fältet töms igen | Tabellen 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:
- Ljudet på en länk hördes inte, eftersom sidan byts. Det är inte inkopplat, och överlämningen behöver en annan händelse.
- Tidskurvan saknades i tabellen. Webbläsarens förval användes, och det står att värdet saknades och vem som ska fylla i det.
- Ett ljud saknades den 8 mars, och referensprojektets används tills det kommer.
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:
- Den styrs från HTML:en. Den som använder den sätter ett attribut och öppnar
aldrig filen. Ljudmodulen läser
data-ljudochdata-ljud-narmeddataset. - Den hittar sina element själv, med en attributselektor i
querySelectorAlloch en loop. - Den vet ingenting om sajten. Inga klasser, texter eller sidnamn från just din sajt står i filen.
- Inställningarna står överst, som versaler, och kommentaren överst säger hur den används och vad som får ändras.
- Den tål att inget matchar. Kopplas den in på en sida utan element går loopen noll varv, och ingenting händer.
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.
- Tangentbordet. Lyssnar du på en
buttoneller enaär det mesta redan klart, se avsnitt 3. Lägg undan musen och gör varje handling i överlämningen med Tab, Enter och mellanslag. - Ett läge, som på och av, når skärmläsaren med
aria-pressed, som lägesknappen. - Ett meddelande som dyker upp, till exempel en bekräftelse, når skärmläsaren
med
aria-live. Ett element medaria-live="polite"läses upp när dess text ändras, så fort skärmläsaren är klar med det den håller på med.
<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:
- En egen mörk palett. Den tas fram med samma metod som färgschemat i Gränssnittsdesign i oktober.
- Kontrasten mätt i båda lägena, med samma verktyg och samma gränser: 4.5:1 för
text, 3:1 för fältkanter och fokusring. Skriv mätningarna i
dokumentation.md. - Paletten på båda ställena, i media queryn och under
:root.morkt, så att systemets val fungerar också utan JavaScript. - Varje färg som variabel. Sök efter färger som står direkt i en regel.
- Prövat på tre sätt: med knappen, med systemets inställning i Rendering-panelen, och med JavaScript avstängt.
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
| Symptom | Trolig orsak | Åtgärd |
|---|---|---|
| Det låter inte, och konsolen är tom | Elementet saknar
data-ljud, ljud.js är inte inkopplad på sidan, eller
html har klassen ljud-av | Elements-fliken:
attributet på elementet och klassen på html. Network-fliken: laddades
ljud.js? |
| NotSupportedError i konsolen när ljudet ska spelas | Filen finns inte där modulen letar: fel namn, fel versal eller fel mapp | Jämför
data-ljud med filnamnet tecken för tecken, och kontrollera
LJUDMAPP |
| Ljudet fungerar lokalt men inte på Pages | Versaler i filnamnet eller i
data-ljud. Pages skiljer på stora och små bokstäver | Gemener ö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är | Elements-fliken: attributet tecken för tecken. Gemener, inga å, ä eller ö |
| NotAllowedError i konsolen | Ljudet spelas innan användaren gjort något: när sidan laddas eller vid hover | Byt 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 inte | Sidan byts innan ljudet hunnit spelas | Avvikelse. Skriv ner den och föreslå en annan händelse |
| Ljudet spelas en gång när sidan laddas, och aldrig mer | Funktionen
anropas i stället för att lämnas över: spela("…") direkt i
addEventListener | Lägg anropet inuti
function () { … } |
| Det låter fast ljudknappen stängt av ljudet | Klassen heter något annat
än ljud-av, eller sätts på ett annat element än
html | Samma 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 saknas | Klicka på radnumret. Kontrollera
selektorn mot HTML:en, defer, och att elementet finns på sidan |
| provknapp is null i Firefox | Samma fel som ovan, med Firefox ord | Som ovan |
| Identifier 'knapp' has already been declared | Två skript på samma sida har en variabel med samma namn | Döp om i ditt eget skript, till
exempel ljudknapp |
| is not a function | Fel stor eller liten bokstav:
queryselector, addEventlistener | Skriv 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ämta | Fel sökväg i
src: stavfel, snedstreck först, omvänt snedstreck eller
versaler | Sökvägen från sidan, med / och gemener |
| Knappen gör ingenting, och konsolen är tom | Händelsens namn är fel:
"onclick" eller "Click" | "click", med
gemener och utan on |
| Klassen sätts, men ingenting syns | Klassen heter olika i CSS:en och i skriptet, eller regeln saknas | Elements-fliken: sätts klassen? Jämför
namnet tecken för tecken med selektorn i stil.css |
Konsolen svarar null | Selektorn hittar inget, oftast för att punkten före klassnamnet saknas | Skriv selektorn som i CSS:
".prova-knapp" |
| Det jag ändrade i konsolen är borta | Konsolen ändrar DOM, inte filen | Det som ska finnas kvar skrivs i en fil |
| Koden jag skrev gör ingenting alls | Den skrevs i Elements-fliken | Byt till konsolen |
| Knappen fungerar med mus men inte med tangentbord | Lyssnaren sitter på
en div | Byt till button |
| Övergången syns inte, fast klassen sätts | Egenskapen i
transition är inte den som ändras, eller så är det
display som byts | Samma 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 ändra | Flytta den direkt efter regeln |
| Rörelsen går inte att stänga av | Den är satt med style i
skriptet | Flytta utseendet till en klass i CSS, och låt skriptet byta klass |
| Knappen syns fast JavaScript är avstängt | hidden 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 JavaScript | Texten skapas av skriptet | Flytta texten till HTML:en |
| Mörkt läge fungerar med knappen men inte med systemets inställning, eller tvärtom | Den mörka paletten står bara på ett av de två ställena | Samma
värden i media queryn och under :root.morkt |
| Några färger stannar ljusa i mörkt läge | Färgen står direkt i regeln, inte som variabel | Sök efter # och hsl( utanför
:root |
| Valet glöms när sidan laddas om | Webblä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 JavaScript | DevTools stängdes i Chrome, eller sidan laddades inte om | Öppna DevTools igen, stäng av, ladda om |
| Ingen sajt fungerar som vanligt i Firefox | javascript.enabled
står kvar på false | Sä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
| Begrepp | Betyder |
|---|---|
| JavaScript | Webbens programmeringsspråk. Säger vad som händer när någon använder sidan |
| modul | Färdig kod som gör en sak och går att koppla in i en sajt den inte byggdes för |
| kommentar | Rad som börjar med //. Till för människor, webbläsaren hoppar över den |
| variabel | Ett namn på ett värde |
| const | Variabel vars värde inte byts ut |
| let | Variabel vars värde får bytas |
| var | Gammalt 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äng | Text inom citattecken, till exempel "ljud/" |
| tal | Ett tal, utan citattecken, till exempel 0.5 |
| true och false | Sant och falskt. Svaret på en fråga i koden |
| funktion | Rader med ett namn, som körs när namnet anropas |
| parameter | Tom plats i en funktion som fylls i vid anropet, till exempel namn i spela(namn) |
| anrop | Att köra en funktion: namnet med parenteser, till exempel spela("vaxla") |
| return | Gå ut ur funktionen. Raderna under körs inte |
| villkor | if: koden inom klamrarna körs bara om svaret är ja |
| else | Annars. Körs när villkoret inte är uppfyllt |
| loop | for … of: gör samma sak en gång för varje element i en lista |
| händelse | Något som händer på sidan: ett klick, en tangent, ett fält som lämnas. Engelska event |
| event listener | Händelselyssnare. En funktion som väntar på en händelse och körs varje gång den kommer |
| addEventListener | Lägger till en lyssnare: vem, vilken händelse, vad som ska hända |
| click | Händelsen när någon klickar, eller trycker Enter eller mellanslag på en knapp |
| change | Händelsen när ett fält har ändrats och lämnats |
| keydown | Händelsen när en tangent trycks ned |
| event.key | Vilken tangent som trycktes |
| element | En del av sidan, till exempel en knapp eller en rubrik |
| DOM | Document Object Model. Sidan som ett träd av element, som webbläsaren håller i minnet |
| barn och förälder | Ett element inuti ett annat är dess barn. Elementet utanför är förälder |
| document | Hela sidan, i JavaScript |
| document.documentElement | Sidans html-element. Samma som :root i CSS |
| querySelector | Hittar det första elementet som matchar en selektor |
| querySelectorAll | Hittar alla element som matchar en selektor. Svarar med en lista |
| null | Ingenting. Svaret när querySelector inte hittar något |
| textContent | Elementets text. Kan läsas och bytas ut |
| classList | Elementets klasser, med add, remove, toggle och contains |
| toggle | Växlar en klass: finns den tas den bort, annars läggs den till |
| contains | Frågar om elementet har en klass. Svarar true eller false |
| setAttribute | Sätter ett attribut: namnet först, värdet sedan |
| hidden | Attribut som döljer ett element. En display-regel i CSS slår ut det |
| data-attribut | Eget attribut som börjar med data-, till exempel data-ljud. Information, inte beteende |
| dataset | Läser data-attribut. data-ljud-nar blir dataset.ljudNar |
| konsol | Flik i DevTools där du skriver JavaScript och får svaret direkt. Ctrl + Shift + J |
| console.log | Skriver i konsolen. Syns inte på sidan |
| felmeddelande | Röd rad i konsolen. Står på raden där felet märktes, inte där det gjordes |
| onclick | JavaScript i ett HTML-attribut. Används inte: beteendet hör hemma i .js-filen |
| script | Elementet som kopplar in en .js-fil, med src |
| defer | Skjuter upp skriptet tills hela HTML:en är läst, så att elementen finns |
| localStorage | Webbläsarens minne för sajten. Finns kvar till nästa besök |
| try och catch | Försök. Går det fel körs det i catch i stället, och sidan fungerar ändå |
| aria-pressed | Säger till skärmläsaren om en knapp är nedtryckt eller inte |
| prefers-color-scheme | Media query: användaren har ställt in mörkt eller ljust i sitt system |
| progressive enhancement | Basen först, sedan förbättringar som får saknas. JavaScript lägger till, det bär aldrig |
| new Audio | Skapar ett ljud ur en fil. play() spelar det |
| NotSupportedError | Felet när ljudfilen inte hittas |
| NotAllowedError | Felet när ett ljud spelas innan användaren gjort något |
| avvikelse | Ställe där koden inte gör som överlämningen säger. Skrivs ner med skäl |