Hvert kodeeksempel står alene. Navne som navn og point genbruges på tværs af afsnit og skal omdøbes, hvis eksemplerne kopieres ind i samme fil.
<!-- Ekstern fil er normalt bedst -->
<script src="app.js" defer></script>
<!-- defer: hent filen nu, kør den først når HTML er læst -->
defer gør, at elementerne i HTML'en findes, når JavaScript-koden kører. Skriv ikke JavaScript direkte i HTML-attributter som onclick="..."; brug addEventListener.
const navn = "Ada"; // kan ikke tildeles igen
let point = 0; // kan ændres
point += 10;
const aktiv = true; // boolean
const alder = 17; // number (heltal og decimal)
const intet = null; // bevidst: ingen værdi
let ukendt; // undefined: ikke tildelt endnu
console.log(typeof navn); // "string"
console.log(`${navn}: ${point} point`);
const som udgangspunkt. Vælg kun let, når variablen senere skal have en ny værdi. Brug aldrig var i ny kode.// regning + - * / % **
const rest = 17 % 5; // 2
const pot = 2 ** 3; // 8
// sammenligning: brug altid === og !==
5 === "5"; // false: type OG værdi
5 == "5"; // true: undgå ==
// logik
alder >= 18 && aktiv; // og
alder < 18 || !aktiv; // eller / ikke
const tekst = aktiv ? "på" : "af";
if (point >= 100) {
console.log("Vundet!");
} else if (point > 0) {
console.log("Fortsæt");
} else {
console.log("Start spillet");
}
for (let i = 0; i < 3; i++) console.log(i);
for (const navn of ["Ada", "Bo", "Cia"]) {
console.log(navn);
}
while (point < 100) point += 10;
for...of går gennem værdierne i et array. Brug ikke for...in til arrays; det går gennem indeks/properties.
function gange(a, b = 1) {
return a * b;
}
const resultat = gange(6, 7); // 42
// Arrow functions
const erLige = tal => tal % 2 === 0;
const laegSammen = (a, b) => a + b;
function visBesked(tekst) {
console.log(tekst); // returnerer undefined
}
Arrow functions er praktiske i fx map, filter og event listeners. En funktion med { ... } skal selv skrive return.
const tal = [4, 7, 2];
tal.push(9); // tilføj sidst
const sidste = tal.pop(); // fjern sidst
const fordoblet = tal.map(t => t * 2);
const store = tal.filter(t => t >= 5);
const fundet = tal.find(t => t > 5); // første eller undefined
const harTo = tal.includes(2);
const sum = tal.reduce((s, t) => s + t, 0);
tal.sort((a, b) => a - b); // sorter stigende (ændrer tal!)
const kopi = [...tal]; // kopi, ikke samme array
map laver et nyt array. filter vælger elementer. find giver ét element. push, pop, sort og reverse ændrer selve arrayet.
const elev = {
navn: "Ada",
klasse: "3x",
point: 42,
hilsen() { return `Hej ${this.navn}`; }
};
elev.navn; // "Ada"
elev["klasse"]; // "3x"
elev.point = 50;
const { navn, point } = elev;
const medNiveau = { ...elev, niveau: 2 };
Et objekt samler navngivne værdier. Spread (...) kopierer kun det øverste niveau; indlejrede objekter deles stadig.
const rå = " Hej verden ";
rå.trim().toUpperCase(); // "HEJ VERDEN"
rå.includes("verden"); // true
rå.replace("verden", "JS"); // ny streng
Number("42"); // 42
Number("hej"); // NaN
Number.isNaN(Number("hej")); // true
const pris = 12.5;
pris.toFixed(2); // "12.50" (streng)
try {
const data = JSON.parse('{"navn":"Ada"}');
console.log(data.navn);
} catch (fejl) {
console.error("Ugyldig JSON", fejl);
}
const by = elev.adresse?.by; // undefined, ikke fejl
const vistNavn = elev.kaldenavn ?? elev.navn;
// ?? bruger højresiden kun ved null eller undefined
?. stopper sikkert, hvis noget mangler. ?? er ofte bedre end ||, fordi 0, false og "" stadig er gyldige værdier.
const elev = { navn: "Ada", point: 42 };
const json = JSON.stringify(elev);
localStorage.setItem("elev", json);
const gemt = localStorage.getItem("elev");
const igen = gemt ? JSON.parse(gemt) : null;
localStorage.removeItem("elev");
localStorage gemmer kun strenge og bevares efter browseren lukkes. Gem derfor objekter som JSON. Gem aldrig adgangskoder eller følsomme data dér.
const overskrift = document.querySelector("h1");
const knap = document.querySelector("#gem-knap");
const felter = document.querySelectorAll(".felt");
overskrift.textContent = "Ny overskrift";
knap.disabled = true;
knap.classList.add("skjult");
knap.classList.toggle("valgt");
for (const felt of felter) {
console.log(felt.value);
}
Argumentet er en CSS-selector: "h1" finder et tag, "#gem-knap" finder id="gem-knap", og ".felt" finder alle med class="felt". querySelector giver det første match (eller null, hvis ingen findes); querySelectorAll giver alle matches som en liste, du kan løbe igennem.
Når du har fat i et element, kan du ændre det: textContent erstatter teksten, disabled slår et inputfelt/knap fra, og classList.add/remove/toggle tilføjer eller fjerner CSS-klasser, så stylingen ændres uden at røre CSS-filen.
Vigtigt om sikkerhed: brug textContent til tekst fra brugeren – ikke innerHTML, som fortolker indholdet som HTML. Indsætter du brugerinput med innerHTML, kan ondsindet kode (fx et skjult <script>) blive kørt på din side. textContent viser teksten bogstaveligt og er derfor sikker.
const form = document.querySelector("#tilmelding");
const input = document.querySelector("#navn");
form.addEventListener("submit", event => {
event.preventDefault();
const navn = input.value.trim();
if (navn === "") {
input.focus();
return;
}
console.log(`Velkommen, ${navn}`);
});
input.addEventListener("input", () => {
console.log(input.value);
});
event.preventDefault() forhindrer browserens normale handling. Til klik bruges "click"; til formularer er "submit" bedst, fordi Enter også virker.
HTML'en på siden er ikke låst fast. JavaScript kan løbende oprette, ændre og slette elementer i DOM'en (browserens levende model af siden) – og skærmen opdateres med det samme, uden at siden genindlæses. Sådan virker fx en indkøbsliste, hvor nye punkter dukker op, når brugeren taster dem ind.
const liste = document.querySelector("#liste");
const li = document.createElement("li");
li.textContent = "Nyt punkt";
li.classList.add("aktiv");
liste.append(li);
// Event delegation: én listener til mange/fremtidige knapper
liste.addEventListener("click", event => {
const knap = event.target.closest("button.slet");
if (knap) knap.closest("li").remove();
});
JavaScript har kun én tråd: ét stykke kode kører ad gangen, og resten venter. Men ventende ting (timere, events, fetch-svar) lægges i køer og afhentes af event loop'en, hver gang den aktuelle kode er færdig. Eksemplet viser rækkefølgen:
console.log("1. start");
setTimeout(() => console.log("3. timeout"), 0);
Promise.resolve().then(() => console.log("2. promise"));
console.log("1. slut");
// Rækkefølge: 1. start → 1. slut → 2. promise → 3. timeout
Call stack: kører én funktion ad gangen – kan ikke afbrydes. Når stacken er tom, tømmes microtask-køen (.then(), await-fortsættelse) helt, og først derefter tages næste macrotask (setTimeout, events, fetch-svar). Derfor slår promise timeout, selv med 0 ms.
async function hentElever() {
try {
const svar = await fetch("elever.json");
if (!svar.ok) throw new Error(`HTTP ${svar.status}`);
return await svar.json();
} catch (fejl) {
console.error("Kunne ikke hente elever", fejl);
return [];
}
}
async function start() {
const elever = await hentElever();
console.log(elever);
}
start();
fetch fejler ikke automatisk ved fx HTTP 404 eller 500. Tjek derfor altid svar.ok. await må kun bruges inde i en async-funktion (eller i et ES-module).console.log(...)skriv til browserens konsol
Array.isArray(x)er x et array?
Object.keys(obj)array med objektets property-navne
Math.random()tilfældigt tal fra 0 (inkl.) til 1 (ekskl.)
Math.floor(x)rund ned til heltal
setTimeout(f, ms)kør f én gang efter millisekunder
setInterval(f, ms)kør f gentagne gange; stop med clearInterval
document.querySelector(s)første element, der matcher CSS-selector s
=== frem for ==.null; kontrollér at elementet findes.input.value er altid en streng – brug fx Number(input.value) ved tal.map/filter ændrer ikke original-arrayet, men sort gør.Promise; brug await eller .then().