2. juli 2026

Introduktion til browser events

En event (på dansk en hændelse)er en signal, der indikerer, at noget er sket. Alle DOM-noder genererer sådanne signaler (men events er ikke begrænset til DOM).

Her er en liste over et par af de mere nyttige DOM-events, bare så du får en fornemmelse af, hvad der er muligt.: Mouse events:

  • click – når musen klikker på et element (ved touchskærme er det når der trykkes på skærmen).
  • contextmenu – når musen højreklikker på et element.
  • mouseover / mouseout – når musen peger over / forlader et element.
  • mousedown / mouseup – når museknappen trykkes / frigives over et element.
  • mousemove – når musen bevæges.

Keyboard events:

  • keydown and keyup – når en tast på tastaturet trykkes og frigives.

Form element events:

  • submit – når besøgende indsender et <form>.
  • focus – når besøgende fokuserer på et element, f.eks. på et <input>.

Document events:

  • DOMContentLoaded – når HTML er hentet og processeret, DOM er færdigbygget.

CSS events:

  • transitionend – når en CSS-animation er færdig.

Der er mange andre events. Vi vil dække dem i detaljer i de kommende kapitler.

Event-håndtering

Til at reagere på events kan vi tildele en handler – en funktion, der kører i tilfælde af en event.

Handlers er en måde at køre JavaScript-kode der reagerer på system- eller brugerhandlinger.

Der er flere måder at tildele en handler. Lad os se nærmere på dem, begyndende med den mest simple.

HTML-attribut

En handler kan sættes direkte i HTML ved med en attribut kaldet on<event>.

For eksempel, for at tildele en click handler til et input, kan vi bruge onclick, som her:

<input value="Klik mig" onclick="alert('Klik!')" type="button">

Når musen klikker på elementet, kører koden inden i onclick.

Bemærk, at inde i onclick bruger vi enkelt anførelsestegn, fordi attributten selv er i dobbelte anførelsestegn. Hvis vi glemmer, at koden er inde i attributten og bruger dobbelte anførelsestegn inde, som dette: onclick="alert("Click!")", så vil det ikke virke korrekt.

En HTML-attribut er ikke et praktisk sted at skrive meget kode, så vi bør oprette en JavaScript-funktion og kalde den der.

Her vil et klik køre funktionen kaldet countRabbits():

<script>
  function countRabbits() {
    for(let i=1; i<=3; i++) {
      alert("Kanin nummer " + i);
    }
  }
</script>

<input type="button" onclick="countRabbits()" value="Tæl kaniner!">

Som vi ved så er HTML-attributter ikke case-sensitive, så ONCLICK virker lige så godt som onClick og onCLICK… Men ofte er attributter i små bogstaver: onclick.

DOM egenskaber

Vi kan tildele en handler ved hjælp af en DOM-egenskab on<event>.

For eksempel, elem.onclick:

<input id="elem" type="button" value="Klik mig">
<script>
  elem.onclick = function() {
    alert('Mange tak!');
  };
</script>

Hvis en handler tildeles ved hjælp af en HTML-attribut, så læser browseren den, opretter en ny funktion fra attributindholdet og skriver den om til en DOM-egenskab.

Så denne måde er faktisk den samme som den forrige.

Disse to stykker kode virker ens:

  1. Kun HTML:

    <input type="button" onclick="alert('Klik!')" value="Button">
  2. HTML + JS:

    <input type="button" id="button" value="Button">
    <script>
      button.onclick = function() {
        alert('Klik!');
      };
    </script>

I det første eksempel bruges HTML-attributten til at initialisere button.onclick, mens det i det andet eksempel er selve scriptet … grundklæggende ingen forskel.

Da der kun er en onclick-egenskab, kan vi ikke tildele mere end én eventhandler.

I eksemplet nedenfor overskriver tildelelsen af en handler med JavaScript den eksisterende handler:

<input type="button" id="elem" onclick="alert('Før')" value="Klik mig">
<script>
  elem.onclick = function() { // overskriver den eksisterende handler
    alert('Efter'); // du vil kun se denne
  };
</script>

For at fjerne en handler – tildel elementet elem.onclick = null.

Tilgå elementet: this

Værdien af this inde i en handler er elementet. Det element, som har handleren på sig.

I koden nedenfor viser button sit indhold ved hjælp af this.innerHTML:

<button onclick="alert(this.innerHTML)">Klik mig</button>

Mulige fejl

