Webbutveckling 1 · område 7 · fördjupning
Diskret funktionalitet: sajter som håller även när något fattas
Den här guiden går längre än de andra. Den är skriven för dig som siktar mot A, men avsnitt 1–6 gäller alla: att sajten inte ska krascha utan JavaScript är ett ska-krav i Projekt 6, inte en utmaning.
Exemplen kommer ur referensprojekt/. Den rätta versionen heter diskret/, den avskräckande hardkodad/. Koden i den här guiden är samma kod som ligger där.
Kraven guiden stödjer står i uppgift/projekt-6.md.
Vad du kan efter guiden
- Förklara vad diskret betyder och varför det är A-spårets kärna.
- Skilja på de tre lagren struktur, stil och beteende – och se när de blandats.
- Säga varför
onclicki HTML:en är fel, med ett skäl som inte är "det är fult". - Bygga en funktion som fungerar utan JavaScript och blir bättre med.
- Använda
hidden-mönstret utan att gå i CSS-fällan. - Skriva ett formulär där HTML validerar och skriptet bara förbättrar.
- Tala om för en skärmläsare att något ändrats, med
aria-live. - Stänga av JavaScript i din webbläsare och bedöma vad du ser.
- Skriva en egen modul som följer reglerna, inte bara kopiera en.
1. Vad diskret betyder
Diskret (på engelska unobtrusive) betyder att JavaScript lägger sig ovanpå en sida som redan fungerar, i stället för att bära den.
Tänk på en hiss i ett hus med trappa. Fungerar hissen tar du hissen. Står den stilla går du i trappan – du kommer fortfarande upp. Ett hus där hissen är enda vägen är ett sämre hus, hur bra hissen än är.
Din sajt är huset. HTML och CSS är trappan. JavaScript är hissen.
Varför någon skulle sakna JavaScript
Frågan kommer alltid, och den är rimlig. Nästan alla har JavaScript på. Men:
| Situation | Hur ofta |
|---|---|
| Skriptet hann inte laddas innan besökaren klickade | Varje dag, på varje sajt |
| Uppkopplingen bröts mitt i hämtningen | Tunnelbana, hiss, landsbygd |
| Ett fel i en annan del av koden stoppade allt | Oftare än du tror |
| Företagets brandvägg blockerar externa skript | Vanligt på arbetsplatser |
| Webbläsartillägg som blockerar skript | Ett par procent av alla besökare |
| Sökmotorn som indexerar sidan | Googles robot kör JavaScript, andra gör det inte |
| Skärmläsare och äldre hjälpmedel | Klarar ofta inte dynamiskt innehåll |
Den vanligaste är den första, och den drabbar alla. Mellan att HTML:en är ritad och att skripten körts finns en lucka. Är knappen klickbar i den luckan och gör ingenting, ser sajten trasig ut – för alla, varje gång.
Det är inte "för säkerhets skull"
En diskret sajt blir också enklare att läsa, enklare att felsöka, bättre för skärmläsare och lättare att flytta. Att den dessutom överlever utan JavaScript är resultatet, inte syftet.
2. De tre lagren
| Lager | Fil | Svarar på |
|---|---|---|
| Struktur | .html | Vad är det här? En rubrik, en lista, ett formulär |
| Stil | .css | Hur ser det ut? |
| Beteende | .js | Vad händer när någon gör något? |
Regeln är att varje lager ska gå att ta bort underifrån och uppåt. Ta bort JavaScript: sidan ser likadan ut och innehållet finns. Ta bort CSS:en också: sidan blir ful men fullt läsbar. Kvar finns strukturen, och den bär allt.
Du har redan sett vad som händer när lagren blandas. I område 3 lärde du dig att inte skriva style= ute i HTML:en. Det här är samma regel, ett steg upp.
Så ser en blandning ut
Ur hardkodad/:
<button type="button" class="lagesknapp"
onclick="byteLage()">Mörkt läge</button>
Ett element som är tre saker samtidigt: struktur (en knapp), utseende (klassen) och beteende (onclick). Ur diskret/:
<button type="button" class="lagesknapp" hidden
aria-pressed="false">Mörkt läge</button>
Struktur och en klass. Beteendet ligger i js/morkt-lage.js, som letar upp knappen själv.
3. Beteende hör inte hemma i HTML
"Det är fult" är inget skäl. Här är fyra som är det.
1. Du hittar det aldrig igen. Ett onclick på rad 48 i en av fem sidor syns inte när du söker i js/-mappen. Skriptet däremot står på ett ställe.
2. Bara en funktion per händelse. onclick kan hålla ett anrop. Behöver du två saker vid ett klick får du skriva om. addEventListener kan sätta hur många lyssnare som helst på samma element, oberoende av varandra.
3. Funktionen måste vara global. onclick="byteLage()" kräver att byteLage finns i fönstrets globala namnrymd. Det är därför hardkodad/js/sidan.js inte kan använda kapseln (function () { … })(); som alla moduler i område 6 har – och därmed kan två skript skriva över varandras variabler utan att någon märker det.
4. Knappen ljuger i luckan. Står onclick i HTML:en är knappen klickbar redan innan skriptet laddats. Klickar någon då händer ingenting, och felet syns inte någonstans.
Samma sak gäller länkar
<a href="javascript:visa('faglar')">Fåglar</a>
En länk som inte leder någonstans. Utan JavaScript är den helt död: den går inte att öppna i ny flik, den betyder ingenting för en sökmotor, och en skärmläsare säger att det är en länk fast det inte är det. Är det en knapp ska det vara en <button>. Leder den någonstans ska href vara den adressen.
<a href="#faglar">Fåglar</a>
Den här fungerar utan skript – den hoppar till avsnittet. Ett skript får gärna fånga klicket och göra något finare i stället.
4. Vad som ska fungera utan JavaScript
Kravet är inte att allt ska fungera. Det är att innehållet ska finnas och ingenting ska se trasigt ut.
| Ska fungera utan JavaScript | Får sluta fungera |
|---|---|
| All text och alla bilder syns | Filtrering och sortering |
| Alla länkar leder rätt | Bildspel som bläddrar automatiskt |
| Menyn går att använda | Att menyn fälls ihop till en hamburgare |
| Formuläret går att fylla i och skicka | Felmeddelanden under fälten medan du skriver |
| Rubrikerna och strukturen står kvar | Mörkt läge som sparas till nästa gång |
| Tabeller och listor är kompletta | Quizet som rättar sig självt |
Det avgörande är vem som äger innehållet. Står texten i HTML:en finns den alltid. Skrivs den in av ett skript finns den bara ibland.
Jämför de två versionerna av stigsidan utan skript:
diskret/ | hardkodad/ | |
|---|---|---|
| Bilder på sidan | 3 | 0 |
| Knappar som inte gör något | 0 | 3 |
Den hårdkodade sidan bygger sina bilder ur en lista i sidan.js. Utan skript finns listan inte, och då finns inte bilderna. Den diskreta har dem i HTML:en och låter skriptet göra om dem till en karusell.
5. hidden-mönstret
Mönstret som bär alla sex modulerna i område 6, i tre rader:
1. Det som bara fungerar med skript är gömt i HTML:en.
<button type="button" class="menyknapp" hidden
aria-expanded="false"
aria-controls="huvudmeny-lista">Meny</button>
2. Skriptet kontrollerar att elementet finns.
const knapp = document.querySelector(KNAPP);
const lista = document.querySelector(LISTA);
if (!knapp || !lista) { return; }
3. Först när skriptet vet att det fungerar tar det bort hidden.
knapp.hidden = false;
Läs ordningen igen: knappen syns inte förrän skriptet som styr den är igång. Därför kan det aldrig finnas en död knapp på sidan. Utan skript stannar hidden kvar, och menyn visas som en vanlig lista med länkar – som på en sida i område 4.
Fällan: CSS slår ut hidden
Det här kostade tid när området byggdes, och det är värt att kunna.
Attributet hidden sätter display: none – men i webbläsarens egen stilmall, och den är svagare än din. Skriver du någonstans i stil.css:
.karusell-styr {
display: flex;
}
…så syns raden ändå, med hidden och allt. Sidan ser rätt ut så länge JavaScript körs, eftersom skriptet tar bort attributet. Med JavaScript av står tre döda knappar kvar, och du får aldrig veta om det förrän någon säger till.
Lösningen är att låta regeln gälla bara när elementet inte är gömt:
.karusell-styr:not([hidden]) {
display: flex;
}
Regeln att komma ihåg: sätter du display på något som också kan ha hidden, skriv :not([hidden]). Det gäller display: flex, display: grid och display: block lika mycket.
6. Progressive enhancement, steg för steg
Progressive enhancement betyder ungefär "förbättra stegvis". Arbetsordningen är hela idén, och den är tvärtom mot hur de flesta börjar.
| Steg | Du bygger | Du kontrollerar |
|---|---|---|
| 1 | HTML som fungerar helt utan stil och skript | Läs sidan utan CSS. Går innehållet att förstå? |
| 2 | CSS som gör den läsbar och responsiv | Sidan är färdig att lämna in. Den är klar här |
| 3 | JavaScript som gör något bättre | Stäng av skriptet. Är du tillbaka på steg 2? |
Steg 2 är den viktiga raden i tabellen: en sajt utan JavaScript ska vara en färdig sajt, inte en trasig.
Ett exempel hela vägen
Reservatets artsida ska gå att filtrera på grupp.
Steg 1, HTML. Tre avsnitt med id och en innehållsförteckning som länkar till dem. Filterraden ligger där, tom och gömd:
<nav class="pasidan" aria-label="Innehåll på sidan">
<ul>
<li><a href="#faglar">Fåglar</a></li>
<li><a href="#daggdjur">Däggdjur</a></li>
</ul>
</nav>
<div class="filter" role="group" aria-label="Visa" hidden></div>
<section id="faglar" data-grupp="faglar"> … </section>
Utan skript har besökaren en innehållsförteckning som hoppar till rätt avsnitt. Det är inte filtrering, men det löser samma problem: att hitta.
Steg 2, CSS. Avsnitten staplas, innehållsförteckningen blir en sidospalt på bred skärm. Filterraden får display: flex – med :not([hidden]), enligt avsnitt 5.
Steg 3, JavaScript. galleri.js läser vilka data-grupp som finns, bygger en knapp per grupp, tar bort hidden och gömmer det som inte valts.
Tre lager, tre filer, och varje steg fungerar utan det ovanför.
Testet som avgör
Fråga dig: om jag tar bort det här skriptet, förlorar besökaren en genväg eller förlorar hen innehållet?
Genväg är rätt svar. Innehåll är fel.
7. Formulär: HTML först, skript sedan
Formulär är där det här blir konkret, och där skillnaden mellan E och A syns tydligast.
Vad HTML gör alldeles själv
<p>
<label for="epost">E-postadress</label>
<input type="email" id="epost" name="epost"
autocomplete="email" required>
</p>
<p>
<label for="antal">Antal personer</label>
<input type="number" id="antal" name="antal"
min="1" max="15" value="1" required>
</p>
Fyra attribut gör fyra jobb utan en rad JavaScript: required kräver ett svar, type="email" kräver ett snabel-a, min och max kräver ett rimligt tal, och autocomplete gör att telefonen fyller i åt besökaren. Webbläsaren stoppar sändningen och visar ett meddelande, på besökarens eget språk.
Den hårdkodade versionen slänger bort allt det:
<form novalidate>
<input type="text" id="namn" name="namn" autocomplete="name">
…
<button type="button"
onclick="skickaAnmalan()">Skicka anmälan</button>
</form>
novalidate stänger av webbläsarens kontroll. type="button" gör att knappen inte skickar något. Utan JavaScript går formuläret att skicka tomt – nej, värre: det går inte att skicka alls, för knappen gör ingenting.
Så gör modulen i stället
Det här är kärnan i hela guiden, och det står på tre rader i validering.js:
const formular = document.querySelector(FORMULAR);
if (!formular) { return; }
formular.setAttribute("novalidate", "");
Läs den sista raden igen. novalidate sätts av skriptet, inte i HTML:en.
| Utan JavaScript | Med JavaScript | |
|---|---|---|
novalidate finns? | Nej | Ja, skriptet satte det |
| Vem kontrollerar? | Webbläsaren | Skriptet |
| Hur visas fel? | Webbläsarens egen bubbla | <p class="fel"> under fältet |
Sidan har alltså alltid validering. Den byts bara ut mot en bättre när det går.
Skriptet bestämmer inte vad som är fel
Ett misstag som ser ut som ambition: att skriva egna regler i JavaScript för vad som är en giltig e-postadress. Modulen gör tvärtom – den frågar webbläsaren:
if (falt.checkValidity()) {
taBortFel(falt);
} else {
visaFel(falt);
}
checkValidity() returnerar sant eller falskt utifrån required, type, min och max i HTML:en. Objektet falt.validity säger sedan vilket av felen det var: valueMissing, typeMismatch, rangeUnderflow, rangeOverflow.
Vinsten är att reglerna bara finns på ett ställe. Lägger du till required på ett fält behöver skriptet inte ändras alls.
Och sändningen
formular.addEventListener("submit", function (handelse) {
const forstaFel = kontrollera();
if (forstaFel) {
handelse.preventDefault();
forstaFel.focus();
}
});
Lyssnaren ligger på submit, inte på knappens click. Det är skillnaden mellan ett formulär som fungerar och ett som bara ser ut att göra det: submit utlöses också av Enter i ett textfält, och av alla sätt att skicka ett formulär som du inte tänkt på.
preventDefault() stoppar sändningen – men bara när det finns fel. Är allt rätt gör skriptet ingenting, och formuläret skickas som vanligt.
8. Säg det som händer: aria-live
När ett skript ändrar något på sidan ser du det. Den som använder skärmläsare gör det inte – uppläsningen står där den står.
aria-live löser det. Ett element med det attributet läses upp när innehållet ändras, utan att fokus flyttas.
<div class="karusell-styr" hidden>
<button type="button" class="karusell-forra">Föregående</button>
<p class="karusell-lage" aria-live="polite"></p>
<button type="button" class="karusell-nasta">Nästa</button>
</div>
Skriptet skriver "Bild 2 av 3" i stycket varje gång någon bläddrar, och skärmläsaren säger det.
| Värde | Betyder |
|---|---|
polite | Läses när användaren är tyst. Det du ska använda |
assertive | Avbryter mitt i. Bara för fel som måste stoppa något |
off | Standard. Läses inte |
Tre regler:
- Elementet ska finnas i HTML:en från början, tomt. Skapar skriptet det samtidigt som det fyller det hinner skärmläsaren inte uppfatta ändringen.
- Skriv kort. "Bild 2 av 3" är bra. Ett helt stycke blir tjat.
- En i taget. Fem
aria-livepå samma sida pratar i munnen på varandra.
Referensprojektet har två: karusellens lägesrad och quizets resultat. Båda är tomma i HTML:en, båda är polite.
aria-live är det tillgänglighetsverktyg du inte hade i område 4, och det är just för att det hör ihop med skript som ändrar saker.
9. Graceful degradation
Graceful degradation betyder att det ska bli sämre, inte trasigt, när något fattas. Det är samma mål som progressive enhancement, sett från andra hållet: där börjar man nerifrån och bygger uppåt, här börjar man med det färdiga och frågar vad som händer när delar faller bort.
| Faller bort | Snyggt | Trasigt |
|---|---|---|
| JavaScript | Menyn är en vanlig lista | Knapp som inte gör något |
| En bildfil | alt-texten står där bilden skulle varit | En tom ruta utan förklaring |
| Ett typsnitt | Texten visas i reservtypsnittet | Sidan står tom och väntar |
localStorage (privat läge) | Läget gäller den här sidan, glöms sedan | Skriptet kraschar och menyn slutar fungera |
| En CSS-egenskap webbläsaren inte kan | Något enklare visas i stället | Layouten faller isär |
Den fjärde raden är verklig. localStorage kastar ett fel i privat läge i vissa webbläsare, och ett fel som inte fångas stoppar resten av filen. Därför ser morkt-lage.js ut så här:
function spara(lage) {
try {
localStorage.setItem(NYCKEL, lage);
} catch (fel) {
// Går inte att spara. Läget gäller ändå tills sidan laddas om.
}
}
try/catch betyder "försök – går det inte, gör det här i stället". Den tomma catch-satsen är ett medvetet val, och kommentaren finns för att en tom catch annars ser ut som ett misstag.
10. Skriv en egen diskret funktion
A-spåret i Projekt 6 vill ha en funktion du skrivit själv. Här är reglerna och ett helt exempel.
Reglerna
| Regel | Varför |
|---|---|
Allt i (function () { "use strict"; … })(); | Ingen global variabel, inga krockar |
| Zon ÄNDRA HÄR överst med klassnamn och texter | Den som använder modulen ska slippa läsa resten |
Leta upp elementen, return om de saknas | Ett skript ska aldrig krascha en sida |
Det som bara fungerar med skript har hidden | Inga döda knappar |
| Byt attribut och klasser – CSS:en gör resten | Beteende och utseende hålls isär |
addEventListener, aldrig onclick i HTML | Avsnitt 3 |
textContent, inte innerHTML, för text | Säkerhet och enkelhet |
Inga console.log kvar | Konsolen ska vara tom när det fungerar |
Ett helt exempel: visa mer
Funktionen: ett långt avsnitt kortas av, och en knapp fäller ut resten. Utan skript står hela texten där.
HTML – hela texten finns, knappen är gömd:
<section class="fallbar">
<h2>Reservatets historia</h2>
<div class="fallbar-text" id="historia-text">
<p>Mossen dikades ut första gången 1912 …</p>
<p>Kommunen köpte marken 1984 …</p>
<p>Reservatet bildades 1987 …</p>
</div>
<button type="button" class="fallbar-knapp" hidden
aria-expanded="false"
aria-controls="historia-text">Visa mer</button>
</section>
CSS – korta av bara när skriptet är igång:
.fallbar-knapp:not([hidden]) {
display: inline-block;
}
.fallbar:has(.fallbar-knapp[aria-expanded="false"]) .fallbar-text {
max-height: 8rem;
overflow: hidden;
}
:has() läser "ett .fallbar som innehåller en knapp med aria-expanded="false"". Knappen står efter texten i HTML:en, och en vanlig väljare kan bara peka framåt – :has() är det som låter regeln peka bakåt. Den fungerar i alla webbläsare sedan 2023.
JavaScript:
(function () {
"use strict";
/* ===== ÄNDRA HÄR ========================================== */
const KNAPP = ".fallbar-knapp";
const TEXT_MER = "Visa mer";
const TEXT_MINDRE = "Visa mindre";
/* ===== SLUT PÅ ÄNDRA HÄR ================================== */
const knapp = document.querySelector(KNAPP);
if (!knapp) { return; }
const id = knapp.getAttribute("aria-controls");
const text = document.getElementById(id);
if (!text) { return; }
knapp.hidden = false;
knapp.textContent = TEXT_MER;
knapp.addEventListener("click", function () {
const utfalld = knapp.getAttribute("aria-expanded") === "true";
knapp.setAttribute("aria-expanded", utfalld ? "false" : "true");
knapp.textContent = utfalld ? TEXT_MER : TEXT_MINDRE;
});
})();
Gå igenom den mot reglerna: kapsel, zon, två kontroller med return, hidden bort först när allt hittats, attributbyte i stället för style, textContent.
Notera .fallbar-text i CSS:en: det är aria-expanded på knappen som styr höjden, inte en klass skriptet sätter. Skriptet ändrar ett attribut, och CSS:en gör tre saker av det.
Vad du kan bygga
Förslag som är lagom stora och lätta att göra diskreta:
| Idé | Utan skript |
|---|---|
| Visa mer / visa mindre | Hela texten syns |
| Tillbaka till toppen-knapp | Sidan går att skrolla |
| Kopiera adressen-knapp | Adressen står som text bredvid |
| Räknare för tecken kvar i ett textfält | maxlength i HTML:en gör jobbet |
| Filtrera en tabell | Tabellen är komplett och sorterad |
| Bekräfta innan en länk öppnas i ny flik | Länken fungerar som vanligt |
11. Testa med JavaScript avstängt
Ett krav du inte provat är ett krav du inte uppfyllt.
Så stänger du av
| Webbläsare | Väg |
|---|---|
| Chrome, Edge | Inspektera → Ctrl + Shift + P → skriv javascript → Disable JavaScript. Gäller så länge Inspektera är öppen |
| Firefox | Skriv about:config i adressfältet, sök javascript.enabled, sätt till false. Kom ihåg att sätta tillbaka den |
| Alla | Ett tillägg som NoScript. Onödigt för det här |
Chrome-vägen är den att använda: den slås av när du stänger Inspektera, så du kan inte glömma den.
Vad du ska titta efter
Gå igenom varje sida och fråga i den här ordningen:
- Finns all text och alla bilder? Saknas något skrivs det in av ett skript och ska flyttas till HTML:en.
- Syns någon knapp eller kontroll som inte gör något? Då saknas
hidden, eller så slår CSS:en ut det – avsnitt 5. - Går menyn att använda? Alla länkar ska nås.
- Går formuläret att fylla i och skicka? Med
requiredkvar? - Ser sidan trasig ut? Tomma rutor, halva layouter, avhuggna avsnitt.
- Står det något som ljuger? "Klicka för att rätta" när knappen är död.
Skriv ner resultatet
I README.md, under en rubrik. Det är det som bedöms – inte att allt fungerar, utan att du vet vad som inte gör det:
Testat med JavaScript avstängt 4 mars:
Startsidan OK. Allt innehåll finns, inga döda knappar
Galleriet OK. Filterraden gömd, alla bilder syns
Kontakt OK. required kvar, webbläsaren validerar själv
Quizsidan Knappen "Rätta" syns och gör inget när den
trycks. Medvetet: den är type="submit" och
sidan laddas om. Frågorna finns kvar.
Sista raden är den som visar att du förstått skillnaden mellan degraderad och trasig.
12. Ta det längre
<noscript> – nästan aldrig svaret
<noscript>Den här sidan kräver JavaScript.</noscript>
Taggen visas bara när JavaScript är av. Frestelsen är att lösa hela guiden med den. Det är fel väg: den förklarar varför sidan är trasig i stället för att låta bli att vara trasig.
Det finns ett rimligt bruk: att säga att en specifik funktion inte är tillgänglig, när innehållet ändå finns. "Kartan visas inte utan JavaScript. Adressen står under."
:has() på fler ställen
Du såg :has() i avsnitt 10. Det gör samma tjänst i större delar av sidan:
.karusell:has(.karusell-styr:not([hidden])) .karusell-bilder {
max-height: 20rem;
overflow: hidden;
}
"Ser karusellen att dess knappar är synliga, alltså att skriptet är igång, så beter sig listan som en karusell." Samma idé som hidden-mönstret, utan att skriptet behöver sätta en klass.
En klass på <html> i stället
Ett vanligt mönster på riktiga sajter: det första skriptet på sidan sätter en klass, och CSS:en kan då skilja på de två världarna.
document.documentElement.classList.add("js");
.js .bara-utan-js { display: none; }
Det fungerar, och det är värt att känna igen. hidden-mönstret är ändå att föredra i den här kursen: det håller ihop element och beteende, och det kräver ingenting av <head>.
Läs modulerna som förlagor
De sex modulerna i diskret/js/ är skrivna efter reglerna i avsnitt 10. meny.js är kortast och lättast att läsa. validering.js är den som lär ut mest.
13. Felsökning
14 rader
| Symtom | Trolig orsak | Gör så här |
|---|---|---|
| Knappen syns utan JavaScript | hidden saknas i HTML:en | Lägg till det. Skriptet tar bort det |
Knappen syns fast hidden står där | En display-regel i CSS:en | :not([hidden]) på regeln. Avsnitt 5 |
| Knappen syns aldrig, inte ens med JavaScript | Skriptet hittar inte sitt element och avslutar | Jämför klassnamnet i HTML och i zonen |
| Innehåll försvinner utan JavaScript | Skriptet skriver in det med innerHTML | Flytta texten till HTML:en |
| Formuläret går att skicka tomt | novalidate står i HTML:en | Ta bort det. Skriptet sätter det själv |
| Formuläret skickas fast fält är fel | Lyssnaren ligger på knappens click | Lyssna på submit i stället |
| Enter i ett fält gör inget | type="button" på knappen | type="submit" |
| Felmeddelandet läses inte upp | aria-live saknas, eller elementet skapades nyss | Ha ett tomt element i HTML:en från början |
| Skärmläsaren läser samma sak om och om | assertive där polite räcker | Byt |
| Allt slutar fungera i privat läge | localStorage utan try/catch | Avsnitt 9 |
| Ett skript stoppar de andra | Fel i en fil som körs innan | Konsolen. Röda raden säger fil och radnummer |
| Sidan fungerar lokalt men inte publicerad | Sökväg med stor bokstav, eller absolut sökväg | Relativa sökvägar, gemener |
aria-expanded ändras men inget händer | CSS:en lyssnar på en klass, inte attributet | Välj ett av dem och håll dig till det |
| Länken gör inget utan JavaScript | href="javascript:…" eller href="#" | Ge den en riktig adress, eller gör den till en button |
14. Snabbreferens
hidden-mönstret
<button type="button" class="knapp" hidden
aria-expanded="false">Text</button>
const knapp = document.querySelector(".knapp");
if (!knapp) { return; }
knapp.hidden = false;
.knapp:not([hidden]) { display: inline-block; }
Formulär som validerar i båda världarna
<input type="email" id="epost" required>
formular.setAttribute("novalidate", "");
if (falt.checkValidity()) { /* rätt */ } else { /* fel */ }
Meddelande som läses upp
<p class="status" aria-live="polite"></p>
Checklistan före inlämning
| 1 | Stäng av JavaScript i Inspektera |
| 2 | Öppna varje sida |
| 3 | Finns all text och alla bilder? |
| 4 | Syns någon kontroll som inte gör något? |
| 5 | Går formuläret att skicka, med required kvar? |
| 6 | Skriv ner resultatet i README.md |
Tre saker som aldrig hör hemma i HTML
onclick="…" | Använd addEventListener |
href="javascript:…" | Använd button, eller en riktig href |
novalidate | Låt skriptet sätta det |
15. Ordlista
Ordträning
| Ord | Betyder |
|---|---|
| aria-live | Attribut som gör att ändringar i ett element läses upp av skärmläsare |
| assertive | aria-live-värde som avbryter uppläsningen. Används sällan |
| checkValidity() | Metod som frågar webbläsaren om ett fält är korrekt ifyllt |
| degraderad | Fungerar sämre men inte trasigt när något fattas |
| diskret | Om skript: lägger sig ovanpå en sida som redan fungerar |
| global | Variabel eller funktion som syns i hela fönstret. Krockar lätt |
| graceful degradation | Att det blir sämre, inte trasigt, när delar faller bort |
| hidden | HTML-attribut som gömmer ett element tills ett skript tar bort det |
| inline-attribut | Beteende skrivet i HTML:en, som onclick. Undviks |
| kapsel | (function () { … })(); – håller variabler för sig själva |
| lucka | Tiden mellan att HTML:en ritats och att skripten körts |
| noscript | Tagg vars innehåll bara visas när JavaScript är av |
| novalidate | Attribut som stänger av webbläsarens formulärkontroll |
| polite | aria-live-värde som väntar tills användaren är tyst |
| preventDefault() | Stoppar det webbläsaren annars skulle gjort, till exempel skicka ett formulär |
| progressive enhancement | Att bygga nerifrån: HTML, sedan CSS, sedan JavaScript |
| submit | Händelsen när ett formulär skickas, oavsett hur |
| try/catch | "Försök – går det inte, gör det här i stället" |
| unobtrusive | Engelska för diskret |
| validity | Objekt som säger vilket fel ett fält har |