Når du starter med at arbejde med events så er der små detaljer der er værd at have i baghovedet.

Vi kan sætte en eksisterende funktion som en handler:

function sayThanks() {
  alert('Tak!');
}

elem.onclick = sayThanks;

Men vær forsigtig: funktionen skal tildeles som sayThanks, ikke sayThanks().

// rigtigt
button.onclick = sayThanks;

// forkert
button.onclick = sayThanks();

Hvis vi tilføjer paranteser, så bliver sayThanks() til et funktionskald. Så den sidste linje tager faktisk resultatet af funktionskaldet, som er undefined (da funktionen ikke returnerer noget), og tildeler det til onclick. Det virker ikke.

…Modsat så behøver vi paranteser i markup’en:

<input type="button" id="button" onclick="sayThanks()">

Forskellen er relativ simpel. Når browseren læser attributten, opretter den en handlerfunktion hvor kroppen er indholdet fra attributten.

Så markup’en genererer denne egenskab:

button.onclick = function() {
  sayThanks(); // <-- attributtens indhold placeres her
};

Brug ikke setAttribute til handlere.

Et kald som dette vil ikke virke:

// et klik på <body> vil generere fejl,
// fordi attributter altid er strenge, bliver function til en streng
document.body.setAttribute('onclick', function() { alert(1) });

DOM-egenskaber kigger efter store og små bogstaver.

Tildel en handler til elem.onclick, ikke elem.ONCLICK, fordi DOM-egenskaber er case-sensitive.

addEventListener

Et grundlæggende problem med de ovenstående måder at tildele handlere er, at vi ikke kan tildele flere handlere til en begivenhed.

Lad os sige, at en del af vores kode vil ønske at fremhæve en knap ved klik, og en anden vil ønske at vise en besked på samme klik.

Vi vil gerne tildele to event-handlere for det. Men en ny DOM-egenskab vil overskrive den eksisterende:

input.onclick = function() { alert(1); }
// ...
input.onclick = function() { alert(2); } // erstatter den forrige handler

Udviklere af web standarden forstod dette for lang tid siden og foreslog en alternative måde at håndtere handlere ved hjælp af de specielle metoder addEventListener og removeEventListener, som ikke er bundet af denne begrænsning.

Syntaksen for at tilføje en handler er:

element.addEventListener(event, handler, [options]);
event
Navn på begivenheden, f.eks. "click".
handler
Handlerfunktionen.
options
Et objekt der kan indeholde yderligere valgfrie egenskaber:
  • once: hvis true, vil listeneren automatisk fjernes efter den udløses.
  • capture: den fase der skal håndtere hændelsen. Dette bliver dækket senere i kapitlet Bubbling and capturing. Af historiske grunde kan options også sættes til false/true, der betyder det samme som {capture: false|true}.
  • passive: hvis true, vil handleren ikke kalde preventDefault(). Det forklares senere i Browser default actions.

For at fjerne en handler, brug removeEventListener:

element.removeEventListener(event, handler, [options]);
Fjernelse kræver den samme funktion

For at fjerne en handler skal vi overføre nøjagtigt den samme funktion, som blev tildelt.

Dette virker ikke:

elem.addEventListener( "click" , () => alert('Tak!'));
// ....
elem.removeEventListener( "click", () => alert('Tak!'));

Denne handler vil ikke blive fjernet, fordi removeEventListener får en anden anonym funktion – med den samme kode. Det spiller ingen rolle, da funktionen er et andet objekt.

Her er den rigtige måde:

function handler() {
  alert( 'Tak!' );
}

input.addEventListener("click", handler);
// ....
input.removeEventListener("click", handler);

Bemærk venligt – hvis vi ikke gemmer funktionen i en variabel så kan vi ikke fjerne den. Der er ikke nogen mulighed for at “trække en handler ud” af et element der har fået den tildelt med addEventListener.

Flere kald til addEventListener tillader os at tilføje flere handlere, som her:

<input id="elem" type="button" value="Klik mig"/>

<script>
  function handler1() {
    alert('Tak!');
  };

  function handler2() {
    alert('Tak igen!');
  }

  elem.onclick = () => alert("Hej");
  elem.addEventListener("click", handler1); // Tak!
  elem.addEventListener("click", handler2); // Tak igen!
</script>

Som det fremgår af eksemplet ovenfor, kan vi sætte handlere både ved hjælp af en DOM-egenskab og addEventListener. Men generelt bruger vi kun én af metoderne.

For nogle events, vil handlere kun virke med addEventListener

Der findes events der ikke kan tildeles via en DOM-egenskab. Kun med addEventListener.

For eksempel eventen DOMContentLoaded, der affyres når documentet er hentet og DOM-træet er bygget færdigt.

// vil aldrig køre
document.onDOMContentLoaded = function() {
  alert("DOM er klar");
};
// denne måde virker
document.addEventListener("DOMContentLoaded", function() {
  alert("DOM er klar");
});

addEventListener er mere universiel … selvom sådanne event mere er undtagelsen end reglen.

Event objekt

For at kunne håndtere en event ordentligt skal vi vide mere om, hvad der er sket. Informationen “click” eller “keydown” er sjældent nok. Vi har ofte også brug for information om, hvilket koordinat musen havde eller hvilken tast der blev trykket på, etc.

Når en event sker så opretter browseren et event objekt, putter detaljer ind i det og sender det som et argument til handleren.

Her er et eksempel på, hvordan man henter musemarkørens koordinater fra event objektet:

<input type="button" value="Klik mig" id="elem">

<script>
  elem.onclick = function(event) {
    // vis event type, element og koordinaterne for klikket
    alert(event.type + " på " + event.currentTarget);
    alert("Koordinater: " + event.clientX + ":" + event.clientY);
  };
</script>

Et par gængse egenskaber for event objekt er:

event.type
Typen af event. Her er det "click".
event.currentTarget
Elementet som håndterede eventet. Det er præcis det samme som this, medmindre handleren er en arrow function, eller dens this er bundet til noget andet, så kan vi altid få elementet fra event.currentTarget.
event.clientX / event.clientY
Koordinater for musemarkøren (og andre pege-events) relativt til vinduet.

Der er mange andre egenskaber. Mange af dem er afhængige af event typen: keyboard events har ét sæt af egenskaber, pointer events har andre. Vi kigger nærmere på de forskellige egenskaber, når vi undersøger dem enkeltvis.

Event objektet er også tilgængeligt i HTML handlere

Hvis vi tildeler en handler i HTML, kan vi også bruge event objektet sådan her:

<input type="button" onclick="alert(event.type)" value="Event type">

Dette er muligt fordi browseren læser attributten og opretter et handler objekt: function(event) { alert(event.type) }. Det betyder: dets første argument kaldes "event" og indholdet bliver taget fra attributten.

Object handlere: handleEvent

Vi kan tildele andet end funktioner med addEventListener – det kan også være et objekt. Når en event sker, kaldes dens handleEvent metode.

For eksempel, her er et objekt der håndterer en click event:

<button id="elem">Klik mig</button>

<script>
  let obj = {
    handleEvent(event) {
      alert(event.type + " på " + event.currentTarget);
    }
  };

  elem.addEventListener('click', obj);
</script>

Som vi kan se, når addEventListener modtager et objekt som handler, kalder det obj.handleEvent(event) i tilfælde af en event.

Vi kunne også bruge objekter af en brugerdefineret klasse, som dette:

<button id="elem">Klik mig</button>

<script>
  class Menu {
    handleEvent(event) {
      switch(event.type) {
        case 'mousedown':
          elem.innerHTML = "Musen trykkes ned";
          break;
        case 'mouseup':
          elem.innerHTML += "...og slippes.";
          break;
      }
    }
  }

  let menu = new Menu();

  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

Her håndterer det samme objekt begge evens. Bemærk, at vi stadig er nødt til at sætte alle listeners med addEventListener. Objektet menu giver os kun mousedown og mouseup her – ikke enhver type af events.

Metoden handleEvent behøver ikke at gøre alt arbejdet selv. Den kan kalde andre event-specifikke metoder i stedet, som dette:

<button id="elem">Klik mig</button>

<script>
  class Menu {
    handleEvent(event) {
      // mousedown -> onMousedown
      let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
      this[method](event);
    }

    onMousedown() {
      elem.innerHTML = "Musen trykkes ned";
    }

    onMouseup() {
      elem.innerHTML += "...og slippes.";
    }
  }

  let menu = new Menu();
  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

Nu er event handlere klart adskilt på en måde der er nemmere at vedligeholde.

Opsummering

Der er 3 måder at tildele event handlere:

  1. HTML attribut: onclick="...".
  2. DOM egenskab: elem.onclick = function.
  3. Metoderne: elem.addEventListener(event, handler[, phase]) til at tilføje, og removeEventListener til at fjerne.

HTML attributer bruges sjældent fordi JavaScript midt inde i et HTML tag ikke er god skik. Vi kan heller ikke skrive meget kode i forbindelse med hændelsen.

DOM egenskaber er ok at bruge, men vi kan ikke tildele mere end én handler af den pågældende event. I mange tilfælde er denne begrænsning ikke kritisk.

Den sidste måde er den mest fleksible, men den er også den længste at skrive. Der er få events, der kun virker med denne måde, for eksempel transitionend og DOMContentLoaded (forklares i et senere kapitel). addEventListener understøtter også objekter som event handlere. I det tilfælde kaldes metoden handleEvent i tilfælde af eventen.

Lige meget hvordan du tildeler en handler får det et event-objekt som det første argument. Objektet indeholder detaljer om hvad der er sket.

Vi lærer mere om events i det hele tag og om forskellige typer af events i de næste kapitler.

Opgaver

vigtighed: 5

Tilføj JavaScript til button for at gøre <div id="text"> usynlig når vi klikker på den.

Demo:

Åbn en sandbox til opgaven.

vigtighed: 5

Opret en knap, der skjuler sig selv ved klik.

Som dette:

Du kan her bruge this i handleren til at referere til “elementet selv”:

<input type="button" onclick="this.hidden=true" value="Klik for at skjule">
vigtighed: 5

Der er et element i variablen (en knap). Der er ingen handlers på den.

Hvilke handlers afvikles ved klik efter følgende kode? Hvilke alerts vises?

button.addEventListener("click", () => alert("1"));

button.removeEventListener("click", () => alert("1"));

button.onclick = () => alert(2);

Svaret er 1 og 2.

Den første handler afvikles, fordi den ikke er blevet fjernet af removeEventListener. For at fjerne handleren skal vi overføre præcis den funktion, der var tildelt. Og i koden bliver en ny funktion overført, som ser den samme ud, men er stadig en anden funktion.

For at fjerne et funktionsobjekt, skal vi gemme en reference til det, sådan her:

function handler() {
  alert(1);
}

button.addEventListener("click", handler);
button.removeEventListener("click", handler);

Handleren button.onclick virker uafhængigt og i tilføjelse til addEventListener.

vigtighed: 5

Flyt bolden hen over plænen til det sted der klikkes. Som dette:

Krav:

  • Boldens center skal være det sted der klikkes (hvis muligt uden at lande uden for plænen).
  • CSS-animationer er velkomne.
  • Bolden må ikke lande uden for plænen.
  • Når siden scrolles, skal det stadig virke.

Note:

  • Koden skal kunne virke ved forskellige bold- og plæne-størrelser og må ikke være bundet til faste værdier.
  • Brug egenskaberne event.clientX/event.clientY for klik-koordinater.

Åbn en sandbox til opgaven.

Først skal vi vælge en metode til at positionere bolden.

Vi kan ikke bruge position:fixed til det, fordi scrolling af siden ville flytte bolden fra plænen.

Så vi bør bruge position:absolute og, for at gøre positioneringen virkelig solid, give selve field en position.

På den måde vil bolden blive positioneret relativt til plænen:

#field {
  width: 200px;
  height: 150px;
  position: relative;
}

#ball {
  position: absolute;
  left: 0; /* relativt til den nærmeste positionerede forælder (field) */
  top: 0;
  transition: 1s all; /* CSS animation for left/top får bolden til at bevæge sig */
}

Derefter skal vi tildele de korrekte ball.style.left/top. De indeholder nu koordinater relative til plænen.

Her er et billede:

Vi har event.clientX/clientY – koordinater relativt til vinduet.

For at få et field-relativt left koordinat, kan vi trække fields venstre side og border tykkelse fra:

let left = event.clientX - fieldCoords.left - field.clientLeft;

Normalt betyder ball.style.left den “venstre kant af et element” (bolden). Så hvis vi tildeler den left, så vil det være boldens venstre side og ikke center der vil være under musemarkøren.

Vi skal flytte den halve bredde af bolden til venstre og den halve højde op for at centrere den.

Så den endelige left vil være:

let left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2;

Den samme logik bruges til at beregne den lodrette koordinat.

Bemærk, at boldens bredde/højde skal være kendt på det tidspunkt, hvor vi tilgår ball.offsetWidth. Den skal angives i HTML eller CSS.

Åbn løsningen i en sandbox.

vigtighed: 5

Opret en menu, der åbner/skjuler ved klik:

PS. Du skal også ændre HTML/CSS i dokumentet for at løse opgaven.

Åbn en sandbox til opgaven.

HTML/CSS

Lad os først oprette HTML/CSS.

En menu er at selvstændigt element på side, så det bør puttes i sit eget DOM-element.

En liste at menupunkter kan opstilles som en liste ul/li.

Here’s the example structure:

<div class="menu">
  <span class="title">Søde sager (klik her)!</span>
  <ul>
    <li>Kage</li>
    <li>Donut</li>
    <li>Honning</li>
  </ul>
</div>

Vi bruger <span> til titlen fordi <div> har en implicit display:block på det, og det vil optage 100% af den horisontale bredde.

Sådan her:

<div style="border: solid red 1px" onclick="alert(1)">Søde sager (klik her)!</div>

Så hvis vi sætter et onclick på det, så vil det også opfange klik til højre for teksten.

Da <span> har en implicit display: inline, optager det kun den plads, der er nødvendig for at rumme hele teksten:

<span style="border: solid red 1px" onclick="alert(1)">Søde sager (klik her)!</span>

Toggling af menu

Toggling af menu bør ændre pilen og vise/skjule menu-listen.

Alle disse ændringer håndteres perfekt af CSS. I JavaScript skal vi justere den tilstand menuen er i ved at tilføje eller fjerne klassen .open.

Uden den vil menuen være uden den vil menuen være lukket:

.menu ul {
  margin: 0;
  list-style: none;
  padding-left: 20px;
  display: none;
}

.menu .title::before {
  content: '▶ ';
  font-size: 80%;
  color: green;
}

… og med .open vil pilen ændres og listen vises:

.menu.open .title::before {
  content: '▼ ';
}

.menu.open ul {
  display: block;
}

Åbn løsningen i en sandbox.

vigtighed: 5

Der er en liste af beskeder.

Brug nu JavaScript til at tilføje en lukkeknap til højre i hjørnet af hver besked.

Resultatet skal se sådan ud:

Åbn en sandbox til opgaven.

For at tilføje knappen kan vi enten bruge position:absolute (og gøre panelet position:relative) eller float:right. Fordelen ved float:right er at knappen aldrig overlapper teksten, men position:absolute giver mere frihed. Så valget er dit.

Koden for hvert panel kan se således ud:

pane.insertAdjacentHTML("afterbegin", '<button class="remove-button">[x]</button>');

Så bliver <button> elementet pane.firstChild. Det gør det nemt at tildele en handler til det med denne kode:

pane.firstChild.onclick = () => pane.remove();

Åbn løsningen i en sandbox.

vigtighed: 4

Opret en “karrusel” – et bånd af billeder, der kan scrolles ved at klikke på pile.

Senere kan vi tilføje flere funktioner: uendelig scrolling, dynamisk indlæsning osv.

PS. I denne opgave er HTML/CSS-strukturen faktisk 90% af løsningen.

Åbn en sandbox til opgaven.

Billederne i båndet kan repræsenteres som en ul/li liste af billeder <img>.

Normalt vil sådan et bånd være meget bredt, men vi putter en fast størrelse <div> omkring for at “beskære” det, så det kun er en del af båndet der er synligt:

For at vise listen vandret, kan vi anvende CSS-egenskaber for <li>, som f.eks. display: inline-block.

For <img> bør vi også justere display, fordi standard er inline. Der er afsat ekstra plads under inline-elementer til “underliggere” (bogstaver der går under grundlinjen). Med display:block fjerner vi den plads.

Til at udføre scrolling, kan vi flytte <ul>. Der er mange måder at gøre det på. For eksempel kan vi ændre margin-left eller (endnu bedre ift ydeevne) bruge transform: translateX():

Den ydre <div> har en fast bredde, så “ekstra” billeder bliver beskåret.

Hele karrusellen er en selvstændig “grafisk komponent” på siden, så vi bør pakke den ind i en enkelt <div class="carousel"> og style tingene inden for den.

Åbn løsningen i en sandbox.

Tutorial-oversigt

Kommentarer

læs dette før du kommenterer…
  • Hvis du har forslag til forbedringer - så opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
  • Hvis du ikke forstår noget i artiklen - så uddyb venligst.
  • For at indsætte få ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen…)