chore(i18n): sync translations with latest source changes (chunk 9/20, 42 files)

pull/1668/head
localizeflow[bot] 2 weeks ago
parent 132fc2db50
commit 5f05a22a96

@ -1,170 +1,209 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "acad15f3164cd6348e065ff38619aae9",
"translation_date": "2025-10-23T21:58:46+00:00",
"original_hash": "5c383cc2cc23bb164b06417d1c107a44",
"translation_date": "2026-01-07T00:06:45+00:00",
"source_file": "1-getting-started-lessons/2-github-basics/README.md",
"language_code": "da"
}
-->
# Introduktion til GitHub
Hej der, fremtidige udvikler! 👋 Klar til at slutte dig til millioner af kodere verden over? Jeg er virkelig begejstret for at introducere dig til GitHub tænk på det som en social medieplatform for programmører, bortset fra at vi deler kode og skaber fantastiske ting sammen i stedet for at dele billeder af vores frokost!
Hej der, kommende udvikler! 👋 Klar til at slutte dig til millioner af programmører rundt om i verden? Jeg er virkelig begejstret for at introducere dig til GitHub tænk på det som et socialt medie for programmører, bortset fra at i stedet for at dele billeder af din frokost, deler vi kode og bygger utrolige ting sammen!
Her er noget, der virkelig blæser mig omkuld: hver app på din telefon, hver hjemmeside du besøger, og de fleste af de værktøjer, du vil lære at bruge, er bygget af teams af udviklere, der samarbejder på platforme som GitHub. Den musikapp, du elsker? Nogen som dig har bidraget til den. Det spil, du ikke kan lægge fra dig? Jep, sandsynligvis bygget med GitHub-samarbejde. Og nu skal DU lære, hvordan du bliver en del af det fantastiske fællesskab!
Her er hvad der virkelig blæser mig omkuld: hver app på din telefon, hver hjemmeside du besøger, og de fleste af de værktøjer, du vil lære at bruge, er bygget af teams af udviklere, der samarbejder på platforme ligesom GitHub. Den musik-app du elsker? Nogen som dig har bidraget til den. Det spil du ikke kan lægge fra dig? Jep, sandsynligvis bygget med GitHub samarbejde. Og nu SKAL DU til at lære, hvordan du bliver en del af det fantastiske fællesskab!
Jeg ved, det kan føles som meget i starten jeg husker selv, hvordan jeg stirrede på min første GitHub-side og tænkte "Hvad i alverden betyder alt dette?" Men her er sagen: hver eneste udvikler startede præcis der, hvor du er lige nu. Ved slutningen af denne lektion vil du have din helt egen GitHub-repository (tænk på det som din personlige projektudstilling i skyen), og du vil vide, hvordan du gemmer dit arbejde, deler det med andre og endda bidrager til projekter, som millioner af mennesker bruger.
Jeg ved, det måske føles som meget i starten pokkers, jeg husker at stirre på min første GitHub-side med tanken "Hvad pokker betyder det hele?" Men her er sagen: hver eneste udvikler startede præcis hvor du er lige nu. Når denne lektion er slut, vil du have dit helt eget GitHub repository (tænk på det som dit personlige projektudstilling i skyen), og du vil vide, hvordan du gemmer dit arbejde, deler det med andre og endda bidrager til projekter, som millioner af mennesker bruger.
Vi tager denne rejse sammen, skridt for skridt. Ingen hast, ingen pres bare dig, mig og nogle virkelig seje værktøjer, der snart bliver dine nye bedste venner!
Vi tager denne rejse sammen, skridt for skridt. Ingen hast, intet pres bare dig, mig og nogle virkelig fede værktøjer, som snart bliver dine nye bedste venner!
![Intro til GitHub](../../../../translated_images/webdev101-github.8846d7971abef6f947909b4f9d343e2a23778aa716ca6b9d71df7174ee5009ac.da.png)
![Intro to GitHub](../../../../translated_images/webdev101-github.8846d7971abef6f9.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
## Quiz før lektionen
[Quiz før lektionen](https://ff-quizzes.netlify.app)
```mermaid
journey
title Din GitHub-eventyr i dag
section Opsætning
Installér Git: 4: You
Opret Konto: 5: You
Første Repository: 5: You
section Mestre Git
Lokale Ændringer: 4: You
Commits & Pushes: 5: You
Branching: 4: You
section Samarbejd
Fork Projekter: 4: You
Pull Requests: 5: You
Open Source: 5: You
```
## For-forelæsning quiz
[For-forelæsning quiz](https://ff-quizzes.netlify.app)
## Introduktion
Før vi dykker ned i de virkelig spændende ting, skal vi gøre din computer klar til noget GitHub-magi! Tænk på det som at organisere dine kunstforsyninger, før du skaber et mesterværk at have de rigtige værktøjer klar gør alting så meget nemmere og meget sjovere.
Før vi dykker ned i det virkelig spændende, lad os gøre din computer klar til noget GitHub-magisk! Tænk på det som at organisere dine kunstmaterialer inden du går i gang med et mesterværk at have de rigtige værktøjer klar gør alting så meget nemmere og meget sjovere.
Jeg vil personligt guide dig gennem hvert opsætningsskridt, og jeg lover, det er slet ikke så skræmmende, som det måske ser ud ved første øjekast. Hvis noget ikke giver mening med det samme, er det helt normalt! Jeg husker, da jeg satte mit første udviklingsmiljø op og følte, at jeg forsøgte at læse gamle hieroglyffer. Hver eneste udvikler har været præcis der, hvor du er lige nu, og undret sig over, om de gør det rigtigt. Spoiler alert: hvis du er her og lærer, gør du det allerede rigtigt! 🌟
Jeg vil guide dig igennem hvert opsætningsskridt personligt, og jeg lover, det er slet ikke så skræmmende, som det måske ser ud ved første øjekast. Hvis noget ikke klikker med det samme, er det helt normalt! Jeg husker at sætte mit første udviklingsmiljø op og følte, det var som at forsøge at læse gamle hieroglyffer. Hver eneste udvikler har været præcis hvor du er lige nu, og spekuleret på, om de gjorde det rigtigt. Spoiler alert: hvis du er her og lærer, gør du det allerede rigtigt! 🌟
I denne lektion vil vi dække:
- hvordan du sporer det arbejde, du laver på din maskine
- hvordan du arbejder på projekter med andre
- hvordan du bidrager til open source-software
- sporing af det arbejde du laver på din maskine
- arbejde på projekter med andre
- hvordan du bidrager til open source software
### Forudsætninger
Lad os gøre din computer klar til noget GitHub-magi! Bare rolig denne opsætning er noget, du kun behøver at gøre én gang, og så er du klar til hele din koderejse.
Lad os gøre din computer klar til noget GitHub-magisk! Bare rolig denne opsætning skal du kun gøre én gang, og så er du klar til hele din kodningsrejse.
Okay, lad os starte med fundamentet! Først skal vi tjekke, om Git allerede er på din computer. Git er grundlæggende som at have en super-smart assistent, der husker hver eneste ændring, du laver i din kode meget bedre end at trykke Ctrl+S hvert andet sekund (vi har alle været der!).
Okay, lad os starte med fundamentet! Først skal vi tjekke, om Git allerede er installeret på din computer. Git er dybest set som at have en super klog assistent, der husker hver eneste ændring, du laver i din kode meget bedre end at hamre på Ctrl+S hvert andet sekund (vi har alle prøvet det!).
Lad os se, om Git allerede er installeret ved at skrive denne magiske kommando i din terminal:
`git --version`
Hvis Git ikke er der endnu, ingen problemer! Bare gå til [download Git](https://git-scm.com/downloads) og hent det. Når du har installeret det, skal vi introducere Git til dig ordentligt:
Hvis Git ikke er der endnu, ingen bekymringer! Bare gå til [download Git](https://git-scm.com/downloads) og hent det. Når du har installeret det, skal du introducere Git til dig ordentligt:
> 💡 **Første opsætning**: Disse kommandoer fortæller Git, hvem du er. Denne information vil blive knyttet til hver commit, du laver, så vælg et navn og en e-mail, du er komfortabel med at dele offentligt.
> 💡 **Første opsætning**: Disse kommandoer fortæller Git, hvem du er. Denne information bliver knyttet til hver commit, du laver, så vælg et navn og en e-mail, du er komfortabel med at dele offentligt.
```bash
git config --global user.name "your-name"
git config --global user.email "your-email"
```
For at tjekke, om Git allerede er konfigureret, kan du skrive:
For at tjekke om Git allerede er konfigureret kan du skrive:
```bash
git config --list
```
Du skal også bruge en GitHub-konto, en kodeeditor (som Visual Studio Code), og du skal åbne din terminal (eller: kommandoprompt).
Du får også brug for en GitHub-konto, en kodeeditor (f.eks. Visual Studio Code), og du skal åbne din terminal (eller: kommandoprompt).
Naviger til [github.com](https://github.com/) og opret en konto, hvis du ikke allerede har en, eller log ind og udfyld din profil.
til [github.com](https://github.com/) og opret en konto, hvis du ikke allerede har en, eller log ind og udfyld din profil.
💡 **Moderne tip**: Overvej at opsætte [SSH-nøgler](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) eller bruge [GitHub CLI](https://cli.github.com/) for nemmere autentificering uden adgangskoder.
💡 **Moderne tip**: Overvej at opsætte [SSH keys](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) eller bruge [GitHub CLI](https://cli.github.com/) for nemmere autentificering uden adgangskoder.
✅ GitHub er ikke det eneste koderepositorium i verden; der findes andre, men GitHub er det mest kendte.
✅ GitHub er ikke det eneste kode lager i verden; der findes andre, men GitHub er det mest kendte
### Forberedelse
Du skal bruge både en mappe med et kodeprojekt på din lokale maskine (laptop eller PC) og et offentligt repository på GitHub, som vil fungere som et eksempel på, hvordan man bidrager til andres projekter.
Du skal bruge både en mappe med et kodeprojekt på din lokale maskine (bærbar eller PC), og et offentligt repository på GitHub, som vil tjene som et eksempel på, hvordan man bidrager til andres projekter.
### Hold din kode sikker
### Sådan beskytter du din kode
Lad os tale om sikkerhed et øjeblik men bare rolig, vi vil ikke overvælde dig med skræmmende ting! Tænk på disse sikkerhedspraksisser som at låse din bil eller dit hus. De er simple vaner, der bliver en selvfølge og holder dit hårde arbejde beskyttet.
Lad os tale lidt om sikkerhed men bare rolig, vi vil ikke overvælde dig med skræmmende ting! Tænk på disse sikkerhedspraksisser som at låse din bil eller dit hus. Det er simple vaner, der bliver naturlige og beskytter dit hårde arbejde.
Vi vil vise dig de moderne, sikre måder at arbejde med GitHub lige fra starten. På den måde vil du udvikle gode vaner, der vil tjene dig godt gennem hele din kodningskarriere.
Vi viser dig moderne, sikre måder at arbejde med GitHub på helt fra starten. På den måde udvikler du gode vaner, som vil tjene dig godt i hele din kodningskarriere.
Når du arbejder med GitHub, er det vigtigt at følge sikkerhedsbedste praksis:
Når du arbejder med GitHub, er det vigtigt at følge bedste sikkerhedspraksis:
| Sikkerhedsområde | Bedste praksis | Hvorfor det er vigtigt |
|------------------|----------------|-------------------------|
| **Autentificering** | Brug SSH-nøgler eller personlige adgangstokens | Adgangskoder er mindre sikre og udfases |
| **To-faktor autentificering** | Aktiver 2FA på din GitHub-konto | Tilføjer et ekstra lag af kontobeskyttelse |
| **Repository-sikkerhed** | Aldrig commit følsomme oplysninger | API-nøgler og adgangskoder bør aldrig være i offentlige repos |
| **Afhængighedsstyring** | Aktiver Dependabot for opdateringer | Holder dine afhængigheder sikre og opdaterede |
|------------------|----------------|-----------------------|
| **Autentificering** | Brug SSH keys eller Personlige adgangstokens | Adgangskoder er mindre sikre og på vej ud |
| **To-faktor-autentificering** | Aktivér 2FA på din GitHub-konto | Tilføjer et ekstra lag af beskyttelse for kontoen |
| **Repository sikkerhed** | Committ aldrig følsomme oplysninger | API-nøgler og adgangskoder må aldrig være i offentlige repos |
| **Afhængighedsstyring** | Aktivér Dependabot til opdateringer | Holder dine afhængigheder sikre og opdaterede |
> ⚠️ **Kritisk sikkerhedspåmindelse**: Aldrig commit API-nøgler, adgangskoder eller andre følsomme oplysninger til noget repository. Brug miljøvariabler og `.gitignore`-filer til at beskytte følsomme data.
> ⚠️ **Kritisk sikkerhedsadvarsel**: Committ aldrig API-nøgler, adgangskoder eller andre følsomme oplysninger til nogen repository. Brug miljøvariabler og `.gitignore` filer til at beskytte følsomme data.
**Moderne autentificeringsopsætning:**
```bash
# Generate SSH key (modern ed25519 algorithm)
# Generer SSH-nøgle (moderne ed25519-algoritme)
ssh-keygen -t ed25519 -C "your_email@example.com"
# Set up Git to use SSH
# Konfigurer Git til at bruge SSH
git remote set-url origin git@github.com:username/repository.git
```
> 💡 **Pro Tip**: SSH-nøgler eliminerer behovet for gentagne adgangskoder og er mere sikre end traditionelle autentificeringsmetoder.
> 💡 **Pro-tip**: SSH keys eliminerer behovet for gentagne adgangskodeindtastninger og er mere sikre end traditionelle autentificeringsmetoder.
---
## Administrer din kode som en professionel
Okay, NU bliver det virkelig spændende! 🎉 Vi skal lære, hvordan man sporer og administrerer sin kode som de professionelle, og ærligt talt er dette en af mine yndlingsting at undervise i, fordi det er en total game-changer.
Forestil dig dette: du skriver en fantastisk historie, og du vil holde styr på hver udkast, hver genial redigering og hver "vent, det er genialt!"-øjeblik undervejs. Det er præcis, hvad Git gør for din kode! Det er som at have den mest utrolige tidsrejsende notesbog, der husker ALT hver tastetryk, hver ændring, hver "ups, det ødelagde alt"-øjeblik, som du straks kan fortryde.
Jeg skal være ærlig det kan føles overvældende i starten. Da jeg begyndte, tænkte jeg "Hvorfor kan jeg ikke bare gemme mine filer som normalt?" Men stol på mig: når Git klikker for dig (og det vil det!), vil du få en af de der lyspære-øjeblikke, hvor du tænker "Hvordan har jeg NOGENSINDE kodet uden dette?" Det er som at opdage, at du kan flyve, når du har gået overalt hele dit liv!
Lad os sige, at du har en mappe lokalt med et kodeprojekt, og du vil begynde at spore din fremgang ved hjælp af git versionskontrolsystemet. Nogle mennesker sammenligner brugen af git med at skrive et kærlighedsbrev til dit fremtidige jeg. Når du læser dine commit-beskeder dage, uger eller måneder senere, vil du kunne huske, hvorfor du tog en beslutning, eller "rulle tilbage" en ændring altså, når du skriver gode "commit-beskeder".
Okay, HER er det, det virkelig bliver spændende! 🎉 Vi skal til at lære, hvordan du sporer og administrerer din kode som profferne, og ærligt talt, det her er en af mine yndlingsting at undervise i, fordi det ændrer virkelig spillet.
Forestil dig det: du skriver en fantastisk historie, og du vil holde styr på hvert udkast, hver genial redigering og hvert "vent, det er genialt!" øjeblik undervejs. Det er præcis, hvad Git gør for din kode! Det er som at have den mest utrolige tidsrejsende notesbog, der husker ALT hver tastaturtryk, hver ændring, hvert "oops, det brød alt" øjeblik, som du øjeblikkeligt kan fortryde.
Jeg vil være ærlig det kan føles overvældende i starten. Da jeg begyndte, tænkte jeg "Hvorfor kan jeg ikke bare gemme mine filer som normalt?" Men tro mig på dette: når Git klikker for dig (og det vil det!), vil du få et af de der lyskugle-øjeblikke, hvor du tænker "Hvordan kunne jeg NOGENSINDE kode uden dette?" Det er som at opdage, du kan flyve, når du har gået alle steder hele dit liv!
Lad os sige, du har en mappe lokalt med et kodeprojekt, og du vil begynde at spore din udvikling med git - versionsstyringssystemet. Nogle sammenligner at bruge git med at skrive et kærlighedsbrev til dit fremtidige jeg. Når du læser dine commit-beskeder dage, uger eller måneder senere, kan du huske, hvorfor du tog en beslutning eller "rulle tilbage" en ændring det kræver selvfølgelig, at du skriver gode "commit-beskeder".
```mermaid
flowchart TD
A[📁 Dine Projektfiler] --> B{Er det et Git-Repository?}
B -->|Nej| C[git init]
B -->|Ja| D[Foretag Ændringer]
C --> D
D --> E[git add .]
E --> F["git commit -m 'besked'"]
F --> G[git push]
G --> H[🌟 Kode på GitHub!]
H --> I{Vil du samarbejde?}
I -->|Ja| J[Forgrene & Klon]
I -->|Nej| D
J --> K[Opret Gren]
K --> L[Foretag Ændringer]
L --> M[Pull Anmodning]
M --> N[🎉 Bidrager!]
style A fill:#fff59d
style H fill:#c8e6c9
style N fill:#ff4081,color:#fff
```
### Opgave: Opret dit første repository!
> 🎯 **Din mission (og jeg er så begejstret for dig!)**: Vi skal sammen oprette dit allerførste GitHub-repository! Når vi er færdige her, vil du have din egen lille hjørne af internettet, hvor din kode bor, og du vil have lavet din første "commit" (det er udvikler-sprog for at gemme dit arbejde på en virkelig smart måde).
> 🎯 **Din mission (og jeg er så glad på dine vegne!)**: Vi skal sammen oprette dit allerførste GitHub-repository! Når vi er færdige her, vil du have dit eget lille hjørne af internettet, hvor din kode bor, og du vil have lavet din første "commit" (det er udviklersprog for at gemme dit arbejde på en virkelig smart måde).
>
> Dette er ærligt talt et helt specielt øjeblik du er ved officielt at slutte dig til det globale fællesskab af udviklere! Jeg husker stadig spændingen ved at oprette mit første repo og tænke "Wow, jeg gør virkelig dette!"
> Det her er virkelig et særligt øjeblik du er ved at officielt blive en del af det globale udviklerfællesskab! Jeg husker stadig spændingen ved at oprette mit første repo og tænke "Wow, jeg gør det virkelig!"
Lad os gå igennem dette eventyr sammen, skridt for skridt. Tag dig god tid med hver del der er ingen præmie for at skynde sig, og jeg lover, at hvert eneste skridt vil give mening. Husk, hver kodningsstjerne, du beundrer, har engang siddet præcis der, hvor du er, klar til at oprette deres første repository. Hvor sejt er det?
Lad os tage denne rejse sammen, trin for trin. Tag dig god tid med hvert trin der er ingen præmie for at skynde sig, og jeg lover, at hvert enkelt trin vil give mening. Husk, at hver eneste kodestjerne, du beundrer, engang sad præcis hvor du sidder nu, klar til at oprette deres første repository. Hvor fedt er det?
> Se video
>
> [![Git og GitHub grundlæggende video](https://img.youtube.com/vi/9R31OUPpxU4/0.jpg)](https://www.youtube.com/watch?v=9R31OUPpxU4)
> [![Git og GitHub basics video](https://img.youtube.com/vi/9R31OUPpxU4/0.jpg)](https://www.youtube.com/watch?v=9R31OUPpxU4)
**Lad os gøre det sammen:**
1. **Opret dit repository på GitHub**. Gå til GitHub.com og kig efter den lysegrønne **Ny**-knap (eller **+**-tegnet i øverste højre hjørne). Klik på den og vælg **Nyt repository**.
1. **Opret dit repository på GitHub**. Gå til GitHub.com og se efter den lysegrønne **New** knap (eller **+** ikonet øverst til højre). Klik på den og vælg **New repository**.
Her er, hvad du skal gøre:
1. Giv dit repository et navn gør det til noget, der betyder noget for dig!
Her er hvad du skal gøre:
1. Giv dit repository et navn vælg noget meningsfuldt for dig!
1. Tilføj en beskrivelse, hvis du vil (det hjælper andre med at forstå, hvad dit projekt handler om)
1. Beslut, om du vil have det offentligt (alle kan se det) eller privat (kun for dig)
1. Jeg anbefaler at markere boksen for at tilføje en README-fil det er som forsiden af dit projekt
1. Klik på **Opret repository** og fejre du har lige oprettet dit første repo! 🎉
1. Beslut om det skal være offentligt (alle kan se det) eller privat (kun for dig)
1. Jeg anbefaler at du sætter kryds i boksen for at tilføje en README-fil det er som forsiden af dit projekt
1. Klik **Create repository** og fejre du har lige oprettet dit første repo! 🎉
2. **Naviger til din projektmappe**. Nu skal vi åbne din terminal (bare rolig, det ser ikke så skræmmende ud!). Vi skal fortælle din computer, hvor dine projektfiler er. Skriv denne kommando:
2. **Naviger til din projektmappe**. Lad os nu åbne din terminal (bare rolig, det er ikke så skræmmende som det ser ud!). Vi skal fortælle din computer, hvor dine projektfiler er. Skriv denne kommando:
```bash
cd [name of your folder]
```
**Hvad vi gør her:**
- Vi siger grundlæggende "Hej computer, tag mig til min projektmappe"
- Det er som at åbne en specifik mappe på dit skrivebord, men vi gør det med tekstkommandoer
- Erstat `[navn på din mappe]` med det faktiske navn på din projektmappe
- Vi siger dybest set "Hej computer, tag mig til min projektmappe"
- Det er som at åbne en bestemt mappe på dit skrivebord, men vi gør det med tekstkommandoer
- Erstat `[name of your folder]` med det faktiske navn på din projektmappe
3. **Gør din mappe til et Git repository**. Her sker magien! Skriv:
3. **Gør din mappe til et Git repository**. Det er her magien sker! Skriv:
```bash
git init
```
**Her er, hvad der lige skete (ret sejt!):**
- Git har lige oprettet en skjult `.git`-mappe i dit projekt du kan ikke se den, men den er der!
**Her er hvad der lige skete (ret fedt!):**
- Git har lige oprettet en skjult `.git` mappe i dit projekt du kan ikke se den, men den er der!
- Din almindelige mappe er nu et "repository", der kan spore hver ændring, du laver
- Tænk på det som at give din mappe superkræfter til at huske alt
4. **Tjek, hvad der sker**. Lad os se, hvad Git synes om dit projekt lige nu:
4. **Se hvad der sker**. Lad os se, hvad Git tænker om dit projekt lige nu:
```bash
git status
```
**Forstå, hvad Git fortæller dig:**
**Forstå hvad Git fortæller dig:**
Du kan se noget, der ser sådan her ud:
Du kan måske se noget, der ligner dette:
```output
Changes not staged for commit:
@ -175,46 +214,46 @@ Lad os gå igennem dette eventyr sammen, skridt for skridt. Tag dig god tid med
modified: file2.txt
```
**Ingen panik! Her er, hvad det betyder:**
**Bare rolig! Her er hvad det betyder:**
- Filer i **rødt** er filer, der har ændringer, men ikke er klar til at blive gemt endnu
- Filer i **grønt** (når du ser dem) er klar til at blive gemt
- Git er hjælpsom ved at fortælle dig præcis, hvad du kan gøre næste gang
- Git er hjælpsom ved at fortælle dig præcis, hvad du kan gøre næste
> 💡 **Pro tip**: Kommandoen `git status` er din bedste ven! Brug den, når du er forvirret over, hvad der foregår. Det er som at spørge Git "Hej, hvad er situationen lige nu?"
> 💡 **Pro-tip**: `git status` kommandoen er din bedste ven! Brug den når som helst du er forvirret over, hvad der foregår. Det er som at spørge Git "Hej, hvad sker der lige nu?"
5. **Gør dine filer klar til at gemme** (det kaldes "staging"):
5. **Gør dine filer klar til at blive gemt** (det kaldes "staging"):
```bash
git add .
```
**Hvad vi lige gjorde:**
- Vi fortalte Git "Hej, jeg vil inkludere ALLE mine filer i den næste gemning"
- `.` er som at sige "alt i denne mappe"
- Nu er dine filer "staged" og klar til næste trin
- Vi sagde til Git "Hey, jeg vil inkludere ALLE mine filer i næste gemning"
- `.` betyder "alt i denne mappe"
- Nu er dine filer "staged" og klar til næste skridt
**Vil du være mere selektiv?** Du kan tilføje kun specifikke filer:
**Vil du være mere selektiv?** Du kan tilføje kun udvalgte filer:
```bash
git add [file or folder name]
```
**Hvorfor vil du måske gøre dette?**
**Hvorfor vil du gøre det?**
- Nogle gange vil du gemme relaterede ændringer sammen
- Det hjælper dig med at organisere dit arbejde i logiske dele
- Gør det lettere at forstå, hvad der ændrede sig og hvornår
- Det hjælper dig med at organisere dit arbejde i logiske bidder
- Gør det nemmere at forstå, hvad der ændrede sig og hvornår
**Har du ombestemt dig?** Ingen problemer! Du kan fjerne filer fra staging som dette:
**Skiftede du mening?** Bare rolig! Du kan fjerne filer fra staging sådan her:
```bash
# Unstage everything
# Fjern alt fra staging-området
git reset
# Unstage just one file
# Fjern kun én fil fra staging-området
git reset [file name]
```
Bare rolig dette sletter ikke dit arbejde, det fjerner bare filer fra "klar til at gemme"-bunken.
Bare rolig det sletter ikke dit arbejde, det tager bare filerne ud af "klar til at gemme" bunken.
6. **Gem dit arbejde permanent** (lav din første commit!):
@ -224,30 +263,30 @@ Lad os gå igennem dette eventyr sammen, skridt for skridt. Tag dig god tid med
**🎉 Tillykke! Du har lige lavet din første commit!**
**Her er, hvad der lige skete:**
**Her er hvad der lige skete:**
- Git tog et "snapshot" af alle dine staged filer på dette præcise tidspunkt
- Din commit-besked "første commit" forklarer, hvad dette gemmepunkt handler om
- Git gav dette snapshot en unik ID, så du altid kan finde det senere
- Du er officielt begyndt at spore din projekthistorik!
- Din commit-besked "first commit" forklarer, hvad dette gemmepunkt går ud på
- Git gav dette snapshot en unik ID, så du altid kan finde det igen
- Du er officielt begyndt at spore dit projekts historie!
> 💡 **Fremtidige commit-beskeder**: For dine næste commits, vær mere beskrivende! I stedet for "opdateret ting", prøv "Tilføj kontaktformular til hjemmeside" eller "Fix fejl i navigationsmenu". Dit fremtidige jeg vil takke dig!
> 💡 **Fremtidige commit-beskeder**: Vær mere beskrivende næste gang! I stedet for "updated stuff" prøv "Tilføj kontaktformular på forsiden" eller "Fix navigation menu bug". Dit fremtidige jeg vil takke dig!
7. **Forbind dit lokale projekt til GitHub**. Lige nu eksisterer dit projekt kun på din computer. Lad os forbinde det til dit GitHub-repository, så du kan dele det med verden!
7. **Forbind dit lokale projekt til GitHub**. Lige nu findes dit projekt kun på din computer. Lad os forbinde det til dit GitHub-repository, så du kan dele det med verden!
Først, gå til din GitHub repository-side og kopier URL'en. Gå derefter tilbage her og skriv:
Gå først til din GitHub-repository side og kopier URLen. Kom så tilbage her og skriv:
```bash
git remote add origin https://github.com/username/repository_name.git
```
(Erstat den URL med din faktiske repository-URL!)
(Erstat den URL med den faktiske URL til dit repository!)
**Hvad vi lige gjorde:**
- Vi har oprettet en forbindelse mellem dit lokale projekt og din GitHub-repository
- "Origin" er bare et kælenavn for din GitHub-repository det er som at tilføje en kontakt til din telefon
- Vi har oprettet en forbindelse mellem dit lokale projekt og dit GitHub-repositorium
- "Origin" er bare et kælenavn for dit GitHub-repositorium det er som at tilføje en kontakt til din telefon
- Nu ved din lokale Git, hvor den skal sende din kode, når du er klar til at dele den
💡 **Nem løsning**: Hvis du har GitHub CLI installeret, kan du gøre dette med én kommando:
💡 **Nemmere måde**: Hvis du har GitHub CLI installeret, kan du gøre det med én kommando:
```bash
gh repo create my-repo --public --push --source=.
```
@ -258,19 +297,19 @@ Lad os gå igennem dette eventyr sammen, skridt for skridt. Tag dig god tid med
git push -u origin main
```
**🚀 Det er nu! Du uploader din kode til GitHub!**
**🚀 Det er det! Du uploader din kode til GitHub!**
**Hvad der sker:**
**Hvad sker der:**
- Dine commits rejser fra din computer til GitHub
- `-u` flaget opretter en permanent forbindelse, så fremtidige pushes bliver nemmere
- "main" er navnet på din primære branch (som hovedmappen)
- "main" er navnet på din primære branch (ligesom hovedmappen)
- Efter dette kan du bare skrive `git push` for fremtidige uploads!
💡 **Hurtig note**: Hvis din branch hedder noget andet (som "master"), skal du bruge det navn. Du kan tjekke med `git branch --show-current`.
💡 **Hurtig note**: Hvis din branch hedder noget andet (som "master"), brug det navn i stedet. Du kan tjekke med `git branch --show-current`.
9. **Din nye daglige kodningsrytme** (her bliver det vanedannende!):
9. **Din nye daglige kode-rytme** (her bliver det vanedannende!):
Fra nu af, hver gang du laver ændringer i dit projekt, har du denne enkle tre-trins dans:
Fra nu af, hver gang du laver ændringer i dit projekt, har du denne simple tre-trins dans:
```bash
git add .
@ -278,44 +317,69 @@ Lad os gå igennem dette eventyr sammen, skridt for skridt. Tag dig god tid med
git push
```
**Dette bliver din kodningspuls:**
- Lav nogle fantastiske ændringer i din kode ✨
**Dette bliver din kodehjerterytme:**
- Lav nogle fantastiske ændringer til din kode ✨
- Stage dem med `git add` ("Hey Git, læg mærke til disse ændringer!")
- Gem dem med `git commit` og en beskrivende besked (fremtidige dig vil takke dig!)
- Del dem med verden ved hjælp af `git push` 🚀
- Gentag seriøst, det bliver lige så naturligt som at trække vejret!
Jeg elsker denne arbejdsgang, fordi det er som at have flere gemmepunkter i et videospil. Lavede du en ændring, du elsker? Commit den! Vil du prøve noget risikabelt? Intet problem du kan altid gå tilbage til din sidste commit, hvis tingene går galt!
> 💡 **Tip**: Du vil måske også bruge en `.gitignore`-fil til at forhindre filer, du ikke vil tracke, i at dukke op på GitHub - som den notesfil, du gemmer i samme mappe, men som ikke hører hjemme i en offentlig repository. Du kan finde skabeloner til `.gitignore`-filer på [.gitignore templates](https://github.com/github/gitignore) eller oprette en ved hjælp af [gitignore.io](https://www.toptal.com/developers/gitignore).
- Gem dem med `git commit` og en beskrivende besked (fremtidige dig takker dig!)
- Del dem med verden med `git push` 🚀
- Gentag seriøst, det bliver ligeså naturligt som at trække vejret!
Jeg elsker denne arbejdsgang, fordi det er som at have flere gemmepunkter i et videospil. Lavede du en ændring, du elsker? Commit den! Vil du prøve noget risikabelt? Intet problem du kan altid gå tilbage til din sidste commit, hvis ting går galt!
> 💡 **Tip**: Du vil måske også bruge en `.gitignore` fil for at forhindre, at filer du ikke ønsker at tracke, dukker op på GitHub som den noter-fil, du gemmer i samme mappe, men som ikke hører hjemme i et offentligt repo. Du kan finde templates til `.gitignore` filer på [.gitignore templates](https://github.com/github/gitignore) eller lave en via [gitignore.io](https://www.toptal.com/developers/gitignore).
### 🧠 **Første Repository Check-in: Hvordan Føltes Det?**
**Tag et øjeblik til at fejre og reflektere:**
- Hvordan føltes det at se din kode dukke op på GitHub for første gang?
- Hvilket trin føltes mest forvirrende, og hvilket føltes overraskende nemt?
- Kan du forklare forskellen mellem `git add`, `git commit` og `git push` med dine egne ord?
```mermaid
stateDiagram-v2
[*] --> LocalFiles: Opret projekt
LocalFiles --> Staged: git add .
Staged --> Committed: git commit
Committed --> GitHub: git push
GitHub --> [*]: Succes! 🎉
note right of Staged
Filer klar til at gemme
end note
note right of Committed
Snapshot oprettet
end note
```
> **Husk**: Selv erfarne udviklere glemmer somme tider præcist kommandoerne. At denne arbejdsgang bliver til muskler kræver øvelse du gør det godt!
#### Moderne Git-arbejdsgange
#### Moderne Git arbejdsgange
Overvej at tage disse moderne praksisser i brug:
Overvej at bruge disse moderne praksisser:
- **Conventional Commits**: Brug et standardiseret format til commit-beskeder som `feat:`, `fix:`, `docs:` osv. Læs mere på [conventionalcommits.org](https://www.conventionalcommits.org/)
- **Atomiske commits**: Sørg for, at hver commit repræsenterer én logisk ændring
- **Konventionelle Commit-beskeder**: Brug et standardiseret format som `feat:`, `fix:`, `docs:`, osv. Læs mere på [conventionalcommits.org](https://www.conventionalcommits.org/)
- **Atome commits**: Hver commit repræsenterer en enkelt logisk ændring
- **Hyppige commits**: Commit ofte med beskrivende beskeder i stedet for store, sjældne commits
#### Commit-beskeder
En god Git commit-emnelinje fuldender følgende sætning:
Hvis anvendt, vil denne commit <din emnelinje her>
En god Git commit-overskrift færdiggør denne sætning:
Hvis anvendt, vil denne commit <din overskrift her>
Brug imperativ nutid: "ændre" i stedet for "ændrede" eller "ændringer".
Som i emnet, brug også imperativ nutid i brødteksten (valgfrit). Brødteksten bør inkludere motivationen for ændringen og kontrastere dette med tidligere adfærd. Du forklarer `hvorfor`, ikke `hvordan`.
Brug imperativ, nutid: "ændre" ikke "ændret" eller "ændringer".
Som i overskriften, brug også i brødteksten (valgfri) imperativ nutid. Brødteksten skal inkludere motivationen for ændringen og kontrastere med tidligere opførsel. Du forklarer `hvorfor`, ikke `hvordan`.
✅ Brug et par minutter på at surfe rundt på GitHub. Kan du finde en virkelig god commit-besked? Kan du finde en meget minimal en? Hvilke oplysninger synes du er de vigtigste og mest nyttige at formidle i en commit-besked?
Tag et par minutter til at surfe rundt på GitHub. Kan du finde en virkelig god commit-besked? Kan du finde en virkelig minimal én? Hvilken information synes du er mest vigtig og nyttig at formidle i en commit-besked?
## Arbejde med andre (Den sjove del!)
## Arbejde med Andre (Den Sjove Del!)
Hold på hat og briller, for HER bliver GitHub helt magisk! 🪄 Du har mestret at administrere din egen kode, men nu dykker vi ned i min absolutte yndlingsdel samarbejde med fantastiske mennesker fra hele verden.
Hold fast i din hat, for HER bliver GitHub rent magisk! 🪄 Du har mestret at styre din egen kode, men nu dykker vi ned i min absolut yndlingsdel samarbejde med fantastiske folk fra hele verden.
Forestil dig dette: Du vågner i morgen og ser, at nogen i Tokyo har forbedret din kode, mens du sov. Så fikser nogen i Berlin en fejl, du har været fastlåst på. Om eftermiddagen har en udvikler i São Paulo tilføjet en funktion, du aldrig engang havde tænkt på. Det er ikke science fiction det er bare tirsdag i GitHub-universet!
Forestil dig dette: Du vågner op i morgen og ser, at nogen i Tokyo forbedrede din kode, mens du sov. Så retter nogen i Berlin en fejl, du har siddet fast i. Om eftermiddagen har en udvikler i São Paulo tilføjet en funktion, du aldrig engang havde tænkt på. Det er ikke science fiction det er bare en tirsdag i GitHub-universet!
Det, der virkelig begejstrer mig, er, at de samarbejdsevner, du er ved at lære? Det er PRÆCIS de samme arbejdsgange, som teams hos Google, Microsoft og dine yndlingsstartups bruger hver eneste dag. Du lærer ikke bare et sejt værktøj du lærer det hemmelige sprog, der får hele softwareverdenen til at arbejde sammen.
Det, der virkelig begejstrer mig, er at de samarbejdsevner, du er ved at lære? Det er præcis de samme arbejdsgange, som teams hos Google, Microsoft og dine yndlings-startups bruger hver dag. Du lærer ikke bare et sejt værktøj du lærer det hemmelige sprog, der får hele softwareverdenen til at fungere sammen.
Seriøst, når du oplever rusen ved at få nogen til at merge din første pull request, vil du forstå, hvorfor udviklere bliver så passionerede omkring open source. Det er som at være en del af verdens største, mest kreative teamprojekt!
Seriøst, når du oplever suset ved at få nogen til at merge din første pull request, forstår du, hvorfor udviklere bliver så passionerede omkring open source. Det er som at være en del af verdens største, mest kreative teamprojekt!
> Se video
>
@ -323,56 +387,77 @@ Seriøst, når du oplever rusen ved at få nogen til at merge din første pull r
Hovedårsagen til at lægge ting på GitHub var at gøre det muligt at samarbejde med andre udviklere.
I din repository, naviger til `Insights > Community` for at se, hvordan dit projekt sammenlignes med anbefalede fællesskabsstandarder.
```mermaid
flowchart LR
A[🔍 Find Projekt] --> B[🍴 Fork Lager]
B --> C[📥 Klon til Lokal]
C --> D[🌿 Opret Gren]
D --> E[✏️ Lav Ændringer]
E --> F[💾 Commit Ændringer]
F --> G[📤 Push Gren]
G --> H[🔄 Opret Pull Request]
H --> I{Vedligeholder Gennemgang}
I -->|✅ Godkendt| J[🎉 Merge!]
I -->|❓ Ændringer Anmodet| K[📝 Lav Opdateringer]
K --> F
J --> L[🧹 Ryd Op i Grener]
style A fill:#e3f2fd
style J fill:#e8f5e8
style L fill:#fff3e0
```
I dit repository, gå til `Insights > Community` for at se, hvordan dit projekt sammenlignes med anbefalede community-standarder.
Vil du gøre din repository professionel og indbydende? Gå til din repository og klik på `Insights > Community`. Denne smarte funktion viser dig, hvordan dit projekt sammenlignes med, hvad GitHub-fællesskabet anser for "gode repository-praksisser."
Vil du gøre dit repository professionelt og indbydende? Gå til dit repository og klik på `Insights > Community`. Denne seje funktion viser dig, hvordan dit projekt sammenlignes med, hvad GitHub-fællesskabet anser for "gode repository-praksisser."
> 🎯 **Få dit projekt til at skinne**: En velorganiseret repository med god dokumentation er som at have en ren, indbydende butik. Det viser folk, at du går op i dit arbejde og får andre til at ville bidrage!
> 🎯 **Få dit projekt til at skinne**: Et godt organiseret repository med god dokumentation er som en ren, indbydende butiksvindue. Det fortæller folk, at du tager dit arbejde seriøst og får andre til at ville bidrage!
**Her er, hvad der gør en repository fantastisk:**
**Her er hvad der gør et repository fantastisk:**
| Hvad skal tilføjes | Hvorfor det er vigtigt | Hvad det gør for dig |
|--------------------|------------------------|----------------------|
| **Beskrivelse** | Førstehåndsindtryk betyder noget! | Folk ved straks, hvad dit projekt gør |
| Hvad der skal tilføjes | Hvorfor det er vigtigt | Hvad det gør for dig |
|------------------------|------------------------|---------------------|
| **Beskrivelse** | Det første indtryk tæller! | Folk ved med det samme, hvad dit projekt gør |
| **README** | Dit projekts forside | Som en venlig guide for nye besøgende |
| **Retningslinjer for bidrag** | Viser, at du byder hjælp velkommen | Folk ved præcis, hvordan de kan hjælpe dig |
| **Bidragsretningslinjer** | Viser at du byder hjælp velkommen | Folk ved præcis, hvordan de kan hjælpe dig |
| **Adfærdskodeks** | Skaber et venligt miljø | Alle føler sig velkomne til at deltage |
| **Licens** | Juridisk klarhed | Andre ved, hvordan de kan bruge din kode |
| **Sikkerhedspolitik** | Viser, at du er ansvarlig | Demonstrerer professionelle praksisser |
| **Licens** | Juridisk klarhed | Andre ved, hvordan de bruge din kode |
| **Sikkerhedspolitik** | Viser, at du tager ansvar | Demonstrerer professionelle praksisser |
> 💡 **Pro Tip**: GitHub tilbyder skabeloner til alle disse filer. Når du opretter en ny repository, skal du markere felterne for automatisk at generere disse filer.
> 💡 **Pro Tip**: GitHub tilbyder templates til alle disse filer. Når du opretter et nyt repository, kan du flueben i for automatisk at generere disse filer.
**Moderne GitHub-funktioner at udforske:**
🤖 **Automatisering & CI/CD:**
🤖 **Automation & CI/CD:**
- **GitHub Actions** til automatiseret test og deployment
- **Dependabot** til automatiske opdateringer af afhængigheder
💬 **Fællesskab & projektstyring:**
- **GitHub Discussions** til fællesskabssamtaler ud over issues
💬 **Community & Projektstyring:**
- **GitHub Discussions** til samtaler i fællesskabet ud over issues
- **GitHub Projects** til kanban-stil projektstyring
- **Branch-beskyttelsesregler** for at sikre kodekvalitetsstandarder
- **Branch-beskyttelsesregler** for at håndhæve kodekvalitetsstandarder
Alle disse ressourcer vil være til gavn for onboarding af nye teammedlemmer. Og det er typisk den slags ting, nye bidragydere kigger på, før de overhovedet ser på din kode, for at finde ud af, om dit projekt er det rette sted for dem at bruge deres tid.
✅ README-filer, selvom de tager tid at forberede, bliver ofte overset af travle vedligeholdere. Kan du finde et eksempel på en særlig beskrivende en? Bemærk: Der findes nogle [værktøjer til at hjælpe med at lave gode README'er](https://www.makeareadme.com/), som du måske vil prøve.
Alle disse ressourcer hjælper med onboarding af nye teammedlemmer. Og det er typisk det, nye bidragsydere kigger på, før de kigger på din kode, for at finde ud af, om dit projekt er det rette sted for dem at bruge deres tid.
✅ README-filer, selvom de tager tid at forberede, bliver ofte negligeret af travle vedligeholdere. Kan du finde et eksempel på en særlig beskrivende en? Bemærk: der findes nogle [værktøjer til at hjælpe med at skabe gode README-filer](https://www.makeareadme.com/), som du måske vil prøve.
### Opgave: Merge noget kode
Bidragsdokumenter hjælper folk med at bidrage til projektet. Det forklarer, hvilke typer bidrag du leder efter, og hvordan processen fungerer. Bidragydere skal gennem en række trin for at kunne bidrage til din repo på GitHub:
Bidragsdokumenter hjælper folk med at bidrage til projektet. Det forklarer, hvilke slags bidrag du leder efter, og hvordan processen fungerer. Bidragsydere skal igennem en række trin for at kunne bidrage til dit repo på GitHub:
1. **Fork din repo**. Du vil sandsynligvis gerne have, at folk _forker_ dit projekt. Forking betyder at oprette en kopi af din repository på deres GitHub-profil.
1. **Clone**. Derfra vil de klone projektet til deres lokale maskine.
1. **Opret en branch**. Du vil gerne bede dem om at oprette en _branch_ til deres arbejde.
1. **Fokuser deres ændring på ét område**. Bed bidragydere om at koncentrere deres bidrag om én ting ad gangen - på den måde er chancerne for, at du kan _merge_ deres arbejde, højere. Forestil dig, at de skriver en fejlrettelse, tilføjer en ny funktion og opdaterer flere tests - hvad hvis du vil, eller kun kan implementere 2 ud af 3, eller 1 ud af 3 ændringer?
1. **Fork dit repo** Du vil nok gerne have, at folk _forker_ dit projekt. At forke betyder at lave en kopi af dit repository på deres egen GitHub-profil.
1. **Clone** Derfra kloner de projektet til deres lokale maskine.
1. **Opret en branch** Du vil bede dem om at oprette en _branch_ til deres arbejde.
1. **Fokusér ændringen på ét område** Bed bidragsydere om at koncentrere deres bidrag om ét ting ad gangen på den måde er chancerne for, at du kan _merge_ deres arbejde, højere. Forestil dig, at de laver en fejlrettelse, tilføjer en ny funktion og opdaterer flere tests hvad hvis du vil, eller kun kan implementere 2 ud af 3, eller 1 ud af 3 ændringer?
✅ Forestil dig en situation, hvor branches er særligt kritiske for at skrive og levere god kode. Hvilke anvendelsesscenarier kan du komme i tanke om?
✅ Forestil dig en situation, hvor branches er særligt afgørende for at skrive og levere god kode. Hvilke brugsscenarier kan du komme i tanke om?
> Bemærk, vær den forandring, du ønsker at se i verden, og opret branches til dit eget arbejde også. Enhver commit, du laver, vil blive lavet på den branch, du i øjeblikket er "checked out" til. Brug `git status` for at se, hvilken branch det er.
> Bemærk, vær den forandring du ønsker at se i verden, og lav også branches til dit eget arbejde. Alle commits du laver, bliver lavet på den branch, du i øjeblikket er “tjekket ud” til. Brug `git status` for at se, hvilken branch det er.
Lad os gennemgå en bidragsarbejdsgang. Antag, at bidragyderen allerede har _forket_ og _klonet_ repoen, så de har en Git-repo klar til at arbejde på, på deres lokale maskine:
Lad os se på en bidragsyder-arbejdsgang. Antag, at bidragsyderen allerede har _forket_ og _klonet_ repoet, så de har et Git repo klar til arbejde på deres lokale maskine:
1. **Opret en branch**. Brug kommandoen `git branch` til at oprette en branch, der vil indeholde de ændringer, de ønsker at bidrage med:
1. **Opret en branch** Brug kommandoen `git branch` til at oprette en branch, der indeholder de ændringer, de vil bidrage med:
```bash
git branch [branch-name]
@ -383,205 +468,310 @@ Lad os gennemgå en bidragsarbejdsgang. Antag, at bidragyderen allerede har _for
git switch -c [branch-name]
```
1. **Skift til arbejdsbranch**. Skift til den angivne branch og opdater arbejdsmappen med `git switch`:
1. **Skift til arbejdsbranch** Skift til den angivne branch og opdater arbejdsområdet med `git switch`:
```bash
git switch [branch-name]
```
> 💡 **Moderne note**: `git switch` er den moderne erstatning for `git checkout`, når du skifter branch. Det er mere klart og sikrere for begyndere.
> 💡 **Moderne note**: `git switch` er den moderne erstatning for `git checkout` ved brancheskift. Det er tydeligere og sikrere for begyndere.
1. **Arbejd**. På dette tidspunkt vil du tilføje dine ændringer. Husk at fortælle Git om det med følgende kommandoer:
1. **Arbejd** Nu skal du tilføje dine ændringer. Glem ikke at fortælle Git om det med følgende kommandoer:
```bash
git add .
git commit -m "my changes"
```
> ⚠️ **Commit-beskedkvalitet**: Sørg for at give din commit et godt navn, både for din egen skyld og for vedligeholderen af repoen, du hjælper med. Vær specifik om, hvad du har ændret!
> ⚠️ **Kvalitet på commit-beskeden**: Sørg for at give din commit et godt navn, både for din egen skyld og for maintaineren af det repo, du hjælper med. Vær konkret omkring, hvad du ændrede!
1. **Kombiner dit arbejde med `main`-branchen**. På et tidspunkt er du færdig med at arbejde, og du vil kombinere dit arbejde med det fra `main`-branchen. `main`-branchen kan have ændret sig i mellemtiden, så sørg for først at opdatere den til det nyeste med følgende kommandoer:
1. **Kombinér dit arbejde med `main` branchen** På et tidspunkt er du færdig med at arbejde, og du ønsker at kombinere dit arbejde med `main` branchen. `main` kan være ændret i mellemtiden, så sørg først for at opdatere den til den nyeste version med følgende kommandoer:
```bash
git switch main
git pull
```
På dette tidspunkt vil du sikre dig, at eventuelle _konflikter_, situationer hvor Git ikke nemt kan _kombinere_ ændringerne, sker i din arbejdsbranch. Derfor skal du køre følgende kommandoer:
Nu vil du sikre, at eventuelle _konflikter_, situationer hvor Git ikke let kan _kombinere_ ændringerne, sker i din arbejdsbranch. Derfor kør følgende kommandoer:
```bash
git switch [branch_name]
git merge main
```
Kommandoen `git merge main` vil bringe alle ændringer fra `main` ind i din branch. Forhåbentlig kan du bare fortsætte. Hvis ikke, vil VS Code fortælle dig, hvor Git er _forvirret_, og du ændrer de berørte filer for at angive, hvilket indhold der er mest korrekt.
`git merge main` kommandoen henter alle ændringer fra `main` ind i din branch. Forhåbentlig kan du bare fortsætte. Hvis ikke, vil VS Code fortælle dig, hvor Git er _forvirret_, og så kan du ændre de berørte filer for at angive, hvilket indhold der er mest korrekt.
💡 **Moderne alternativ**: Overvej at bruge `git rebase` for en renere historik:
```bash
git rebase main
```
Dette afspiller dine commits oven på den nyeste main-branch og skaber en lineær historik.
Dette "afspiller" dine commits oven på den nyeste main-branch og skaber en lineær historik.
1. **Send dit arbejde til GitHub**. At sende dit arbejde til GitHub betyder to ting. At pushe din branch til din repo og derefter åbne en PR, Pull Request.
1. **Send dit arbejde til GitHub** At sende dit arbejde til GitHub indebærer to ting. Pushe din branch til dit repo og derefter oprette en PR, Pull Request.
```bash
git push --set-upstream origin [branch-name]
```
Kommandoen ovenfor opretter branchen på din forkede repo.
Ovenstående kommando opretter branchen på dit forkede repo.
### 🤝 **Samarbejdsevner Check: Klar til at arbejde med andre?**
**Lad os se, hvordan du føler om samarbejde:**
- Giver ideen om forking og pull requests mening for dig nu?
- Hvad er én ting ved at arbejde med branches, som du gerne vil øve mere?
- Hvor tryg føler du dig ved at bidrage til andres projekter?
```mermaid
mindmap
root((Git Samarbejde))
Branching
Feature branches
Bug fix branches
Eksperimentelt arbejde
Pull Requests
Kodegennemgang
Diskussion
Testning
Best Practices
Klare commit-beskeder
Små fokuserede ændringer
God dokumentation
```
> **Selvtillidsboost**: Hver eneste udvikler, du beundrer, var engang nervøs for deres første pull request. GitHub-fællesskabet er utroligt imødekommende overfor nye!
1. **Åbn en PR**. Dernæst vil du åbne en PR. Det gør du ved at navigere til den forkede repo på GitHub. Du vil se en indikation på GitHub, hvor den spørger, om du vil oprette en ny PR, du klikker på det, og du bliver ført til en grænseflade, hvor du kan ændre commit-beskedens titel, give den en mere passende beskrivelse. Nu vil vedligeholderen af repoen, du forkede, se denne PR, og _fingers crossed_ vil de værdsætte og _merge_ din PR. Du er nu en bidragyder, yay :)
1. **Åbn en PR** Dernæst vil du åbne en PR. Det gør du ved at navigere til det forkede repo på GitHub. Du vil se en indikation på GitHub, hvor det spørger, om du vil oprette en ny PR, klik på den, og du kommer til et interface, hvor du kan ændre commit-besked titel og give en mere passende beskrivelse. Nu vil maintaineren af det repo, du forkede, se denne PR og _finger kryds_ de vil sætte pris på og _merge_ din PR. Du er nu en bidragsyder, yay :)
💡 **Moderne tip**: Du kan også oprette PR'er ved hjælp af GitHub CLI:
💡 **Moderne tip**: Du kan også oprette PRs vha. GitHub CLI:
```bash
gh pr create --title "Your PR title" --body "Description of changes"
```
🔧 **Bedste praksis for PR'er**:
- Link til relaterede issues ved hjælp af nøgleord som "Fixes #123"
🔧 **Bedste praksis for PRs**:
- Link til relaterede issues med nøgleord som "Fixes #123"
- Tilføj screenshots for UI-ændringer
- Anmod om specifikke reviewers
- Brug draft PR'er til igangværende arbejde
- Sørg for, at alle CI-tjek er bestået, før du anmoder om review
1. **Ryd op**. Det anses for god praksis at _rydde op_, efter du har fået succes med at merge en PR. Du vil rydde op i både din lokale branch og den branch, du pushede til GitHub. Først skal du slette den lokalt med følgende kommando:
- Brug draft PRs til arbejde under udvikling
- Sørg for at alle CI checks er bestået, før du anmoder om review
1. **Ryd op**. Det betragtes som god praksis at _rydde op_ efter du med succes har flettet en PR. Du vil gerne rydde op både i din lokale gren og den gren, du har pushet til GitHub. Lad os først slette den lokalt med følgende kommando:
```bash
git branch -d [branch-name]
```
Sørg for at gå til GitHub-siden for den forkede repo bagefter og fjerne den eksterne branch, du lige har pushet til den.
`Pull request` virker som et fjollet udtryk, fordi du egentlig vil pushe dine ændringer til projektet. Men vedligeholderen (projektets ejer) eller kerneholdet skal overveje dine ændringer, før de merges med projektets "main"-branch, så du anmoder faktisk om en beslutning om ændringen fra en vedligeholder.
En pull request er stedet, hvor man kan sammenligne og diskutere forskellene, der introduceres på en branch, med reviews, kommentarer, integrerede tests og mere. En god pull request følger stort set de samme regler som en commit-besked. Du kan tilføje en reference til et issue i issue-tracker, når dit arbejde for eksempel løser et issue. Dette gøres ved hjælp af et `#` efterfulgt af nummeret på dit issue. For eksempel `#97`.
🤞Krydser fingre for, at alle tjek går igennem, og projektets ejer(e) godkender dine ændringer og integrerer dem i projektet🤞
Opdater din nuværende lokale arbejdsgren med alle nye commits fra den tilsvarende fjerngren på GitHub:
`git pull`
Sørg for at gå til GitHub-siden for den forkede repo bagefter og fjern den remote gren, du lige har pushet til den.
## Bidrag til Open Source (Din Chance for at Gøre en Forskel!)
`Pull request` virker som et mærkeligt udtryk, fordi du egentlig vil pushe dine ændringer til projektet. Men vedligeholderen (projektets ejer) eller kerne teamet skal overveje dine ændringer, før de flettes med projektets "main" gren, så du anmoder egentlig en vedligeholder om at træffe en beslutning om ændringen.
Er du klar til noget, der vil blæse dit sind? 🤯 Lad os tale om at bidrage til open source-projekter jeg får gåsehud bare ved tanken om at dele dette med dig!
En pull request er stedet at sammenligne og diskutere forskellene, der introduceres på en gren med anmeldelser, kommentarer, integrerede tests og mere. En god pull request følger omtrent de samme regler som en commit-besked. Du kan tilføje en reference til et issue i issues-trackeren, når dit arbejde for eksempel løser et problem. Dette gøres med en `#` efterfulgt af nummeret på dit issue. For eksempel `#97`.
Dette er din chance for at blive en del af noget virkelig ekstraordinært. Forestil dig at forbedre de værktøjer, som millioner af udviklere bruger hver dag, eller rette en fejl i en app, som dine venner elsker. Det er ikke bare en drøm det er, hvad open source-bidrag handler om!
🤞Fingre krydsede for, at alle checks passerer, og at projektets ejer(e) fletter dine ændringer ind i projektet🤞
Her er det, der giver mig kuldegysninger hver gang jeg tænker på det: hvert eneste værktøj, du har lært med din kodeeditor, de frameworks vi vil udforske, endda den browser, du læser dette i startede med en person præcis som dig, der lavede deres allerførste bidrag. Den geniale udvikler, der byggede din yndlings VS Code-udvidelse? De var engang en nybegynder, der nervøst klikkede på "create pull request", præcis som du er ved at gøre.
Opdater din aktuelle lokale arbejdsgren med alle nye commits fra den tilsvarende remote gren på GitHub:
Og her er det smukkeste: open source-fællesskabet er som internettets største gruppekram. De fleste projekter leder aktivt efter nybegyndere og har problemer mærket "good first issue" specifikt for folk som dig! Maintainers bliver oprigtigt begejstrede, når de ser nye bidragsydere, fordi de husker deres egne første skridt.
`git pull`
Du lærer ikke bare at kode her du forbereder dig på at blive en del af en global familie af skabere, der vågner op hver dag og tænker "Hvordan kan vi gøre den digitale verden lidt bedre?" Velkommen til klubben! 🌟
## Bidrage til Open Source (Din Chance for at Gøre en Forskel!)
Er du klar til noget, der vil blæse dit sind helt væk? 🤯 Lad os tale om at bidrage til open source-projekter og jeg får gåsehud bare ved at tænke på at dele dette med dig!
Dette er din chance for at blive en del af noget virkelig ekstraordinært. Forestil dig at forbedre de værktøjer, som millioner af udviklere bruger hver dag, eller at rette en fejl i en app, som dine venner elsker. Det er ikke bare en drøm det er det, open source-bidrag handler om!
Her er hvad der giver mig kuldegysninger hver gang, jeg tænker på det: hvert eneste værktøj, du har lært med din kodeeditor, de frameworks vi skal udforske, selv browseren du læser dette i startede med nogen præcis som dig, som lavede deres allerførste bidrag. Den fantastiske udvikler, der byggede din yndlings VS Code-udvidelse? De var engang en begynder, der klikkede på "create pull request" med rystende hænder, ligesom du er ved at gøre nu.
Og her kommer det smukkeste: open source-fællesskabet er som internettets største gruppe-kram. De fleste projekter leder aktivt efter nye mennesker og har issues markeret med "good first issue" specifikt for folk som dig! Vedligeholdere bliver oprigtigt begejstrede, når de ser nye bidragydere, fordi de husker deres egne første skridt.
```mermaid
flowchart TD
A[🔍 Udforsk GitHub] --> B[🏷️ Find "god første opgave"]
B --> C[📖 Læs bidragsvejledning]
C --> D[🍴 Fork repository]
D --> E[💻 Opsæt lokal miljø]
E --> F[🌿 Opret feature-branch]
F --> G[✨ Lav dit bidrag]
G --> H[🧪 Test dine ændringer]
H --> I[📝 Skriv klar commit]
I --> J[📤 Push & opret PR]
J --> K[💬 Engager dig i feedback]
K --> L[🎉 Merged! Du er en bidragyder!]
L --> M[🌟 Find næste opgave]
style A fill:#e1f5fe
style L fill:#c8e6c9
style M fill:#fff59d
```
Du lærer ikke bare at kode her du forbereder dig på at slutte dig til en global familie af skabere, der vågner op hver dag og tænker "Hvordan kan vi gøre den digitale verden en smule bedre?" Velkommen til klubben! 🌟
Først skal vi finde et repository (eller **repo**) på GitHub, som interesserer dig, og som du gerne vil bidrage med en ændring til. Du vil gerne kopiere dets indhold til din maskine.
Først, lad os finde et repository (eller **repo**) på GitHub, som interesserer dig, og som du gerne vil bidrage til med en ændring. Du vil gerne kopiere dets indhold til din maskine.
✅ En god måde at finde 'begynder-venlige' repos er at [søge efter tagget 'good-first-issue'](https://github.blog/2020-01-22-browse-good-first-issues-to-start-contributing-to-open-source/).
✅ En god måde at finde 'begynder-venlige' repos er at [søgge efter tagget 'good-first-issue'](https://github.blog/2020-01-22-browse-good-first-issues-to-start-contributing-to-open-source/).
![Kopier et repo lokalt](../../../../translated_images/clone_repo.5085c48d666ead57664f050d806e325d7f883be6838c821e08bc823ab7c66665.da.png)
![Copy a repo locally](../../../../translated_images/clone_repo.5085c48d666ead57.da.png)
Der er flere måder at kopiere kode på. En måde er at "klone" indholdet af repositoryet ved hjælp af HTTPS, SSH eller GitHub CLI (Command Line Interface).
Der er flere måder at kopiere kode på. En måde er at "klone" indholdet af repositoryet, ved brug af HTTPS, SSH, eller GitHub CLI (Command Line Interface).
Åbn din terminal og klon repositoryet sådan her:
Åbn din terminal og klon repositoryet således:
```bash
# Using HTTPS
# Bruger HTTPS
git clone https://github.com/ProjectURL
# Using SSH (requires SSH key setup)
# Bruger SSH (kræver opsætning af SSH-nøgle)
git clone git@github.com:username/repository.git
# Using GitHub CLI
# Bruger GitHub CLI
gh repo clone username/repository
```
For at arbejde på projektet, skift til den rigtige mappe:
`cd ProjectURL`
Du kan også åbne hele projektet ved hjælp af:
- **[GitHub Codespaces](https://github.com/features/codespaces)** - GitHubs cloud-udviklingsmiljø med VS Code i browseren
Du kan også åbne hele projektet med:
- **[GitHub Codespaces](https://github.com/features/codespaces)** - GitHubs sky-udviklingsmiljø med VS Code i browseren
- **[GitHub Desktop](https://desktop.github.com/)** - En GUI-applikation til Git-operationer
- **[GitHub.dev](https://github.dev)** - Tryk på `.`-tasten på et hvilket som helst GitHub-repo for at åbne VS Code i browseren
- **[GitHub.dev](https://github.dev)** - Tryk på `.` tasten på et vilkårligt GitHub repo for at åbne VS Code i browseren
- **VS Code** med GitHub Pull Requests-udvidelsen
Til sidst kan du downloade koden i en zip-mappe.
Endelig kan du downloade koden i en zip-mappe.
### Et par flere interessante ting om GitHub
Du kan stjerne, følge og/eller "forke" ethvert offentligt repository på GitHub. Du kan finde dine stjernemarkerede repositories i drop-down-menuen øverst til højre. Det er som bogmærker, men for kode.
Du kan stjerne, overvåge og/eller "forke" ethvert offentligt repository på GitHub. Du kan finde dine stjernemarkerede repositories i dropdown-menuen øverst til højre. Det er som bogmærker, men til kode.
Projekter har en problemtracker, som oftest på GitHub i "Issues"-fanen, medmindre andet er angivet, hvor folk diskuterer problemer relateret til projektet. Og fanen Pull Requests er, hvor folk diskuterer og gennemgår ændringer, der er undervejs.
Projekter har en issue tracker, for det meste på GitHub under fanen "Issues" medmindre andet er angivet, hvor folk diskuterer problemer relateret til projektet. Og tabben Pull Requests er hvor folk diskuterer og gennemgår igangværende ændringer.
Projekter kan også have diskussioner i fora, mailinglister eller chatkanaler som Slack, Discord eller IRC.
🔧 **Moderne GitHub-funktioner**:
- **GitHub Discussions** - Indbygget forum til fællesskabssamtaler
- **GitHub Sponsors** - Støt maintainers økonomisk
- **Security tab** - Rapporter om sårbarheder og sikkerhedsrådgivning
- **Actions tab** - Se automatiserede workflows og CI/CD-pipelines
- **Insights tab** - Analyse af bidragsydere, commits og projektets sundhed
🔧 **Moderne GitHub funktioner**:
- **GitHub Discussions** - Indbygget forum til fællesskabs-samtaler
- **GitHub Sponsors** - Støt vedligeholdere økonomisk
- **Security tab** - Sårbarhedsrapporter og sikkerhedsadvarsler
- **Actions tab** - Se automatiserede workflows og CI/CD pipelines
- **Insights tab** - Analyse af bidragydere, commits og projektsundhed
- **Projects tab** - GitHubs indbyggede projektstyringsværktøjer
Tag et kig rundt i dit nye GitHub-repo og prøv et par ting, som at redigere indstillinger, tilføje information til dit repo, oprette et projekt (som et Kanban-board) og opsætte GitHub Actions til automatisering. Der er meget, du kan gøre!
Kig lidt rundt i dit nye GitHub repo og prøv et par ting, som at redigere indstillinger, tilføje information til dit repo, oprette et projekt (som et Kanban board), og sætte GitHub Actions op til automation. Der er meget, du kan gøre!
---
## 🚀 Udfordring
## 🚀 Udfordring
Okay, det er tid til at teste dine nye GitHub-superkræfter! 🚀 Her er en udfordring, der vil få det hele til at give mening på den mest tilfredsstillende måde:
Okay, det er tid til at teste dine skinnende nye GitHub superkræfter! 🚀 Her er en udfordring, der vil få det hele til at falde på plads på den mest tilfredsstillende måde:
Tag fat i en ven (eller det familiemedlem, der altid spørger, hvad du laver med alt det "computer-noget") og begiv jer ud på et samarbejdende kodeeventyr sammen! Her sker den ægte magi opret et projekt, lad dem forke det, lav nogle grene og flet ændringer som de professionelle, I er ved at blive.
Tag en ven (eller et familiemedlem, der altid spørger, hvad du laver med alt det "computerhalløj") og gå på en fælles kodningsrejse sammen! Her sker den ægte magi opret et projekt, lad dem forke det, lav nogle grene, og flet ændringer som de professionelle, du er ved at blive.
Jeg vil ikke lyve I vil sandsynligvis grine på et tidspunkt (især når I begge prøver at ændre den samme linje), måske klø jer i hovedet af forvirring, men I vil helt sikkert have de fantastiske "aha!"-øjeblikke, der gør al læring det værd. Plus, der er noget særligt ved at dele den første vellykkede fletning med en anden det er som en lille fejring af, hvor langt I er kommet!
Jeg vil ikke lyve I kommer sikkert til at grine på et tidspunkt (især når I begge forsøger at ændre den samme linje), måske klø jer i hovedet i forvirring, men I får helt sikkert de fantastiske "aha!"-øjeblikke, der gør alt læringen værd. Plus, der er noget særligt ved at dele den første succesfulde fletning med en anden det er som en lille fejring af, hvor langt du er kommet!
Har du ikke en kodekammerat endnu? Ingen problemer! GitHub-fællesskabet er fyldt med utroligt imødekommende mennesker, der husker, hvordan det var at være ny. Kig efter repositories med "good first issue"-mærker de siger i bund og grund "Hej nybegyndere, kom og lær med os!" Hvor fedt er det?
Har du ikke en kodebuddy endnu? Intet problem! GitHub-fællesskabet er fyldt med utroligt imødekommende mennesker, der husker, hvordan det var at være ny. Kig efter repositories med mærket "good first issue" de siger egentlig "Hej begyndere, kom og lær med os!" Ganske fantastisk, ikke?
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/en/)
## Post-forelæsning quiz
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/en/)
## Gennemgang & Fortsæt med at Lære
## Gennemgå & Bliv ved med at lære
Pyha! 🎉 Se på dig du har lige erobret GitHub-grundlæggende som en absolut mester! Hvis din hjerne føles lidt fyldt lige nu, er det helt normalt og ærligt talt et godt tegn. Du har lige lært værktøjer, der tog mig uger at føle mig komfortabel med, da jeg startede.
Puha! 🎉 Se bare på dig du har lige mestret GitHub-grundlæggende som en ægte mester! Hvis din hjerne føles lidt fyldt lige nu, er det helt normalt og faktisk et godt tegn. Du har lige lært værktøjer, som det tog mig uger at føle mig tryg ved, da jeg startede.
Git og GitHub er utroligt kraftfulde (som, virkelig kraftfulde), og hver udvikler jeg kender inklusive dem, der nu virker som troldmænd måtte øve sig og snuble lidt rundt, før det hele gav mening. Det faktum, at du er kommet igennem denne lektion, betyder, at du allerede er på vej til at mestre nogle af de vigtigste værktøjer i en udviklers værktøjskasse.
Git og GitHub er utroligt kraftfulde (altså, seriøst kraftfulde), og hver udvikler jeg kender inklusiv dem der nu virker som troldmænd måtte øve sig og snuble lidt, før det hele faldt på plads. Det faktum, at du har klaret denne lektion, betyder, at du allerede er godt på vej til at mestre nogle af de vigtigste værktøjer i en udviklers værktøjskasse.
Her er nogle helt fantastiske ressourcer, der kan hjælpe dig med at øve dig og blive endnu mere fantastisk:
Her er nogle helt fantastiske ressourcer til at hjælpe dig med at øve og blive endnu sejere:
- [Guide til at bidrage til open source-software](https://opensource.guide/how-to-contribute/#how-to-submit-a-contribution) Din køreplan til at gøre en forskel
- [Contributing to open source software guide](https://opensource.guide/how-to-contribute/#how-to-submit-a-contribution) Din køreplan til at gøre en forskel
- [Git cheatsheet](https://training.github.com/downloads/github-git-cheat-sheet/) Hav denne ved hånden til hurtig reference!
Og husk: øvelse gør fremskridt, ikke perfektion! Jo mere du bruger Git og GitHub, jo mere naturligt bliver det. GitHub har skabt nogle fantastiske interaktive kurser, der lader dig øve dig i et sikkert miljø:
Og husk: øvelse gør fremskridt, ikke perfektion! Jo mere du bruger Git og GitHub, desto mere naturligt bliver det. GitHub har lavet nogle fantastiske interaktive kurser, der lader dig øve i et sikkert miljø:
- [Introduktion til GitHub](https://github.com/skills/introduction-to-github)
- [Kommunikér ved hjælp af Markdown](https://github.com/skills/communicate-using-markdown)
- [Introduction to GitHub](https://github.com/skills/introduction-to-github)
- [Communicate using Markdown](https://github.com/skills/communicate-using-markdown)
- [GitHub Pages](https://github.com/skills/github-pages)
- [Håndtering af fletningskonflikter](https://github.com/skills/resolve-merge-conflicts)
- [Managing merge conflicts](https://github.com/skills/resolve-merge-conflicts)
**Føler du dig eventyrlysten? Tjek disse moderne værktøjer:**
- [GitHub CLI-dokumentation](https://cli.github.com/manual/) Når du vil føle dig som en kommandolinje-troldmand
- [GitHub Codespaces-dokumentation](https://docs.github.com/en/codespaces) Kod i skyen!
- [GitHub Actions-dokumentation](https://docs.github.com/en/actions) Automatiser alt
- [Git bedste praksis](https://www.atlassian.com/git/tutorials/comparing-workflows) Forbedr din arbejdsgang
**Føler du dig eventyrlysten? Prøv disse moderne værktøjer:**
- [GitHub CLI documentation](https://cli.github.com/manual/) Til når du vil føle dig som en kommandolinjemagi
- [GitHub Codespaces documentation](https://docs.github.com/en/codespaces) Kode i skyen!
- [GitHub Actions documentation](https://docs.github.com/en/actions) Automatiser alle tingene
- [Git best practices](https://www.atlassian.com/git/tutorials/comparing-workflows) Opgrader dit workflow-spil
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at gennemføre følgende udfordring:
**Beskrivelse:** Opret et samarbejdende webudviklingsprojekt, der demonstrerer den komplette GitHub-arbejdsgang, du har lært i denne lektion. Denne udfordring vil hjælpe dig med at øve repository-oprettelse, samarbejdsfunktioner og moderne Git-arbejdsgange i et virkeligt scenarie.
**Beskrivelse:** Opret et samarbejds-baseret webudviklingsprojekt, der demonstrerer den fulde GitHub-workflow, du har lært i denne lektion. Denne udfordring vil hjælpe dig med at øve repository-oprettelse, samarbejdsfunktioner og moderne Git-workflows i en realistisk scenarie.
**Opgave:** Opret et nyt offentligt GitHub-repository til et simpelt "Web Development Resources"-projekt. Repositoryet skal inkludere en velstruktureret README.md-fil, der lister nyttige webudviklingsværktøjer og ressourcer, organiseret efter kategorier (HTML, CSS, JavaScript osv.). Opsæt repositoryet med korrekte fællesskabsstandarder, inklusive en licens, bidragsretningslinjer og en adfærdskodeks. Opret mindst to funktionsgrene: en til at tilføje CSS-ressourcer og en anden til JavaScript-ressourcer. Lav commits til hver gren med beskrivende commit-beskeder, og opret derefter pull requests for at flette ændringerne tilbage til main. Aktiver GitHub-funktioner som Issues, Discussions, og opsæt et grundlæggende GitHub Actions-workflow til automatiske tjek.
**Prompt:** Opret et nyt offentligt GitHub-repository til et simpelt "Web Development Resources" projekt. Repositoryet bør inkludere en velstruktureret README.md-fil, der lister nyttige webudviklingsværktøjer og ressourcer, organiseret efter kategorier (HTML, CSS, JavaScript osv.). Opsæt repositoryet med passende fællesskabsstandarder inklusive en licens, bidragsretningslinjer og en adfærdskodeks. Opret mindst to feature-branches: en til tilføjelse af CSS-ressourcer og en anden til JavaScript-ressourcer. Lav commits til hver gren med beskrivende commit-beskeder, og opret derefter pull requests for at flette ændringerne tilbage til main. Aktivér GitHub-funktioner som Issues, Discussions, og opsæt en grundlæggende GitHub Actions workflow for automatiserede checks.
## Opgave
## Opgave
Din mission, hvis du vælger at acceptere den: Fuldfør [Introduktion til GitHub](https://github.com/skills/introduction-to-github)-kurset på GitHub Skills. Dette interaktive kursus lader dig øve alt, hvad du har lært, i et sikkert, vejledt miljø. Plus, du får et sejt badge, når du er færdig! 🏅
Din mission, hvis du vælger at acceptere den: Gennemfør [Introduction to GitHub](https://github.com/skills/introduction-to-github) kurset på GitHub Skills. Dette interaktive kursus lader dig øve alt, hvad du har lært, i et sikkert, vejledt miljø. Derudover får du et fedt badge, når du er færdig! 🏅
**Føler du dig klar til flere udfordringer?**
- Opsæt SSH-autentifikation for din GitHub-konto (ikke flere adgangskoder!)
**Klar til flere udfordringer?**
- Opsæt SSH-autentificering til din GitHub-konto (ingen flere adgangskoder!)
- Prøv at bruge GitHub CLI til dine daglige Git-operationer
- Opret et repository med et GitHub Actions-workflow
- Udforsk GitHub Codespaces ved at åbne netop dette repository i en cloud-baseret editor
- Opret et repository med en GitHub Actions workflow
- Udforsk GitHub Codespaces ved at åbne dette repository i en cloud-baseret editor
---
## 🚀 Din GitHub Mesterskabs Tidslinje
### ⚡ **Hvad du kan gøre inden for de næste 5 minutter**
- [ ] Stjerne dette repository og 3 andre projekter, der interesserer dig
- [ ] Opsæt to-faktor autentificering på din GitHub-konto
- [ ] Opret en simpel README til dit første repository
- [ ] Følg 5 udviklere, hvis arbejde inspirerer dig
### 🎯 **Hvad du kan nå på den næste time**
- [ ] Fuldfør quizzen efter lektionen og reflekter over din GitHub-rejse
- [ ] Opsæt SSH-nøgler for adgangskodefri GitHub autentificering
- [ ] Lav din første meningsfulde commit med en fremragende commit-besked
- [ ] Udforsk GitHubs "Explore"-fane for at opdage trending projekter
- [ ] Øv dig i at forke et repository og lave en lille ændring
### 📅 **Din ugelange GitHub-oplevelse**
- [ ] Fuldfør GitHub Skills-kurserne (Introduction to GitHub, Markdown)
- [ ] Lav din første pull request til et open source-projekt
- [ ] Opsæt en GitHub Pages-side til at fremvise dit arbejde
- [ ] Deltag i GitHub Discussions om projekter, du interesserer dig for
- [ ] Opret et repository med passende fællesskabsstandarder (README, Licens osv.)
- [ ] Prøv GitHub Codespaces til cloud-baseret udvikling
### 🌟 **Din månedslange forvandling**
- [ ] Bidrag til 3 forskellige open source-projekter
- [ ] Vær mentor for nogen, der er nye til GitHub (giv videre!)
- [ ] Opsæt automatiserede workflows med GitHub Actions
- [ ] Byg en portfolio, der viser dine GitHub-bidrag
- [ ] Deltag i Hacktoberfest eller lignende fællesskabsbegivenheder
- [ ] Bliv vedligeholder af dit eget projekt, som andre bidrager til
### 🎓 **Endelig GitHub Mesterskabs Check-in**
**Fejr hvor langt du er nået:**
- Hvad er dit yndlingsaspekt ved at bruge GitHub?
- Hvilken samarbejdsfunktion begejstrer dig mest?
- Hvor selvsikker føler du dig i forhold til at bidrage til open source nu?
- Hvilket projekt vil du gerne bidrage til som det første?
```mermaid
journey
title Din GitHub Tillidsrejse
section I dag
Nervøs: 3: You
Nysgerrig: 4: You
Begejstret: 5: You
section Denne uge
Øver: 4: You
Bidrager: 5: You
Forbinder: 5: You
section Næste måned
Samarbejder: 5: You
Leder: 5: You
Inspirerer Andre: 5: You
```
> 🌍 **Velkommen til det globale udviklerfællesskab!** Du har nu værktøjerne til at samarbejde med millioner af udviklere verden over. Dit første bidrag kan virke lille, men husk hvert stort open source-projekt startede med nogen, der lavede deres allerførste commit. Spørgsmålet er ikke, om du vil gøre en forskel, men hvilket fantastisk projekt der først vil få gavn af dit unikke perspektiv! 🚀
Husk: hver ekspert var engang en nybegynder. Du kan klare det! 💪
Husk: enhver ekspert har engang været begynder. Du klarer det! 💪
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiske oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på originalsproget bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

@ -1,50 +1,97 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "d0092f6533ae3ec3adad5b5ece68efaf",
"translation_date": "2025-10-23T21:52:10+00:00",
"original_hash": "672b0bb6e8b431075f3bdb7130590d2d",
"translation_date": "2026-01-06T23:29:50+00:00",
"source_file": "2-js-basics/1-data-types/README.md",
"language_code": "da"
}
-->
# JavaScript Grundlæggende: Datatyper
![JavaScript Grundlæggende - Datatyper](../../../../translated_images/webdev101-js-datatypes.4cc470179730702c756480d3ffa46507f746e5975ebf80f99fdaaf1cff09a7f4.da.png)
![JavaScript Basics - Data types](../../../../translated_images/webdev101-js-datatypes.4cc470179730702c.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
Datatyper er en af de grundlæggende koncepter i JavaScript, som du vil støde på i hvert program, du skriver. Tænk på datatyper som det arkiveringssystem, som de gamle bibliotekarer i Alexandria brugte de havde specifikke steder til skriftruller med poesi, matematik og historiske optegnelser. JavaScript organiserer information på en lignende måde med forskellige kategorier for forskellige slags data.
```mermaid
journey
title Din JavaScript Datatyper Eventyr
section Fundament
Variabler & Konstanter: 5: You
Deklarationssyntaks: 4: You
Tildelingskoncepter: 5: You
section Kernetyper
Tal & Matematik: 4: You
Strenge & Tekst: 5: You
Booleaner & Logik: 4: You
section Anvend Viden
Typekonvertering: 4: You
Virkelighedseksempler: 5: You
Bedste Praksis: 5: You
```
Datatyper er et af de grundlæggende begreber i JavaScript, som du vil støde på i hvert program, du skriver. Tænk på datatyper som filsystemet brugt af gamle bibliotekarer i Alexandria de havde specifikke steder til ruller med poesi, matematik og historiske optegnelser. JavaScript organiserer information på en lignende måde med forskellige kategorier til forskellige slags data.
I denne lektion vil vi udforske de centrale datatyper, der får JavaScript til at fungere. Du vil lære at håndtere tal, tekst, sand/falsk værdier og forstå, hvorfor det er vigtigt at vælge den korrekte type for dine programmer. Disse koncepter kan virke abstrakte i starten, men med øvelse vil de blive en naturlig del af din programmering.
I denne lektion vil vi udforske de kerne-datatyper, der får JavaScript til at fungere. Du vil lære at håndtere tal, tekst, sand/falsk-værdier og forstå, hvorfor det er vigtigt at vælge den rette type til dine programmer. Disse koncepter kan virke abstrakte i starten, men med øvelse bliver de en anden natur.
At forstå datatyper vil gøre alt andet i JavaScript meget klarere. Ligesom arkitekter skal forstå forskellige byggematerialer, før de konstruerer en katedral, vil disse grundlæggende begreber støtte alt, hvad du bygger fremover.
At forstå datatyper vil gøre alt andet i JavaScript meget klarere. Ligesom arkitekter skal forstå forskellige byggematerialer, før de bygger en katedral, vil disse grundprincipper støtte alt, hvad du bygger fremover.
## Quiz før lektionen
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/)
## Forud-forelæsning Quiz
[Forud-forelæsning quiz](https://ff-quizzes.netlify.app/web/)
Denne lektion dækker det grundlæggende i JavaScript, sproget der giver interaktivitet på nettet.
Denne lektion dækker det grundlæggende i JavaScript, sproget der skaber interaktivitet på nettet.
> Du kan tage denne lektion på [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-variables/?WT.mc_id=academic-77807-sagibbon)!
[![Variabler](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Variabler i JavaScript")
[![Variables](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Variables in JavaScript")
[![Datatyper i JavaScript](https://img.youtube.com/vi/AWfA95eLdq8/0.jpg)](https://youtube.com/watch?v=AWfA95eLdq8 "Datatyper i JavaScript")
[![Data Types in JavaScript](https://img.youtube.com/vi/AWfA95eLdq8/0.jpg)](https://youtube.com/watch?v=AWfA95eLdq8 "Data Types in JavaScript")
> 🎥 Klik på billederne ovenfor for videoer om variabler og datatyper
Lad os starte med variabler og de datatyper, der fylder dem!
Lad os starte med variabler og de datatyper, der udfylder dem!
```mermaid
mindmap
root((JavaScript Data))
Variabler
let myVar
const PI = 3.14
var oldStyle
Primitiv Typer
tal
42
3.14
-5
streng
"Hej"
'Verden'
`Skabelon`
boolesk
sand
falsk
udefineret
null
Operationer
Aritmetisk
+ - * / %
Streng Metoder
sammenkædning
skabelonliteraler
Type Konvertering
implicit
eksplicit
```
## Variabler
Variabler er fundamentale byggesten i programmering. Ligesom de mærkede krukker, som middelalderens alkymister brugte til at opbevare forskellige stoffer, giver variabler dig mulighed for at gemme information og give den et beskrivende navn, så du kan referere til det senere. Skal du huske en persons alder? Gem det i en variabel kaldet `age`. Vil du holde styr på en brugers navn? Gem det i en variabel kaldet `userName`.
Variabler er grundlæggende byggesten i programmering. Ligesom de mærkede krukker, som middelalderlige alkymister brugte til at opbevare forskellige stoffer, tillader variabler dig at gemme information og give den et beskrivende navn, så du kan referere til det senere. Skal du huske en persons alder? Gem den i en variabel kaldet `age`. Vil du holde styr på en brugers navn? Gem det i en variabel kaldet `userName`.
Vi vil fokusere på den moderne tilgang til at oprette variabler i JavaScript. De teknikker, du lærer her, repræsenterer års udvikling af sproget og bedste praksis udviklet af programmeringsfællesskabet.
Vi vil fokusere på den moderne tilgang til at oprette variabler i JavaScript. De teknikker, du lærer her, repræsenterer års sprogets udvikling og bedste praksis udviklet af programmeringsfællesskabet.
At oprette og **deklarere** en variabel har følgende syntaks **[nøgleord] [navn]**. Det består af to dele:
Oprettelse og **deklarering** af en variabel har følgende syntaks **[keyword] [name]**. Det består af to dele:
- **Nøgleord**. Brug `let` til variabler, der kan ændre sig, eller `const` til værdier, der forbliver de samme.
- **Variabelnavnet**, dette er et beskrivende navn, du selv vælger.
- **Nøgleord**. Brug `let` til variabler, der kan ændres, eller `const` til værdier, der forbliver den samme.
- **Variabelnavnet**, dette er et beskrivende navn, du vælger selv.
✅ Nøgleordet `let` blev introduceret i ES6 og giver din variabel en såkaldt _blokscope_. Det anbefales, at du bruger `let` eller `const` i stedet for det ældre nøgleord `var`. Vi vil dække blokscope mere detaljeret i fremtidige dele.
✅ Nøgleordet `let` blev introduceret i ES6 og giver din variabel et såkaldt _blokscope_. Det anbefales, at du bruger `let` eller `const` i stedet for det ældre `var`-nøgleord. Vi vil dække blokscope mere detaljeret i fremtidige dele.
### Opgave - arbejde med variabler
@ -55,35 +102,35 @@ At oprette og **deklarere** en variabel har følgende syntaks **[nøgleord] [nav
```
**Hvad dette opnår:**
- Dette fortæller JavaScript at oprette en lagerplads kaldet `myVariable`
- JavaScript allokerer plads i hukommelsen til denne variabel
- Dette fortæller JavaScript at oprette et lagersted kaldet `myVariable`
- JavaScript tildeler plads i hukommelsen til denne variabel
- Variablen har i øjeblikket ingen værdi (undefined)
2. **Giv den en værdi**. Lad os nu placere noget i vores variabel:
2. **Giv den en værdi**. Lad os nu putte noget i vores variabel:
```javascript
myVariable = 123;
```
**Hvordan tildeling fungerer:**
- Operatøren `=` tildeler værdien 123 til vores variabel
- Variablen indeholder nu denne værdi i stedet for at være undefined
- Du kan referere til denne værdi i hele din kode ved hjælp af `myVariable`
**Sådan fungerer tildeling:**
- Operatoren `=` tildeler værdien 123 til vores variabel
- Variablen indeholder nu denne værdi i stedet for værende undefined
- Du kan referere til denne værdi i hele din kode ved at bruge `myVariable`
> Bemærk: brugen af `=` i denne lektion betyder, at vi bruger en "tildelingsoperator", der bruges til at sætte en værdi til en variabel. Det angiver ikke lighed.
> Bemærk: brugen af `=` i denne lektion betyder, at vi bruger en "tildelingsoperator", som bruges til at sætte en værdi til en variabel. Det betyder ikke lighed.
3. **Gør det på den smarte måde**. Lad os faktisk kombinere de to trin:
3. **Gør det den smarte måde**. Faktisk, lad os kombinere de to trin:
```javascript
let myVariable = 123;
```
**Denne tilgang er mere effektiv:**
- Du deklarerer variablen og tildeler en værdi i én erklæring
- Du deklarerer variablen og tildeler en værdi i en enkelt erklæring
- Dette er standardpraksis blandt udviklere
- Det reducerer kodelængden, mens det opretholder klarhed
- Det reducerer kodelængden samtidig med at klarheden bevares
4. **Skift mening**. Hvad hvis vi vil gemme et andet nummer?
4. **Skift mening**. Hvad hvis vi vil gemme et andet tal?
```javascript
myVariable = 321;
@ -92,122 +139,187 @@ At oprette og **deklarere** en variabel har følgende syntaks **[nøgleord] [nav
**Forståelse af gen-tildeling:**
- Variablen indeholder nu 321 i stedet for 123
- Den tidligere værdi erstattes variabler gemmer kun én værdi ad gangen
- Denne mutabilitet er den nøgleegenskab ved variabler, der er deklareret med `let`
- Denne mutabilitet er den vigtigste egenskab ved variabler erklæret med `let`
✅ Prøv det! Du kan skrive JavaScript direkte i din browser. Åbn et browservindue og naviger til Developer Tools. I konsollen finder du en prompt; skriv `let myVariable = 123`, tryk på return, og skriv derefter `myVariable`. Hvad sker der? Bemærk, du vil lære mere om disse koncepter i de følgende lektioner.
✅ Prøv det! Du kan skrive JavaScript direkte i din browser. Åbn et browservindue og naviger til Udviklerværktøjer. I konsollen finder du en prompt; skriv `let myVariable = 123`, tryk enter, og skriv derefter `myVariable`. Hvad sker der? Du vil lære mere om disse koncepter i efterfølgende lektioner.
### 🧠 **Master Check af Variabler: Bliv Fortrolig**
**Lad os se, hvordan du har det med variabler:**
- Kan du forklare forskellen mellem at deklarere og tildele en variabel?
- Hvad sker der, hvis du prøver at bruge en variabel, før du har erklæret den?
- Hvornår vil du vælge `let` over `const` for en variabel?
```mermaid
stateDiagram-v2
[*] --> Declared: lad minVar
Declared --> Assigned: minVar = 123
Assigned --> Reassigned: minVar = 456
Assigned --> [*]: Variabel klar!
Reassigned --> [*]: Opdateret værdi
note right of Declared
Variablen findes, men
har ingen værdi (undefined)
end note
note right of Assigned
Variablen indeholder
værdien 123
end note
```
> **Hurtigt tip**: Tænk på variabler som mærkede opbevaringsbokse. Du opretter boksen (`let`), lægger noget i den (`=`), og kan senere erstatte indholdet, hvis det er nødvendigt!
## Konstanter
Nogle gange har du brug for at gemme information, der aldrig må ændres under programkørslen. Tænk på konstanter som de matematiske principper, som Euklid etablerede i det gamle Grækenland når de først er bevist og dokumenteret, forbliver de faste for al fremtidig reference.
Nogle gange skal du gemme information, der aldrig må ændres under programmets udførelse. Tænk på konstanter som de matematiske principper, som Euklid etablerede i oldtidens Grækenland når de først var bevist og dokumenteret, forblev de faste for al fremtidig brug.
Konstanter fungerer på samme måde som variabler, men med en vigtig begrænsning: når du har tildelt deres værdi, kan den ikke ændres. Denne uforanderlighed hjælper med at forhindre utilsigtede ændringer af kritiske værdier i dit program.
Konstanter fungerer ligesom variabler, men med en vigtig begrænsning: når du først har tildelt deres værdi, kan den ikke ændres. Denne uforanderlighed hjælper med at forhindre utilsigtede ændringer af kritiske værdier i dit program.
Deklaration og initialisering af en konstant følger de samme koncepter som en variabel, med undtagelse af nøgleordet `const`. Konstanter deklareres typisk med store bogstaver.
Deklaration og initialisering af en konstant følger samme koncept som for en variabel, med undtagelse af nøgleordet `const`. Konstanter erklæres typisk med store bogstaver.
```javascript
const MY_VARIABLE = 123;
```
**Hvad denne kode gør:**
- **Opretter** en konstant kaldet `MY_VARIABLE` med værdien 123
- **Bruger** store bogstaver som navnekonvention for konstanter
- **Forhindrer** fremtidige ændringer af denne værdi
**Dette er, hvad koden gør:**
- **Opretter** en konstant med navnet `MY_VARIABLE` med værdien 123
- **Bruger** store bogstaver som navngivningskonvention for konstanter
- **Forhindrer** enhver fremtidig ændring af denne værdi
Konstanter har to hovedregler:
- **Du skal give dem en værdi med det samme** ingen tomme konstanter tilladt!
- **Du kan aldrig ændre den værdi** JavaScript vil give en fejl, hvis du prøver. Lad os se, hvad jeg mener:
- **Du skal give dem en værdi med det samme** tomme konstanter er ikke tilladt!
- **Du kan aldrig ændre den værdi** JavaScript vil kaste en fejl, hvis du prøver. Lad os se, hvad jeg mener:
**Simpel værdi** - Følgende er IKKE tilladt:
**Simpel værdi** Følgende er IKKE tilladt:
```javascript
const PI = 3;
PI = 4; // ikke tilladt
```
**Hvad du skal huske:**
- **Forsøg** på at gen-tildeling af en konstant vil forårsage en fejl
**Husk dette:**
- **Forsøg** på at gen-tildele en konstant vil forårsage en fejl
- **Beskytter** vigtige værdier mod utilsigtede ændringer
- **Sikrer** at værdien forbliver konsistent i hele dit program
- **Sikrer** at værdien forbliver konsistent gennem hele dit program
**Objektreference er beskyttet** - Følgende er IKKE tilladt:
**Objektreferencen er beskyttet** Følgende er IKKE tilladt:
```javascript
const obj = { a: 3 };
obj = { b: 5 } // ikke tilladt
```
**Forståelse af disse koncepter:**
- **Forhindrer** at erstatte hele objektet med et nyt
**Forstå disse koncepter:**
- **Forhindrer** at hele objektet bliver erstattet med et nyt
- **Beskytter** referencen til det oprindelige objekt
- **Bevarer** objektets identitet i hukommelsen
- **Opholder** objektets identitet i hukommelsen
**Objektværdi er ikke beskyttet** - Følgende ER tilladt:
**Objektets værdi er ikke beskyttet** Følgende ER tilladt:
```javascript
const obj = { a: 3 };
obj.a = 5; // tilladt
```
**Hvad der sker her:**
- **Ændrer** egenskabsværdien inde i objektet
- **Bevarer** den samme objektreference
- **Viser** at objektindhold kan ændres, mens referencen forbliver konstant
**Her er, hvad der sker:**
- **Ændrer** værdien af en egenskab inde i objektet
- **Beholder** den samme objektreference
- **Viser** at objektets indhold kan ændres, mens referencen forbliver konstant
> Bemærk, en `const` betyder, at referencen er beskyttet mod gen-tildeling. Værdien er dog ikke _uforanderlig_ og kan ændre sig, især hvis det er en kompleks konstruktion som et objekt.
## Datatyper
JavaScript organiserer information i forskellige kategorier kaldet datatyper. Dette koncept afspejler, hvordan gamle lærde kategoriserede viden Aristoteles skelnede mellem forskellige typer af ræsonnement, idet han vidste, at logiske principper ikke kunne anvendes ensartet på poesi, matematik og naturfilosofi.
JavaScript organiserer information i forskellige kategorier kaldet datatyper. Dette koncept spejler, hvordan gamle lærde kategoriserede viden Aristoteles skelnede mellem forskellige typer af ræsonnement, vel vidende, at logiske principper ikke kunne anvendes ensartet på poesi, matematik og naturfilosofi.
Datatyper er vigtige, fordi forskellige operationer fungerer med forskellige slags information. Ligesom du ikke kan udføre aritmetik på en persons navn eller alfabetisere en matematisk ligning, kræver JavaScript den passende datatype for hver operation. At forstå dette forhindrer fejl og gør din kode mere pålidelig.
Datatyper er vigtige, fordi forskellige operationer arbejder med forskellige slags information. Ligesom du ikke kan udføre aritmetik på en persons navn eller alfabetisere en matematisk ligning, kræver JavaScript den passende datatype til hver operation. At forstå dette forhindrer fejl og gør din kode mere pålidelig.
Variabler kan gemme mange forskellige typer værdier, såsom tal og tekst. Disse forskellige typer værdier er kendt som **datatyper**. Datatyper er en vigtig del af softwareudvikling, fordi det hjælper udviklere med at træffe beslutninger om, hvordan koden skal skrives, og hvordan softwaren skal køre. Desuden har nogle datatyper unikke funktioner, der hjælper med at transformere eller udtrække yderligere information fra en værdi.
Variabler kan gemme mange forskellige typer værdier, som tal og tekst. Disse forskellige værdityper kaldes for **datatyper**. Datatyper er en vigtig del af softwareudvikling, fordi de hjælper udviklere med at træffe beslutninger om, hvordan koden skal skrives, og hvordan softwaren skal køre. Derudover har nogle datatyper unikke egenskaber, der hjælper med at transformere eller udvinde yderligere information fra en værdi.
✅ Datatyper kaldes også JavaScript data primitives, da de er de laveste niveau datatyper, der leveres af sproget. Der er 7 primitive datatyper: string, number, bigint, boolean, undefined, null og symbol. Tag et øjeblik til at visualisere, hvad hver af disse primitives kunne repræsentere. Hvad er en `zebra`? Hvad med `0`? `true`?
✅ Datatyper kaldes også JavaScript data-primitiver, da de er de lavest-niveau datatyper, som sproget tilbyder. Der er 7 primitive datatyper: string, number, bigint, boolean, undefined, null og symbol. Brug et øjeblik på at visualisere, hvad hver af disse primitive kunne repræsentere. Hvad er en `zebra`? Hvad med `0`? `true`?
### Tal
Tal er den mest ligetil datatype i JavaScript. Uanset om du arbejder med hele tal som 42, decimaler som 3.14 eller negative tal som -5, håndterer JavaScript dem ensartet.
Tal er den mest ligetil datatype i JavaScript. Uanset om du arbejder med heltal som 42, decimaltal som 3.14, eller negative tal som -5, håndterer JavaScript dem ensartet.
Husk vores variabel fra tidligere? Den 123, vi gemte, var faktisk en tal-datatype:
Kan du huske vores variabel fra tidligere? Det 123, vi gemte, var faktisk en tal-datatype:
```javascript
let myVariable = 123;
```
**Vigtige egenskaber:**
**Nøglekarakteristika:**
- JavaScript genkender automatisk numeriske værdier
- Du kan udføre matematiske operationer med disse variabler
- Ingen eksplicit typeerklæring er nødvendig
- Ingen eksplicit typeangivelse er nødvendig
Variabler kan gemme alle typer tal, inklusive decimaler eller negative tal. Tal kan også bruges med aritmetiske operatorer, som dækkes i [næste afsnit](../../../../2-js-basics/1-data-types).
Variabler kan gemme alle typer tal, inklusive decimaltal eller negative tal. Tal kan også bruges med aritmetiske operatorer, som dækkes i [næste sektion](../../../../2-js-basics/1-data-types).
```mermaid
flowchart LR
A["🔢 Tal"] --> B[" Addition"]
A --> C[" Subtraktion"]
A --> D["✖️ Multiplikation"]
A --> E["➗ Division"]
A --> F["📊 Rest %"]
B --> B1["1 + 2 = 3"]
C --> C1["5 - 3 = 2"]
D --> D1["4 * 3 = 12"]
E --> E1["10 / 2 = 5"]
F --> F1["7 % 3 = 1"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
### Aritmetiske Operatorer
Aritmetiske operatorer giver dig mulighed for at udføre matematiske beregninger i JavaScript. Disse operatorer følger de samme principper, som matematikere har brugt i århundreder de samme symboler, der optrådte i værker af lærde som Al-Khwarizmi, der udviklede algebraisk notation.
Aritmetiske operatorer giver dig mulighed for at udføre matematiske beregninger i JavaScript. Disse operatorer følger de samme principper som matematikere har brugt i århundreder de samme symboler, der optrådte i værker af lærde som Al-Khwarizmi, som udviklede algebraisk notation.
Operatorerne fungerer, som du ville forvente fra traditionel matematik: plus for addition, minus for subtraktion og så videre.
Operatorerne fungerer som forventet fra traditionel matematik: plus for addition, minus for subtraktion, osv.
Der er flere typer operatorer, du kan bruge, når du udfører aritmetiske funktioner, og nogle er listet her:
Der er flere typer operatorer, som du kan bruge til aritmetiske funktioner, og nogle er her listet:
| Symbol | Beskrivelse | Eksempel |
| ------ | ----------------------------------------------------------------------- | -------------------------------- |
| `+` | **Addition**: Beregner summen af to tal | `1 + 2 //forventet svar er 3` |
| `-` | **Subtraktion**: Beregner forskellen mellem to tal | `1 - 2 //forventet svar er -1` |
| `*` | **Multiplikation**: Beregner produktet af to tal | `1 * 2 //forventet svar er 2` |
| `/` | **Division**: Beregner kvotienten af to tal | `1 / 2 //forventet svar er 0.5` |
| `%` | **Rest**: Beregner resten fra divisionen af to tal | `1 % 2 //forventet svar er 1` |
| Symbol | Beskrivelse | Eksempel |
| ------ | ------------------------------------------------------------------- | -------------------------------- |
| `+` | **Addition**: Beregner summen af to tal | `1 + 2 //forventet svar er 3` |
| `-` | **Subtraktion**: Beregner forskellen mellem to tal | `1 - 2 //forventet svar er -1` |
| `*` | **Multiplikation**: Beregner produktet af to tal | `1 * 2 //forventet svar er 2` |
| `/` | **Division**: Beregner kvotienten af to tal | `1 / 2 //forventet svar er 0.5` |
| `%` | **Resterende**: Beregner resten fra divisionen af to tal | `1 % 2 //forventet svar er 1` |
✅ Prøv det! Prøv en aritmetisk operation i din browsers konsol. Overrasker resultaterne dig?
### Strings
### 🧮 **Matematikfærdighedscheck: Regn med Selvsikkerhed**
**Test din aritmetiske forståelse:**
- Hvad er forskellen mellem `/` (division) og `%` (rest)?
- Kan du forudsige, hvad `10 % 3` er? (Hint: det er ikke 3,33...)
- Hvorfor kunne restoperatoren være nyttig i programmering?
```mermaid
pie title "Brug af JavaScript-taloperationer"
"Addition (+)" : 35
"Subtraktion (-)" : 20
"Multiplikation (*)" : 20
"Division (/)" : 15
"Rest (%)" : 10
```
> **Virkelighedsindsigt**: Restoperatoren (%) er super nyttig til at tjekke, om tal er lige/ulige, skabe mønstre eller cykle gennem arrays!
### Strenge
I JavaScript repræsenteres tekstdata som strings. Udtrykket "string" kommer fra konceptet med tegn, der er bundet sammen i en sekvens, ligesom skrivere i middelalderens klostre forbandt bogstaver for at danne ord og sætninger i deres manuskripter.
I JavaScript repræsenteres tekstdata som strenge. Begrebet "streng" kommer fra idéen om tegn sat sammen i sekvens, ligesom skribenter i middelalderlige klostre ville forbinde bogstaver for at danne ord og sætninger i deres manuskripter.
Strings er fundamentale for webudvikling. Hver tekst, der vises på en hjemmeside brugernavne, knapetiketter, fejlmeddelelser, indhold håndteres som string-data. At forstå strings er afgørende for at skabe funktionelle brugergrænseflader.
Strenge er grundlæggende for webudvikling. Hver tekst, der vises på et website brugernavne, knap-etiketter, fejlmeddelelser, indhold håndteres som strengdata. At forstå strenge er essentielt for at skabe funktionelle brugerflader.
Strings er sæt af tegn, der er placeret mellem enkelt- eller dobbeltanførselstegn.
Strenge er sæt af tegn, der befinder sig mellem enkelt- eller dobbeltanførselstegn.
```javascript
'This is a string'
@ -215,65 +327,118 @@ Strings er sæt af tegn, der er placeret mellem enkelt- eller dobbeltanførselst
let myString = 'This is a string value stored in a variable';
```
**Forståelse af disse koncepter:**
- **Bruger** enten enkeltanførselstegn `'` eller dobbeltanførselstegn `"` til at definere strings
- **Gemmer** tekstdata, der kan inkludere bogstaver, tal og symboler
- **Tildeler** string-værdier til variabler til senere brug
**Forstå disse koncepter:**
- **Bruger** enten enkeltanførselstegn `'` eller dobbeltanførselstegn `"` til at definere strenge
- **Gemmer** tekstdata, som kan inkludere bogstaver, tal og symboler
- **Tildeler** strengværdier til variabler til senere brug
- **Kræver** anførselstegn for at skelne tekst fra variabelnavne
Husk at bruge anførselstegn, når du skriver en string, ellers vil JavaScript antage, at det er et variabelnavn.
Husk at bruge anførselstegn, når du skriver en streng, ellers vil JavaScript antage, det er et variabelnavn.
### Formatering af Strings
String-manipulation giver dig mulighed for at kombinere tekstelementer, inkorporere variabler og skabe dynamisk indhold, der reagerer på programmets tilstand. Denne teknik gør det muligt for dig at konstruere tekst programmæssigt.
```mermaid
flowchart TD
A["📝 Strenge"] --> B["Enkelt anførselstegn"]
A --> C["Dobbelte anførselstegn"]
A --> D["Skabelon literals"]
B --> B1["'Hej Verden'"]
C --> C1["\"Hej Verden\""]
D --> D1["`Hej \${name}`"]
E["String operationer"] --> F["Kædning"]
E --> G["Skabelon indsættelse"]
E --> H["Længde & metoder"]
F --> F1["'Hej' + ' ' + 'Verden'"]
G --> G1["`Hej \${firstName} \${lastName}`"]
H --> H1["minString.længde"]
style A fill:#e3f2fd
style E fill:#fff3e0
style D fill:#e8f5e8
style G fill:#e8f5e8
```
### Formatering af Strenge
Ofte har du brug for at kombinere flere strings denne proces kaldes sammenkædning.
Strengmanipulation giver dig mulighed for at kombinere tekstdele, inkorporere variabler og skabe dynamisk indhold, der reagerer på programmets tilstand. Denne teknik gør det muligt at konstruere tekst programmatisk.
For at **sammenkæde** to eller flere strings, eller forbinde dem, skal du bruge operatoren `+`.
Ofte skal du sammenkæde flere strenge denne proces kaldes konkatenering.
For at **sammenkæde** to eller flere strenge, eller forbinde dem sammen, brug `+` operatoren.
```javascript
let myString1 = "Hello";
let myString2 = "World";
myString1 + myString2 + "!"; //HelloWorld!
myString1 + " " + myString2 + "!"; //Hello World!
myString1 + ", " + myString2 + "!"; //Hello, World!
myString1 + myString2 + "!"; //HejVerden!
myString1 + " " + myString2 + "!"; //Hej Verden!
myString1 + ", " + myString2 + "!"; //Hej, Verden!
```
**Trin for trin, her er hvad der sker:**
- **Kombinerer** flere strings ved hjælp af operatoren `+`
- **Forbinder** strings direkte sammen uden mellemrum i det første eksempel
- **Tilføjer** mellemrumstegn `" "` mellem strings for læsbarhed
- **Kombinerer** flere strenge ved brug af `+` operatoren
- **Forbinder** strenge direkte sammen uden mellemrum i det første eksempel
- **Tilføjer** mellemrumstegn `" "` mellem strenge for læsbarhed
- **Indsætter** tegnsætning som kommaer for at skabe korrekt formatering
✅ Hvorfor er `1 + 1 = 2` i JavaScript, men `'1' + '1' = 11?` Tænk over det. Hvad med `'1' + 1`?
**Template literals** er en anden måde at formatere strings på, bortset fra at der bruges backtick i stedet for anførselstegn. Alt, der ikke er almindelig tekst, skal placeres i pladsholdere `${ }`. Dette inkluderer eventuelle variabler, der kan være strings.
**Template literals** er en anden måde at formatere strenge på, bortset fra at backtick bruges i stedet for citationstegn. Alt der ikke er almindelig tekst, skal placeres inden i pladsholdere `${ }`. Dette inkluderer eventuelle variable, som kan være strenge.
```javascript
let myString1 = "Hello";
let myString2 = "World";
`${myString1} ${myString2}!` //Hello World!
`${myString1}, ${myString2}!` //Hello, World!
`${myString1} ${myString2}!` //Hej Verden!
`${myString1}, ${myString2}!` //Hej, Verden!
```
**Lad os forstå hver del:**
- **Bruger** backticks `` ` `` i stedet for almindelige anførselstegn til at oprette template literals
- **Indlejrer** variabler direkte ved hjælp af `${}` pladsholder-syntaks
- **Bevarer** mellemrum og formatering præcis som skrevet
- **Giver** en mere overskuelig måde at oprette komplekse strings med variabler
Du kan opnå dine formateringsmål med begge metoder, men skabelonlitteraler vil respektere alle mellemrum og linjeskift.
✅ Hvornår ville du bruge en skabelonlitteral i stedet for en almindelig streng?
- **Bruger** backticks `` ` `` i stedet for almindelige citationstegn til at lave template literals
- **Indlejrer** variable direkte ved brug af `${}` pladsholder-syntaks
- **Bevarer** mellemrum og formatering nøjagtigt som skrevet
- **Tilbyder** en renere måde at skabe komplekse strenge med variable på
Du kan opnå dine formateringsmål med begge metoder, men template literals vil respektere ethvert mellemrum og linjeskift.
✅ Hvornår ville du bruge en template literal kontra en almindelig streng?
### 🔤 **Kontrol af strengfærdigheder: Tillid til tekstopredigering**
**Evaluer dine strengfærdigheder:**
- Kan du forklare, hvorfor `'1' + '1'` er lig med `'11'` i stedet for `2`?
- Hvilken strengmetode synes du er mest læsbar: sammenkædning eller template literals?
- Hvad sker der, hvis du glemmer citaterne omkring en streng?
```mermaid
stateDiagram-v2
[*] --> PlainText: "Hej"
[*] --> Variable: name = "Alice"
PlainText --> Concatenated: + " " + name
Variable --> Concatenated
PlainText --> Template: `Hej ${name}`
Variable --> Template
Concatenated --> Result: "Hej Alice"
Template --> Result
note right of Concatenated
Traditionel metode
Mere omstændelig
end note
note right of Template
Moderne ES6-syntaks
Renere & mere læselig
end note
```
> **Pro tip**: Template literals foretrækkes som regel til kompleks strengbygning, fordi de er mere læsbare og håndterer flersporede strenge smukt!
### Booleans
Booleans repræsenterer den simpleste form for data: de kan kun have én af to værdier `true` eller `false`. Dette binære logiksystem stammer fra George Boole, en matematiker fra det 19. århundrede, som udviklede Boolesk algebra.
Booleans repræsenterer den simpleste form for data: de kan kun indeholde en af to værdier `true` eller `false`. Dette binære logiksystem kan føres tilbage til George Boole, en matematiker fra det 19. århundrede, som udviklede boolsk algebra.
På trods af deres enkelhed er booleans essentielle for programlogik. De gør det muligt for din kode at træffe beslutninger baseret på betingelser om en bruger er logget ind, om en knap er blevet klikket, eller om visse kriterier er opfyldt.
På trods af deres enkelhed er booleans essentielle for programlogikken. De gør det muligt for din kode at træffe beslutninger baseret på betingelser om en bruger er logget ind, om en knap blev klikket, eller om bestemte kriterier er opfyldt.
Booleans kan kun have to værdier: `true` eller `false`. Booleans kan hjælpe med at beslutte, hvilke linjer kode der skal køres, når visse betingelser er opfyldt. I mange tilfælde hjælper [operatorer](../../../../2-js-basics/1-data-types) med at sætte værdien af en Boolean, og du vil ofte bemærke og skrive variabler, der bliver initialiseret eller deres værdier opdateret med en operator.
Booleans kan kun have to værdier: `true` eller `false`. Booleans kan hjælpe med at træffe beslutninger om, hvilke kode linjer der skal køre, når visse betingelser opfyldes. I mange tilfælde hjælper [operatorer](../../../../2-js-basics/1-data-types) med at sætte værdien af en Boolean, og du vil ofte bemærke og skrive variable, der initialiseres eller får opdateret deres værdier med en operator.
```javascript
let myTrueBool = true;
@ -281,43 +446,173 @@ let myFalseBool = false;
```
**I ovenstående har vi:**
- **Oprettet** en variabel, der gemmer Boolean-værdien `true`
- **Demonstreret** hvordan man gemmer Boolean-værdien `false`
- **Brugt** de præcise nøgleord `true` og `false` (ingen anførselstegn nødvendige)
- **Forberedt** disse variabler til brug i betingede udsagn
- **Oprettet** en variabel, der gemmer den boolske værdi `true`
- **Demonstreret** hvordan man gemmer den boolske værdi `false`
- **Brugt** de præcise nøgleord `true` og `false` (ingen citationstegn nødvendig)
- **Forberedt** disse variable til brug i betingede udsagn
✅ En variabel kan betragtes som 'truthy', hvis den evalueres til en boolean `true`. Interessant nok er [alle værdier i JavaScript truthy, medmindre de er defineret som falsy](https://developer.mozilla.org/docs/Glossary/Truthy).
✅ En variabel kan betragtes som 'truthy', hvis den evalueres til en boolsk `true`. Interessant nok er [alle værdier truthy undtagen dem, der er defineret som falsy](https://developer.mozilla.org/docs/Glossary/Truthy) i JavaScript.
```mermaid
flowchart LR
A["🔘 Booleske værdier"] --> B["true"]
A --> C["false"]
D["Sande værdier"] --> D1["'hello'"]
D --> D2["42"]
D --> D3["[]"]
D --> D4["{}"]
E["Falske værdier"] --> E1["false"]
E --> E2["0"]
E --> E3["''"]
E --> E4["null"]
E --> E5["undefined"]
E --> E6["NaN"]
style B fill:#e8f5e8
style C fill:#ffebee
style D fill:#e3f2fd
style E fill:#fff3e0
```
### 🎯 **Boolean logik test: Beslutningstagningsevner**
**Test din forståelse af booleans:**
- Hvorfor tror du, JavaScript har "truthy" og "falsy" værdier ud over blot `true` og `false`?
- Kan du forudsige, hvilken af disse er falsy: `0`, `"0"`, `[]`, `"false"`?
- Hvordan kan booleans være nyttige til at kontrollere programflow?
```mermaid
pie title "Almindelige anvendelsestilfælde for booleske værdier"
"Betinget logik" : 40
"Brugerstatus" : 25
"Funktionstoggles" : 20
"Validering" : 15
```
> **Husk**: I JavaScript er kun 6 værdier falsy: `false`, `0`, `""`, `null`, `undefined`, og `NaN`. Alt andet er truthy!
---
## GitHub Copilot Agent Challenge 🚀
## 📊 **Dit data-type værktøjssæt - resume**
```mermaid
graph TD
A["🎯 JavaScript Datatyper"] --> B["📦 Variable"]
A --> C["🔢 Tal"]
A --> D["📝 Strenge"]
A --> E["🔘 Booleske"]
B --> B1["let muterbar"]
B --> B2["const uforanderlig"]
C --> C1["42, 3.14, -5"]
C --> C2["+ - * / %"]
D --> D1["'anførselstegn' eller \\\"anførselstegn\\\""]
D --> D2["`skabelonstrenge`"]
E --> E1["sand eller falsk"]
E --> E2["sand vs falsk"]
F["⚡ Nøglebegreber"] --> F1["Type er vigtig for operationer"]
F --> F2["JavaScript er dynamisk typet"]
F --> F3["Variabler kan ændre type"]
F --> F4["Navngivning er casesensitiv"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at fuldføre den følgende udfordring:
**Beskrivelse:** Opret en personlig informationsmanager, der demonstrerer alle de JavaScript-datatyper, du har lært i denne lektion, mens du håndterer scenarier med data fra den virkelige verden.
**Beskrivelse:** Opret en personlig informationsmanager, der demonstrerer alle de JavaScript-datatyper, du har lært i denne lektion, mens den håndterer virkelige data scenarier.
**Opgave:** Byg et JavaScript-program, der opretter et brugerprofilobjekt, som indeholder: en persons navn (string), alder (number), status som studerende (boolean), yndlingsfarver som en array, og en adresseobjekt med egenskaberne gade, by og postnummer. Inkluder funktioner til at vise profilinformationen og opdatere individuelle felter. Sørg for at demonstrere strengsammenkædning, skabelonlitteraler, aritmetiske operationer med alderen og boolean-logik for studerendes status.
**Prompt:** Byg et JavaScript-program, der opretter et brugerprofilobjekt med: en persons navn (streng), alder (nummer), status som studerende (boolean), yndlingsfarver som et array og et adresseobjekt med gade, by og postnummer-egenskaber. Inkluder funktioner til at vise profilinformationen og opdatere individuelle felter. Sørg for at demonstrere strengsammenkædning, template literals, aritmetiske operationer med alder og boolean-logik for studerendes status.
Læs mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
JavaScript har nogle adfærdsmønstre, der kan overraske udviklere. Her er et klassisk eksempel at udforske: prøv at skrive dette i din browserkonsol: `let age = 1; let Age = 2; age == Age` og observer resultatet. Det returnerer `false` kan du finde ud af hvorfor?
JavaScript har nogle adfærdsmønstre, som kan overraske udviklere. Her er et klassisk eksempel at udforske: prøv at skrive dette i din browserkonsol: `let age = 1; let Age = 2; age == Age` og observer resultatet. Det returnerer `false` kan du afgøre hvorfor?
Dette repræsenterer én af mange JavaScript-adfærdsmønstre, som er værd at forstå. Kendskab til disse quirks vil hjælpe dig med at skrive mere pålidelig kode og fejlfinde problemer mere effektivt.
Dette repræsenterer en af de mange JavaScript-adfærd, det er værd at forstå. Fortrolighed med disse finurligheder vil hjælpe dig med at skrive mere pålidelig kode og fejlfinde mere effektivt.
## Quiz efter lektionen
[Quiz efter lektionen](https://ff-quizzes.netlify.app)
[Post-lecture quiz](https://ff-quizzes.netlify.app)
## Gennemgang & Selvstudie
## Gennemgang og Selvstudie
Tag et kig på [denne liste over JavaScript-øvelser](https://css-tricks.com/snippets/javascript/) og prøv en. Hvad lærte du?
Tag et kig på [denne liste med JavaScript øvelser](https://css-tricks.com/snippets/javascript/) og prøv en. Hvad lærte du?
## Opgave
[Øvelse med datatyper](assignment.md)
[Data Types Practice](assignment.md)
## 🚀 Din JavaScript Data Types Mestringstidslinje
### ⚡ **Hvad du kan gøre inden for de næste 5 minutter**
- [ ] Åbn din browserkonsol og opret 3 variable med forskellige datatyper
- [ ] Prøv udfordringen: `let age = 1; let Age = 2; age == Age` og find ud af hvorfor det er falsk
- [ ] Øv sammenkædning af strenge med dit navn og yndlingstal
- [ ] Test hvad der sker, når du lægger et tal til en streng
### 🎯 **Hvad du kan opnå i denne time**
- [ ] Færdiggør quizzen efter lektionen og gennemgå eventuelle forvirrende koncepter
- [ ] Opret en mini lommeregner, der lægger sammen, trækker fra, ganger og dividerer to tal
- [ ] Byg en simpel navneformatterer ved hjælp af template literals
- [ ] Udforsk forskellene mellem `==` og `===` sammenligningsoperatorer
- [ ] Øv dig i at konvertere mellem forskellige datatyper
### 📅 **Din ugelange JavaScript Grundlæggende**
- [ ] Færdiggør opgaven med selvtillid og kreativitet
- [ ] Opret et personligt profilobjekt ved brug af alle lærte datatyper
- [ ] Øv dig med [JavaScript-øvelser fra CSS-Tricks](https://css-tricks.com/snippets/javascript/)
- [ ] Byg en simpel formular-valideringsfunktion ved hjælp af boolean-logik
- [ ] Eksperimenter med array- og objekt-datatyper (forhåndsvisning af kommende lektioner)
- [ ] Deltag i et JavaScript-community og stil spørgsmål om datatyper
### 🌟 **Din månedlange transformation**
- [ ] Integrer datatypologiforståelse i større programmeringsprojekter
- [ ] Forstå hvornår og hvorfor man bruger hver datatype i virkelige applikationer
- [ ] Hjælp andre begyndere med at forstå JavaScript-grundlæggende
- [ ] Byg en lille applikation, der håndterer forskellige typer brugerdata
- [ ] Udforsk avancerede datatypebegreber som typecoercion og streng lighed
- [ ] Bidrag til open source JavaScript-projekter med dokumentationsforbedringer
### 🧠 **Afsluttende Mestringskontrol af Datatyper**
**Fejr din JavaScript-grundlæggende:**
- Hvilken datatype overraskede dig mest med sin opførsel?
- Hvor tryg føler du dig ved at forklare variable versus konstanter til en ven?
- Hvad er det mest interessante, du har opdaget om JavaScripts typesystem?
- Hvilken virkelighedsnær applikation kan du forestille dig at bygge med disse grundlæggende koncepter?
```mermaid
journey
title Din JavaScript Selvtillidsrejse
section I dag
Forvirret: 3: You
Nysgerrig: 4: You
Spændt: 5: You
section Denne uge
Øver: 4: You
Forstår: 5: You
Bygger: 5: You
section Næste måned
Problemløsning: 5: You
Underviser andre: 5: You
Rigtige projekter: 5: You
```
> 💡 **Du har bygget fundamentet!** At forstå datatyper er som at lære alfabetet, før du skriver historier. Enhver JavaScript-program, du nogensinde skriver, vil bruge disse grundlæggende koncepter. Du har nu byggeklodserne til at skabe interaktive hjemmesider, dynamiske applikationer og løse virkelige problemer med kode. Velkommen til den vidunderlige verden af JavaScript! 🎉
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på originalsproget bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,57 +1,105 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "ec43b53e8e015cdabfd3ad877b3c28e5",
"translation_date": "2025-10-23T21:52:49+00:00",
"original_hash": "71f7d7dafa1c7194d79ddac87f669ff9",
"translation_date": "2026-01-06T23:28:31+00:00",
"source_file": "2-js-basics/2-functions-methods/README.md",
"language_code": "da"
}
-->
# JavaScript Grundlæggende: Metoder og Funktioner
![JavaScript Basics - Functions](../../../../translated_images/webdev101-js-functions.be049c4726e94f8b7605c36330ac42eeb5cd8ed02bcdd60fdac778174d6cb865.da.png)
![JavaScript Basics - Functions](../../../../translated_images/webdev101-js-functions.be049c4726e94f8b.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
## Quiz før lektionen
[Quiz før lektionen](https://ff-quizzes.netlify.app)
```mermaid
journey
title Din JavaScript Funktions Eventyr
section Grundlag
Funktionssyntaks: 5: You
Kald af Funktioner: 4: You
Parametre & Argumenter: 5: You
section Avancerede Begreber
Returnværdier: 4: You
Standardparametre: 5: You
Funktionskomposition: 4: You
section Moderne JavaScript
Pilfunktioner: 5: You
Anonyme Funktioner: 4: You
Højere-ordens Funktioner: 5: You
```
## For-forelæsning Quiz
[For-forelæsning quiz](https://ff-quizzes.netlify.app)
At skrive den samme kode igen og igen er en af de mest frustrerende ting ved programmering. Funktioner løser dette problem ved at lade dig pakke kode ind i genanvendelige blokke. Tænk på funktioner som de standardiserede dele, der gjorde Henry Fords samlebånd revolutionerende når du først har skabt en pålidelig komponent, kan du bruge den hvor som helst uden at skulle bygge den fra bunden igen.
At skrive den samme kode igen og igen er en af programmeringens mest almindelige frustrationer. Funktioner løser dette problem ved at lade dig pakke kode ind i genanvendelige blokke. Tænk på funktioner som de standardiserede dele, der gjorde Henry Fords samlebånd revolutionerende når du først har skabt en pålidelig komponent, kan du bruge den, hvor det end er nødvendigt uden at bygge den fra bunden.
Funktioner giver dig mulighed for at samle stykker af kode, så du kan genbruge dem i hele dit program. I stedet for at kopiere og indsætte den samme logik overalt, kan du oprette en funktion én gang og kalde den, når det er nødvendigt. Denne tilgang holder din kode organiseret og gør opdateringer meget nemmere.
Funktioner giver dig mulighed for at samle kode stykker, så du kan genbruge dem gennem hele dit program. I stedet for at kopiere og indsætte den samme logik alle steder, kan du oprette en funktion en gang og kalde den, når det er nødvendigt. Denne tilgang holder din kode organiseret og gør opdateringer meget nemmere.
I denne lektion lærer du, hvordan du opretter dine egne funktioner, sender information til dem og får nyttige resultater tilbage. Du vil opdage forskellen mellem funktioner og metoder, lære moderne syntaksmetoder og se, hvordan funktioner kan arbejde sammen med andre funktioner. Vi bygger disse koncepter trin for trin.
I denne lektion lærer du, hvordan du opretter dine egne funktioner, sender information til dem og får nyttige resultater retur. Du vil opdage forskellen mellem funktioner og metoder, lære moderne syntaksmetoder og se, hvordan funktioner kan arbejde sammen med andre funktioner. Vi bygger disse koncepter trin for trin.
[![Metoder og Funktioner](https://img.youtube.com/vi/XgKsD6Zwvlc/0.jpg)](https://youtube.com/watch?v=XgKsD6Zwvlc "Metoder og Funktioner")
[![Metoder og Funktioner](https://img.youtube.com/vi/XgKsD6Zwvlc/0.jpg)](https://youtube.com/watch?v=XgKsD6Zwvlc "Methods and Functions")
> 🎥 Klik på billedet ovenfor for en video om metoder og funktioner.
> Du kan tage denne lektion på [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-functions/?WT.mc_id=academic-77807-sagibbon)!
```mermaid
mindmap
root((JavaScript-funktioner))
Grundlæggende Begreber
Deklaration
Traditionel syntaks
Pilfunktion syntaks
Kald
Brug af parenteser
Parenteser påkrævet
Parametre
Inputværdier
Flere parametre
Standardværdier
Argumenter
Værdier sendt ind
Kan være enhver type
Returværdier
Outputdata
return-sætning
Afslut funktion
Brug resultater
Gem i variable
Kæd funktioner
Avancerede Mønstre
Højere Ordens
Funktioner som parametre
Callbacks
Anonyme
Intet navn nødvendigt
Inline-definition
```
## Funktioner
En funktion er en selvstændig blok af kode, der udfører en specifik opgave. Den indkapsler logik, som du kan udføre, når det er nødvendigt.
En funktion er en selvstændig kodeblok, der udfører en specifik opgave. Den indkapsler logik, som du kan køre, når det er nødvendigt.
I stedet for at skrive den samme kode flere gange i dit program, kan du pakke den ind i en funktion og kalde den funktion, når du har brug for det. Denne tilgang holder din kode ren og gør opdateringer meget nemmere. Tænk på, hvor besværligt det ville være at ændre logik, der var spredt over 20 forskellige steder i din kodebase.
I stedet for at skrive den samme kode flere gange i dit program, kan du pakke den ind i en funktion og kalde denne funktion, når du har brug for det. Denne tilgang holder din kode ren og gør opdateringer meget nemmere. Overvej hvor svært det ville være at vedligeholde, hvis du skulle ændre logik spredt over 20 forskellige steder i din kodebase.
Det er vigtigt at give dine funktioner beskrivende navne. En godt navngivet funktion kommunikerer dens formål klart når du ser `cancelTimer()`, forstår du straks, hvad den gør, ligesom en tydeligt mærket knap fortæller dig præcis, hvad der vil ske, når du klikker på den.
Det er vigtigt at navngive dine funktioner beskrivende. En godt navngivet funktion kommunikerer sit formål klart når du ser `cancelTimer()`, forstår du straks, hvad den gør, ligesom en klart mærket knap fortæller dig præcis, hvad der sker, når du klikker på den.
## Oprettelse og kald af en funktion
Lad os undersøge, hvordan man opretter en funktion. Syntaksen følger et konsekvent mønster:
Lad os se, hvordan man opretter en funktion. Syntaksen følger et konsekvent mønster:
```javascript
function nameOfFunction() { // function definition
// function definition/body
function nameOfFunction() { // funktionsdefinition
// funktionsdefinition/krop
}
```
Lad os bryde det ned:
- Nøgleordet `function` fortæller JavaScript "Hey, jeg opretter en funktion!"
- `function` nøgleordet fortæller JavaScript "Hey, jeg laver en funktion!"
- `nameOfFunction` er, hvor du giver din funktion et beskrivende navn
- Parenteserne `()` er, hvor du kan tilføje parametre (det kommer vi snart til)
- Parentesserne `()` er, hvor du kan tilføje parametre (det vender vi tilbage til snart)
- Krøllede parenteser `{}` indeholder den faktiske kode, der kører, når du kalder funktionen
Lad os oprette en simpel hilsningsfunktion for at se dette i praksis:
Lad os lave en simpel hilsensfunktion for at se det i praksis:
```javascript
function displayGreeting() {
@ -59,34 +107,57 @@ function displayGreeting() {
}
```
Denne funktion udskriver "Hello, world!" til konsollen. Når du først har defineret den, kan du bruge den så mange gange, som du har brug for.
Denne funktion udskriver "Hello, world!" til konsollen. Når du har defineret den, kan du bruge den så mange gange, som du har brug for.
For at udføre (eller "kalde") din funktion skal du skrive dens navn efterfulgt af parenteser. JavaScript tillader dig at definere din funktion før eller efter, du kalder den JavaScript-motoren håndterer eksekveringsrækkefølgen.
For at udføre (eller "kalde") din funktion, skriv dens navn efterfulgt af parenteser. JavaScript tillader dig at definere din funktion før eller efter du kalder den JavaScript motoren håndterer udførelsesrækkefølgen.
```javascript
// calling our function
// kalder vores funktion
displayGreeting();
```
Når du kører denne linje, udfører den al koden inde i din `displayGreeting`-funktion og viser "Hello, world!" i din browsers konsol. Du kan kalde denne funktion gentagne gange.
> **Note:** Du har brugt **metoder** gennem hele disse lektioner. `console.log()` er en metode i bund og grund en funktion, der tilhører `console`-objektet. Den væsentlige forskel er, at metoder er knyttet til objekter, mens funktioner står selvstændigt. Mange udviklere bruger disse termer ombytteligt i uformelle samtaler.
Når du kører denne linje, eksekveres al koden inde i din `displayGreeting` funktion, og "Hello, world!" vises i browserens konsol. Du kan kalde denne funktion gentagne gange.
### 🧠 **Grundlæggende Funktionstjek: Byg Dine Første Funktioner**
**Lad os se, hvordan du føler dig med grundlæggende funktioner:**
- Kan du forklare, hvorfor vi bruger krøllede parenteser `{}` i funktionsdefinitioner?
- Hvad sker der, hvis du skriver `displayGreeting` uden parenteserne?
- Hvorfor kan det være nyttigt at kalde den samme funktion flere gange?
```mermaid
flowchart TD
A["✏️ Definer Funktion"] --> B["📦 Pakke kode"]
B --> C["🏷️ Giv det et Navn"]
C --> D["📞 Ring når nødvendigt"]
D --> E["🔄 Genbrug overalt"]
F["💡 Fordele"] --> F1["Ingen kode gentagelse"]
F --> F2["Nemt at vedligeholde"]
F --> F3["Klar organisering"]
F --> F4["Nemmere testning"]
style A fill:#e3f2fd
style E fill:#e8f5e8
style F fill:#fff3e0
```
> **Bemærk:** Du har brugt **metoder** gennem disse lektioner. `console.log()` er en metode i bund og grund en funktion, som tilhører `console` objektet. Den vigtigste forskel er, at metoder er bundet til objekter, mens funktioner står selvstændigt. Mange udviklere bruger disse termer i daglig tale ombytteligt.
### Bedste praksis for funktioner
Her er nogle tips til at hjælpe dig med at skrive gode funktioner:
Her er et par tips til at hjælpe dig med at skrive gode funktioner:
- Giv dine funktioner klare, beskrivende navne din fremtidige selv vil takke dig!
- Brug **camelCasing** til navne med flere ord (som `calculateTotal` i stedet for `calculate_total`)
- Giv dine funktioner klare, beskrivende navne din fremtidige version vil takke dig!
- Brug **camelCase** til flersprogede navne (f.eks. `calculateTotal` i stedet for `calculate_total`)
- Hold hver funktion fokuseret på at gøre én ting godt
## At sende information til en funktion
Vores `displayGreeting`-funktion er begrænset den kan kun vise "Hello, world!" for alle. Parametre giver os mulighed for at gøre funktioner mere fleksible og nyttige.
Vores `displayGreeting` funktion er begrænset den kan kun vise "Hello, world!" til alle. Parametre tillader os at gøre funktioner mere fleksible og nyttige.
**Parametre** fungerer som pladsholdere, hvor du kan indsætte forskellige værdier hver gang du bruger funktionen. På denne måde kan den samme funktion arbejde med forskellig information ved hver kald.
**Parametre** fungerer som pladsholdere, hvor du kan indsætte forskellige værdier hver gang, du bruger funktionen. På den måde kan den samme funktion arbejde med forskellig information ved hvert kald.
Du angiver parametre inden for parenteserne, når du definerer din funktion, og adskiller flere parametre med kommaer:
Du opremser parametre inde i parenteserne, når du definerer din funktion, adskilt med kommaer, hvis der er flere:
```javascript
function name(param, param2, param3) {
@ -94,9 +165,9 @@ function name(param, param2, param3) {
}
```
Hver parameter fungerer som en pladsholder når nogen kalder din funktion, giver de faktiske værdier, der indsættes i disse pladser.
Hver parameter fungerer som en pladsholder når nogen kalder din funktion, giver de egentlige værdier, der sættes ind i disse pladsholdere.
Lad os opdatere vores hilsningsfunktion til at acceptere en persons navn:
Lad os opdatere vores hilsensfunktion, så den tager imod et navn:
```javascript
function displayGreeting(name) {
@ -105,22 +176,44 @@ function displayGreeting(name) {
}
```
Bemærk, hvordan vi bruger backticks (`` ` ``) og `${}` til at indsætte navnet direkte i vores besked dette kaldes en template literal, og det er en virkelig praktisk måde at bygge strenge med variabler blandet ind.
Bemærk hvordan vi bruger backticks (`` ` ``) og `${}` til at indsætte navnet direkte i vores besked dette kaldes en template literal, og det er en rigtig praktisk måde at bygge strenge med variable blandet ind.
Nu, når vi kalder vores funktion, kan vi sende ethvert navn:
Nu kan vi, når vi kalder vores funktion, sende et hvilket som helst navn:
```javascript
displayGreeting('Christopher');
// displays "Hello, Christopher!" when run
// viser "Hej, Christopher!" når det køres
```
JavaScript tager strengen `'Christopher'`, tildeler den til parameteren `name` og skaber den personlige besked "Hello, Christopher!"
JavaScript tager strengen `'Christopher'`, tildeler den til `name` parameteren og skaber den personlige besked "Hello, Christopher!"
```mermaid
flowchart LR
A["🎯 Funktionskald"] --> B["📥 Parametre"]
B --> C["⚙️ Funktionsindhold"]
C --> D["📤 Resultat"]
A1["displayGreeting('Alice')"] --> A
B1["name = 'Alice'"] --> B
C1["Skabelonliteral\n\`Hej, \${name}!\`"] --> C
D1["'Hej, Alice!'"] --> D
E["🔄 Parametertyper"] --> E1["Strenge"]
E --> E2["Tal"]
E --> E3["Booleske"]
E --> E4["Objekter"]
E --> E5["Funktioner"]
style A fill:#e3f2fd
style C fill:#e8f5e8
style D fill:#fff3e0
style E fill:#f3e5f5
```
## Standardværdier
Hvad hvis vi vil gøre nogle parametre valgfrie? Det er her, standardværdier kommer til nytte!
Hvad hvis vi vil gøre nogle parametre valgfrie? Det er her standardværdier kommer ind i billedet!
Lad os sige, at vi vil give folk mulighed for at tilpasse hilsningsordet, men hvis de ikke angiver et, bruger vi bare "Hello" som en reserve. Du kan opsætte standardværdier ved at bruge lighedstegnet, ligesom når du sætter en variabel:
Lad os sige, at vi vil lade folk tilpasse hilsens ord, men hvis de ikke angiver noget, bruger vi bare "Hello" som en standard. Du kan opsætte standardværdier ved at bruge lighedstegn, ligesom når du sætter en variabel:
```javascript
function displayGreeting(name, salutation='Hello') {
@ -128,35 +221,63 @@ function displayGreeting(name, salutation='Hello') {
}
```
Her er `name` stadig påkrævet, men `salutation` har en reserveværdi på `'Hello'`, hvis ingen angiver en anden hilsen.
Her er `name` stadig påkrævet, men `salutation` har en backup værdi på `'Hello'`, hvis ingen giver en anden hilsen.
Nu kan vi kalde denne funktion på to forskellige måder:
```javascript
displayGreeting('Christopher');
// displays "Hello, Christopher"
// viser "Hej, Christopher"
displayGreeting('Christopher', 'Hi');
// displays "Hi, Christopher"
// viser "Hej, Christopher"
```
Ved det første kald bruger JavaScript standardværdien "Hello", da vi ikke angav en hilsen. Ved det andet kald bruger den vores tilpassede "Hi" i stedet. Denne fleksibilitet gør funktioner tilpasningsdygtige til forskellige scenarier.
Ved det første kald bruger JavaScript standarden "Hello", da vi ikke har angivet en hilsen. Ved det andet kald bruger den vores brugerdefinerede "Hi" i stedet. Denne fleksibilitet gør funktioner tilpasningsdygtige til forskellige scenarier.
### 🎛️ **Parametre Mester Check: Gør Funktioner Fleksible**
**Test din forståelse af parametre:**
- Hvad er forskellen mellem en parameter og et argument?
- Hvorfor er standardværdier nyttige i programmering i den virkelige verden?
- Kan du forudsige, hvad der sker, hvis du sender flere argumenter end parametre?
```mermaid
stateDiagram-v2
[*] --> NoParams: function greet() {}
[*] --> WithParams: function greet(name) {}
[*] --> WithDefaults: function greet(name, greeting='Hi') {}
NoParams --> Static: Samme output altid
WithParams --> Dynamic: Ændrer sig med input
WithDefaults --> Flexible: Valgfri tilpasning
Static --> [*]
Dynamic --> [*]
Flexible --> [*]
note right of WithDefaults
Mest fleksible tilgang
Bagudkompatibel
end note
```
> **Pro tip**: Standardparametre gør dine funktioner mere brugervenlige. Brugere kan hurtigt komme i gang med fornuftige standardindstillinger, men kan stadig tilpasse, når det er nødvendigt!
## Returneringsværdier
## Returnerede værdier
Vores funktioner indtil videre har bare udskrevet beskeder til konsollen, men hvad hvis du vil have en funktion til at beregne noget og give dig resultatet tilbage?
Vores funktioner hidtil har bare udskrevet beskeder til konsollen, men hvad hvis du ønsker, at en funktion skal beregne noget og give dig resultatet tilbage?
Det er her **returneringsværdier** kommer ind. I stedet for bare at vise noget kan en funktion give dig en værdi, som du kan gemme i en variabel eller bruge i andre dele af din kode.
Det er her **returnerede værdier** kommer ind i billedet. I stedet for bare at vise noget, kan en funktion give dig en værdi tilbage, som du kan gemme i en variabel eller bruge andre steder i din kode.
For at sende en værdi tilbage bruger du nøgleordet `return` efterfulgt af det, du vil returnere:
For at sende en værdi tilbage bruger du nøgleordet `return` efterfulgt af det, du ønsker at returnere:
```javascript
return myVariable;
```
Her er noget vigtigt: når en funktion rammer en `return`-sætning, stopper den straks med at køre og sender den værdi tilbage til den, der kaldte den.
Her er noget vigtigt: Når en funktion rammer en `return` erklæring, stopper den øjeblikkeligt med at køre og sender den værdi tilbage til den, som kaldte den.
Lad os ændre vores hilsningsfunktion til at returnere beskeden i stedet for at udskrive den:
Lad os ændre vores hilsensfunktion, så den returnerer beskeden i stedet for at udskrive den:
```javascript
function createGreetingMessage(name) {
@ -165,43 +286,81 @@ function createGreetingMessage(name) {
}
```
Nu, i stedet for at udskrive hilsningen, skaber denne funktion beskeden og giver den tilbage til os.
Nu skaber denne funktion beskeden og sender den tilbage til os i stedet for at udskrive den.
For at bruge den returnerede værdi kan vi gemme den i en variabel, ligesom enhver anden værdi:
For at bruge den returnerede værdi kan vi gemme den i en variabel ligesom enhver anden værdi:
```javascript
const greetingMessage = createGreetingMessage('Christopher');
```
Nu indeholder `greetingMessage` "Hello, Christopher", og vi kan bruge den hvor som helst i vores kode til at vise den på en webside, inkludere den i en e-mail eller sende den til en anden funktion.
Nu indeholder `greetingMessage` "Hello, Christopher" og vi kan bruge den hvor som helst i vores kode til at vise den på en webside, inkludere den i en email eller sende den til en anden funktion.
```mermaid
flowchart TD
A["🔧 Funktionsbehandling"] --> B{"return sætning?"}
B -->|Ja| C["📤 Returner værdi"]
B -->|Nej| D["📭 Returner undefined"]
C --> E["💾 Gem i variabel"]
C --> F["🔗 Brug i udtryk"]
C --> G["📞 Send til funktion"]
D --> H["⚠️ Normalt ikke nyttigt"]
I["📋 Anvendelser af return værdi"] --> I1["Beregn resultater"]
I --> I2["Valider input"]
I --> I3["Transformer data"]
I --> I4["Opret objekter"]
style C fill:#e8f5e8
style D fill:#ffebee
style I fill:#e3f2fd
```
### 🔄 **Returnerede Værdier Check: Få Resultater Tilbage**
**Evaluer din forståelse af returnerede værdier:**
- Hvad sker der med kode efter en `return` erklæring i en funktion?
- Hvorfor er det ofte bedre at returnere værdier end bare at udskrive til konsollen?
- Kan en funktion returnere forskellige typer værdier (streng, tal, boolsk)?
```mermaid
pie title "Almindelige returværdityper"
"Strenge" : 30
"Tal" : 25
"Objekter" : 20
"Booleske værdier" : 15
"Arrays" : 10
```
> **Vigtig indsigt**: Funktioner, der returnerer værdier, er mere alsidige, fordi den, der kalder funktionen, beslutter, hvad der skal gøres med resultatet. Dette gør din kode mere modulær og genanvendelig!
## Funktioner som parametre for funktioner
Funktioner kan sendes som parametre til andre funktioner. Selvom dette koncept kan virke komplekst i starten, er det en kraftfuld funktion, der muliggør fleksible programmeringsmønstre.
Funktioner kan sendes som parametre til andre funktioner. Selvom dette koncept kan virke komplekst til at starte med, er det en kraftfuld funktion, der muliggør fleksible programmeringsmønstre.
Dette mønster er meget almindeligt, når du vil sige "når noget sker, gør dette andet." For eksempel, "når timeren er færdig, kør denne kode" eller "når brugeren klikker på knappen, kald denne funktion."
Dette mønster er super almindeligt, når du vil sige "når noget sker, gør dette andet." For eksempel "når timeren slutter, kør denne kode" eller "når brugeren klikker på knappen, kald denne funktion."
Lad os se på `setTimeout`, som er en indbygget funktion, der venter en vis tid og derefter kører noget kode. Vi skal fortælle den, hvilken kode der skal køres perfekt brugsscenarie til at sende en funktion!
Lad os se på `setTimeout`, som er en indbygget funktion, der venter et bestemt tidsrum og så kører noget kode. Vi skal fortælle den, hvilken kode den skal køre perfekt brug af at sende en funktion!
Prøv denne kode efter 3 sekunder vil du se en besked:
Prøv denne kode efter 3 sekunder ser du en besked:
```javascript
function displayDone() {
console.log('3 seconds has elapsed');
}
// timer value is in milliseconds
// timer værdien er i millisekunder
setTimeout(displayDone, 3000);
```
Bemærk, hvordan vi sender `displayDone` (uden parenteser) til `setTimeout`. Vi kalder ikke funktionen selv vi giver den videre til `setTimeout` og siger "kald denne om 3 sekunder."
Bemærk hvordan vi sender `displayDone` (uden parenteser) til `setTimeout`. Vi kalder ikke funktionen selv vi overgiver den til `setTimeout` og siger "kald denne om 3 sekunder."
### Anonyme funktioner
Nogle gange har du brug for en funktion til kun én ting og vil ikke give den et navn. Tænk over det hvis du kun bruger en funktion én gang, hvorfor så fylde din kode med et ekstra navn?
Nogle gange har du brug for en funktion til bare én ting og vil ikke give den et navn. Tænk over det hvis du kun bruger en funktion én gang, hvorfor så fylde din kode med et ekstra navn?
JavaScript lader dig oprette **anonyme funktioner** funktioner uden navne, som du kan definere lige der, hvor du har brug for dem.
JavaScript lader dig oprette **anonyme funktioner** funktioner uden navne, som du kan definere lige dér, hvor du har brug for dem.
Her er, hvordan vi kan omskrive vores timer-eksempel ved hjælp af en anonym funktion:
Sådan kan vi omskrive vores timer-eksempel med en anonym funktion:
```javascript
setTimeout(function() {
@ -209,15 +368,15 @@ setTimeout(function() {
}, 3000);
```
Dette opnår det samme resultat, men funktionen er defineret direkte inden for `setTimeout`-kaldet, hvilket eliminerer behovet for en separat funktionsdeklaration.
Dette opnår det samme resultat, men funktionen er defineret direkte inden i `setTimeout` kaldet, hvilket eliminerer behovet for en separat funktionsdeklaration.
### Fed pil-funktioner
### Fat arrow funktioner
Moderne JavaScript har en endnu kortere måde at skrive funktioner på, kaldet **pil-funktioner**. De bruger `=>` (som ligner en pil forstået?) og er super populære blandt udviklere.
Moderne JavaScript har en endnu kortere måde at skrive funktioner på kaldet **arrow functions**. De bruger `=>` (som ligner en pil forstår du?) og er super populære blandt udviklere.
Pil-funktioner lader dig springe nøgleordet `function` over og skrive mere kortfattet kode.
Arrow-funktioner lader dig springe `function` nøgleordet over og skrive mere koncis kode.
Her er vores timer-eksempel ved hjælp af en pil-funktion:
Her er vores timer-eksempel med en arrow-funktion:
```javascript
setTimeout(() => {
@ -225,11 +384,64 @@ setTimeout(() => {
}, 3000);
```
`()` er, hvor parametre ville gå (tom i dette tilfælde), derefter kommer pilen `=>`, og til sidst funktionskroppen i krøllede parenteser. Dette giver samme funktionalitet med mere kortfattet syntaks.
### Hvornår skal man bruge hver strategi
Hvornår skal du bruge hver tilgang? En praktisk retningslinje: Hvis du vil bruge funktionen flere gange, skal du give den et navn og definere den separat. Hvis det er til én specifik brug, kan du overveje en anonym funktion. Både pil-funktioner og traditionel syntaks er gyldige valg, selvom pil-funktioner er udbredte i moderne JavaScript-kodebaser.
`()` er hvor parametrene går (tomt i dette tilfælde), derefter kommer pilen `=>`, og til sidst funktionskroppen i krøllede parenteser. Dette giver den samme funktionalitet med en mere koncis syntaks.
```mermaid
flowchart LR
A["📝 Funktionsstile"] --> B["Traditionel"]
A --> C["Pil"]
A --> D["Anonym"]
B --> B1["function navn() {}"]
B --> B2["Løftet"]
B --> B3["Navngivet"]
C --> C1["const navn = () => {}"]
C --> C2["Konkis syntax"]
C --> C3["Moderne stil"]
D --> D1["function() {}"]
D --> D2["Intet navn"]
D --> D3["Engangsbrug"]
E["⏰ Hvornår man skal bruge"] --> E1["Traditionel: Genanvendelige funktioner"]
E --> E2["Pil: Korte tilbagekald"]
E --> E3["Anonym: Event-håndterere"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
### Hvornår skal man bruge hvilken strategi
Hvornår skal du bruge hvilken tilgang? En praktisk tommelfingerregel: Hvis du vil bruge funktionen flere gange, giv den et navn og definer den separat. Hvis det er til ét specifikt formål, kan du overveje en anonym funktion. Både arrow-funktioner og traditionel syntaks er gyldige valg, selvom arrow-funktioner er udbredte i moderne JavaScript-kodebaser.
### 🎨 **Funktion Styles Mester Check: Vælg den Rigtige Syntaks**
**Test din syntaksforståelse:**
- Hvornår foretrækker du måske arrow functions fremfor traditionel funktionssyntaks?
- Hvad er hovedfordelen ved anonyme funktioner?
- Kan du tænke på en situation, hvor en navngivet funktion er bedre end en anonym?
```mermaid
quadrantChart
title Beslutningsmatrix for Funktionsvalg
x-axis Simpel --> Kompleks
y-axis Engangsbrug --> Genanvendelig
quadrant-1 Pilefunktioner
quadrant-2 Navngivne Funktioner
quadrant-3 Anonyme Funktioner
quadrant-4 Traditionelle Funktioner
Event Handlers: [0.3, 0.2]
Utility Functions: [0.7, 0.8]
Callbacks: [0.2, 0.3]
Class Methods: [0.8, 0.7]
Mathematical Operations: [0.4, 0.6]
```
> **Moderne trend**: Arrow functions bliver standardvalget for mange udviklere på grund af deres koncise syntaks, men traditionelle funktioner har stadig deres plads!
---
@ -237,35 +449,135 @@ Hvornår skal du bruge hver tilgang? En praktisk retningslinje: Hvis du vil brug
## 🚀 Udfordring
Kan du formulere forskellen mellem funktioner og metoder i én sætning? Prøv det!
Kan du formulere forskellen på funktioner og metoder i én sætning? Giv det et forsøg!
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Opret et hjælpebibliotek med matematiske funktioner, der demonstrerer forskellige funktionelle koncepter, der er dækket i denne lektion, herunder parametre, standardværdier, returneringsværdier og pil-funktioner.
**Beskrivelse:** Opret et hjælpemiddel bibliotek af matematiske funktioner, der demonstrerer forskellige funktionskoncepter dækket i denne lektion, inklusive parametre, standardværdier, returnerede værdier og arrow funktioner.
**Opgave:** Opret en JavaScript-fil kaldet `mathUtils.js`, der indeholder følgende funktioner:
1. En funktion `add`, der tager to parametre og returnerer deres sum
2. En funktion `multiply` med standardparameter-værdier (anden parameter har som standard værdien 1)
3. En pil-funktion `square`, der tager et tal og returnerer dets kvadrat
4. En funktion `calculate`, der accepterer en anden funktion som parameter og to tal, og derefter anvender funktionen på disse tal
**Prompt:** Opret en JavaScript-fil kaldet `mathUtils.js`, som indeholder følgende funktioner:
1. En funktion `add` der tager to parametre og returnerer summen
2. En funktion `multiply` med standard parameter værdier (anden parameter standard til 1)
3. En arrow funktion `square`, der tager et tal og returnerer dets kvadrat
4. En funktion `calculate` som accepterer en anden funktion som parameter og to tal, og derefter anvender funktionen på tallene
5. Demonstrer kald af hver funktion med passende testcases
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## Quiz efter lektionen
[Quiz efter lektionen](https://ff-quizzes.netlify.app)
## Efter-forelæsning Quiz
[Efter-forelæsning quiz](https://ff-quizzes.netlify.app)
## Gennemgang & Selvstudie
## Gennemgang & Selvstudium
Det er værd at [læse lidt mere om pil-funktioner](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions), da de i stigende grad bruges i kodebaser. Øv dig i at skrive en funktion og derefter omskrive den med denne syntaks.
Det er værd at [læse lidt mere om arrow-funktioner](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions), da de i stigende grad bruges i kodebaser. Øv dig i at skrive en funktion og derefter omskrive den med denne syntaks.
## Opgave
[Leg med Funktioner](assignment.md)
[Sjov med Funktioner](assignment.md)
---
## 🧰 **Dit JavaScript Funktionsværktøjssæt Resumé**
```mermaid
graph TD
A["🎯 JavaScript Funktioner"] --> B["📋 Funktionsdeklaration"]
A --> C["📥 Parametre"]
A --> D["📤 Returværdier"]
A --> E["🎨 Moderne Syntaks"]
B --> B1["function navn() {}"]
B --> B2["Beskrivende navngivning"]
B --> B3["Genanvendelige kodeblokke"]
C --> C1["Input data"]
C --> C2["Standardværdier"]
C --> C3["Flere parametre"]
D --> D1["return sætning"]
D --> D2["Afslut funktion"]
D --> D3["Send data tilbage"]
E --> E1["Pilestrengsfunktioner: () =>"]
E --> E2["Anonyme funktioner"]
E --> E3["Højereordensfunktioner"]
F["⚡ Nøglefordele"] --> F1["Kodegenbrug"]
F --> F2["Bedre organisering"]
F --> F3["Nemmere testning"]
F --> F4["Modulært design"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 Din JavaScript Funktionsmester Tidslinje
### ⚡ **Hvad du kan gøre i de næste 5 minutter**
- [ ] Skriv en simpel funktion, der returnerer dit yndlingstal
- [ ] Opret en funktion med to parametre, der lægger dem sammen
- [ ] Prøv at konvertere en traditionel funktion til arrow function-syntaks
- [ ] Øv udfordringen: forklar forskellen mellem funktioner og metoder
### 🎯 **Hvad du kan nå denne time**
- [ ] Færdiggør quizzen efter lektionen og gennemgå eventuelle forvirrende begreber
- [ ] Byg matematikværktøjsbiblioteket fra GitHub Copilot-udfordringen
- [ ] Opret en funktion, der bruger en anden funktion som parameter
- [ ] Øv dig i at skrive funktioner med standardparametre
- [ ] Eksperimenter med template literals i funktionsreturværdier
### 📅 **Din uge-lange funktionsmestring**
- [ ] Færdiggør opgaven "Sjov med funktioner" med kreativitet
- [ ] Refaktorer noget gentaget kode, du har skrevet, til genanvendelige funktioner
- [ ] Byg en lille lommeregner kun ved hjælp af funktioner (ingen globale variabler)
- [ ] Øv arrow functions med array-metoder som `map()` og `filter()`
- [ ] Opret en samling af hjælpefunktioner til almindelige opgaver
- [ ] Studer højereordensfunktioner og funktionelle programmeringskoncepter
### 🌟 **Din månedslange transformation**
- [ ] Mestre avancerede funktionskoncepter som closures og scope
- [ ] Byg et projekt, der i høj grad bruger funktionskomposition
- [ ] Bidrag til open source ved at forbedre funktionsdokumentation
- [ ] Lær en anden om funktioner og forskellige syntaksstile
- [ ] Udforsk funktionelle programmeringsparadigmer i JavaScript
- [ ] Opret et personligt bibliotek af genanvendelige funktioner til fremtidige projekter
### 🏆 **Endelig funktionsmester tjek-ind**
**Fejr din funktionsmestring:**
- Hvad er den mest nyttige funktion, du har skabt indtil nu?
- Hvordan har det at lære om funktioner ændret din måde at tænke på kodeorganisation?
- Hvilken funktionssyntaks foretrækker du og hvorfor?
- Hvilket virkeligt problem ville du løse ved at skrive en funktion?
```mermaid
journey
title Din Funktion Tillidsudvikling
section I dag
Forvirret over Syntax: 3: You
Forståelse af Grundlæggende: 4: You
Skriver Enkle Funktioner: 5: You
section Denne Uge
Brug af Parametre: 4: You
Returnerer Værdier: 5: You
Moderne Syntax: 5: You
section Næste Måned
Funktionssammensætning: 5: You
Avancerede Mønstre: 5: You
Underviser Andre: 5: You
```
> 🎉 **Du har mestret et af programmeringens mest magtfulde koncepter!** Funktioner er byggestenene til større programmer. Enhver applikation, du nogensinde bygger, vil bruge funktioner til at organisere, genbruge og strukturere kode. Du forstår nu, hvordan du pakker logik ind i genanvendelige komponenter, hvilket gør dig til en mere effektiv og dygtig programmør. Velkommen til verden af modulær programmering! 🚀
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For vigtig information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,149 +1,295 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "90a3c32c3377f83ab750c2447c77ab98",
"translation_date": "2025-10-23T21:53:51+00:00",
"original_hash": "c688385d15dd3645e924ea0ffee8967f",
"translation_date": "2026-01-06T23:31:20+00:00",
"source_file": "2-js-basics/3-making-decisions/README.md",
"language_code": "da"
}
-->
# JavaScript Grundlæggende: At træffe beslutninger
# JavaScript Basics: At træffe beslutninger
![JavaScript Grundlæggende - At træffe beslutninger](../../../../translated_images/webdev101-js-decisions.69e1b20f272dd1f0b1cb2f8adaff3ed2a77c4f91db96d8a0594132a353fa189a.da.png)
![JavaScript Basics - Making decisions](../../../../translated_images/webdev101-js-decisions.69e1b20f272dd1f0.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
Har du nogensinde undret dig over, hvordan applikationer træffer smarte beslutninger? Som hvordan et navigationssystem vælger den hurtigste rute, eller hvordan en termostat beslutter, hvornår den skal tænde for varmen? Dette er det grundlæggende koncept for beslutningstagning i programmering.
```mermaid
journey
title Din JavaScript Beslutningstagningseventyr
section Grundlag
Boolean Værdier: 5: You
Sammenligningsoperatorer: 4: You
Logisk Tænkning: 5: You
section Grundlæggende Beslutninger
If Udsagn: 4: You
If-Ellers Logik: 5: You
Switch Udsagn: 4: You
section Avanceret Logik
Logiske Operatorer: 5: You
Komplekse Betingelser: 4: You
Ternære Udtryk: 5: You
```
Har du nogensinde spekuleret på, hvordan applikationer træffer smarte beslutninger? Som hvordan et navigationssystem vælger den hurtigste rute, eller hvordan en termostat beslutter, hvornår varmen skal tændes? Dette er det grundlæggende koncept bag beslutningstagning i programmering.
Ligesom Charles Babbages Analytical Engine blev designet til at følge forskellige sekvenser af operationer baseret på betingelser, skal moderne JavaScript-programmer træffe valg baseret på skiftende omstændigheder. Denne evne til at forgrene sig og træffe beslutninger er det, der forvandler statisk kode til responsive, intelligente applikationer.
Ligesom Charles Babbages Analytical Engine var designet til at følge forskellige sekvenser af operationer baseret på betingelser, skal moderne JavaScript-programmer træffe valg baseret på varierende omstændigheder. Denne evne til at forgrene sig og træffe beslutninger er, hvad der forvandler statisk kode til responsive, intelligente applikationer.
I denne lektion lærer du, hvordan du implementerer betinget logik i dine programmer. Vi vil udforske betingede udsagn, sammenligningsoperatorer og logiske udtryk, der gør det muligt for din kode at evaluere situationer og reagere passende.
I denne lektion lærer du, hvordan du implementerer betinget logik i dine programmer. Vi vil udforske betingede udsagn, sammenligningsoperatorer og logiske udtryk, der giver din kode mulighed for at evaluere situationer og reagere passende.
## Quiz før lektionen
## For-forelæsning quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/11)
[For-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/11)
Evnen til at træffe beslutninger og kontrollere programflow er en grundlæggende del af programmering. Dette afsnit dækker, hvordan du styrer eksekveringsvejen for dine JavaScript-programmer ved hjælp af Boolean-værdier og betinget logik.
Evnen til at træffe beslutninger og styre programflow er en grundlæggende del af programmering. Dette afsnit dækker, hvordan du styrer udførelsesvejen for dine JavaScript-programmer ved hjælp af booleske værdier og betinget logik.
[![At træffe beslutninger](https://img.youtube.com/vi/SxTp8j-fMMY/0.jpg)](https://youtube.com/watch?v=SxTp8j-fMMY "At træffe beslutninger")
[![Making Decisions](https://img.youtube.com/vi/SxTp8j-fMMY/0.jpg)](https://youtube.com/watch?v=SxTp8j-fMMY "Making Decisions")
> 🎥 Klik på billedet ovenfor for en video om at træffe beslutninger.
> Du kan tage denne lektion på [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-if-else/?WT.mc_id=academic-77807-sagibbon)!
```mermaid
mindmap
root((Beslutningstagning))
Boolean Logic
sandt/falsk
Sammenligningsresultater
Logiske udtryk
Betingede Udsagn
if-udsagn
Enkel betingelse
Kodeudførelse
if-else
To veje
Alternative handlinger
switch
Flere muligheder
Ryd struktur
Operatorer
Sammenligning
=== !== < > <= >=
Værdi relationer
Logisk
&& || !
Kombiner betingelser
Avancerede Mønstre
Ternær
? : syntaks
Inline beslutninger
Kompleks Logik
Indlejrede betingelser
Flere kriterier
```
## En kort opsummering af Booleans
Før vi udforsker beslutningstagning, lad os genbesøge Boolean-værdier fra vores tidligere lektion. Opkaldt efter matematikeren George Boole repræsenterer disse værdier binære tilstande enten `true` eller `false`. Der er ingen tvetydighed, ingen mellemvej.
Før vi udforsker beslutningstagning, lad os genbesøge booleske værdier fra vores tidligere lektion. Navngivet efter matematikeren George Boole, repræsenterer disse værdier binære tilstande enten `true` eller `false`. Der er ingen tvetydighed, intet midtpunkt.
Disse binære værdier danner grundlaget for al beregningslogik. Hver beslutning, dit program træffer, reduceres i sidste ende til en Boolean-evaluering.
Disse binære værdier danner fundamentet for al beregningslogik. Hver beslutning, dit program træffer, reduceres til sidst til en boolesk evaluering.
At oprette Boolean-variabler er ligetil:
At oprette booleske variable er ligetil:
```javascript
let myTrueBool = true;
let myFalseBool = false;
```
Dette opretter to variabler med eksplicitte Boolean-værdier.
Dette opretter to variable med eksplicitte booleske værdier.
✅ Booleans er opkaldt efter den engelske matematiker, filosof og logiker George Boole (18151864).
## Sammenligningsoperatorer og Booleans
I praksis vil du sjældent manuelt indstille Boolean-værdier. I stedet genererer du dem ved at evaluere betingelser: "Er dette tal større end det andet?" eller "Er disse værdier ens?"
I praksis vil du sjældent sætte booleske værdier manuelt. I stedet genererer du dem ved at evaluere betingelser: "Er dette tal større end det andet?" eller "Er disse værdier ens?"
Sammenligningsoperatorer muliggør disse evalueringer. De sammenligner værdier og returnerer Boolean-resultater baseret på forholdet mellem operandene.
Sammenligningsoperatorer muliggør disse evalueringer. De sammenligner værdier og returnerer booleske resultater baseret på forholdet mellem operanderne.
| Symbol | Beskrivelse | Eksempel |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
| `<` | **Mindre end**: Sammenligner to værdier og returnerer `true` Boolean-datatype, hvis værdien på venstre side er mindre end højre | `5 < 6 // true` |
| `<=` | **Mindre end eller lig med**: Sammenligner to værdier og returnerer `true` Boolean-datatype, hvis værdien på venstre side er mindre end eller lig med højre | `5 <= 6 // true` |
| `>` | **Større end**: Sammenligner to værdier og returnerer `true` Boolean-datatype, hvis værdien på venstre side er større end højre | `5 > 6 // false` |
| `>=` | **Større end eller lig med**: Sammenligner to værdier og returnerer `true` Boolean-datatype, hvis værdien på venstre side er større end eller lig med højre | `5 >= 6 // false` |
| `===` | **Streng lighed**: Sammenligner to værdier og returnerer `true` Boolean-datatype, hvis værdierne på højre og venstre er ens OG har samme datatype. | `5 === 6 // false` |
| `!==` | **Ulighed**: Sammenligner to værdier og returnerer den modsatte Boolean-værdi af, hvad en streng lighedsoperator ville returnere | `5 !== 6 // true` |
✅ Test din viden ved at skrive nogle sammenligninger i din browsers konsol. Overrasker nogle af de returnerede data dig?
| `<` | **Mindre end**: Sammenligner to værdier og returnerer den booleske `true`, hvis værdien til venstre er mindre end værdien til højre | `5 < 6 // true` |
| `<=` | **Mindre end eller lig med**: Sammenligner to værdier og returnerer den booleske `true`, hvis værdien til venstre er mindre end eller lig med værdien til højre | `5 <= 6 // true` |
| `>` | **Større end**: Sammenligner to værdier og returnerer den booleske `true`, hvis værdien til venstre er større end værdien til højre | `5 > 6 // false` |
| `>=` | **Større end eller lig med**: Sammenligner to værdier og returnerer den booleske `true`, hvis værdien til venstre er større end eller lig med værdien til højre | `5 >= 6 // false` |
| `===` | **Streng lighed**: Sammenligner to værdier og returnerer den booleske `true`, hvis værdierne til højre og venstre er lige og af samme datatype | `5 === 6 // false` |
| `!==` | **Ulighed**: Sammenligner to værdier og returnerer det modsatte booleske resultat af, hvad en streng lighedsoperator ville returnere | `5 !== 6 // true` |
✅ Test din viden ved at skrive nogle sammenligninger i din browsers konsol. Overrasker nogen af de returnerede værdier dig?
```mermaid
flowchart LR
A["🔢 Værdier"] --> B["⚖️ Sammenligning"]
B --> C["✅ Boolesk Resultat"]
D["5"] --> E["< 6"]
E --> F["sand"]
G["10"] --> H["=== '10'"]
H --> I["falsk"]
J["'hej'"] --> K["!== 'verden'"]
K --> L["sand"]
M["📋 Operator Typer"] --> M1["Lighed: === !=="]
M --> M2["Relationel: < > <= >="]
M --> M3["Streng vs Løs"]
style A fill:#e3f2fd
style C fill:#e8f5e8
style M fill:#fff3e0
```
### 🧠 **Sammenligningsmesterskab: Forståelse af boolesk logik**
**Test din forståelse af sammenligninger:**
- Hvorfor tror du, at `===` (streng lighed) generelt foretrækkes frem for `==` (løs lighed)?
- Kan du forudsige, hvad `5 === '5'` returnerer? Hvad med `5 == '5'`?
- Hvad er forskellen mellem `!==` og `!=`?
```mermaid
stateDiagram-v2
[*] --> Comparison: To værdier
Comparison --> StrictEqual: === eller !==
Comparison --> Relational: < > <= >=
StrictEqual --> TypeCheck: Tjek type OG værdi
Relational --> NumberCompare: Konverter til tal
TypeCheck --> BooleanResult: sand eller falsk
NumberCompare --> BooleanResult
note right of StrictEqual
Foretrukken tilgang
Ingen typekonvertering
end note
note right of Relational
Nyttig for intervaller
Numeriske sammenligninger
end note
```
> **Pro tip**: Brug altid `===` og `!==` til lighedstjek, medmindre du specifikt har brug for typekonvertering. Dette forhindrer uventet adfærd!
## If-sætning
## If-udsagn
`if`-sætningen er som at stille et spørgsmål i din kode. "Hvis denne betingelse er sand, så gør dette." Det er sandsynligvis det vigtigste værktøj, du vil bruge til at træffe beslutninger i JavaScript.
`if`-udsagnet er som at stille et spørgsmål i din kode. "Hvis denne betingelse er sand, så gør dette." Det er sandsynligvis det vigtigste værktøj, du vil bruge til at træffe beslutninger i JavaScript.
Sådan fungerer det:
```javascript
if (condition) {
// Condition is true. Code in this block will run.
// Betingelsen er sand. Koden i denne blok vil køre.
}
```
Betingelsen går ind i parenteserne, og hvis den er `true`, kører JavaScript koden inde i de krøllede parenteser. Hvis den er `false`, springer JavaScript bare hele blokken over.
Betingelsen kommer inden i parenteserne, og hvis den er `true`, kører JavaScript koden inden i de krøllede parenteser. Hvis den er `false`, springer JavaScript hele blokken over.
Du vil ofte bruge sammenligningsoperatorer til at oprette disse betingelser. Lad os se et praktisk eksempel:
Du vil ofte bruge sammenligningsoperatorer til at skabe disse betingelser. Lad os se et praktisk eksempel:
```javascript
let currentMoney = 1000;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
// Condition is true. Code in this block will run.
// Betingelsen er sand. Koden i denne blok vil blive kørt.
console.log("Getting a new laptop!");
}
```
Da `1000 >= 800` evalueres til `true`, udføres koden inde i blokken, og "Får en ny bærbar computer!" vises i konsollen.
## If..Else-sætning
Da `1000 >= 800` evaluerer til `true`, kører koden inden i blokken og viser "Getting a new laptop!" i konsollen.
```mermaid
flowchart TD
A["🚀 Programstart"] --> B{"💰 currentMoney >= laptopPrice?"}
B -->|true| C["🎉 'Køber en ny laptop!'"]
B -->|false| D["⏭️ Spring kodeblok over"]
C --> E["📋 Fortsæt program"]
D --> E
F["📊 If-sætningsstruktur"] --> F1["if (condition) {"]
F1 --> F2[" // kode, der kører hvis sand"]
F2 --> F3["}"]
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#ffebee
style F fill:#e3f2fd
```
## If..Else-udsagn
Men hvad hvis du vil have, at dit program skal gøre noget andet, når betingelsen er falsk? Det er her, `else` kommer ind det er som at have en backup-plan.
Men hvad hvis du vil have dit program til at gøre noget andet, når betingelsen er falsk? Det er her, `else` kommer ind det er som at have en backupplan.
`else`-sætningen giver dig en måde at sige "hvis denne betingelse ikke er sand, gør i stedet dette andet."
`else`-udsagnet giver dig en måde at sige "hvis denne betingelse ikke er sand, så gør dette i stedet."
```javascript
let currentMoney = 500;
let laptopPrice = 800;
if (currentMoney >= laptopPrice) {
// Condition is true. Code in this block will run.
// Betingelsen er sand. Koden i dette blok vil køre.
console.log("Getting a new laptop!");
} else {
// Condition is false. Code in this block will run.
// Betingelsen er falsk. Koden i dette blok vil køre.
console.log("Can't afford a new laptop, yet!");
}
```
Nu, da `500 >= 800` er `false`, springer JavaScript den første blok over og kører i stedet `else`-blokken. Du vil se "Har ikke råd til en ny bærbar computer, endnu!" i konsollen.
✅ Test din forståelse af denne kode og den følgende kode ved at køre den i en browserkonsol. Ændr værdierne for variablerne currentMoney og laptopPrice for at ændre den returnerede `console.log()`.
Nu da `500 >= 800` er `false`, springer JavaScript den første blok over og kører `else`-blokken i stedet. Du vil se "Can't afford a new laptop, yet!" i konsollen.
✅ Test din forståelse af denne kode og følgende kode ved at køre den i en browserkonsol. Ændr værdierne af variablerne currentMoney og laptopPrice for at ændre den returnerede `console.log()`.
### 🎯 **If-Else-logik Check: Forgreningsveje**
**Evaluer din forståelse af betinget logik:**
- Hvad sker der, hvis `currentMoney` præcis er lig med `laptopPrice`?
- Kan du komme i tanke om et scenarie fra den virkelige verden, hvor if-else-logik ville være nyttig?
- Hvordan kunne du udvide dette til at håndtere flere prisniveauer?
```mermaid
flowchart TD
A["🔍 Evaluer betingelse"] --> B{"Betingelse sand?"}
B -->|Ja| C["📤 Udfør HVIS blok"]
B -->|Nej| D["📥 Udfør ELLERS blok"]
C --> E["✅ Én sti valgt"]
D --> E
F["🌐 Virkelige eksempler"] --> F1["Bruger login status"]
F --> F2["Aldersverifikation"]
F --> F3["Formularvalidering"]
F --> F4["Spiltilstandsændringer"]
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#e3f2fd
style F fill:#f3e5f5
```
> **Nøgleindsigt**: If-else sikrer, at præcis én sti vælges. Det garanterer, at dit program altid har et svar på enhver betingelse!
## Switch-sætning
## Switch-udsagn
Nogle gange skal du sammenligne én værdi med flere muligheder. Selvom du kunne kæde flere `if..else`-sætninger sammen, bliver denne tilgang uhåndterlig. `switch`-sætningen giver en mere overskuelig struktur til at håndtere flere diskrete værdier.
Nogle gange skal du sammenligne én værdi med flere muligheder. Selvom du kunne kæde flere `if..else`-udsagn sammen, bliver denne tilgang hurtigt uhåndterlig. `switch`-udsagnet giver en renere struktur til at håndtere flere diskrete værdier.
Konceptet minder om de mekaniske koblingssystemer, der blev brugt i tidlige telefoncentraler én inputværdi bestemmer, hvilken specifik vej eksekveringen følger.
Konceptet minder om de mekaniske koblingssystemer, der blev brugt i tidlige telefoncentraler én inputværdi bestemmer, hvilken specifik vej udførelsen følger.
```javascript
switch (expression) {
case x:
// code block
// kodeblok
break;
case y:
// code block
// kodeblok
break;
default:
// code block
// kodeblok
}
```
Sådan er det struktureret:
- JavaScript evaluerer udtrykket én gang
- Det gennemgår hver `case` for at finde et match
- Når det finder et match, kører det den kodeblok
- `break` fortæller JavaScript at stoppe og afslutte switchen
- Hvis ingen cases matcher, kører det `default`-blokken (hvis du har en)
- Den gennemgår hver `case` for at finde et match
- Når den finder et match, kører den den kodeblok
- `break` fortæller JavaScript at stoppe og forlade switch
- Hvis ingen cases matcher, kører den `default`-blokken (hvis du har en)
```javascript
// Program using switch statement for day of week
// Program ved hjælp af switch-sætning for ugedag
let dayNumber = 2;
let dayName;
@ -164,27 +310,95 @@ switch (dayNumber) {
console.log(`Today is ${dayName}`);
```
I dette eksempel ser JavaScript, at `dayNumber` er `2`, finder den matchende `case 2`, sætter `dayName` til "Tirsdag" og bryder derefter ud af switchen. Resultatet? "I dag er det tirsdag" bliver logget til konsollen.
I dette eksempel ser JavaScript, at `dayNumber` er `2`, finder det matchende `case 2`, sætter `dayName` til "Tuesday" og bryder derefter ud af switch. Resultatet? "Today is Tuesday" bliver logget til konsollen.
```mermaid
flowchart TD
A["📥 switch(udtryk)"] --> B["🔍 Evaluer én gang"]
B --> C{"Matcher tilfælde 1?"}
C -->|Ja| D["📋 Udfør tilfælde 1"]
C -->|Nej| E{"Matcher tilfælde 2?"}
E -->|Ja| F["📋 Udfør tilfælde 2"]
E -->|Nej| G{"Matcher tilfælde 3?"}
G -->|Ja| H["📋 Udfør tilfælde 3"]
G -->|Nej| I["📋 Udfør standard"]
D --> J["🛑 stop"]
F --> K["🛑 stop"]
H --> L["🛑 stop"]
J --> M["✅ Forlad switch"]
K --> M
L --> M
I --> M
style A fill:#e3f2fd
style B fill:#fff3e0
style M fill:#e8f5e8
```
✅ Test din forståelse af denne kode og følgende kode ved at køre dem i en browserkonsol. Ændr værdien af variablen a for at ændre det returnerede `console.log()`.
### 🔄 **Switch-udsagnsmesterskab: Flere muligheder**
✅ Test din forståelse af denne kode og den følgende kode ved at køre den i en browserkonsol. Ændr værdierne for variablen a for at ændre den returnerede `console.log()`.
**Test din forståelse af switch:**
- Hvad sker der, hvis du glemmer et `break`-udsagn?
- Hvornår bruger du `switch` i stedet for flere `if-else`-udsagn?
- Hvorfor er `default`-casen nyttig, selvom du tror, du har dækket alle muligheder?
```mermaid
pie title "Hvornår man skal bruge hver beslutningsstruktur"
"Simpel hvis-ellers" : 40
"Komplekse hvis-ellers kæder" : 25
"Switch-udsagn" : 20
"Ternære operatorer" : 15
```
> **Bedste praksis**: Brug `switch`, når du sammenligner én variabel med flere specifikke værdier. Brug `if-else` til intervalkontrol eller komplekse betingelser!
## Logiske operatorer og Booleans
Komplekse beslutninger kræver ofte evaluering af flere betingelser samtidigt. Ligesom Boolean-algebra giver matematikere mulighed for at kombinere logiske udtryk, giver programmering logiske operatorer til at forbinde flere Boolean-betingelser.
Komplekse beslutninger kræver ofte, at flere betingelser evalueres samtidigt. Ligesom boolsk algebra giver matematikere mulighed for at kombinere logiske udtryk, giver programmering logiske operatorer til at forbinde flere booleske betingelser.
Disse operatorer muliggør sofistikeret betinget logik ved at kombinere simple sand/falsk-evalueringer.
Disse operatorer muliggør sofistikeret betinget logik ved at kombinere simple sande/falske evalueringer.
| Symbol | Beskrivelse | Eksempel |
| ------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| `&&` | **Logisk OG**: Sammenligner to Boolean-udtryk. Returnerer true **kun** hvis begge sider er true | `(5 > 3) && (5 < 10) // Begge sider er true. Returnerer true` |
| `\|\|` | **Logisk ELLER**: Sammenligner to Boolean-udtryk. Returnerer true, hvis mindst én side er true | `(5 > 10) \|\| (5 < 10) // Én side er false, den anden er true. Returnerer true` |
| `!` | **Logisk IKKE**: Returnerer den modsatte værdi af et Boolean-udtryk | `!(5 > 10) // 5 er ikke større end 10, så "!" gør det true` |
| `&&` | **Logisk OG**: Sammenligner to booleske udtryk. Returnerer sandt **kun** hvis begge sider er sande | `(5 > 3) && (5 < 10) // Begge sider er sande. Returnerer true` |
| `\|\|` | **Logisk ELLER**: Sammenligner to booleske udtryk. Returnerer sandt, hvis mindst den ene side er sand | `(5 > 10) \|\| (5 < 10) // Den ene side er falsk, den anden sand. Returnerer true` |
| `!` | **Logisk IKKE**: Returnerer det modsatte af et boolesk udtryk | `!(5 > 10) // 5 er ikke større end 10, så "!" gør det sandt` |
Disse operatorer lader dig kombinere betingelser på nyttige måder:
- OG (`&&`) betyder, at begge betingelser skal være sande
- ELLER (`||`) betyder, at mindst én betingelse skal være sand
- IKKE (`!`) vender sand til falsk (og omvendt)
- IKKE (`!`) vender sandt til falsk (og omvendt)
```mermaid
flowchart LR
A["🔗 Logiske operatorer"] --> B["&& OG"]
A --> C["|| ELLER"]
A --> D["! IKKE"]
B --> B1["Begge skal være sande"]
B --> B2["true && true = true"]
B --> B3["true && false = false"]
C --> C1["Mindst én sand"]
C --> C2["true || false = true"]
C --> C3["false || false = false"]
D --> D1["Vender værdien"]
D --> D2["!true = false"]
D --> D3["!false = true"]
E["🌍 Virkelige eksempler"] --> E1["Alder >= 18 && harKørekort"]
E --> E2["erWeekend || erHelligdag"]
E --> E3["!erLoggetInd"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
## Betingelser og beslutninger med logiske operatorer
Lad os se disse logiske operatorer i aktion med et mere realistisk eksempel:
@ -192,42 +406,70 @@ Lad os se disse logiske operatorer i aktion med et mere realistisk eksempel:
```javascript
let currentMoney = 600;
let laptopPrice = 800;
let laptopDiscountPrice = laptopPrice - (laptopPrice * 0.2); // Laptop price at 20 percent off
let laptopDiscountPrice = laptopPrice - (laptopPrice * 0.2); // Laptoppris med 20 procent rabat
if (currentMoney >= laptopPrice || currentMoney >= laptopDiscountPrice) {
// Condition is true. Code in this block will run.
// Tilstanden er sand. Koden i denne blok vil køre.
console.log("Getting a new laptop!");
} else {
// Condition is false. Code in this block will run.
// Tilstanden er falsk. Koden i denne blok vil køre.
console.log("Can't afford a new laptop, yet!");
}
```
I dette eksempel: vi beregner en rabatpris på 20% (640), og evaluerer derefter, om vores tilgængelige midler dækker enten den fulde pris ELLER rabatprisen. Da 600 opfylder rabatprisgrænsen på 640, evalueres betingelsen til sand.
I dette eksempel: beregner vi en 20% rabatpris (640), og evaluerer derefter, om vores tilgængelige midler dækker enten fuld pris ELLER rabatprisen. Da 600 opfylder rabatprisens tærskel på 640, evalueres betingelsen til sand.
### 🧮 **Logiske operatorer Check: Kombinere betingelser**
**Test din forståelse af logiske operatorer:**
- I udtrykket `A && B`, hvad sker der, hvis A er falsk? Bliver B overhovedet evalueret?
- Kan du tænke på en situation, hvor du ville bruge alle tre operatorer (&&, ||, !) sammen?
- Hvad er forskellen på `!user.isActive` og `user.isActive !== true`?
```mermaid
stateDiagram-v2
[*] --> EvaluateA: A && B
EvaluateA --> CheckB: A er sand
EvaluateA --> ReturnFalse: A er falsk
CheckB --> ReturnTrue: B er sand
CheckB --> ReturnFalse: B er falsk
[*] --> EvaluateC: A || B
EvaluateC --> ReturnTrue: A er sand
EvaluateC --> CheckD: A er falsk
CheckD --> ReturnTrue: B er sand
CheckD --> ReturnFalse: B er falsk
note right of EvaluateA
Kortslutningsevaluering:
Hvis A er falsk, bliver B aldrig tjekket
end note
```
> **Performance tip**: JavaScript bruger "short-circuit evaluation" i `A && B`, hvis A er falsk, bliver B slet ikke evalueret. Brug dette til din fordel!
### Negationsoperator
Nogle gange er det nemmere at tænke på, hvornår noget IKKE er sandt. Som i stedet for at spørge "Er brugeren logget ind?", vil du måske spørge "Er brugeren IKKE logget ind?" Udråbstegnet (`!`) operatør vender logikken for dig.
Nogle gange er det lettere at tænke i, hvornår noget IKKE er sandt. Som i stedet for at spørge "Er brugeren logget ind?", vil du måske spørge "Er brugeren IKKE logget ind?" Udråbstegnsoperatoren (`!`) vender logikken for dig.
```javascript
if (!condition) {
// runs if condition is false
// kører hvis betingelsen er falsk
} else {
// runs if condition is true
// kører hvis betingelsen er sand
}
```
`!`-operatoren er som at sige "det modsatte af..." hvis noget er `true`, gør `!` det `false`, og omvendt.
`!`-operatoren er som at sige "det modsatte af..." hvis noget er `true`, gør `!` det til `false`, og omvendt.
### Ternære udtryk
For enkle betingede tildelinger giver JavaScript den **ternære operator**. Denne korte syntaks giver dig mulighed for at skrive et betinget udtryk på én linje, nyttigt når du skal tildele en af to værdier baseret på en betingelse.
Til simple betingede tildelinger tilbyder JavaScript **ternæroperatoren**. Denne korte syntaks tillader dig at skrive et betinget udtryk på én linje, nyttigt når du skal tildele én af to værdier baseret på en betingelse.
```javascript
let variable = condition ? returnThisIfTrue : returnThisIfFalse;
```
Det læses som et spørgsmål: "Er denne betingelse sand? Hvis ja, brug denne værdi. Hvis nej, brug den anden værdi."
Det læses som et spørgsmål: "Er denne betingelse sand? Hvis ja, brug denne værdi. Hvis nej, brug den værdi."
Nedenfor er et mere håndgribeligt eksempel:
@ -237,11 +479,11 @@ let secondNumber = 10;
let biggestNumber = firstNumber > secondNumber ? firstNumber : secondNumber;
```
✅ Tag et øjeblik til at læse denne kode et par gange. Forstår du, hvordan disse operatorer fungerer?
✅ Tag et øjeblik til at læse denne kode et par gange. Forstår du, hvordan disse operatorer virker?
Her er, hvad denne linje siger: "Er `firstNumber` større end `secondNumber`? Hvis ja, sæt `firstNumber` i `biggestNumber`. Hvis nej, sæt `secondNumber` i `biggestNumber`."
Denne linje siger: "Er `firstNumber` større end `secondNumber`? Hvis ja, sæt `firstNumber` i `biggestNumber`. Hvis nej, sæt `secondNumber` i `biggestNumber`."
Den ternære operator er bare en kortere måde at skrive denne traditionelle `if..else`-sætning:
Den ternære operator er bare en kortere måde at skrive det traditionelle `if..else`-udsagn på:
```javascript
let biggestNumber;
@ -252,25 +494,46 @@ if (firstNumber > secondNumber) {
}
```
Begge tilgange giver identiske resultater. Den ternære operator tilbyder kortfattethed, mens den traditionelle if-else-struktur kan være mere læsbar for komplekse betingelser.
Begge tilgange giver identiske resultater. Den ternære operator tilbyder kortfattethed, mens den traditionelle if-else-struktur kan være mere læsbar ved komplekse betingelser.
```mermaid
flowchart LR
A["🤔 Ternary-operator"] --> B["betingelse ?"]
B --> C["værdiHvisSand :"]
C --> D["værdiHvisFalsk"]
E["📝 Traditionel If-Else"] --> F["if (betingelse) {"]
F --> G[" returner værdiHvisSand"]
G --> H["} else {"]
H --> I[" returner værdiHvisFalsk"]
I --> J["}"]
K["⚡ Hvornår man skal bruge"] --> K1["Enkle tildelinger"]
K --> K2["Korte betingelser"]
K --> K3["Inline beslutninger"]
K --> K4["Return-sætninger"]
style A fill:#e3f2fd
style E fill:#fff3e0
style K fill:#e8f5e8
```
---
## 🚀 Udfordring
Lav et program, der først er skrevet med logiske operatorer, og omskriv det derefter ved hjælp af et ternært udtryk. Hvilken syntaks foretrækker du?
Lav et program, der først er skrevet med logiske operatorer, og omskriv det derefter ved brug af et ternært udtryk. Hvad foretrækker du som syntaks?
---
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Lav en omfattende karakterberegner, der demonstrerer flere beslutningstagende koncepter fra denne lektion, inklusive if-else-sætninger, switch-sætninger, logiske operatorer og ternære udtryk.
**Beskrivelse:** Opret en omfattende karakterberegner, som demonstrerer flere beslutningstagningselementer fra denne lektion, inklusive if-else-udsagn, switch-udsagn, logiske operatorer og ternære udtryk.
**Opgave:** Skriv et JavaScript-program, der tager en elevs numeriske score (0-100) og bestemmer deres bogstavkarakter ved hjælp af følgende kriterier:
**Prompt:** Skriv et JavaScript-program, der tager en elevs numeriske score (0-100) og bestemmer den tilsvarende karakter efter følgende kriterier:
- A: 90-100
- B: 80-89
- C: 70-79
@ -278,32 +541,132 @@ Brug Agent-tilstand til at fuldføre følgende udfordring:
- F: Under 60
Krav:
1. Brug en if-else-sætning til at bestemme bogstavkarakteren
2. Brug logiske operatorer til at tjekke, om eleven består (karakter >= 60) OG har udmærkelse (karakter >= 90)
3. Brug en switch-sætning til at give specifik feedback for hver bogstavkarakter
4. Brug en ternær operator til at bestemme, om eleven er berettiget til det næste kursus (karakter >= 70)
5. Inkluder inputvalidering for at sikre, at scoren er mellem 0 og 100
Test dit program med forskellige scores, inklusive kanttilfælde som 59, 60, 89, 90 og ugyldige input.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## Quiz efter lektionen
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/12)
## Gennemgang & Selvstudie
Læs mere om de mange operatorer, der er tilgængelige for brugeren [på MDN](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators).
Gå igennem Josh Comeaus fantastiske [operator-oversigt](https://joshwcomeau.com/operator-lookup/)!
## Opgave
[Operatorer](assignment.md)
1. Brug et if-else-udsagn til at bestemme karakteren
2. Brug logiske operatorer til at tjekke, om den studerende består (karakter >= 60) OG har udmærkelse (karakter >= 90)
3. Brug en switch-sætning til at give specifik feedback for hver bogstavkarakter
4. Brug en ternær operator til at afgøre, om den studerende er berettiget til næste kursus (karakter >= 70)
5. Inkluder inputvalidering for at sikre, at scoren er mellem 0 og 100
Test dit program med forskellige scores, inklusive grænsetilfælde som 59, 60, 89, 90 og ugyldige input.
Lær mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## Post-Lecture Quiz
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/12)
## Review & Self Study
Læs mere om de mange tilgængelige operatorer for brugeren [på MDN](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators).
Gå igennem Josh Comeaus vidunderlige [operator lookup](https://joshwcomeau.com/operator-lookup/)!
## Assignment
[Operators](assignment.md)
---
## 🧠 **Din beslutningstagning værktøjskasse - Resumé**
```mermaid
graph TD
A["🎯 JavaScript Beslutninger"] --> B["🔍 Boolean Logik"]
A --> C["📊 Betingede Udsagn"]
A --> D["🔗 Logiske Operatorer"]
A --> E["⚡ Avancerede Mønstre"]
B --> B1["true/false værdier"]
B --> B2["Sammenligningsoperatorer"]
B --> B3["Sandhedsbegreber"]
C --> C1["if udsagn"]
C --> C2["if-else kæder"]
C --> C3["switch udsagn"]
D --> D1["&& (OG)"]
D --> D2["|| (ELLER)"]
D --> D3["! (IKKE)"]
E --> E1["Ternær operator"]
E --> E2["Kortslutningsvurdering"]
E --> E3["Komplekse betingelser"]
F["💡 Nøgleprincipper"] --> F1["Klare læselige betingelser"]
F --> F2["Konsistent sammenligningsstil"]
F --> F3["Korrekt operatorpræcedens"]
F --> F4["Effektiv vurderingsrækkefølge"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 Din JavaScript beslutningstagning mesterskab tidslinje
### ⚡ **Hvad du kan gøre de næste 5 minutter**
- [ ] Øv sammenligningsoperatorer i din browserkonsol
- [ ] Skriv en simpel if-else sætning, der tjekker din alder
- [ ] Prøv udfordringen: omskriv en if-else med en ternær operator
- [ ] Test hvad der sker med forskellige "truthy" og "falsy" værdier
### 🎯 **Hvad du kan opnå denne time**
- [ ] Fuldfør post-lektion quizzen og gennemgå forvirrende koncepter
- [ ] Byg den omfattende karakterberegner fra GitHub Copilot-udfordringen
- [ ] Skab et simpelt beslutningstræ til et virkeligt scenarie (som at vælge hvad du skal have på)
- [ ] Øv dig i at kombinere flere betingelser med logiske operatorer
- [ ] Eksperimentér med switch-sætninger til forskellige brugstilfælde
### 📅 **Din uge-lange logik-mesterskab**
- [ ] Fuldfør operators-opgaven med kreative eksempler
- [ ] Byg en mini quiz-applikation med forskellige betingede strukturer
- [ ] Skab en formularvalidator, der tjekker flere inputbetingelser
- [ ] Øv Josh Comeaus [operator lookup](https://joshwcomeau.com/operator-lookup/) øvelser
- [ ] Refaktor eksisterende kode til at bruge mere passende betingede strukturer
- [ ] Studér short-circuit evaluering og performance-implikationer
### 🌟 **Din månedslange transformation**
- [ ] Mestre komplekse indlejrede betingelser og bevare kode-læselighed
- [ ] Byg en applikation med sofistikeret beslutningstagning logik
- [ ] Bidrag til open source ved at forbedre betinget logik i eksisterende projekter
- [ ] Lær en anden om forskellige betingede strukturer og hvornår hver skal bruges
- [ ] Udforsk funktionelle programmeringsmetoder til betinget logik
- [ ] Skab en personlig referenceguide for bedste praksis i betingelser
### 🏆 **Endelig beslutningstagning mester Check-in**
**Fejr dit logiske tankemesterværk:**
- Hvad er den mest komplekse beslutningslogik, du med succes har implementeret?
- Hvilken betinget struktur føles mest naturlig for dig og hvorfor?
- Hvordan har det at lære om logiske operatorer ændret din problemløsnings-tilgang?
- Hvilken virkelighedsbaseret applikation ville have fordel af sofistikeret beslutningstagning logik?
```mermaid
journey
title Din Logiske Tænkning Udvikling
section I dag
Boolean Forvirring: 3: You
If-Else Forståelse: 4: You
Operator Genkendelse: 5: You
section Denne Uge
Komplekse Betingelser: 4: You
Switch Ekspertise: 5: You
Logiske Kombinationer: 5: You
section Næste Måned
Avancerede Mønstre: 5: You
Ydelsesbevidsthed: 5: You
Undervisning af Andre: 5: You
```
> 🧠 **Du har mestret kunsten i digital beslutningstagning!** Enhver interaktiv applikation er afhængig af betinget logik for intelligent at kunne reagere på brugerhandlinger og skiftende betingelser. Du forstår nu, hvordan du får dine programmer til at tænke, evaluere og vælge passende svar. Dette logiske fundament vil drive enhver dynamisk applikation, du bygger! 🎉
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. Ved kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,25 +1,41 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "9197f8af0fef9be6e81d4dbda23c7e7d",
"translation_date": "2025-10-23T21:53:19+00:00",
"original_hash": "1710a50a519a6e4a1b40a5638783018d",
"translation_date": "2026-01-06T23:32:59+00:00",
"source_file": "2-js-basics/4-arrays-loops/README.md",
"language_code": "da"
}
-->
# JavaScript Grundlæggende: Arrays og Løkker
# JavaScript Basics: Arrays and Loops
![JavaScript Basics - Arrays](../../../../translated_images/webdev101-js-arrays.439d7528b8a294558d0e4302e448d193f8ad7495cc407539cc81f1afe904b470.da.png)
![JavaScript Basics - Arrays](../../../../translated_images/webdev101-js-arrays.439d7528b8a29455.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
## Quiz før forelæsning
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/13)
```mermaid
journey
title Din Arrays & Løkker Eventyr
section Array Grundlæggende
Oprettelse af Arrays: 5: You
Adgang til Elementer: 4: You
Array Metoder: 5: You
section Løkke Mestring
For Løkker: 4: You
Mens Løkker: 5: You
Moderne Syntax: 4: You
section Databehandling
Array + Løkker: 5: You
Virkelige Anvendelser: 4: You
Ydelsesoptimering: 5: You
```
## Pre-Lecture Quiz
[Pre-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/13)
Har du nogensinde undret dig over, hvordan hjemmesider holder styr på varer i indkøbskurven eller viser din venneliste? Det er her arrays og løkker kommer ind i billedet. Arrays er som digitale beholdere, der kan indeholde flere stykker information, mens løkker gør det muligt at arbejde med alle disse data effektivt uden gentagende kode.
Har du nogensinde spekuleret på, hvordan hjemmesider holder styr på varer i indkøbskurven eller viser din venneoversigt? Det er her arrays og løkker kommer ind i billedet. Arrays er som digitale beholdere, der rummer flere stykker information, mens løkker giver dig mulighed for at arbejde med alle disse data effektivt uden gentagende kode.
Sammen udgør disse to begreber fundamentet for at håndtere information i dine programmer. Du vil lære at gå fra manuelt at skrive hver enkelt trin til at skabe smart, effektiv kode, der kan behandle hundredevis eller endda tusindvis af elementer hurtigt.
Sammen danner disse to begreber fundamentet for håndtering af information i dine programmer. Du vil lære at bevæge dig fra manuelt at skrive hvert enkelt trin til at skabe smart, effektiv kode, der hurtigt kan behandle hundredvis eller endda tusindvis af elementer.
Ved slutningen af denne lektion vil du forstå, hvordan du kan udføre komplekse databehandlingsopgaver med blot nogle få linjer kode. Lad os udforske disse essentielle programmeringsbegreber.
Ved slutningen af denne lektion vil du forstå, hvordan du kan udføre komplekse databehandlingsopgaver med bare få linjer kode. Lad os udforske disse essentielle programmeringskoncepter.
[![Arrays](https://img.youtube.com/vi/1U4qTyq02Xw/0.jpg)](https://youtube.com/watch?v=1U4qTyq02Xw "Arrays")
@ -29,82 +45,150 @@ Ved slutningen af denne lektion vil du forstå, hvordan du kan udføre komplekse
> Du kan tage denne lektion på [Microsoft Learn](https://docs.microsoft.com/learn/modules/web-development-101-arrays/?WT.mc_id=academic-77807-sagibbon)!
```mermaid
mindmap
root((Databehandling))
Arrays
Struktur
Firkantede parenteser syntaks
Nul-baseret indeksering
Dynamisk størrelsestilpasning
Operationer
push/pop
shift/unshift
indexOf/includes
Typer
Tal-array
Streng-array
Blandet typer
Loops
For Loops
Optælling af iterationer
Array behandling
Forudsigelig flow
While Loops
Betingelsesbaseret
Ukendt antal iterationer
Brugerinput
Moderne syntaks
for...of
forEach
Funktionelle metoder
Anvendelser
Dataanalyse
Statistik
Filtrering
Transformationer
Brugergrænseflader
Lister
Menuer
Gallerier
```
## Arrays
Tænk på arrays som et digitalt arkivskab - i stedet for at opbevare ét dokument per skuffe, kan du organisere flere relaterede elementer i en enkelt, struktureret beholder. I programmering giver arrays dig mulighed for at gemme flere stykker information i én organiseret pakke.
Tænk på arrays som et digitalt arkivskab i stedet for at opbevare ét dokument pr. skuffe, kan du organisere flere relaterede elementer i en enkelt, struktureret beholder. I programmering giver arrays dig mulighed for at gemme flere stykker information i én organiseret pakke.
Uanset om du bygger et fotogalleri, administrerer en to-do-liste eller holder styr på high scores i et spil, giver arrays fundamentet for dataorganisation. Lad os se, hvordan de fungerer.
Uanset om du bygger et fotogalleri, administrerer en opgaveliste eller holder styr på high scores i et spil, giver arrays fundamentet for dataorganisering. Lad os se, hvordan de fungerer.
✅ Arrays er overalt omkring os! Kan du komme i tanke om et eksempel fra det virkelige liv, som en solcellepanel-array?
✅ Arrays er overalt omkring os! Kan du tænke på et eksempel fra virkeligheden på et array, som f.eks. et solcellepanel-array?
### Oprettelse af Arrays
At oprette et array er super enkelt - brug bare firkantede parenteser!
At oprette et array er super nemt brug bare firkantede parenteser!
```javascript
// Empty array - like an empty shopping cart waiting for items
// Tomt array - som en tom indkøbskurv, der venter på varer
const myArray = [];
```
**Hvad sker der her?**
Du har lige oprettet en tom beholder ved hjælp af de firkantede parenteser `[]`. Tænk på det som en tom boghylde - den er klar til at indeholde de bøger, du vil organisere der.
Du har lige oprettet en tom beholder ved hjælp af de firkantede parenteser `[]`. Tænk på det som en tom bibliotekshylde den er klar til at indeholde de bøger, du vil organisere der.
Du kan også fylde dit array med startværdier fra begyndelsen:
Du kan også udfylde dit array med startværdier med det samme:
```javascript
// Your ice cream shop's flavor menu
// Din isbutiks smagsmenu
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// A user's profile info (mixing different types of data)
// En brugers profilinfo (blandende forskellige typer data)
const userData = ["John", 25, true, "developer"];
// Test scores for your favorite class
// Testresultater for din yndlingsklasse
const scores = [95, 87, 92, 78, 85];
```
**Seje ting at bemærke:**
- Du kan gemme tekst, tal eller endda sand/falsk værdier i det samme array
- Bare adskil hvert element med et komma - nemt!
- Arrays er perfekte til at holde relateret information samlet
- Du kan gemme tekst, tal eller endda sand/falsk-værdier i samme array
- Bare adskil hvert element med et komma nemt!
- Arrays er perfekte til at holde relaterede oplysninger samlet
```mermaid
flowchart LR
A["📦 Arrays"] --> B["Opret [ ]"]
A --> C["Gem flere elementer"]
A --> D["Adgang via indeks"]
B --> B1["const arr = []"]
B --> B2["const arr = [1,2,3]"]
C --> C1["Tal"]
C --> C2["Strenge"]
C --> C3["Booleske værdier"]
C --> C4["Blandet typer"]
D --> D1["arr[0] = første"]
D --> D2["arr[1] = anden"]
D --> D3["arr[2] = tredje"]
E["📊 Arrayindeks"] --> E1["Indeks 0: Første"]
E --> E2["Indeks 1: Anden"]
E --> E3["Indeks 2: Tredje"]
E --> E4["Indeks n-1: Sidste"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
```
### Array-indeksering
Her er noget, der måske virker usædvanligt i starten: Arrays nummererer deres elementer fra 0, ikke 1. Denne nulbaserede indeksering har sine rødder i, hvordan computerhukommelse fungerer - det har været en programmeringskonvention siden de tidlige dage med programmeringssprog som C. Hvert sted i arrayet får sit eget adresse nummer kaldet et **indeks**.
Her er noget, der kan virke usædvanligt i starten: arrays nummererer deres elementer begyndende fra 0, ikke 1. Denne nul-baserede indeksering stammer fra, hvordan computerhukommelse fungerer det har været en programmeringskonvention siden de tidlige dage med programmeringssprog som C. Hver plads i arrayet får sit eget adressenummer kaldet et **indeks**.
| Indeks | Værdi | Beskrivelse |
|--------|-------|-------------|
| 0 | "Chokolade" | Første element |
| 1 | "Jordbær" | Andet element |
| 2 | "Vanilje" | Tredje element |
| 3 | "Pistacie" | Fjerde element |
| 4 | "Rocky Road" | Femte element |
|--------|--------|-------------|
| 0 | "Chokolade" | Første element |
| 1 | "Jordbær" | Andet element |
| 2 | "Vanilje" | Tredje element |
| 3 | "Pistacie" | Fjerde element |
| 4 | "Rocky Road" | Femte element |
✅ Er det overraskende for dig, at arrays starter ved nul-indekset? I nogle programmeringssprog starter indekser ved 1. Der er en interessant historie bag dette, som du kan [læse på Wikipedia](https://en.wikipedia.org/wiki/Zero-based_numbering).
Overrasker det dig, at arrays starter ved nul-indekset? I nogle programmeringssprog starter indekser ved 1. Der er en interessant historie omkring dette, som du kan [læse om på Wikipedia](https://en.wikipedia.org/wiki/Zero-based_numbering).
**Adgang til array-elementer:**
**Tilgang til array-elementer:**
```javascript
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// Access individual elements using bracket notation
console.log(iceCreamFlavors[0]); // "Chocolate" - first element
console.log(iceCreamFlavors[2]); // "Vanilla" - third element
console.log(iceCreamFlavors[4]); // "Rocky Road" - last element
// Få adgang til individuelle elementer ved hjælp af kantede parenteser
console.log(iceCreamFlavors[0]); // "Chokolade" - første element
console.log(iceCreamFlavors[2]); // "Vanilje" - tredje element
console.log(iceCreamFlavors[4]); // "Rocky Road" - sidste element
```
**Hvad sker der her:**
- **Bruger** firkantet parentesnotation med indeksnummeret for at få adgang til elementer
**Nedbrydning af, hvad der sker her:**
- **Bruger** firkantet parentesnotation med indeksnummer for at få adgang til elementer
- **Returnerer** værdien, der er gemt på den specifikke position i arrayet
- **Starter** tælling fra 0, hvilket gør det første element til indeks 0
- **Begynder** tælling fra 0, hvilket gør det første element til indeks 0
**Ændring af array-elementer:**
```javascript
// Change an existing value
// Ændr en eksisterende værdi
iceCreamFlavors[4] = "Butter Pecan";
console.log(iceCreamFlavors[4]); // "Butter Pecan"
// Add a new element at the end
// Tilføj et nyt element i slutningen
iceCreamFlavors[5] = "Cookie Dough";
console.log(iceCreamFlavors[5]); // "Cookie Dough"
```
@ -112,11 +196,11 @@ console.log(iceCreamFlavors[5]); // "Cookie Dough"
**I ovenstående har vi:**
- **Ændret** elementet ved indeks 4 fra "Rocky Road" til "Butter Pecan"
- **Tilføjet** et nyt element "Cookie Dough" ved indeks 5
- **Udvidet** arrayets længde automatisk ved at tilføje ud over de nuværende grænser
- **Udvidet** arrayets længde automatisk, når der tilføjes ud over de nuværende grænser
### Array-længde og almindelige metoder
Arrays kommer med indbyggede egenskaber og metoder, der gør arbejdet med data meget lettere.
Arrays kommer med indbyggede egenskaber og metoder, der gør det meget nemmere at arbejde med data.
**Find array-længde:**
@ -124,12 +208,12 @@ Arrays kommer med indbyggede egenskaber og metoder, der gør arbejdet med data m
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
console.log(iceCreamFlavors.length); // 5
// Length updates automatically as array changes
// Længde opdateres automatisk efterhånden som arrayet ændres
iceCreamFlavors.push("Mint Chip");
console.log(iceCreamFlavors.length); // 6
```
**Vigtige punkter at huske:**
**Vigtige ting at huske:**
- **Returnerer** det samlede antal elementer i arrayet
- **Opdateres** automatisk, når elementer tilføjes eller fjernes
- **Giver** en dynamisk optælling, der er nyttig til løkker og validering
@ -139,88 +223,179 @@ console.log(iceCreamFlavors.length); // 6
```javascript
const fruits = ["apple", "banana", "orange"];
// Add elements
fruits.push("grape"); // Adds to end: ["apple", "banana", "orange", "grape"]
fruits.unshift("strawberry"); // Adds to beginning: ["strawberry", "apple", "banana", "orange", "grape"]
// Tilføj elementer
fruits.push("grape"); // Tilføjer til slut: ["apple", "banana", "orange", "grape"]
fruits.unshift("strawberry"); // Tilføjer til begyndelsen: ["strawberry", "apple", "banana", "orange", "grape"]
// Remove elements
const lastFruit = fruits.pop(); // Removes and returns "grape"
const firstFruit = fruits.shift(); // Removes and returns "strawberry"
// Fjern elementer
const lastFruit = fruits.pop(); // Fjerner og returnerer "grape"
const firstFruit = fruits.shift(); // Fjerner og returnerer "strawberry"
// Find elements
const index = fruits.indexOf("banana"); // Returns 1 (position of "banana")
const hasApple = fruits.includes("apple"); // Returns true
// Find elementer
const index = fruits.indexOf("banana"); // Returnerer 1 (positionen af "banana")
const hasApple = fruits.includes("apple"); // Returnerer sandt
```
**Forståelse af disse metoder:**
- **Tilføjer** elementer med `push()` (slut) og `unshift()` (start)
- **Fjerner** elementer med `pop()` (slut) og `shift()` (start)
**Forstå disse metoder:**
- **Tilføjer** elementer med `push()` (til slutningen) og `unshift()` (til begyndelsen)
- **Fjerner** elementer med `pop()` (fra slutningen) og `shift()` (fra begyndelsen)
- **Finder** elementer med `indexOf()` og tjekker eksistens med `includes()`
- **Returnerer** nyttige værdier som fjernede elementer eller positionsindekser
✅ Prøv det selv! Brug din browsers konsol til at oprette og manipulere et array, du selv har lavet.
✅ Prøv det selv! Brug din browsers konsol til at oprette og manipulere dit eget array.
### 🧠 **Grundlæggende om Arrays: Organisering af dine data**
**Test din forståelse af arrays:**
- Hvorfor tror du, arrays tæller fra 0 i stedet for 1?
- Hvad sker der, hvis du prøver at tilgå et indeks, der ikke findes (som `arr[100]` i et 5-element array)?
- Kan du tænke på tre virkelige situationer, hvor arrays ville være nyttige?
```mermaid
stateDiagram-v2
[*] --> EmptyArray: const arr = []
EmptyArray --> WithItems: Tilføj elementer
WithItems --> Accessing: Brug indekser
Accessing --> Modifying: Ændr værdier
Modifying --> Processing: Brug metoder
WithItems --> WithItems: push(), unshift()
Processing --> Processing: pop(), shift()
note right of Accessing
Nul-baseret indeksering
arr[0] = første element
end note
note right of Processing
Indbyggede metoder
Dynamiske operationer
end note
```
> **Virkelighedsindsigt**: Arrays er overalt i programmering! Sociale medier feeds, indkøbskurve, fotogallerier, spillelister de er alle arrays bag scenen!
## Løkker
Tænk på den berømte straf fra Charles Dickens' romaner, hvor elever skulle skrive linjer gentagne gange på en tavle. Forestil dig, hvis du bare kunne instruere nogen til "skriv denne sætning 100 gange" og få det gjort automatisk. Det er præcis, hvad løkker gør for din kode.
Tænk på den berømte straf i Charles Dickens romaner, hvor elever skulle skrive linjer gentagne gange på en tavle. Forestil dig, at du kunne instruere nogen bare at "skriv denne sætning 100 gange" og få det gjort automatisk. Det er præcis, hvad løkker gør for din kode.
Løkker er som at have en utrættelig assistent, der kan gentage opgaver uden fejl. Uanset om du skal tjekke hver vare i en indkøbskurv eller vise alle billeder i et album, håndterer løkker gentagelsen effektivt.
JavaScript tilbyder flere typer løkker at vælge imellem. Lad os undersøge hver enkelt og forstå, hvornår de skal bruges.
### For-løkke
```mermaid
flowchart TD
A["🔄 Looptyper"] --> B["For-løkke"]
A --> C["While-løkke"]
A --> D["For...of-løkke"]
A --> E["forEach-metoden"]
B --> B1["Kendte iterationer"]
B --> B2["Tæller-baseret"]
B --> B3["for(init; betingelse; inkrement)"]
C --> C1["Ukendte iterationer"]
C --> C2["Betingelses-baseret"]
C --> C3["while(betingelse)"]
D --> D1["Moderne ES6+"]
D --> D2["Array-iteration"]
D --> D3["for(item af array)"]
E --> E1["Funktionel stil"]
E --> E2["Array-metode"]
E --> E3["array.forEach(callback)"]
F["⏰ Hvornår man bruger"] --> F1["For: Tælling, indekser"]
F --> F2["While: Brugerinput, søgning"]
F --> F3["For...of: Simpel iteration"]
F --> F4["forEach: Funktionel programmering"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
### For Løkke
`for`-løkken er som at sætte en timer - du ved præcis, hvor mange gange du vil have noget til at ske. Den er super organiseret og forudsigelig, hvilket gør den perfekt, når du arbejder med arrays eller skal tælle ting.
`for`-løkke er som at sætte et ur du ved præcis, hvor mange gange du vil have noget til at ske. Den er super organiseret og forudsigelig, hvilket gør den perfekt, når du arbejder med arrays eller har brug for at tælle ting.
**For-løkke struktur:**
**Struktur for For Løkke:**
| Komponent | Formål | Eksempel |
|-----------|--------|----------|
|-----------|---------|----------|
| **Initialisering** | Sætter startpunkt | `let i = 0` |
| **Betingelse** | Hvornår skal den fortsætte | `i < 10` |
| **Betingelse** | Hvornår den fortsætter | `i < 10` |
| **Inkrement** | Hvordan den opdateres | `i++` |
```javascript
// Counting from 0 to 9
// Tæller fra 0 til 9
for (let i = 0; i < 10; i++) {
console.log(`Count: ${i}`);
}
// More practical example: processing scores
// Mere praktisk eksempel: behandling af scores
const testScores = [85, 92, 78, 96, 88];
for (let i = 0; i < testScores.length; i++) {
console.log(`Student ${i + 1}: ${testScores[i]}%`);
}
```
**Trin for trin, her er hvad der sker:**
- **Initialiserer** tæller-variablen `i` til 0 fra starten
- **Tjekker** betingelsen `i < 10` før hver iteration
**Trin for trin, hvad der sker:**
- **Initialiserer** tællervariablen `i` til 0 i starten
- **Tjekker** betingelsen `i < 10` før hver gennemkørsel
- **Udfører** kodeblokken, når betingelsen er sand
- **Inkrementerer** `i` med 1 efter hver iteration med `i++`
- **Stopper**, når betingelsen bliver falsk (når `i` når 10)
✅ Kør denne kode i en browserkonsol. Hvad sker der, når du laver små ændringer i tælleren, betingelsen eller iterationsudtrykket? Kan du få den til at køre baglæns og lave en nedtælling?
- **Forøger** `i` med 1 efter hver gennemkørsel med `i++`
- **Stopper** når betingelsen bliver falsk (når `i` når 10)
✅ Kør denne kode i en browserkonsol. Hvad sker der, hvis du laver små ændringer i tælleren, betingelsen eller inkrement-udtrykket? Kan du få den til at køre baglæns og lave en nedtælling?
### 🗓️ **For Løkke Færdighedstest: Kontrolleret Gentagelse**
**Vurdér din forståelse af for-løkken:**
- Hvad er de tre dele af en for-løkke, og hvad gør hver del?
- Hvordan ville du løbe gennem et array baglæns?
- Hvad sker der, hvis du glemmer inkrement-delen (`i++`)?
```mermaid
flowchart TD
A["🚀 Start For Loop"] --> B["Initialiser: lad i = 0"]
B --> C{"Betingelse: i < array.length?"}
C -->|true| D["Udfør kodeblok"]
D --> E["Inkrementer: i++"]
E --> C
C -->|false| F["✅ Afslut løkke"]
G["📋 Almindelige Mønstre"] --> G1["for(let i=0; i<n; i++)"]
G --> G2["for(let i=n-1; i>=0; i--)"]
G --> G3["for(let i=0; i<arr.length; i+=2)"]
style A fill:#e3f2fd
style F fill:#e8f5e8
style G fill:#fff3e0
```
> **Loop-indsigt**: For-løkker er perfekte, når du ved præcis, hvor mange gange noget skal gentages. De er det mest almindelige valg til behandling af arrays!
### While-løkke
### While Løkke
`while`-løkken er som at sige "bliv ved med at gøre dette indtil..." - du ved måske ikke præcis, hvor mange gange den vil køre, men du ved, hvornår den skal stoppe. Den er perfekt til ting som at bede en bruger om input, indtil de giver dig det, du har brug for, eller søge gennem data, indtil du finder det, du leder efter.
`while`-løkke er som at sige "bliv ved med at gøre dette indtil..." du ved måske ikke præcist, hvor mange gange den kører, men du ved, hvornår den skal stoppe. Den er perfekt til ting som at bede en bruger om input, indtil de giver det, du har brug for, eller søge gennem data, indtil du finder det, du leder efter.
**While-løkke karakteristika:**
- **Fortsætter** med at køre, så længe betingelsen er sand
- **Kræver** manuel styring af eventuelle tæller-variabler
**Karakteristika for While Løkke:**
- **Fortsætter** med at køre så længe betingelsen er sand
- **Kræver** manuel styring af eventuelle tællervariabler
- **Tjekker** betingelsen før hver iteration
- **Risikerer** uendelige løkker, hvis betingelsen aldrig bliver falsk
```javascript
// Basic counting example
// Grundlæggende tælleeksempel
let i = 0;
while (i < 10) {
console.log(`While count: ${i}`);
i++; // Don't forget to increment!
i++; // Glem ikke at øge!
}
// More practical example: processing user input
// Mere praktisk eksempel: behandling af brugerinput
let userInput = "";
let attempts = 0;
const maxAttempts = 3;
@ -235,86 +410,144 @@ if (attempts >= maxAttempts) {
}
```
**Forståelse af disse eksempler:**
- **Styrer** tæller-variablen `i` manuelt inde i løkkens krop
- **Inkrementerer** tælleren for at undgå uendelige løkker
- **Demonstrerer** praktisk anvendelse med brugerinput og begrænsning af forsøg
- **Inkluderer** sikkerhedsmekanismer for at forhindre endeløs udførelse
**Forstå disse eksempler:**
- **Håndterer** tællervariablen `i` manuelt inde i løkkens krop
- **Forøger** tælleren for at forhindre uendelige løkker
- **Demonstrerer** praktisk brug med brugerinput og forsøg-begrænsning
- **Inkluderer** sikkerhedsforanstaltninger for at forhindre uendelig udførelse
### ♾️ **While Løkke Visdomstest: Betingelsesbaseret Gentagelse**
**Test din forståelse af while-løkker:**
- Hvad er hovedfaren ved at bruge while-løkker?
- Hvornår ville du vælge en while-løkke frem for en for-løkke?
- Hvordan kan du forhindre uendelige løkker?
```mermaid
flowchart LR
A["🔄 While vs For"] --> B["While-løkke"]
A --> C["For-løkke"]
B --> B1["Ukendte iterationer"]
B --> B2["Betingelsesstyret"]
B --> B3["Brugerinput, søgning"]
B --> B4["⚠️ Risiko: uendelige løkker"]
C --> C1["Kendte iterationer"]
C --> C2["Tællerstyret"]
C --> C3["Array-behandling"]
C --> C4["✅ Sikker: forudsigelig slutning"]
D["🛡️ Sikkerhedstips"] --> D1["Ændr altid betingelsesvariablen"]
D --> D2["Inkluder flugt-betingelser"]
D --> D3["Sæt maksimum iterationsgrænser"]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#ffebee
```
> **Sikkerhed først**: While-løkker er kraftfulde, men kræver omhyggelig betingelsesstyring. Sørg altid for, at din løkkebetingelse til sidst bliver falsk!
### Moderne løkkealternativer
### Moderne Loop-alternativer
JavaScript tilbyder moderne løkkesyntaks, der kan gøre din kode mere læsbar og mindre fejlbehæftet.
JavaScript tilbyder moderne løkkesyntakser, der kan gøre din kode mere læsbar og mindre fejlbehæftet.
**For...of-løkke (ES6+):**
**For...of Løkke (ES6+):**
```javascript
const colors = ["red", "green", "blue", "yellow"];
// Modern approach - cleaner and safer
// Moderne tilgang - renere og sikrere
for (const color of colors) {
console.log(`Color: ${color}`);
}
// Compare with traditional for loop
// Sammenlign med traditionel for-løkke
for (let i = 0; i < colors.length; i++) {
console.log(`Color: ${colors[i]}`);
}
```
**Vigtige fordele ved for...of:**
- **Eliminerer** styring af indeks og potentielle fejl
- **Eliminerer** indekshåndtering og potentielle off-by-one fejl
- **Giver** direkte adgang til array-elementer
- **Forbedrer** kodelæsbarhed og reducerer syntaks-kompleksitet
- **Forbedrer** kode-læsbarhed og reducerer syntaks-kompleksitet
**forEach-metode:**
**forEach Metode:**
```javascript
const prices = [9.99, 15.50, 22.75, 8.25];
// Using forEach for functional programming style
// Brug af forEach til funktionel programmeringsstil
prices.forEach((price, index) => {
console.log(`Item ${index + 1}: $${price.toFixed(2)}`);
});
// forEach with arrow functions for simple operations
// forEach med pilefunktioner til simple operationer
prices.forEach(price => console.log(`Price: $${price}`));
```
**Hvad du skal vide om forEach:**
**Det du skal vide om forEach:**
- **Udfører** en funktion for hvert array-element
- **Giver** både elementværdi og indeks som parametre
- **Kan ikke** stoppes tidligt (i modsætning til traditionelle løkker)
- **Returnerer** undefined (skaber ikke et nyt array)
✅ Hvorfor ville du vælge en for-løkke frem for en while-løkke? 17K læsere havde det samme spørgsmål på StackOverflow, og nogle af meningerne [kan være interessante for dig](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript).
- **Giver** både elementværdien og indekset som parametre
- **Kan ikke** afbrydes tidligt (i modsætning til traditionelle løkker)
- **Returnerer** undefined (opretter ikke et nyt array)
✅ Hvorfor ville du vælge en for-løkke fremfor en while-løkke? 17K seere havde det samme spørgsmål på StackOverflow, og nogle af meningene [kan være interessante for dig](https://stackoverflow.com/questions/39969145/while-loops-vs-for-loops-in-javascript).
### 🎨 **Moderne Løkkesyntaks Test: Omfavn ES6+**
**Vurder din moderne JavaScript-forståelse:**
- Hvad er fordelene ved `for...of` i forhold til traditionelle for-løkker?
- Hvornår ville du stadig foretrække traditionelle for-løkker?
- Hvad er forskellen mellem `forEach` og `map`?
```mermaid
quadrantChart
title Guide til valg af løkker
x-axis Traditionel --> Moderne
y-axis Simpel --> Kompleks
quadrant-1 Moderne Kompleks
quadrant-2 Traditionel Kompleks
quadrant-3 Traditionel Simpel
quadrant-4 Moderne Simpel
Traditionel For: [0.2, 0.7]
While Løkke: [0.3, 0.6]
For...of: [0.8, 0.3]
forEach: [0.9, 0.4]
Array-metoder: [0.8, 0.8]
```
> **Moderne trend**: ES6+ syntakser som `for...of` og `forEach` bliver den foretrukne tilgang til array-iteration, fordi det er renere og mindre fejlbehæftet!
## Løkker og Arrays
Kombinationen af arrays med løkker skaber kraftfulde databehandlingsmuligheder. Denne kombination er grundlæggende for mange programmeringsopgaver, fra visning af lister til beregning af statistikker.
Kombinationen af arrays med løkker skaber kraftfulde muligheder for databehandling. Dette par er fundamentalt for mange programmeringsopgaver, fra at vise lister til at beregne statistikker.
**Traditionel array-behandling:**
**Traditionel Array-behandling:**
```javascript
const iceCreamFlavors = ["Chocolate", "Strawberry", "Vanilla", "Pistachio", "Rocky Road"];
// Classic for loop approach
// Klassisk for-løkke tilgang
for (let i = 0; i < iceCreamFlavors.length; i++) {
console.log(`Flavor ${i + 1}: ${iceCreamFlavors[i]}`);
}
// Modern for...of approach
// Moderne for...of tilgang
for (const flavor of iceCreamFlavors) {
console.log(`Available flavor: ${flavor}`);
}
```
**Lad os forstå hver tilgang:**
- **Bruger** array-længdeegenskaben til at bestemme løkkens grænse
- **Får adgang** til elementer via indeks i traditionelle for-løkker
- **Giver** direkte adgang til elementer i for...of-løkker
- **Bruger** array-længde egenskaben til at bestemme løkkens grænser
- **Tilgår** elementer via indeks i traditionelle for-løkker
- **Giver** direkte elementadgang i for...of løkker
- **Behandler** hvert array-element præcist én gang
**Praktisk databehandlingseksempel:**
**Praktisk eksempel på databehandling:**
```javascript
const studentGrades = [85, 92, 78, 96, 88, 73, 89];
@ -322,7 +555,7 @@ let total = 0;
let highestGrade = studentGrades[0];
let lowestGrade = studentGrades[0];
// Process all grades with a single loop
// Behandl alle karakterer med en enkelt løkke
for (let i = 0; i < studentGrades.length; i++) {
const grade = studentGrades[i];
total += grade;
@ -343,45 +576,175 @@ console.log(`Lowest: ${lowestGrade}`);
```
**Sådan fungerer denne kode:**
- **Initialiserer** sporingsvariabler for sum og ekstremer
- **Initialiserer** sporingsvariabler for sum og yderpunkter
- **Behandler** hver karakter med en enkelt effektiv løkke
- **Akkumulerer** det samlede for gennemsnitsberegning
- **Sporer** højeste og laveste værdier under iteration
- **Akkumulerer** totalen til gennemsnitsberegning
- **Sporer** højeste og laveste værdier under iterationen
- **Beregner** endelige statistikker efter løkkens afslutning
✅ Eksperimenter med at iterere over et array, du selv har lavet, i din browsers konsol.
✅ Eksperimentér med at løbe gennem et array, du selv har lavet, i din browsers konsol.
```mermaid
flowchart TD
A["📦 Array Data"] --> B["🔄 Løkkebehandling"]
B --> C["📈 Resultater"]
A1["[85, 92, 78, 96, 88]"] --> A
B --> B1["Beregn total"]
B --> B2["Find min/max"]
B --> B3["Tæl betingelser"]
B --> B4["Transformér data"]
C --> C1["Gennemsnit: 87.8"]
C --> C2["Højeste: 96"]
C --> C3["Bestået: 5/5"]
C --> C4["Bogstavkarakterer"]
D["⚡ Behandlingsmønstre"] --> D1["Akkumulering (sum)"]
D --> D2["Sammenligning (min/max)"]
D --> D3["Filtrering (betingelser)"]
D --> D4["Kortlægning (transformation)"]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e8
style D fill:#f3e5f5
```
---
## GitHub Copilot Agent Challenge 🚀
Brug Agent-mode til at fuldføre følgende udfordring:
Brug Agent-tilstanden til at løse følgende udfordring:
**Beskrivelse:** Byg en omfattende databehandlingsfunktion, der kombinerer arrays og løkker for at analysere et datasæt og generere meningsfulde indsigter.
**Beskrivelse:** Byg en omfattende databehandlingsfunktion, der kombinerer arrays og løkker til at analysere et datasæt og generere meningsfulde indsigter.
**Prompt:** Opret en funktion kaldet `analyzeGrades`, der tager et array af studenterkarakterobjekter (hver indeholder navn og score-egenskaber) og returnerer et objekt med statistikker, inklusive den højeste score, laveste score, gennemsnitsscore, antal studenter, der bestod (score >= 70), og et array af studenternavne, der scorede over gennemsnittet. Brug mindst to forskellige løkketyper i din løsning.
**Prompt:** Opret en funktion kaldet `analyzeGrades`, som tager et array af elevkarakterobjekter (hver indeholder navn- og score-egenskaber) og returnerer et objekt med statistik inklusive højeste score, laveste score, gennemsnitsscore, antallet af elever, der bestod (score >= 70), og et array med navne på elever, der har scoret over gennemsnittet. Brug mindst to forskellige løkketyper i din løsning.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
JavaScript tilbyder flere moderne array-metoder, der kan erstatte traditionelle løkker til specifikke opgaver. Udforsk [forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [for-of](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...of), [map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map), [filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) og [reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
JavaScript tilbyder flere moderne array-metoder, der kan erstatte traditionelle løkker til specifikke opgaver. Udforsk [forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [for-of](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...of), [map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map), [filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter), og [reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
**Din udfordring:** Refaktorer eksemplet med studenterkarakterer ved hjælp af mindst tre forskellige array-metoder. Bemærk, hvor meget renere og mere læsbar koden bliver med moderne JavaScript-syntaks.
**Din udfordring:** Refaktorér eksemplet med elevkarakterer ved hjælp af mindst tre forskellige array-metoder. Bemærk, hvor meget renere og mere læselig koden bliver med moderne JavaScript-syntaks.
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/14)
## Gennemgang & Selvstudie
## Gennemgang & Selvstudium
Arrays i JavaScript har mange metoder knyttet til dem, som er ekstremt nyttige til datamanipulation. [Læs om disse metoder](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) og prøv nogle af dem (som push, pop, slice og splice) på et array, du selv har lavet.
Arrays i JavaScript har mange metoder knyttet til sig, som er ekstremt nyttige til datamanipulation. [Læs om disse metoder](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) og prøv nogle af dem (som push, pop, slice og splice) på et array, du selv opretter.
## Opgave
[Iterér et Array](assignment.md)
[Loop an Array](assignment.md)
---
## 📊 **Din oversigt over Arrays & Loops værktøjer**
```mermaid
graph TD
A["🎯 Arrays & Løkker Mestring"] --> B["📦 Array Grundlæggende"]
A --> C["🔄 Løkketyper"]
A --> D["🔗 Databehandling"]
A --> E["🎨 Moderne Teknikker"]
B --> B1["Oprettelse: [ ]"]
B --> B2["Indeksering: arr[0]"]
B --> B3["Metoder: push, pop"]
B --> B4["Egenskaber: længde"]
C --> C1["For: Kendte iterationer"]
C --> C2["While: Betingelsesbaseret"]
C --> C3["For...of: Direkte adgang"]
C --> C4["forEach: Funktionel"]
D --> D1["Statistik beregning"]
D --> D2["Datatransformation"]
D --> D3["Filtrering & søgning"]
D --> D4["Realtidsbehandling"]
E --> E1["Pilefunktioner"]
E --> E2["Metodekædning"]
E --> E3["Destrukturering"]
E --> E4["Skabelonlitteraler"]
F["💡 Nøglefordele"] --> F1["Effektiv datahåndtering"]
F --> F2["Reduceret kodegentagelse"]
F --> F3["Skalerbare løsninger"]
F --> F4["Renere syntaks"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
```
---
## 🚀 Din tidslinje for mestring af Arrays & Loops
### ⚡ **Hvad du kan nå de næste 5 minutter**
- [ ] Opret et array med dine yndlingsfilm og få adgang til specifikke elementer
- [ ] Skriv en for-løkke, der tæller fra 1 til 10
- [ ] Prøv udfordringen med moderne array-metoder fra lektionen
- [ ] Øv array-indeksering i din browser-konsol
### 🎯 **Hvad du kan opnå denne time**
- [ ] Fuldfør quizzen efter lektionen og gennemgå eventuelle udfordrende koncepter
- [ ] Byg den omfattende karakteranalysator fra GitHub Copilot-udfordringen
- [ ] Opret en simpel indkøbskurv, der tilføjer og fjerner varer
- [ ] Øv dig i at konvertere mellem forskellige løkke-typer
- [ ] Eksperimenter med array-metoder som `push`, `pop`, `slice` og `splice`
### 📅 **Din en-ugers rejse i databehandling**
- [ ] Fuldfør opgaven "Loop an Array" med kreative forbedringer
- [ ] Byg en to-do-liste applikation ved hjælp af arrays og loops
- [ ] Opret en simpel statistikberegner til numeriske data
- [ ] Øv dig med [MDN array-metoder](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)
- [ ] Byg en fotogalleri- eller musikafspilningsliste-grænseflade
- [ ] Udforsk funktionel programmering med `map`, `filter` og `reduce`
### 🌟 **Din månedslange transformation**
- [ ] Mestring af avancerede array-operationer og performanceoptimering
- [ ] Byg et komplet dashboard til datavisualisering
- [ ] Bidrag til open source-projekter inden for databehandling
- [ ] Lær en anden om arrays og loops med praktiske eksempler
- [ ] Opret et personligt bibliotek med genanvendelige data-behandlingsfunktioner
- [ ] Udforsk algoritmer og datastrukturer baseret på arrays
### 🏆 **Endelig check-in for mester i databehandling**
**Fejr din mestring af arrays og loops:**
- Hvad er den mest nyttige array-operation, du har lært til virkelige anvendelser?
- Hvilken type løkke føles mest naturlig for dig, og hvorfor?
- Hvordan har forståelsen af arrays og loops ændret din tilgang til organisering af data?
- Hvilken kompleks databehandlingsopgave vil du gerne tage fat på næste gang?
```mermaid
journey
title Din Data Behandlings Udvikling
section I dag
Array Forvirring: 3: You
Grundlæggende Løkker: 4: You
Forståelse af Indeks: 5: You
section Denne Uge
Metodebeherskelse: 4: You
Effektiv Behandling: 5: You
Moderne Syntaks: 5: You
section Næste Måned
Komplekse Algoritmer: 5: You
Ydelsesoptimering: 5: You
Undervisning af Andre: 5: You
```
> 📦 **Du har låst op for kraften i dataorganisering og -behandling!** Arrays og loops er grundlaget for næsten alle applikationer, du nogensinde vil bygge. Fra simple lister til kompleks dataanalyse har du nu værktøjerne til at håndtere information effektivt og elegant. Hver dynamisk hjemmeside, mobilapp og datadrevet applikation bygger på disse fundamentale koncepter. Velkommen til verdenen af skalerbar databehandling! 🎉
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Mens vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,47 +1,92 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "20c72cf2e5b0050d38ca3cb14a75a9df",
"translation_date": "2025-10-23T22:03:58+00:00",
"original_hash": "3fcfa99c4897e051b558b5eaf1e8cc74",
"translation_date": "2026-01-06T23:57:01+00:00",
"source_file": "3-terrarium/1-intro-to-html/README.md",
"language_code": "da"
}
-->
# Terrarium Projekt Del 1: Introduktion til HTML
![Introduktion til HTML](../../../../translated_images/webdev101-html.4389c2067af68e98280c1bde52b6c6269f399eaae3659b7c846018d8a7b0bbd9.da.png)
```mermaid
journey
title Din HTML læringsrejse
section Fundament
Opret HTML fil: 3: Student
Tilføj DOCTYPE: 4: Student
Strukturér dokument: 5: Student
section Indhold
Tilføj metadata: 4: Student
Inkludér billeder: 5: Student
Organisér layout: 5: Student
section Semantik
Brug korrekte tags: 4: Student
Forbedr tilgængelighed: 5: Student
Byg terrarium: 5: Student
```
![Introduktion til HTML](../../../../translated_images/webdev101-html.4389c2067af68e98.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
HTML, eller HyperText Markup Language, er fundamentet for alle hjemmesider, du nogensinde har besøgt. Tænk på HTML som skelettet, der giver struktur til websider det definerer, hvor indholdet skal placeres, hvordan det er organiseret, og hvad hver del repræsenterer. Mens CSS senere vil "klæde" din HTML med farver og layout, og JavaScript vil bringe det til live med interaktivitet, giver HTML den essentielle struktur, der gør alt andet muligt.
I denne lektion vil du oprette HTML-strukturen til en virtuel terrarium-grænseflade. Dette praktiske projekt vil lære dig grundlæggende HTML-koncepter, mens du bygger noget visuelt engagerende. Du vil lære, hvordan man organiserer indhold ved hjælp af semantiske elementer, arbejder med billeder og skaber fundamentet for en interaktiv webapplikation.
Ved slutningen af denne lektion vil du have en fungerende HTML-side, der viser plantebilleder i organiserede kolonner, klar til styling i næste lektion. Bare rolig, hvis det ser enkelt ud i starten det er præcis, hvad HTML skal gøre, før CSS tilføjer den visuelle finish.
## Quiz før lektionen
HTML, eller HyperText Markup Language, er fundamentet for enhver hjemmeside, du nogensinde har besøgt. Tænk på HTML som skelettet, der giver struktur til websider det definerer, hvor indholdet skal være, hvordan det organiseres, og hvad hvert enkelt element repræsenterer. Mens CSS senere "pynter" din HTML med farver og layouts, og JavaScript bringer det til live med interaktivitet, giver HTML den essentielle struktur, som gør alt andet muligt.
I denne lektion vil du lave HTML-strukturen til en virtuel terrariumgrænseflade. Dette hands-on projekt vil lære dig grundlæggende HTML-koncepter, samtidig med at du bygger noget visuelt engagerende. Du lærer, hvordan du organiserer indhold ved hjælp af semantiske elementer, arbejder med billeder og laver fundamentet for en interaktiv webapplikation.
Ved slutningen af denne lektion vil du have en fungerende HTML-side, der viser plantebilleder i organiserede kolonner, klar til styling i næste lektion. Vær ikke bekymret, hvis det ser grundlæggende ud til at starte med det er præcis, hvad HTML skal gøre, før CSS tilføjer det visuelle finish.
```mermaid
mindmap
root((HTML Grundlæggende))
Structure
DOCTYPE Deklaration
HTML Element
Head Sektion
Body Indhold
Elements
Tags & Attributter
Selv-lukkende Tags
Indlejrede Elementer
Block vs Inline
Content
Tekstelementer
Billeder
Containere (div)
Lister
Semantics
Betydningsfulde Tags
Tilgængelighed
Skærmlæsere
SEO Fordele
Best Practices
Korrekt Indlejring
Gyldig Markup
Beskrivende Alt Tekst
Organiseret Struktur
```
## Forud-forelæsningstest
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/15)
[Forud-forelæsningstest](https://ff-quizzes.netlify.app/web/quiz/15)
> 📺 **Se og lær**: Tjek denne nyttige videooversigt
> 📺 **Se og lær**: Tjek denne hjælpsomme videooversigt
>
> [![HTML Fundamentals Video](https://img.youtube.com/vi/1TvxJKBzhyQ/0.jpg)](https://www.youtube.com/watch?v=1TvxJKBzhyQ)
> [![HTML Grundprincipper Video](https://img.youtube.com/vi/1TvxJKBzhyQ/0.jpg)](https://www.youtube.com/watch?v=1TvxJKBzhyQ)
## Opsætning af dit projekt
Før vi dykker ned i HTML-koden, lad os oprette et ordentligt arbejdsområde til dit terrarium-projekt. At skabe en organiseret filstruktur fra starten er en vigtig vane, der vil gavne dig gennem hele din webudviklingsrejse.
Før vi dykker ned i HTML-koden, lad os sætte et passende arbejdsområde op til dit terrariumprojekt. At skabe en organiseret filstruktur fra starten er en vigtig vane, der vil gavne dig gennem hele din webudviklingsrejse.
### Opgave: Opret din projektstruktur
Du vil oprette en dedikeret mappe til dit terrarium-projekt og tilføje din første HTML-fil. Her er to metoder, du kan bruge:
Du skal oprette en dedikeret mappe til dit terrariumprojekt og tilføje din første HTML-fil. Her er to metoder, du kan bruge:
**Mulighed 1: Brug af Visual Studio Code**
1. Åbn Visual Studio Code
2. Klik på "File" → "Open Folder" eller brug `Ctrl+K, Ctrl+O` (Windows/Linux) eller `Cmd+K, Cmd+O` (Mac)
3. Opret en ny mappe kaldet `terrarium` og vælg den
4. I Explorer-panelet skal du klikke på ikonet "New File"
4. I Explorer-panelet, klik på ikonet "New File"
5. Navngiv din fil `index.html`
![VS Code Explorer viser oprettelse af ny fil](../../../../translated_images/vs-code-index.e2986cf919471eb984a0afef231380c8b132b000635105f2397bd2754d1b689c.da.png)
![VS Code Explorer viser oprettelse af ny fil](../../../../translated_images/vs-code-index.e2986cf919471eb9.da.png)
**Mulighed 2: Brug af terminalkommandoer**
```bash
@ -51,48 +96,73 @@ touch index.html
code index.html
```
**Her er, hvad disse kommandoer gør:**
**Det er, hvad disse kommandoer gør:**
- **Opretter** en ny mappe kaldet `terrarium` til dit projekt
- **Navigerer** ind i terrarium-mappen
- **Opretter** en tom `index.html`-fil
- **Navigerer** ind i terrarium-mappen
- **Opretter** en tom fil ved navn `index.html`
- **Åbner** filen i Visual Studio Code til redigering
> 💡 **Pro Tip**: Filnavnet `index.html` er specielt i webudvikling. Når nogen besøger en hjemmeside, leder browsere automatisk efter `index.html` som standard-siden, der skal vises. Det betyder, at en URL som `https://mysite.com/projects/` automatisk vil vise `index.html`-filen fra `projects`-mappen uden at skulle angive filnavnet i URL'en.
## Forstå HTML-dokumentstruktur
Hvert HTML-dokument følger en specifik struktur, som browsere skal forstå og vise korrekt. Tænk på denne struktur som et formelt brev det har nødvendige elementer i en bestemt rækkefølge, der hjælper modtageren (i dette tilfælde browseren) med at behandle indholdet korrekt.
> 💡 **Pro-tip**: Filnavnet `index.html` er specielt i webudvikling. Når nogen besøger en hjemmeside, søger browseren automatisk efter `index.html` som standard side at vise. Det betyder, at en URL som `https://mysite.com/projects/` automatisk åbner `index.html`-filen i `projects`-mappen uden at skulle specificere filnavnet i URLen.
## Forståelse af HTML-dokumentstruktur
Hvert HTML-dokument følger en specifik struktur, som browsere skal forstå og vise korrekt. Tænk på denne struktur som et formelt brev det har nødvendige elementer i en bestemt rækkefølge, som hjælper modtageren (i dette tilfælde browseren) med at behandle indholdet korrekt.
```mermaid
flowchart TD
A["<!DOCTYPE html>"] --> B["<html>"]
B --> C["<head>"]
C --> D["<title>"]
C --> E["<meta charset>"]
C --> F["<meta viewport>"]
B --> G["<body>"]
G --> H["<h1> Overskrift"]
G --> I["<div> Beholdere"]
G --> J["<img> Billeder"]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#fff3e0
style G fill:#e8f5e8
```
Lad os starte med at tilføje det essentielle fundament, som hvert HTML-dokument har brug for.
### DOCTYPE-deklarationen og roden
### DOCTYPE-deklarationen og rootelementet
De første to linjer i enhver HTML-fil fungerer som dokumentets "introduktion" til browseren:
De første to linjer i en hvilken som helst HTML-fil fungerer som dokumentets "introduktion" til browseren:
```html
<!DOCTYPE html>
<html></html>
```
**Forstå, hvad denne kode gør:**
**Sådan fungerer denne kode:**
- **Deklarerer** dokumenttypen som HTML5 ved hjælp af `<!DOCTYPE html>`
- **Opretter** rod-elementet `<html>`, der vil indeholde alt sideindhold
- **Etablerer** moderne webstandarder for korrekt browsergengivelse
- **Sikrer** ensartet visning på tværs af forskellige browsere og enheder
- **Opretter** root-`<html>` elementet, som skal indeholde alt sideindhold
- **Etablerer** moderne webstandarder for korrekt browsers rendering
- **Sikrer** konsistent visning på tværs af forskellige browsere og enheder
> 💡 **VS Code Tip**: Hold musen over ethvert HTML-tag i VS Code for at se nyttige oplysninger fra MDN Web Docs, inklusive eksempler på brug og browserkompatibilitet.
> 💡 **VS Code-tip**: Hold musen over en HTML-tag i VS Code for at se nyttige oplysninger fra MDN Web Docs, inklusive brugseksempler og browserkompatibilitet.
> 📚 **Lær mere**: DOCTYPE-deklarationen forhindrer browsere i at gå ind i "quirks mode", som blev brugt til at understøtte meget gamle hjemmesider. Moderne webudvikling bruger den enkle `<!DOCTYPE html>`-deklaration for at sikre [standards-kompatibel rendering](https://developer.mozilla.org/docs/Web/HTML/Quirks_Mode_and_Standards_Mode).
> 📚 **Lær mere**: DOCTYPE-deklarationen forhindrer browsere i at gå i "quirks mode", som blev brugt til at understøtte meget gamle hjemmesider. Moderne webudvikling bruger den simple `<!DOCTYPE html>`-deklaration for at sikre [standard-kompatibel rendering](https://developer.mozilla.org/docs/Web/HTML/Quirks_Mode_and_Standards_Mode).
### 🔄 **Pædagogisk status**
**Pause og refleksion**: Før du fortsætter, skal du sikre dig, at du forstår:
- ✅ Hvorfor hvert HTML-dokument har brug for en DOCTYPE-deklaration
- ✅ Hvad `<html>`-root-elementet indeholder
- ✅ Hvordan denne struktur hjælper browsere med at vise sider korrekt
**Hurtig selvtest**: Kan du forklare med dine egne ord, hvad "standard-kompatibel rendering" betyder?
## Tilføjelse af essentiel dokumentmetadata
Sektionen `<head>` i et HTML-dokument indeholder vigtig information, som browsere og søgemaskiner har brug for, men som besøgende ikke ser direkte på siden. Tænk på det som "bag kulisserne"-information, der hjælper din webside med at fungere korrekt og vises korrekt på tværs af forskellige enheder og platforme.
`<head>`-sektionen i et HTML-dokument indeholder vigtig information, som browsere og søgemaskiner har brug for, men som besøgende ikke ser direkte på siden. Tænk på det som "bag kulisserne"-informationen, der hjælper din webside med at fungere korrekt og vises rigtigt på forskellige enheder og platforme.
Denne metadata fortæller browsere, hvordan de skal vise din side, hvilken tegnkodning der skal bruges, og hvordan de skal håndtere forskellige skærmstørrelser alt sammen essentielt for at skabe professionelle, tilgængelige websider.
Denne metadata fortæller browserne, hvordan de skal vise din side, hvilken tegnkodning der skal bruges, og hvordan forskellige skærmstørrelser håndteres alt sammen essentielt for at lave professionelle, tilgængelige websider.
### Opgave: Tilføj dokumentets hoved
### Opgave: Tilføj sidehovedet
Indsæt denne `<head>`-sektion mellem dine åbning og lukning `<html>`-tags:
Indsæt denne `<head>`-sektion mellem dine åbnings- og lukningstags `<html>`:
```html
<head>
@ -103,28 +173,28 @@ Indsæt denne `<head>`-sektion mellem dine åbning og lukning `<html>`-tags:
</head>
```
**Gennemgang af, hvad hvert element gør:**
- **Indstiller** sidens titel, der vises i browserfaner og søgeresultater
- **Angiver** UTF-8 tegnkodning for korrekt tekstvisning verden over
**Hvad hvert element gør:**
- **Sætter** sidetitlen, som vises i browsertabs og søgeresultater
- **Specificerer** UTF-8 tegnkodning for korrekt visning af tekst verden over
- **Sikrer** kompatibilitet med moderne versioner af Internet Explorer
- **Konfigurerer** responsivt design ved at indstille viewport til at matche enhedens bredde
- **Kontrollerer** initial zoom-niveau for at vise indhold i naturlig størrelse
- **Konfigurerer** responsivt design ved at sætte viewport til at matche enhedens bredde
- **Styrer** startzoom for at vise indhold i naturlig størrelse
> 🤔 **Tænk over dette**: Hvad ville der ske, hvis du indstillede en viewport meta-tag som denne: `<meta name="viewport" content="width=600">`? Dette ville tvinge siden til altid at være 600 pixels bred, hvilket ville ødelægge responsivt design! Lær mere om [korrekt viewport-konfiguration](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag).
> 🤔 **Tænk over dette**: Hvad ville der ske, hvis du satte en viewport meta-tag som denne: `<meta name="viewport" content="width=600">`? Det ville tvinge siden til altid at være 600 pixels bred, hvilket bryder det responsive design! Læs mere om [korrekt viewport-konfiguration](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag).
## Opbygning af dokumentets krop
## Bygning af dokumentets krop
Elementet `<body>` indeholder alt det synlige indhold på din webside alt, hvad brugerne vil se og interagere med. Mens sektionen `<head>` gav instruktioner til browseren, indeholder sektionen `<body>` det faktiske indhold: tekst, billeder, knapper og andre elementer, der skaber din brugergrænseflade.
`<body>`-elementet indeholder alt synligt indhold på din webside alt brugerne vil se og interagere med. Mens `<head>`-sektionen giver instruktioner til browseren, indeholder `<body>`-sektionen det faktiske indhold: tekst, billeder, knapper og andre elementer, der udgør brugerfladen.
Lad os tilføje kropsstrukturen og forstå, hvordan HTML-tags arbejder sammen for at skabe meningsfuldt indhold.
Lad os tilføje kroppens struktur og forstå, hvordan HTML-tags arbejder sammen for at skabe meningsfuldt indhold.
### Forstå HTML-tagstruktur
### Forståelse af HTML-tagstruktur
HTML bruger parrede tags til at definere elementer. De fleste tags har et åbningstag som `<p>` og et lukningstag som `</p>`, med indhold imellem: `<p>Hello, world!</p>`. Dette skaber et afsnitselement, der indeholder teksten "Hello, world!".
HTML bruger parrede tags til at definere elementer. De fleste tags har en åbnings-tag som `<p>` og en luknings-tag som `</p>`, med indhold imellem: `<p>Hello, world!</p>`. Det skaber et afsnitselement, der indeholder teksten "Hello, world!".
### Opgave: Tilføj kropselementet
### Opgave: Tilføj body-elementet
Opdater din HTML-fil for at inkludere elementet `<body>`:
Opdater din HTML-fil til at inkludere `<body>`-elementet:
```html
<!DOCTYPE html>
@ -139,24 +209,24 @@ Opdater din HTML-fil for at inkludere elementet `<body>`:
</html>
```
**Her er, hvad denne komplette struktur giver:**
- **Etablerer** den grundlæggende HTML5-dokumentramme
- **Inkluderer** essentiel metadata for korrekt browsergengivelse
- **Opretter** en tom krop klar til dit synlige indhold
**Denne komplette struktur giver:**
- **Etablerer** det grundlæggende HTML5-dokumentrammeværk
- **Inkluderer** vigtig metadata for korrekt browser-gengivelse
- **Skaber** en tom krop klar til dit synlige indhold
- **Følger** moderne webudviklings bedste praksis
Nu er du klar til at tilføje de synlige elementer i dit terrarium. Vi vil bruge `<div>`-elementer som beholdere til at organisere forskellige sektioner af indhold og `<img>`-elementer til at vise plantebilleder.
Nu er du klar til at tilføje de synlige elementer i dit terrarium. Vi bruger `<div>`-elementer som containere til at organisere forskellige sektioner af indhold, og `<img>`-elementer til at vise plantebilleder.
### Arbejde med billeder og layoutbeholdere
### Arbejde med billeder og layoutcontainere
Billeder er specielle i HTML, fordi de bruger "selvlukkende" tags. I modsætning til elementer som `<p></p>`, der omslutter indhold, indeholder `<img>`-tagget alle de oplysninger, det har brug for, inden for selve tagget ved hjælp af attributter som `src` til billedfilens sti og `alt` for tilgængelighed.
Billeder er specielle i HTML, fordi de bruger "self-closing" tags. I modsætning til elementer som `<p></p>`, der omslutter indhold, indeholder `<img>`-taget al den nødvendige information i selve tagget ved hjælp af attributter som `src` for billedfilens sti og `alt` for tilgængelighed.
Før du tilføjer billeder til din HTML, skal du organisere dine projektfiler korrekt ved at oprette en billedmappe og tilføje plantegrafikken.
Før du tilføjer billeder til din HTML, skal du organisere dine projektfiler korrekt ved at oprette en billedmappe og tilføje plantegrafikkerne.
**Først skal du opsætte dine billeder:**
1. Opret en mappe kaldet `images` inde i din terrarium-projektmappe
**Først, sæt dine billeder op:**
1. Opret en mappe kaldet `images` inde i din terrariumprojektmappe
2. Download plantebillederne fra [løsningsmappen](../../../../3-terrarium/solution/images) (14 plantebilleder i alt)
3. Kopier alle plantebillederne ind i din nye `images`-mappe
3. Kopier alle plantebilleder ind i din nye `images`-mappe
### Opgave: Opret plantevisningslayoutet
@ -166,109 +236,152 @@ Tilføj nu plantebillederne organiseret i to kolonner mellem dine `<body></body>
<div id="page">
<div id="left-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant1" src="../../../../translated_images/plant1.d87946a2ca70cc4316bda6e6c3af7210fbe9ada5539a7885141a9ce0efaf7be3.da.png" />
<img class="plant" alt="plant" id="plant1" src="../../../../translated_images/plant1.d87946a2ca70cc43.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant2" src="../../../../translated_images/plant2.8daa1606c9c1ad896bb171212c7d1d882e504b76b8ec3a2d1c337d775cf50dc3.da.png" />
<img class="plant" alt="plant" id="plant2" src="../../../../translated_images/plant2.8daa1606c9c1ad89.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant3" src="../../../../translated_images/plant3.8b0d484381a2a2a77c5c06ad97ab6ae5b7023da8c6c7678b0183bc0e46ea17a7.da.png" />
<img class="plant" alt="plant" id="plant3" src="../../../../translated_images/plant3.8b0d484381a2a2a7.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant4" src="../../../../translated_images/plant4.656e16ae1df37be2af5f4e7b5ab6c5decc432c3d3ec2eb98b904ddbecad49db0.da.png" />
<img class="plant" alt="plant" id="plant4" src="../../../../translated_images/plant4.656e16ae1df37be2.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant5" src="../../../../translated_images/plant5.2b41b9355f11ebccd62d327f5f14e56531ecda9c6f970bc89e386ee9f0273bb0.da.png" />
<img class="plant" alt="plant" id="plant5" src="../../../../translated_images/plant5.2b41b9355f11ebcc.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant6" src="../../../../translated_images/plant6.3d1827d03b6569946be13ae5da1f32947ae56732638a43757a7c616a6adccc5d.da.png" />
<img class="plant" alt="plant" id="plant6" src="../../../../translated_images/plant6.3d1827d03b656994.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant7" src="../../../../translated_images/plant7.8152c302ac97f621a6c595bdf3939103568f9efc7d3b06a0f02a1ea66f479de0.da.png" />
<img class="plant" alt="plant" id="plant7" src="../../../../translated_images/plant7.8152c302ac97f621.da.png" />
</div>
</div>
<div id="right-container" class="container">
<div class="plant-holder">
<img class="plant" alt="plant" id="plant8" src="../../../../translated_images/plant8.38d6428174ffa850a47cd1b81d528fa528adda7d23f3ae0bb42f4a27356ca5e6.da.png" />
<img class="plant" alt="plant" id="plant8" src="../../../../translated_images/plant8.38d6428174ffa850.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant9" src="../../../../translated_images/plant9.f0e38d3327c37fc29cd2734d48d20c2cf69300898ece6d46708829e02ce540e3.da.png" />
<img class="plant" alt="plant" id="plant9" src="../../../../translated_images/plant9.f0e38d3327c37fc2.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant10" src="../../../../translated_images/plant10.b159d6d6e985595f56d86b4b38061b8e7b4c9969c210c199fe967269cf935e7f.da.png" />
<img class="plant" alt="plant" id="plant10" src="../../../../translated_images/plant10.b159d6d6e985595f.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant11" src="../../../../translated_images/plant11.2a03a1c2ec8ea84ef3a80c06cc6883f3960fbb669f2c0b0bd824ba33d7eb7d32.da.png" />
<img class="plant" alt="plant" id="plant11" src="../../../../translated_images/plant11.2a03a1c2ec8ea84e.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant12" src="../../../../translated_images/plant12.60e9b53e538fbaf3e5797ebf800acb483baf5639e6cf378292ac2321ab8a5ea9.da.png" />
<img class="plant" alt="plant" id="plant12" src="../../../../translated_images/plant12.60e9b53e538fbaf3.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant13" src="../../../../translated_images/plant13.07a51543c820bcf57f67a9a6c0acbd6211ff795e2e67a42a9718224534e95fab.da.png" />
<img class="plant" alt="plant" id="plant13" src="../../../../translated_images/plant13.07a51543c820bcf5.da.png" />
</div>
<div class="plant-holder">
<img class="plant" alt="plant" id="plant14" src="../../../../translated_images/plant14.6e486371ba7d36ba3520d9828887993cb4c3edad8bdd8ff9b1b315717ff8cb63.da.png" />
<img class="plant" alt="plant" id="plant14" src="../../../../translated_images/plant14.6e486371ba7d36ba.da.png" />
</div>
</div>
</div>
```
**Trin for trin, her er hvad der sker i denne kode:**
- **Opretter** en hovedsidebeholder med `id="page"` til at holde alt indhold
- **Etablerer** to kolonnebeholdere: `left-container` og `right-container`
**Her sker der trin for trin i koden:**
- **Opretter** en hovedside-container med `id="page"` til at holde alt indhold
- **Etablerer** to kolonnecontainere: `left-container` og `right-container`
- **Organiserer** 7 planter i venstre kolonne og 7 planter i højre kolonne
- **Omslutter** hvert plantebillede i en `plant-holder` div for individuel positionering
- **Anvender** konsistente klassenavne til CSS-styling i næste lektion
- **Tildeler** unikke ID'er til hvert plantebillede for JavaScript-interaktion senere
- **Inkluderer** korrekte filstier, der peger på billedmappen
> 🤔 **Overvej dette**: Bemærk, at alle billeder i øjeblikket har den samme alt-tekst "plant". Dette er ikke ideelt for tilgængelighed. Brugere af skærmlæsere ville høre "plant" gentaget 14 gange uden at vide, hvilken specifik plante hvert billede viser. Kan du tænke på bedre, mere beskrivende alt-tekst for hvert billede?
> 📝 **HTML-elementtyper**: `<div>`-elementer er "blokniveau" og optager fuld bredde, mens `<span>`-elementer er "inline" og kun optager nødvendig bredde. Hvad tror du, der ville ske, hvis du ændrede alle disse `<div>`-tags til `<span>`-tags?
Med denne markup tilføjet vil planterne vises på skærmen, selvom de endnu ikke ser polerede ud det er det, CSS er til i næste lektion! For nu har du et solidt HTML-fundament, der organiserer dit indhold korrekt og følger bedste praksis for tilgængelighed.
## Brug af semantisk HTML for tilgængelighed
Semantisk HTML betyder at vælge HTML-elementer baseret på deres betydning og formål, ikke kun deres udseende. Når du bruger semantisk markup, kommunikerer du strukturen og betydningen af dit indhold til browsere, søgemaskiner og hjælpemidler som skærmlæsere.
Denne tilgang gør dine hjemmesider mere tilgængelige for brugere med handicap og hjælper søgemaskiner med bedre at forstå dit indhold. Det er et grundlæggende princip for moderne webudvikling, der skaber bedre oplevelser for alle.
- **Omslutter** hvert plantebillede i en `plant-holder` div til individuel placering
- **Anvender** konsekvente klassenavne til CSS-styling i næste lektion
- **Tildeler** unikke IDer til hvert plantebillede til JavaScript-interaktion senere
- **Indeholder** korrekte filstier, der peger på billedmappen
> 🤔 **Overvej dette**: Bemærk, at alle billeder i øjeblikket har samme alt-tekst "plant". Det er ikke optimalt for tilgængelighed. Brugere med skærmlæsere vil høre "plant" gentaget 14 gange uden at vide, hvilken specifik plante hvert billede viser. Kan du tænke på bedre, mere beskrivende alt-tekster for hvert billede?
> 📝 **HTML-elementtyper**: `<div>`-elementer er "blokniveau" og fylder hele bredden, mens `<span>`-elementer er "inline" og kun fylder nødvendig bredde. Hvad tror du der ville ske, hvis du ændrede alle disse `<div>`-tags til `<span>`-tags?
### 🔄 **Pædagogisk status**
**Strukturforståelse**: Tag et øjeblik til at gennemgå din HTML-struktur:
- ✅ Kan du identificere hovedcontainerne i dit layout?
- ✅ Forstår du, hvorfor hvert billede har en unik ID?
- ✅ Hvordan vil du beskrive formålet med `plant-holder` diverne?
**Visuel inspektion**: Åbn din HTML-fil i en browser. Du burde se:
- En basal liste af plantebilleder
- Billeder organiseret i to kolonner
- Simpelt, ustylet layout
**Husk**: Dette simple udseende er præcis, hvordan HTML skal se ud, før CSS styling!
Med denne markup tilføjet vil planterne vises på skærmen, selvom de endnu ikke ser polerede ud det er hvad CSS er til i næste lektion! For nu har du et solidt HTML-fundament, der korrekt organiserer dit indhold og følger bedste praksis for tilgængelighed.
## Brug af semantisk HTML til tilgængelighed
Semantisk HTML betyder at vælge HTML-elementer baseret på deres betydning og formål, ikke kun deres udseende. Når du bruger semantisk markup, kommunikerer du indholdets struktur og betydning til browsere, søgemaskiner og hjælpemetoder som skærmlæsere.
```mermaid
flowchart TD
A[Skal der tilføjes indhold?] --> B{Hvilken type?}
B -->|Hovedoverskrift| C["<h1>"]
B -->|Underoverskrift| D["<h2>, <h3>, etc."]
B -->|Afsnit| E["<p>"]
B -->|Liste| F["<ul>, <ol>"]
B -->|Navigation| G["<nav>"]
B -->|Artikel| H["<article>"]
B -->|Sektion| I["<section>"]
B -->|Generisk beholder| J["<div>"]
C --> K[Skærmlæsere annoncerer som hovedtitel]
D --> L[Opretter korrekt overskriftsstruktur]
E --> M[Giver korrekt tekstafstand]
F --> N[Muliggør genveje til listnavigation]
G --> O[Identificerer navigationslandemærker]
H --> P[Markerer selvstændigt indhold]
I --> Q[Grupperer relateret indhold]
J --> R[Brug kun når ingen semantisk tag passer]
style C fill:#4caf50
style D fill:#4caf50
style E fill:#4caf50
style F fill:#4caf50
style G fill:#2196f3
style H fill:#2196f3
style I fill:#2196f3
style J fill:#ff9800
```
Denne tilgang gør dine hjemmesider mere tilgængelige for brugere med handicap og hjælper søgemaskiner med bedre at forstå dit indhold. Det er et grundlæggende princip i moderne webudvikling, der skaber bedre oplevelser for alle.
### Tilføjelse af en semantisk sidetitel
### Tilføj en semantisk sidetitel
Lad os tilføje en ordentlig overskrift til din terrarium-side. Indsæt denne linje lige efter dit åbningstag `<body>`:
Lad os tilføje en ordentlig overskrift til din terrariumsside. Indsæt denne linje lige efter dit åbnings-tag `<body>`:
```html
<h1>My Terrarium</h1>
```
**Hvorfor semantisk markup er vigtig:**
- **Hjælper** skærmlæsere med at navigere og forstå sidestruktur
- **Forbedrer** søgemaskineoptimering (SEO) ved at tydeliggøre indholdshierarki
- **Øger** tilgængeligheden for brugere med synshandicap eller kognitive forskelle
**Hvorfor semantisk markup betyder noget:**
- **Hjælper** skærmlæsere med at navigere og forstå sidestrukturen
- **Forbedrer** søgemaskineoptimering (SEO) ved at tydeliggøre indholdshierarkiet
- **Forstærker** tilgængelighed for brugere med syns- eller kognitive forskelle
- **Skaber** bedre brugeroplevelser på tværs af alle enheder og platforme
- **Følger** webstandarder og bedste praksis for professionel udvikling
- **Følger** webstandarder og bedste praksis til professionel udvikling
**Eksempler på semantiske vs. ikke-semantiske valg:**
| Formål | ✅ Semantisk valg | ❌ Ikke-semantisk valg |
|---------|-------------------|------------------------|
| Hovedoverskrift | `<h1>Title</h1>` | `<div class="big-text">Title</div>` |
| Hovedoverskrift | `<h1>Titel</h1>` | `<div class="big-text">Titel</div>` |
| Navigation | `<nav><ul><li></li></ul></nav>` | `<div class="menu"><div></div></div>` |
| Knap | `<button>Click me</button>` | `<span onclick="...">Click me</span>` |
| Knap | `<button>Klik på mig</button>` | `<span onclick="...">Klik på mig</span>` |
| Artikelindhold | `<article><p></p></article>` | `<div class="content"><div></div></div>` |
> 🎥 **Se det i aktion**: Se [hvordan skærmlæsere interagerer med websider](https://www.youtube.com/watch?v=OUDV1gqs9GA) for at forstå, hvorfor semantisk markup er afgørende for tilgængelighed. Bemærk, hvordan korrekt HTML-struktur hjælper brugere med at navigere effektivt.
> 🎥 **Se det i praksis**: Se [hvordan skærmlæsere interagerer med websider](https://www.youtube.com/watch?v=OUDV1gqs9GA) for at forstå, hvorfor semantisk markup er afgørende for tilgængelighed. Bemærk, hvordan korrekt HTML-struktur hjælper brugere med at navigere effektivt.
## Oprettelse af terrarium-beholderen
## Oprettelse af terrariumcontaineren
Lad os nu tilføje HTML-strukturen til selve terrariet glasbeholderen, hvor planter senere vil blive placeret. Denne sektion demonstrerer et vigtigt koncept: HTML giver struktur, men uden CSS-styling vil disse elementer endnu ikke være synlige.
Lad os nu tilføje HTML-strukturen til selve terrariet glasbeholderen, hvor planter til sidst skal placeres. Denne sektion demonstrerer et vigtigt koncept: HTML giver struktur, men uden CSS-styling vil disse elementer endnu ikke være synlige.
Terrarium-markupen bruger beskrivende klassenavne, der vil gøre CSS-styling intuitiv og vedligeholdelsesvenlig i næste lektion.
Terrarium-markupen bruger beskrivende klassenavne, som vil gøre CSS-styling intuitivt og vedligeholdeligt i næste lektion.
### Opgave: Tilføj terrarium-strukturen
### Opgave: Tilføj terrariumstrukturen
Indsæt denne markup over det sidste `</div>`-tag (før lukningstaget for sidebeholderen):
Indsæt denne markup ovenover det sidste `</div>` tag (før lukningstagen for sidecontaineren):
```html
<div id="terrarium">
@ -282,84 +395,202 @@ Indsæt denne markup over det sidste `</div>`-tag (før lukningstaget for sidebe
</div>
```
**Forstå denne terrarium-struktur:**
- **Opretter** en hovedterrarium-beholder med et unikt ID til styling
**Forståelse af denne terrariumstruktur:**
- **Opretter** en hovedterrarium-container med et unikt ID til styling
- **Definerer** separate elementer for hver visuel komponent (top, vægge, jord, bund)
- **Inkluderer** indlejrede elementer for glaseffekter (glossy elementer)
- **Bruger** beskrivende klassenavne, der tydeligt angiver hvert elements formål
- **Forbereder** strukturen til CSS-styling, der vil skabe glas-terrarium-udseendet
- **Indeholder** indlejrede elementer til glasrefleksionseffekter (glansfulde elementer)
- **Bruger** beskrivende klassenavne, som tydeligt angiver hvert elements formål
- **Forbereder** strukturen til CSS-styling, der vil skabe glasterrariumets udseende
> 🤔 **Lægger du mærke til noget?**: Selvom du har tilføjet denne markup, ser du ikke noget nyt på siden! Dette illustrerer perfekt, hvordan HTML leverer struktur, mens CSS står for udseendet. Disse `<div>`-elementer findes, men har endnu ingen visuel styling det kommer i næste lektion!
```mermaid
flowchart TD
A[HTML Dokument] --> B[Dokumenthoved]
A --> C[Dokumentkrop]
B --> D[Titelemnet]
B --> E[Meta Tegnsæt]
B --> F[Meta Visningsvindue]
C --> G[Hovedoverskrift]
C --> H[Sidebeholder]
H --> I[Venstre beholder med 7 planter]
H --> J[Højre beholder med 7 planter]
H --> K[Terrariefremstilling]
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e8
style H fill:#f3e5f5
```
### 🔄 **Pædagogisk Tjek-ind**
**Beherskelse af HTML-struktur**: Før du går videre, sørg for at du kan:
- ✅ Forklare forskellen mellem HTML-struktur og visuelt udseende
- ✅ Identificere semantiske vs. ikke-semantiske HTML-elementer
- ✅ Beskrive hvordan korrekt markup gavner tilgængelighed
- ✅ Genkende den komplette dokumenttræsstruktur
> 🤔 **Bemærk noget?**: Selvom du har tilføjet denne markup, ser du ikke noget nyt på siden! Dette illustrerer perfekt, hvordan HTML giver struktur, mens CSS giver udseende. Disse `<div>`-elementer eksisterer, men har endnu ingen visuel styling det kommer i næste lektion!
**Test din forståelse**: Prøv at åbne din HTML-fil i en browser med JavaScript slået fra og CSS fjernet. Dette viser dig den rene semantiske struktur, du har skabt!
---
## GitHub Copilot Agent Udfordring
## GitHub Copilot Agent-udfordring
Brug Agent-tilstand til at fuldføre følgende udfordring:
**Beskrivelse:** Opret en semantisk HTML-struktur til en planteplejevejledning, der kunne tilføjes til terrarium-projektet.
**Prompt:** Opret en semantisk HTML-sektion, der inkluderer en hovedoverskrift "Guide til plantepleje", tre underafsnit med overskrifterne "Vanding", "Lyskrav" og "Jordpleje", hver med et afsnit om planteplejeinformation. Brug korrekte semantiske HTML-tags som `<section>`, `<h2>`, `<h3>` og `<p>` til at strukturere indholdet korrekt.
**Beskrivelse:** Opret en semantisk HTML-struktur til en planteplejevejledning, der kan tilføjes til terrariumprojektet.
**Prompt:** Opret en semantisk HTML-sektion, der inkluderer en hovedoverskrift "Plant Care Guide", tre undersektioner med overskrifterne "Watering", "Light Requirements" og "Soil Care", hver med et afsnit om planteplejeinformation. Brug korrekte semantiske HTML-tags som `<section>`, `<h2>`, `<h3>`, og `<p>` til at strukturere indholdet passende.
Lær mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## Udforsk HTML-historieudfordring
## Udforsk HTML-historie-udfordring
**Lær om webudviklingens historie**
**Lær om webudviklingens evolution**
HTML har udviklet sig markant, siden Tim Berners-Lee skabte den første webbrowser på CERN i 1990. Nogle ældre tags som `<marquee>` er nu forældede, fordi de ikke fungerer godt med moderne tilgængelighedsstandarder og principper for responsivt design.
HTML har udviklet sig betydeligt siden Tim Berners-Lee skabte den første webbrowser hos CERN i 1990. Nogle ældre tags som `<marquee>` er nu forældede, fordi de ikke fungerer godt med moderne tilgængelighedsstandarder og responsive designprincipper.
**Prøv dette eksperiment:**
1. Indpak midlertidigt din `<h1>` titel i et `<marquee>` tag: `<marquee><h1>Mit terrarium</h1></marquee>`
1. Pak midlertidigt din `<h1>`-titel ind i et `<marquee>`-tag: `<marquee><h1>My Terrarium</h1></marquee>`
2. Åbn din side i en browser og observer den rullende effekt
3. Overvej, hvorfor dette tag blev forældet (hint: tænk på brugeroplevelse og tilgængelighed)
4. Fjern `<marquee>` tagget og vend tilbage til semantisk markup
3. Overvej hvorfor dette tag blev forældet (hint: tænk på brugeroplevelse og tilgængelighed)
4. Fjern `<marquee>`-taget og vend tilbage til semantisk markup
**Refleksionsspørgsmål:**
- Hvordan kan en rullende titel påvirke brugere med synshandicap eller følsomhed over for bevægelse?
- Hvilke moderne CSS-teknikker kan opnå lignende visuelle effekter mere tilgængeligt?
- Hvorfor er det vigtigt at bruge aktuelle webstandarder i stedet for forældede elementer?
- Hvorfor er det vigtigt at bruge gældende webstandarder frem for forældede elementer?
Udforsk mere om [forældede og udgåede HTML-elementer](https://developer.mozilla.org/docs/Web/HTML/Element#Obsolete_and_deprecated_elements) for at forstå, hvordan webstandarder udvikler sig for at forbedre brugeroplevelsen.
## Quiz efter forelæsning
## Quiz efter lektionen
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/16)
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/16)
## Gennemgang & Selvstudie
## Gennemgang og selvstudie
**Uddyb din HTML-viden**
**Dyk dybere ned i din HTML-viden**
HTML har været fundamentet for internettet i over 30 år og har udviklet sig fra et simpelt dokumentmærkningssprog til en sofistikeret platform til opbygning af interaktive applikationer. At forstå denne udvikling hjælper dig med at værdsætte moderne webstandarder og træffe bedre udviklingsbeslutninger.
HTML har dannet fundamentet for nettet i over 30 år, og er gået fra et simpelt dokumentmarkup-sprog til en avanceret platform for at bygge interaktive applikationer. Forståelsen af denne udvikling hjælper dig med at værdsætte moderne webstandarder og træffe bedre udviklingsbeslutninger.
**Anbefalede læringsveje:**
1. **HTML-historie og udvikling**
- Undersøg tidslinjen fra HTML 1.0 til HTML5
- Udforsk, hvorfor visse tags blev forældede (tilgængelighed, mobilvenlighed, vedligeholdelse)
- Undersøg nye HTML-funktioner og forslag
- Udforsk hvorfor visse tags blev forældede (tilgængelighed, mobilvenlighed, vedligeholdelse)
- Undersøg nye funktioner og forslag til HTML
2. **Dybdegående semantisk HTML**
- Studér den komplette liste over [HTML5 semantiske elementer](https://developer.mozilla.org/docs/Web/HTML/Element)
- Øv dig i at identificere, hvornår du skal bruge `<article>`, `<section>`, `<aside>` og `<main>`
- Lær om ARIA-attributter for forbedret tilgængelighed
- Øv dig i at identificere hvornår du skal bruge `<article>`, `<section>`, `<aside>` og `<main>`
- Lær om ARIA-attributter til forbedret tilgængelighed
3. **Moderne webudvikling**
- Udforsk [opbygning af responsive websites](https://docs.microsoft.com/learn/modules/build-simple-website/?WT.mc_id=academic-77807-sagibbon) på Microsoft Learn
- Forstå, hvordan HTML integreres med CSS og JavaScript
- Udforsk [at bygge responsive websites](https://docs.microsoft.com/learn/modules/build-simple-website/?WT.mc_id=academic-77807-sagibbon) på Microsoft Learn
- Forstå hvordan HTML integreres med CSS og JavaScript
- Lær om webperformance og SEO bedste praksis
**Refleksionsspørgsmål:**
- Hvilke forældede HTML-tags opdagede du, og hvorfor blev de fjernet?
- Hvilke nye HTML-funktioner bliver foreslået til fremtidige versioner?
- Hvilke nye HTML-funktioner foreslås til fremtidige versioner?
- Hvordan bidrager semantisk HTML til webtilgængelighed og SEO?
### ⚡ **Hvad du kan nå på de næste 5 minutter**
- [ ] Åbn DevTools (F12) og inspicer HTML-strukturen på dit yndlingswebsite
- [ ] Opret en simpel HTML-fil med grundlæggende tags: `<h1>`, `<p>`, og `<img>`
- [ ] Valider din HTML med W3C HTML-validator online
- [ ] Prøv at tilføje en kommentar til din HTML ved hjælp af `<!-- comment -->`
### 🎯 **Hvad du kan opnå i denne time**
- [ ] Gennemfør quizzen efter lektionen og gennemgå koncepter om semantisk HTML
- [ ] Byg en simpel hjemmeside om dig selv med korrekt HTML-struktur
- [ ] Eksperimenter med forskellige overskriftsniveauer og tekstformaterings-tags
- [ ] Tilføj billeder og links for at øve multimediaintegration
- [ ] Undersøg HTML5-funktioner, du ikke har prøvet endnu
### 📅 **Din ugelange HTML-rejse**
- [ ] Færdiggør terrariumprojekt-øvelsen med semantisk markup
- [ ] Opret en tilgængelig hjemmeside med ARIA-labels og roller
- [ ] Øv formoprettelse med forskellige input-typer
- [ ] Udforsk HTML5 APIs som localStorage eller geolocation
- [ ] Studér responsive HTML-mønstre og mobile-first design
- [ ] Gennemgå andre udvikleres HTML-kode for bedste praksis
### 🌟 **Dit månedlange webgrundlag**
- [ ] Byg en porteføljeside, der viser dit HTML-mesterskab
- [ ] Lær HTML-templating med et framework som Handlebars
- [ ] Bidrag til open source-projekter ved at forbedre HTML-dokumentation
- [ ] Mestre avancerede HTML-koncepter som custom elements
- [ ] Integrer HTML med CSS-frameworks og JavaScript-biblioteker
- [ ] Vær mentor for andre, der lærer HTML-grundlaget
## 🎯 Din HTML-mesterskabstidslinje
```mermaid
timeline
title HTML Lærings-Progression
section Grundlag (5 minutter)
Dokumentstruktur: DOCTYPE erklæring
: HTML rodclement
: Forståelse af Head vs Body
section Metadata (10 minutter)
Væsentlige Meta Tags: Tegnkodning
: Viewport konfiguration
: Browser-kompatibilitet
section Indholdsoprettelse (15 minutter)
Billedintegration: Korrekte filstier
: Betydningen af alt-tekst
: Selv-lukkende tags
section Layout Organisering (20 minutter)
Container Strategi: Div elementer til struktur
: Klasse- og ID-navngivning
: Indlejret elementhierarki
section Semantisk Færdighed (30 minutter)
Meningsfuld Markup: Overskrifthierarki
: Skærmlæsernavigation
: Tilgængeligheds bedste praksis
section Avancerede Koncepter (1 time)
HTML5 Funktioner: Moderne semantiske elementer
: ARIA attributter
: Ydeevne overvejelser
section Professionelle Færdigheder (1 uge)
Kode Organisering: Filstrukturmønstre
: Vedligeholdelig markup
: Team samarbejde
section Ekspertniveau (1 måned)
Moderne Webstandarder: Progressiv forbedring
: Kryds-browser kompatibilitet
: Opdateringer i HTML-specifikation
```
### 🛠️ Din HTML-værktøjssamling
Efter at have gennemført denne lektion har du nu:
- **Dokumentstruktur**: Fuld HTML5-fundament med korrekt DOCTYPE
- **Semantisk markup**: Meningsfulde tags, der forbedrer tilgængelighed og SEO
- **Billedeintegration**: Korrekt filorganisering og brug af alt-tekster
- **Layoutcontainere**: Strategisk brug af divs med beskrivende klassenavne
- **Tilgængelighedsbevidsthed**: Forståelse for skærmlæsere og navigation
- **Moderne standarder**: Aktuelle HTML5-praksisser og viden om forældede tags
- **Projekts фундамент**: Solid base til CSS-styling og JavaScript-interaktivitet
**Næste skridt**: Din HTML-struktur er klar til CSS-styling! Det semantiske fundament, du har bygget, vil gøre næste lektion meget nemmere at forstå.
## Opgave
[Øv dig i HTML: Byg en blogmockup](assignment.md)
[Øv dig i HTML: Byg en blogmodel](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiske oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå ved brug af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,87 +1,141 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "92c4431eac70670b0450b02c1d11279a",
"translation_date": "2025-10-23T22:03:08+00:00",
"original_hash": "e39f3a4e3bcccf94639e3af1248f8a4d",
"translation_date": "2026-01-06T23:59:57+00:00",
"source_file": "3-terrarium/2-intro-to-css/README.md",
"language_code": "da"
}
-->
# Terrarium Projekt Del 2: Introduktion til CSS
![Introduktion til CSS](../../../../translated_images/webdev101-css.3f7af5991bf53a200d79e7257e5e450408d8ea97f5b531d31b2e3976317338ee.da.png)
```mermaid
journey
title Din CSS Styling Rejse
section Fundament
Link CSS-fil: 3: Student
Forstå cascade: 4: Student
Lær arv: 4: Student
section Vælgere
Element målretning: 4: Student
Klasse mønstre: 5: Student
ID specificitet: 5: Student
section Layout
Positioner elementer: 4: Student
Opret containere: 5: Student
Byg terrarium: 5: Student
section Polering
Tilføj visuelle effekter: 5: Student
Responsivt design: 5: Student
Glasreflektioner: 5: Student
```
![Introduktion til CSS](../../../../translated_images/webdev101-css.3f7af5991bf53a20.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
Kan du huske, hvordan dit HTML-terrarium så ret enkelt ud? Med CSS kan vi forvandle den enkle struktur til noget visuelt tiltalende.
Hvis HTML er som at bygge rammen til et hus, så er CSS alt det, der får det til at føles som et hjem - farverne på væggene, møblernes placering, belysningen og hvordan rummene hænger sammen. Tænk på, hvordan Versailles-paladset startede som en simpel jagthytte, men med omhyggelig opmærksomhed på dekoration og layout blev det forvandlet til en af verdens mest storslåede bygninger.
I dag vil vi forvandle dit terrarium fra funktionelt til poleret. Du vil lære, hvordan man placerer elementer præcist, skaber layouts, der reagerer på forskellige skærmstørrelser, og skaber den visuelle appel, der gør hjemmesider engagerende.
Ved slutningen af denne lektion vil du se, hvordan strategisk CSS-styling kan forbedre dit projekt dramatisk. Lad os tilføje lidt stil til dit terrarium.
## Quiz før lektionen
Kan du huske, hvordan dit HTML-terrarium så temmelig grundlæggende ud? CSS er stedet, hvor vi forvandler den enkle struktur til noget visuelt tiltalende.
Hvis HTML er som at bygge rammen til et hus, så er CSS alt det, der får det til at føles som et hjem malefarverne, møblernes placering, belysningen og hvordan rummene flyder sammen. Tænk på, hvordan Versailles-paladset startede som en simpel jagtlodge, men med omhyggelig opmærksomhed på dekoration og layout blev det til et af verdens mest fantastiske bygninger.
I dag vil vi forvandle dit terrarium fra funktionelt til poleret. Du vil lære at placere elementer præcist, lave layouts der tilpasser sig forskellige skærmstørrelser, og skabe det visuelle udtryk, som gør hjemmesider engagerende.
Ved slutningen af denne lektion vil du se, hvordan strategisk CSS-styling dramatisk kan forbedre dit projekt. Lad os tilføre lidt stil til dit terrarium.
```mermaid
mindmap
root((CSS Grundlæggende))
Cascade
Specificity Regler
Arv
Prioritetsrækkefølge
Konfliktløsning
Selectors
Element Tags
Klasser (.class)
IDs (#id)
Kombinatorer
Box Model
Margin
Kant
Polstring
Indhold
Layout
Positionering
Display Typer
Flexbox
Grid
Visuelle Effekter
Farver
Skygger
Overgange
Animationer
Responsivt Design
Medieforespørgsler
Fleksible Enheder
Viewport Meta
Mobile Først
```
## Forud-forelæsning quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/17)
[Forud-forelæsningsquiz](https://ff-quizzes.netlify.app/web/quiz/17)
## Kom godt i gang med CSS
CSS bliver ofte betragtet som "at gøre tingene pæne," men det tjener et langt bredere formål. CSS er som at være instruktør for en film - du styrer ikke kun, hvordan alt ser ud, men også hvordan det bevæger sig, reagerer på interaktion og tilpasser sig forskellige situationer.
CSS bliver ofte betragtet som blot at "gøre ting pæne," men det tjener et langt bredere formål. CSS er som at være instruktør på en film du kontrollerer ikke kun, hvordan alt ser ud, men også hvordan det bevæger sig, reagerer på interaktion og tilpasser sig forskellige situationer.
Moderne CSS er utroligt kapabelt. Du kan skrive kode, der automatisk justerer layouts til telefoner, tablets og computere. Du kan skabe glidende animationer, der leder brugernes opmærksomhed derhen, hvor det er nødvendigt. Resultaterne kan være ret imponerende, når alt fungerer sammen.
Moderne CSS er utroligt kapabelt. Du kan skrive kode, som automatisk justerer layouts til telefoner, tablets og desktopcomputere. Du kan skabe glatte animationer, der guider brugerens opmærksomhed, hvor det er nødvendigt. Resultaterne kan være ret imponerende, når alt spiller sammen.
> 💡 **Pro Tip**: CSS udvikler sig konstant med nye funktioner og kapaciteter. Tjek altid [CanIUse.com](https://caniuse.com) for at verificere browserstøtte til nyere CSS-funktioner, før du bruger dem i produktionsprojekter.
> 💡 **Pro Tip**: CSS udvikler sig konstant med nye funktioner og muligheder. Tjek altid [CanIUse.com](https://caniuse.com) for at verificere browserunderstøttelse for nyere CSS-funktioner, før du bruger dem i produktionsprojekter.
**Her er, hvad vi vil opnå i denne lektion:**
- **Skaber** et komplet visuelt design til dit terrarium ved hjælp af moderne CSS-teknikker
- **Udforsker** grundlæggende begreber som kaskade, arv og CSS-selektorer
- **Implementerer** responsive positionerings- og layoutstrategier
- **Bygger** terrariumbeholderen ved hjælp af CSS-former og styling
- **Skabe** et komplet visuelt design til dit terrarium ved hjælp af moderne CSS-teknikker
- **Udforske** grundlæggende koncepter som cascade, arv og CSS-selektorer
- **Implementere** responsive positionerings- og layoutstrategier
- **Bygge** terrarium-beholderen ved hjælp af CSS-former og styling
### Forudsætning
Du bør have fuldført HTML-strukturen for dit terrarium fra den forrige lektion og have det klar til at blive stylet.
Du skal have færdiggjort HTML-strukturen til dit terrarium fra den forrige lektion og have den klar til at blive stylet.
> 📺 **Videomateriale**: Se denne nyttige video-gennemgang
> 📺 **Video Ressource**: Se denne hjælpsomme videovejledning
>
> [![CSS Basics Tutorial](https://img.youtube.com/vi/6yIdOIV9p1I/0.jpg)](https://www.youtube.com/watch?v=6yIdOIV9p1I)
### Opsætning af din CSS-fil
Før vi kan begynde at style, skal vi forbinde CSS til vores HTML. Denne forbindelse fortæller browseren, hvor den skal finde stylinginstruktionerne til vores terrarium.
Før vi kan begynde at style, skal vi forbinde CSS til vores HTML. Denne forbindelse fortæller browseren, hvor den kan finde stylinginstruktionerne til vores terrarium.
I din terrarium-mappe skal du oprette en ny fil kaldet `style.css` og derefter linke den i din HTML-dokuments `<head>`-sektion:
I din terrarium-mappe, opret en ny fil kaldet `style.css`, og link den derefter i din HTML-dokus `<head>` sektion:
```html
<link rel="stylesheet" href="./style.css" />
```
**Hvad denne kode gør:**
**Her er, hvad denne kode gør:**
- **Opretter** en forbindelse mellem dine HTML- og CSS-filer
- **Fortæller** browseren at indlæse og anvende stilarterne fra `style.css`
- **Bruger** attributten `rel="stylesheet"` for at specificere, at dette er en CSS-fil
- **Refererer** til filstien med `href="./style.css"`
- **Fortæller** browseren at hente og anvende stilarterne fra `style.css`
- **Bruger** `rel="stylesheet"` attributten for at angive, at det er en CSS-fil
- **Refererer** filstien med `href="./style.css"`
## Forstå CSS-kaskaden
## Forståelse af CSS Cascade
Har du nogensinde undret dig over, hvorfor CSS kaldes "Cascading" Style Sheets? Stilarter kaskaderer ned som et vandfald, og nogle gange konflikter de med hinanden.
Har du nogensinde undret dig over, hvorfor CSS kaldes "Cascading" Style Sheets? Stilarter strømmer ned som et vandfald, og nogle gange kommer de i konflikt med hinanden.
Tænk på, hvordan militære kommandostrukturer fungerer - en generel ordre kan sige "alle tropper skal bære grønt," men en specifik ordre til din enhed kan sige "bær blå uniformer til ceremonien." Den mere specifikke instruktion har forrang. CSS følger lignende logik, og det gør fejlfinding meget mere håndterbart, når man forstår denne hierarki.
Tænk på, hvordan militær kommando virker en general ordre kunne sige "alle tropper skal have grøn uniform," men en specifik ordre til din enhed kunne sige "bær ceremoni-blåt til begivenheden." Den mere specifikke instruktion har forrang. CSS følger lignende logik, og at forstå denne hierarki gør fejlfinding meget lettere.
### Eksperimenter med kaskadeprioritet
### Eksperiment med kaskadeprioritet
Lad os se kaskaden i aktion ved at skabe en stilkonflikt. Først skal du tilføje en inline-stil til din `<h1>`-tag:
Lad os se kaskaden i aktion ved at skabe en stil konflikt. Først tilføj en inline style til din `<h1>` tag:
```html
<h1 style="color: red">My Terrarium</h1>
```
**Hvad denne kode gør:**
- **Anvender** en rød farve direkte på `<h1>`-elementet ved hjælp af inline-styling
- **Bruger** attributten `style` til at indlejre CSS direkte i HTML
- **Skaber** den højeste prioritet for stilreglen for dette specifikke element
- **Anvender** en rød farve direkte på `<h1>` elementet ved brug af inline-styling
- **Bruger** `style` attributten til at indlejre CSS direkte i HTML
- **Skaber** den højeste prioriterede regler for netop dette element
Tilføj derefter denne regel til din `style.css`-fil:
Dernæst tilføj denne regel til din `style.css` fil:
```css
h1 {
@ -90,28 +144,49 @@ h1 {
```
**I ovenstående har vi:**
- **Defineret** en CSS-regel, der målretter alle `<h1>`-elementer
- **Indstillet** tekstfarven til blå ved hjælp af et eksternt stylesheet
- **Skabt** en lavere prioritet regel sammenlignet med inline-stilarter
**Videnscheck**: Hvilken farve vises i din webapp? Hvorfor vinder den farve? Kan du tænke på scenarier, hvor du måske vil tilsidesætte stilarter?
> 💡 **CSS-prioritetsrækkefølge (højeste til laveste):**
> 1. **Inline-stilarter** (style-attribut)
- **Defineret** en CSS-regel, der målretter alle `<h1>` elementer
- **Sat** tekstfarven til blå ved brug af ekstern stylesheet
- **Skabt** en lavere prioriteret regel i forhold til inline styles
**Videnstjek**: Hvilken farve vises i din webapp? Hvorfor vinder netop den farve? Kan du tænke på situationer, hvor du vil tilsidesætte stilarter?
```mermaid
flowchart TD
A["Browser støder på h1 element"] --> B{"Tjek for inline-stilarter"}
B -->|Fundet| C["style='color: red'"]
B -->|Ingen| D{"Tjek for ID-regler"}
C --> E["Anvend rød farve (1000 point)"]
D -->|Fundet| F["#heading { color: green }"]
D -->|Ingen| G{"Tjek for klasse-regler"}
F --> H["Anvend grøn farve (100 point)"]
G -->|Fundet| I[".title { color: blue }"]
G -->|Ingen| J{"Tjek element-regler"}
I --> K["Anvend blå farve (10 point)"]
J -->|Fundet| L["h1 { color: purple }"]
J -->|Ingen| M["Brug browser standard"]
L --> N["Anvend lilla farve (1 point)"]
style C fill:#ff6b6b
style F fill:#51cf66
style I fill:#339af0
style L fill:#9775fa
```
> 💡 **CSS Prioritetsorden (højeste til laveste):**
> 1. **Inline styles** (style attribut)
> 2. **IDs** (#myId)
> 3. **Klasser** (.myClass) og attributter
> 4. **Element-selektorer** (h1, div, p)
> 5. **Browser-standarder**
> 5. **Browserstandarder**
## CSS-arv i praksis
## CSS Arv i praksis
CSS-arv fungerer som genetik - elementer arver visse egenskaber fra deres forældrelementer. Hvis du indstiller skrifttypen på body-elementet, vil al tekst indeni automatisk bruge den samme skrifttype. Det er lidt som hvordan Habsburg-familiens karakteristiske kæbelinje dukkede op gennem generationer uden at være specificeret for hver enkelt person.
CSS arv fungerer som genetik elementer arver visse egenskaber fra deres forældrelementer. Hvis du sætter font-familien på `<body>` elementet, bruger al tekst inden i automatisk den samme font. Det minder om, hvordan Habsburg familiens karakteristiske kæbelinje optrådte gennem generationer uden at skulle specificeres for hver enkelt.
Men ikke alt bliver arvet. Tekststilarter som skrifttyper og farver arves, men layoutegenskaber som marginer og kanter gør ikke. Ligesom børn kan arve fysiske træk, men ikke deres forældres tøjstil.
Men ikke alt arves. Tekststile som skrifttyper og farver arves, mens layout-egenskaber som margin og rammer ikke gør. Ligesom børn kan arve fysiske træk, men ikke deres forældres tøjstil.
### Observering af skrifttypearv
### Observering af font-arv
Lad os se arv i aktion ved at indstille en skrifttype på `<body>`-elementet:
Lad os se arv i praksis ved at sætte en font-familie på `<body>` elementet:
```css
body {
@ -120,30 +195,45 @@ body {
```
**Hvad der sker her:**
- **Indstiller** skrifttypen for hele siden ved at målrette `<body>`-elementet
- **Bruger** en skrifttype-stak med alternative muligheder for bedre browserkompatibilitet
- **Anvender** moderne systemskrifttyper, der ser godt ud på tværs af forskellige operativsystemer
- **Sikrer**, at alle underordnede elementer arver denne skrifttype, medmindre det specifikt tilsidesættes
- **Sætter** font-familien for hele siden ved at målrette `<body>` elementet
- **Bruger** en font-stak med fallback muligheder for bedre browser-kompatibilitet
- **Anvender** moderne systemfonte, der ser godt ud på forskellige operativsystemer
- **Sikrer** at alle børnelementer arver denne font, medmindre de specifikt overskrives
Åbn din browsers udviklerværktøjer (F12), naviger til fanen Elements, og inspicer dit `<h1>`-element. Du vil se, at det arver skrifttypen fra body:
Åbn din browsers udviklerværktøjer (F12), gå til Elements fanen, og undersøg dit `<h1>` element. Du vil se, at det arver font-familien fra body:
![arvet skrifttype](../../../../translated_images/1.cc07a5cbe114ad1d4728c35134584ac1b87db688eff83cf75985cf31fe0ed95c.da.png)
![arvet font](../../../../translated_images/1.cc07a5cbe114ad1d.da.png)
**Eksperimenttid**: Prøv at indstille andre arvelige egenskaber på `<body>` som `color`, `line-height` eller `text-align`. Hvad sker der med din overskrift og andre elementer?
**Eksperimenter**: Prøv at sætte andre arvelige egenskaber på `<body>` som `color`, `line-height` eller `text-align`. Hvad sker der med din overskrift og andre elementer?
> 📝 **Arvelige egenskaber inkluderer**: `color`, `font-family`, `font-size`, `line-height`, `text-align`, `visibility`
>
> **Ikke-arvelige egenskaber inkluderer**: `margin`, `padding`, `border`, `width`, `height`, `position`
## Mestre CSS-selektorer
### 🔄 **Pædagogisk Tjek-ind**
**CSS Grundlæggende Forståelse**: Før du går videre til selektorer, skal du kunne:
- ✅ Forklare forskellen mellem cascade og arv
- ✅ Forudsige hvilken stil der vinder i en specifik konflikt
- ✅ Identificere hvilke egenskaber der arves fra forældrelementer
- ✅ Korrekt forbinde CSS-filer til HTML
CSS-selektorer er din måde at målrette specifikke elementer for styling. De fungerer som at give præcise anvisninger - i stedet for at sige "huset," kan du sige "det blå hus med den røde dør på Maple Street."
**Hurtig Test**: Hvis du har disse stilarter, hvilken farve får et `<h1>` inde i en `<div class="special">`?
```css
div { color: blue; }
.special { color: green; }
h1 { color: red; }
```
*Svar: Rød (element-selektor målretter direkte h1)*
## Mestre CSS Selektorer
CSS selektorer er din måde at målrette specifikke elementer til styling. De fungerer som at give præcise anvisninger i stedet for at sige "huset", kan du sige "det blå hus med den røde dør på Maple Street."
CSS tilbyder forskellige måder at være specifik på, og det at vælge den rigtige selektor er som at vælge det passende værktøj til opgaven. Nogle gange skal du style alle døre i nabolaget, og andre gange kun én specifik dør.
CSS tilbyder forskellige måder at være specifik på, og at vælge den rigtige selektor er som at vælge det rigtige værktøj til opgaven. Nogle gange skal du style alle døre i nabolaget, og andre gange kun en enkelt bestemt dør.
### Element-selektorer (Tags)
### Element-selektorer (tags)
Element-selektorer målretter HTML-elementer efter deres tag-navn. De er perfekte til at indstille basisstilarter, der gælder bredt på tværs af din side:
Element-selektorer målretter HTML-elementer på deres tag-navn. De er perfekte til at sætte basale stilarter, som gælder bredt på din side:
```css
body {
@ -160,19 +250,19 @@ h1 {
}
```
**Forståelse af disse stilarter:**
- **Indstiller** konsekvent typografi på hele siden med `body`-selektoren
- **Fjerner** standard browser-marginer og -polstring for bedre kontrol
**Disse stilarter betyder:**
- **Sætter** en ensartet typografi på hele siden med `body` selektoren
- **Fjerner** standard browser-marginer og padding for bedre kontrol
- **Styler** alle overskriftselementer med farve, justering og afstand
- **Bruger** `rem`-enheder for skalerbar, tilgængelig skrifttypestørrelse
- **Bruger** `rem` enheder til skalerbar og tilgængelig skriftstørrelse
Selvom element-selektorer fungerer godt til generel styling, har du brug for mere specifikke selektorer for at style individuelle komponenter som planterne i dit terrarium.
Mens element-selektorer fungerer godt til generel styling, har du brug for mere specifikke selektorer for at style individuelle komponenter som planterne i dit terrarium.
### ID-selektorer til unikke elementer
ID-selektorer bruger symbolet `#` og målretter elementer med specifikke `id`-attributter. Da ID'er skal være unikke på en side, er de perfekte til at style individuelle, specielle elementer som vores venstre og højre plantebeholdere.
ID-selektorer bruger `#` symbolet og målretter elementer med specifikke `id` attributter. Da IDs skal være unikke på en side, er de perfekte til at style individuelle, særlige elementer som vores venstre og højre plantebeholdere.
Lad os oprette styling til vores terrariums sidebeholdere, hvor planterne skal være:
Lad os lave stilen til terrariets sidecontainere, hvor planterne skal bo:
```css
#left-container {
@ -199,13 +289,13 @@ Lad os oprette styling til vores terrariums sidebeholdere, hvor planterne skal v
```
**Hvad denne kode opnår:**
- **Placerer** beholdere ved de yderste venstre og højre kanter ved hjælp af `absolute` positionering
- **Bruger** `vh` (viewport height) enheder for responsiv højde, der tilpasser sig skærmstørrelsen
- **Anvender** `box-sizing: border-box`, så polstring inkluderes i den samlede bredde
- **Fjerner** unødvendige `px`-enheder fra nulværdier for renere kode
- **Indstiller** en subtil baggrundsfarve, der er mere behagelig for øjnene end skarp grå
- **Positionerer** containere i yderste venstre og højre kant ved brug af `absolute` positionering
- **Bruger** `vh` (viewport højde) enheder for responsiv højde, der tilpasser sig skærmstørrelse
- **Anvender** `box-sizing: border-box` så padding indgår i den totale bredde
- **Fjerner** unødvendige `px` enheder fra nul-værdier for renere kode
- **Sætter** en diskret baggrundsfarve, der er lettere for øjnene end skarp grå
**Kodekvalitetsudfordring**: Bemærk, hvordan denne CSS bryder DRY-princippet (Don't Repeat Yourself). Kan du omstrukturere det ved at bruge både en ID og en klasse?
**Kodekvalitet Udfordring**: Bemærk hvordan denne CSS overtræder DRY-princippet (Don't Repeat Yourself). Kan du refaktorere den ved at bruge både et ID og en klasse?
**Forbedret tilgang:**
```html
@ -235,24 +325,24 @@ Lad os oprette styling til vores terrariums sidebeholdere, hvor planterne skal v
### Klasse-selektorer til genanvendelige stilarter
Klasse-selektorer bruger symbolet `.` og er perfekte, når du vil anvende de samme stilarter på flere elementer. I modsætning til ID'er kan klasser genbruges i hele din HTML, hvilket gør dem ideelle til konsistente stylingmønstre.
Klasse-selektorer bruger `.` symbolet og er perfekte, når du vil anvende de samme stilarter på flere elementer. I modsætning til IDs kan klasser genbruges over hele dit HTML, hvilket gør dem ideelle til konsistente stylingmønstre.
I vores terrarium har hver plante brug for lignende styling, men også individuel placering. Vi vil bruge en kombination af klasser til fælles stilarter og ID'er til unik placering.
I vores terrarium har hver plante brug for lignende styling, men også individuel positionering. Vi bruger en kombination af klasser til delte stilarter og IDs til unik placering.
**Her er HTML-strukturen for hver plante:**
```html
<div class="plant-holder">
<img class="plant" alt="Decorative plant for terrarium" id="plant1" src="../../../../translated_images/plant1.d18b18ffe73da18f8b1ac7aba73b4050af52f4a0c9174aeac464b85123fc2850.da.png" />
<img class="plant" alt="Decorative plant for terrarium" id="plant1" src="../../../../translated_images/plant1.d18b18ffe73da18f.da.png" />
</div>
```
**Vigtige elementer forklaret:**
- **Bruger** `class="plant-holder"` for ensartet container-styling på tværs af alle planter
- **Anvender** `class="plant"` for fælles billedstyling og adfærd
- **Inkluderer** unik `id="plant1"` for individuel placering og JavaScript-interaktion
- **Giver** beskrivende alt-tekst for skærmlæser-tilgængelighed
- **Bruger** `class="plant-holder"` for ensartet container-styling på alle planter
- **Anvender** `class="plant"` til delt billed-styling og adfærd
- **Indeholder** unik `id="plant1"` til individuel positionering og JavaScript-interaktion
- **Giver** beskrivende alt-tekst for skærmlæser tilgængelighed
Tilføj nu disse stilarter til din `style.css`-fil:
Tilføj nu disse stilarter til din `style.css` fil:
```css
.plant-holder {
@ -274,37 +364,53 @@ Tilføj nu disse stilarter til din `style.css`-fil:
}
```
**Nedbrydning af disse stilarter:**
- **Skaber** relativ positionering for planteholderen for at etablere en positioneringskontekst
- **Indstiller** hver planteholder til 13% højde, så alle planter passer lodret uden scrolling
- **Flytter** holdere lidt til venstre for bedre at centrere planter inden for deres beholdere
**Hvad disse stilarter gør:**
- **Opretter** relativ positionering for planteholderen for at etablere en positionerings-kontekst
- **Sætter** hver planteholder til 13% højde, så alle planter passer lodret uden scroll
- **Skubber** holdere let mod venstre for bedre centrerede planter i deres containere
- **Tillader** planter at skalere responsivt med `max-width` og `max-height` egenskaber
- **Bruger** `z-index` til at placere planter over andre elementer i terrariet
- **Tilføjer** en subtil hover-effekt med CSS-overgange for bedre brugerinteraktion
- **Bruger** `z-index` til at lægge planter ovenpå andre elementer i terrariet
- **Tilføjer** en subtil hover-effekt med CSS transitions for bedre brugerinteraktion
**Kritisk tænkning**: Hvorfor har vi brug for både `.plant-holder` og `.plant` selektorer? Hvad ville der ske, hvis vi kun prøvede at bruge én?
**Kritisk Tænkning**: Hvorfor har vi brug for både `.plant-holder` og `.plant` selektorer? Hvad ville ske, hvis vi kun brugte én?
> 💡 **Designmønster**: Containeren (`.plant-holder`) styrer layout og positionering, mens indholdet (`.plant`) styrer udseende og skalering. Denne adskillelse gør koden mere vedligeholdelsesvenlig og fleksibel.
## Forstå CSS-positionering
## Forstå CSS Positionering
CSS-positionering er som at være scenedirektør for et teaterstykke - du styrer, hvor hver skuespiller står, og hvordan de bevæger sig rundt på scenen. Nogle skuespillere følger standardformationen, mens andre har brug for specifik placering for dramatisk effekt.
CSS positionering er som at være sceneinstruktør i et teaterstykke du bestemmer, hvor hver skuespiller skal stå og hvordan de bevæger sig på scenen. Nogle skuespillere følger standardformationen, mens andre har brug for specifik placering for dramatisk effekt.
Når du forstår positionering, bliver mange layoutudfordringer håndterbare. Har du brug for en navigationsbjælke, der forbliver øverst, mens brugerne scroller? Positionering klarer det. Vil du have en tooltip, der vises på et specifikt sted? Det er også positionering.
Når du forstår positionering, bliver mange layoutudfordringer håndterbare. Skal du bruge en navigationslinje, som bliver øverst, når brugerne scroller? Positionering håndterer det. Vil du have et tooltip, der vises et bestemt sted? Det er også positionering.
### De fem positioneringsværdier
| Positioneringsværdi | Adfærd | Brugsscenarie |
|---------------------|--------|---------------|
| `static` | Standardflow, ignorerer top/left/right/bottom | Normal dokumentlayout |
| `relative` | Positioneret relativt til sin normale position | Små justeringer, skaber positioneringskontekst |
```mermaid
quadrantChart
title CSS Positioneringsstrategi
x-axis Dokumentflow --> Fjernet fra flow
y-axis Statisk position --> Præcis kontrol
quadrant-1 Absolut
quadrant-2 Fast
quadrant-3 Statisk
quadrant-4 Klæbrig
Static: [0.2, 0.2]
Relative: [0.3, 0.6]
Absolute: [0.8, 0.8]
Fixed: [0.9, 0.7]
Sticky: [0.5, 0.9]
```
| Position Værdi | Adfærd | Brugsscenarie |
|----------------|----------|----------|
| `static` | Standard flow, ignorerer top/left/right/bottom | Normal dokumentlayout |
| `relative` | Positioneret relativt til sin normale position | Små justeringer, skabe positioneringskontekst |
| `absolute` | Positioneret relativt til nærmeste positionerede forfader | Præcis placering, overlays |
| `fixed` | Positioneret relativt til viewport | Navigationsbjælker, flydende elementer |
| `sticky` | Skifter mellem relativ og fast baseret på scroll | Overskrifter, der klæber ved scrolling |
| `fixed` | Positioneret relativt til viewport | Navigationselementer, flydende elementer |
| `sticky` | Skifter mellem relativ og fast baseret på scroll | Overskrifter der sidder fast ved scroll |
### Positionering i vores terrarium
Vores terrarium bruger en strategisk kombination af positioneringstyper til at skabe det ønskede layout:
Vores terrarium bruger en strategisk kombination af positionerings-typer for at skabe det ønskede layout:
```css
/* Container positioning */
@ -327,26 +433,60 @@ Vores terrarium bruger en strategisk kombination af positioneringstyper til at s
```
**Forståelse af positioneringsstrategien:**
- **Absolutte containere** fjernes fra det normale dokumentflow og fastgøres til skærmkanten
- **Relativ planteholder** skaber en positioneringskontekst, mens den forbliver i dokumentflowet
- **Absolutte containere** fjernes fra normal dokumentflow og fastgøres på skærmkanten
- **Relative planteholdere** skaber positioneringskontekst samtidig med at de forbliver i dokumentflowet
- **Absolutte planter** kan placeres præcist inden for deres relative containere
- **Denne kombination** gør det muligt for planter at stables lodret, mens de kan placeres individuelt
- **Denne kombination** tillader planter at stables vertikalt, samtidig med individuel positionering
> 🎯 **Hvorfor dette er vigtigt**: `plant`-elementerne har brug for absolut positionering for at kunne blive trukket og flyttet i den næste lektion. Absolut positionering fjerner dem fra det normale layoutflow, hvilket gør drag-and-drop-interaktioner mulige.
> 🎯 **Hvorfor det er vigtigt**: `plant` elementerne skal have absolut positionering for at kunne blive dragbare i næste lektion. Absolut positionering fjerner dem fra normal layout-flow, hvilket muliggør drag-and-drop interaktioner.
**Eksperimenttid**: Prøv at ændre positioneringsværdierne og observer resultaterne:
**Eksperimenter**: Prøv at ændre positioneringsværdierne og observer resultaterne:
- Hvad sker der, hvis du ændrer `.container` fra `absolute` til `relative`?
- Hvordan ændrer layoutet sig, hvis `.plant-holder` bruger `absolute` i stedet for `relative`?
- Hvad sker der, når du skifter `.plant` til `relative` positionering?
## Bygning af terrariet med CSS
Nu skal vi bygge en glasbeholder ved hjælp af kun CSS - ingen billeder eller grafisk software er nødvendige.
At skabe realistisk udseende glas, skygger og dybdeeffekter ved hjælp af positionering og gennemsigtighed demonstrerer CSS's visuelle kapaciteter. Denne teknik minder om, hvordan arkitekter i Bauhaus-bevægelsen brugte simple geometriske former til at skabe komplekse, smukke strukturer. Når du forstår disse principper, vil du genkende CSS-teknikkerne bag mange webdesigns.
### 🔄 **Pædagogisk status**
**CSS Positioneringsfærdigheder**: Stop op og bekræft din forståelse:
- ✅ Kan du forklare, hvorfor planter har brug for absolut positionering for drag-and-drop?
- ✅ Forstår du, hvordan relative containere skaber et positioneringskontekst?
- ✅ Hvorfor bruger sidecontainerne absolut positionering?
- ✅ Hvad ville der ske, hvis du fjernede positioneringsdeklarationerne helt?
**Forbindelse til virkeligheden**: Tænk på, hvordan CSS-positionering afspejler layout i den virkelige verden:
- **Statisk**: Bøger på en hylde (naturlig orden)
- **Relativ**: Flytte en bog lidt, men beholde dens plads
- **Absolut**: Placere et bogmærke på et præcist sidetal
- **Fast**: En klæbrig seddel, der bliver synlig, mens du bladrer sider
## Byg terrariet med CSS
Nu bygger vi et glasglas kun med CSS ingen billeder eller grafisk software nødvendig.
At skabe realistisk glas, skygger og dybdeeffekter ved hjælp af positionering og gennemsigtighed demonstrerer CSSs visuelle kapabiliteter. Denne teknik spejler, hvordan arkitekter i Bauhaus-bevægelsen brugte simple geometriske former til at skabe komplekse, flotte strukturer. Når du forstår disse principper, vil du genkende CSS-teknikker bag mange webdesigns.
```mermaid
flowchart LR
A[Glaslåg] --> E[Færdigt Terrarium]
B[Glasvægge] --> E
C[Jordlag] --> E
D[Glasbund] --> E
F[Glaseffekter] --> E
A1["50% bredde<br/>5% højde<br/>Øverste position"] --> A
B1["60% bredde<br/>80% højde<br/>Afrundede hjørner<br/>0.5 opacitet"] --> B
C1["60% bredde<br/>5% højde<br/>Mørkebrun<br/>Bundlag"] --> C
D1["50% bredde<br/>1% højde<br/>Bundposition"] --> D
F1["Diskrete skygger<br/>Gennemsigtighed<br/>Z-indeks lagdeling"] --> F
style E fill:#d1e1df,stroke:#3a241d
style A fill:#e8f5e8
style B fill:#e8f5e8
style C fill:#8B4513
style D fill:#e8f5e8
```
### Oprettelse af glasbeholderens komponenter
### Skabelse af glasbeholderens komponenter
Lad os bygge terrarium-krukken stykke for stykke. Hver del bruger absolut positionering og procentbaseret størrelsesangivelse for responsivt design:
Lad os bygge terrariumglaset stykke for stykke. Hver del bruger absolut positionering og procentbaserede størrelser for responsivt design:
```css
.jar-walls {
@ -398,58 +538,70 @@ Lad os bygge terrarium-krukken stykke for stykke. Hver del bruger absolut positi
}
```
**Forståelse af terrarium-konstruktionen:**
- **Bruger** procentbaserede dimensioner for responsiv skalering på tværs af alle skærmstørrelser
- **Positionerer** elementer absolut for at stable og justere dem præcist
**Forstå terrariumkonstruktionen:**
- **Bruger** procentbaserede dimensioner for responsiv skalering på alle skærmstørrelser
- **Positionerer** elementer absolut for præcis stabling og justering
- **Anvender** forskellige opacitetsværdier for at skabe glaseffekten
- **Implementerer** `z-index` lagdeling, så planterne vises inde i krukken
- **Tilføjer** subtile skygger og raffineret afrunding af kanter for et mere realistisk udseende
- **Implementerer** `z-index` lagdeling, så planter vises inde i glasset
- **Tilføjer** diskret box-shadow og finpudset border-radius for mere realistisk udseende
### Responsivt design med procentangivelser
### Responsivt design med procent
Bemærk, hvordan alle dimensioner bruger procentangivelser i stedet for faste pixelværdier:
Bemærk, hvordan alle dimensioner bruger procent i stedet for faste pixelværdier:
**Hvorfor det er vigtigt:**
- **Sikrer**, at terrariet skalerer proportionalt på enhver skærmstørrelse
- **Bevarer** de visuelle relationer mellem krukkens komponenter
- **Giver** en ensartet oplevelse fra mobiltelefoner til store desktop-skærme
- **Tillader**, at designet tilpasser sig uden at bryde den visuelle layout
- **Sikrer** at terrariet skalerer proportionelt på enhver skærmstørrelse
- **Bevarer** de visuelle relationer mellem glaskomponenterne
- **Giver** en ensartet oplevelse fra mobilen til store stationære skærme
- **Tillader** designet at tilpasse sig uden at bryde layoutets udseende
### CSS-enheder i praksis
### CSS-enheder i aktion
Vi bruger `rem`-enheder til afrunding af kanter, som skalerer i forhold til rodens skriftstørrelse. Dette skaber mere tilgængelige designs, der respekterer brugerens skriftpræferencer. Læs mere om [CSS relative enheder](https://www.w3.org/TR/css-values-3/#font-relative-lengths) i den officielle specifikation.
Vi bruger `rem` enheder til border-radius, som skalerer relativt til roden fontstørrelse. Dette skaber mere tilgængelige designs, der respekterer brugerens fontpræferencer. Læs mere om [CSS relative enheder](https://www.w3.org/TR/css-values-3/#font-relative-lengths) i den officielle specifikation.
**Visuel eksperimentering**: Prøv at ændre disse værdier og observer effekterne:
- Ændre krukkens opacitet fra 0.5 til 0.8 hvordan påvirker det glaseffekten?
- Juster jordens farve fra `#3a241d` til `#8B4513` hvilken visuel effekt har det?
- Modificer jordens `z-index` til 2 hvad sker der med lagdelingen?
- Ændr glassets opacitet fra 0.5 til 0.8 hvordan påvirker det glasudseendet?
- Juster jordfarven fra `#3a241d` til `#8B4513` hvilken visuel effekt får det?
- Ændr `z-index` på jorden til 2 hvad sker der med lagdelingen?
### 🔄 **Pædagogisk status**
**Forståelse af visuel CSS**: Bekræft din forståelse af visuelt CSS:
- ✅ Hvordan skaber procentbaserede dimensioner responsivt design?
- ✅ Hvorfor skaber opacitet glaseffekten?
- ✅ Hvilken rolle spiller z-index i lagdeling af elementer?
- ✅ Hvordan skaber border-radius værdier glassets form?
**Designprincip**: Bemærk, hvordan vi bygger komplekse visuelle effekter ud fra enkle former:
1. **Rektangler****Afrundede rektangler** → **Glaskomponenter**
2. **Flade farver****Opacitet** → **Glaseffekt**
3. **Individuelle elementer****Lag på lag** → **3D-udseende**
---
## GitHub Copilot Agent-udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at fuldføre denne udfordring:
**Beskrivelse:** Opret en CSS-animation, der får terrarium-planterne til at svaje blidt frem og tilbage, som om de bevæger sig i en naturlig brise. Dette vil hjælpe dig med at øve CSS-animationer, transformationer og keyframes, samtidig med at det forbedrer terrariets visuelle appel.
**Beskrivelse:** Opret en CSS-animation, der får terrariets planter til forsigtigt at svaje frem og tilbage, som i en naturlig brise. Dette hjælper dig med at øve CSS-animationer, transforms og keyframes samtidig med at dit terrariums visuelle appel forbedres.
**Opgave:** Tilføj CSS-keyframe-animationer for at få planterne i terrariet til at svaje blidt fra side til side. Opret en svajende animation, der roterer hver plante let (2-3 grader) til venstre og højre med en varighed på 3-4 sekunder, og anvend den på `.plant`-klassen. Sørg for, at animationen kører uendeligt og har en easing-funktion for naturlig bevægelse.
**Prompt:** Tilføj CSS keyframe-animationer for at få planterne i terrariet til blidt at svaje fra side til side. Skab en svajende animation, der roterer hver plante let (2-3 grader) til venstre og højre med en varighed på 3-4 sekunder, og anvend den på `.plant` klassen. Sørg for, at animationen kører uendeligt i loop og bruger en easing-funktion for naturlig bevægelse.
Læs mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring: Tilføj glasreflektioner
## 🚀 Udfordring: Tilføjelse af glasreflektioner
Klar til at forbedre dit terrarium med realistiske glasreflektioner? Denne teknik vil tilføje dybde og realisme til designet.
Klar til at forbedre dit terrarium med realistiske glasreflektioner? Denne teknik tilføjer dybde og realisme til designet.
Du vil skabe subtile highlights, der simulerer, hvordan lys reflekteres på glasoverflader. Denne tilgang minder om, hvordan renæssancemalere som Jan van Eyck brugte lys og refleksion til at få malet glas til at fremstå tredimensionelt. Her er, hvad du sigter efter:
Du vil skabe subtile highlights, der simulerer, hvordan lys reflekteres på glasoverflader. Denne metode svarer til, hvordan renæssancens malere som Jan van Eyck brugte lys og refleksion til at få malet glas til at fremstå tredimensionelt. Sådan skal resultatet se ud:
![færdigt terrarium](../../../../translated_images/terrarium-final.2f07047ffc597d0a06b06cab28a77801a10dd12fdb6c7fc630e9c40665491c53.da.png)
![færdigt terrarium](../../../../translated_images/terrarium-final.2f07047ffc597d0a.da.png)
**Din udfordring:**
- **Skab** subtile hvide eller lyse ovale former til glasreflektionerne
- **Positioner** dem strategisk på venstre side af krukken
- **Anvend** passende opacitet og sløringseffekter for realistisk lysreflektion
- **Brug** `border-radius` til at skabe organiske, boblelignende former
- **Eksperimenter** med gradienter eller skygger for forbedret realisme
- **Opret** subtile hvide eller lyse ovale former til glasreflektionerne
- **Positioner** dem strategisk på venstre side af glasset
- **Anvend** passende opacitet og sløring for realistisk lysrefleksion
- **Brug** `border-radius` for at skabe organiske, bobleagtige former
- **Eksperimenter** med gradients eller box-shadows for forbedret realisme
## Quiz efter forelæsning
@ -457,30 +609,127 @@ Du vil skabe subtile highlights, der simulerer, hvordan lys reflekteres på glas
## Udvid din CSS-viden
CSS kan virke komplekst i starten, men forståelse af disse kernekoncepter giver et solidt fundament for mere avancerede teknikker.
CSS kan føles komplekst i starten, men forståelse af disse kernebegreber giver et solidt fundament til mere avancerede teknikker.
**Dine næste CSS-læringsområder:**
- **Flexbox** - gør det nemmere at justere og fordele elementer
- **CSS Grid** - giver kraftfulde værktøjer til at skabe komplekse layouts
- **CSS-variabler** - reducerer gentagelse og forbedrer vedligeholdelse
- **Responsivt design** - sikrer, at websites fungerer godt på tværs af forskellige skærmstørrelser
- **Flexbox** forenkler justering og distribution af elementer
- **CSS Grid** giver kraftfulde værktøjer til at skabe komplekse layouts
- **CSS-variabler** mindsker gentagelse og forbedrer vedligeholdelse
- **Responsivt design** sikrer at sider fungerer godt på forskellige skærmstørrelser
### Interaktive læringsressourcer
Øv dig på disse koncepter med disse engagerende, praktiske spil:
- 🐸 [Flexbox Froggy](https://flexboxfroggy.com/) - Bliv mester i Flexbox gennem sjove udfordringer
- 🌱 [Grid Garden](https://codepip.com/games/grid-garden/) - Lær CSS Grid ved at dyrke virtuelle gulerødder
- 🎯 [CSS Battle](https://cssbattle.dev/) - Test dine CSS-færdigheder med kodningsudfordringer
Øv disse koncepter med disse engagerende, praktiske spil:
- 🐸 [Flexbox Froggy](https://flexboxfroggy.com/) Mestre Flexbox gennem sjove udfordringer
- 🌱 [Grid Garden](https://codepip.com/games/grid-garden/) Lær CSS Grid ved at dyrke virtuelle gulerødder
- 🎯 [CSS Battle](https://cssbattle.dev/) Test dine CSS-evner med kodningsudfordringer
### Yderligere læring
For omfattende CSS-grundlæggende, gennemfør dette Microsoft Learn-modul: [Style your HTML app with CSS](https://docs.microsoft.com/learn/modules/build-simple-website/4-css-basics/?WT.mc_id=academic-77807-sagibbon)
For en grundig indføring i CSS, gennemfør denne Microsoft Learn modul: [Style your HTML app with CSS](https://docs.microsoft.com/learn/modules/build-simple-website/4-css-basics/?WT.mc_id=academic-77807-sagibbon)
### ⚡ **Hvad du kan nå på de næste 5 minutter**
- [ ] Åbn DevTools og undersøg CSS-stilarter på en hvilken som helst hjemmeside via Elements-panelet
- [ ] Opret en simpel CSS-fil og link den til en HTML-side
- [ ] Prøv at ændre farver ved hjælp af forskellige metoder: hex, RGB og navngivne farver
- [ ] Øv box-modellen ved at tilføje padding og margin til en div
### 🎯 **Hvad du kan opnå denne time**
- [ ] Fuldfør quizzen efter lektionen og gennemgå CSS-grundlæggende
- [ ] Style din HTML-side med fonte, farver og afstande
- [ ] Opret et simpelt layout med flexbox eller grid
- [ ] Eksperimenter med CSS-transitioner for glidende effekter
- [ ] Øv responsivt design med medieforespørgsler
### 📅 **Din ugelange CSS-rejse**
- [ ] Fuldfør terrariumstilingsopgaven med kreativ flair
- [ ] Mestre CSS Grid ved at bygge et fotogalleri-layout
- [ ] Lær CSS-animationer for at bringe dine designs til live
- [ ] Udforsk CSS preprocessorer som Sass eller Less
- [ ] Studér designprincipper og anvend dem i dit CSS
- [ ] Analyser og genskab interessante designs, du finder online
### 🌟 **Din månedslange designdomination**
- [ ] Byg et komplet responsivt webdesigntema
- [ ] Lær CSS-in-JS eller utility-first frameworks som Tailwind
- [ ] Bidrag til open source-projekter med CSS-forbedringer
- [ ] Mestre avancerede CSS-koncepter som CSS-tilpassede egenskaber og containment
- [ ] Skab genanvendelige komponentbiblioteker med modulær CSS
- [ ] Vejled andre, der lærer CSS, og del designviden
## 🎯 Din CSS-mesterskabstidslinje
```mermaid
timeline
title CSS Læringsforløb
section Grundlag (10 minutter)
Filforbindelse: Forbind CSS til HTML
: Forstå kaskaderegler
: Lær arv grundlæggende
section Selektorer (15 minutter)
Målrettede Elementer: Elementselektorer
: Klasse mønstre
: ID-specificitet
: Kombinatorer
section Boksemodel (20 minutter)
Layout Grundlæggende: Margin og padding
: Kant egenskaber
: Indholds-størrelse
: Box-sizing opførsel
section Positionering (25 minutter)
Elementsplacering: Statisk vs relativ
: Absolut positionering
: Z-index lagdeling
: Responsive enheder
section Visuelt Design (30 minutter)
Styling Mestring: Farver og opacitet
: Skygger og effekter
: Overgange
: Transform egenskaber
section Responsivt Design (45 minutter)
Multi-Device Support: Medieforespørgsler
: Fleksible layouts
: Mobil-først tilgang
: Viewport optimering
section Avancerede Teknikker (1 uge)
Moderne CSS: Flexbox layouts
: CSS Grid systemer
: Tilpassede egenskaber
: Animation keyframes
section Professionelle Færdigheder (1 måned)
CSS Arkitektur: Komponent mønstre
: Vedligeholdelig kode
: Performance optimering
: Cross-browser kompatibilitet
```
### 🛠️ Dit CSS-værktøjssæt resumé
Efter at have gennemført denne lektion har du nu:
- **Forståelse af kaskade**: Hvordan stilarter nedarves og overskrives
- **Selector-mesterskab**: Præcis målretning med elementer, klasser og IDer
- **Positioneringskompetencer**: Strategisk placering og lagdeling af elementer
- **Visuelt design**: Skabelse af glaseffekter, skygger og gennemsigtighed
- **Responsivitetsteknikker**: Procentbaserede layouts, der tilpasser sig enhver skærm
- **Kodeorganisation**: Ren, vedligeholdelsesvenlig CSS-struktur
- **Moderne praksis**: Brug af relative enheder og tilgængelige designmønstre
**Næste skridt**: Dit terrarium har nu både struktur (HTML) og stil (CSS). Den sidste lektion tilføjer interaktivitet med JavaScript!
## Opgave
[CSS Refactoring](assignment.md)
[CSS Refaktorering](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,105 +1,205 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "bc93f6285423033ebf5b8abeb5282888",
"translation_date": "2025-10-23T22:02:15+00:00",
"original_hash": "973e48ad87d67bf5bb819746c9f8e302",
"translation_date": "2026-01-06T23:58:12+00:00",
"source_file": "3-terrarium/3-intro-to-DOM-and-closures/README.md",
"language_code": "da"
}
-->
# Terrarium Projekt Del 3: DOM-manipulation og JavaScript Closures
![DOM og en closure](../../../../translated_images/webdev101-js.10280393044d7eaaec7e847574946add7ddae6be2b2194567d848b61d849334a.da.png)
```mermaid
journey
title Din JavaScript DOM Rejse
section Fundament
Forstå DOM: 3: Student
Lær closures: 4: Student
Forbind elementer: 4: Student
section Interaktion
Opsæt trækbegivenheder: 4: Student
Spor koordinater: 5: Student
Håndter bevægelse: 5: Student
section Polering
Tilføj oprydning: 4: Student
Test funktionalitet: 5: Student
Fuldfør terrarium: 5: Student
```
![DOM og en closure](../../../../translated_images/webdev101-js.10280393044d7eaa.da.png)
> Sketchnote af [Tomomi Imura](https://twitter.com/girlie_mac)
Velkommen til en af de mest engagerende aspekter af webudvikling - at gøre ting interaktive! Document Object Model (DOM) fungerer som en bro mellem din HTML og JavaScript, og i dag vil vi bruge det til at bringe dit terrarium til live. Da Tim Berners-Lee skabte den første webbrowser, forestillede han sig en web, hvor dokumenter kunne være dynamiske og interaktive - DOM gør den vision mulig.
Velkommen til en af de mest engagerende aspekter af webudvikling - at gøre ting interaktive! Document Object Model (DOM) er som en bro mellem dit HTML og JavaScript, og i dag bruger vi det til at bringe dit terrarium til live. Da Tim Berners-Lee skabte den første webbrowser, forestillede han sig et web, hvor dokumenter kunne være dynamiske og interaktive - DOM gør denne vision mulig.
Vi vil også udforske JavaScript closures, som måske lyder skræmmende i starten. Tænk på closures som "hukommelseslommer", hvor dine funktioner kan huske vigtig information. Det er som om hver plante i dit terrarium har sin egen dataregistrering til at spore sin position. Ved slutningen af denne lektion vil du forstå, hvor naturlige og nyttige de er.
Vi vil også udforske JavaScript closures, som måske lyder skræmmende til at begynde med. Tænk på closures som at skabe "hukommelseslommer", hvor dine funktioner kan huske vigtig information. Det er som om hver plante i dit terrarium har sin egen dataoptegnelse til at spore sin position. Ved slutningen af denne lektion vil du forstå, hvor naturlige og nyttige de er.
Her er, hvad vi bygger: et terrarium, hvor brugere kan trække og slippe planter hvor som helst. Du vil lære DOM-manipulationsteknikker, der driver alt fra drag-and-drop filuploads til interaktive spil. Lad os få dit terrarium til at komme til live.
Her er, hvad vi bygger: et terrarium, hvor brugere kan trække og slippe planter hvor som helst de ønsker. Du vil lære DOM-manipulationsteknikker, som driver alt fra træk-og-slip filuploads til interaktive spil. Lad os få dit terrarium til at leve.
## Quiz før lektionen
```mermaid
mindmap
root((DOM & JavaScript))
DOM Tree
Elementvalg
Egenskabstilgang
Hændelseshåndtering
Dynamiske Opdateringer
Hændelser
Pointerhændelser
Musehændelser
Berøringshændelser
Hændelseslyttere
Closures
Private Variabler
Funktionsscope
Hukommelsespermanens
Tilstandsadministration
Træk & Slip
Positionssporing
Koordinatgeometri
Hændelseslivscyklus
Brugerinteraktion
Moderne Mønstre
Hændelsesdelegering
Ydeevne
Tvær-enhed
Tilgængelighed
```
## Før-forelæsning Quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/19)
[Før-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/19)
## Forståelse af DOM: Din adgang til interaktive websider
## Forståelse af DOM: Din port til interaktive websider
Document Object Model (DOM) er, hvordan JavaScript kommunikerer med dine HTML-elementer. Når din browser indlæser en HTML-side, skaber den en struktureret repræsentation af den side i hukommelsen - det er DOM. Tænk på det som et stamtræ, hvor hvert HTML-element er et familiemedlem, som JavaScript kan få adgang til, ændre eller omarrangere.
Document Object Model (DOM) er, hvordan JavaScript kommunikerer med dine HTML-elementer. Når din browser loader en HTML-side, skaber den en struktureret repræsentation af siden i hukommelsen - det er DOM'en. Tænk på det som et familie-træ, hvor hvert HTML-element er et familiemedlem, som JavaScript kan få adgang til, ændre eller omarrangere.
DOM-manipulation forvandler statiske sider til interaktive websites. Hver gang du ser en knap skifte farve ved hover, indhold opdateres uden sideopfriskning, eller elementer du kan trække rundt, er det DOM-manipulation i aktion.
DOM-manipulation forvandler statiske sider til interaktive hjemmesider. Hver gang du ser en knap skifte farve ved hover, indhold opdatere uden sideopdatering, eller elementer du kan trække rundt, er det DOM-manipulation, der arbejder.
![DOM-træ repræsentation](../../../../translated_images/dom-tree.7daf0e763cbbba9273f9a66fe04c98276d7d23932309b195cb273a9cf1819b42.da.png)
```mermaid
flowchart TD
A["Dokument"] --> B["HTML"]
B --> C["Head"]
B --> D["Body"]
C --> E["Titel"]
C --> F["Meta Tags"]
D --> G["H1: Mit Terrarium"]
D --> H["Div: Sidebeholder"]
H --> I["Div: Venstre beholder"]
H --> J["Div: Højre beholder"]
H --> K["Div: Terrarium"]
I --> L["Planteelementer 1-7"]
J --> M["Planteelementer 8-14"]
L --> N["img#plant1"]
L --> O["img#plant2"]
M --> P["img#plant8"]
M --> Q["img#plant9"]
style A fill:#e1f5fe
style B fill:#f3e5f5
style D fill:#e8f5e8
style H fill:#fff3e0
style N fill:#ffebee
style O fill:#ffebee
style P fill:#ffebee
style Q fill:#ffebee
```
![DOM trærepresentation](../../../../translated_images/dom-tree.7daf0e763cbbba92.da.png)
> En repræsentation af DOM og HTML-markeringen, der refererer til det. Fra [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
> En repræsentation af DOM og den HTML markup, som henviser til den. Fra [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
**Her er, hvad der gør DOM kraftfuld:**
**Det der gør DOM kraftfuld:**
- **Giver** en struktureret måde at få adgang til ethvert element på din side
- **Muliggør** dynamiske indholdsopdateringer uden sideopfriskninger
- **Tillader** realtidsrespons på brugerinteraktioner som klik og træk
- **Muliggør** dynamiske indholdsopdateringer uden sideopdateringer
- **Tillader** realtidsrespons til brugerinteraktioner som klik og træk
- **Skaber** fundamentet for moderne interaktive webapplikationer
## JavaScript Closures: Skab organiseret, kraftfuld kode
## JavaScript Closures: Skabe organiseret, kraftfuld kode
En [JavaScript closure](https://developer.mozilla.org/docs/Web/JavaScript/Closures) er som at give en funktion sin egen private arbejdsplads med vedvarende hukommelse. Tænk på, hvordan Darwins finker på Galápagos-øerne hver udviklede specialiserede næb baseret på deres specifikke miljø - closures fungerer på samme måde, og skaber specialiserede funktioner, der "husker" deres specifikke kontekst, selv efter deres overordnede funktion er afsluttet.
En [JavaScript closure](https://developer.mozilla.org/docs/Web/JavaScript/Closures) er som at give en funktion sit eget private arbejdsområde med vedvarende hukommelse. Tænk på, hvordan Darwins finker på Galápagosøerne hver udviklede specialiserede næb baseret på deres specifikke miljø closures fungerer på samme måde, ved at skabe specialiserede funktioner, der "husker" deres specifikke kontekst, selv efter deres overordnede funktion er færdig.
I vores terrarium hjælper closures hver plante med at huske sin egen position uafhængigt. Dette mønster optræder overalt i professionel JavaScript-udvikling, hvilket gør det til et værdifuldt koncept at forstå.
I vores terrarium hjælper closures hver plante med at huske sin egen position uafhængigt. Dette mønster optræder gennem hele professionel JavaScript-udvikling, og gør det til et værdifuldt koncept at forstå.
> 💡 **Forståelse af Closures**: Closures er et vigtigt emne i JavaScript, og mange udviklere bruger dem i årevis, før de fuldt ud forstår alle de teoretiske aspekter. I dag fokuserer vi på praktisk anvendelse - du vil se closures naturligt opstå, mens vi bygger vores interaktive funktioner. Forståelsen vil udvikle sig, når du ser, hvordan de løser reelle problemer.
```mermaid
flowchart LR
A["dragElement(plant1)"] --> B["Opretter lukning"]
A2["dragElement(plant2)"] --> B2["Opretter lukning"]
B --> C["Private variabler"]
B2 --> C2["Private variabler"]
C --> D["pos1, pos2, pos3, pos4"]
C --> E["pointerDrag funktion"]
C --> F["elementDrag funktion"]
C --> G["stopElementDrag funktion"]
C2 --> D2["pos1, pos2, pos3, pos4"]
C2 --> E2["pointerDrag funktion"]
C2 --> F2["elementDrag funktion"]
C2 --> G2["stopElementDrag funktion"]
H["Plante 1 husker sin position"] --> B
H2["Plante 2 husker sin position"] --> B2
style B fill:#e8f5e8
style B2 fill:#e8f5e8
style C fill:#fff3e0
style C2 fill:#fff3e0
```
> 💡 **Forståelse af Closures**: Closures er et væsentligt emne i JavaScript, og mange udviklere bruger år på at forstå alle de teoretiske aspekter fuldt ud. I dag fokuserer vi på praktisk anvendelse du vil se closures naturligt opstå, når vi bygger vores interaktive funktioner. Forståelsen udvikler sig, når du ser, hvordan de løser virkelige problemer.
![DOM-træ repræsentation](../../../../translated_images/dom-tree.7daf0e763cbbba9273f9a66fe04c98276d7d23932309b195cb273a9cf1819b42.da.png)
![DOM trærepresentation](../../../../translated_images/dom-tree.7daf0e763cbbba92.da.png)
> En repræsentation af DOM og HTML-markeringen, der refererer til det. Fra [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
> En repræsentation af DOM og den HTML markup, som henviser til den. Fra [Olfa Nasraoui](https://www.researchgate.net/publication/221417012_Profile-Based_Focused_Crawler_for_Social_Media-Sharing_Websites)
I denne lektion vil vi færdiggøre vores interaktive terrariumprojekt ved at skabe JavaScript, der giver en bruger mulighed for at manipulere planterne på siden.
I denne lektion vil vi færdiggøre vores interaktive terrariumprojekt ved at skabe JavaScript, som tillader brugeren at manipulere planterne på siden.
## Før vi begynder: Forberedelse til succes
Du skal bruge dine HTML- og CSS-filer fra de tidligere terrarium-lektioner - vi er ved at gøre det statiske design interaktivt. Hvis du er med for første gang, vil det give vigtig kontekst at gennemføre de lektioner først.
Du får brug for dine HTML- og CSS-filer fra de tidligere terrarium-lektioner - vi er ved at gøre det statiske design interaktivt. Hvis du er med for første gang, vil det være vigtigt at gennemføre disse før for vigtig kontekst.
Her er, hvad vi bygger:
- **Glidende drag-and-drop** for alle terrariumplanter
- **Glidende træk-og-slip** for alle terrariumplanter
- **Koordinatsporing**, så planter husker deres positioner
- **Et komplet interaktivt interface** ved hjælp af vanilla JavaScript
- **Ren, organiseret kode** ved hjælp af closure-mønstre
- **Et komplet interaktivt interface** med vanilla JavaScript
- **Ren, organiseret kode** med closure-mønstre
## Opsætning af din JavaScript-fil
Lad os oprette JavaScript-filen, der gør dit terrarium interaktivt.
Lad os oprette JavaScript-filen, der vil gøre dit terrarium interaktivt.
**Trin 1: Opret din script-fil**
I din terrarium-mappe skal du oprette en ny fil kaldet `script.js`.
I din terrarium-mappe opret en ny fil kaldet `script.js`.
**Trin 2: Link JavaScript til din HTML**
**Trin 2: Link JavaScript til dit HTML**
Tilføj denne script-tag til `<head>` sektionen af din `index.html` fil:
Tilføj dette script-tag til `<head>` sektionen i din `index.html` fil:
```html
<script src="./script.js" defer></script>
```
**Hvorfor `defer` attributten er vigtig:**
- **Sikrer**, at din JavaScript venter, indtil al HTML er indlæst
- **Sikrer**, at din JavaScript venter, indtil alt HTML er indlæst
- **Forhindrer** fejl, hvor JavaScript leder efter elementer, der ikke er klar endnu
- **Garanterer**, at alle dine planteelementer er tilgængelige for interaktion
- **Garantérer**, at alle dine planteelementer er tilgængelige for interaktion
- **Giver** bedre ydeevne end at placere scripts nederst på siden
> ⚠️ **Vigtig Bemærkning**: `defer` attributten forhindrer almindelige timingproblemer. Uden den kan JavaScript forsøge at få adgang til HTML-elementer, før de er indlæst, hvilket forårsager fejl.
> ⚠️ **Vigtig bemærkning**: `defer` attributten forhindrer almindelige timing-problemer. Uden den kan JavaScript forsøge at til HTML-elementer, før de er indlæst, hvilket forårsager fejl.
---
## Forbind JavaScript til dine HTML-elementer
## Tilslut JavaScript til dine HTML-elementer
Før vi kan gøre elementer dragbare, skal JavaScript finde dem i DOM. Tænk på dette som et bibliotekskatalogsystem - når du har katalognummeret, kan du finde præcis den bog, du har brug for, og få adgang til alt dens indhold.
Før vi kan gøre elementer trækbare, skal JavaScript finde dem i DOMen. Tænk på dette som et bibliotekssystem - når du har katalognummeret, kan du finde præcis den bog, du har brug for, og få adgang til alt dens indhold.
Vi vil bruge metoden `document.getElementById()` til at lave disse forbindelser. Det er som at have et præcist arkiveringssystem - du giver et ID, og det lokaliserer præcis det element, du har brug for i din HTML.
Vi vil bruge `document.getElementById()` metoden til at skabe disse forbindelser. Det er som at have et præcist arkivsystem - du giver et ID, og det lokaliserer præcis det element, du har brug for i dit HTML.
### Aktivering af drag-funktionalitet for alle planter
### Aktivering af trækfunktionalitet for alle planter
Tilføj denne kode til din `script.js` fil:
```javascript
// Enable drag functionality for all 14 plants
// Aktivér trækfunktionalitet for alle 14 planter
dragElement(document.getElementById('plant1'));
dragElement(document.getElementById('plant2'));
dragElement(document.getElementById('plant3'));
@ -116,39 +216,49 @@ dragElement(document.getElementById('plant13'));
dragElement(document.getElementById('plant14'));
```
**Her er, hvad denne kode opnår:**
- **Finder** hvert planteelement i DOM ved hjælp af dets unikke ID
- **Henter** en JavaScript-reference til hvert HTML-element
- **Sender** hvert element til en `dragElement` funktion (som vi vil oprette næste)
- **Forbereder** hver plante til drag-and-drop interaktion
- **Forbinder** din HTML-struktur med JavaScript-funktionalitet
**Dette opnår denne kode:**
- **Lokaliserer** hvert planteelement i DOM med dets unikke ID
- **Henter** en JavaScript reference til hvert HTML-element
- **Videregiver** hvert element til en `dragElement` funktion (som vi opretter næste)
- **Forbereder** hver plante til træk-og-slip interaktion
- **Forbinder** din HTML-struktur med JavaScript funktionalitet
> 🎯 **Hvorfor bruge IDs i stedet for klasser?** IDs giver unikke identifikatorer til specifikke elementer, mens CSS-klasser er designet til at style grupper af elementer. Når JavaScript skal manipulere individuelle elementer, giver IDs den præcision og ydeevne, vi har brug for.
> 💡 **Pro tip**: Bemærk, hvordan vi kalder `dragElement()` for hver plante individuelt. Denne tilgang sikrer, at hver plante får sin egen uafhængige træk-opførsel, hvilket er essentielt for en glidende brugeroplevelse.
> 🎯 **Hvorfor bruge IDs i stedet for klasser?** IDs giver unikke identifikatorer for specifikke elementer, mens CSS-klasser er designet til at style grupper af elementer. Når JavaScript skal manipulere individuelle elementer, giver IDs den præcision og ydeevne, vi har brug for.
### 🔄 **Pædagogisk kontrol**
**Forståelse af DOM-forbindelse:** Før vi går videre til trækfunktionalitet, bekræft at du kan:
- ✅ Forklare, hvordan `document.getElementById()` lokaliserer HTML-elementer
- ✅ Forstå hvorfor vi bruger unikke IDs til hver plante
- ✅ Beskrive formålet med `defer` attributten i script-tags
- ✅ Genkende, hvordan JavaScript og HTML forbindes via DOM
> 💡 **Pro Tip**: Bemærk, hvordan vi kalder `dragElement()` for hver plante individuelt. Denne tilgang sikrer, at hver plante får sin egen uafhængige drag-adfærd, hvilket er essentielt for en glat brugerinteraktion.
**Hurtig selvtest:** Hvad vil ske, hvis to elementer har samme ID? Hvorfor returnerer `getElementById()` kun ét element?
*Svar: IDs skal være unikke; hvis duplikeret returneres kun det første element*
---
## Bygning af Drag Element Closure
## Bygning af dragElement Closure
Nu vil vi oprette hjertet af vores drag-funktionalitet: en closure, der styrer drag-adfærden for hver plante. Denne closure vil indeholde flere indre funktioner, der arbejder sammen for at spore musebevægelser og opdatere elementpositioner.
Nu vil vi skabe kernen i vores trækfunktionalitet: en closure, som styrer træk-adfærden for hver plante. Denne closure vil indeholde flere indre funktioner, som arbejder sammen om at spore musens bevægelser og opdatere elementpositioner.
Closures er perfekte til denne opgave, fordi de giver os mulighed for at skabe "private" variabler, der vedvarer mellem funktionskald, hvilket giver hver plante sit eget uafhængige koordinatsporingssystem.
Closures er perfekte til denne opgave, fordi de tillader os at skabe "private" variabler, der bevares mellem funktionskald og giver hver plante sit eget uafhængige koordinatsporingssystem.
### Forståelse af Closures med et simpelt eksempel
### Forstå closures med et simpelt eksempel
Lad mig demonstrere closures med et simpelt eksempel, der illustrerer konceptet:
Lad mig vise closures med et simpelt eksempel, som illustrerer konceptet:
```javascript
function createCounter() {
let count = 0; // This is like a private variable
let count = 0; // Dette er ligesom en privat variabel
function increment() {
count++; // The inner function remembers the outer variable
count++; // Den indre funktion husker den ydre variabel
return count;
}
return increment; // We're giving back the inner function
return increment; // Vi giver den indre funktion tilbage
}
const myCounter = createCounter();
@ -156,128 +266,167 @@ console.log(myCounter()); // 1
console.log(myCounter()); // 2
```
**Her er, hvad der sker i dette closure-mønster:**
- **Opretter** en privat `count` variabel, der kun eksisterer inden for denne closure
- **Den indre funktion** kan få adgang til og ændre den ydre variabel (closure-mekanismen)
- **Når vi returnerer** den indre funktion, bevarer den sin forbindelse til de private data
- **Selv efter** `createCounter()` afslutter sin eksekvering, vedvarer `count` og husker sin værdi
**Det der sker i dette closure-mønster:**
- **Opretter** en privat `count` variabel, som kun eksisterer inden for denne closure
- **Den indre funktion** kan tilgå og ændre denne ydre variabel (closure mekanismen)
- **Når vi returnerer** den indre funktion, bevares dens forbindelse til disse private data
- **Selv efter** `createCounter()` er færdig med at køre, bevares og huskes `count` sin værdi
### Hvorfor Closures er perfekte til Drag-funktionalitet
### Hvorfor closures er perfekte til trækfunktionalitet
For vores terrarium skal hver plante huske sine aktuelle positionskoordinater. Closures giver den perfekte løsning:
Til vores terrarium har hver plante brug for at huske sine aktuelle positionkoordinater. Closures giver den perfekte løsning:
**Nøglefordele for vores projekt:**
- **Bevarer** private positionsvariabler for hver plante uafhængigt
- **Vedvarer** koordinatdata mellem drag-events
- **Forhindrer** variabelkonflikter mellem forskellige dragbare elementer
- **Skaber** en ren, organiseret kode-struktur
**Vigtige fordele for vores projekt:**
- **Bevarer** private positionvariabler for hver plante uafhængigt
- **Opholder** koordinatdata mellem træk-begivenheder
- **Forhindrer** variabelkonflikter mellem forskellige trækbare elementer
- **Skaber** ren, organiseret kode
> 🎯 **Læringsmål**: Du behøver ikke mestre alle aspekter af closures lige nu. Fokusér på at se, hvordan de hjælper os med at organisere kode og vedligeholde tilstand for vores drag-funktionalitet.
> 🎯 **Læringsmål**: Du behøver ikke mestre alle aspekter af closures lige nu. Fokuser på at se, hvordan de hjælper os med at organisere kode og bevare tilstand for vores trækfunktionalitet.
### Oprettelse af dragElement-funktionen
```mermaid
stateDiagram-v2
[*] --> Ready: Side indlæses
Ready --> DragStart: Bruger trykker ned (pointerdown)
DragStart --> Dragging: Mus/finger bevæger sig (pointermove)
Dragging --> Dragging: Fortsæt bevægelsen
Dragging --> DragEnd: Bruger slipper (pointerup)
DragEnd --> Ready: Nulstil til næste træk
state DragStart {
[*] --> CapturePosition
CapturePosition --> SetupListeners
SetupListeners --> [*]
}
state Dragging {
[*] --> CalculateMovement
CalculateMovement --> UpdatePosition
UpdatePosition --> [*]
}
state DragEnd {
[*] --> RemoveListeners
RemoveListeners --> CleanupState
CleanupState --> [*]
}
```
### Opret dragElement Funktionen
Nu skal vi bygge hovedfunktionen, der håndterer al drag-logik. Tilføj denne funktion under dine planteelementdeklarationer:
Lad os bygge hovedfunktionen, der håndterer al træk-logikken. Tilføj denne funktion under dine plante-elementer:
```javascript
function dragElement(terrariumElement) {
// Initialize position tracking variables
let pos1 = 0, // Previous mouse X position
pos2 = 0, // Previous mouse Y position
pos3 = 0, // Current mouse X position
pos4 = 0; // Current mouse Y position
// Initialiser positionstrackningsvariabler
let pos1 = 0, // Forrige muse X-position
pos2 = 0, // Forrige muse Y-position
pos3 = 0, // Nuværende muse X-position
pos4 = 0; // Nuværende muse Y-position
// Set up the initial drag event listener
// Opsæt den indledende træk-begivenhedslytter
terrariumElement.onpointerdown = pointerDrag;
}
```
**Forståelse af positionssporingssystemet:**
**Forstå positionsporingssystemet:**
- **`pos1` og `pos2`**: Gemmer forskellen mellem gamle og nye musepositioner
- **`pos3` og `pos4`**: Sporer de aktuelle musekoordinater
- **`terrariumElement`**: Det specifikke planteelement, vi gør dragbart
- **`onpointerdown`**: Eventet, der udløses, når brugeren begynder at trække
**Her er, hvordan closure-mønsteret fungerer:**
- **Opretter** private positionsvariabler for hvert planteelement
- **Bevarer** disse variabler gennem hele drag-livscyklussen
- **Sikrer**, at hver plante sporer sine egne koordinater uafhængigt
- **Giver** en ren grænseflade gennem `dragElement` funktionen
### Hvorfor bruge Pointer Events?
Du undrer dig måske over, hvorfor vi bruger `onpointerdown` i stedet for det mere velkendte `onclick`. Her er forklaringen:
| Eventtype | Bedst til | Udfordringen |
|-----------|-----------|--------------|
| `onclick` | Enkle knapklik | Kan ikke håndtere drag (kun klik og slip) |
| `onpointerdown` | Både mus og berøring | Nyere, men godt understøttet i dag |
| `onmousedown` | Kun desktop-mus | Udelukker mobilbrugere |
**Hvorfor pointer-events er perfekte til det, vi bygger:**
- **Fungerer godt**, uanset om nogen bruger en mus, finger eller endda en stylus
- **Føles ens** på en bærbar computer, tablet eller telefon
- **Håndterer** den faktiske drag-bevægelse (ikke kun klik-og-færdig)
- **Skaber** en glat oplevelse, som brugere forventer af moderne webapps
> 💡 **Fremtidssikring**: Pointer-events er den moderne måde at håndtere brugerinteraktioner på. I stedet for at skrive separat kode for mus og berøring, får du begge dele gratis. Ret smart, ikke?
- **`pos3` og `pos4`**: Sporer aktuelle musekoordinater
- **`terrariumElement`**: Det specifikke planteelement, vi gør trækbart
- **`onpointerdown`**: Begivenheden, der trigges, når brugeren begynder at trække
**Sådan virker closure-mønsteret:**
- **Opretter** private positionvariabler for hvert planteelement
- **Bevarer** disse variabler gennem hele træk-livscyklussen
- **Sikrer** at hver plante sporer sine egne koordinater uafhængigt
- **Giver** en ren grænseflade via `dragElement` funktionen
### Hvorfor bruge pointer-events?
Du tænker måske, hvorfor vi bruger `onpointerdown` i stedet for det mere kendte `onclick`. Her er forklaringen:
| Begivenhedstype | Bedst til | Ulempen |
|------------|----------|-------------|
| `onclick` | Simpel knapklik | Kan ikke håndtere træk (kun klik og slip) |
| `onpointerdown` | Både mus og touch | Nyere, men godt understøttet i dag |
| `onmousedown` | Kun desktop mus | Udelukker mobilbrugere |
**Hvorfor pointer-events er perfekte til vores projekt:**
- **Fungerer godt** både med mus, finger eller stylus
- **Føles ens** på laptop, tablet eller telefon
- **Håndterer** selve trækbevægelserne (ikke bare klik og færdig)
- **Skaber** en glidende oplevelse, som moderne webapps forventer
> 💡 **Fremtidssikring**: Pointer-events er den moderne måde at håndtere brugerinteraktioner på. I stedet for at skrive separat kode til mus og touch får du begge dele gratis. Ret smart, ikke?
### 🔄 **Pædagogisk kontrol**
**Forståelse af begivenhedshåndtering:** Hold en pause og bekræft din forståelse af begivenheder:
- ✅ Hvorfor bruger vi pointer-events i stedet for mus-begivenheder?
- ✅ Hvordan bevares closure-variable mellem funktionskald?
- ✅ Hvilken rolle spiller `preventDefault()` i glidende træk?
- ✅ Hvorfor tilføjer vi event-lyttere til dokumentet i stedet for individuelle elementer?
**Virkelighedsforbindelse:** Tænk på træk-og-slip interfaces, du bruger dagligt:
- **Filuploads**: Træk filer ind i browseren
- **Kanban-tavler**: Flyt opgaver mellem kolonner
- **Billedgallerier**: Omarranger rækkefølgen på fotos
- **Mobile interfaces**: Swipe og træk på touchskærme
---
## Funktionen pointerDrag: Fange starten på en drag
## pointerDrag Funktionen: Fanger starten på et træk
Når en bruger trykker ned på en plante (uanset om det er med et museklik eller en fingerberøring), aktiveres `pointerDrag`-funktionen. Denne funktion fanger de indledende koordinater og opsætter drag-systemet.
Når en bruger trykker ned på en plante (uanset om det er med mus eller finger), sættes `pointerDrag` funktionen i gang. Denne funktion fanger de indledende koordinater og forbereder træk-systemet.
Tilføj denne funktion inde i din `dragElement` closure, lige efter linjen `terrariumElement.onpointerdown = pointerDrag;`:
```javascript
function pointerDrag(e) {
// Prevent default browser behavior (like text selection)
// Forhindre standard browseradfærd (som tekstmarkering)
e.preventDefault();
// Capture the initial mouse/touch position
pos3 = e.clientX; // X coordinate where drag started
pos4 = e.clientY; // Y coordinate where drag started
// Registrer den oprindelige mus-/berøringsposition
pos3 = e.clientX; // X koordinat hvor træk startede
pos4 = e.clientY; // Y koordinat hvor træk startede
// Set up event listeners for the dragging process
// Opsæt event-lyttere til trækprocessen
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
}
```
**Trin for trin, her er hvad der sker:**
- **Forhindrer** standardbrowseradfærd, der kan forstyrre drag
- **Registrerer** de præcise koordinater, hvor brugeren startede drag-bevægelsen
- **Etablerer** event listeners for den igangværende drag-bevægelse
- **Forbereder** systemet til at spore mus/fingerbevægelser over hele dokumentet
**Trin for trin sker følgende:**
- **Forhindrer** browserens standardadfærd, som kunne genere trækningen
- **Optager** de præcise koordinater, hvor brugeren startede træk-gestus
- **Opretter** event-lyttere til den igangværende trækbevægelse
- **Forbereder** systemet til at spore mus-/fingerbevægelse over hele dokumentet
### Forståelse af Event Prevention
### Forståelse af event prevention
Linjen `e.preventDefault()` er afgørende for en glat drag-oplevelse:
Linjen `e.preventDefault()` er afgørende for glat træk:
**Uden forebyggelse kan browsere:**
- **Markere** tekst, når der trækkes hen over siden
- **Udløse** kontekstmenuer ved højreklik-drag
- **Forstyrre** vores tilpassede drag-adfærd
- **Skabe** visuelle artefakter under drag-operationen
**Uden prevention kan browsere:**
- **Markere** tekst, når der trækkes over siden
- **Åbne** kontekstmenuer ved højreklik træk
- **Forstyrre** vores brugerdefinerede trækadfærd
- **Skabe** visuelle artefakter under trækoperationen
> 🔍 **Eksperiment**: Efter at have afsluttet denne lektion, prøv at fjerne `e.preventDefault()` og se, hvordan det påvirker drag-oplevelsen. Du vil hurtigt forstå, hvorfor denne linje er essentiel!
> 🔍 **Eksperiment:** Når du har fuldført denne lektion, prøv at fjerne `e.preventDefault()` og se, hvordan det påvirker trækoplevelsen. Så forstår du hurtigt, hvorfor denne linje er vigtig!
### Koordinatsporingssystem
Egenskaberne `e.clientX` og `e.clientY` giver os præcise mus/berøringskoordinater:
| Egenskab | Hvad måler den | Anvendelse |
|----------|----------------|------------|
| `clientX` | Vandret position i forhold til viewport | Sporing af venstre-højre bevægelse |
| `clientY` | Lodret position i forhold til viewport | Sporing af op-ned bevægelse |
`e.clientX` og `e.clientY` egenskaberne giver os præcise mus-/touch-koordinater:
**Forståelse af disse koordinater:**
- **Giver** pixel-præcis positionsinformation
- **Opdateres** i realtid, mens brugeren bevæger sin pointer
| Egenskab | Hvad måles | Brugssituation |
|----------|------------|---------------|
| `clientX` | Vandret position relativt til viewport | Sporing af venstre-højre bevægelse |
| `clientY` | Lodret position relativt til viewport | Sporing af op-ned bevægelse |
**Forstå disse koordinater:**
- **Giver** pixel-præcis placeringsinformation
- **Opdateres** i realtid, mens brugeren bevæger sin markør
- **Forbliver** konsistent på tværs af forskellige skærmstørrelser og zoomniveauer
- **Muliggør** glatte, responsive drag-interaktioner
- **Muliggør** glidende, responsive træk-interaktioner
### Opsætning af dokumentniveau event listeners
### Opsætning af dokumentniveau event-lyttere
Bemærk, hvordan vi tilføjer move- og stop-events til hele `document`, ikke kun planteelementet:
@ -287,46 +436,69 @@ document.onpointerup = stopElementDrag;
```
**Hvorfor tilføje til dokumentet:**
- **Fortsætter** med at spore, selv når musen forlader planteelementet
- **Forhindrer** afbrydelse af drag, hvis brugeren bevæger sig hurtigt
- **Giver** glat drag over hele skærmen
- **Håndterer** kanttilfælde, hvor cursoren bevæger sig uden for browservinduet
- **Fortsætter** sporing, selv når musen forlader planteelementet
- **Forhindrer** afbrydelse af træk, hvis brugeren bevæger sig hurtigt
- **Giver** glidende træk på tværs af hele skærmen
- **Håndterer** kanttilfælde, hvor cursoren bevæger sig uden for browser-vinduet
> ⚡ **Ydelsesnotat**: Vi rydder op i disse dokumentniveau lyttere, når drag stopper for at undgå hukommelseslækager og ydeevneproblemer.
> ⚡ **Ydelsesnote**: Vi rydder op i disse dokumentniveau-lyttere, når træk stopper for at undgå hukommelseslækager og ydeevneproblemer.
## Afslutning af drag-systemet: Bevægelse og oprydning
## Fuldførelse af træk-systemet: Bevægelse og oprydning
Nu vil vi tilføje de to resterende funktioner, der håndterer den faktiske drag-bevægelse og oprydningen, når drag stopper. Disse funktioner arbejder sammen for at skabe glat, responsiv plantebevægelse over dit terrarium.
Nu tilføjer vi de to sidste funktioner, der håndterer selve træk-bevægelsen og oprydningen, når trækket stopper. Disse funktioner arbejder sammen om at skabe glidende, responsive plantebevægelser på dit terrarium.
### Funktionen elementDrag: Sporing af bevægelse
### elementDrag-funktionen: Sporing af bevægelse
Tilføj funktionen `elementDrag` lige efter den afsluttende krøllede parentes for `pointerDrag`:
Tilføj `elementDrag`-funktionen lige efter lukningen af `pointerDrag`-krølleparentesen:
```javascript
function elementDrag(e) {
// Calculate the distance moved since the last event
pos1 = pos3 - e.clientX; // Horizontal distance moved
pos2 = pos4 - e.clientY; // Vertical distance moved
// Beregn den tilbagelagte afstand siden sidste begivenhed
pos1 = pos3 - e.clientX; // Vandret tilbagelagt afstand
pos2 = pos4 - e.clientY; // Lodret tilbagelagt afstand
// Update the current position tracking
pos3 = e.clientX; // New current X position
pos4 = e.clientY; // New current Y position
// Opdater den nuværende positionssporing
pos3 = e.clientX; // Ny nuværende X-position
pos4 = e.clientY; // Ny nuværende Y-position
// Apply the movement to the element's position
// Anvend bevægelsen på elementets position
terrariumElement.style.top = (terrariumElement.offsetTop - pos2) + 'px';
terrariumElement.style.left = (terrariumElement.offsetLeft - pos1) + 'px';
}
```
**Forståelse af koordinatmatematikken:**
**Forstå koordinat-matematikken:**
- **`pos1` og `pos2`**: Beregner, hvor langt musen er flyttet siden sidste opdatering
- **`pos3` og `pos4`**: Gem den aktuelle museposition til næste beregning
- **`offsetTop` og `offsetLeft`**: Hent elementets aktuelle position på siden
- **Subtraktionslogik**: Flytter elementet med samme afstand, som musen bevægede sig
- **`pos3` og `pos4`**: Gemmer musens nuværende position til næste beregning
- **`offsetTop` og `offsetLeft`**: Henter elementets aktuelle position på siden
- **Subtraktionslogik**: Flytter elementet med samme afstand, som musen er flyttet
**Her er en oversigt over bevægelsesberegningen:**
1. **Måler** forskellen mellem den gamle og nye museposition
2. **Beregner**, hvor meget elementet skal flyttes baseret på musebevægelsen
```mermaid
sequenceDiagram
participant User
participant Mouse
participant JavaScript
participant Plant
User->>Mouse: Start træk ved (100, 50)
Mouse->>JavaScript: pointerdown begivenhed
JavaScript->>JavaScript: Gem startposition (pos3=100, pos4=50)
JavaScript->>JavaScript: Opsæt bevægelses-/slip-lyttere
User->>Mouse: Flyt til (110, 60)
Mouse->>JavaScript: pointermove begivenhed
JavaScript->>JavaScript: Beregn: pos1=10, pos2=10
JavaScript->>Plant: Opdater: left += 10px, top += 10px
Plant->>Plant: Render ved ny position
User->>Mouse: Slip ved (120, 65)
Mouse->>JavaScript: pointerup begivenhed
JavaScript->>JavaScript: Fjern lyttere
JavaScript->>JavaScript: Nulstil til næste træk
```
**Her er bevægelsesberegningens opdeling:**
1. **Måler** forskellen mellem gamle og nye musepositioner
2. **Beregner** hvor meget elementet skal flyttes baseret på musebevægelsen
3. **Opdaterer** elementets CSS-positionsegenskaber i realtid
4. **Gemmer** den nye position som udgangspunkt for næste bevægelsesberegning
@ -338,147 +510,254 @@ sequenceDiagram
participant JavaScript
participant Plant
Mouse->>JavaScript: Move from (100,50) to (110,60)
JavaScript->>JavaScript: Calculate: moved 10px right, 10px down
JavaScript->>Plant: Update position by +10px right, +10px down
Plant->>Plant: Render at new position
Mouse->>JavaScript: Flyt fra (100,50) til (110,60)
JavaScript->>JavaScript: Beregn: flyttet 10px til højre, 10px ned
JavaScript->>Plant: Opdater position med +10px til højre, +10px ned
Plant->>Plant: Render på ny position
```
### stopElementDrag-funktionen: Oprydning
### stopElementDrag-funktionen: Ryd op
Tilføj oprydningsfunktionen efter den afsluttende krøllede parentes i `elementDrag`:
Tilføj oprydningsfunktionen efter lukningen af `elementDrag`-krølleparentesen:
```javascript
function stopElementDrag() {
// Remove the document-level event listeners
// Fjern dokumentniveauets begivenhedslyttere
document.onpointerup = null;
document.onpointermove = null;
}
```
**Hvorfor oprydning er vigtig:**
- **Forhindrer** hukommelseslækager fra hængende event listeners
- **Stopper** trækadfærden, når brugeren slipper planten
- **Muliggør**, at andre elementer kan trækkes uafhængigt
- **Forhindrer** hukommelseslækager fra hængende event-lyttere
- **Stopper** træk-adfærden, når brugeren slipper planten
- **Tillader** andre elementer at blive trukket uafhængigt
- **Nulstiller** systemet til næste trækoperation
**Hvad der sker uden oprydning:**
- Event listeners fortsætter med at køre, selv efter trækning stopper
- Ydeevnen forringes, da ubrugte listeners hober sig op
**Hvad sker der uden oprydning:**
- Event-lyttere fortsætter med at køre, selv efter trækket er stoppet
- Ydelsen forringes, efterhånden som ubrugte lyttere hober sig op
- Uventet adfærd ved interaktion med andre elementer
- Browserressourcer spildes på unødvendig eventhåndtering
- Browserressourcer spildes på unødvendig event-håndtering
### Forståelse af CSS-positionsegenskaber
### Forståelse af CSS-positions-egenskaber
Vores træksystem manipulerer to nøgle-CSS-egenskaber:
Vores træk-system manipulerer to nøgle CSS-egenskaber:
| Egenskab | Hvad den styrer | Hvordan vi bruger den |
|----------|-----------------|-----------------------|
| `top` | Afstand fra den øverste kant | Lodret placering under træk |
| `left` | Afstand fra den venstre kant | Vandret placering under træk |
|----------|-----------------|----------------------|
| `top` | Afstand fra øverste kant | Vertikal position under træk |
| `left` | Afstand fra venstre kant | Horisontal position under træk |
**Vigtige indsigter om offset-egenskaber:**
- **`offsetTop`**: Aktuel afstand fra toppen af det positionerede overordnede element
- **`offsetLeft`**: Aktuel afstand fra venstre side af det positionerede overordnede element
- **Positioneringskontekst**: Disse værdier er relative til den nærmeste positionerede forfader
- **Realtidsopdateringer**: Ændres straks, når vi modificerer CSS-egenskaberne
- **`offsetLeft`**: Aktuel afstand fra venstre af det positionerede overordnede element
- **Positionskontekst**: Disse værdier er relative til nærmeste positionerede forfader
- **Real-time opdateringer**: Ændrer sig straks, når vi modificerer CSS-egenskaberne
> 🎯 **Designfilosofi**: Dette træksystem er bevidst fleksibelt der er ingen "drop-zoner" eller begrænsninger. Brugere kan placere planter hvor som helst og få fuld kreativ kontrol over deres terrariumdesign.
> 🎯 **Designfilosofi**: Dette træk-system er bevidst fleksibelt der er ingen "drop-zoner" eller begrænsninger. Brugere kan placere planter hvor som helst og have fuld kreativ kontrol over deres terrarium-design.
## Samlet oversigt: Dit komplette træksystem
## Sammenfatning: Dit komplette træk-system
Tillykke! Du har lige bygget et sofistikeret træk-og-slip-system ved hjælp af ren JavaScript. Din komplette `dragElement`-funktion indeholder nu en kraftfuld closure, der styrer:
Tillykke! Du har lige bygget et avanceret drag-and-drop-system med vanilla JavaScript. Din komplette `dragElement` funktion indeholder nu et kraftfuldt closure, der håndterer:
**Hvad din closure opnår:**
- **Vedligeholder** private positionsvariabler for hver plante uafhængigt
- **Håndterer** hele træklivscyklussen fra start til slut
- **Sikrer** jævn, responsiv bevægelse over hele skærmen
- **Rydder** ressourcer korrekt for at forhindre hukommelseslækager
- **Skaber** en intuitiv, kreativ grænseflade til terrariumdesign
**Hvad dit closure opnår:**
- **Bevarer** private positionsvariabler for hver plante uafhængigt
- **Håndterer** hele træk-livscyklussen fra start til slut
- **Giver** glidende, responsive bevægelser på hele skærmen
- **Rydder** op i ressourcer korrekt for at undgå hukommelseslækager
- **Skaber** en intuitiv, kreativ brugerflade til terrarium-design
### Test dit interaktive terrarium
Test nu dit interaktive terrarium! Åbn din `index.html`-fil i en webbrowser og prøv funktionaliteten:
Test nu dit interaktive terrarium! Åbn din `index.html` fil i en webbrowser og prøv funktionen:
1. **Klik og hold** på en plante for at starte trækket
2. **Bevæg musen eller fingeren**, og se planten følge glidende med
3. **Slip** for at placere planten i den nye position
4. **Eksperimenter** med forskellige arrangementer for at udforske brugerfladen
1. **Klik og hold** på en plante for at starte trækning
2. **Flyt musen eller fingeren**, og se planten følge med jævnt
3. **Slip**, for at placere planten i dens nye position
4. **Eksperimenter** med forskellige arrangementer for at udforske grænsefladen
🥇 **Præstation**: Du har skabt en fuldt interaktiv webapplikation ved at bruge kernemekanismer, som professionelle udviklere anvender dagligt. Denne drag-and-drop-funktion bruger de samme principper som filuploads, kanban-tavler og mange andre interaktive brugerflader.
🥇 **Resultat**: Du har skabt en fuldt interaktiv webapplikation ved hjælp af kernekoncepter, som professionelle udviklere bruger dagligt. Denne træk-og-slip-funktionalitet anvender de samme principper som filuploads, kanban-tavler og mange andre interaktive grænseflader.
### 🔄 **Pædagogisk tjek**
**Fuld systemforståelse**: Verificer din beherskelse af hele træk-systemet:
- ✅ Hvordan bevarer closures uafhængig tilstand for hver plante?
- ✅ Hvorfor er koordinatberegningsmatematikken nødvendig for glidende bevægelse?
- ✅ Hvad ville ske, hvis vi glemte at rydde op i event-lyttere?
- ✅ Hvordan skalerer dette mønster til mere komplekse interaktioner?
![færdigt terrarium](../../../../translated_images/terrarium-final.0920f16e87c13a84cd2b553a5af9a3ad1cffbd41fbf8ce715d9e9c43809a5e2c.da.png)
**Refleksion over kodekvalitet**: Gennemgå din komplette løsning:
- **Modulært design**: Hver plante får sin egen closure-instans
- **Event-effektivitet**: Korrekt opsætning og oprydning af lyttere
- **Tvær-enheds support**: Virker på desktop og mobiler
- **Ydelsesbevidsthed**: Ingen hukommelseslækager eller overflødige beregninger
![færdigt terrarium](../../../../translated_images/terrarium-final.0920f16e87c13a84.da.png)
---
## GitHub Copilot Agent Challenge 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Forbedr terrariumprojektet ved at tilføje en nulstillingsfunktion, der returnerer alle planter til deres oprindelige positioner med glatte animationer.
**Beskrivelse:** Forbedr terrarium-projektet ved at tilføje en nulstillingsfunktion, som returnerer alle planter til deres oprindelige positioner med glidende animationer.
**Prompt:** Opret en nulstillingsknap, der, når den klikkes, animerer alle planter tilbage til deres oprindelige sidebar-positioner ved hjælp af CSS-overgange. Funktionen skal gemme de oprindelige positioner, når siden indlæses, og glat overføre planterne tilbage til disse positioner over 1 sekund, når nulstillingsknappen trykkes.
**Prompt:** Opret en nulstillingsknap, der ved klik animerer alle planter tilbage til deres oprindelige position i sidebaren ved hjælp af CSS-transitioner. Funktionen skal gemme de oprindelige positioner, når siden indlæses, og glidende flytte planterne tilbage til disse positioner over 1 sekund, når nulstillingsknappen trykkes.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
Læs mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Ekstra udfordring: Udvid dine færdigheder
Klar til at tage dit terrarium til næste niveau? Prøv at implementere disse forbedringer:
Klar til at løfte dit terrarium til næste niveau? Prøv at implementere disse forbedringer:
**Kreative udvidelser:**
- **Dobbeltklik** på en plante for at bringe den foran (z-index-manipulation)
- **Tilføj visuel feedback** som en subtil glød ved hover over planter
- **Dobbeltklik** på en plante for at bringe den i front (z-indeks manipulation)
- **Tilføj visuel feedback** som et subtilt skær, når du holder musen over planter
- **Implementer grænser** for at forhindre planter i at blive trukket uden for terrariet
- **Opret en gemmefunktion**, der husker plantepositioner ved hjælp af localStorage
- **Tilføj lydeffekter** til at samle op og placere planter
- **Lav en gemmefunktion** der husker plantepositioner ved hjælp af localStorage
- **Tilføj lydeffekter** ved afhentning og placering af planter
> 💡 **Læringsmulighed**: Hver af disse udfordringer vil lære dig nye aspekter af DOM-manipulation, eventhåndtering og brugeroplevelsesdesign.
> 💡 **Læringsmulighed**: Hver af disse udfordringer vil lære dig nye aspekter af DOM-manipulation, event-håndtering og brugeroplevelsesdesign.
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/20)
## Gennemgang & Selvstudie: Uddyb din forståelse
## Gennemgang og selvstudie: Uddybe din forståelse
Du har mestret grundlæggende DOM-manipulation og closures, men der er altid mere at udforske! Her er nogle veje til at udvide din viden og færdigheder.
Du har mestret grundlæggende DOM-manipulation og closures, men der er altid mere at udforske! Her er nogle veje til at udvide din viden og dine færdigheder.
### Alternative træk-og-slip-metoder
### Alternative drag and drop-tilgange
Vi brugte pointer events for maksimal fleksibilitet, men webudvikling tilbyder flere metoder:
Vi brugte pointer events for maksimal fleksibilitet, men webudvikling tilbyder flere tilgange:
| Metode | Bedst til | Læringsværdi |
|--------|-----------|--------------|
| Tilgang | Bedst til | Læringsværdi |
|----------|----------|--------------|
| [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) | Filuploads, formelle trækzoner | Forståelse af native browserfunktioner |
| [Touch Events](https://developer.mozilla.org/docs/Web/API/Touch_events) | Mobile-specifikke interaktioner | Mobile-first udviklingsmønstre |
| CSS `transform`-egenskaber | Glatte animationer | Teknikker til optimering af ydeevne |
| [Touch Events](https://developer.mozilla.org/docs/Web/API/Touch_events) | Mobil-specifikke interaktioner | Mobil-først udviklingsmønstre |
| CSS `transform`-egenskaber | Glatte animationer | Ydelsesoptimeringsteknikker |
### Avancerede DOM-manipulationsemner
### Avancerede emner inden for DOM-manipulation
**Næste skridt i din læringsrejse:**
- **Event delegation**: Effektiv håndtering af events for flere elementer
- **Intersection Observer**: Registrering af, hvornår elementer kommer ind/ud af visningsområdet
- **Mutation Observer**: Overvågning af ændringer i DOM-strukturen
- **Web Components**: Oprettelse af genanvendelige, indkapslede UI-elementer
- **Virtuelle DOM-koncepter**: Forståelse af, hvordan frameworks optimerer DOM-opdateringer
- **Event delegation**: Effektiv event-håndtering for flere elementer
- **Intersection Observer**: Registrere når elementer går ind/ud af viewport
- **Mutation Observer**: Observere ændringer i DOM-struktur
- **Web Components**: Skabe genanvendelige, indkapslede UI-elementer
- **Virtual DOM koncepter**: Forstå hvordan frameworks optimerer DOM-opdateringer
### Væsentlige ressourcer til fortsat læring
**Teknisk dokumentation:**
- [MDN Pointer Events Guide](https://developer.mozilla.org/docs/Web/API/Pointer_events) - Omfattende pointer event-reference
- [W3C Pointer Events Specification](https://www.w3.org/TR/pointerevents1/) - Officiel standarddokumentation
- [MDN Pointer Events Guide](https://developer.mozilla.org/docs/Web/API/Pointer_events) - Omfattende reference for pointer events
- [W3C Pointer Events-specifikation](https://www.w3.org/TR/pointerevents1/) - Officiel standarddokumentation
- [JavaScript Closures Deep Dive](https://developer.mozilla.org/docs/Web/JavaScript/Closures) - Avancerede closure-mønstre
**Browserkompatibilitet:**
- [CanIUse.com](https://caniuse.com/) - Tjek funktionssupport på tværs af browsere
- [MDN Browser Compatibility Data](https://github.com/mdn/browser-compat-data) - Detaljeret kompatibilitetsinformation
- [CanIUse.com](https://caniuse.com/) - Tjek funktioners understøttelse i browsere
- [MDN Browser Compatibility Data](https://github.com/mdn/browser-compat-data) - Detaljerede kompatibilitetsoplysninger
**Øvelsesmuligheder:**
- **Byg** et puslespil med lignende træk-mekanikker
- **Lav** en kanban-tavle med drag-and-drop opgavestyring
- **Design** et billedgalleri med trækbare fotoarrangementer
- **Eksperimentér** med touch-gestures til mobilbrugerflader
> 🎯 **Læringsstrategi**: Den bedste måde at mestre disse koncepter er gennem praksis. Prøv at bygge variationer af trækbare brugerflader hvert projekt vil lære dig noget nyt om brugerinteraktion og DOM-manipulation.
### ⚡ **Hvad du kan gøre i de næste 5 minutter**
- [ ] Åbn browserens DevTools og skriv `document.querySelector('body')` i konsollen
- [ ] Prøv at ændre tekst på en side ved hjælp af `innerHTML` eller `textContent`
- [ ] Tilføj en klik-eventlytter til en knap eller link på en side
- [ ] Inspicer DOM-strukturens træ i Elements-panelet
### 🎯 **Hvad du kan nå på en time**
- [ ] Fuldfør quizzen efter lektionen og gennemgå DOM-manipulationskoncepter
- [ ] Lav en interaktiv webside, der responderer på brugerklik
- [ ] Øv event-håndtering med forskellige event-typer (klik, mouseover, keypress)
- [ ] Byg en simpel to-do liste eller tæller med DOM-manipulation
- [ ] Udforsk relationen mellem HTML-elementer og JavaScript-objekter
### 📅 **Din uge-lange JavaScript-rejse**
- [ ] Fuldfør det interaktive terrarium-projekt med drag-and-drop funktionalitet
- [ ] Mest event delegation for effektiv event-håndtering
- [ ] Lær om event loop og asynkron JavaScript
- [ ] Øv closures ved at bygge moduler med privat tilstand
- [ ] Udforsk moderne DOM-API'er som Intersection Observer
- [ ] Byg interaktive komponenter uden frameworks
### 🌟 **Din månedslange JavaScript-mesterskab**
- [ ] Skab en kompleks single-page applikation med vanilla JavaScript
- [ ] Lær et moderne framework (React, Vue eller Angular) og sammenlign med vanilla DOM
- [ ] Bidrag til open source JavaScript-projekter
- [ ] Mest avancerede koncepter som web components og custom elements
- [ ] Byg ydedygtige webapplikationer med optimerede DOM-mønstre
- [ ] Undervis andre i DOM-manipulation og JavaScript-grundprincipper
## 🎯 Din JavaScript DOM-mesterskabs Tidslinje
```mermaid
timeline
title DOM & JavaScript Læringsforløb
section Fundament (15 minutter)
DOM Forståelse: Elementvalg metoder
: Træstruktur navigation
: Egenskabsadgang mønstre
section Event Håndtering (20 minutter)
Brugerinteraktion: Pointer event grundlæggende
: Event lytter opsætning
: Cross-device kompatibilitet
: Event forebyggelses teknikker
section Closures (25 minutter)
Scope Håndtering: Oprettelse af private variable
: Funktions vedvarende tilstand
: State management mønstre
: Hukommelseseffektivitet
section Drag System (30 minutter)
Interaktive Funktioner: Koordinat sporing
: Positions beregning
: Bevægelses matematik
: Oprydnings procedurer
section Avancerede Mønstre (45 minutter)
Professionelle Kompetencer: Event delegation
: Performance optimering
: Fejlhåndtering
: Tilgængeligheds overvejelser
section Framework Forståelse (1 uge)
Moderne Udvikling: Virtual DOM koncepter
: State management biblioteker
: Komponent arkitekturer
: Build tool integration
section Ekspert Niveau (1 måned)
Avancerede DOM APIs: Intersection Observer
: Mutation Observer
: Custom Elements
: Web Components
```
### 🛠️ Din JavaScript værktøjskasse kort opsummering
Efter at have gennemført denne lektion, har du nu:
- **DOM-mesterskab**: Elementudvælgelse, ejendomshåndtering og trænavigation
- **Event-ekspertise**: Tvær-enheds interaktionshåndtering med pointer events
- **Closure-forståelse**: Privat tilstandsstyring og funktionspersistens
- **Interaktive systemer**: Kompleks drag-and-drop-implementering fra bunden
- **Ydelsesbevidsthed**: Korrekt oprydning af events og hukommelsesstyring
- **Moderne mønstre**: Kodeorganiseringsteknikker anvendt professionelt
- **Brugeroplevelse**: Skabelse af intuitive, responsive brugerflader
**Praktiske muligheder:**
- **Byg** et puslespil ved hjælp af lignende træk-mekanik
- **Opret** en kanban-tavle med træk-og-slip opgavestyring
- **Design** et billedgalleri med flytbare fotoarrangementer
- **Eksperimenter** med berøringsbevægelser til mobile grænseflader
**Professionelle færdigheder opnået**: Du har bygget funktioner med samme teknikker som:
- **Trello/Kanban boards**: Korttræk mellem kolonner
- **Filupload-systemer**: Drag-and-drop filhåndtering
- **Billedgallerier**: Fotoarrangementsbrugerflader
- **Mobilapps**: Touch-baserede interaktionsmønstre
> 🎯 **Læringsstrategi**: Den bedste måde at styrke disse koncepter på er gennem praksis. Prøv at bygge variationer af trækbare grænseflader hvert projekt vil lære dig noget nyt om brugerinteraktion og DOM-manipulation.
**Næste niveau**: Du er klar til at udforske moderne frameworks som React, Vue eller Angular, der bygger videre på disse grundlæggende DOM-manipulationskoncepter!
## Opgave
@ -486,5 +765,7 @@ Vi brugte pointer events for maksimal fleksibilitet, men webudvikling tilbyder f
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets modersmål bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,45 +1,30 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "7965cd2bc5dc92ad888dc4c6ab2ab70a",
"translation_date": "2025-08-26T21:28:21+00:00",
"original_hash": "bc5c5550f79d10add90ce419ee34abb3",
"translation_date": "2026-01-06T23:24:13+00:00",
"source_file": "3-terrarium/README.md",
"language_code": "da"
}
-->
# Mit Terrarium: Et projekt til at lære om HTML, CSS og DOM-manipulation med JavaScript 🌵🌱
## Udrul dit Terrarium
En lille drag-and-drop kode-meditation. Med lidt HTML, JS og CSS vil du kunne bygge en webgrænseflade, style den og endda tilføje flere interaktioner efter eget valg.
Du kan udrulle eller publicere dit Terrarium på nettet ved hjælp af **Azure Static Web Apps**.
![mit terrarium](../../../translated_images/screenshot_gray.0c796099a1f9f25e40aa55ead81f268434c00af30d7092490759945eda63067d.da.png)
1. Fork dette repo
# Lektioner
2. Tryk på denne knap 👇
1. [Introduktion til HTML](./1-intro-to-html/README.md)
2. [Introduktion til CSS](./2-intro-to-css/README.md)
3. [Introduktion til DOM og JS Closures](./3-intro-to-DOM-and-closures/README.md)
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.StaticApp)
## Kreditering
Skrevet med ♥️ af [Jen Looper](https://www.twitter.com/jenlooper)
Terrariet skabt via CSS er inspireret af Jakub Mandras glasbeholder [codepen](https://codepen.io/Rotarepmi/pen/rjpNZY).
Illustrationerne er håndtegnede af [Jen Looper](http://jenlooper.com) med hjælp fra Procreate.
## Udgiv dit Terrarium
Du kan udgive eller publicere dit terrarium på nettet ved hjælp af Azure Static Web Apps.
1. Fork dette repository
2. Tryk på denne knap
[![Deploy to Azure button](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/?feature.customportal=false&WT.mc_id=academic-77807-sagibbon#create/Microsoft.StaticApp)
3. Gå igennem guiden for at oprette din app. Sørg for at sætte app-roden til enten `/solution` eller roden af din kodebase. Der er ingen API i denne app, så det behøver du ikke bekymre dig om. En GitHub-mappe vil blive oprettet i dit forkede repository, som vil hjælpe Azure Static Web Apps' build-tjenester med at bygge og publicere din app til en ny URL.
3. Følg opsætningsguiden for at oprette din app.
- Sæt **App root** til enten `/solution` eller roden af din kodebase.
- Der er ingen API i denne app, så du kan springe API-konfigurationen over.
- En `.github` mappe oprettes automatisk for at hjælpe Azure Static Web Apps med at bygge og publicere din app.
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiske oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
En lille drag-and-drop kode-meditation. Med lidt HTML, JS og CSS kan du bygge en webgrænseflade, style den og tilføje interaktion.
![mit terrarium](../../../../translated_images/screenshot_gray.0c796099a1f9f25e40aa55ead81f268434c00af30d7092490759945eda63067d.da.png)
![mit terrarium](../../../../translated_images/screenshot_gray.0c796099a1f9f25e.da.png)
## Krediteringer

@ -1,42 +1,282 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "5adea7059676fcdb1b546ccd54c956c2",
"translation_date": "2025-10-23T22:08:38+00:00",
"original_hash": "efa2ab875b8bb5a7883816506da6b6d2",
"translation_date": "2026-01-06T23:23:29+00:00",
"source_file": "4-typing-game/README.md",
"language_code": "da"
}
-->
# Event-Drevet Programmering - Byg et Skrivespil
# Eventdreven Programmering - Byg et Typespil
```mermaid
journey
title Din udviklingsrejse for skrive-spil
section Fundament
Planlæg spillestruktur: 3: Student
Design brugerflade: 4: Student
Opsæt HTML-elementer: 4: Student
section Funktionalitet
Håndter brugerinput: 4: Student
Hold styr på timing: 5: Student
Beregn nøjagtighed: 5: Student
section Funktioner
Tilføj visuel feedback: 5: Student
Implementer spilleslogik: 5: Student
Finpuds oplevelse: 5: Student
```
## Introduktion
Her er noget, som enhver udvikler ved, men sjældent taler om: at skrive hurtigt er en superkraft! 🚀 Tænk over det - jo hurtigere du kan få dine ideer fra hjernen til din kodeeditor, jo mere kan din kreativitet flyde. Det er som at have en direkte pipeline mellem dine tanker og skærmen.
Her er noget, som alle udviklere kender til, men sjældent taler om: at skrive hurtigt er en superkraft! 🚀 Tænk over det - jo hurtigere du kan få dine idéer fra din hjerne til din kodeeditor, jo mere kan din kreativitet flyde. Det er som at have en direkte forbindelse mellem dine tanker og skærmen.
Vil du vide en af de bedste måder at forbedre denne evne? Du gættede det - vi skal lave et spil!
```mermaid
pie title Spilfunktioner
"Realtidsfeedback" : 25
"Ydelsessporing" : 20
"Interaktivt UI" : 20
"Tidsmålersystem" : 15
"Citatstyring" : 10
"Resultatvisning" : 10
```
Vil du vide en af de bedste måder at opgradere denne færdighed på? Du gættede rigtigt - vi skal bygge et spil!
> Lad os sammen skabe et fantastisk skrivespil!
```mermaid
flowchart LR
A[Spiller starter spil] --> B[Tilfældigt citat vises]
B --> C[Spiller taster tegn]
C --> D{Tegnet korrekt?}
D -->|Ja| E[Grøn markering]
D -->|Nej| F[Rød markering]
E --> G[Opdater nøjagtighed]
F --> G
G --> H{Citat færdigt?}
H -->|Nej| C
H -->|Ja| I[Beregning af WPM]
I --> J[Vis resultater]
J --> K[Spil igen?]
K -->|Ja| B
K -->|Nej| L[Spil slut]
style A fill:#e1f5fe
style D fill:#fff3e0
style E fill:#e8f5e8
style F fill:#ffebee
style I fill:#f3e5f5
```
> Lad os skabe et fantastisk typespil sammen!
Klar til at bruge alle de JavaScript-, HTML- og CSS-færdigheder, du har lært? Vi skal bygge et skrivespil, der udfordrer dig med tilfældige citater fra den legendariske detektiv [Sherlock Holmes](https://en.wikipedia.org/wiki/Sherlock_Holmes). Spillet vil måle, hvor hurtigt og præcist du kan skrive - og tro mig, det er mere vanedannende, end du måske tror!
Klar til at sætte alle de JavaScript-, HTML- og CSS-færdigheder, du har lært, i spil? Vi skal bygge et typespil, der vil udfordre dig med tilfældige citater fra den legendariske detektiv [Sherlock Holmes](https://en.wikipedia.org/wiki/Sherlock_Holmes). Spillet vil holde styr på, hvor hurtigt og præcist du kan skrive - og tro mig, det er mere vanedannende, end du måske tror!
```mermaid
mindmap
root((Udvikling af Tastespil))
User Interface
Input Elements
Visuel Feedback
Responsivt Design
Tilgængelighed
Game Logic
Citat Udvælgelse
Tidsstyring
Præcision Overvågning
Scoringsberegning
Event Handling
Tastaturinput
Knapklik
Opdateringer i Real-tid
Spiltilstand Ændringer
Performance Metrics
Ord Per Minut
Tegnpræcision
Fejl Overvågning
Fremdriftsvisning
User Experience
Øjeblikkelig Feedback
Klare Instruktioner
Engagerende Indhold
Opnåelsessystem
```
![demo](../../../4-typing-game/images/demo.gif)
## Hvad Du Skal Vide
Før vi går i gang, skal du sikre dig, at du er komfortabel med disse begreber (bare rolig, hvis du har brug for en hurtig genopfriskning - det har vi alle prøvet!):
```mermaid
flowchart TD
A[Bruger Handling] --> B{Hændelsestype?}
B -->|Tastetryk| C[Tastatur Hændelse]
B -->|Klik på Knap| D[Mus Hændelse]
B -->|Timer| E[Tids Hændelse]
C --> F[Tjek Tegn]
D --> G[Start/Nulstil Spil]
E --> H[Opdater Timer]
F --> I{Korrekt?}
I -->|Ja| J[Fremhæv Grøn]
I -->|Nej| K[Fremhæv Rød]
J --> L[Opdater Score]
K --> L
L --> M[Tjek Spiltilstand]
G --> N[Generer Nyt Citér]
H --> O[Vis Tid]
M --> P{Spil Færdigt?}
P -->|Ja| Q[Vis Resultater]
P -->|Nej| R[Fortsæt Spil]
style A fill:#e1f5fe
style F fill:#e8f5e8
style I fill:#fff3e0
style Q fill:#f3e5f5
```
Før vi går i gang, så sørg for, at du er komfortabel med disse koncepter (bare rolig, hvis du har brug for en hurtig opfriskning - det har vi alle prøvet!):
- Oprettelse af tekstinput og knapkontroller
- CSS og opsætning af stilarter ved hjælp af klasser
- Grundlæggende JavaScript
- Oprettelse af en array
- Generering af et tilfældigt tal
- Hentning af den aktuelle tid
- Oprettelse af tekstinput og knapkontroller
- CSS og at sætte stilarter med klasser
- Grundlæggende JavaScript
- Oprettelse af et array
- Oprettelse af et tilfældigt nummer
- At hente den aktuelle tid
Hvis nogle af disse føles lidt rustne, er det helt okay! Nogle gange er den bedste måde at styrke din viden på at kaste sig ud i et projekt og finde ud af tingene undervejs.
Hvis nogle af disse føles lidt rustne, er det helt i orden! Nogle gange er den bedste måde at konsolidere din viden på at kaste sig ud i et projekt og finde tingene ud af undervejs.
## Lad os Bygge Det!
### 🔄 **Pædagogisk Status**
**Fundamentvurdering**: Før du starter udviklingen, skal du sikre dig, at du forstår:
- ✅ Hvordan HTML-formularer og input-elementer fungerer
- ✅ CSS-klasser og dynamisk styling
- ✅ JavaScript event-lyttere og -håndterere
- ✅ Array-manipulation og tilfældigt valg
- ✅ Tidsmåling og beregninger
[Opret et skrivespil ved hjælp af event-drevet programmering](./typing-game/README.md)
**Hurtig Selvevaluering**: Kan du forklare, hvordan disse koncepter fungerer sammen i et interaktivt spil?
- **Events** udløses, når brugerne interagerer med elementer
- **Handlers** behandler disse events og opdaterer spillets tilstand
- **CSS** giver visuel feedback på brugerhandlinger
- **Timing** muliggør måling af præstation og spillets fremdrift
```mermaid
quadrantChart
title Udvikling af skrivefærdigheder i spil
x-axis Begynder --> Ekspert
y-axis Statisk --> Interaktiv
quadrant-1 Avancerede spil
quadrant-2 Realtids-apps
quadrant-3 Grundlæggende sider
quadrant-4 Interaktive sites
HTML Forms: [0.3, 0.2]
CSS Styling: [0.4, 0.3]
Event Handling: [0.7, 0.8]
Game Logic: [0.8, 0.9]
Performance Tracking: [0.9, 0.7]
```
## Lad Os Bygge Det Her!
[Oprettelse af et typespil ved brug af eventdrevet programmering](./typing-game/README.md)
### ⚡ **Det Kan Du Nå På 5 Minutter**
- [ ] Åbn din browserkonsol og prøv at lytte til tastatur-events med `addEventListener`
- [ ] Opret en simpel HTML-side med et inputfelt og test skrivningsdetektion
- [ ] Øv strengmanipulation ved at sammenligne skrevet tekst med målteksten
- [ ] Eksperimenter med `setTimeout` for at forstå timing-funktioner
### 🎯 **Det Kan Du Opnå På En Time**
- [ ] Fuldfør quizzen efter lektionen og forstå eventdrevet programmering
- [ ] Byg en basal version af typespillet med ordvalidering
- [ ] Tilføj visuel feedback ved korrekt og forkert skrivning
- [ ] Implementer et simpelt pointsystem baseret på hastighed og nøjagtighed
- [ ] Stil dit spil med CSS, så det bliver visuelt tiltalende
### 📅 **Din Ugelange Spiludvikling**
- [ ] Udarbejd det fulde typespil med alle funktioner og finish
- [ ] Tilføj sværhedsgrader med varierende ordkompleksitet
- [ ] Implementer brugerstatistikker (WPM, nøjagtighed over tid)
- [ ] Skab lydeffekter og animationer for bedre brugeroplevelse
- [ ] Gør dit spil mobilvenligt til touch-enheder
- [ ] Del dit spil online og indsamle feedback fra brugere
### 🌟 **Din Månedslange Interaktive Udvikling**
- [ ] Byg flere spil, der udforsker forskellige interaktionsmønstre
- [ ] Lær om spillelooper, tilstandshåndtering og performanceoptimering
- [ ] Bidrag til open source-spiludviklingsprojekter
- [ ] Mestre avancerede timing-koncepter og glidende animationer
- [ ] Skab en portefølje med forskellige interaktive applikationer
- [ ] Mentorér andre, der er interesserede i spiludvikling og brugerinteraktion
## 🎯 Din Tidslinje for Mastery i Typespil
```mermaid
timeline
title Spiludviklings læringsfremskridt
section Setup (10 minutter)
Projektstruktur: HTML fundament
: CSS stilopsætning
: Oprettelse af JavaScript-fil
section Brugergrænseflade (20 minutter)
Interaktive elementer: Indtastningsfelter
: Knapkontroller
: Visningsområder
: Responsivt layout
section Hændelseshåndtering (25 minutter)
Brugerinteraktion: Tastaturhændelser
: Mus-hændelser
: Realtids feedback
: Tilstandshåndtering
section Spillogik (30 minutter)
Kernefunktionalitet: Citatgenerering
: Tegnsammenligning
: Præcisionberegning
: Timerimplementering
section Præstationssporing (35 minutter)
Mål & Analyse: WPM beregning
: Fejlsporing
: Fremskridt visualisering
: Resultatvisning
section Polering & Forbedring (45 minutter)
Brugeroplevelse: Visuel feedback
: Lyd effekter
: Animationer
: Tilgængelighedsfunktioner
section Avancerede funktioner (1 uge)
Udvidet funktionalitet: Sværhedsgrader
: Leaderboards
: Tilpassede citater
: Multiplayer muligheder
section Professionelle færdigheder (1 måned)
Spiludvikling: Præstationsoptimering
: Kodearkitektur
: Teststrategier
: Udrulningsmønstre
```
### 🛠️ Dit Spiludviklingsværktøjssæt Oversigt
Efter at have fuldført dette projekt, vil du have mestret:
- **Eventdreven Programmering**: Reaktive brugerflader, der reagerer på input
- **Feedback i Realtime**: Øjeblikkelige visuelle og performance-opdateringer
- **Præstationsmåling**: Præcise timing- og pointsystemer
- **Spil-tilstandshåndtering**: Kontrol over applikationens flow og brugeroplevelse
- **Interaktivt Design**: Skabelse af engagerende, vanedannende brugeroplevelser
- **Moderne Web-APIer**: Udnyttelse af browserfunktioner til rige interaktioner
- **Tilgængelighedsmønstre**: Inklusivt design for alle brugere
**Virkelige Anvendelser**: Disse færdigheder kan direkte anvendes på:
- **Webapplikationer**: Enhver interaktiv brugerflade eller dashboard
- **Uddannelsessoftware**: Læringsplatforme og færdighedsvurderingsværktøjer
- **Produktivitetsværktøjer**: Teksteditorer, IDEer og samarbejdsværktøjer
- **Spilindustrien**: Browserspil og interaktiv underholdning
- **Mobiludvikling**: Touch-baserede brugerflader og gestushåndtering
**Næste Niveau**: Du er klar til at udforske avancerede spilframeworks, realtids multiplayer-systemer eller komplekse interaktive applikationer!
## Credits
@ -44,5 +284,7 @@ Skrevet med ♥️ af [Christopher Harrison](http://www.twitter.com/geektrainer)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiske oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For vigtig information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,113 +1,112 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "e6b75e5b8caae906473a8a09d77b7121",
"translation_date": "2025-10-23T22:08:45+00:00",
"original_hash": "da8bc72041a2bb3826a54654ee1a8844",
"translation_date": "2026-01-06T23:55:37+00:00",
"source_file": "4-typing-game/typing-game/README.md",
"language_code": "da"
}
-->
# Oprettelse af et spil ved hjælp af events
Har du nogensinde undret dig over, hvordan hjemmesider ved, når du klikker på en knap eller skriver i en tekstboks? Det er magien ved event-drevet programmering! Hvilken bedre måde at lære denne essentielle færdighed på end ved at bygge noget nyttigt - et skrivehastighedsspil, der reagerer på hver tast, du trykker.
Har du nogensinde spekuleret på, hvordan hjemmesider ved, når du klikker på en knap eller skriver i en tekstboks? Det er magien ved begivenhedsdrevet programmering! Hvad er en bedre måde at lære denne essentielle færdighed på end ved at bygge noget nyttigt - et skrivehastighedsspil, der reagerer på hver eneste tasteanslag, du laver.
Du kommer til at se med egne øjne, hvordan webbrowsere "taler" med din JavaScript-kode. Hver gang du klikker, skriver eller bevæger musen, sender browseren små beskeder (vi kalder dem events) til din kode, og du bestemmer, hvordan du vil reagere!
Du vil selv opleve, hvordan webbrowsere "taler" med din JavaScript-kode. Hver gang du klikker, skriver eller bevæger din mus, sender browseren små beskeder (vi kalder dem events) til din kode, og du bestemmer, hvordan du vil svare!
Når vi er færdige her, har du bygget et rigtigt skrive-spil, der sporer din hastighed og nøjagtighed. Endnu vigtigere, du vil forstå de grundlæggende begreber, der driver hver interaktiv hjemmeside, du nogensinde har brugt. Lad os komme i gang!
Når vi er færdige her, har du bygget et rigtigt skrivespil, der følger din hastighed og nøjagtighed. Endnu vigtigere, du vil forstå de grundlæggende koncepter, der driver hver interaktiv hjemmeside, du nogensinde har brugt. Lad os dykke ned i det!
## Quiz før forelæsning
## Forud-forelæsning Quiz
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/21)
[Forud-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/21)
## Event-drevet programmering
## Begivenhedsdrevet programmering
Tænk på din yndlingsapp eller -hjemmeside - hvad får den til at føles levende og responsiv? Det handler om, hvordan den reagerer på det, du gør! Hver tryk, klik, swipe eller tastetryk skaber det, vi kalder en "event," og det er her, den virkelige magi i webudvikling sker.
Tænk på din yndlingsapp eller hjemmeside - hvad får den til at føles levende og lydhør? Det handler alt sammen om, hvordan den reagerer på det, du gør! Hvert tryk, klik, swipe eller tasteanslag skaber det, vi kalder en "begivenhed", og det er her den ægte magi i webudvikling sker.
Her er, hvad der gør programmering for nettet så interessant: vi ved aldrig, hvornår nogen vil klikke på den knap eller begynde at skrive i en tekstboks. De kan klikke med det samme, vente fem minutter eller måske aldrig klikke overhovedet! Denne uforudsigelighed betyder, at vi skal tænke anderledes om, hvordan vi skriver vores kode.
Her er det, der gør programmering til web så interessant: vi ved aldrig, hvornår nogen vil klikke på den knap eller begynde at skrive i en tekstboks. De kan klikke med det samme, vente fem minutter eller måske aldrig klikke! Denne uforudsigelighed betyder, at vi skal tænke anderledes om, hvordan vi skriver vores kode.
I stedet for at skrive kode, der kører fra top til bund som en opskrift, skriver vi kode, der tålmodigt venter på, at noget skal ske. Det er lidt som telegrafoperatører i 1800-tallet, der sad ved deres maskiner, klar til at reagere i det øjeblik, en besked kom igennem ledningen.
I stedet for at skrive kode, der kører fra top til bund som en opskrift, skriver vi kode, der tålmodigt venter på, at noget skal ske. Det minder om, hvordan telegrafoperatører i 1800-tallet sad ved deres maskiner og var klar til at svare det øjeblik, en besked kom gennem ledningen.
Så hvad er en "event" egentlig? Kort sagt, det er noget, der sker! Når du klikker på en knap - det er en event. Når du skriver et bogstav - det er en event. Når du bevæger musen - det er en anden event.
Så hvad er egentlig en "begivenhed"? Kort sagt, det er noget, der sker! Når du klikker på en knap - det er en begivenhed. Når du skriver et bogstav - det er en begivenhed. Når du bevæger din mus - det er en anden begivenhed.
Event-drevet programmering giver os mulighed for at opsætte vores kode til at lytte og reagere. Vi skaber specielle funktioner kaldet **event listeners**, der tålmodigt venter på specifikke ting, der skal ske, og så springer de i aktion, når det sker.
Begivenhedsdrevet programmering lader os sætte vores kode op til at lytte og reagere. Vi opretter specielle funktioner kaldet **event listeners**, som tålmodigt venter på at bestemte ting sker, og så springer i aktion, når de gør.
Tænk på event listeners som at have en dørklokke til din kode. Du opsætter dørklokken (`addEventListener()`), fortæller den, hvilken lyd den skal lytte efter (som et 'klik' eller 'tastetryk'), og specificerer derefter, hvad der skal ske, når nogen ringer på den (din brugerdefinerede funktion).
Tænk på event listeners som at have en dørklokke for din kode. Du sætter dørklokken op (`addEventListener()`), fortæller den, hvilken lyd den skal lytte efter (som et 'click' eller 'keypress'), og så specificerer du, hvad der skal ske, når nogen ringer på (din brugerdefinerede funktion).
**Sådan fungerer event listeners:**
- **Lytter** efter specifikke brugerhandlinger som klik, tastetryk eller musebevægelser
- **Udfører** din brugerdefinerede kode, når den specificerede event opstår
- **Reagerer** øjeblikkeligt på brugerinteraktioner og skaber en problemfri oplevelse
- **Håndterer** flere events på det samme element ved hjælp af forskellige lyttere
- **Lytter** efter specifikke brugerhandlinger som klik, tasteanslag eller musebevægelser
- **Udfører** din brugerdefinerede kode, når den angivne begivenhed opstår
- **Reagerer** øjeblikkeligt på brugerinteraktioner og skaber en sømløs oplevelse
- **Håndterer** flere begivenheder på det samme element ved hjælp af forskellige lyttere
> **NOTE:** Det er værd at fremhæve, at der er mange måder at oprette event listeners på. Du kan bruge anonyme funktioner eller oprette navngivne. Du kan bruge forskellige genveje, som at indstille `click`-egenskaben eller bruge `addEventListener()`. I vores øvelse vil vi fokusere på `addEventListener()` og anonyme funktioner, da det sandsynligvis er den mest almindelige teknik, webudviklere bruger. Det er også den mest fleksible, da `addEventListener()` fungerer for alle events, og eventnavnet kan angives som en parameter.
> **NOTE:** Det er værd at fremhæve, at der findes mange måder at oprette event listeners på. Du kan bruge anonyme funktioner eller oprette navngivne. Du kan bruge forskellige genveje, som at sætte `click`-egenskaben, eller bruge `addEventListener()`. I vores øvelse vil vi fokusere på `addEventListener()` og anonyme funktioner, da det sandsynligvis er den mest almindelige teknik, webudviklere bruger. Det er også den mest fleksible, da `addEventListener()` virker for alle begivenheder, og eventnavnet kan gives som en parameter.
### Almindelige events
### Almindelige begivenheder
Selvom webbrowsere tilbyder dusinvis af forskellige events, du kan lytte efter, er de fleste interaktive applikationer afhængige af blot en håndfuld essentielle events. At forstå disse kerne-events vil give dig fundamentet til at bygge sofistikerede brugerinteraktioner.
Mens webbrowsere tilbyder dusinvis af forskellige begivenheder, du kan lytte efter, bygger de fleste interaktive applikationer kun på en håndfuld essentielle begivenheder. At forstå disse kernebegivenheder vil give dig fundamentet til at skabe sofistikerede brugerinteraktioner.
Der er [dusinvis af events](https://developer.mozilla.org/docs/Web/Events), du kan lytte til, når du opretter en applikation. Grundlæggende set udløser alt, hvad en bruger gør på en side, en event, hvilket giver dig stor magt til at sikre, at de får den oplevelse, du ønsker. Heldigvis har du normalt kun brug for en lille håndfuld events. Her er nogle almindelige (inklusive de to, vi vil bruge, når vi opretter vores spil):
Der findes [dusiner af begivenheder](https://developer.mozilla.org/docs/Web/Events), du kan lytte efter, når du skaber en applikation. Grundlæggende rejser alle handlinger en bruger foretager på en side en begivenhed, hvilket giver dig stor magt til at sikre, at de får den oplevelse, du ønsker. Heldigvis behøver du normalt kun en lille håndfuld begivenheder. Her er nogle almindelige (inklusive de to, vi vil bruge til at skabe vores spil):
| Event | Beskrivelse | Almindelige anvendelser |
|-------|-------------|--------------------------|
| `click` | Brugeren klikker på noget | Knapper, links, interaktive elementer |
| `contextmenu` | Brugeren klikker med højre museknap | Brugerdefinerede højreklik-menuer |
| `select` | Brugeren markerer noget tekst | Tekstredigering, kopieringsoperationer |
| `input` | Brugeren indtaster tekst | Formularvalidering, realtidssøgning |
| Begivenhed | Beskrivelse | Almindelige brugssituationer |
|------------|-------------|------------------------------|
| `click` | Brugeren klikkede på noget | Knapper, links, interaktive elementer |
| `contextmenu` | Brugeren klikkede med højre museknap | Tilpassede højrekliksmenuer |
| `select` | Brugeren markerede noget tekst | Tekstredigering, kopiering |
| `input` | Brugeren indtastede noget tekst | Formularvalidering, søgning i realtid |
**Forståelse af disse event-typer:**
- **Udløses**, når brugere interagerer med specifikke elementer på din side
- **Giver** detaljerede oplysninger om brugerens handling gennem event-objekter
- **Muliggør**, at du kan skabe responsive, interaktive webapplikationer
**At forstå disse event-typer:**
- **Udløses** når brugere interagerer med specifikke elementer på din side
- **Giver** detaljeret information om brugerens handling via event-objekter
- **Gør det muligt** at skabe responsive, interaktive webapplikationer
- **Fungerer** konsekvent på tværs af forskellige browsere og enheder
## Oprettelse af spillet
Nu hvor du forstår, hvordan events fungerer, lad os omsætte den viden til praksis ved at bygge noget nyttigt. Vi vil skabe et skrivehastighedsspil, der demonstrerer event-håndtering, mens det hjælper dig med at udvikle en vigtig udviklerfærdighed.
Nu hvor du forstår, hvordan events fungerer, lad os sætte den viden i praksis ved at bygge noget nyttigt. Vi vil skabe et skrivehastighedsspil, der demonstrerer eventhåndtering samtidig med, at du udvikler en vigtig udviklerfærdighed.
Vi skal lave et spil for at udforske, hvordan events fungerer i JavaScript. Vores spil vil teste en spillers skrivefærdigheder, som er en af de mest undervurderede færdigheder, alle udviklere bør have. Sjovt faktum: QWERTY-tastaturlayoutet, vi bruger i dag, blev faktisk designet i 1870'erne til skrivemaskiner - og gode skrivefærdigheder er stadig lige så værdifulde for programmører i dag! Den generelle struktur for spillet vil se sådan ud:
Vi laver et spil for at udforske, hvordan events fungerer i JavaScript. Vores spil vil teste en spillers skrivefærdighed, hvilket er en af de mest undervurderede færdigheder, alle udviklere burde have. Sjov fakta: QWERTY-tastaturlayoutet, vi bruger i dag, blev faktisk designet i 1870'erne til skrivemaskiner - og gode skrivefærdigheder er stadig lige så værdifulde for programmører i dag! Det generelle forløb i spillet vil se således ud:
```mermaid
flowchart TD
A[Player clicks Start] --> B[Random quote displays]
B --> C[Player types in textbox]
C --> D{Word complete?}
D -->|Yes| E[Highlight next word]
D -->|No| F{Correct so far?}
F -->|Yes| G[Keep normal styling]
F -->|No| H[Show error styling]
E --> I{Quote complete?}
I -->|No| C
I -->|Yes| J[Show success message with time]
A[Spiller klikker Start] --> B[Tilfældigt citat vises]
B --> C[Spiller skriver i tekstfelt]
C --> D{Ord fuldført?}
D -->|Ja| E[Fremhæv næste ord]
D -->|Nej| F{Korrekt indtil nu?}
F -->|Ja| G[Behold normal styling]
F -->|Nej| H[Vis fejlstyling]
E --> I{Citat fuldført?}
I -->|Nej| C
I -->|Ja| J[Vis succesbesked med tid]
G --> C
H --> C
```
**Sådan fungerer vores spil:**
- **Starter**, når spilleren klikker på startknappen og viser et tilfældigt citat
**Sådan vil vores spil virke:**
- **Starter** når spilleren klikker på startknappen og viser et tilfældigt citat
- **Sporer** spillerens skrivefremskridt ord for ord i realtid
- **Fremhæver** det aktuelle ord for at guide spillerens fokus
- **Giver** øjeblikkelig visuel feedback for skrivefejl
- **Beregner** og viser den samlede tid, når citatet er fuldført
- **Giver** øjeblikkelig visuel feedback ved skrivefejl
- **Beregner** og viser den samlede tid, når citatet er færdigskrevet
Lad os bygge vores spil og lære om events!
Lad os bygge vores spil, og lære om events!
### Filstruktur
Før vi begynder at kode, lad os organisere os! At have en ren filstruktur fra starten vil spare dig for hovedpine senere og gøre dit projekt mere professionelt. 😊
Før vi begynder at kode, lad os få styr på tingene! At have en ren filstruktur fra starten vil spare dig for hovedpine senere og gøre dit projekt mere professionelt. 😊
Vi vil holde det enkelt med kun tre filer: `index.html` til vores sidestruktur, `script.js` til al vores spil-logik og `style.css` for at få det hele til at se godt ud. Dette er den klassiske trio, der driver det meste af nettet!
Vi holder det simpelt med kun tre filer: `index.html` til vores sidestruktur, `script.js` til al vores spislogik og `style.css` for at få det til at se godt ud. Det er det klassiske triptæm, der driver det meste af internettet!
**Opret en ny mappe til dit arbejde ved at åbne en konsol eller terminal og udføre følgende kommando:**
**Opret en ny mappe til dit arbejde ved at åbne en konsol eller terminal og indsende følgende kommando:**
```bash
# Linux or macOS
# Linux eller macOS
mkdir typing-game && cd typing-game
# Windows
md typing-game && cd typing-game
```
**Hvad disse kommandoer gør:**
**Her er, hvad disse kommandoer gør:**
- **Opretter** en ny mappe kaldet `typing-game` til dine projektfiler
- **Navigerer** automatisk ind i den nyoprettede mappe
- **Opsætter** et rent arbejdsområde til din spiludvikling
@ -121,33 +120,33 @@ code .
**Denne kommando:**
- **Starter** Visual Studio Code i den aktuelle mappe
- **Åbner** din projektmappe i editoren
- **Giver** adgang til alle de udviklingsværktøjer, du har brug for
- **Giver** adgang til alle udviklingsværktøjer, du får brug for
**Tilføj tre filer til mappen i Visual Studio Code med følgende navne:**
- `index.html` - Indeholder strukturen og indholdet af dit spil
- `script.js` - Håndterer al spil-logik og event listeners
- `script.js` - Håndterer al spislogikken og event listeners
- `style.css` - Definerer det visuelle udseende og styling
## Opret brugergrænsefladen
Nu skal vi bygge scenen, hvor al vores spilhandling vil finde sted! Tænk på dette som at designe kontrolpanelet til et rumskib - vi skal sikre, at alt, hvad vores spillere har brug for, er lige der, hvor de forventer det.
Lad os nu bygge scenen, hvor al vores spilhandling vil foregå! Tænk på det som at designe kontrolpanelet til et rumskib - vi skal sikre, at alt det, vores spillere har brug for, er lige, hvor de forventer det.
Lad os finde ud af, hvad vores spil faktisk har brug for. Hvis du spillede et skrive-spil, hvad ville du så gerne se på skærmen? Her er, hvad vi har brug for:
Lad os finde ud af, hvad vores spil egentlig har brug for. Hvis du spillede et skrive-spil, hvad ville du så gerne se på skærmen? Her er, hvad vi r brug for:
| UI-element | Formål | HTML-element |
|------------|--------|--------------|
| Citatvisning | Viser teksten, der skal skrives | `<p>` med `id="quote"` |
|------------|---------|--------------|
| Citatvisning | Viser teksten, der skal tastes | `<p>` med `id="quote"` |
| Beskedområde | Viser status- og succesbeskeder | `<p>` med `id="message"` |
| Tekstinput | Hvor spillere skriver citatet | `<input>` med `id="typed-value"` |
| Tekstinput | Hvor spillerne skriver citatet | `<input>` med `id="typed-value"` |
| Startknap | Starter spillet | `<button>` med `id="start"` |
**Forståelse af UI-strukturen:**
- **Organiserer** indhold logisk fra top til bund
- **Tildeler** unikke IDs til elementer for JavaScript-målretning
- **Giver** klar visuel hierarki for bedre brugeroplevelse
- **Tildeler** unikke IDer til elementer for JavaScript-målretning
- **Giver** klart visuelt hierarki for bedre brugeroplevelse
- **Inkluderer** semantiske HTML-elementer for tilgængelighed
Hvert af disse elementer skal have IDs, så vi kan arbejde med dem i vores JavaScript. Vi vil også tilføje referencer til de CSS- og JavaScript-filer, vi skal oprette.
Hver af disse skal have IDer, så vi kan arbejde med dem i vores JavaScript. Vi vil også tilføje referencer til CSS- og JavaScript-filerne, vi skal oprette.
Opret en ny fil med navnet `index.html`. Tilføj følgende HTML:
@ -172,50 +171,50 @@ Opret en ny fil med navnet `index.html`. Tilføj følgende HTML:
</html>
```
**Hvad denne HTML-struktur opnår:**
- **Linker** CSS-stylesheetet i `<head>` for styling
- **Skaber** en klar overskrift og instruktioner til brugerne
- **Etablerer** pladsholder-afsnit med specifikke IDs til dynamisk indhold
**Det denne HTML-struktur udfører:**
- **Linker** CSS-stylesheetet i `<head>` til styling
- **Opretter** en klar overskrift og instruktioner til brugerne
- **Etablerer** pladsholder-afsnit med specifikke IDer til dynamisk indhold
- **Inkluderer** et inputfelt med tilgængelighedsattributter
- **Giver** en startknap til at starte spillet
- **Tilbyder** en startknap til at trigge spillet
- **Indlæser** JavaScript-filen til sidst for optimal ydeevne
### Start applikationen
At teste din applikation ofte under udvikling hjælper dig med at opdage problemer tidligt og se din fremgang i realtid. Live Server er et uvurderligt værktøj, der automatisk opdaterer din browser, hver gang du gemmer ændringer, hvilket gør udviklingen meget mere effektiv.
Det hjælper at teste din applikation hyppigt under udviklingen for at fange problemer tidligt og følge din fremgang i realtid. Live Server er et uvurderligt værktøj, der automatisk opdaterer din browser, hver gang du gemmer ændringer, hvilket gør udviklingen meget mere effektiv.
Det er altid bedst at udvikle iterativt for at se, hvordan tingene ser ud. Lad os starte vores applikation. Der er en fantastisk udvidelse til Visual Studio Code kaldet [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon), som både hoster din applikation lokalt og opdaterer browseren, hver gang du gemmer.
Det er altid bedst at udvikle iterativt for at se, hvordan tingene ser ud. Lad os starte vores applikation. Der findes en fantastisk udvidelse til Visual Studio Code kaldet [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon), som både vil hoste din applikation lokalt og opdatere browseren, hver gang du gemmer.
**Installer [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) ved at følge linket og klikke på Installér:**
**Installer [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) ved at følge linket og klikke på Installer:**
**Hvad der sker under installationen:**
- **Åbner** din browser for at starte Visual Studio Code
- **Vejleder** dig gennem installationsprocessen for udvidelsen
- **Kan kræve**, at du genstarter Visual Studio Code for at fuldføre opsætningen
**Sådan foregår installationen:**
- **Beder** din browser om at åbne Visual Studio Code
- **Guides** dig gennem installationsprocessen
- **Kan kræve** genstart af Visual Studio Code for at fuldføre opsætningen
**Når det er installeret, skal du i Visual Studio Code trykke på Ctrl-Shift-P (eller Cmd-Shift-P) for at åbne kommandopaletten:**
**Når den er installeret, klik i Visual Studio Code på Ctrl-Shift-P (eller Cmd-Shift-P) for at åbne kommandopaletten:**
**Forståelse af kommandopaletten:**
- **Giver** hurtig adgang til alle VS Code-kommandoer
- **Søger** kommandoer, mens du skriver
- **Tilbyder** genvejstaster for hurtigere udvikling
- **Søger** i kommandoer, mens du skriver
- **Tilbyder** tastaturgenveje for hurtigere udvikling
**Skriv "Live Server: Open with Live Server":**
**Hvad Live Server gør:**
- **Starter** en lokal udviklingsserver til dit projekt
- **Opdaterer** automatisk browseren, når du gemmer filer
- **Serverer** dine filer fra en lokal URL (typisk `localhost:5500`)
- **Opdaterer automatisk** browseren, når du gemmer filer
- **Servidorer** dine filer fra en lokal URL (typisk `localhost:5500`)
**Åbn en browser og naviger til `https://localhost:5500`:**
**Åbn en browser og til `https://localhost:5500`:**
Du bør nu se den side, du har oprettet! Lad os tilføje noget funktionalitet.
Du skulle nu kunne se den side, du har oprettet! Lad os tilføje noget funktionalitet.
## Tilføj CSS
Nu skal vi få tingene til at se godt ud! Visuel feedback har været afgørende for brugergrænseflader siden computingens tidlige dage. I 1980'erne opdagede forskere, at øjeblikkelig visuel feedback dramatisk forbedrer brugerens præstation og reducerer fejl. Det er præcis, hvad vi skal skabe.
Lad os nu gøre tingene pænere! Visuel feedback har været afgørende for brugerflader siden de tidlige dage med computerbrug. I 1980'erne opdagede forskere, at øjeblikkelig visuel feedback dramatisk forbedrer brugerens præstation og reducerer fejl. Det er lige præcis det, vi vil skabe.
Vores spil skal være krystalklart omkring, hvad der sker. Spillere skal straks vide, hvilket ord de skal skrive, og hvis de laver en fejl, skal de se det med det samme. Lad os lave noget simpelt, men effektivt styling:
Vores spil skal være krystalklart om, hvad der sker. Spillere skal med det samme kunne se, hvilket ord de skal skrive, og hvis de laver en fejl, skal de se det med det samme. Lad os lave enkel, men effektiv styling:
Opret en ny fil med navnet `style.css` og tilføj følgende syntaks.
@ -232,60 +231,60 @@ Opret en ny fil med navnet `style.css` og tilføj følgende syntaks.
```
**Forståelse af disse CSS-klasser:**
- **Fremhæver** det aktuelle ord med en gul baggrund for klar visuel vejledning
- **Signaliserer** skrivefejl med en lys koralfarvet baggrund
- **Fremhæver** det aktuelle ord med gul baggrund for klar visuel vejledning
- **Signalere** skrivefejl med en lys koralbaggrundsfarve
- **Giver** øjeblikkelig feedback uden at forstyrre brugerens skriveflow
- **Bruger** kontrastfarver for tilgængelighed og klar visuel kommunikation
- **Bruger** kontrasterende farver for tilgængelighed og klar visuel kommunikation
✅ Når det kommer til CSS, kan du designe din side, som du vil. Brug lidt tid på at gøre siden mere tiltalende:
✅ Når det kommer til CSS, kan du indrette din side, som du vil. Tag dig lidt tid og gør siden mere tiltalende:
- Vælg en anden skrifttype
- Farvelæg overskrifterne
- Juster størrelsen på elementerne
- Ændr størrelsen på elementer
## JavaScript
Nu bliver det interessant! 🎉 Vi har vores HTML-struktur og vores CSS-styling, men lige nu er vores spil som en smuk bil uden motor. JavaScript bliver den motor - det er det, der får alting til at fungere og reagere på, hvad spillerne gør.
Her bliver det interessant! 🎉 Vi har vores HTML-struktur og vores CSS-styling, men lige nu er vores spil som en smuk bil uden en motor. JavaScript bliver motoren - det er det, der får alt til faktisk at fungere og reagere på, hvad spillerne gør.
Her vil du se din kreation komme til live. Vi vil tackle dette trin for trin, så intet føles overvældende:
Her vil du se dit værk komme til live. Vi går det igennem trin for trin, så det ikke føles overvældende:
| Trin | Formål | Hvad du lærer |
|------|--------|---------------|
| [Opret konstanterne](../../../../4-typing-game/typing-game) | Opsæt citater og DOM-referencer | Variabelhåndtering og DOM-udvælgelse |
| [Event listener til at starte spillet](../../../../4-typing-game/typing-game) | Håndter spilinitialisering | Event-håndtering og UI-opdateringer |
| [Event listener til skrivning](../../../../4-typing-game/typing-game) | Behandl brugerinput i realtid | Inputvalidering og dynamisk feedback |
|-------|---------|---------------|
| [Opret konstanterne](../../../../4-typing-game/typing-game) | Sæt citater og DOM-referencer op | Variabelhåndtering og DOM-udvælgelse |
| [Event listener til at starte spillet](../../../../4-typing-game/typing-game) | Håndter spilinitialisering | Eventhåndtering og UI-opdateringer |
| [Event listener til skrivehandling](../../../../4-typing-game/typing-game) | Behandl brugerinput i realtid | Inputvalidering og dynamisk feedback |
**Denne strukturerede tilgang hjælper dig med at:**
- **Organisere** din kode i logiske, håndterbare sektioner
- **Bygge** funktionalitet gradvist for lettere fejlfinding
- **Forstå**, hvordan forskellige dele af din applikation arbejder sammen
- **Organisere** din kode i logiske, overskuelige sektioner
- **Bygge** funktionalitet trinvis for lettere fejlfinding
- **Forstå** hvordan forskellige dele af din applikation arbejder sammen
- **Skabe** genanvendelige mønstre til fremtidige projekter
Men først, opret en ny fil med navnet `script.js`.
Men først, opret en ny fil ved navn `script.js`.
### Opret konstanterne
### Tilføj konstanterne
Før vi dykker ned i handlingen, lad os samle alle vores ressourcer! Ligesom hvordan NASA's mission control opsætter alle deres overvågningssystemer før en opsendelse, er det meget nemmere, når du har alt forberedt og klar. Dette sparer os for at skulle lede efter ting senere og hjælper med at undgå tastefejl.
Før vi går i gang, lad os samle alle vores ressourcer! Ligesom NASA's missionskontrol sætter alle deres overvågningssystemer op før opsendelse, er det meget nemmere, når du har alt klar på forhånd. Det sparer os for at lede efter ting senere og hjælper med at forhindre tastefejl.
Her er, hvad vi først skal opsætte:
Her er, hvad vi skal sætte op først:
| Datatype | Formål | Eksempel |
|-----------|--------|----------|
| Array af citater | Gem alle mulige citater til spillet | `['Citat 1', 'Citat 2', ...]` |
| Ord-array | Opdel det aktuelle citat i individuelle ord | `['Når', 'du', 'har', ...]` |
| Ordindeks | Hold styr på hvilket ord spilleren skriver | `0, 1, 2, 3...` |
| Starttid | Beregn forløbet tid til scoring | `Date.now()` |
|----------|---------|---------|
| Array of quotes | Gem alle mulige citater til spillet | `['Quote 1', 'Quote 2', ...]` |
| Word array | Opdel det aktuelle citat i individuelle ord | `['When', 'you', 'have', ...]` |
| Word index | Spor hvilket ord spilleren skriver | `0, 1, 2, 3...` |
| Start time | Beregn forløbet tid til pointberegning | `Date.now()` |
**Vi skal også bruge referencer til vores UI-elementer:**
**Vi får også brug for referencer til vores UI-elementer:**
| Element | ID | Formål |
|---------|----|---------|
| Tekstinput | `typed-value` | Hvor spillere skriver |
| Citatvisning | `quote` | Viser citatet, der skal skrives |
| Citatvisning | `quote` | Viser citatet der skal skrives |
| Beskedområde | `message` | Viser statusopdateringer |
```javascript
// inside script.js
// all of our quotes
// inde i script.js
// alle vores citater
const quotes = [
'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.',
'There is nothing more deceptive than an obvious fact.',
@ -295,261 +294,320 @@ const quotes = [
'Nothing clears up a case so much as stating it to another person.',
'Education never ends, Watson. It is a series of lessons, with the greatest for the last.',
];
// store the list of words and the index of the word the player is currently typing
// gem listen over ord og indekset for det ord, spilleren i øjeblikket skriver
let words = [];
let wordIndex = 0;
// the starting time
// starttiden
let startTime = Date.now();
// page elements
// sideelementer
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message');
const typedValueElement = document.getElementById('typed-value');
```
**Hvad denne opsætningskode opnår:**
- **Gemmer** en array af Sherlock Holmes-citater ved hjælp af `const`, da citaterne ikke ændrer sig
- **Initialiserer** sporingsvariabler med `let`, da disse værdier vil opdateres under spillet
- **Fanger** referencer til DOM-elementer ved hjælp af `document.getElementById()` for effektiv adgang
- **Opsætter** fundamentet for al spilfunktionalitet med klare, beskrivende variabelnavne
**En gennemgang af hvad denne opsætningskode opnår:**
- **Gemmer** et array af Sherlock Holmes-citater med `const`, da citaterne ikke skal ændres
- **Initialiserer** sporingsvariabler med `let`, da disse værdier opdateres under gameplay
- **Indsamler** referencer til DOM-elementer med `document.getElementById()` for effektiv adgang
- **Opsætter** grundlaget for al spilstyring med klare, beskrivende variabelnavne
- **Organiserer** relaterede data og elementer logisk for lettere vedligeholdelse af koden
✅ Gå videre og tilføj flere citater til dit spil
> 💡 **Pro Tip**: Vi kan hente elementerne, når som helst vi vil i koden, ved at bruge `document.getElementById()`. Fordi vi ofte vil referere til disse elementer, undgår vi tastefejl med strenglitteraler ved at bruge konstanter. Frameworks som [Vue.js](https://vuejs.org/) eller [React](https://reactjs.org/) kan hjælpe dig med bedre at centralisere din kode.
> 💡 **Pro-tip**: Vi kan hente elementerne når som helst i koden ved at bruge `document.getElementById()`. Fordi vi henviser til disse elementer regelmæssigt, undgår vi tastefejl med strengliteral-konstanter. Frameworks som [Vue.js](https://vuejs.org/) eller [React](https://reactjs.org/) kan hjælpe med bedre central styring af koden.
>
**Derfor fungerer denne tilgang så godt:**
- **Forhindrer** stavefejl, når elementer refereres til flere gange
- **Forbedrer** kodens læsbarhed med beskrivende konstantnavne
- **Muliggør** bedre IDE-support med autoudfyldning og fejlkontrol
- **Gør** refaktorering lettere, hvis element-ID'er ændres senere
- **Forebygger** stavefejl når elementer refereres flere gange
- **Forbedrer** kodelæsbarhed med beskrivende konstanter
- **Muliggør** bedre IDE-understøttelse med autocomplete og fejlkontrol
- **Gør** refaktorering lettere hvis element-ider senere ændres
Tag et øjeblik til at se en video om brugen af `const`, `let` og `var`
Tag et minut til at se en video om brugen af `const`, `let` og `var`
[![Typer af variabler](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Typer af variabler")
[![Typer af variabler](https://img.youtube.com/vi/JNIXfGiDWM8/0.jpg)](https://youtube.com/watch?v=JNIXfGiDWM8 "Types of variables")
> 🎥 Klik på billedet ovenfor for en video om variabler.
### Tilføj startlogik
Nu begynder det hele at falde på plads! 🚀 Du er ved at skrive din første rigtige event listener, og der er noget meget tilfredsstillende ved at se din kode reagere på et knaptryk.
Her falder alting på plads! 🚀 Du er ved at skrive din første rigtige event listener, og der er noget særligt tilfredsstillende i at se din kode reagere på et klik på en knap.
Tænk over det: et sted derude vil en spiller trykke på "Start"-knappen, og din kode skal være klar til dem. Vi har ingen idé om, hvornår de vil trykke - det kan være med det samme, eller efter de har hentet en kop kaffe - men når de gør det, springer dit spil til live.
Tænk over det: et sted derude klikker en spiller på “Start”-knappen, og din kode skal være klar til det. Vi ved ikke hvornår de klikker - måske med det samme, måske efter en kop kaffe - men når de gør, springer spillet i aktion.
Når brugeren klikker `start`, skal vi vælge et citat, opsætte brugergrænsefladen og opsætte sporingen for det aktuelle ord og tiden. Nedenfor er den JavaScript-kode, du skal tilføje; vi diskuterer den lige efter scriptblokken.
Når brugeren klikker `start`, skal vi vælge et citat, sætte brugergrænsefladen op og begynde at spore det aktuelle ord og tid. Nedenfor finder du den JavaScript, du skal tilføje; vi gennemgår den lige efter script-blokken.
```javascript
// at the end of script.js
// i slutningen af script.js
document.getElementById('start').addEventListener('click', () => {
// get a quote
// få et citat
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// Put the quote into an array of words
// Sæt citatet i en array af ord
words = quote.split(' ');
// reset the word index for tracking
// nulstil ordindekset til sporing
wordIndex = 0;
// UI updates
// Create an array of span elements so we can set a class
// UI-opdateringer
// Opret en array af span-elementer, så vi kan sætte en klasse
const spanWords = words.map(function(word) { return `<span>${word} </span>`});
// Convert into string and set as innerHTML on quote display
// Konverter til streng og sæt som innerHTML på citatskærmen
quoteElement.innerHTML = spanWords.join('');
// Highlight the first word
// Fremhæv det første ord
quoteElement.childNodes[0].className = 'highlight';
// Clear any prior messages
// Ryd eventuelle tidligere beskeder
messageElement.innerText = '';
// Setup the textbox
// Clear the textbox
// Opsæt tekstboksen
// Ryd tekstboksen
typedValueElement.value = '';
// set focus
// sæt fokus
typedValueElement.focus();
// set the event handler
// sæt eventhandleren
// Start the timer
// Start timingen
startTime = new Date().getTime();
});
```
**Lad os bryde koden ned i logiske sektioner:**
**Lad os opdele koden i logiske afsnit:**
**📊 Opsætning af ordsporing:**
- **Vælger** et tilfældigt citat ved hjælp af `Math.floor()` og `Math.random()` for variation
- **Konverterer** citatet til en array af individuelle ord ved hjælp af `split(' ')`
- **Nulstiller** `wordIndex` til 0, da spillere starter med det første ord
- **Forbereder** spiltilstanden til en ny runde
**🎨 UI-opsætning og visning:**
- **Opretter** en array af `<span>`-elementer, der omslutter hvert ord for individuel styling
- **Samler** span-elementerne til en enkelt streng for effektiv DOM-opdatering
- **Vælger** et tilfældigt citat vha. `Math.floor()` og `Math.random()` for variation
- **Omformer** citatet til et array af individuelle ord med `split(' ')`
- **Nulstiller** `wordIndex` til 0, da spillerne starter med første ord
- **Forbereder** spillets tilstand til en frisk runde
**🎨 UI Opsætning og visning:**
- **Opretter** et array af `<span>` elementer, hvor hvert ord pakkes ind for individuel styling
- **Sammenføjer** span-elementerne til en enkelt streng for effektiv DOM-opdatering
- **Fremhæver** det første ord ved at tilføje CSS-klassen `highlight`
- **Rydder** eventuelle tidligere spilbeskeder for at give en ren start
- **Fjerner** tidligere spilmeldinger for at give en ren start
**⌨️ Forberedelse af tekstfelt:**
- **Rydder** eksisterende tekst i inputfeltet
- **Sætter fokus** på tekstfeltet, så spillere kan begynde at skrive med det samme
- **Forbereder** inputområdet til den nye spilsession
- **Sætter fokus** på tekstfeltet så spilleren kan begynde at skrive straks
- **Forbereder** indtastningsområdet til den nye spil-session
**⏱️ Initialisering af timer:**
- **Fanger** det aktuelle tidsstempel ved hjælp af `new Date().getTime()`
- **Muliggør** nøjagtig beregning af skrivehastighed og afslutningstid
- **Starter** performance-sporingen for spillet
**⏱️ Timer initialisering:**
- **Fanger** det aktuelle tidsstempel med `new Date().getTime()`
- **Muliggør** præcis beregning af skrivehastighed og tid
- **Starter** performance-sporing for spillet
### Tilføj skrive-logik
Her tackler vi kernen i vores spil! Bare rolig, hvis det virker som meget i starten - vi går igennem hver del, og til sidst vil du se, hvor logisk det hele er.
Her håndterer vi kernen i vores spil! Bare rolig, hvis det virker meget i starten vi gennemgår det hele trin for trin, og til sidst vil du se, hvor logisk det hele hænger sammen.
Det, vi bygger her, er ret sofistikeret: hver gang nogen skriver et bogstav, vil vores kode tjekke, hvad de har skrevet, give dem feedback og beslutte, hvad der skal ske næste gang. Det minder om, hvordan tidlige tekstbehandlingsprogrammer som WordStar i 1970'erne gav realtidsfeedback til skrivere.
Det vi bygger her, er ret avanceret: hver gang nogen skriver et bogstav, tjekker koden hvad der blev skrevet, giver feedback og bestemmer hvad der skal ske næste gang. Det minder om hvordan tidlige tekstbehandlere som WordStar i 1970erne gav realtids-feedback til skrivere.
```javascript
// at the end of script.js
// i slutningen af script.js
typedValueElement.addEventListener('input', () => {
// Get the current word
// Hent det aktuelle ord
const currentWord = words[wordIndex];
// get the current value
// hent den aktuelle værdi
const typedValue = typedValueElement.value;
if (typedValue === currentWord && wordIndex === words.length - 1) {
// end of sentence
// Display success
// slutningen af sætningen
// Vis succes
const elapsedTime = new Date().getTime() - startTime;
const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`;
messageElement.innerText = message;
} else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) {
// end of word
// clear the typedValueElement for the new word
// slutningen af ordet
// ryd typedValueElement for det nye ord
typedValueElement.value = '';
// move to the next word
// gå videre til det næste ord
wordIndex++;
// reset the class name for all elements in quote
// nulstil klassenavnet for alle elementer i citatet
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// highlight the new word
// fremhæv det nye ord
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// currently correct
// highlight the next word
// korrekt i øjeblikket
// fremhæv det næste ord
typedValueElement.className = '';
} else {
// error state
// fejltillstand
typedValueElement.className = 'error';
}
});
```
**Forstå flowet i skrive-logikken:**
**Forståelse af skrive-logikkens flow:**
Denne funktion bruger en vandfaldstilgang, hvor betingelser tjekkes fra mest specifik til mest generel. Lad os bryde hver situation ned:
Funktionen bruger en trinvist faldende (waterfall) tilgang, der tjekker betingelser fra mest specifik til mest generel. Lad os gennemgå hver situation:
```mermaid
flowchart TD
A[Player types character] --> B[Get current word and typed value]
B --> C{Quote complete?}
C -->|Yes| D[Show completion message with time]
C -->|No| E{Word complete with space?}
E -->|Yes| F[Clear input, move to next word, update highlight]
E -->|No| G{Typing correctly so far?}
G -->|Yes| H[Remove error styling]
G -->|No| I[Show error styling]
A[Spiller indtaster tegn] --> B[Hent nuværende ord og indtastet værdi]
B --> C{Fynt bliver færdig?}
C -->|Ja| D[Vis færdiggørelsesbesked med tid]
C -->|Nej| E{Ord færdigt med mellemrum?}
E -->|Ja| F[Ryd input, gå til næste ord, opdater fremhævning]
E -->|Nej| G{Skriver korrekt indtil nu?}
G -->|Ja| H[Fjern fejlstil]
G -->|Nej| I[Vis fejlstil]
```
**🏁 Citat fuldført (Scenario 1):**
- **Tjekker** om den indtastede værdi matcher det aktuelle ord, OG om vi er på det sidste ord
- **Beregner** forløbet tid ved at trække starttidspunktet fra det aktuelle tidspunkt
- **Konverterer** millisekunder til sekunder ved at dividere med 1.000
- **Viser** en gratulationsbesked med afslutningstid
**✅ Ord fuldført (Scenario 2):**
- **Registrerer** ordafslutning, når input slutter med et mellemrum
- **Validerer** at trimmet input matcher det aktuelle ord præcist
- **Rydder** inputfeltet for det næste ord
- **Går videre** til det næste ord ved at øge `wordIndex`
**🏁 Citat færdig (Scenario 1):**
- **Tjekker** om den indtastede værdi matcher det aktuelle ord OG vi er på sidste ord
- **Beregner** forløbet tid ved at trække starttidspunkt fra nuværende tid
- **Omregner** millisekunder til sekunder ved at dividere med 1.000
- **Viser** en tillykke-besked med afslutningstiden
**✅ Ord færdigt (Scenario 2):**
- **Registrerer** ordfærdiggørelse når input slutter med et mellemrum
- **Bekræfter** at trimmet input præcist matcher det nuværende ord
- **Rydder** inputfeltet til næste ord
- **Går videre** til næste ord ved at øge `wordIndex`
- **Opdaterer** visuel fremhævning ved at fjerne alle klasser og fremhæve det nye ord
**📝 Skriveprocessen (Scenario 3):**
- **Bekræfter** at det aktuelle ord starter med det, der er blevet skrevet indtil videre
- **Fjerner** eventuel fejlstyling for at vise, at input er korrekt
- **Tillader** fortsat skrivning uden afbrydelse
**📝 Skriver fortsætter (Scenario 3):**
- **Sikrer** at det nuværende ord starter med det der er skrevet indtil nu
- **Fjerner** fejlstyling for at vise at input er korrekt
- **Tillader** fortsat skrivning uden afbrydelser
**❌ Fejltilstand (Scenario 4):**
- **Udløses** når den indtastede tekst ikke matcher starten af det forventede ord
- **Anvender** fejl-CSS-klassen for at give øjeblikkelig visuel feedback
- **Aktiveres** når indtastet tekst ikke matcher forventet begyndelse af ordet
- **Anvender** fejl-CSS-klasse for at give øjeblikkelig visuel feedback
- **Hjælper** spillere med hurtigt at identificere og rette fejl
## Test din applikation
Se, hvad du har opnået! 🎉 Du har lige bygget et rigtigt, fungerende skrive-spil fra bunden ved hjælp af event-drevet programmering. Tag et øjeblik til at værdsætte det - det er ikke nogen lille bedrift!
Se hvad du har opnået! 🎉 Du har netop bygget et ægte, fungerende skrive-spil helt fra bunden med event-drevet programmering. Tag et øjeblik til at værdsætte det det er ikke noget lille projekt!
Nu kommer testfasen! Vil det fungere som forventet? Har vi overset noget? Her er sagen: hvis noget ikke fungerer perfekt med det samme, er det helt normalt. Selv erfarne udviklere finder regelmæssigt fejl i deres kode. Det er en del af udviklingsprocessen!
Nu følger testfasen! Vil det virke som forventet? Har vi glemt noget? Det er helt normalt hvis noget ikke virker perfekt med det samme. Selv erfarne udviklere støder ofte på fejl undervejs. Det hører til processen!
Klik på `start`, og begynd at skrive! Det skulle se lidt ud som animationen, vi så før.
Klik på `start` og begynd at skrive! Det skulle gerne ligne den animation, vi så tidligere.
![Animation af spillet i aktion](../../../../4-typing-game/images/demo.gif)
**Hvad du skal teste i din applikation:**
- **Bekræfter** at klik på Start viser et tilfældigt citat
**Hvad skal du teste i din applikation:**
- **Sikrer** at klik på Start viser et tilfældigt citat
- **Bekræfter** at skrivning fremhæver det aktuelle ord korrekt
- **Tjekker** at fejlstyling vises ved forkert skrivning
- **Sikrer** at fuldførte ord flytter fremhævelsen korrekt
- **Tester** at afslutning af citatet viser afslutningsbeskeden med tid
- **Sikrer** at færdiggørelse af ord skifter fremhævning korrekt
- **Tester** at afslutning af citatet viser færdiggørelsesbesked med tid
**Almindelige fejlfindingsråd:**
**Almindelige fejlfindingstips:**
- **Tjek** browserkonsollen (F12) for JavaScript-fejl
- **Bekræft** at alle filnavne matcher præcist (case-sensitive)
- **Sikrer** at Live Server kører og opdaterer korrekt
- **Test** forskellige citater for at verificere, at den tilfældige udvælgelse fungerer
- **Verificer** at alle filnavne matcher præcist (store/små bogstaver betyder noget)
- **Sørg for** at Live Server kører og opdaterer korrekt
- **Test** forskellige citater for at sikre at tilfældig udvælgelse virker
---
## GitHub Copilot Agent Challenge 🎮
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Udvid skrive-spillet ved at implementere et sværhedssystem, der justerer spillet baseret på spillerens præstation. Denne udfordring vil hjælpe dig med at øve avanceret eventhåndtering, dataanalyse og dynamiske UI-opdateringer.
**Beskrivelse:** Udbyg skrive-spillet ved at implementere et sværhedsgradssystem, som justerer spillet baseret på spillerens præstation. Denne udfordring træner dig i avanceret eventhåndtering, dataanalyse og dynamiske UI-opdateringer.
**Opgave:** Opret et sværhedssystem for skrive-spillet, der:
1. Sporer spillerens skrivehastighed (ord per minut) og nøjagtighedsprocent
2. Justerer automatisk til tre sværhedsgrader: Let (simple citater), Mellem (nuværende citater), Svær (komplekse citater med tegnsætning)
3. Viser den aktuelle sværhedsgrad og spillerstatistik på UI'en
4. Implementerer en streak-tæller, der øger sværhedsgraden efter 3 på hinanden følgende gode præstationer
5. Tilføjer visuel feedback (farver, animationer) for at indikere ændringer i sværhedsgrad
**Opgave:** Opret et sværhedsgradssystem til skrive-spillet som:
1. Sporer spillerens skrivehastighed (ord pr. minut) og nøjagtighed i procent
2. Justerer automatisk mellem tre sværhedsgrader: Let (enkle citater), Medium (nuværende citater), Svær (komplekse citater med tegnsætning)
3. Viser den aktuelle sværhedsgrad og spillerstatistik på UI
4. Implementerer en række-tæller, der øger sværhedsgraden efter 3 på hinanden følgende gode præstationer
5. Tilføjer visuel feedback (farver, animationer) for at indikere sværhedsgradsskift
Tilføj de nødvendige HTML-elementer, CSS-stilarter og JavaScript-funktioner for at implementere denne funktion. Inkluder korrekt fejlhåndtering og sørg for, at spillet forbliver tilgængeligt med passende ARIA-labels.
Tilføj de nødvendige HTML-elementer, CSS-styles og JavaScript-funktioner for at udføre denne funktionalitet. Inkluder korrekt fejlhåndtering og sørg for at spillet forbliver tilgængeligt med passende ARIA-labels.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Klar til at tage dit skrive-spil til næste niveau? Prøv at implementere disse avancerede funktioner for at uddybe din forståelse af eventhåndtering og DOM-manipulation:
Klar til at tage dit skrive-spil til næste niveau? Prøv at implementere disse avancerede funktioner for at styrke din forståelse af event-håndtering og DOM-manipulation:
**Tilføj mere funktionalitet:**
| Funktion | Beskrivelse | Færdigheder du vil øve |
|----------|-------------|------------------------|
| **Inputkontrol** | Deaktiver `input` event listener ved afslutning, og aktiver den igen, når knappen klikkes | Eventhåndtering og tilstandskontrol |
| **UI-tilstandsadministration** | Deaktiver tekstfeltet, når spilleren fuldfører citatet | DOM-egenskabsmanipulation |
| **Modal dialog** | Vis en modal dialogboks med succesbeskeden | Avancerede UI-mønstre og tilgængelighed |
| **Highscore-system** | Gem highscores ved hjælp af `localStorage` | Browser storage API'er og datavedholdenhed |
| Funktion | Beskrivelse | Færdigheder du øver |
|---------|-------------|---------------------|
| **Input Control** | Deaktiver `input` event listener ved færdiggørelse, og genaktiver ved knapklik | Eventstyring og statekontrol |
| **UI State Management** | Deaktiver tekstboksen når spilleren færdiggør citatet | DOM egenskabs-manipulation |
| **Modal Dialog** | Vis en modal dialogboks med succesbeskeden | Avancerede UI-mønstre og tilgængelighed |
| **High Score System** | Gem high scores med `localStorage` | Browserlagring og dataperistens |
**Implementeringstip:**
- **Undersøg** `localStorage.setItem()` og `localStorage.getItem()` til vedvarende lagring
- **Øv** dig i dynamisk at tilføje og fjerne event listeners
- **Udforsk** HTML-dialog elementer eller CSS modale patterns
- **Tænk på** tilgængelighed når form-kontroller aktiveres/deaktiveres
## Quiz efter lektionen
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/22)
**Implementeringstips:**
- **Undersøg** `localStorage.setItem()` og `localStorage.getItem()` for vedholdende lagring
- **Øv** dig i at tilføje og fjerne event listeners dynamisk
- **Udforsk** HTML dialog-elementer eller CSS modal-mønstre
- **Overvej** tilgængelighed, når du deaktiverer og aktiverer formkontroller
---
## Quiz efter forelæsning
## 🚀 Din tidslinje for mestre skrive-spillet
### ⚡ **Hvad du kan nå på de næste 5 minutter**
- [ ] Test dit skrive-spil med forskellige citater for at sikre det kører problemfrit
- [ ] Eksperimenter med CSS-styling - prøv at ændre fremhævnings- og fejlfarver
- [ ] Åbn browserens DevTools (F12) og følg Console mens du spiller
- [ ] Udfordr dig selv til at færdiggøre et citat så hurtigt som muligt
### ⏰ **Hvad du kan nå på en time**
- [ ] Tilføj flere citater til arrayet (måske fra dine yndlingsbøger eller film)
- [ ] Implementer localStorage high score systemet fra udfordringen
- [ ] Lav en ord-pr.-minut beregner som vises efter hvert spil
- [ ] Tilføj lydeffekter for korrekt skrivning, fejl og færdiggørelse
### 📅 **Din uge-lange rejse**
- [ ] Byg en multiplayer version hvor venner kan konkurrere side om side
- [ ] Skab forskellige sværhedsgrader med varierende citatkompleksitet
- [ ] Tilføj en statusbar, der viser hvor langt i citatet man er
- [ ] Implementer bruger-konti med personlig statistiksporing
- [ ] Design brugerdefinerede temaer og lad brugerne vælge deres foretrukne styling
### 🗓️ **Din måned-lange transformation**
- [ ] Opret et skrivekursus med lektioner der gradvist lærer korrekt fingerplacering
- [ ] Byg analyser som viser hvilke bogstaver eller ord der giver flest fejl
- [ ] Tilføj understøttelse af forskellige sprog og tastaturlayouter
- [ ] Integrer med uddannelses-APIer for at hente citater fra litteraturdatabaser
- [ ] Udgiv dit forbedrede skrive-spil, så andre kan bruge og nyde det
### 🎯 **Endeligt refleksions-tjek**
**Før du går videre, tag et øjeblik til at fejre:**
- Hvad var det mest tilfredsstillende øjeblik mens du byggede dette spil?
- Hvordan føler du om event-drevet programmering nu sammenlignet med da du startede?
- Hvilken funktion glæder du dig mest til at tilføje for at gøre spillet unikt for dig?
- Hvordan kan du anvende event-håndteringskoncepter til andre projekter?
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/22)
```mermaid
journey
title Din Rejse til Tillid i Event-Programmering
section I dag
Forståelse af begivenheder: 3: You
Opbygning af brugergrænseflade: 4: You
Skrivning af begivenhedslyttere: 5: You
section Denne uge
Tilføjelse af funktioner: 4: You
Fejlfinding af problemer: 5: You
Forbedring af brugeroplevelse: 4: You
section Næste måned
Opbygning af komplekse apps: 5: You
Undervise andre: 5: You
Oprettelse af frameworks: 5: You
```
> 🌟 **Husk**: Du har netop mestret et af de kernekoncepter, der driver alle interaktive hjemmesider og applikationer. Event-drevet programmering er det, der får nettet til at føles levende og responsivt. Hver gang du ser en dropdown-menu, en formular der valideres mens du skriver, eller et spil der reagerer på dine klik, forstår du nu magien bagved. Du lærer ikke bare at kode du lærer at skabe oplevelser der føles intuitive og engagerende! 🎉
---
## Gennemgang & Selvstudie
## Gennemgang & selvstudie
Læs om [alle de tilgængelige events](https://developer.mozilla.org/docs/Web/Events) for udvikleren via webbrowseren, og overvej de scenarier, hvor du ville bruge hver enkelt.
Læs op på [alle events tilgængelige](https://developer.mozilla.org/docs/Web/Events) for udviklere via browseren, og overvej scenarier hvor du kunne bruge hver af dem.
## Opgave
[Opret et nyt tastaturspil](assignment.md)
[Opret et nyt keyboard-spil](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,177 +1,279 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "33a875c522f237a2026e4653240dfc07",
"translation_date": "2025-10-23T22:14:23+00:00",
"original_hash": "00aa85715e1efd4930c17a23e3012e69",
"translation_date": "2026-01-06T23:47:10+00:00",
"source_file": "5-browser-extension/1-about-browsers/README.md",
"language_code": "da"
}
-->
# Browserudvidelsesprojekt Del 1: Alt om Browsere
# Browser Extension Project Del 1: Alt om Browsere
![Browser sketchnote](../../../../translated_images/browser.60317c9be8b7f84adce43e30bff8d47a1ae15793beab762317b2bc6b74337c1a.da.jpg)
```mermaid
journey
title Din Browserudvidelses Udviklingsrejse
section Fundament
Forstå browsere: 3: Student
Lær udvidelsestyper: 4: Student
Opsæt udvikling: 4: Student
section Udvikling
Byg grænseflade: 4: Student
Tilføj funktionalitet: 5: Student
Håndter data: 5: Student
section Integration
Test i browser: 5: Student
Fejlret problemer: 4: Student
Finpuds oplevelse: 5: Student
```
![Browser sketchnote](../../../../translated_images/browser.60317c9be8b7f84a.da.jpg)
> Sketchnote af [Wassim Chegham](https://dev.to/wassimchegham/ever-wondered-what-happens-when-you-type-in-a-url-in-an-address-bar-in-a-browser-3dob)
## Quiz før forelæsning
## Forudgående Quiz
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/23)
[Forudgående quiz](https://ff-quizzes.netlify.app/web/quiz/23)
### Introduktion
Browserudvidelser er små applikationer, der forbedrer din webbrowseroplevelse. Ligesom Tim Berners-Lees oprindelige vision om en interaktiv web, udvider udvidelser browserens kapaciteter ud over simpel dokumentvisning. Fra adgangskodeadministratorer, der holder dine konti sikre, til farvevælgere, der hjælper designere med at finde de perfekte nuancer, løser udvidelser daglige browsingudfordringer.
Browser-udvidelser er mini-applikationer, der forbedrer din webbrowseroplevelse. Ligesom Tim Berners-Lees oprindelige vision om et interaktivt web, udvider udvidelser browserens kapaciteter ud over blot visning af dokumenter. Fra adgangskodeadministratorer, der holder dine konti sikre, til farvevælgere, der hjælper designere med at hente perfekte nuancer, løser udvidelser daglige browserudfordringer.
Før vi bygger din første udvidelse, lad os forstå, hvordan browsere fungerer. Ligesom Alexander Graham Bell måtte forstå lydtransmission, før han opfandt telefonen, vil kendskab til browserens grundlæggende funktioner hjælpe dig med at skabe udvidelser, der integreres problemfrit med eksisterende browsersystemer.
Før vi bygger din første udvidelse, lad os forstå, hvordan browsere fungerer. Ligesom Alexander Graham Bell måtte forstå lydtransmission, før han opfandt telefonen, vil kendskab til browsergrundlaget hjælpe dig med at skabe udvidelser, der integreres problemfrit med eksisterende browsersystemer.
Ved slutningen af denne lektion vil du forstå browserarkitektur og være begyndt at bygge din første udvidelse.
Ved slutningen af denne lektion vil du forstå browserarkitektur og være startet med at bygge din første udvidelse.
## Forståelse af webbrowsere
```mermaid
mindmap
root((Browser Arkitektur))
Core Components
Rendering Engine
JavaScript Engine
Network Stack
Storage APIs
User Interface
Adressefelt
Fanestyring
Bogmærker
Udvidelsesikoner
Extension System
Manifestfiler
Indholdsscripts
Baggrundssider
Popup-vinduer
Security Model
Samme oprindelsespolitik
Tilladelses-API
Indholdssikkerhed
Isolerede verdener
Development Tools
DevTools-integration
Fejlkonsol
Ydelsesmonitor
Udvidelsesinspektør
```
## Forståelse af Webbrowsere
En webbrowser er i bund og grund en sofistikeret dokumentfortolker. Når du skriver "google.com" i adressefeltet, udfører browseren en kompleks række operationer - anmoder om indhold fra servere verden over, derefter analyserer og gengiver den kode til de interaktive websider, du ser.
En webbrowser er essentielt en avanceret dokumentfortolker. Når du skriver "google.com" i adressebaren, udfører browseren en kompleks række operationer anmoder om indhold fra servere over hele verden, og fortolker og renderer derefter koden til de interaktive websider, du ser.
Denne proces afspejler, hvordan den første webbrowser, WorldWideWeb, blev designet af Tim Berners-Lee i 1990 for at gøre hyperlinkede dokumenter tilgængelige for alle.
**Lidt historie**: Den første browser blev kaldt 'WorldWideWeb' og blev skabt af Sir Timothy Berners-Lee i 1990.
**Lidt historie**: Den første browser hed 'WorldWideWeb' og blev skabt af Sir Timothy Berners-Lee i 1990.
![tidlige browsere](../../../../translated_images/earlybrowsers.d984b711cdf3a42ddac919d46c4b5ca7232f68ccfbd81395e04e5a64c0015277.da.jpg)
![early browsers](../../../../translated_images/earlybrowsers.d984b711cdf3a42d.da.jpg)
> Nogle tidlige browsere, via [Karen McGrane](https://www.slideshare.net/KMcGrane/week-4-ixd-history-personal-computing)
### Hvordan browsere behandler webindhold
### Hvordan Browsere Behandler Webindhold
Processen fra at indtaste en URL til at se en webside involverer flere koordinerede trin, der sker inden for få sekunder:
Processen mellem at indtaste en URL og se en webside involverer flere koordinerede trin, der sker inden for sekunder:
```mermaid
sequenceDiagram
participant User
participant Browser
participant Extension
participant DNS
participant Server
User->>Browser: Types URL and presses Enter
Browser->>DNS: Looks up server IP address
DNS->>Browser: Returns IP address
Browser->>Server: Requests web page content
Server->>Browser: Sends HTML, CSS, and JavaScript
Browser->>User: Renders complete web page
User->>Browser: Indtaster URL og trykker Enter
Browser->>Extension: Udløser beforeRequest-begivenhed
Extension->>Extension: Tjek om URL skal ændres
Browser->>DNS: Slår serverens IP-adresse op
DNS->>Browser: Returnerer IP-adresse
Browser->>Server: Anmoder om websideindhold
Server->>Browser: Sender HTML, CSS og JavaScript
Browser->>Extension: Udløser beforeResponse-begivenhed
Extension->>Extension: Ændr indhold hvis nødvendigt
Browser->>User: Viser komplet webside
Extension->>User: Vis opdateringer i udvidelses-brugerflade
```
**Dette opnår processen:**
- **Oversætter** den menneskeligt læsbare URL til en server-IP-adresse via DNS-opslag
- **Etablerer** en sikker forbindelse med webserveren ved hjælp af HTTP- eller HTTPS-protokoller
- **Anmoder** om det specifikke websideindhold fra serveren
- **Modtager** HTML-markup, CSS-styling og JavaScript-kode fra serveren
- **Gengiver** alt indhold til den interaktive webside, du ser
### Browserens kernefunktioner
Moderne browsere tilbyder adskillige funktioner, som udvidelsesudviklere kan udnytte:
| Funktion | Formål | Muligheder for udvidelser |
|----------|--------|---------------------------|
| **Gengivelsesmotor** | Viser HTML, CSS og JavaScript | Indholdsmodifikation, stylingindsprøjtning |
| **JavaScript-motor** | Udfører JavaScript-kode | Brugerdefinerede scripts, API-interaktioner |
| **Lokal lagring** | Gemmer data lokalt | Brugerpræferencer, cachelagrede data |
| **Netværksstak** | Håndterer webanmodninger | Anmodningsovervågning, dataanalyse |
| **Sikkerhedsmodel** | Beskytter brugere mod skadeligt indhold | Indholdsfiltrering, sikkerhedsforbedringer |
**At forstå disse funktioner hjælper dig med:**
- **Identificere** hvor din udvidelse kan tilføje mest værdi
**Dette opnår denne proces:**
- **Oversætter** den menneskelige læsbare URL til en server-IP-adresse via DNS opslag
- **Etablerer** en sikker forbindelse til webserveren ved brug af HTTP eller HTTPS protokoller
- **Anmoder** om den specifikke websideindhold fra serveren
- **Modtager** HTML markup, CSS styling og JavaScript kode fra serveren
- **Renderer** alt indhold til den interaktive webside, du ser
### Browsers Kernefunktioner
Moderne browsere tilbyder mange funktioner, som udvidelsesudviklere kan udnytte:
| Funktion | Formål | Udvidelsesmuligheder |
|---------|---------|------------------------|
| **Rendering Engine** | Viser HTML, CSS og JavaScript | Indholdsmodifikation, stylingindsprøjtning |
| **JavaScript Engine** | Udfører JavaScript kode | Egne scripts, API-interaktioner |
| **Local Storage** | Gemmer data lokalt | Brugerpræferencer, cachelagrede data |
| **Network Stack** | Håndterer webanmodninger | Anmodningsovervågning, dataanalyse |
| **Security Model** | Beskytter brugere mod ondsindet indhold | Indholdsfiltrering, sikkerhedsforbedringer |
**At forstå disse funktioner hjælper dig med at:**
- **Identificere** hvor din udvidelse kan tilføre mest værdi
- **Vælge** de rigtige browser-API'er til din udvidelses funktionalitet
- **Designe** udvidelser, der fungerer effektivt med browsersystemer
- **Sikre** at din udvidelse følger browserens sikkerhedsbedste praksis
- **Designe** udvidelser, der arbejder effektivt med browsersystemerne
- **Sikre** at din udvidelse følger browserens sikkerheds bedste praksis
### Overvejelser om udvikling på tværs af browsere
### Overvejelser om Cross-Browser Udvikling
Forskellige browsere implementerer standarder med små variationer, ligesom forskellige programmeringssprog kan håndtere den samme algoritme forskelligt. Chrome, Firefox og Safari har hver deres unikke karakteristika, som udviklere skal tage højde for under udvidelsesudvikling.
Forskellige browsere implementerer standarder med små variationer, ligesom forskellige programmeringssprog kan håndtere den samme algoritme forskelligt. Chrome, Firefox og Safari har hver unikke karakteristika, som udviklere må tage højde for under udvidelsesudvikling.
> 💡 **Pro Tip**: Brug [caniuse.com](https://www.caniuse.com) til at tjekke, hvilke webteknologier der understøttes på tværs af forskellige browsere. Dette er uvurderligt, når du planlægger din udvidelses funktioner!
**Vigtige overvejelser for udvidelsesudvikling:**
- **Test** din udvidelse på tværs af Chrome, Firefox og Edge-browsere
- **Tilpas** til forskellige browserudvidelses-API'er og manifestformater
- **Håndter** varierende ydeevneegenskaber og begrænsninger
- **Tilbyd** alternativer for browserspecifikke funktioner, der måske ikke er tilgængelige
**Vigtige overvejelser ved udvidelsesudvikling:**
- **Test** din udvidelse på Chrome, Firefox og Edge browsere
- **Tilpas** til forskellige browser-udvidelses-API'er og manifestformater
- **Håndter** forskellig ydelseskarakteristik og begrænsninger
- **Giv** fallback-løsninger for browser-specifikke funktioner, der måske ikke er tilgængelige
**Analyseindsigt**: Du kan finde ud af, hvilke browsere dine brugere foretrækker, ved at installere analysepakker i dine webudviklingsprojekter. Disse data hjælper dig med at prioritere, hvilke browsere der skal understøttes først.
**Analyseindsigt**: Du kan afgøre, hvilke browsere dine brugere foretrækker, ved at installere analysepakker i dine webudviklingsprojekter. Disse data hjælper dig med at prioritere, hvilke browsere du skal understøtte først.
## Forståelse af browserudvidelser
## Forståelse af Browserudvidelser
Browserudvidelser løser almindelige webbrowserudfordringer ved at tilføje funktionalitet direkte til browserens grænseflade. I stedet for at kræve separate applikationer eller komplekse arbejdsgange, giver udvidelser øjeblikkelig adgang til værktøjer og funktioner.
Browserudvidelser løser almindelige webbrowseudfordringer ved at tilføje funktionalitet direkte i browserens interface. I stedet for at kræve separate applikationer eller komplekse arbejdsgange, giver udvidelser øjeblikkelig adgang til værktøjer og funktioner.
Dette koncept afspejler, hvordan tidlige computerpionerer som Douglas Engelbart forestillede sig at forstærke menneskelige kapaciteter med teknologi - udvidelser forstærker browserens grundlæggende funktionalitet.
Dette koncept afspejler, hvordan tidlige computerpionerer som Douglas Engelbart forestillede sig at forstærke menneskelige evner med teknologi udvidelser forstærker din browsers basisfunktionalitet.
```mermaid
quadrantChart
title Kategorier af browserudvidelser
x-axis Simpel --> Kompleks
y-axis Personligt brug --> Professionelle værktøjer
quadrant-1 Udviklerværktøjer
quadrant-2 Virksomhedsløsninger
quadrant-3 Personlige værktøjer
quadrant-4 Produktivitetsapps
Ad Blockers: [0.3, 0.2]
Password Managers: [0.7, 0.3]
Color Pickers: [0.4, 0.8]
Code Formatters: [0.8, 0.9]
Note Taking: [0.6, 0.5]
Video Downloaders: [0.5, 0.2]
Time Trackers: [0.7, 0.6]
Screenshot Tools: [0.4, 0.4]
```
**Populære udvidelseskategorier og deres fordele:**
- **Produktivitetsværktøjer**: Opgavestyring, notatapps og tidsregistrering, der hjælper dig med at holde dig organiseret
- **Sikkerhedsforbedringer**: Adgangskodeadministratorer, annonceblokering og privatlivsværktøjer, der beskytter dine data
- **Udviklingsværktøjer**: Kodeformateringsværktøjer, farvevælgere og fejlfindingsværktøjer, der effektiviserer udviklingen
- **Indholdsforbedring**: Læsetilstande, videodownloadere og skærmbilledeværktøjer, der forbedrer din weboplevelse
- **Produktivitetsværktøjer**: Opgavestyring, notattagningsapps og tidsmålere, der hjælper dig med at holde styr på tingene
- **Sikkerhedsforbedringer**: Adgangskodeadministratorer, annonceblokkere og privatlivsværktøjer, der beskytter dine data
- **Udviklerværktøjer**: Kodeformaterere, farvevælgere og debug-værktøjer, der effektiviserer udvikling
- **Indholdsforbedring**: Læsetilstande, video-downloadere og screenshot-værktøjer, der forbedrer din weoplevelse
**Refleksionsspørgsmål**: Hvilke browserudvidelser er dine favoritter? Hvilke specifikke opgaver udfører de, og hvordan forbedrer de din browseroplevelse?
**Reflektionsspørgsmål**: Hvad er dine yndlingsbrowserudvidelser? Hvilke specifikke opgaver udfører de, og hvordan forbedrer de din browseroplevelse?
### 🔄 **Pædagogisk Tjek-ind**
**Forståelse af Browserarkitektur**: Før du går i gang med udvidelsesudvikling, skal du sørge for, at du kan:
- ✅ Forklare, hvordan browsere håndterer webanmodninger og renderer indhold
- ✅ Identificere hovedkomponenterne i browserarkitekturen
- ✅ Forstå, hvordan udvidelser integreres med browserfunktionalitet
- ✅ Genkende sikkerhedsmodellen, der beskytter brugerne
## Installation og administration af udvidelser
**Hurtig Selvtest**: Kan du spore vejen fra det at indtaste en URL til at se en webside?
1. **DNS opslag** omdanner URL til IP-adresse
2. **HTTP-anmodning** henter indhold fra serveren
3. **Parsing** behandler HTML, CSS og JavaScript
4. **Rendering** viser den endelige webside
5. **Udvidelser** kan ændre indhold på flere trin
At forstå udvidelsesinstallationsprocessen hjælper dig med at forudse brugeroplevelsen, når folk installerer din udvidelse. Installationsprocessen er standardiseret på tværs af moderne browsere, med mindre variationer i grænsefladedesign.
## Installation og Administration af Udvidelser
![skærmbillede af Edge-browseren, der viser den åbne edge://extensions-side og åbne indstillingsmenu](../../../../translated_images/install-on-edge.d68781acaf0b3d3dada8b7507cde7a64bf74b7040d9818baaa9070668e819f90.da.png)
At forstå installationsprocessen for udvidelser hjælper dig med at forudse brugeroplevelsen, når folk installerer din udvidelse. Installationsprocessen er standardiseret på tværs af moderne browsere med mindre variationer i grænsefladedesign.
> **Vigtigt**: Sørg for at aktivere udviklertilstand og tillade udvidelser fra andre butikker, når du tester dine egne udvidelser.
![screenshot of the Edge browser showing the open edge://extensions page and open settings menu](../../../../translated_images/install-on-edge.d68781acaf0b3d3d.da.png)
### Udviklingsudvidelses installationsproces
> **Vigtigt**: Sørg for at slå udviklertilstand til og tillade udvidelser fra andre butikker, når du tester dine egne udvidelser.
Når du udvikler og tester dine egne udvidelser, skal du følge denne arbejdsgang:
### Udviklingsinstallation af Udvidelser
Når du udvikler og tester dine egne udvidelser, følg denne arbejdsgang:
```mermaid
flowchart TD
A[Skriv kode] --> B[Byg udvidelse]
B --> C{Første installation?}
C -->|Ja| D[Indlæs opak]
C -->|Nej| E[Genindlæs udvidelse]
D --> F[Test funktionalitet]
E --> F
F --> G{Fungerer korrekt?}
G -->|Nej| H[Fejlret problemer]
G -->|Ja| I[Klar til brugere]
H --> A
I --> J[Publicer i butik]
style A fill:#e1f5fe
style F fill:#e8f5e8
style I fill:#f3e5f5
style J fill:#fff3e0
```
```bash
# Step 1: Build your extension
# Trin 1: Byg din udvidelse
npm run build
```
**Hvad denne kommando opnår:**
- **Kompilerer** din kildekode til browserklare filer
**Dette kommando opnår:**
- **Kompilerer** din kildekode til filer, der er klar til browseren
- **Pakker** JavaScript-moduler i optimerede pakker
- **Genererer** de endelige udvidelsesfiler i `/dist`-mappen
- **Genererer** de endelige udvidelsesfiler i `/dist` mappen
- **Forbereder** din udvidelse til installation og test
**Trin 2: Naviger til browserudvidelser**
1. **Åbn** din browsers udvidelsesadministrationsside
2. **Klik** på "Indstillinger og mere"-knappen (ikonet `...`) øverst til højre
3. **Vælg** "Udvidelser" fra rullemenuen
**Trin 2: Naviger til Browser Udvidelser**
1. **Åbn** din browsers side til håndtering af udvidelser
2. **Klik**knappen "Indstillinger og mere" (ikonet med `...`) øverst til højre
3. **Vælg** "Udvidelser" fra dropdown-menuen
**Trin 3: Indlæs din udvidelse**
- **For nye installationer**: Vælg `load unpacked` og vælg din `/dist`-mappe
- **For opdateringer**: Klik på `reload` ved siden af din allerede installerede udvidelse
- **For test**: Aktiver "Udviklertilstand" for at få adgang til yderligere fejlfindingsfunktioner
**Trin 3: Indlæs din Udvidelse**
- **For nye installationer**: Vælg `load unpacked` og vælg din `/dist` mappe
- **For opdateringer**: Klik `reload` ved siden af din allerede installerede udvidelse
- **For test**: Aktiver "Udviklertilstand" for at få adgang til yderligere debugging-funktioner
### Produktion Udvidelsesinstallation
### Produktionsinstallation af Udvidelser
> ✅ **Bemærk**: Disse udviklingsinstruktioner er specifikt for udvidelser, du selv bygger. For at installere offentliggjorte udvidelser, besøg de officielle browserudvidelsesbutikker som [Microsoft Edge Add-ons store](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home).
> ✅ **Bemærk**: Disse udviklingsinstruktioner gælder specifikt for udvidelser, du selv bygger. For at installere offentliggjorte udvidelser, besøg de officielle browser-udvidelsesbutikker som [Microsoft Edge Add-ons store](https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home).
**Forstå forskellen:**
- **Udviklingsinstallationer** giver dig mulighed for at teste upublicerede udvidelser under udvikling
- **Butiksinstallationer** tilbyder godkendte, offentliggjorte udvidelser med automatiske opdateringer
- **Sideloading** tillader installation af udvidelser fra udenfor officielle butikker (kræver udviklertilstand)
- **Udviklingsinstallationer** giver mulighed for at teste upublicerede udvidelser under udvikling
- **Butiksinstallationer** leverer godkendte, offentliggjorte udvidelser med automatiske opdateringer
- **Sideindlæsning** tillader installation af udvidelser uden for officielle butikker (kræver udviklertilstand)
## Bygning af din CO2-fodaftryk-udvidelse
## Byg din Carbon Footprint Udvidelse
Vi vil skabe en browserudvidelse, der viser CO2-fodaftrykket af din regions energiforbrug. Dette projekt demonstrerer essentielle udvidelsesudviklingskoncepter, mens det skaber et praktisk værktøj til miljøbevidsthed.
Vi vil skabe en browserudvidelse, der viser kulstofaftrykket for dit regions energiforbrug. Dette projekt demonstrerer essentielle konceptuelle principper i udvidelsesudvikling, samtidig med at det skaber et praktisk værktøj til miljøbevidsthed.
Denne tilgang følger princippet om "learning by doing", som har vist sig effektivt siden John Deweys uddannelsesteorier - kombinerer tekniske færdigheder med meningsfulde virkelige applikationer.
Denne tilgang følger princippet om "læring ved at gøre", der har vist sig effektivt siden John Deweys pædagogiske teorier en kombination af tekniske færdigheder med meningsfulde, virkelige anvendelser.
### Projektkrav
Før du begynder udviklingen, lad os samle de nødvendige ressourcer og afhængigheder:
Før begyndelsen af udviklingen, lad os samle de nødvendige ressourcer og afhængigheder:
**Påkrævet API-adgang:**
- **[CO2 Signal API-nøgle](https://www.co2signal.com/)**: Indtast din e-mailadresse for at modtage din gratis API-nøgle
- **[Regionskode](http://api.electricitymap.org/v3/zones)**: Find din regionskode ved hjælp af [Electricity Map](https://www.electricitymap.org/map) (for eksempel bruger Boston 'US-NEISO')
- **[CO2 Signal API nøgle](https://www.co2signal.com/)**: Indtast din e-mailadresse for at modtage din gratis API-nøgle
- **[Regionskode](http://api.electricitymap.org/v3/zones)**: Find din regionskode med [Electricity Map](https://www.electricitymap.org/map) (for eksempel bruger Boston 'US-NEISO')
**Udviklingsværktøjer:**
- **[Node.js og NPM](https://www.npmjs.com)**: Pakkehåndteringsværktøj til installation af projektets afhængigheder
- **[Startkode](../../../../5-browser-extension/start)**: Download `start`-mappen for at begynde udviklingen
- **[Node.js og NPM](https://www.npmjs.com)**: Værktøj til pakkeadministration til installation af projektets afhængigheder
- **[Startkode](../../../../5-browser-extension/start)**: Download `start` mappen for at begynde udviklingen
**Lær Mere**: Forbedr dine færdigheder inden for pakkehåndtering med dette [omfattende Learn-modul](https://docs.microsoft.com/learn/modules/create-nodejs-project-dependencies/?WT.mc_id=academic-77807-sagibbon)
**Lær mere**: Forbedr dine færdigheder i pakkehåndtering med dette [omfattende Learn modul](https://docs.microsoft.com/learn/modules/create-nodejs-project-dependencies/?WT.mc_id=academic-77807-sagibbon)
### Forståelse af projektstrukturen
### Forståelse af Projektstrukturen
At forstå projektstrukturen hjælper med at organisere udviklingsarbejdet effektivt. Ligesom hvordan Biblioteket i Alexandria blev organiseret for nem videnhentning, gør en velstruktureret kodebase udviklingen mere effektiv:
At forstå projektstrukturen hjælper med at organisere udviklingsarbejdet effektivt. Ligesom Alexandria Biblioteket var organiseret for nem adgang til viden, gør en veldesignet kodebase udviklingen mere effektiv:
```
project-root/
@ -180,40 +282,42 @@ project-root/
│ ├── index.html # User interface markup
│ ├── background.js # Background script functionality
│ └── main.js # Compiled JavaScript bundle
└── src/ # Source development files
└── index.js # Your main JavaScript code
├── src/ # Source development files
│ └── index.js # Your main JavaScript code
├── package.json # Project dependencies and scripts
└── webpack.config.js # Build configuration
```
**Hvad hver fil opnår:**
- **`manifest.json`**: **Definerer** udvidelses metadata, tilladelser og indgangspunkter
- **`index.html`**: **Skaber** brugergrænsefladen, der vises, når brugere klikker på din udvidelse
- **`background.js`**: **Håndterer** baggrundsopgaver og browserhændelseslyttere
- **`main.js`**: **Indeholder** den endelige bundtede JavaScript efter byggeprocessen
- **`src/index.js`**: **Indeholder** din hovedudviklingskode, der bliver kompileret til `main.js`
**Hvad hver fil står for:**
- **`manifest.json`**: **Definerer** udvidelsens metadata, tilladelser og indgangspunkt
- **`index.html`**: **Skaber** brugergrænsefladen, der vises, når brugerne klikker på udvidelsen
- **`background.js`**: **Håndterer** baggrundsopgaver og browserens event-lyttere
- **`main.js`**: **Indeholder** den endelige bundlede JavaScript efter build-processen
- **`src/index.js`**: **Huser** din hovedudviklingskode, som kompileres til `main.js`
> 💡 **Organisations Tip**: Gem din API-nøgle og regionskode i en sikker note for nem reference under udviklingen. Du vil få brug for disse værdier for at teste din udvidelses funktionalitet.
> 💡 **Organiseringstip**: Gem din API-nøgle og regionskode i en sikker note for nem reference under udvikling. Du får brug for disse værdier til at teste din udvidelses funktionalitet.
**Sikkerhedsnotat**: Undlad at gemme API-nøgler eller følsomme legitimationsoplysninger i dit koderepository. Vi viser dig, hvordan du håndterer disse sikkert i de næste trin.
**Sikkerhedsnote**: Indsend aldrig API-nøgler eller følsomme legitimationsoplysninger i dit kodearkiv. Vi vil vise dig, hvordan du håndterer disse sikkert i næste trin.
## Skabelse af udvidelsesgrænsefladen
## Oprettelse af Udvidelsesinterfacet
Nu bygger vi komponenterne til brugergrænsefladen. Udvidelsen bruger en to-skærms tilgang: en konfigurationsskærm til den første opsætning og en resultatskærm til datavisning.
Nu bygger vi brugergrænsefladekomponenterne. Udvidelsen bruger en to-skærms tilgang: en konfigurationsskærm til den indledende opsætning og en resultatskærm til visning af data.
Dette følger princippet om progressiv afsløring, der bruges i grænsefladedesign siden de tidlige dage af computing - at afsløre information og muligheder i en logisk rækkefølge for at undgå at overvælde brugerne.
Dette følger princippet om gradvis afsløring, brugt i interface design siden datidens tidlige dage at afsløre information og muligheder i en logisk rækkefølge for at undgå at overvælde brugerne.
### Oversigt over udvidelsesvisninger
### Oversigt over Udvidelsesvisninger
**Opsætningsvisning** - Førstegangs brugeropsætning:
![skærmbillede af den færdige udvidelse åbnet i en browser, der viser en formular med inputfelter for regionsnavn og API-nøgle.](../../../../translated_images/1.b6da8c1394b07491afeb6b2a8e5aca73ebd3cf478e27bcc9aeabb187e722648e.da.png)
**Opsætningsvisning** første gangs bruger-konfiguration:
![screenshot of the completed extension open in a browser, displaying a form with inputs for region name and API key.](../../../../translated_images/1.b6da8c1394b07491.da.png)
**Resultatvisning** - Visning af CO2-fodaftryksdata:
![skærmbillede af den færdige udvidelse, der viser værdier for CO2-forbrug og procentdel af fossile brændstoffer for regionen US-NEISO.](../../../../translated_images/2.1dae52ff0804224692cd648afbf2342955d7afe3b0101b617268130dfb427f55.da.png)
**Resultatvisning** visning af kulstofaftrykdata:
![screenshot of the completed extension displaying values for carbon usage and fossil fuel percentage for the US-NEISO region.](../../../../translated_images/2.1dae52ff08042246.da.png)
### Bygning af konfigurationsformularen
### Bygning af Konfigurationsformularen
Opsætningsformularen indsamler brugerens konfigurationsdata under første brug. Når den er konfigureret, gemmes disse oplysninger i browserens lager til fremtidige sessioner.
Opsætningsformularen indsamler brugerens konfigurationsdata under første brug. Når konfigureret, gemmes oplysningerne i browserens lager til fremtidige sessioner.
I filen `/dist/index.html`, tilføj denne formularstruktur:
I filen `/dist/index.html`, tilføj denne struktur for formularen:
```html
<form class="form-data" autocomplete="on">
@ -232,16 +336,16 @@ I filen `/dist/index.html`, tilføj denne formularstruktur:
</form>
```
**Hvad denne formular opnår:**
- **Skaber** en semantisk formularstruktur med korrekte etiketter og inputforbindelser
- **Muliggør** browserens autofuldførelsesfunktionalitet for forbedret brugeroplevelse
- **Kræver** begge felter udfyldt før indsendelse ved hjælp af attributten `required`
- **Organiserer** input med beskrivende klassenavne for nem styling og JavaScript-målretning
- **Giver** klare instruktioner til brugere, der opsætter udvidelsen for første gang
**Dette opnår formularen:**
- **Skaber** en semantisk formularstruktur med korrekte labels og input-associationer
- **Muliggør** browserens autocomplete-funktion for bedre brugervenlighed
- **Kræver**, at begge felter udfyldes før indsendelse ved brug af `required` attributten
- **Organiserer** inputs med beskrivende klassenavne for nem styling og JavaScript-målretning
- **Giver** klare instruktioner til brugere, der opsætter udvidelsen første gang
### Bygning af resultatvisningen
### Bygning af Resultatvisningen
Dernæst opretter vi resultatområdet, der viser CO2-fodaftryksdataene. Tilføj denne HTML under formularen:
Dernæst opretter du resultatområdet, som viser kulstofaftryksdataene. Tilføj denne HTML under formularen:
```html
<div class="result">
@ -257,78 +361,195 @@ Dernæst opretter vi resultatområdet, der viser CO2-fodaftryksdataene. Tilføj
</div>
```
**Hvad denne struktur tilbyder:**
- **`loading`**: **Viser** en indlæsningsmeddelelse, mens API-data hentes
- **`errors`**: **Viser** fejlmeddelelser, hvis API-opkald mislykkes eller data er ugyldige
- **`data`**: **Indeholder** rå data til fejlfinding under udvikling
- **`result-container`**: **Præsenterer** formateret CO2-fodaftryksinformation til brugere
- **`clear-btn`**: **Giver** brugere mulighed for at ændre deres region og rekonfigurere udvidelsen
**Hvad denne struktur leverer:**
- **`loading`**: **Viser** en indlæsningsbesked, mens API-data hentes
- **`errors`**: **Viser** fejlbeskeder, hvis API-kald fejler eller data er ugyldige
- **`data`**: **Holder** rå data til debugging under udviklingen
- **`result-container`**: **Præsenterer** formateret kulstofaftryksinformation for brugerne
- **`clear-btn`**: **Tillader** brugere at ændre deres region og konfigurere udvidelsen igen
### Opsætning af byggeprocessen
### Opsætning af Build-processen
Lad os nu installere projektets afhængigheder og teste byggeprocessen:
Nu installerer vi projektets afhængigheder og tester build-processen:
```bash
npm install
```
**Hvad denne installationsproces opnår:**
- **Downloader** Webpack og andre udviklingsafhængigheder specificeret i `package.json`
- **Konfigurerer** byggeværktøjskæden til at kompilere moderne JavaScript
- **Forbereder** udviklingsmiljøet til udvidelsesbygning og test
- **Muliggør** kodebundtning, optimering og funktioner til kompatibilitet på tværs af browsere
- **Downloader** Webpack og andre udviklingsafhængigheder som specificeret i `package.json`
- **Konfigurerer** byggeværktøjskæden til kompilering af moderne JavaScript
- **Forbereder** udviklingsmiljøet til at bygge og teste udvidelsen
- **Muliggør** kodesamling, optimering og tværbrowserkompatibilitet
> 💡 **Indsigt i byggeprocessen**: Webpack bundter din kildekode fra `/src/index.js` til `/dist/main.js`. Denne proces optimerer din kode til produktion og sikrer browserkompatibilitet.
> 💡 **Build Process Indsigt**: Webpack samler din kildekode fra `/src/index.js` til `/dist/main.js`. Denne proces optimerer din kode til produktion og sikrer browserkompatibilitet.
### Test af din fremgang
### Test af din Fremgang
På dette tidspunkt kan du teste din udvidelse:
1. **Kør** build-kommandoen for at kompilere din kode
2. **Indlæs** udvidelsen i din browser ved hjælp af udviklertilstand
3. **Bekræft**, at formularen vises korrekt og ser professionel ud
4. **Tjek**, at alle formelementer er korrekt justeret og funktionelle
**Det du har opnået:**
- **Bygget** den grundlæggende HTML-struktur til din udvidelse
- **Oprettet** både konfigurations- og resultatgrænseflader med korrekt semantisk markup
- **Opsat** en moderne udviklingsworkflow med brancheførende værktøjer
- **Forberedt** fundamentet for at tilføje interaktiv JavaScript-funktionalitet
### 🔄 **Pædagogisk status**
**Udvidelsesudviklingsfremskridt**: Bekræft din forståelse før du fortsætter:
- ✅ Kan du forklare formålet med hver fil i projektstrukturen?
- ✅ Forstår du, hvordan build-processen transformerer din kildekode?
- ✅ Hvorfor adskiller vi konfiguration og resultater i forskellige UI-sektioner?
- ✅ Hvordan understøtter formularstrukturen både brugervenlighed og tilgængelighed?
**Forståelse af udviklingsworkflow**: Du bør nu kunne:
1. **Ændre** HTML og CSS for din udvidelsesgrænseflade
2. **Køre** build-kommandoen for at kompilere dine ændringer
3. **Genindlæse** udvidelsen i din browser for at teste opdateringer
4. **Fejlsøge** problemer ved hjælp af browserens udviklerværktøjer
Du har gennemført den første fase af browserudvidelsesudvikling. Ligesom brødrene Wright først skulle forstå aerodynamik før de opnåede flyvning, forbereder forståelsen af disse grundlæggende koncepter dig på at bygge mere komplekse interaktive funktioner i næste lektion.
1. **Kør** byggekommandoen for at kompilere din kode
2. **Indlæs** udvidelsen i din browser ved hjælp af udviklertilstand
3. **Bekræft** at formularen vises korrekt og ser professionel ud
4. **Kontroller** at alle formelementer er korrekt justeret og funktionelle
## GitHub Copilot Agent Challenge 🚀
**Hvad du har opnået:**
- **Bygget** den grundlæggende HTML-struktur til din udvidelse
- **Skabt** både konfigurations- og resultatgrænseflader med korrekt semantisk markup
- **Opsat** en moderne udviklingsarbejdsgang ved hjælp af industristandardværktøjer
- **Forberedt** fundamentet til at tilføje interaktiv JavaScript-funktionalitet
Brug Agent-tilstand til at fuldføre følgende udfordring:
Du har fuldført den første fase af browserudvidelsesudvikling. Ligesom hvordan Wright-brødrene først måtte forstå aerodynamik, før de opnåede flyvning, forbereder forståelsen af disse grundlæggende koncepter dig til at bygge mere komplekse interaktive funktioner i den næste lektion.
**Beskrivelse:** Forbedr browserudvidelsen ved at tilføje validering af formular og brugerfeedbackfunktioner for at forbedre brugeroplevelsen ved indtastning af API-nøgler og regionskoder.
## GitHub Copilot Agent Challenge 🚀
**Prompt:** Opret JavaScript-valideringsfunktioner, der kontrollerer, om API-nøgelfeltet indeholder mindst 20 tegn, og om regionskoden følger korrekt format (som 'US-NEISO'). Tilføj visuel feedback ved at ændre inputkantfarver til grøn for gyldige input og rød for ugyldige. Tilføj også en skiftemulighed til at vise/skjule API-nøglen for sikkerhedsformål.
Brug Agent-tilstand til at fuldføre følgende udfordring:
**Beskrivelse:** Forbedr browserudvidelsen ved at tilføje formularvalidering og brugerfeedbackfunktioner for at forbedre brugeroplevelsen, når der indtastes API-nøgler og regionskoder.
Lær mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
**Opgave:** Opret JavaScript-valideringsfunktioner, der kontrollerer, om API-nøglefeltet indeholder mindst 20 tegn, og om regionskoden følger det korrekte format (som 'US-NEISO'). Tilføj visuel feedback ved at ændre inputgrænsefarver til grøn for gyldige input og rød for ugyldige. Tilføj også en skiftefunktion til at vise/skjule API-nøglen af sikkerhedsmæssige årsager.
## 🚀 Udfordring
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
Tag et kig på en browserudvidelsesbutik og installer en udvidelse i din browser. Du kan undersøge dens filer på interessante måder. Hvad opdager du?
## 🚀 Udfordring
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/24)
Tag et kig på en browserudvidelsesbutik og installer en udvidelse i din browser. Du kan undersøge dens filer på interessante måder. Hvad opdager du?
## Gennemgang & Selvlæring
## Quiz efter lektionen
I denne lektion lærte du lidt om historien bag webbrowsere; benyt lejligheden til at lære, hvordan opfinderne af World Wide Web forestillede sig dets brug ved at læse mere om dets historie. Nogle nyttige sider inkluderer:
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/24)
[Historien om webbrowsere](https://www.mozilla.org/firefox/browsers/browser-history/)
## Gennemgang & Selvstudie
[Historien om Webben](https://webfoundation.org/about/vision/history-of-the-web/)
I denne lektion lærte du lidt om webbrowserens historie; benyt lejligheden til at lære om, hvordan opfinderne af World Wide Web forestillede sig dens anvendelse ved at læse mere om dens historie. Nogle nyttige sider inkluderer:
[Et interview med Tim Berners-Lee](https://www.theguardian.com/technology/2019/mar/12/tim-berners-lee-on-30-years-of-the-web-if-we-dream-a-little-we-can-get-the-web-we-want)
[Historien om webbrowsere](https://www.mozilla.org/firefox/browsers/browser-history/)
### ⚡ **Hvad du kan nå på de næste 5 minutter**
- [ ] Åbn Chrome/Edge-udvidelsessiden (chrome://extensions) og udforsk, hvad du har installeret
- [ ] Se på din browsers DevTools-netværksfane mens en webside indlæses
- [ ] Prøv at se sidekilde (Ctrl+U) for at se HTML-strukturen
- [ ] Inspicer et hvilket som helst websides element og ændr dets CSS i DevTools
[Historien om nettet](https://webfoundation.org/about/vision/history-of-the-web/)
### 🎯 **Hvad du kan opnå i denne time**
- [ ] Fuldfør quizzen efter lektionen og forstå browsergrundlag
- [ ] Opret en grundlæggende manifest.json-fil til en browserudvidelse
- [ ] Byg en simpel "Hello World" udvidelse med popup
- [ ] Test at indlæse din udvidelse i udviklertilstand
- [ ] Undersøg browserudvidelsesdokumentationen for din målbrowser
[Et interview med Tim Berners-Lee](https://www.theguardian.com/technology/2019/mar/12/tim-berners-lee-on-30-years-of-the-web-if-we-dream-a-little-we-can-get-the-web-we-want)
### 📅 **Din ugelange udvidelsesrejse**
- [ ] Fuldfør en funktionel browserudvidelse med reel nytte
- [ ] Lær om content scripts, baggrundsscripts og popup-interaktioner
- [ ] Mestre browser-APIer som storage, tabs og messaging
- [ ] Design brugervenlige grænseflader til din udvidelse
- [ ] Test din udvidelse på forskellige websites og scenarier
- [ ] Udgiv din udvidelse i browserens udvidelsesbutik
## Opgave
### 🌟 **Din månedlange browserudvikling**
- [ ] Byg flere udvidelser, der løser forskellige brugerproblemer
- [ ] Lær avancerede browser-APIer og sikkerhedspraksis
- [ ] Bidrag til open source-browserudvidelsesprojekter
- [ ] Mestre tværbrowser-kompatibilitet og progressiv forbedring
- [ ] Skab udviklingsværktøjer og skabeloner til udvidelser for andre
- [ ] Bliv en browserudvidelsekspert, der hjælper andre udviklere
[Restyle din udvidelse](assignment.md)
## 🎯 Din browserudvidelses-mestertidlinje
```mermaid
timeline
title Udviklingsfremskridt for Browserudvidelse
section Grundlag (15 minutter)
Browserforståelse: Kernearkitektur
: Renderingproces
: Udvidelsesintegrationspunkter
section Opsætning (20 minutter)
Udviklingsmiljø: Projektstruktur
: Konfiguration af byggeværktøjer
: Browser udviklertilstand
: Indlæsningsproces for udvidelser
section Grænsefladedesign (25 minutter)
Brugeroplevelse: HTML-struktur
: CSS-styling
: Formularvalidering
: Responsivt design
section Kernefunktionalitet (35 minutter)
JavaScript-integration: Hændelseshåndtering
: API-interaktioner
: Dataopbevaring
: Fejlhåndtering
section Browser-API'er (45 minutter)
Platformintegration: Tilladelsessystem
: Lager-API'er
: Faneadministration
: Kontextmenuer
section Avancerede funktioner (1 uge)
Professionelle udvidelser: Baggrundsscripts
: Indholdsscripts
: Tværbrowserkompatibilitet
: Ydelsesoptimering
section Udgivelse (2 uger)
Distribution: Butiksindsendelse
: Gennemgangsproces
: Brugerfeedback
: Opdateringsstyring
section Ekspertniveau (1 måned)
Udvidelsesøkosystem: Avancerede API'er
: Sikkerhedspraksis
: Enterprise-funktioner
: Framework-integration
```
### 🛠️ Oversigt over dit udvidelsesudviklingsværktøj
Efter at have gennemført denne lektion har du nu:
- **Viden om browserarkitektur**: Forståelse for gengivelsesmotorer, sikkerhedsmodeller og integration af udvidelser
- **Udviklingsmiljø**: Moderne værktøjskæde med Webpack, NPM og fejlfinding
- **UI/UX-fundament**: Semantisk HTML-struktur med progressiv afsløringsmønstre
- **Sikkerhedsbevidsthed**: Forståelse for browser-tilladelser og sikker udvikling
- **Tværbrowser-koncept**: Viden om kompatibilitetsbetragtninger og testmetoder
- **API-integration**: Fundament for at arbejde med eksterne datakilder
- **Professionelt workflow**: Brancheførende udviklings- og testprocedurer
**Anvendelser i praksis**: Disse færdigheder gælder direkte for:
- **Webudvikling**: Single-page apps og progressive web apps
- **Desktop-applikationer**: Electron og webbaserede desktopsprogrammer
- **Mobiludvikling**: Hybride apps og webbaserede mobil-løsninger
- **Enterprise-værktøj**: Interne produktivitetsapplikationer og automatisering af workflows
- **Open Source**: Bidrag til browserudvidelsesprojekter og webstandarder
**Næste niveau**: Du er klar til at tilføje interaktiv funktionalitet, arbejde med browser-APIer og skabe udvidelser, der løser reelle brugerproblemer!
## Opgave
[Stil din udvidelse om](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets sprog bør betragtes som den autoritative kilde. Ved kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,43 +1,114 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8c8cd4af6086cc1d47e1d43aa4983d20",
"translation_date": "2025-10-23T22:13:42+00:00",
"original_hash": "2b6203a48c48d8234e0948353b47d84e",
"translation_date": "2026-01-06T23:44:17+00:00",
"source_file": "5-browser-extension/2-forms-browsers-local-storage/README.md",
"language_code": "da"
}
-->
# Browserudvidelsesprojekt del 2: Kald en API, brug lokal lagring
## Quiz før forelæsning
# Browserudvidelsesprojekt Del 2: Kald et API, brug Lokal Lager
```mermaid
journey
title Din API-integration og lagringsrejse
section Grundlag
Opsæt DOM-referencer: 3: Student
Tilføj hændelseslyttere: 4: Student
Håndter formularindsendelse: 4: Student
section Datahåndtering
Implementer lokal lagring: 4: Student
Byg API-kald: 5: Student
Håndter asynkrone operationer: 5: Student
section Brugeroplevelse
Tilføj fejlhåndtering: 5: Student
Opret indlæsningsstatusser: 4: Student
Forfin interaktioner: 5: Student
```
## For-forelæsning Quiz
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/25)
[For-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/25)
## Introduktion
Kan du huske den browserudvidelse, du begyndte at bygge? Lige nu har du en flot formular, men den er i bund og grund statisk. I dag vil vi give den liv ved at forbinde den til rigtige data og give den hukommelse.
Tænk på Apollo-missionens kontrolcomputere - de viste ikke bare fast information. De kommunikerede konstant med rumfartøjet, opdaterede med telemetridata og huskede kritiske missionparametre. Det er den slags dynamisk adfærd, vi bygger i dag. Din udvidelse vil hente data fra internettet, indsamle reelle miljødata og huske dine indstillinger til næste gang.
API-integration kan lyde komplekst, men det handler i bund og grund om at lære din kode at kommunikere med andre tjenester. Uanset om du henter vejroplysninger, sociale mediefeeds eller information om CO2-aftryk, som vi vil gøre i dag, handler det om at etablere disse digitale forbindelser. Vi vil også udforske, hvordan browsere kan gemme information - ligesom biblioteker har brugt kortkataloger til at huske, hvor bøger hører hjemme.
Ved slutningen af denne lektion vil du have en browserudvidelse, der henter rigtige data, gemmer brugerpræferencer og giver en glidende oplevelse. Lad os komme i gang!
✅ Følg de nummererede segmenter i de relevante filer for at vide, hvor du skal placere din kode.
Kan du huske den browserudvidelse, du begyndte at bygge? Lige nu har du en pæn form, men den er i bund og grund statisk. I dag vil vi bringe den til live ved at forbinde den til rigtige data og give den hukommelse.
Tænk på Apollo-missionens kontrolcomputere - de viste ikke bare faste oplysninger. De kommunikerede konstant med rumfartøjer, opdaterede med telemetridata og huskede kritiske missionparametre. Det er den slags dynamisk opførsel, vi bygger i dag. Din udvidelse vil række ud på internettet, hente rigtige miljødata og huske dine indstillinger til næste gang.
API-integration kan lyde komplekst, men det handler egentlig bare om at lære din kode at kommunikere med andre tjenester. Uanset om du henter vejrudsigter, sociale mediefeeds eller CO2-aftryksinformation som vi vil gøre i dag, drejer det sig om at etablere disse digitale forbindelser. Vi vil også udforske, hvordan browsere kan gemme information - ligesom biblioteker tidligere har brugt kortkataloger til at huske, hvor bøgerne hører til.
Ved slutningen af denne lektion vil du have en browserudvidelse, der henter rigtige data, gemmer brugerpræferencer og leverer en glat brugeroplevelse. Lad os komme i gang!
```mermaid
mindmap
root((Dynamiske Udvidelser))
DOM Manipulation
Elementvalg
Hændelseshåndtering
Tilstandsadministration
UI Opdateringer
Lokal Lager
Dataperistent
Nøgle-Værdi Par
Sessionstyring
Brugerpræferencer
API Integration
HTTP Anmodninger
Godkendelse
Datatolkning
Fejlhåndtering
Async Programmering
Promiser
Async/Await
Fejlfangst
Ikke-blokerende Kode
Brugeroplevelse
Indlæsningsstatusser
Fejlmeddelelser
Glatte Overgange
Datavalidering
```
✅ Følg de nummererede segmenter i de relevante filer for at vide, hvor du skal placere din kode
## Opsæt elementerne til manipulation i udvidelsen
Før din JavaScript kan manipulere grænsefladen, skal den have referencer til specifikke HTML-elementer. Tænk på det som et teleskop, der skal pege på bestemte stjerner - før Galileo kunne studere Jupiters måner, måtte han finde og fokusere på Jupiter selv.
I din `index.js`-fil opretter vi `const`-variabler, der fanger referencer til hvert vigtigt form-element. Dette er ligesom hvordan videnskabsfolk mærker deres udstyr - i stedet for at søge gennem hele laboratoriet hver gang, kan de direkte få adgang til det, de har brug for.
Før din JavaScript kan manipulere interfacet, har den brug for referencer til specifikke HTML-elementer. Tænk på det som et teleskop, der skal rettes mod bestemte stjerner før Galileo kunne studere Jupiters måner, måtte han finde og fokusere på Jupiter selv.
I din `index.js` fil vil vi oprette `const` variabler, der fanger referencer til hvert vigtigt formelement. Det svarer til, hvordan videnskabsfolk mærker deres udstyr i stedet for at lede gennem hele laboratoriet hver gang, kan de direkte tilgå, hvad de behøver.
```mermaid
flowchart LR
A[JavaScript-kode] --> B[document.querySelector]
B --> C[CSS-vælgere]
C --> D[HTML-elementer]
D --> E[".form-data"]
D --> F[".region-name"]
D --> G[".api-key"]
D --> H[".loading"]
D --> I[".errors"]
D --> J[".result-container"]
E --> K[Formular-element]
F --> L[Inputfelt]
G --> M[Inputfelt]
H --> N[UI-element]
I --> O[UI-element]
J --> P[UI-element]
style A fill:#e1f5fe
style D fill:#e8f5e8
style K fill:#fff3e0
style L fill:#fff3e0
style M fill:#fff3e0
```
```javascript
// form fields
// formularfelter
const form = document.querySelector('.form-data');
const region = document.querySelector('.region-name');
const apiKey = document.querySelector('.api-key');
// results
// resultater
const errors = document.querySelector('.errors');
const loading = document.querySelector('.loading');
const results = document.querySelector('.result-container');
@ -47,55 +118,87 @@ const myregion = document.querySelector('.my-region');
const clearBtn = document.querySelector('.clear-btn');
```
**Hvad denne kode gør:**
- **Fanger** form-elementer ved hjælp af `document.querySelector()` med CSS-klassevælgere
- **Opretter** referencer til inputfelter for regionnavn og API-nøgle
- **Etablerer** forbindelser til resultatvisningselementer for data om CO2-forbrug
- **Opsætter** adgang til UI-elementer som indlæsningsindikatorer og fejlmeddelelser
- **Gemmer** hver elementreference i en `const`-variabel for nem genbrug i din kode
**Dette gør koden:**
- **Fanger** formelementer ved hjælp af `document.querySelector()` med CSS-klassevælgere
- **Opretter** referencer til inputfelterne for regionsnavn og API-nøgle
- **Etablerer** forbindelse til resultatelementer for data om CO2-forbrug
- **Opsætter** adgang til UI-elementer som indlæsningsindikatorer og fejllmeddelelser
- **Gemmer** hver elementreference i en `const` variabel for nem genbrug gennem din kode
## Tilføj event listeners
Nu får vi din udvidelse til at reagere på brugerhandlinger. Event listeners er din kodes måde at overvåge brugerinteraktioner på. Tænk på dem som operatører i de tidlige telefoncentraler - de lyttede efter indgående opkald og forbandt de rigtige kredsløb, når nogen ville ringe.
Nu vil vi gøre din udvidelse reagerende på brugerhandlinger. Event listeners er din kodes måde at overvåge brugerinteraktioner på. Tænk på dem som telefoncentraloperatører i de tidlige telefonvekslinger de lyttede efter indkommende opkald og forbindede de rette kredsløb, når nogen ønskede at etablere forbindelse.
```mermaid
sequenceDiagram
participant User
participant Form
participant JavaScript
participant API
participant Storage
User->>Form: Udfylder region/API-nøgle
User->>Form: Klikker på send
Form->>JavaScript: Udløser submit-begivenhed
JavaScript->>JavaScript: handleSubmit(e)
JavaScript->>Storage: Gemmer brugerpræferencer
JavaScript->>API: Henter CO2-data
API->>JavaScript: Returnerer data
JavaScript->>Form: Opdaterer UI med resultater
User->>Form: Klikker på fjern-knap
Form->>JavaScript: Udløser klik-begivenhed
JavaScript->>Storage: Sletter gemte data
JavaScript->>Form: Nulstiller til starttilstand
```
```javascript
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
```
**Forståelse af disse begreber:**
- **Tilføjer** en submit listener til formularen, der aktiveres, når brugere trykker på Enter eller klikker på send
- **Forbinder** en kliklistener til nulstillingsknappen for at nulstille formularen
- **Sender** hændelsesobjektet `(e)` til håndteringsfunktioner for yderligere kontrol
- **Kalder** `init()`-funktionen med det samme for at opsætte udvidelsens starttilstand
**Forståelse af disse koncepter:**
- **Tilføjer** en submit listener til formen, der udløses, når brugere trykker Enter eller klikker for at sende
- **Forbinder** en click listener til ryd-knappen for at nulstille formen
- **Sender** event-objektet `(e)` til håndteringsfunktioner for yderligere kontrol
- **Kalder** `init()` funktionen med det samme for at sætte den indledende tilstand for din udvidelse
✅ Bemærk den korte pilfunktion-syntaks, der bruges her. Denne moderne JavaScript-tilgang er mere elegant end traditionelle funktionsudtryk, men begge fungerer lige godt!
✅ Bemærk den korte pilefunktionssyntaks, der bruges her. Denne moderne JavaScript-tilgang er renere end traditionelle funktionsudtryk, men begge fungerer lige godt!
## Byg initialiserings- og nulstillingsfunktionerne
### 🔄 **Pædagogisk Check-in**
**Forståelse af Event Handling**: Før vi går videre til initialisering, skal du kunne:
- ✅ Forklare hvordan `addEventListener` forbinder brugerhandlinger til JavaScript-funktioner
- ✅ Forstå hvorfor vi sender event-objektet `(e)` til håndteringsfunktionerne
- ✅ Genkende forskellen mellem `submit` og `click` events
- ✅ Beskrive hvornår `init()` funktionen kører og hvorfor
Lad os oprette initialiseringslogikken for din udvidelse. `init()`-funktionen er som et skibs navigationssystem, der kontrollerer sine instrumenter - den bestemmer den aktuelle tilstand og justerer grænsefladen derefter. Den kontrollerer, om nogen har brugt din udvidelse før og indlæser deres tidligere indstillinger.
**Hurtig Selvtest**: Hvad ville der ske, hvis du glemte `e.preventDefault()` i en formindsendelse?
*Svar: Siden ville genindlæses og miste al JavaScript-tilstand, hvilket afbryder brugeroplevelsen*
`reset()`-funktionen giver brugerne en frisk start - ligesom hvordan videnskabsfolk nulstiller deres instrumenter mellem eksperimenter for at sikre rene data.
## Byg initialiserings- og nulstillingsfunktioner
Lad os oprette initialiseringslogikken til din udvidelse. `init()` funktionen er som et skibs navigationssystem, der tjekker sine instrumenter den vurderer den aktuelle tilstand og justerer interfacet tilsvarende. Den tjekker, om nogen har brugt din udvidelse før, og indlæser deres tidligere indstillinger.
`reset()` funktionen giver brugeren en frisk start ligesom videnskabsfolk nulstiller deres instrumenter mellem eksperimenter for at sikre rene data.
```javascript
function init() {
// Check if user has previously saved API credentials
// Tjek om brugeren tidligere har gemt API-legitimationsoplysninger
const storedApiKey = localStorage.getItem('apiKey');
const storedRegion = localStorage.getItem('regionName');
// Set extension icon to generic green (placeholder for future lesson)
// TODO: Implement icon update in next lesson
// Sæt udvidelsesikonet til generisk grøn (pladsholder til fremtidig lektion)
// TODO: Implementer ikonopdatering i næste lektion
if (storedApiKey === null || storedRegion === null) {
// First-time user: show the setup form
// Førstegangsbruger: vis opsætningsformularen
form.style.display = 'block';
results.style.display = 'none';
loading.style.display = 'none';
clearBtn.style.display = 'none';
errors.textContent = '';
} else {
// Returning user: load their saved data automatically
// Tilbagevendende bruger: indlæs deres gemte data automatisk
displayCarbonUsage(storedApiKey, storedRegion);
results.style.display = 'none';
form.style.display = 'none';
@ -105,49 +208,72 @@ function init() {
function reset(e) {
e.preventDefault();
// Clear stored region to allow user to choose a new location
// Ryd gemt region for at tillade brugeren at vælge en ny placering
localStorage.removeItem('regionName');
// Restart the initialization process
// Genstart initialiseringsprocessen
init();
}
```
**Hvad der sker her:**
- **Henter** gemt API-nøgle og region fra browserens lokale lagring
- **Kontrollerer**, om det er en førstegangsbruger (ingen gemte legitimationsoplysninger) eller en tilbagevendende bruger
- **Viser** opsætningsformularen for nye brugere og skjuler andre grænsefladeelementer
- **Indlæser** gemte data automatisk for tilbagevendende brugere og viser nulstillingsmuligheden
- **Administrerer** brugergrænsefladens tilstand baseret på tilgængelige data
**Vigtige begreber om lokal lagring:**
- **Bevarer** data mellem browsersessioner (i modsætning til session storage)
- **Gemmer** data som nøgle-værdi-par ved hjælp af `getItem()` og `setItem()`
- **Returnerer** `null`, når der ikke findes data for en given nøgle
- **Tilbyder** en enkel måde at huske brugerpræferencer og indstillinger
> 💡 **Forståelse af browserlagring**: [LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) er som at give din udvidelse en vedvarende hukommelse. Tænk på, hvordan det gamle bibliotek i Alexandria opbevarede skriftruller - information forblev tilgængelig, selv når forskere forlod og vendte tilbage.
- **Henter** gemt API-nøgle og region fra browserens local storage
- **Tjekker** om det er første gang brugeren (ingen gemte oplysninger) eller en tilbagevendende bruger
- **Viser** opsætningsformularen for nye brugere og skjuler andre interfaceelementer
- **Indlæser** automatisk gemte data for tilbagevendende brugere og viser nulstillingsmuligheden
- **Styrer** brugergrænsefladens tilstand baseret på tilgængelig data
**Vigtige koncepter om Local Storage:**
- **Gemmer** data mellem browser-sessioner (modsat session storage)
- **Gemmer** data som nøgle-værdi par ved hjælp af `getItem()` og `setItem()`
- **Returnerer** `null` hvis ingen data findes for en given nøgle
- **Giver** en enkel måde at huske brugerpræferencer og indstillinger
> 💡 **Forståelse af browserlagring**: [LocalStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) svarer til at give din udvidelse en vedvarende hukommelse. Tænk på, hvordan det gamle bibliotek i Alexandria lagrede skriftruller - information forblev tilgængelig, selv når lærde gik og vendte tilbage.
>
> **Vigtige egenskaber:**
> - **Bevarer** data, selv efter du lukker din browser
> - **Overlever** computer-genstarter og browsernedbrud
> - **Tilbyder** betydelig lagerplads til brugerpræferencer
> - **Giver** øjeblikkelig adgang uden netværksforsinkelser
> **Vigtig bemærkning**: Din browserudvidelse har sin egen isolerede lokale lagring, der er adskilt fra almindelige websider. Dette giver sikkerhed og forhindrer konflikter med andre websites.
Du kan se dine gemte data ved at åbne browserens Developer Tools (F12), navigere til fanen **Application** og udvide sektionen **Local Storage**.
![Lokal lagringspanel](../../../../translated_images/localstorage.472f8147b6a3f8d141d9551c95a2da610ac9a3c6a73d4a1c224081c98bae09d9.da.png)
> **Nøglekarakteristika:**
> - **Bevarer** data, selv efter browseren lukkes
> - **Overlever** computer genstarter og browsernedbrud
> - **Giver** betydelig lagerplads til brugerindstillinger
> - **Tilbyder** øjeblikkelig adgang uden netværksforsinkelser
> **Vigtigt:** Din browserudvidelse har sin egen isolerede local storage, adskilt fra almindelige websider. Dette giver sikkerhed og forhindrer konflikter med andre websites.
Du kan se dine gemte data ved at åbne browserens udviklerværktøjer (F12), gå til fanen **Application** og udvide sektionen **Local Storage**.
```mermaid
stateDiagram-v2
[*] --> CheckStorage: Udvidelsen starter
CheckStorage --> FirstTime: Ingen gemte data
CheckStorage --> Returning: Data fundet
FirstTime --> ShowForm: Vis opsætningsformular
ShowForm --> UserInput: Bruger indtaster data
UserInput --> SaveData: Gem i localStorage
SaveData --> FetchAPI: Hent karbondata
Returning --> LoadData: Læs fra localStorage
LoadData --> FetchAPI: Hent karbondata
FetchAPI --> ShowResults: Vis data
ShowResults --> UserAction: Bruger interagerer
UserAction --> Reset: Ryd knap klikket
UserAction --> ShowResults: Se data
Reset --> ClearStorage: Fjern gemte data
ClearStorage --> FirstTime: Tilbage til opsætning
```
![Lokal lagringspanel](../../../../translated_images/localstorage.472f8147b6a3f8d1.da.png)
> ⚠️ **Sikkerhedsovervejelse**: I produktionsapplikationer udgør det sikkerhedsrisici at gemme API-nøgler i LocalStorage, da JavaScript kan få adgang til disse data. Til læringsformål fungerer denne tilgang fint, men rigtige applikationer bør bruge sikker server-side lagring til følsomme legitimationsoplysninger.
> ⚠️ **Sikkerhedsovervejelse**: I produktionsapplikationer udgør lagring af API-nøgler i LocalStorage sikkerhedsrisici, da JavaScript kan tilgå disse data. Til læringsformål fungerer dette fint, men rigtige applikationer bør bruge sikker server-side lagring til følsomme oplysninger.
## Håndter formularindsendelse
## Håndter formindsendelse
Nu vil vi håndtere, hvad der sker, når nogen indsender din formular. Som standard genindlæser browsere siden, når formularer indsendes, men vi vil afbryde denne adfærd for at skabe en mere glidende oplevelse.
Nu håndterer vi, hvad der sker, når nogen indsender din form. Som standard genindlæser browsere siden ved formindsendelser, men vi vil opsnappe denne adfærd for at skabe en glattere oplevelse.
Denne tilgang afspejler, hvordan mission control håndterer rumfartøjskommunikation - i stedet for at nulstille hele systemet for hver transmission opretholder de kontinuerlig drift, mens de behandler ny information.
Denne tilgang afspejler, hvordan mission control håndterer rumfartøjskommunikation - i stedet for at nulstille hele systemet for hver transmission, opretholder de kontinuerlig drift, mens de behandler ny information.
Opret en funktion, der fanger hændelsen med formularindsendelse og udtrækker brugerens input:
Opret en funktion, der fanger formindsendelsesbegivenheden og udtrækker brugerens input:
```javascript
function handleSubmit(e) {
@ -157,92 +283,147 @@ function handleSubmit(e) {
```
**I ovenstående har vi:**
- **Forhindret** den standardformularindsendelsesadfærd, der ville opdatere siden
- **Udtrukket** brugerens inputværdier fra API-nøgle- og regionsfelterne
- **Sendt** formulardataene til `setUpUser()`-funktionen til behandling
- **Opretholdt** single-page application-adfærd ved at undgå sideopdateringer
- **Forhindret** standard formindsendelsesadfærd, der ellers ville opdatere siden
- **Udtrukket** brugerinputværdier fra API-nøgle og region felterne
- **Sendt** formdata til `setUpUser()` funktionen til behandling
- **Opretholdt** single-page applikationsadfærd ved at undgå sidegenindlæsninger
✅ Husk, at dine HTML-formularfelter inkluderer attributten `required`, så browseren validerer automatisk, at brugerne angiver både API-nøgle og region, før denne funktion kører.
✅ Husk at dine HTML-formfelter indeholder `required` attributten, så browseren automatisk validerer, at brugerne har angivet både API-nøgle og region, før denne funktion kører.
## Opsæt brugerpræferencer
`setUpUser`-funktionen er ansvarlig for at gemme brugerens legitimationsoplysninger og igangsætte det første API-kald. Dette skaber en glidende overgang fra opsætning til visning af resultater.
`setUpUser` funktionen er ansvarlig for at gemme brugerens legitimationsoplysninger og igangsætte det første API-kald. Det skaber en glidende overgang fra opsætning til visning af resultater.
```javascript
function setUpUser(apiKey, regionName) {
// Save user credentials for future sessions
// Gem bruger legitimationsoplysninger til fremtidige sessioner
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
// Update UI to show loading state
// Opdater brugergrænsefladen for at vise indlæsningsstatus
loading.style.display = 'block';
errors.textContent = '';
clearBtn.style.display = 'block';
// Fetch carbon usage data with user's credentials
// Hent data om kulstofforbrug med brugerens legitimationsoplysninger
displayCarbonUsage(apiKey, regionName);
}
```
**Trin for trin, her er hvad der sker:**
- **Gemmer** API-nøgle og regionsnavn i lokal lagring til fremtidig brug
- **Viser** en indlæsningsindikator for at informere brugerne om, at data hentes
- **Fjerner** eventuelle tidligere fejlmeddelelser fra visningen
- **Viser** nulstillingsknappen, så brugerne kan nulstille deres indstillinger senere
- **Starter** API-kaldet for at hente reelle data om CO2-forbrug
Denne funktion skaber en problemfri brugeroplevelse ved at administrere både databevaring og opdateringer af brugergrænsefladen i én koordineret handling.
## Vis data om CO2-forbrug
Nu vil vi forbinde din udvidelse til eksterne datakilder via API'er. Dette forvandler din udvidelse fra et selvstændigt værktøj til noget, der kan få adgang til realtidsinformation fra hele internettet.
**Forståelse af API'er**
[API'er](https://www.webopedia.com/TERM/A/API.html) er, hvordan forskellige applikationer kommunikerer med hinanden. Tænk på dem som telegrafsystemet, der forbandt fjerne byer i det 19. århundrede - operatører sendte anmodninger til fjerne stationer og modtog svar med de ønskede oplysninger. Hver gang du tjekker sociale medier, stiller en stemmeassistent et spørgsmål eller bruger en leveringsapp, faciliterer API'er disse dataudvekslinger.
**Vigtige begreber om REST API'er:**
**Trin for trin sker der her:**
- **Gemmer** API-nøglen og regionsnavnet i local storage til fremtidig brug
- **Viser** en indlæsningsindikator for at informere brugere om, at data hentes
- **Rydder** tidligere fejlbeskeder fra visningen
- **Viser** ryd-knappen, så brugere kan nulstille deres indstillinger senere
- **Starter** API-kaldet for at hente data om CO2-forbrug
Denne funktion skaber en sømløs brugeroplevelse ved at håndtere både datapersistering og brugerfladeopdateringer i en koordineret handling.
## Vis CO2-forbrugsdata
Nu forbinder vi din udvidelse til eksterne datakilder via APIer. Det forvandler din udvidelse fra et stand-alone værktøj til noget, der kan hente realtidsinformation fra hele internettet.
**Forståelse af APIer**
[APIer](https://www.webopedia.com/TERM/A/API.html) er, hvordan forskellige applikationer kommunikerer med hinanden. Tænk på dem som telegrafsystemet, der forbandt fjerne byer i 1800-tallet operatører sendte forespørgsler til fjerne stationer og modtog svar med den ønskede information. Hver gang du tjekker sociale medier, spørger en stemmeassistent eller bruger en leveringsapp, faciliterer APIer disse dataudvekslinger.
```mermaid
flowchart TD
A[Din Udvidelse] --> B[HTTP Anmodning]
B --> C[CO2 Signal API]
C --> D{Gyldig Anmodning?}
D -->|Ja| E[Spørg Database]
D -->|Nej| F[Returner Fejl]
E --> G[Kulstofdata]
G --> H[JSON Svar]
H --> I[Din Udvidelse]
F --> I
I --> J[Opdater Brugerflade]
subgraph "API Anmodning"
K[Headers: auth-token]
L[Parametre: countryCode]
M[Metode: GET]
end
subgraph "API Svar"
N[Kulstof Intenstet]
O[Fossilt Brændstof %]
P[Tidsstempel]
end
style C fill:#e8f5e8
style G fill:#fff3e0
style I fill:#e1f5fe
```
**Vigtige koncepter om REST APIer:**
- **REST** står for 'Representational State Transfer'
- **Bruger** standard HTTP-metoder (GET, POST, PUT, DELETE) til at interagere med data
- **Returnerer** data i forudsigelige formater, typisk JSON
- **Tilbyder** konsistente, URL-baserede endpoints til forskellige typer anmodninger
- **Tilbyder** konsistente, URL-baserede endepunkter for forskellige typer forespørgsler
✅ [CO2 Signal API](https://www.co2signal.com/), som vi vil bruge, giver realtidsdata om kulstofintensitet fra elektriske netværk verden over. Dette hjælper brugere med at forstå miljøpåvirkningen af deres elforbrug!
✅ [CO2 Signal API](https://www.co2signal.com/), som vi bruger, leverer realtidsdata om kulstofintensitet fra elektricitetsnetværk verden over. Det hjælper brugere med at forstå miljøpåvirkningen af deres elforbrug!
> 💡 **Forståelse af asynkron JavaScript**: [`async`-nøgleordet](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) gør det muligt for din kode at håndtere flere operationer samtidigt. Når du anmoder om data fra en server, vil du ikke have, at hele din udvidelse fryser - det ville være som om flyveledelsen stoppede alle operationer, mens de ventede på svar fra et fly.
> 💡 **Forståelse af Asynkron JavaScript**: [`async`-nøgleordet](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function) gør det muligt for din kode at håndtere flere operationer samtidig. Når du anmoder data fra en server, ønsker du ikke, at hele din udvidelse fryser det ville være som, hvis lufttrafikkontrol standsede alle operationer, mens den ventede på et enkelt flys svar.
>
> **Vigtige fordele:**
> - **Opretholder** udvidelsens responsivitet, mens data indlæses
> - **Tillader** anden kode at fortsætte med at køre under netværksanmodninger
> - **Forbedrer** kodens læsbarhed sammenlignet med traditionelle callback-mønstre
> - **Bevarer** udvidelsens responsivitet, mens data indlæses
> - **Tillader** anden kode at fortsætte under netværksanmodninger
> - **Forbedrer** kodeoverskuelighed sammenlignet med traditionelle callback-mønstre
> - **Muliggør** elegant fejlhåndtering ved netværksproblemer
Her er en hurtig video om `async`:
[![Async og Await til håndtering af promises](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async og Await til håndtering af promises")
[![Async og Await til håndtering af promises](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async and Await for managing promises")
> 🎥 Klik på billedet ovenfor for en video om async/await.
Opret funktionen til at hente og vise data om CO2-forbrug:
### 🔄 **Pædagogisk Check-in**
**Forståelse af Asynkron Programmering**: Før vi dykker ned i API-funktionen, skal du kunne:
- ✅ Hvorfor vi bruger `async/await` i stedet for at blokere hele udvidelsen
- ✅ Hvordan `try/catch` blokke håndterer netværksfejl elegant
- ✅ Forskellen på synkrone og asynkrone operationer
- ✅ Hvorfor API-kald kan fejle, og hvordan man håndterer sådanne fejl
**Virkelighedsnære eksempler på async:**
- **Bestilling af mad**: Du venter ikke i køkkenet du får en kvittering og kan lave andre ting
- **Afsendelse af emails**: Din mailapp fryser ikke under afsendelse du kan komponere flere mails
- **Indlæsning af websider**: Billeder indlæses løbende, mens du allerede kan læse teksten
**API Autentificeringsflow**:
```mermaid
sequenceDiagram
participant Ext as Udvidelse
participant API as CO2 Signal API
participant DB as Database
Ext->>API: Forespørgsel med auth-token
API->>API: Valider token
API->>DB: Forespørg kulstofdata
DB->>API: Returner data
API->>Ext: JSON svar
Ext->>Ext: Opdater brugerflade
```
Opret funktionen til at hente og vise CO2-forbrugsdata:
```javascript
// Modern fetch API approach (no external dependencies needed)
// Moderne fetch API tilgang (ingen eksterne afhængigheder påkrævet)
async function displayCarbonUsage(apiKey, region) {
try {
// Fetch carbon intensity data from CO2 Signal API
// Hent data om kulstofintensitet fra CO2 Signal API
const response = await fetch('https://api.co2signal.com/v1/latest', {
method: 'GET',
headers: {
'auth-token': apiKey,
'Content-Type': 'application/json'
},
// Add query parameters for the specific region
// Tilføj forespørgselsparametre for den specifikke region
...new URLSearchParams({ countryCode: region }) && {
url: `https://api.co2signal.com/v1/latest?countryCode=${region}`
}
});
// Check if the API request was successful
// Tjek om API-forespørgslen var succesfuld
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`);
}
@ -250,10 +431,10 @@ async function displayCarbonUsage(apiKey, region) {
const data = await response.json();
const carbonData = data.data;
// Calculate rounded carbon intensity value
// Beregn afrundet værdi for kulstofintensitet
const carbonIntensity = Math.round(carbonData.carbonIntensity);
// Update the user interface with fetched data
// Opdater brugergrænsefladen med hentede data
loading.style.display = 'none';
form.style.display = 'none';
myregion.textContent = region.toUpperCase();
@ -261,12 +442,12 @@ async function displayCarbonUsage(apiKey, region) {
fossilfuel.textContent = `${carbonData.fossilFuelPercentage.toFixed(2)}% (percentage of fossil fuels used to generate electricity)`;
results.style.display = 'block';
// TODO: calculateColor(carbonIntensity) - implement in next lesson
// TODO: calculateColor(carbonIntensity) - implementeres i næste lektion
} catch (error) {
console.error('Error fetching carbon data:', error);
// Show user-friendly error message
// Vis brugervenlig fejlmeddelelse
loading.style.display = 'none';
results.style.display = 'none';
errors.textContent = 'Sorry, we couldn\'t fetch data for that region. Please check your API key and region code.';
@ -275,73 +456,208 @@ async function displayCarbonUsage(apiKey, region) {
```
**Hvad der sker her:**
- **Bruger** den moderne `fetch()`-API i stedet for eksterne biblioteker som Axios for renere, afhængighedsfri kode
- **Implementerer** korrekt fejltjek med `response.ok` for tidligt at fange API-fejl
- **Håndterer** asynkrone operationer med `async/await` for mere læsbar kodeflow
- **Autentificerer** med CO2 Signal API ved hjælp af `auth-token`-headeren
- **Parser** JSON-svardata og udtrækker information om kulstofintensitet
- **Bruger** det moderne `fetch()` API i stedet for eksterne biblioteker som Axios for renere, afhængighedsfri kode
- **Implementerer** korrekt fejltjek med `response.ok` for tidlig håndtering af API-fejl
- **Håndterer** asynkrone operationer med `async/await` for mere læselig kodeafvikling
- **Autentificerer** med CO2 Signal API ved at bruge `auth-token` headeren
- **Parser** JSON-responsdata og udtrækker kulstofintensitetsinformation
- **Opdaterer** flere UI-elementer med formaterede miljødata
- **Tilbyder** brugervenlige fejlmeddelelser, når API-kald mislykkes
- **Giver** brugervenlige fejlmeddelelser, når API-kald fejler
**Vigtige moderne JavaScript-begreber demonstreret:**
- **Template literals** med `${}`-syntaks for ren strengformatering
- **Fejlhåndtering** med try/catch-blokke for robuste applikationer
- **Async/await**-mønster for elegant håndtering af netværksanmodninger
- **Objekt-destructuring** for at udtrække specifikke data fra API-svar
**Vigtige moderne JavaScript-koncept demonstreret:**
- **Template literals** med `${}` syntaks til ren strengformatering
- **Fejlhåndtering** med try/catch blokke for robuste applikationer
- **Async/await** mønster til flot håndtering af netværksanmodninger
- **Objektdestruktion** til at udtrække specifikke data fra API-svar
- **Metodekædning** til flere DOM-manipulationer
✅ Denne funktion demonstrerer flere vigtige webudviklingskoncepter - kommunikation med eksterne servere, håndtering af autentifikation, databehandling, opdatering af grænseflader og elegant håndtering af fejl. Dette er grundlæggende færdigheder, som professionelle udviklere regelmæssigt bruger.
🎉 **Hvad du har opnået:** Du har skabt en browserudvidelse, der:
- **Forbinder** til internettet og henter reelle miljødata
- **Bevarer** brugerindstillinger mellem sessioner
- **Håndterer** fejl elegant i stedet for at gå ned
- **Tilbyder** en glidende, professionel brugeroplevelse
Test dit arbejde ved at køre `npm run build` og opdatere din udvidelse i browseren. Du har nu en funktionel CO2-aftryk tracker. Den næste lektion vil tilføje dynamisk ikonfunktionalitet for at fuldende udvidelsen.
✅ Denne funktion demonstrerer flere vigtige webudviklingsfærdigheder kommunikation med eksterne servere, håndtering af autentificering, databehandling, opdatering af interfaces og elegant fejlhåndtering. Det er grundlæggende kompetencer, som professionelle udviklere bruger dagligt.
```mermaid
flowchart TD
A[Start API-kald] --> B[Hent forespørgsel]
B --> C{Netværk succes?}
C -->|Nej| D[Netværksfejl]
C -->|Ja| E{Respons OK?}
E -->|Nej| F[API-fejl]
E -->|Ja| G[Parse JSON]
G --> H{Gyldige data?}
H -->|Nej| I[Datafejl]
H -->|Ja| J[Opdater UI]
D --> K[Vis fejlmeddelelse]
F --> K
I --> K
J --> L[Skjul indlæsning]
K --> L
style A fill:#e1f5fe
style J fill:#e8f5e8
style K fill:#ffebee
style L fill:#f3e5f5
```
### 🔄 **Pædagogisk Check-in**
**Helhedsoverblik over systemet**: Bekræft din mestring af hele flowet:
- ✅ Hvordan DOM-referencer gør det muligt for JavaScript at styre interfacet
- ✅ Hvorfor local storage skaber persistens mellem browser-sessioner
- ✅ Hvordan async/await gør API-kald uden at fryse udvidelsen
- ✅ Hvad der sker, når API-kald fejler, og hvordan fejl håndteres
- ✅ Hvorfor brugeroplevelsen inkluderer indlæsningsstatus og fejlmeddelelser
🎉 **Det har du opnået:** Du har skabt en browserudvidelse, der:
- **Forbinder** til internettet og henter ægte miljødata
- **Gemmer** brugerindstillinger mellem sessioner
- **Håndterer** fejl elegant uden at crashe
- **Leverer** en glat, professionel brugeroplevelse
Test dit arbejde ved at køre `npm run build` og opdatere din udvidelse i browseren. Du har nu en funktionel CO2-aftryksmåler. Næste lektion vil tilføje dynamisk ikonfunktionalitet for at færdiggøre udvidelsen.
---
## GitHub Copilot Agent Challenge 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
**Beskrivelse:** Forbedr browserudvidelsen ved at tilføje forbedringer til fejlhåndtering og brugeroplevelsesfunktioner. Denne udfordring vil hjælpe dig med at øve dig i at arbejde med API'er, lokal lagring og DOM-manipulation ved hjælp af moderne JavaScript-mønstre.
Brug Agent-tilstanden til at løse følgende udfordring:
**Beskrivelse:** Forbedr browserudvidelsen ved at tilføje fejlhåndteringsforbedringer og brugeroplevelsesfunktioner. Denne udfordring hjælper dig med at øve arbejde med API'er, lokal lagring og DOM-manipulation ved hjælp af moderne JavaScript-mønstre.
**Opgave:** Opret en forbedret version af displayCarbonUsage-funktionen, der inkluderer: 1) En genforsøgsmekanisme for mislykkede API-kald med eksponentiel backoff, 2) Inputvalidering for regionskoden før API-kaldet, 3) En indlæsningsanimation med statusindikatorer, 4) Caching af API-svar i localStorage med udløbstidsstempler (cache i 30 minutter), og 5) En funktion til at vise historiske data fra tidligere API-kald. Tilføj også korrekte TypeScript-stil JSDoc-kommentarer for at dokumentere alle funktionsparametre og returtyper.
**Opgave:** Opret en forbedret version af funktionen displayCarbonUsage, der inkluderer: 1) En retry-mekanisme for mislykkede API-kald med eksponentiel backoff, 2) Inputvalidering for regionskoden inden API-kaldet foretages, 3) En loading-animation med fremdriftsindikatorer, 4) Caching af API-svar i localStorage med udløbstidspunkter (cache i 30 minutter), og 5) En funktion til at vise historiske data fra tidligere API-kald. Tilføj også passende TypeScript-style JSDoc-kommentarer til dokumentation af alle funktionsparametre og returværdier.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Udvid din forståelse af API'er ved at udforske de mange browserbaserede API'er, der er tilgængelige for webudvikling. Vælg en af disse browser-API'er og byg en lille demonstration:
Udvid din forståelse af API'er ved at udforske de mange browserbaserede API'er, der findes til webudvikling. Vælg en af disse browser-API'er og byg en lille demonstration:
- [Geolocation API](https://developer.mozilla.org/docs/Web/API/Geolocation_API) - Få brugerens aktuelle placering
- [Notification API](https://developer.mozilla.org/docs/Web/API/Notifications_API) - Send desktop-notifikationer
- [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) - Opret interaktive trækgrænseflader
- [HTML Drag and Drop API](https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API) - Opret interaktive drag-grænseflader
- [Web Storage API](https://developer.mozilla.org/docs/Web/API/Web_Storage_API) - Avancerede teknikker til lokal lagring
- [Fetch API](https://developer.mozilla.org/docs/Web/API/Fetch_API) - Moderne alternativ til XMLHttpRequest
**Forskningsspørgsmål at overveje:**
- Hvilke reelle problemer løser denne API?
- Hvilke virkelige problemer løser denne API?
- Hvordan håndterer API'en fejl og kanttilfælde?
- Hvilke sikkerhedsovervejelser findes der ved brug af denne API?
- Hvor bredt understøttes denne API på tværs af forskellige browsere?
- Hvilke sikkerhedshensyn findes, når man bruger denne API?
- Hvor bredt understøttet er denne API på tværs af forskellige browsere?
Efter din forskning, identificer hvilke egenskaber der gør en API udviklervenlig og pålidelig.
Efter din forskning, identificer hvilke karakteristika der gør en API brugervenlig og pålidelig for udviklere.
## Quiz efter forelæsning
## Quiz efter lektionen
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/26)
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/26)
## Gennemgang & Selvstudie
Du har lært om LocalStorage og API'er i denne lektion, begge meget nyttige for den professionelle webudvikler. Kan du tænke over, hvordan disse to ting arbejder sammen? Overvej, hvordan du ville designe en hjemmeside, der gemmer elementer til brug af en API.
Du lærte om LocalStorage og API'er i denne lektion, begge meget nyttige for den professionelle webudvikler. Kan du tænke over, hvordan disse to ting arbejder sammen? Overvej, hvordan du ville arkitekture en webside, der gemmer elementer til brug for en API.
### ⚡ **Hvad du kan gøre i de næste 5 minutter**
- [ ] Åbn DevTools Application-fanen og udforsk localStorage på en hvilken som helst hjemmeside
- [ ] Opret en simpel HTML-formular og test formularvalidering i browseren
- [ ] Prøv at gemme og hente data ved hjælp af localStorage i browserkonsollen
- [ ] Inspicér formulardata, der sendes, ved brug af Netværks-fanen
### 🎯 **Hvad du kan nå i denne time**
- [ ] Fuldfør quizzen efter lektionen og forstå formularhåndteringskonceptet
- [ ] Byg en browserudvidelsesformular, der gemmer brugerpræferencer
- [ ] Implementer klient-side formularvalidering med nyttige fejlmeddelelser
- [ ] Øv brug af chrome.storage API til lagring af data i udvidelsen
- [ ] Opret en brugergrænseflade, der reagerer på gemte brugerindstillinger
### 📅 **Din uge-lange udvidelsesudvikling**
- [ ] Fuldfør en fuldt funktionsdygtig browserudvidelse med formularfunktionalitet
- [ ] Mestre forskellige lagringsmuligheder: lokal, synkroniseret og session-lagring
- [ ] Implementer avancerede formularfunktioner som autoudfyldning og validering
- [ ] Tilføj import/eksport-funktionalitet for brugerdata
- [ ] Test din udvidelse grundigt på tværs af forskellige browsere
- [ ] Forbedr din udvidelses brugeroplevelse og fejlhåndtering
### 🌟 **Din månedslange mestring af web-API'er**
- [ ] Byg komplekse applikationer ved brug af forskellige browserlager-API'er
- [ ] Lær om offline-første udviklingsmønstre
- [ ] Bidrag til open source-projekter med fokus på dataperistens
- [ ] Mestre privatlivsfokuseret udvikling og GDPR-overholdelse
- [ ] Opret genanvendelige biblioteker til formularhåndtering og dataadministration
- [ ] Del viden om web-API'er og udvidelsesudvikling
## 🎯 Din tidslinje for mestring af udvidelsesudvikling
```mermaid
timeline
title API Integration & Storage Læringsprogression
section DOM Grundlæggende (15 minutter)
Elementreferencer: querySelector mestring
: Event listener opsætning
: Tilstandsadministration grundlæggende
section Lokal Lager (20 minutter)
Datapersistens: Nøgle-værdi lagring
: Sessionsstyring
: Håndtering af brugerpræferencer
: Lagerinspektionsværktøjer
section Form Håndtering (25 minutter)
Brugerinput: Formvalidering
: Forhindring af hændelser
: Dataudtræk
: UI tilstandsovergange
section API Integration (35 minutter)
Ekstern Kommunikation: HTTP forespørgsler
: Autentificeringsmønstre
: JSON dataparsering
: Responsbehandling
section Async Programmering (40 minutter)
Moderne JavaScript: Promise håndtering
: Async/await mønstre
: Fejlstyring
: Ikke-blokerende operationer
section Fejl Håndtering (30 minutter)
Robuste Applikationer: Try/catch blokke
: Brugervenlige beskeder
: Graceful degradering
: Debugging teknikker
section Avancerede Mønstre (1 uge)
Professionel Udvikling: Caching strategier
: Rate begrænsning
: Retry mekanismer
: Performance optimering
section Produktionsfærdigheder (1 måned)
Enterprise Funktioner: Sikkerhed bedste praksis
: API versionering
: Overvågning & logning
: Skalerbar arkitektur
```
### 🛠️ Opsummering af dit full-stack udviklingsværktøjssæt
Efter at have gennemført denne lektion har du nu:
- **DOM-mesterskab**: Præcis målretning og manipulation af elementer
- **Lager-ekspertise**: Vedvarende datastyring med localStorage
- **API-integration**: Realtidsdatahentning og autentificering
- **Asynkron programmering**: Ikke-blokerende operationer med moderne JavaScript
- **Fejlhåndtering**: Robust applikationer, der håndterer fejl elegant
- **Brugeroplevelse**: Indlæsningsstatus, validering og glatte interaktioner
- **Moderne mønstre**: fetch API, async/await og ES6+ funktioner
**Professionelle færdigheder opnået**: Du har implementeret mønstre anvendt i:
- **Webapplikationer**: Single-page apps med eksterne datakilder
- **Mobiludvikling**: API-drevne apps med offline-muligheder
- **Desktop-software**: Electron-apps med vedvarende lagring
- **Virksomhedssystemer**: Autentificering, caching og fejlhåndtering
- **Moderne frameworks**: React/Vue/Angular datastyringsmønstre
**Næste niveau**: Du er klar til at udforske avancerede emner som caching-strategier, realtids WebSocket-forbindelser eller kompleks state management!
## Opgave
[Adoptér en API](assignment.md)
[Adopter en API](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Fraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for eventuelle misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,163 +1,332 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "eb358f3f4c2c082f9f3a4f98efa1d337",
"translation_date": "2025-10-23T22:12:59+00:00",
"original_hash": "b275fed2c6fc90d2b9b6661a3225faa2",
"translation_date": "2026-01-06T23:45:43+00:00",
"source_file": "5-browser-extension/3-background-tasks-and-performance/README.md",
"language_code": "da"
}
-->
# Browserudvidelsesprojekt del 3: Lær om baggrundsopgaver og ydeevne
Har du nogensinde undret dig over, hvad der gør nogle browserudvidelser hurtige og responsive, mens andre virker langsomme? Hemmeligheden ligger i, hvad der sker bag kulisserne. Mens brugerne klikker rundt i din udvidelses grænseflade, er der en hel verden af baggrundsprocesser, der stille og roligt håndterer datahentning, ikonopdateringer og systemressourcer.
# Browserudvidelsesprojekt Del 3: Lær om Baggrundsopgaver og Ydeevne
```mermaid
journey
title Din Performanceoptimeringsrejse
section Fundament
Lær browserværktøjer: 3: Student
Forstå profilering: 4: Student
Identificer flaskehalse: 4: Student
section Udvidelsesfunktioner
Byg farvesystem: 4: Student
Opret baggrundsopgaver: 5: Student
Opdater ikoner dynamisk: 5: Student
section Optimering
Overvåg ydeevne: 5: Student
Fejlret problemer: 4: Student
Finpuds oplevelse: 5: Student
```
Nogen sinde undret dig over, hvad der gør nogle browserudvidelser hurtige og lydhøre, mens andre virker træge? Hemmeligheden ligger i det, der foregår bag kulisserne. Mens brugere klikker rundt i din udvidelses interface, er der en hel verden af baggrundsprocesser, der stille håndterer datahentning, ikonopdateringer og systemressourcer.
Dette er vores sidste lektion i browserudvidelses-serien, og vi vil gøre din CO2-fodaftryk-tracker mere effektiv. Du vil tilføje dynamiske ikonopdateringer og lære, hvordan du opdager ydeevneproblemer, før de bliver til reelle udfordringer. Det er som at finjustere en racerbil - små optimeringer kan gøre en stor forskel i, hvordan alt fungerer.
Dette er vores sidste lektion i browserudvidelsesserien, og vi vil få din CO2-aftryksmåler til at fungere gnidningsløst. Du vil tilføje dynamiske ikonopdateringer og lære at spotte ydeevneproblemer før de bliver til problemer. Det er som at finjustere en racerbils motor små optimeringer kan gøre en kæmpe forskel i, hvordan alt kører.
Når vi er færdige, vil du have en poleret udvidelse og forstå de ydeevneprincipper, der adskiller gode webapps fra fantastiske. Lad os dykke ned i browseroptimeringens verden.
Når vi er færdige, har du en poleret udvidelse og forstår de ydeevneprincipper, der adskiller gode webapps fra fantastiske. Lad os dykke ned i browseroptimeringens verden.
## Quiz før lektionen
## For-lektions quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/27)
[For-lektions quiz](https://ff-quizzes.netlify.app/web/quiz/27)
### Introduktion
I vores tidligere lektioner har du bygget en formular, forbundet den til en API og arbejdet med asynkron datahentning. Din udvidelse tager form på en flot måde.
Nu skal vi tilføje de sidste detaljer - som at få udvidelsesikonet til at skifte farve baseret på CO2-dataene. Det minder mig om, hvordan NASA måtte optimere hvert system på Apollo-rumfartøjet. De kunne ikke tillade sig at spilde cyklusser eller hukommelse, fordi liv afhængede af ydeevnen. Selvom vores browserudvidelse ikke er helt så kritisk, gælder de samme principper - effektiv kode skaber bedre brugeroplevelser.
## Grundlæggende om webydeevne
Når din kode kører effektivt, kan folk faktisk *mærke* forskellen. Du kender det øjeblik, hvor en side indlæses med det samme, eller en animation flyder glat? Det er god ydeevne i aktion.
Ydeevne handler ikke kun om hastighed - det handler om at skabe weboplevelser, der føles naturlige i stedet for klodsede og frustrerende. Tilbage i computerens tidlige dage havde Grace Hopper berømt en nanosekund (et stykke ledning omkring en fod lang) på sit skrivebord for at vise, hvor langt lys rejser på en milliardtedel af et sekund. Det var hendes måde at forklare, hvorfor hver mikrosekund betyder noget i computing. Lad os udforske de værktøjer, der hjælper dig med at finde ud af, hvad der sænker farten.
> "Webside-ydeevne handler om to ting: hvor hurtigt siden indlæses, og hvor hurtigt koden på den kører." -- [Zack Grossbart](https://www.smashingmagazine.com/2012/06/javascript-profiling-chrome-developer-tools/)
Emnet om, hvordan man gør sine websites lynhurtige på alle slags enheder, for alle slags brugere, i alle slags situationer, er naturligvis omfattende. Her er nogle punkter at huske på, når du bygger enten et standard webprojekt eller en browserudvidelse.
Det første skridt i at optimere dit site er at forstå, hvad der faktisk sker bag kulisserne. Heldigvis har din browser kraftfulde værktøjer indbygget.
For at åbne Developer Tools i Edge skal du klikke på de tre prikker øverst til højre, derefter gå til Flere værktøjer > Developer Tools. Eller brug tastaturgenvejen: `Ctrl` + `Shift` + `I` på Windows eller `Option` + `Command` + `I` på Mac. Når du er der, skal du klikke på fanen Ydeevne - det er her, du vil foretage din undersøgelse.
**Her er dit ydeevne-detektion værktøj:**
- **Åbn** Developer Tools (du vil bruge dem konstant som udvikler!)
- **Gå til** fanen Ydeevne - tænk på det som din webapps fitness tracker
- **Tryk på** optageknappen og se din side i aktion
- **Undersøg** resultaterne for at finde ud af, hvad der sænker farten
Lad os prøve dette. Åbn en hjemmeside (Microsoft.com fungerer godt til dette) og klik på 'Optag'-knappen. Opdater nu siden og se profileringsværktøjet fange alt, hvad der sker. Når du stopper optagelsen, vil du se en detaljeret oversigt over, hvordan browseren 'scriptede', 'renderede' og 'malede' siden. Det minder mig om, hvordan kontrolcenteret overvåger hvert system under en raketopsendelse - du får realtidsdata om præcis, hvad der sker og hvornår.
✅ [Microsoft Dokumentation](https://docs.microsoft.com/microsoft-edge/devtools-guide/performance/?WT.mc_id=academic-77807-sagibbon) har masser af detaljer, hvis du vil dykke dybere.
> Pro tip: Ryd din browsercache før testning for at se, hvordan dit site performer for førstegangsbesøgende - det er normalt ret anderledes end gentagne besøg!
Vælg elementer på profilens tidslinje for at zoome ind på begivenheder, der sker, mens din side indlæses.
Få et øjebliksbillede af din sides ydeevne ved at vælge en del af profilens tidslinje og kigge på oversigtspanelet:
![Edge profiler snapshot](../../../../translated_images/snapshot.97750180ebcad73794a3594b36925eb5c8dbaac9e03fec7f9b974188c9ac63c7.da.png)
Tjek begivenhedslogpanelet for at se, om nogen begivenhed tog længere end 15 ms:
![Edge event log](../../../../translated_images/log.804026979f3707e00eebcfa028b2b5a88cec6292f858767bb6703afba65a7d9c.da.png)
✅ Lær din profiler at kende! Åbn udviklerværktøjerne på denne side og se, om der er nogen flaskehalse. Hvilken ressource tager længst tid at indlæse? Hvilken er hurtigst?
## Hvad skal man kigge efter, når man profilerer
I vores tidligere lektioner byggede du en formular, tilknyttede den til en API, og håndterede asynkron datahentning. Din udvidelse tager form på flot vis.
Nu skal vi tilføje de sidste detaljer - som at få udvidelsesikonet til at skifte farve baseret på CO2-dataene. Det minder mig om, hvordan NASA måtte optimere hvert system på Apollo-rumfartøjet. De havde ikke råd til spildte cyklusser eller hukommelse, fordi liv var afhængige af ydeevnen. Selvom vores browserudvidelse ikke er helt så kritisk, gælder de samme principper effektiv kode skaber bedre brugeroplevelser.
```mermaid
mindmap
root((Ydeevne & Baggrundsopgaver))
Browser Performance
Rendering Pipeline
Assetoptimering
DOM-manipulation
JavaScript-udførelse
Profilering Værktøjer
Udviklerværktøjer
Ydelsestab
Tidslinjeanalyse
Flaskehalsregistrering
Udvidelsesarkitektur
Baggrundsscripts
Indholdsscripts
Beskedudveksling
Ikonstyring
Optimeringsstrategier
Kodeopdeling
Dovent Belastning
Caching
Ressourcekomprimering
Visuel Feedback
Dynamiske Ikoner
Farvekodning
Opdateringer i Real-tid
Brugeroplevelse
```
## Grundlæggende Webydelse
Når din kode kører effektivt, kan folk rent faktisk *mærke* forskellen. Du kender det øjeblik, hvor en side loader med det samme, eller en animation kører flydende? Det er god ydeevne, der er på spil.
Ydeevne handler ikke kun om hastighed det handler om at skabe weboplevelser, der føles naturlige i stedet for klodsede og frustrerende. Tilbage i de tidlige dage med computing havde Grace Hopper berømt en nanosekund (et stykke wire omkring en fod langt) på sit skrivebord til at vise, hvor langt lyset bevæger sig på en milliardtedel af et sekund. Det var hendes måde at forklare, hvorfor hvert mikrosekund tæller i computing. Lad os udforske detektiveredskaberne, der hjælper dig med at finde ud af, hvad der sænker tingene ned.
> "Websiders ydeevne handler om to ting: hvor hurtigt siden loader, og hvor hurtigt koden på den kører." -- [Zack Grossbart](https://www.smashingmagazine.com/2012/06/javascript-profiling-chrome-developer-tools/)
Emnet om, hvordan du gør dine websites lynhurtige på alle slags enheder, for alle slags brugere, i alle slags situationer, er ikke overraskende meget omfattende. Her er nogle punkter, du bør have i tankerne, når du bygger enten et almindeligt webprojekt eller en browserudvidelse.
Det første skridt i at optimere dit site er at forstå, hvad der faktisk sker under motorhjelmen. Heldigvis kommer din browser med kraftfulde detektivværktøjer indbygget.
```mermaid
flowchart LR
A[HTML] --> B[Parse]
B --> C[DOM Tree]
D[CSS] --> E[Parse]
E --> F[CSSOM]
G[JavaScript] --> H[Udfør]
C --> I[Render Træ]
F --> I
H --> I
I --> J[Layout]
J --> K[Mal]
K --> L[Sammensæt]
L --> M[Visning]
subgraph "Kritisk Renderingssti"
N["1. Parse HTML"]
O["2. Parse CSS"]
P["3. Udfør JS"]
Q["4. Byg Render Træ"]
R["5. Layout Elementer"]
S["6. Mal Pixels"]
T["7. Sammensæt Lag"]
end
style M fill:#e8f5e8
style I fill:#fff3e0
style H fill:#ffebee
```
For at åbne Udviklerværktøjer i Edge, klik på de tre prikker øverst til højre, og gå så til Flere værktøjer > Udviklerværktøjer. Eller brug tastaturgenvejen: `Ctrl` + `Shift` + `I` på Windows eller `Option` + `Command` + `I` på Mac. Når du er der, klik på fanen Ydeevne - det er her, du laver din undersøgelse.
**Her er dit ydeevne-detektiudstyr:**
- **Åbn** Udviklerværktøjer (du vil bruge disse hele tiden som udvikler!)
- **Gå til** fanen Ydeevne tænk på den som din webapps fitness tracker
- **Tryk på** Optagelsesknappen og se din side i aktion
- **Studér** resultaterne for at spotte, hvad der sænker tingene
Lad os prøve det. Åbn en hjemmeside (Microsoft.com fungerer godt til dette) og klik på 'Optag' knappen. Opdater nu siden, og se profilen fange alt, hvad der sker. Når du stopper optagelsen, får du en detaljeret opdeling af, hvordan browseren script, renderer og maler sitet. Det minder mig om, hvordan mission control overvåger hvert system under en raketopsendelse du får realtime-data om præcis, hvad der sker og hvornår.
![Edge profiler](../../../../translated_images/profiler.5a4a62479c5df01c.da.png)
✅ [Microsoft Dokumentationen](https://docs.microsoft.com/microsoft-edge/devtools-guide/performance/?WT.mc_id=academic-77807-sagibbon) har masser af flere detaljer, hvis du vil dykke dybere ned
> Profi-tip: Ryd din browsercache før test for at se, hvordan dit site performer for førstegangsbesøgende det er som regel meget anderledes end gentagne besøg!
Vælg elementer i profilens tidslinje for at zoome ind på begivenheder, der sker, mens din side loader.
Få et øjebliksbillede af din sides ydeevne ved at vælge et stykke af profiltidslinjen og kigge på oversigtspanelet:
![Edge profiler snapshot](../../../../translated_images/snapshot.97750180ebcad737.da.png)
Tjek Event Log-panelet for at se, om nogen begivenhed tog længere tid end 15 ms:
![Edge event log](../../../../translated_images/log.804026979f3707e0.da.png)
✅ Lær din profiler at kende! Åbn udviklerværktøjerne på denne side og se, om der er nogen flaskehalse. Hvad er det langsomt indlæsende element? Hvad er det hurtigste?
```mermaid
flowchart TD
A[Åbn DevTools] --> B[Naviger til Performance-fanen]
B --> C[Klik på Optageknappen]
C --> D[Udfør Handlinger]
D --> E[Stop Optagelse]
E --> F{Analyser Resultater}
F --> G[Tjek Tidslinje]
F --> H[Gennemgå Netværk]
F --> I[Undersøg Scripts]
F --> J[Identificer Malebegivenheder]
G --> K{Lange Opgaver?}
H --> L{Store Ressourcer?}
I --> M{Renderingsblokering?}
J --> N{Dyre Maleprocesser?}
K -->|Ja| O[Optimer JavaScript]
L -->|Ja| P[Komprimer Ressourcer]
M -->|Ja| Q[Tilføj Async/Defer]
N -->|Ja| R[Forenkle Stilarter]
O --> S[Test Igen]
P --> S
Q --> S
R --> S
style A fill:#e1f5fe
style F fill:#fff3e0
style S fill:#e8f5e8
```
## Hvad du skal kigge efter, når du profilerer
At køre profileringsværktøjet er kun begyndelsen - den virkelige færdighed er at vide, hvad de farverige diagrammer faktisk fortæller dig. Bare rolig, du vil lære at læse dem. Erfarne udviklere har lært at spotte advarselstegn, før de bliver til fuldgyldige problemer.
At køre profilen er kun begyndelsen den rigtige færdighed er at vide, hvad de farverige diagrammer rent faktisk fortæller dig. Bare rolig, du vil lære at læse dem. Erfarne udviklere har lært at spotte advarselstegnene, før de udvikler sig til problemer.
Lad os tale om de sædvanlige mistænkte - de ydeevneproblemer, der har en tendens til at snige sig ind i webprojekter. Ligesom Marie Curie nøje måtte overvåge strålingsniveauer i sit laboratorium, skal vi holde øje med visse mønstre, der indikerer problemer under opsejling. At fange disse tidligt vil spare dig (og dine brugere) for en masse frustration.
Lad os tale om de sædvanlige mistænkte ydelsesproblemerne, der plejer at snige sig ind i webprojekter. Ligesom Marie Curie måtte overvåge strålingsniveauerne omhyggeligt i sit laboratorium, skal vi holde øje med visse mønstre, der indikerer problemer under opsejling. At fange dem tidligt vil spare dig (og dine brugere) for meget frustration.
**Filstørrelser**: Websites er blevet "tungere" gennem årene, og meget af den ekstra vægt kommer fra billeder. Det er som om, vi har fyldt mere og mere i vores digitale kufferter.
**Asset-størrelser**: Websites er blevet "tungere" gennem årene, og meget af den ekstra vægt kommer fra billeder. Det er som om, vi har pakket mere og mere i vores digitale kufferter.
✅ Tjek [Internet Archive](https://httparchive.org/reports/page-weight) for at se, hvordan sidestørrelser er vokset over tid - det er ret afslørende.
✅ Tjek [Internet Archive](https://httparchive.org/reports/page-weight) for at se, hvordan sidestørrelser er vokset over tid det er ret afslørende.
**Sådan optimerer du dine filer:**
- **Komprimer** billederne! Moderne formater som WebP kan reducere filstørrelser dramatisk
- **Server** den rigtige billedstørrelse til hver enhed - der er ingen grund til at sende enorme desktopbilleder til telefoner
- **Minimer** din CSS og JavaScript - hver byte tæller
- **Brug** lazy loading, så billeder kun downloades, når brugerne faktisk ruller ned til dem
**Sådan holder du dine assets optimerede:**
- **Komprimer** dine billeder! Moderne formater som WebP kan skære drastisk i filstørrelserne
- **Server** den rette billedstørrelse til hver enhed ikke brug kæmpestore desktop-billeder på telefoner
- **Minificér** dit CSS og JavaScript hver byte tæller
- **Brug** lazy loading, så billeder kun hentes, når brugere rent faktisk scroller til dem
**DOM-gennemløb**: Browseren skal opbygge sin Document Object Model baseret på den kode, du skriver, så det er i god sideydeevnes interesse at holde dine tags minimale og kun bruge og style det, siden har brug for. For eksempel kan overskydende CSS, der er knyttet til en side, optimeres; stilarter, der kun skal bruges på én side, behøver ikke at være inkluderet i hovedstilarket.
**DOM-gennemgange**: Browseren skal bygge sit Document Object Model baseret på den kode, du skriver, så det er i interesse for god sidetilstand at holde dine tags minimale, kun bruge og style, hvad siden har brug for. I den sammenhæng kunne overflødigt CSS, der tilhører en side, optimeres; stilarter, der kun skal bruges på én side, behøver ikke inkluderes i hovedstylesheetet, for eksempel.
**Nøglestrategier for DOM-optimering:**
- **Minimerer** antallet af HTML-elementer og indlejringsniveauer
- **Fjerner** ubrugte CSS-regler og konsoliderer stilark effektivt
- **Organiserer** CSS til kun at indlæse det, der er nødvendigt for hver side
- **Minimerer** antallet af HTML-elementer og indlejringstrin
- **Fjerner** ubrugte CSS-regler og konsoliderer stylesheets effektivt
- **Organiserer** CSS til kun at loade det, der er nødvendigt for hver side
- **Strukturerer** HTML semantisk for bedre browserparsing
**JavaScript**: Hver JavaScript-udvikler bør holde øje med 'render-blocking' scripts, der skal indlæses, før resten af DOM kan gennemløbes og males til browseren. Overvej at bruge `defer` med dine inline scripts (som det gøres i Terrarium-modulet).
**JavaScript**: Enhver JavaScript-udvikler bør holde øje med 'render-blokerende' scripts, som skal indlæses, før resten af DOM kan gennemgås og males i browseren. Overvej at bruge `defer` med dine inline scripts (som i Terrarium-modulet).
**Moderne JavaScript-optimeringsteknikker:**
- **Bruger** attributten `defer` til at indlæse scripts efter DOM-parsing
- **Implementerer** kodeopdeling for kun at indlæse nødvendigt JavaScript
- **Anvender** lazy loading til ikke-kritisk funktionalitet
- **Bruger** `defer` attributten til at loade scripts efter DOM-parsning
- **Implementerer** kodesplitning for kun at loade nødvendigt JavaScript
- **Anvender** lazy loading for ikke-kritisk funktionalitet
- **Minimerer** brugen af tunge biblioteker og frameworks, når det er muligt
✅ Prøv nogle websites på en [Site Speed Test website](https://www.webpagetest.org/) for at lære mere om de almindelige kontrolpunkter, der bruges til at bestemme sideydeevne.
✅ Prøv nogle sites på et [Site Speed Test website](https://www.webpagetest.org/) for at lære mere om de almindelige tests, der foretages for at afgøre websidens ydeevne.
Nu hvor du har en idé om, hvordan browseren gengiver de ressourcer, du sender til den, lad os se på de sidste ting, du skal gøre for at fuldføre din udvidelse:
### 🔄 **Pædagogisk tjek-ind**
**Ydeevneforståelse**: Før du bygger udvidelsesfunktioner, skal du kunne:
- ✅ Forklare den kritiske gengivelsessti fra HTML til pixels
- ✅ Identificere almindelige ydeevneflaskehalse i webapplikationer
- ✅ Bruge browserens udviklerværktøjer til at profilere sidens ydeevne
- ✅ Forstå, hvordan asset-størrelse og DOM-kompleksitet påvirker hastigheden
### Opret en funktion til at beregne farve
**Hurtig Selvtest**: Hvad sker der, når du har render-blokerende JavaScript?
*Svar: Browseren skal downloade og udføre scriptet, før den kan fortsætte med at parse HTML og gengive siden*
Nu vil vi oprette en funktion, der omdanner numeriske data til meningsfulde farver. Tænk på det som et trafiklyssystem - grønt for ren energi, rødt for høj CO2-intensitet.
**Virkelighedens ydeevneeffekt**:
- **100 ms forsinkelse**: Brugere bemærker langsommere ydelse
- **1 sekund forsinkelse**: Brugere begynder at miste fokus
- **3+ sekunder**: 40% af brugere forlader siden
- **Mobilnetværk**: Ydelse betyder endnu mere
Denne funktion vil tage CO2-dataene fra vores API og bestemme, hvilken farve der bedst repræsenterer miljøpåvirkningen. Det er ligesom hvordan forskere bruger farvekodning i varmekort til at visualisere komplekse dataprofiler - fra havtemperaturer til stjernedannelse. Lad os tilføje dette til `/src/index.js`, lige efter de `const`-variabler, vi tidligere har oprettet:
Nu hvor du har en idé om, hvordan browseren gengiver de assets, du sender, lad os se på de sidste ting, du skal gøre for at færdiggøre din udvidelse:
### Opret en funktion til at beregne farve
Nu opretter vi en funktion, der omsætter numeriske data til meningsfulde farver. Tænk på det som et trafiklys - grønt for ren energi, rødt for høj CO2-intensitet.
Denne funktion vil tage CO2-dataene fra vores API og bestemme, hvilken farve der bedst repræsenterer miljøpåvirkningen. Det minder om, hvordan forskere bruger farvekodning i varmekort til at visualisere komplekse datamønstre fra ocean-temperaturer til stjernedannelse. Lad os tilføje dette til `/src/index.js`, lige efter de `const` variabler, vi satte op tidligere:
```mermaid
flowchart LR
A[CO2 Værdi] --> B[Find Nærmeste Skala Punkt]
B --> C[Få Skala Indeks]
C --> D[Kortlæg til Farve]
D --> E[Send til Baggrund]
subgraph "Farskala"
F["0-150: Grøn (Ren)"]
G["150-600: Gul (Moderat)"]
H["600-750: Orange (Høj)"]
I["750+: Brun (Meget Høj)"]
end
subgraph "Besked Overførsel"
J[Indholds Script]
K[chrome.runtime.sendMessage]
L[Baggrunds Script]
M[Ikon Opdatering]
end
style A fill:#e1f5fe
style D fill:#e8f5e8
style E fill:#fff3e0
```
```javascript
function calculateColor(value) {
// Define CO2 intensity scale (grams per kWh)
// Definer CO2-intensitetsskala (gram pr. kWh)
const co2Scale = [0, 150, 600, 750, 800];
// Corresponding colors from green (clean) to dark brown (high carbon)
// Tilsvarende farver fra grøn (ren) til mørkebrun (høj kulstof)
const colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
// Find the closest scale value to our input
// Find den nærmeste skaleværdi til vores input
const closestNum = co2Scale.sort((a, b) => {
return Math.abs(a - value) - Math.abs(b - value);
})[0];
console.log(`${value} is closest to ${closestNum}`);
// Find the index for color mapping
// Find indekset for farvekortlægning
const num = (element) => element > closestNum;
const scaleIndex = co2Scale.findIndex(num);
const closestColor = colors[scaleIndex];
console.log(scaleIndex, closestColor);
// Send color update message to background script
// Send farveopdateringsbesked til baggrundsskriptet
chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } });
}
```
**Lad os bryde denne smarte lille funktion ned:**
- **Opsætter** to arrays - et for CO2-niveauer, et andet for farver (grøn = ren, brun = beskidt!)
- **Finder** det nærmeste match til vores faktiske CO2-værdi ved hjælp af smart array-sortering
- **Henter** den matchende farve ved hjælp af metoden findIndex()
- **Opsætter** to arrays ét for CO2-niveauer, ét for farver (grøn = ren, brun = beskidt!)
- **Finder** det tættest matchende tal til vores faktiske CO2-værdi ved hjælp af smart arraysortering
- **Henter** den matchende farve med findIndex()-metoden
- **Sender** en besked til Chromes baggrundsscript med den valgte farve
- **Bruger** template literals (de der backticks) for renere strengformatering
- **Bruger** template literals (de backticks) for flottere strengformatering
- **Holder** alt organiseret med const-deklarationer
`chrome.runtime` [API](https://developer.chrome.com/extensions/runtime) er som nervesystemet i din udvidelse - det håndterer al baggrundskommunikation og opgaver:
`chrome.runtime` [API](https://developer.chrome.com/extensions/runtime) er som udvidelsens nervesystem det håndterer al kommunikation og opgaver bag kulisserne:
> "Brug chrome.runtime API til at hente baggrundssiden, returnere detaljer om manifestet og lytte til og reagere på begivenheder i appens eller udvidelsens livscyklus. Du kan også bruge denne API til at konvertere relative URL-stier til fuldt kvalificerede URL'er."
> "Brug chrome.runtime APIen til at hente baggrundssiden, returnere detaljer om manifestet og lytte efter og reagere på begivenheder i app- eller udvidelseslivscyklussen. Du kan også bruge APIen til at konvertere relative URL-stier til fuldt kvalificerede URLer."
**Hvorfor Chrome Runtime API er så praktisk:**
- **Lader** forskellige dele af din udvidelse tale med hinanden
**Hvorfor Chrome Runtime APIen er så smart:**
- **Lader** forskellige dele af din udvidelse tale sammen
- **Håndterer** baggrundsarbejde uden at fryse brugergrænsefladen
- **Administrerer** din udvidelses livscyklusbegivenheder
- **Gør** beskedudveksling mellem scripts super nemt
✅ Hvis du udvikler denne browserudvidelse til Edge, kan det overraske dig, at du bruger en Chrome API. De nyere Edge-browserversioner kører på Chromium-browsermotoren, så du kan drage fordel af disse værktøjer.
> **Pro Tip**: Hvis du vil profilere en browserudvidelse, skal du åbne udviklerværktøjerne inden for selve udvidelsen, da det er sin egen separate browserinstans. Dette giver dig adgang til udvidelsesspecifikke ydeevnemålinger.
✅ Hvis du udvikler denne browserudvidelse til Edge, vil det måske overraske dig, at du bruger en chrome API. De nyere Edge-versioner kører på Chromium-browsermotoren, så du kan bruge disse værktøjer.
```mermaid
architecture-beta
group browser(logos:chrome)[Browser]
service popup(logos:html5)[Popup UI] in browser
service content(logos:javascript)[Indholdsskript] in browser
service background(database)[Baggrundsskript] in browser
service api(logos:api)[Ekstern API] in browser
popup:R -- L:content
content:R -- L:background
background:T -- B:api
content:T -- B:api
junction junctionCenter in browser
popup:R -- L:junctionCenter
junctionCenter:R -- L:background
```
> **Profi-tip**: Hvis du vil profilere en browserudvidelse, så start dev-værktøjerne fra udvidelsen selv, da det er en separat browserinstans. Det giver dig adgang til udvidelsesspecifikke ydeevnemålinger.
### Sæt en standard ikonfarve
Før vi begynder at hente rigtige data, lad os give vores udvidelse et startpunkt. Ingen kan lide at stirre på et tomt eller ødelagt ikon. Vi starter med en grøn farve, så brugerne ved, at udvidelsen fungerer fra det øjeblik, de installerer den.
Før vi begynder at hente rigtige data, lad os give vores udvidelse et udgangspunkt. Ingen kan lide at stirre på et tomt eller ødelagt ikon. Vi starter med en grøn farve, så brugerne ved, at udvidelsen virker allerede fra installationstidspunktet.
I din `init()`-funktion, lad os opsætte det standard grønne ikon:
I din `init()` funktion, lad os sætte standardikonet til den grønne farve:
```javascript
chrome.runtime.sendMessage({
@ -168,110 +337,261 @@ chrome.runtime.sendMessage({
});
```
**Hvad denne initialisering opnår:**
**Dette opnår initialiseringen:**
- **Sætter** en neutral grøn farve som standardtilstand
- **Giver** øjeblikkelig visuel feedback, når udvidelsen indlæses
- **Giver** øjeblikkelig visuel feedback, når udvidelsen loader
- **Etablerer** kommunikationsmønsteret med baggrundsscriptet
- **Sikrer** at brugerne ser en funktionel udvidelse, før data indlæses
- **Sikrer** at brugerne ser en funktionsdygtig udvidelse før data indlæses
### Kald funktionen, udfør kaldet
Nu skal vi forbinde det hele, så når friske CO2-data kommer ind, opdaterer dit ikon automatisk med den rigtige farve. Det er som at forbinde den sidste kreds i en elektronisk enhed - pludselig fungerer alle de individuelle komponenter som et system.
Lad os nu forbinde det hele, sådan at når friske CO2-data kommer ind, opdateres ikonet automatisk med den rette farve. Det er som at forbinde den sidste kreds i en elektronisk enhed pludselig arbejder alle komponenterne som ét system.
Tilføj denne linje lige efter, at du får CO2-dataene fra API'en:
Tilføj denne linje lige efter, at du har fået CO2-dataene fra APIen:
```javascript
// After retrieving CO2 data from the API
// let CO2 = data.data[0].intensity.actual;
// Efter at have hentet CO2-data fra API'en
// lad CO2 = data.data[0].intensity.actual;
calculateColor(CO2);
```
**Denne integration opnår:**
- **Forbinder** API-datastrømmen med det visuelle indikator-system
- **Udløser** ikonopdateringer automatisk, når nye data ankommer
- **Sikrer** realtids visuel feedback baseret på den aktuelle CO2-intensitet
- **Bevarer** adskillelsen af ansvar mellem datahentning og visuel logik
**Dette integrerer:**
- **Forbinder** API-datastreamen med det visuelle indikator-system
- **Trigger** ikonopdateringer automatisk ved nye data
- **Sikrer** realtime visuel feedback baseret på gældende CO2-intensitet
- **Bevarer** adskillelsen mellem datahentning og visningslogik
Og til sidst, i `/dist/background.js`, tilføj lytteren for disse baggrundsaktionskald:
Og til sidst, i `/dist/background.js`, tilføj lytteren for disse baggrundsopkald:
```javascript
// Listen for messages from the content script
// Lyt efter beskeder fra indholdsscriptet
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.action.setIcon({ imageData: drawIcon(msg.value) });
}
});
// Draw dynamic icon using Canvas API
// Borrowed from energy lollipop extension - nice feature!
// Tegn dynamisk ikon ved hjælp af Canvas API
// Lånt fra energy lollipop-udvidelsen - flot funktion!
function drawIcon(value) {
// Create an offscreen canvas for better performance
// Opret et offscreen canvas for bedre ydeevne
const canvas = new OffscreenCanvas(200, 200);
const context = canvas.getContext('2d');
// Draw a colored circle representing carbon intensity
// Tegn en farvet cirkel, der repræsenterer kulstofintensitet
context.beginPath();
context.fillStyle = value.color;
context.arc(100, 100, 50, 0, 2 * Math.PI);
context.fill();
// Return the image data for the browser icon
// Returner billeddata til browserikonet
return context.getImageData(50, 50, 100, 100);
}
```
**Hvad dette baggrundsscript gør:**
- **Lytter** til beskeder fra dit hovedscript (som en receptionist, der tager imod opkald)
- **Behandler** disse 'updateIcon'-anmodninger for at ændre dit værktøjslinjeikon
- **Opretter** nye ikoner dynamisk ved hjælp af Canvas API
**Dette baggrundsscripts opgaver:**
- **Lytter** efter beskeder fra dit hovedscript (som en receptionist der tager imod opkald)
- **Behandler** 'updateIcon'-anmodninger for at ændre toolbar-ikonet
- **Opretter** nye ikoner on-the-fly med Canvas API
- **Tegner** en simpel farvet cirkel, der viser den aktuelle CO2-intensitet
- **Opdaterer** din browser-værktøjslinje med det nye ikon
- **Bruger** OffscreenCanvas for glat ydeevne (ingen UI-blokering)
✅ Du vil lære mere om Canvas API i [Space Game-lektionerne](../../6-space-game/2-drawing-to-canvas/README.md).
**Tid til at teste din udvidelse:**
- **Opdaterer** din browser toolbar med det friske ikon
- **Bruger** OffscreenCanvas for jævn ydeevne (uden UI-blokering)
✅ Du vil lære mere om Canvas APIen i [Space Game lektionerne](../../6-space-game/2-drawing-to-canvas/README.md).
```mermaid
sequenceDiagram
participant CS as Indholdsscript
participant BG as Baggrundsscript
participant Canvas as OffscreenCanvas
participant Browser as Browserikon
CS->>BG: sendMessage({action: 'updateIcon', color})
BG->>Canvas: new OffscreenCanvas(200, 200)
Canvas->>Canvas: getContext('2d')
Canvas->>Canvas: beginPath() + fillStyle + arc()
Canvas->>Canvas: fill() + getImageData()
Canvas->>BG: Returner billeddata
BG->>Browser: chrome.action.setIcon(imageData)
Browser->>Browser: Opdater værktøjslinjeikon
```
### 🔄 **Pædagogisk tjek-ind**
**Fuldstændig udvidelsesforståelse**: Bekræft din beherskelse af hele systemet:
- ✅ Hvordan fungerer beskedudveksling mellem forskellige udvidelsesscripts?
- ✅ Hvorfor bruger vi OffscreenCanvas i stedet for almindelig Canvas for ydeevne?
- ✅ Hvilken rolle spiller Chrome Runtime API i extensionsarkitekturen?
- ✅ Hvordan kortlægger farveberegningsalgoritmen data til visuel feedback?
**Ydeevneovervejelser**: Din extension demonstrerer nu:
- **Effektiv beskedudveksling**: Ren kommunikation mellem scriptkontekster
- **Optimeret rendering**: OffscreenCanvas forhindrer blokering af UI
- **Opdateringer i realtid**: Dynamiske ikonændringer baseret på live data
- **Hukommelsesstyring**: Korrekt oprydning og håndtering af ressourcer
**Tid til at teste din extension:**
- **Byg** alt med `npm run build`
- **Genindlæs** din udvidelse i browseren (glem ikke dette trin)
- **Åbn** din udvidelse og se ikonet skifte farve
- **Tjek** hvordan det reagerer på rigtige CO2-data fra hele verden
- **Genindlæs** din extension i browseren (glem ikke dette trin)
- **Åbn** din extension og se ikonet skifte farve
- **Tjek** hvordan den reagerer på virkelig CO2-data fra hele verden
Nu vil du ved et øjekast vide, om det er et godt tidspunkt at vaske tøj, eller om du skal vente på renere energi. Du har lige bygget noget virkelig nyttigt og lært om browserens ydeevne undervejs.
Nu kan du på et øjeblik se, om det er et godt tidspunkt til den vask eller om du skal vente på renere energi. Du har lige bygget noget virkelig nyttigt og lært om browserens ydeevne undervejs.
## GitHub Copilot Agent Challenge 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstanden til at gennemføre følgende udfordring:
**Beskrivelse:** Forbedr browserudvidelsens ydeevneovervågningsfunktioner ved at tilføje en funktion, der sporer og viser indlæsningstider for forskellige komponenter i udvidelsen.
**Beskrivelse:** Udvid browserextensionens ydeevneovervågningsfunktioner ved at tilføje en funktion, der sporer og viser indlæsningstider for forskellige komponenter i extensionen.
**Opgave:** Opret et ydeevneovervågningssystem for browserudvidelsen, der måler og logger den tid, det tager at hente CO2-data fra API'en, beregne farver og opdatere ikonet. Tilføj en funktion kaldet `performanceTracker`, der bruger Performance API til at måle disse operationer og viser resultaterne i browserens konsol med tidsstempler og varighedsmetrikker.
**Prompt:** Opret et ydeevneovervågningssystem til browserextensionen, som måler og logger den tid, det tager at hente CO2-data fra API'en, beregne farver og opdatere ikonet. Tilføj en funktion kaldet `performanceTracker`, der bruger Performance API til at måle disse operationer og viser resultaterne i browserkonsollen med tidsstempler og varighedsmål.
Lær mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
s mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Her er en interessant detektivopgave: vælg et par open source-websites, der har eksisteret i flere år (tænk Wikipedia, GitHub eller Stack Overflow), og dyk ned i deres commit-historik. Kan du finde steder, hvor de har foretaget forbedringer af ydeevnen? Hvilke problemer blev ved med at dukke op?
**Din undersøgelsesmetode:**
- **Søg** i commit-beskeder efter ord som "optimere," "ydeevne," eller "hurtigere"
- **Se** efter mønstre - bliver de ved med at rette de samme typer problemer?
- **Identificer** de almindelige syndere, der gør websites langsomme
- **Del** hvad du opdager - andre udviklere kan lære af eksempler fra den virkelige verden
## Quiz efter forelæsning
Her er en interessant detektivmission: vælg et par open source-websteder, som har eksisteret i årevis (tænk Wikipedia, GitHub eller Stack Overflow), og kig i deres commit-historik. Kan du spotte, hvor de har lavet ydeevneforbedringer? Hvilke problemer dukkede gentagne gange op?
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/28)
## Gennemgang & Selvstudie
Overvej at tilmelde dig et [nyhedsbrev om ydeevne](https://perf.email/)
Undersøg nogle af de måder, browsere vurderer webydeevne på ved at kigge gennem ydeevnefanerne i deres webværktøjer. Finder du nogen væsentlige forskelle?
**Din undersøgelsesmetode:**
- **Søg** commit-beskeder efter ord som "optimize," "performance," eller "faster"
- **Se** efter mønstre fortsætter de med at løse de samme typer problemer?
- **Identificer** de almindelige syndere, som sænker websteder
- **Del** dine opdagelser andre udviklere lærer af virkelige tilfælde
## Post-Lecture Quiz
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/28)
## Gennemgang & Selvstudium
Overvej at tilmelde dig et [ydeevnenyhedsbrev](https://perf.email/)
Undersøg nogle af de måder, browsere vurderer webperformance på ved at kigge igennem performancefanerne i deres webværktøjer. Finder du store forskelle?
### ⚡ **Hvad du kan gøre de næste 5 minutter**
- [ ] Åbn browserens Jobliste (Shift+Esc i Chrome) for at se extensionens ressourceforbrug
- [ ] Brug DevTools' Performance-fane til at optage og analysere websideydelse
- [ ] Tjek browserens Extension-side for at se, hvilke extensions der påvirker opstartstid
- [ ] Prøv midlertidigt at deaktivere extensions for at se ydelsesforskelle
### 🎯 **Hvad du kan nå denne time**
- [ ] Færdiggør post-lesson quizzen og forstå performancekoncepter
- [ ] Implementer et baggrundsscript til din browserextension
- [ ] Lær at bruge browser.alarms til effektive baggrundsopgaver
- [ ] Øv messaging mellem content scripts og background scripts
- [ ] Mål og optimer din extensions ressourceforbrug
### 📅 **Din ugentlige ydeevnerejse**
- [ ] Færdiggør en højtydende browserextension med baggrundsfunktionalitet
- [ ] Mestring af service workers og moderne extensionsarkitektur
- [ ] Implementer effektive datasynkroniserings- og cache-strategier
- [ ] Lær avancerede debuggingteknikker for extension-ydeevne
- [ ] Optimer din extension for både funktionalitet og ressourceeffektivitet
- [ ] Skab omfattende tests for ydeevnescenarier for extensions
### 🌟 **Din månedslange optimeringsmestring**
- [ ] Byg enterprise-grade browserextensions med optimal ydeevne
- [ ] Lær om Web Workers, Service Workers og moderne web-ydelse
- [ ] Bidrag til open source-projekter fokuseret på performanceoptimering
- [ ] Mestring af browserinternals og avancerede debuggingmetoder
- [ ] Skab ydeevneovervågningsværktøjer og best practice-guides
- [ ] Bliv en performanceekspert, som hjælper med at optimere webapplikationer
## 🎯 Din browserextensions mestringstidslinje
```mermaid
timeline
title Færdiggørelse af udvidelsesudviklingsforløb
section Grundlæggende ydeevne (20 minutter)
Browserprofilering: DevTools-mesterskab
: Tidslinjeanalyse
: Flaskehalsidentifikation
: Kritisk gengivelsessti
section Baggrundsopgaver (25 minutter)
Udvidelsesarkitektur: Beskedafsendelse
: Baggrundsscripts
: Runtime API brug
: Kommunikation på tværs af kontekster
section Visuel feedback (30 minutter)
Dynamisk UI: Farveberegningsalgoritmer
: Canvas API integration
: Ikongenerering
: Opdateringer i realtid
section Ydeevneoptimering (35 minutter)
Effektiv kode: Asynkrone operationer
: Hukommelsesstyring
: Ressourceoprydning
: Ydeevneovervågning
section Produktionsklar (45 minutter)
Finpudsning & testning: Kompatibilitet på tværs af browsere
: Fejlhåndtering
: Brugeroplevelse
: Ydeevnevalidering
section Avancerede funktioner (1 uge)
Udvidelsesøkosystem: Chrome Web Store
: Brugerfeedback
: Analyseintegration
: Opdateringsstyring
section Professionel udvikling (2 uger)
Enterprise-udvidelser: Teamsamarbejde
: Kodereviews
: CI/CD-pipelines
: Sikkerhedsrevisioner
section Ekspertmestring (1 måned)
Platformsekspertise: Avancerede Chrome API'er
: Ydeevneoptimering
: Arkitekturmodeller
: Open source-bidrag
```
### 🛠️ Dit komplette værktøjssæt til extension-udvikling
Efter at have gennemført denne trilogi har du nu mestring af:
- **Browserarkitektur**: Dybt kendskab til, hvordan extensions integreres med browsersystemer
- **Ydeevneprofilering**: Evne til at identificere og rette flaskehalse med udviklerværktøjer
- **Asynkron programmering**: Moderne JavaScript-mønstre for responsive, ikke-blokerende operationer
- **API-integration**: Hentning af ekstern data med autentifikation og fejlbehandling
- **Visuelt design**: Dynamiske UI-opdateringer og Canvas-baseret grafikgenerering
- **Message Passing**: Kommunikation mellem scripts i extensionsarkitektur
- **Brugeroplevelse**: Indlæsningstilstande, fejlbehandling og intuitive interaktioner
- **Produktionsfærdigheder**: Testning, debugging og optimering til real-world deployment
**Virkelige anvendelser**: Dine extensionudviklingsevner anvendes direkte til:
- **Progressive Web Apps**: Lignende arkitektur og ydeevnemønstre
- **Electron Desktop Apps**: Cross-platform applikationer baseret på webteknologier
- **Mobile Hybrid Apps**: Cordova/PhoneGap-udvikling med web-APIer
- **Enterprise Webapplikationer**: Komplekse dashboards og produktivitetsværktøjer
- **Chrome DevTools Extensions**: Avancerede udviklerværktøjer og debugging
- **Web API-integration**: Enhver applikation, der kommunikerer med eksterne tjenester
**Professionel påvirkning**: Du kan nu:
- **Bygge** produktionsklare browserextensions fra koncept til deployment
- **Optimere** webapplikationers ydeevne vha. industristandard profileringværktøjer
- **Arkitektere** skalerbare systemer med korrekt separering af ansvar
- **Debugge** komplekse asynkrone operationer og kommunikation på tværs af kontekster
- **Bidrage** til open source extension-projekter og browserstandarder
**Næste niveau muligheder**:
- **Chrome Web Store-udvikler**: Publicer extensions til millioner af brugere
- **Web Performance Engineer**: Specialiser dig i optimering og brugeroplevelse
- **Browser Platform Developer**: Bidrag til udvikling af browsermotorer
- **Extension Framework Skaber**: Byg værktøjer, der hjælper andre udviklere
- **Developer Relations**: Del viden gennem undervisning og indholdsskabelse
🌟 **Præmie låst op**: Du har bygget en komplet, funktionel browserextension, som demonstrerer professionel udviklingspraksis og moderne webstandarder!
## Opgave
[Analyser et site for ydeevne](assignment.md)
[Analyser et site for performance](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For vigtig information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -23,7 +23,7 @@ Denne udvidelse kan bruges ad hoc af en bruger, når en API-nøgle og regionskod
### Krediteringer
![en grøn browserudvidelse](../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![en grøn browserudvidelse](../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Krediteringer

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Ved at bruge tmrow's CO2 Signal API til at spore elforbrug, kan du bygge en browserudvidelse, så du får en påmindelse direkte i din browser om, hvor belastet din regions elforbrug er. Ved at bruge denne udvidelse ad hoc kan du træffe bedre beslutninger om dine aktiviteter baseret på denne information.
![extension screenshot](../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![extension screenshot](../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du bruge 'tre-punkts'-menuen øverst til højre i browseren for at finde panelet Udvidelser. Derfra vælger du 'Indlæs ikke-pakket' for at indlæse en ny udvidelse. Åbn mappen 'dist', når du bliver bedt om det, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal's API ([få en her via e-mail](https://www.co2signal.com/) - indtast din e-mail i boksen på denne side) og koden for din region ([find den her](http://api.electricitymap.org/v3/zones)) svarende til [Electricity Map](https://www.electricitymap.org/map) (i Boston bruger jeg for eksempel 'US-NEISO').
![installing](../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installing](../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og regionen er indtastet i udvidelsens grænseflade, bør den farvede prik i browserens udvidelsesbjælke ændre sig for at afspejle din regions energiforbrug og give dig en indikation af, hvilke energitunge aktiviteter der ville være passende at udføre. Konceptet bag dette 'prik'-system blev inspireret af [Energy Lollipop-udvidelsen](https://energylollipop.com/) for Californiens emissioner.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Ved hjælp af tmrow's CO2 Signal API til at overvåge elforbrug, kan du oprette en browserudvidelse, der giver dig en påmindelse direkte i din browser om elforbruget i dit område. Brug af denne ad hoc-udvidelse kan hjælpe dig med at træffe beslutninger om dine aktiviteter baseret på disse oplysninger.
![udvidelsesbillede](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![udvidelsesbillede](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du bruge menuen med 'tre prikker' i øverste højre hjørne af browseren for at finde panelet Udvidelser. Derfra vælger du 'Indlæs udvidelse uden komprimering' for at tilføje en ny udvidelse. Åbn mappen 'dist' ved prompten, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal API ([få en her via e-mail](https://www.co2signal.com/) - indtast din e-mail i feltet på denne side) og [koden for din region](http://api.electricitymap.org/v3/zones), som svarer til [Electricity Map](https://www.electricitymap.org/map) (i Boston, for eksempel, bruger jeg 'US-NEISO').
![installation](../../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installation](../../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og regionen er indtastet i udvidelsens interface, bør den farvede prik i browserens udvidelsesbjælke ændre sig for at afspejle energiforbruget i dit område og give dig en indikator for, hvilke energikrævende aktiviteter det ville være passende at udføre. Konceptet bag dette 'prik'-system blev inspireret af [Energy Lollipop-udvidelsen](https://energylollipop.com/) for californiske emissioner.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Ved at bruge tmrow's CO2 Signal API til at spore elforbrug, bygger vi en browserudvidelse, der kan minde dig om, hvor belastet strømforbruget i dit område er. Ved at bruge denne udvidelse kan du træffe beslutninger om dine aktiviteter baseret på denne information.
![Udvidelse skærmbillede](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![Udvidelse skærmbillede](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du bruge 'tre prikker'-menuen øverst til højre i browseren for at finde udvidelsespanelet. Derfra skal du vælge 'Load unpacked' for at indlæse en ny udvidelse. Når du bliver bedt om det, skal du åbne 'dist'-mappen, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal ([fås her via e-mail](https://www.co2signal.com/) - indtast din e-mail i boksen på denne side) og [koden for dit område](http://api.electricitymap.org/v3/zones) fra [Electricity Map](https://www.electricitymap.org/map) (for eksempel bruger jeg 'US-NEISO' for Boston).
![installering](../../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installering](../../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og området er indtastet i udvidelsens interface, bør den farvede prik i browserens udvidelsesbjælke ændre sig for at afspejle energiforbruget i dit område og give dig en indikator for, hvilke energiintensive aktiviteter der er passende. Konceptet bag dette 'prik'-system blev inspireret af [Energy Lollipop Extension](https://energylollipop.com/) for Californiske emissioner.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Du vil bruge tmrow's Signal CO2 API til at overvåge elforbruget og oprette en browserudvidelse, så du kan få en påmindelse direkte i din browser om, hvor tungt elforbruget er i din region. Brug af denne ad hoc-udvidelse vil hjælpe dig med at vurdere dine aktiviteter baseret på disse oplysninger.
![skærmbillede af udvidelsen](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![skærmbillede af udvidelsen](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du bruge "tre prikker"-menuen i øverste højre hjørne af browseren for at finde panelet Udvidelser. Hvis det ikke allerede er aktiveret, skal du aktivere Udviklertilstand (nederst til venstre). Vælg "Indlæs pakket udvidelse" for at tilføje en ny udvidelse. Åbn mappen "dist" ved prompten, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal API'en (du kan [få den her via e-mail](https://www.co2signal.com/) - indtast din e-mail i feltet på denne side) og [koden for din region](http://api.electricitymap.org/v3/zones), som svarer til [elektricitetskortet](https://www.electricitymap.org/map) (for eksempel er koden for Boston "US-NEISO").
![installation](../../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installation](../../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og regionen er indtastet i udvidelsens interface, bør den farvede prik i browserens udvidelsesbjælke ændre sig for at afspejle regionens energiforbrug og give en indikation af, hvilke aktiviteter med højt energiforbrug der ville være passende at udføre. Konceptet bag dette "prik"-system er inspireret af [Energy Lollipop-udvidelsen](https://energylollipop.com/) for Californiens emissioner.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Byg en browser-udvidelse, der fungerer som en påmindelse om, hvor meget energi der bruges i dit område, ved at spore strømforbruget med tmrow's CO2 Signal API. Ved at bruge denne udvidelse ad hoc kan du træffe beslutninger om dine aktiviteter baseret på denne information.
![extension screenshot](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![extension screenshot](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du finde "Udvidelser"-panelet via "tre prikker"-menuen øverst til højre i browseren. Derfra vælger du "Load Unpacked" og indlæser den nye udvidelse. Når du bliver bedt om det, skal du åbne "dist"-mappen, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal API ([få en her via e-mail](https://www.co2signal.com/) - indtast din e-mail i boksen på siden) og en [kode for din region](http://api.electricitymap.org/v3/zones), som er kompatibel med [Electricity Map](https://www.electricitymap.org/map) (for eksempel bruges 'US-NEISO' i Boston).
![installing](../../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installing](../../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når du har indtastet API-nøglen og din region i udvidelsens interface, vil en farvet prik vises i browserens udvidelsesbjælke. Denne prik ændrer farve for at afspejle energiforbruget i dit område og hjælper dig med at vurdere, hvilke aktiviteter der kræver energi, der er passende at udføre. Konceptet med dette "prik"-system blev inspireret af [Energy Lollipop extension](https://energylollipop.com/) for emissioner i Californien.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Ved at bruge CO2 Signal API fra tmrow til at overvåge elforbrug, kan du bygge en browserudvidelse, der giver dig advarsler i din browser om, hvor belastet dit områdes elforbrug er. Denne udvidelse kan hjælpe dig med at tage beslutninger om dine aktiviteter baseret på disse oplysninger.
![skærmbillede af browserudvidelse](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![skærmbillede af browserudvidelse](../../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere i Edge skal du bruge menuen med 'tre prikker' i øverste højre hjørne af browseren for at finde panelet Udvidelser. Derfra vælger du 'Load Unpacked' for at indlæse den nye udvidelse. Åbn mappen 'dist', når du bliver bedt om det, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal API ([få en her via e-mail](https://www.co2signal.com/) - indtast din e-mail i boksen på denne side) og [koden for dit område](http://api.electricitymap.org/v3/zones), som svarer til [Electricity Map](https://www.electricitymap.org/map) (i Boston bruger jeg for eksempel 'US-NEISO').
![downloader](../../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![downloader](../../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og området er indtastet i udvidelsens interface, vil en farvet prik i browserens udvidelsesbjælke ændre sig for at afspejle dit områdes energiforbrug og give dig anbefalinger om passende aktiviteter baseret på belastningen. Konceptet bag dette 'prik'-system blev inspireret af [Energy Lollipop browserudvidelsen](https://energylollipop.com/) for Californiens emissioner.

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
Ved at bruge tmrow's CO2 Signal API til at spore elforbrug, kan du bygge en browserudvidelse, så du får en påmindelse direkte i din browser om, hvor belastet din regions elforbrug er. Ved at bruge denne udvidelse ad hoc kan du træffe beslutninger om dine aktiviteter baseret på denne information.
![udvidelsesskærmbillede](../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3875e1bc9405edd45a3d2e02963e48900adb91926a62a5807.da.png)
![udvidelsesskærmbillede](../../../../translated_images/extension-screenshot.0e7f5bfa110e92e3.da.png)
## Kom godt i gang
@ -31,7 +31,7 @@ npm run build
For at installere på Edge skal du bruge menuen med de 'tre prikker' øverst til højre i browseren for at finde panelet Udvidelser. Derfra skal du vælge 'Indlæs upakket' for at indlæse en ny udvidelse. Åbn mappen 'dist', når du bliver bedt om det, og udvidelsen vil blive indlæst. For at bruge den skal du have en API-nøgle til CO2 Signal's API ([få en her via e-mail](https://www.co2signal.com/) - indtast din e-mail i boksen på denne side) og koden for din region ([find den her](http://api.electricitymap.org/v3/zones)) svarende til [Electricity Map](https://www.electricitymap.org/map) (i Boston bruger jeg for eksempel 'US-NEISO').
![installation](../../../../translated_images/install-on-edge.78634f02842c48283726c531998679a6f03a45556b2ee99d8ff231fe41446324.da.png)
![installation](../../../../translated_images/install-on-edge.78634f02842c4828.da.png)
Når API-nøglen og regionen er indtastet i udvidelsens grænseflade, bør den farvede prik i browserens udvidelsesbjælke ændre sig for at afspejle din regions energiforbrug og give dig en indikation af, hvilke energitunge aktiviteter der ville være passende at udføre. Konceptet bag dette 'prik'-system blev givet til mig af [Energy Lollipop-udvidelsen](https://energylollipop.com/) for Californiens emissioner.

@ -1,59 +1,142 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "862f7f2ef320f6f8950fae379e6ece45",
"translation_date": "2025-10-23T22:12:09+00:00",
"original_hash": "a6332a7bb4d0be3bfd24199c83993777",
"translation_date": "2026-01-06T23:41:14+00:00",
"source_file": "6-space-game/1-introduction/README.md",
"language_code": "da"
}
-->
# Byg et rumspil del 1: Introduktion
![Animation af rumspil, der viser gameplay](../../../../6-space-game/images/pewpew.gif)
Ligesom NASAs mission control koordinerer flere systemer under en rumopsendelse, skal vi bygge et rumspil, der demonstrerer, hvordan forskellige dele af et program kan arbejde sammen problemfrit. Mens vi skaber noget, du faktisk kan spille, vil du lære essentielle programmeringskoncepter, der gælder for ethvert softwareprojekt.
Vi vil udforske to grundlæggende tilgange til at organisere kode: arv og komposition. Disse er ikke bare akademiske begreber det er de samme mønstre, der driver alt fra videospil til banksystemer. Vi vil også implementere et kommunikationssystem kaldet pub/sub, der fungerer som kommunikationsnetværkene, der bruges i rumfartøjer, og som tillader forskellige komponenter at dele information uden at skabe afhængigheder.
Ved slutningen af denne serie vil du forstå, hvordan man bygger applikationer, der kan skalere og udvikle sig uanset om du udvikler spil, webapplikationer eller andre softwaresystemer.
```mermaid
journey
title Din Spiludviklingsrejse
section Fundament
Lær spilaritektur: 3: Student
Forstå arv: 4: Student
Udforsk sammensætning: 4: Student
section Kommunikation
Byg pub/sub system: 4: Student
Design hændelsesflow: 5: Student
Forbind komponenter: 5: Student
section Anvendelse
Opret spilelementer: 5: Student
Implementér mønstre: 5: Student
Planlæg spilstruktur: 5: Student
```
![Animeret illustration af rumspil, der viser gameplay](../../../../6-space-game/images/pewpew.gif)
Ligesom NASAs mission control koordinerer flere systemer under en rumopsendelse, skal vi bygge et rumspil, der demonstrerer, hvordan forskellige dele af et program kan arbejde sammen problemfrit. Mens du skaber noget, du faktisk kan spille, vil du lære essentielle programmeringskoncepter, der gælder for ethvert softwareprojekt.
Vi vil udforske to grundlæggende tilgange til at organisere kode: arv og sammensætning. Disse er ikke bare akademiske koncepter de er de samme mønstre, der driver alt fra videospil til banksystemer. Vi vil også implementere et kommunikationssystem kaldet pub/sub, som fungerer som kommunikationsnetværkene brugt i rumfartøjer, hvilket tillader forskellige komponenter at dele information uden at skabe afhængigheder.
Ved slutningen af denne serie vil du forstå, hvordan man bygger applikationer, der kan skaleres og udvikles uanset om du udvikler spil, webapplikationer eller andre softwaresystemer.
```mermaid
mindmap
root((Spil Arkitektur))
Object Organization
Inheritance
Composition
Class Hierarchies
Behavior Mixing
Communication Patterns
Pub/Sub System
Event Emitters
Message Passing
Loose Coupling
Game Objects
Properties (x, y)
Behaviors (move, collide)
Lifecycle Management
State Management
Design Patterns
Factory Functions
Observer Pattern
Component System
Event-Driven Architecture
Scalability
Modular Design
Maintainable Code
Testing Strategies
Performance Optimization
```
## Quiz før forelæsning
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/29)
## Arv og komposition i spiludvikling
## Arv og sammensætning i spiludvikling
Når projekter vokser i kompleksitet, bliver kodeorganisation kritisk. Det, der begynder som et simpelt script, kan blive svært at vedligeholde uden ordentlig struktur meget ligesom hvordan Apollo-missionerne krævede omhyggelig koordinering mellem tusindvis af komponenter.
Efterhånden som projekter bliver mere komplekse, bliver kodeorganisering kritisk. Det, der begynder som et enkelt script, kan blive svært at vedligeholde uden ordentlig struktur ligesom Apollo-missionerne krævede omhyggelig koordinering mellem tusindvis af komponenter.
Vi vil udforske to grundlæggende tilgange til at organisere kode: arv og komposition. Hver har sine unikke fordele, og forståelse af begge hjælper dig med at vælge den rigtige tilgang til forskellige situationer. Vi vil demonstrere disse koncepter gennem vores rumspil, hvor helte, fjender, power-ups og andre objekter skal interagere effektivt.
Vi vil udforske to grundlæggende tilgange til at organisere kode: arv og sammensætning. Begge har distinkte fordele, og at forstå begge hjælper dig med at vælge den rigtige tilgang til forskellige situationer. Vi vil demonstrere disse koncepter gennem vores rumspil, hvor helte, fjender, power-ups og andre objekter skal interagere effektivt.
✅ En af de mest berømte programmeringsbøger nogensinde handler om [designmønstre](https://en.wikipedia.org/wiki/Design_Patterns).
I ethvert spil har du `spilobjekter` de interaktive elementer, der befolker din spilverden. Helte, fjender, power-ups og visuelle effekter er alle spilobjekter. Hvert objekt eksisterer på specifikke skærmkoordinater ved hjælp af `x` og `y` værdier, ligesom at plotte punkter på et koordinatsystem.
I ethvert spil har du `spilobjekter` de interaktive elementer, der befolker din spilverden. Helte, fjender, power-ups og visuelle effekter er alle spilobjekter. Hvert eksisterer ved specifikke skærmkoordinater ved brug af `x` og `y` værdier, ligesom at plotte punkter på et koordinatplan.
På trods af deres visuelle forskelle deler disse objekter ofte grundlæggende adfærd:
- **De eksisterer et sted** Hvert objekt har x- og y-koordinater, så spillet ved, hvor det skal tegnes
- **Mange kan bevæge sig rundt** Helte løber, fjender jager, kugler flyver hen over skærmen
- **De har en levetid** Nogle forbliver for evigt, andre (som eksplosioner) vises kortvarigt og forsvinder
- **De reagerer på ting** Når ting kolliderer, samles power-ups op, og livsbjælker opdateres
✅ Tænk på et spil som Pac-Man. Kan du identificere de fire objekttyper, der er nævnt ovenfor, i dette spil?
### Udtryk adfærd gennem kode
- **Mange kan bevæge sig** Helte løber, fjender forfølger, kugler flyver over skærmen
- **De har en levetid** Nogle bliver omkring for evigt, andre (som eksplosioner) vises kortvarigt og forsvinder
- **De reagerer på ting** Når ting kolliderer, samles power-ups op, opdateres helbredsbarer
✅ Tænk på et spil som Pac-Man. Kan du identificere de fire objekt-typer nævnt ovenfor i dette spil?
```mermaid
classDiagram
class GameObject {
+x: number
+y: number
+type: string
+exists_somewhere()
}
class MovableObject {
+moveTo(x, y)
+kan_bevæge_sig_rundt()
}
class TemporaryObject {
+levetid: number
+har_levetid()
}
class InteractiveObject {
+vedKollision()
+reagerer_på_ting()
}
GameObject <|-- MovableObject
GameObject <|-- TemporaryObject
GameObject <|-- InteractiveObject
MovableObject <|-- Hero
MovableObject <|-- Enemy
MovableObject <|-- Bullet
TemporaryObject <|-- PowerUp
TemporaryObject <|-- Explosion
InteractiveObject <|-- Collectible
InteractiveObject <|-- Obstacle
```
### At udtrykke adfærd gennem kode
Nu hvor du forstår de fælles adfærd, som spilobjekter deler, lad os udforske, hvordan man implementerer disse adfærd i JavaScript. Du kan udtrykke objektadfærd gennem metoder, der er knyttet til enten klasser eller individuelle objekter, og der er flere tilgange at vælge imellem.
Nu hvor du forstår de fælles adfærdsmønstre spilobjekter deler, lad os udforske, hvordan man implementerer disse adfærd i JavaScript. Du kan udtrykke objekters adfærd gennem metoder knyttet til enten klasser eller individuelle objekter, og der er flere tilgange at vælge imellem.
**Den klassebaserede tilgang**
Klasser og arv giver en struktureret tilgang til at organisere spilobjekter. Ligesom det taksonomiske klassifikationssystem udviklet af Carl Linnaeus, starter du med en basisklasse, der indeholder fælles egenskaber, og derefter opretter du specialiserede klasser, der arver disse fundamenter, mens de tilføjer specifikke funktioner.
Klasser og arv giver en struktureret måde at organisere spilobjekter på. Ligesom det taksonomiske klassifikationssystem udviklet af Carl Linnaeus, starter du med en basisklasse, der indeholder fælles egenskaber, og skaber derefter specialiserede klasser, der arver disse fundamentale træk og tilføjer specifikke kapaciteter.
✅ Arv er et vigtigt koncept at forstå. Læs mere i [MDNs artikel om arv](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
✅ Arv er et vigtigt koncept at forstå. Lær mere i [MDNs artikel om arv](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).
Her er, hvordan du kan implementere spilobjekter ved hjælp af klasser og arv:
Sådan kan du implementere spilobjekter ved hjælp af klasser og arv:
```javascript
// Step 1: Create the base GameObject class
// Trin 1: Opret basis GameObject klassen
class GameObject {
constructor(x, y, type) {
this.x = x;
@ -63,19 +146,19 @@ class GameObject {
}
```
**Lad os bryde dette ned trin for trin:**
- Vi opretter en grundlæggende skabelon, som hvert spilobjekt kan bruge
- Konstruktøren gemmer, hvor objektet er (`x`, `y`), og hvilken type det er
- Dette bliver fundamentet, som alle dine spilobjekter vil bygge på
**Lad os bryde det ned trin for trin:**
- Vi skaber en grundlæggende skabelon, som alle spilobjekter kan bruge
- Konstruktøren gemmer, hvor objektet er (`x`, `y`) og hvilken type det er
- Dette bliver fundamentet, som alle dine spilobjekter vil bygge videre
```javascript
// Step 2: Add movement capability through inheritance
// Trin 2: Tilføj bevægelsesmulighed gennem arv
class Movable extends GameObject {
constructor(x, y, type) {
super(x, y, type); // Call parent constructor
super(x, y, type); // Kald forældres konstruktør
}
// Add the ability to move to a new position
// Tilføj evnen til at flytte til en ny position
moveTo(x, y) {
this.x = x;
this.y = y;
@ -85,45 +168,45 @@ class Movable extends GameObject {
**I ovenstående har vi:**
- **Udvidet** GameObject-klassen for at tilføje bevægelsesfunktionalitet
- **Kaldt** forældrekonstruktøren ved hjælp af `super()` for at initialisere arvede egenskaber
- **Tilføjet** en `moveTo()` metode, der opdaterer objektets position
- **Kaldt** forældrekonstruktøren ved brug af `super()` for at initialisere arvede egenskaber
- **Tilføjet** en `moveTo()` metode, som opdaterer objektets position
```javascript
// Step 3: Create specific game object types
// Trin 3: Opret specifikke spilobjekttyper
class Hero extends Movable {
constructor(x, y) {
super(x, y, 'Hero'); // Set type automatically
super(x, y, 'Hero'); // Indstil type automatisk
}
}
class Tree extends GameObject {
constructor(x, y) {
super(x, y, 'Tree'); // Trees don't need movement
super(x, y, 'Tree'); // Træer behøver ikke at bevæge sig
}
}
// Step 4: Use your game objects
// Trin 4: Brug dine spilobjekter
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // Hero can move!
hero.moveTo(5, 5); // Helten kan bevæge sig!
const tree = new Tree(10, 15);
// tree.moveTo() would cause an error - trees can't move
// tree.moveTo() ville forårsage en fejl - træer kan ikke bevæge sig
```
**Forståelse af disse koncepter:**
**At forstå disse koncepter:**
- **Skaber** specialiserede objekttyper, der arver passende adfærd
- **Demonstrerer** hvordan arv tillader selektiv funktionalitet
- **Viser** at helte kan bevæge sig, mens træer forbliver stationære
- **Illustrerer** hvordan klassehierarkiet forhindrer upassende handlinger
- **Demonstrerer**, hvordan arv tillader selektiv funktionsinklusion
- **Viser**, at helte kan bevæge sig, mens træer forbliver stationære
- **Illustrerer**, hvordan klassehierarkiet forhindrer upassende handlinger
✅ Tag et øjeblik til at forestille dig en Pac-Man helt (Inky, Pinky eller Blinky, for eksempel) og hvordan det ville blive skrevet i JavaScript.
✅ Tag et par minutter til at genoverveje en Pac-Man helt (Inky, Pinky eller Blinky for eksempel) og hvordan den ville blive skrevet i JavaScript.
**Kompositionsmetoden**
**Den sammensatte tilgang**
Komposition følger en modulær designfilosofi, ligesom ingeniører designer rumfartøjer med udskiftelige komponenter. I stedet for at arve fra en forældresklasse kombinerer du specifikke adfærd for at skabe objekter med præcis den funktionalitet, de har brug for. Denne tilgang tilbyder fleksibilitet uden stive hierarkiske begrænsninger.
Sammensætning følger en modulær designfilosofi, ligesom hvordan ingeniører designer rumfartøjer med udskiftelige komponenter. I stedet for at arve fra en forældrekasse kombinerer du specifikke adfærd for at skabe objekter med præcis den funktionalitet, de behøver. Denne tilgang tilbyder fleksibilitet uden stive hierarkiske begrænsninger.
```javascript
// Step 1: Create base behavior objects
// Trin 1: Opret basis adfærdsobjekter
const gameObject = {
x: 0,
y: 0,
@ -138,16 +221,16 @@ const movable = {
};
```
**Her er, hvad denne kode gør:**
- **Definerer** et grundlæggende `gameObject` med position og type egenskaber
- **Opretter** et separat `movable` adfærdsobjekt med bevægelsesfunktionalitet
**Dette gør koden:**
- **Definerer** et basis `gameObject` med position og typeegenskaber
- **Skaber** et separat `movable` adfærdsobjekt med bevægelsesfunktionalitet
- **Adskiller** bekymringer ved at holde positionsdata og bevægelseslogik uafhængige
```javascript
// Step 2: Compose objects by combining behaviors
// Trin 2: Sammensæt objekter ved at kombinere adfærd
const movableObject = { ...gameObject, ...movable };
// Step 3: Create factory functions for different object types
// Trin 3: Opret fabriksfunktioner for forskellige objekttyper
function createHero(x, y) {
return {
...movableObject,
@ -168,67 +251,126 @@ function createStatic(x, y, type) {
```
**I ovenstående har vi:**
- **Kombineret** basisobjektegenskaber med bevægelsesadfærd ved hjælp af spread-syntaks
- **Oprettet** fabrikfunktioner, der returnerer tilpassede objekter
- **Kombineret** basisobjektegenskaber med bevægelsesadfærd ved brug af spread-syntax
- **Skabt** fabrikationsfunktioner, der returnerer tilpassede objekter
- **Muliggjort** fleksibel objektoprettelse uden stive klassehierarkier
- **Givet** objekter præcis den adfærd, de har brug for
- **Givet** objekter præcis de adfærd, de behøver
```javascript
// Step 4: Create and use your composed objects
// Trin 4: Opret og brug dine sammensatte objekter
const hero = createHero(10, 10);
hero.moveTo(5, 5); // Works perfectly!
hero.moveTo(5, 5); // Fungerer perfekt!
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() is undefined - no movement behavior was composed
// tree.moveTo() er udefineret - ingen bevægelsesadfærd blev sammensat
```
**Vigtige punkter at huske:**
- **Komponerer** objekter ved at blande adfærd i stedet for at arve dem
- **Tilbyder** mere fleksibilitet end stive arvhierarkier
- **Giver** objekter præcis de funktioner, de har brug for
- **Bruger** moderne JavaScript spread-syntaks for ren objektkombination
- **Sammensætter** objekter ved at blande adfærd fremfor at arve den
- **Giver** mere fleksibilitet end stive arvehierarkier
- **Tillader** objekter at have præcis de funktioner, de har brug for
- **Bruger** moderne JavaScript spread-syntax for ren kombination af objekter
```
**Which Pattern Should You Choose?**
> 💡 **Pro Tip**: Both patterns have their place in modern JavaScript development. Classes work well for clearly defined hierarchies, while composition shines when you need maximum flexibility.
>
**Here's when to use each approach:**
- **Choose** inheritance when you have clear "is-a" relationships (a Hero *is-a* Movable object)
- **Select** composition when you need "has-a" relationships (a Hero *has* movement abilities)
- **Consider** your team's preferences and project requirements
- **Remember** that you can mix both approaches in the same application
## Communication Patterns: The Pub/Sub System
As applications grow complex, managing communication between components becomes challenging. The publish-subscribe pattern (pub/sub) solves this problem using principles similar to radio broadcasting one transmitter can reach multiple receivers without knowing who's listening.
Consider what happens when a hero takes damage: the health bar updates, sound effects play, visual feedback appears. Rather than coupling the hero object directly to these systems, pub/sub allows the hero to broadcast a "damage taken" message. Any system that needs to respond can subscribe to this message type and react accordingly.
**Which Pattern Should You Choose?**
**Pub/Sub** stands for 'publish-subscribe'
```mermaid
quadrantChart
title Code Organization Patterns
x-axis Simple --> Complex
y-axis Rigid --> Flexible
quadrant-1 Advanced Composition
quadrant-2 Hybrid Approaches
quadrant-3 Basic Inheritance
quadrant-4 Modern Composition
Class Inheritance: [0.3, 0.2]
Interface Implementation: [0.6, 0.4]
Mixin Patterns: [0.7, 0.7]
Pure Composition: [0.8, 0.9]
Factory Functions: [0.5, 0.8]
Prototype Chain: [0.4, 0.3]
```
### Understanding the Pub/Sub Architecture
> 💡 **Pro-tip**: Begge mønstre har deres plads i moderne JavaScript-udvikling. Klasser fungerer godt til klart definerede hierarkier, mens sammensætning skinner, når du har brug for maksimal fleksibilitet.
>
**Her er hvornår du skal vælge hver tilgang:**
- **Vælg** arv, når du har klare "er-en" relationer (en Helt *er en* bevægelig genstand)
- **Vælg** sammensætning, når du har "har-en" relationer (en Helt *har* bevægelsesevner)
- **Overvej** dit teams præferencer og projektkrav
- **Husk**, at du kan blande begge tilgange i samme applikation
### 🔄 **Pædagogisk kontrol**
**Forståelse af objektorganisering**: Før vi bevæger os videre til kommunikationsmønstre, skal du sikre, at du kan:
- ✅ Forklare forskellen mellem arv og sammensætning
- ✅ Identificere hvornår man bruger klasser vs. fabrikationsfunktioner
- ✅ Forstå hvordan `super()` nøgleordet fungerer i arv
- ✅ Genkende fordelene ved hver tilgang i spiludvikling
**Hurtig selvtest**: Hvordan vil du skabe en flyvende fjende, der både kan bevæge sig og flyve?
- **Arvsmetode**: `class FlyingEnemy extends Movable`
- **Sammensætningsmetode**: `{ ...movable, ...flyable, ...gameObject }`
**Virkelighedsnær forbindelse**: Disse mønstre ses overalt:
- **React-komponenter**: Props (sammensætning) vs. klassearv
- **Spilmotorer**: Entity-component systemer bruger sammensætning
- **Mobilapps**: UI-rammer bruger ofte arvehierarkier
## Kommunikationsmønstre: Pub/Sub-systemet
Efterhånden som applikationer bliver komplekse, bliver det en udfordring at styre kommunikation mellem komponenter. Publish-subscribe-mønsteret (pub/sub) løser dette problem med principper, der ligner radioudsendelse én sender kan nå flere modtagere uden at vide, hvem der lytter.
Tænk på, hvad der sker, når en helt tager skade: helbredsbaren opdateres, lydeffekter afspilles, visuel feedback vises. I stedet for at koble heltens objekt direkte til disse systemer, tillader pub/sub helten at udsende en "skade taget"-besked. Enhver system, der skal reagere, kan abonnere på denne meddelelsestype og reagere derefter.
**Pub/Sub** står for 'publish-subscribe' (udgiv-abonnér)
```mermaid
flowchart TD
A[Helten tager skade] --> B[Udgiv: HERO_DAMAGED]
B --> C[Hændelsessystem]
C --> D[Abonnent på sundhedsmåler]
C --> E[Abonnent på lyssystem]
C --> F[Abonnent på visuelle effekter]
C --> G[Abonnent på præstationssystem]
D --> H[Opdater helbredsvisning]
E --> I[Afspil skadeslyd]
F --> J[Vis rød flash]
G --> K[Tjek overlevelsespremier]
style A fill:#ffebee
style B fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
style I fill:#fff3e0
style J fill:#fff3e0
style K fill:#fff3e0
```
### Forstå pub/sub-arkitekturen
The pub/sub pattern keeps different parts of your application loosely coupled, meaning they can work together without being directly dependent on each other. This separation makes your code more maintainable, testable, and flexible to changes.
Pub/sub-mønsteret holder forskellige dele af din applikation løst koblet, hvilket betyder, at de kan arbejde sammen uden direkte afhængighed af hinanden. Denne adskillelse gør din kode nemmere at vedligeholde, teste og fleksibel over for ændringer.
**The key players in pub/sub:**
- **Messages** Simple text labels like `'PLAYER_SCORED'` that describe what happened (plus any extra info)
- **Publishers** The objects that shout out "Something happened!" to anyone who's listening
- **Subscribers** The objects that say "I care about that event" and react when it happens
- **Event System** The middleman that makes sure messages get to the right listeners
**De vigtigste aktører i pub/sub:**
- **Beskeder** Enkle tekstlabels som `'PLAYER_SCORED'`, der beskriver, hvad der skete (plus eventuel ekstra info)
- **Udgivere** De objekter, der råber "Noget skete!" til alle, der lytter
- **Abonnenter** De objekter, der siger "Jeg interesserer mig for denne begivenhed" og reagerer, når den sker
- **Begivenhedssystem** Mellemmanden, der sørger for, at beskeder når de rigtige lyttere
### Building an Event System
### Byg et begivenhedssystem
Let's create a simple but powerful event system that demonstrates these concepts:
Lad os skabe et simpelt, men kraftfuldt begivenhedssystem, der demonstrerer disse koncepter:
```javascript
// Step 1: Create the EventEmitter class
// Trin 1: Opret EventEmitter-klassen
class EventEmitter {
constructor() {
this.listeners = {}; // Store all event listeners
this.listeners = {}; // Gem alle begivenhedslyttere
}
// Register a listener for a specific message type
// Registrer en lytter for en specifik beskedtype
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
@ -236,7 +378,7 @@ class EventEmitter {
this.listeners[message].push(listener);
}
// Send a message to all registered listeners
// Send en besked til alle registrerede lyttere
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(listener => {
@ -247,37 +389,37 @@ class EventEmitter {
}
```
**Nedbrydning af, hvad der sker her:**
- **Opretter** et centralt begivenhedsstyringssystem ved hjælp af en simpel klasse
**Nedbrydning af hvad der sker her:**
- **Skaber** et centralt event management system ved brug af en simpel klasse
- **Gemmer** lyttere i et objekt organiseret efter beskedtype
- **Registrerer** nye lyttere ved hjælp af metoden `on()`
- **Sender** beskeder til alle interesserede lyttere ved hjælp af `emit()`
- **Understøtter** valgfrie dataloads til at sende relevante oplysninger
- **Registrerer** nye lyttere ved hjælp af `on()` metoden
- **Sender** beskeder til alle interesserede lyttere via `emit()`
- **Understøtter** valgfrie datapayloads til at sende relevant information
### Sæt det hele sammen: Et praktisk eksempel
### Saml det hele: Et praktisk eksempel
Okay, lad os se det i aktion! Vi bygger et simpelt bevægelsessystem, der viser, hvor rent og fleksibelt pub/sub kan være:
Lad os se det i aktion! Vi bygger et simpelt bevægelsessystem, der viser, hvor rent og fleksibelt pub/sub kan være:
```javascript
// Step 1: Define your message types
// Trin 1: Definer dine beskedtyper
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
// Step 2: Create your event system and game objects
// Trin 2: Opret dit begivenhedssystem og spilobjekter
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);
```
**Her er, hvad denne kode gør:**
- **Definerer** et konstantobjekt for at forhindre tastefejl i beskednavne
- **Opretter** en event emitter-instans til at håndtere al kommunikation
- **Initialiserer** et helteobjekt på startpositionen
**Dette gør koden:**
- **Definerer** et konstant-objekt for at undgå tastefejl i meddelelsesnavne
- **Skaber** en event emitter-instans til at håndtere al kommunikation
- **Initialiserer** et helt-objekt på startpositionen
```javascript
// Step 3: Set up event listeners (subscribers)
// Trin 3: Opsæt begivenhedslyttere (abonnenter)
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
@ -289,14 +431,14 @@ eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
});
```
**I ovenstående har vi:**
- **Registreret** begivenhedslyttere, der reagerer på bevægelsesbeskeder
- **Opdateret** heltens position baseret på bevægelsesretningen
- **Tilføjet** console logging for at spore ændringer i heltens position
- **Adskilt** bevægelseslogikken fra inputhåndteringen
**Vi har i ovenstående:**
- **Registreret** eventlyttere, der reagerer på bevægelsesbeskeder
- **Opdateret** heltens position baseret på bevægelsesretning
- **Tilføjet** konsollogning for at spore heltens positionsændringer
- **Adskilt** bevægelseslogik fra inputhåndtering
```javascript
// Step 4: Connect keyboard input to events (publishers)
// Trin 4: Forbind tastaturinput til hændelser (udgivere)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
@ -310,60 +452,211 @@ window.addEventListener('keydown', (event) => {
```
**Forståelse af disse koncepter:**
- **Forbinder** tastaturinput til spilbegivenheder uden tæt kobling
- **Muliggør** inputsystemet at kommunikere med spilobjekter indirekte
- **Tillader** flere systemer at reagere på de samme tastaturbegivenheder
- **Gør** det nemt at ændre tastebindinger eller tilføje nye inputmetoder
> 💡 **Pro Tip**: Skønheden ved dette mønster er fleksibilitet! Du kan nemt tilføje lydeffekter, skærmrystelser eller partikeleffekter ved blot at tilføje flere begivenhedslyttere ingen grund til at ændre den eksisterende tastatur- eller bevægelseskode.
- **Forbinder** tastaturinput med spilevents uden stram kobling
- **Muliggør**, at inputsystemet kommunikerer indirekte med spilobjekter
- **Tillader**, at flere systemer reagerer på de samme tastaturevents
- **Gør** det nemt at ændre tastaturbindinger eller tilføje nye inputmetoder
```mermaid
sequenceDiagram
participant User
participant Keyboard
participant EventEmitter
participant Hero
participant SoundSystem
participant Camera
User->>Keyboard: Trykker på PilVenstre
Keyboard->>EventEmitter: emit('HERO_MOVE_LEFT')
EventEmitter->>Hero: Bevæg til venstre 5 pixels
EventEmitter->>SoundSystem: Afspil fodtrin lyd
EventEmitter->>Camera: Følg helt
Hero->>Hero: Opdater position
SoundSystem->>SoundSystem: Afspil lyd
Camera->>Camera: Juster visningsvindue
```
> 💡 **Pro-tip**: Skønheden ved dette mønster er fleksibilitet! Du kan nemt tilføje lydeffekter, skærmrystelser eller partikeleffekter ved blot at tilføje flere eventlyttere uden at skulle ændre eksisterende tastatur- eller bevægelseskode.
>
**Her er hvorfor du vil elske denne tilgang:**
- Det bliver super nemt at tilføje nye funktioner lyt bare efter de begivenheder, du er interesseret i
- Flere ting kan reagere på den samme begivenhed uden at forstyrre hinanden
- Testning bliver meget enklere, fordi hver del fungerer uafhængigt
**Derfor vil du elske denne tilgang:**
- Tilføjelse af nye funktioner bliver super nemt lyt bare til de events, du interesserer dig for
- Flere ting kan reagere på den samme event uden at forstyrre hinanden
- Testning bliver meget lettere, fordi hver del fungerer uafhængigt
- Når noget går galt, ved du præcis, hvor du skal kigge
### Hvorfor Pub/Sub skalerer effektivt
### Hvorfor pub/sub skalerer effektivt
Pub/sub-mønsteret opretholder enkelhed, når applikationer vokser i kompleksitet. Uanset om det handler om at administrere dusinvis af fjender, dynamiske UI-opdateringer eller lydsystemer, håndterer mønsteret øget skala uden arkitektoniske ændringer. Nye funktioner integreres i det eksisterende begivenhedssystem uden at påvirke etableret funktionalitet.
Pub/sub-mønsteret bevarer enkeltheden, mens applikationer vokser i kompleksitet. Uanset om du styrer dusinvis af fjender, dynamiske UI-opdateringer eller lydsystemer, håndterer mønsteret øget skala uden arkitektoniske ændringer. Nye funktioner integreres i det eksisterende eventsystem uden at påvirke etableret funktionalitet.
> ⚠️ **Almindelig fejl**: Opret ikke for mange specifikke beskedtyper tidligt. Start med brede kategorier og finjuster dem, efterhånden som dit spils behov bliver klarere.
> ⚠️ **Almindelig fejl**: Opret ikke for mange specifikke beskedtyper tidligt. Start med brede kategorier og forfin dem, efterhånden som dit spils behov bliver klarere.
>
**Bedste praksis at følge:**
- **Grupperer** relaterede beskeder i logiske kategorier
- **Bruger** beskrivende navne, der tydeligt angiver, hvad der skete
- **Holder** beskedpayloads enkle og fokuserede
- **Dokumenterer** dine beskedtyper for samarbejde i teamet
- **Grupper** relaterede beskeder i logiske kategorier
- **Brug** beskrivende navne, der tydeligt angiver, hvad der skete
- **Hold** beskedens payloads simple og fokuserede
- **Dokumenter** dine beskedtyper for teamsamarbejde
### 🔄 **Pædagogisk kontrol**
**Forstå eventdrevet arkitektur**: Verificer din forståelse af hele systemet:
- ✅ Hvordan forhindrer pub/sub et stramt koblingsforhold mellem komponenter?
- ✅ Hvorfor er det nemmere at tilføje nye funktioner med eventdrevet arkitektur?
- ✅ Hvilken rolle spiller EventEmitter i kommunikationsflowet?
- ✅ Hvordan forhindrer beskedkonstanter fejl og forbedrer vedligeholdelse?
**Designudfordring**: Hvordan vil du håndtere disse spilsituationer med pub/sub?
1. **Fjende dør**: Opdater score, afspil lyd, spawn power-up, fjern fra skærm
2. **Niveau færdigt**: Stop musik, vis UI, gem fremgang, indlæs næste niveau
3. **Power-up samlet**: Forbedr evner, opdater UI, afspil effekt, start timer
**Professionel forbindelse**: Dette mønster ses i:
- **Frontend-rammer**: React/Vue event-systemer
- **Backend-services**: Mikrotjenestekommunikation
- **Spilmotorer**: Unitys eventsystem
- **Mobiludvikling**: iOS/Android notifikationssystemer
---
## GitHub Copilot Agent Challenge 🚀
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-mode til at fuldføre følgende udfordring:
Brug Agent-tilstand til at fuldføre følgende udfordring:
**Beskrivelse:** Opret et simpelt spilobjektsystem ved hjælp af både arv og pub/sub-mønsteret. Du skal implementere et grundlæggende spil, hvor forskellige objekter kan kommunikere gennem begivenheder uden direkte at kende til hinanden.
**Beskrivelse:** Skab et simpelt spilobjektsystem ved brug af både arv og pub/sub-mønsteret. Du implementerer et grundlæggende spil, hvor forskellige objekter kan kommunikere gennem events uden at kende hinanden direkte.
**Prompt:** Opret et JavaScript-spilsystem med følgende krav: 1) Opret en base GameObject-klasse med x, y-koordinater og en type-egenskab. 2) Opret en Hero-klasse, der udvider GameObject og kan bevæge sig. 3) Opret en Enemy-klasse, der udvider GameObject og kan jage helten. 4) Implementer en EventEmitter-klasse til pub/sub-mønsteret. 5) Opsæt begivenhedslyttere, så når helten bevæger sig, modtager nærliggende fjender en 'HERO_MOVED'-begivenhed og opdaterer deres position for at bevæge sig mod helten. Inkluder console.log-udsagn for at vise kommunikationen mellem objekter.
**Prompt:** Opret et JavaScript-spilsystem med følgende krav: 1) Opret en basis GameObject-klasse med x, y-koordinater og en type-egenskab. 2) Opret en Hero-klasse, der udvider GameObject og kan bevæge sig. 3) Opret en Enemy-klasse, der udvider GameObject og kan forfølge helten. 4) Implementer en EventEmitter-klasse til pub/sub-mønsteret. 5) Opsæt eventlyttere, så når helten bevæger sig, modtager fjender i nærheden en 'HERO_MOVED'-begivenhed og opdaterer deres position for at bevæge sig mod helten. Inkluder console.log-udsagn for at vise kommunikationen mellem objekterne.
Lær mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
Lær mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Overvej, hvordan pub-sub-mønsteret kan forbedre spilarkitekturen. Identificer hvilke komponenter der skal udsende begivenheder, og hvordan systemet skal reagere. Design et spilkoncept og kortlæg kommunikationsmønstrene mellem dets komponenter.
Overvej, hvordan pub-sub-mønsteret kan forbedre spilarkitekturen. Identificer hvilke komponenter der bør udsende begivenheder, og hvordan systemet skal reagere. Design et spilkoncept og kortlæg kommunikationsmønstrene mellem dets komponenter.
## Post-Lecture Quiz
## Quiz efter forelæsning
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/30)
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/30)
## Gennemgang & Selvstudie
## Review & Self Study
Lær mere om Pub/Sub ved at [læse om det](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon).
## Opgave
### ⚡ **Hvad du kan gøre i de næste 5 minutter**
- [ ] Åbn et hvilket som helst HTML5-spil online og inspicer dets kode ved hjælp af DevTools
- [ ] Opret et simpelt HTML5 Canvas-element og tegn en grundlæggende form
- [ ] Prøv at bruge `setInterval` for at skabe en simpel animationssløjfe
- [ ] Udforsk Canvas API-dokumentationen og prøv en tegnefunktion
### 🎯 **Hvad du kan opnå denne time**
- [ ] Gennemfør post-lesson quizen og forstå spiludviklingskoncepter
- [ ] Opsæt din spilprojektstruktur med HTML-, CSS- og JavaScript-filer
- [ ] Opret en grundlæggende spil-sløjfe, der opdaterer og renderer kontinuerligt
- [ ] Tegn dine første spil-sprites på canvasen
- [ ] Implementer grundlæggende asset loading for billeder og lyde
### 📅 **Din ugentlige spils skabelse**
- [ ] Færdiggør det komplette rumspil med alle planlagte funktioner
- [ ] Tilføj polerede grafik, lydeffekter og glatte animationer
- [ ] Implementer spiltilstande (startskærm, gameplay, game over)
- [ ] Opret et scoringssystem og spillerprogressionssporing
- [ ] Gør dit spil responsivt og tilgængeligt på tværs af enheder
- [ ] Del dit spil online og indsamle feedback fra spillere
### 🌟 **Din månedlange spiludvikling**
- [ ] Byg flere spil, der udforsker forskellige genrer og mekanikker
- [ ] Lær et spiludviklingsframework som Phaser eller Three.js
- [ ] Bidrag til open source spiludviklingsprojekter
- [ ] Mestér avancerede spilprogrammeringsmønstre og optimering
- [ ] Skab en portefølje, der fremviser dine spiludviklingskompetencer
- [ ] Mentorér andre, der er interesserede i spiludvikling og interaktivt medie
## 🎯 Din tidslinje for spiludviklingsmestring
```mermaid
timeline
title Spilarkitektur læringsforløb
section Objektmønstre (20 minutter)
Kodeorganisering: Klassearv
: Kompositionsmønstre
: Fabriksfunktioner
: Adfærdsblanding
section Kommunikationssystemer (25 minutter)
Begivenhedsarkitektur: Pub/Sub-implementering
: Meddelelsesdesign
: Begivenhedsudsendere
: Løs kobling
section Spilobjektdesign (30 minutter)
Enhedssystemer: Egenskabshåndtering
: Adfærdscomposition
: Tilstandshåndtering
: Livscyklusstyring
section Arkitekturmønstre (35 minutter)
Systemdesign: Komponentsystemer
: Observatørmønster
: Kommandomønster
: Tilstandsmaskiner
section Avancerede koncepter (45 minutter)
Skalerbar arkitektur: Ydelsesoptimering
: Hukommelsesstyring
: Moduledesign
: Teststrategier
section Spilmotorbegreber (1 uge)
Professionel udvikling: Scenediagrammer
: Ressourcestyring
: Renderingspipelines
: Fysisk integration
section Framework-mestring (2 uger)
Moderne spiludvikling: React-spilmønstre
: Canvas-optimering
: WebGL-grundlæggende
: PWA-spil
section Branchepraksis (1 måned)
Professionelle færdigheder: Team samarbejde
: Koderevisioner
: Spildesignmønstre
: Ydelsesprofilering
```
### 🛠️ Din opsummering af spilarkitekturværktøjskassen
Efter at have gennemført denne lektion har du nu:
- **Mesterskab i designmønstre**: Forståelse af arv vs. kompositions afvejninger
- **Begivenhedsdrevet arkitektur**: Pub/sub-implementering for skalerbar kommunikation
- **Objektorienteret design**: Klassehierarkier og adfærds-komposition
- **Moderne JavaScript**: Factory-funktioner, spread-syntaks og ES6+ mønstre
- **Skalerbar arkitektur**: Løs kobling og modulære designprincipper
- **Grundlag for spiludvikling**: Entitetssystemer og komponentmønstre
- **Professionelle mønstre**: Branche-standard tilgange til kodeorganisering
**Virkelige anvendelser**: Disse mønstre anvendes direkte til:
- **Frontend-frameworks**: React/Vue-komponentarkitektur og tilstandsadministration
- **Backend-tjenester**: Mikrotjenestekommunikation og begivenhedsdrevne systemer
- **Mobiludvikling**: iOS/Android app-arkitektur og notifikationssystemer
- **Spilmotorer**: Unity, Unreal og webbaseret spiludvikling
- **Enterprise-software**: Event sourcing og distribueret systemdesign
- **API-design**: RESTful tjenester og realtidskommunikation
**Professionelle færdigheder opnået**: Du kan nu:
- **Designe** skalerbare softwarearkitekturer ved hjælp af velafprøvede mønstre
- **Implementere** begivenhedsdrevne systemer, der håndterer komplekse interaktioner
- **Vælge** passende kodeorganisatoriske strategier til forskellige scenarier
- **Fejlsøge** og vedligeholde løst koblede systemer effektivt
- **Kommunikere** tekniske beslutninger ved hjælp af branchesprogeterminalogi
**Næste niveau**: Du er klar til at implementere disse mønstre i et rigtigt spil, udforske avancerede spiludviklingsemner eller anvende disse arkitekturkoncept på webapplikationer!
🌟 **Opnåelse opnået**: Du har mestret grundlæggende softwarearkitektur-mønstre, der driver alt fra simple spil til komplekse enterprise-systemer!
## Assignment
[Lav en mockup af et spil](assignment.md)
[Mock up a game](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved brug af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. Ved kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå ved brug af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,113 +1,232 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "84053695dca714e16ed064366503ebd5",
"translation_date": "2025-10-23T22:10:18+00:00",
"original_hash": "7994743c5b21fdcceb36307916ef249a",
"translation_date": "2026-01-06T23:37:35+00:00",
"source_file": "6-space-game/2-drawing-to-canvas/README.md",
"language_code": "da"
}
-->
# Byg et rumspil del 2: Tegn helte og monstre på lærredet
Canvas API er en af webudviklingens mest kraftfulde funktioner til at skabe dynamisk, interaktiv grafik direkte i din browser. I denne lektion forvandler vi det tomme HTML `<canvas>`-element til en spilverden fyldt med helte og monstre. Tænk på lærredet som din digitale tegneplade, hvor kode bliver til visuelle elementer.
Vi bygger videre på det, du lærte i den forrige lektion, og nu dykker vi ned i de visuelle aspekter. Du vil lære, hvordan man indlæser og viser spilfigurer, placerer elementer præcist og skaber det visuelle fundament for dit rumspil. Dette bygger bro mellem statiske websider og dynamiske, interaktive oplevelser.
Ved slutningen af denne lektion vil du have en komplet spillescene med dit helteskib korrekt placeret og fjendtlige formationer klar til kamp. Du vil forstå, hvordan moderne spil gengiver grafik i browsere og få færdigheder til at skabe dine egne interaktive visuelle oplevelser. Lad os udforske canvas-grafik og bringe dit rumspil til live!
## Quiz før lektionen
# Byg et rumspil del 2: Tegn helt og monstre på Canvas
```mermaid
journey
title Din Canvas Grafiske Rejse
section Fundament
Forstå Canvas API: 3: Student
Lær koordinatsystemet: 4: Student
Tegn grundlæggende former: 4: Student
section Billedbehandling
Indlæs spilressourcer: 4: Student
Håndter asynkron indlæsning: 5: Student
Positionér sprites: 5: Student
section Spil Rendering
Opret spils skærm: 5: Student
Byg formationer: 5: Student
Optimer ydeevne: 4: Student
```
Canvas API'en er en af webudviklingens mest kraftfulde funktioner til at skabe dynamiske, interaktive grafik direkte i din browser. I denne lektion vil vi omdanne det tomme HTML `<canvas>` element til en spilverden fyldt med helte og monstre. Tænk på canvas som dit digitale lærred, hvor kode bliver til visuel fremstilling.
Vi bygger videre på, hvad du lærte i den forrige lektion, og nu dykker vi ned i de visuelle aspekter. Du vil lære at indlæse og vise spilsprites, placere elementer præcist og skabe det visuelle fundament for dit rumspil. Dette bygger bro mellem statiske websider og dynamiske, interaktive oplevelser.
I slutningen af denne lektion vil du have en komplet spilsccene med dit helteskib korrekt placeret og fjendeformationer klar til kamp. Du vil forstå, hvordan moderne spil gengiver grafik i browsere og få færdigheder til at skabe dine egne interaktive visuelle oplevelser. Lad os udforske canvas-grafik og bringe dit rumspil til live!
```mermaid
mindmap
root((Canvas-grafik))
Canvas-element
HTML5-funktion
2D-kontekst
Koordinatsystem
Pixelkontrol
Tegneoperationer
Grundlæggende former
Tekstgengivelse
Billedvisning
Sti-tegning
Ressourcestyring
Billedindlæsning
Asynkrone operationer
Fejlhåndtering
Ydeevne
Spilgengivelse
Sprite-positionering
Formationslayout
Sceneopbygning
Frame-opdateringer
Visuelle effekter
Farver og stilarter
Transformationer
Animationer
Lagdeling
```
## For-forelæsning quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/31)
[For-forelæsningsquiz](https://ff-quizzes.netlify.app/web/quiz/31)
## Canvas
Så hvad er præcist dette `<canvas>`-element? Det er HTML5's løsning til at skabe dynamisk grafik og animationer i webbrowseren. I modsætning til almindelige billeder eller videoer, der er statiske, giver lærredet dig kontrol over hver pixel, der vises på skærmen. Dette gør det perfekt til spil, datavisualiseringer og interaktiv kunst. Tænk på det som en programmerbar tegneflade, hvor JavaScript bliver din pensel.
Så hvad er dette `<canvas>` element egentlig? Det er HTML5's løsning til at skabe dynamisk grafik og animationer i webbrowsere. I modsætning til almindelige billeder eller videoer, der er statiske, giver canvas dig pixel-niveau kontrol over alt, der vises på skærmen. Det gør det perfekt til spil, datavisualiseringer og interaktiv kunst. Tænk på det som en programmerbar tegneflade, hvor JavaScript bliver din pensel.
Som standard ser et canvas-element ud som et tomt, gennemsigtigt rektangel på din side. Men det er her, potentialet ligger! Dets virkelige kraft kommer frem, når du bruger JavaScript til at tegne figurer, indlæse billeder, skabe animationer og få ting til at reagere på brugerinteraktioner. Det minder om, hvordan de tidlige computer-grafikpionerer hos Bell Labs i 1960'erne måtte programmere hver pixel for at skabe de første digitale animationer.
Som standard ligner et canvas-element en tom, transparent rektangel på din side. Men det er her potentialet ligger! Dets sande kraft dukker op, når du bruger JavaScript til at tegne former, indlæse billeder, skabe animationer og få ting til at reagere på brugerinteraktioner. Det minder om, hvordan tidlige computer-grafik pionerer hos Bell Labs i 1960'erne måtte programmere hver eneste pixel for at skabe de første digitale animationer.
✅ Læs [mere om Canvas API](https://developer.mozilla.org/docs/Web/API/Canvas_API) på MDN.
Her er, hvordan det typisk deklareres som en del af sidens body:
Sådan erklæres det normalt som en del af sidens body:
```html
<canvas id="myCanvas" width="200" height="100"></canvas>
```
**Her er, hvad denne kode gør:**
- **Sætter** `id`-attributten, så du kan referere til dette specifikke canvas-element i JavaScript
- **Definerer** bredden i pixels for at styre lærredets horisontale størrelse
- **Fastlægger** højden i pixels for at bestemme lærredets vertikale dimensioner
## Tegning af simpel geometri
Nu hvor du ved, hvad canvas-elementet er, lad os udforske, hvordan man faktisk tegner på det! Lærredet bruger et koordinatsystem, der måske føles bekendt fra matematikundervisningen, men der er en vigtig forskel, der er specifik for computer-grafik.
Lærredet bruger kartesiske koordinater med en x-akse (vandret) og en y-akse (lodret) til at placere alt, hvad du tegner. Men her er den afgørende forskel: I modsætning til koordinatsystemet fra matematikundervisningen starter oprindelsespunktet `(0,0)` i øverste venstre hjørne, hvor x-værdierne stiger, når du bevæger dig til højre, og y-værdierne stiger, når du bevæger dig nedad. Denne tilgang stammer fra tidlige computerskærme, hvor elektronstråler scannede fra top til bund, hvilket gjorde øverste venstre hjørne til det naturlige startpunkt.
![lærredets gitter](../../../../translated_images/canvas_grid.5f209da785ded492a01ece440e3032afe51efa500cc2308e5ea4252487ceaf0b.da.png)
**Dette gør koden:**
- **Sætter** `id` attributten, så du kan referere til dette specifikke canvas-element i JavaScript
- **Definerer** `width` i pixels for at kontrollere canvasens horisontale størrelse
- **Fastlægger** `height` i pixels for at bestemme canvasens vertikale dimensioner
## Tegn enkel geometri
Nu hvor du ved, hvad canvas-elementet er, så lad os udforske faktisk at tegne på det! Canvas bruger et koordinatsystem, der måske føles bekendt fra matematikundervisningen, men der er et vigtigt twist, som er særligt for computer-grafik.
Canvas bruger kartesiske koordinater med en x-akse (vandret) og y-akse (lodret) til at placere alt, hvad du tegner. Men her er hovedforskellen: i modsætning til koordinatsystemet i matematik starter origo `(0,0)` i øverste venstre hjørne, hvor x-værdierne stiger, når du bevæger dig mod højre, og y-værdierne stiger, når du bevæger dig nedad. Denne tilgang stammer fra tidlige computerskærme, hvor elektronstråler scannede fra top til bund, hvilket gjorde øverste venstre hjørne til det naturlige udgangspunkt.
```mermaid
quadrantChart
title Kanvas Koordinatsystem
x-axis Venstre --> Højre
y-axis Top --> Bund
quadrant-1 Kvadrant 1
quadrant-2 Kvadrant 2
quadrant-3 Kvadrant 3
quadrant-4 Kvadrant 4
Origin Point: [0.1, 0.1]
Hero Center: [0.5, 0.8]
Enemy Formation: [0.3, 0.2]
Power-up: [0.7, 0.6]
UI Elements: [0.9, 0.1]
```
![canvas gitteret](../../../../translated_images/canvas_grid.5f209da785ded492.da.png)
> Billede fra [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes)
For at tegne på canvas-elementet følger du den samme tretrinsproces, der danner grundlaget for al canvas-grafik. Når du har gjort dette et par gange, bliver det en vane:
1. **Få en reference** til dit canvas-element fra DOM'en (ligesom ethvert andet HTML-element)
2. **Få 2D-renderingskonteksten** dette giver alle tegnefunktionerne
3. **Begynd at tegne!** Brug kontekstens indbyggede metoder til at skabe din grafik
For at tegne på canvas-elementet følger du den samme tre-trins proces, som udgør fundamentet for al canvas grafik. Når du har gjort det et par gange, bliver det anden natur:
```mermaid
flowchart LR
A[HTML Canvas Element] --> B[Få Canvas Reference]
B --> C[Få 2D Kontekst]
C --> D[Tegneoperationer]
D --> E[Tegn Former]
D --> F[Tegn Tekst]
D --> G[Tegn Billeder]
D --> H[Anvend Stilarter]
E --> I[Render til Skærm]
F --> I
G --> I
H --> I
style A fill:#e1f5fe
style C fill:#e8f5e8
style I fill:#fff3e0
```
1. **Hent en reference** til dit Canvas-element fra DOMen (lige som ethvert andet HTML-element)
2. **Hent 2D rendering context** dette giver alle tegne-metoderne
3. **Begynd at tegne!** Brug contextens indbyggede metoder til at skabe din grafik
Her er, hvordan det ser ud i kode:
Sådan ser det ud i kode:
```javascript
// Step 1: Get the canvas element
// Trin 1: Hent canvas-elementet
const canvas = document.getElementById("myCanvas");
// Step 2: Get the 2D rendering context
// Trin 2: Hent 2D-renderingskonteksten
const ctx = canvas.getContext("2d");
// Step 3: Set fill color and draw a rectangle
// Trin 3: Indstil fyldfarve og tegn en rektangel
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 200, 200); // x, y, width, height
ctx.fillRect(0, 0, 200, 200); // x, y, bredde, højde
```
**Lad os bryde dette ned trin for trin:**
- Vi **henter** vores canvas-element ved hjælp af dets ID og gemmer det i en variabel
- Vi **får** 2D-renderingskonteksten dette er vores værktøjskasse fuld af tegnefunktioner
- Vi **fortæller** lærredet, at vi vil fylde ting med rød ved hjælp af `fillStyle`-egenskaben
- Vi **tegner** et rektangel, der starter i øverste venstre hjørne (0,0), som er 200 pixels bredt og højt
**Lad os bryde det ned trin for trin:**
- Vi **henter** vores canvas-element med dets ID og gemmer det i en variabel
- Vi **får fat i** 2D rendering context det er vores værktøjskasse fyldt med tegne-metoder
- Vi **siger** til canvas, at vi vil fylde med rød ved at sætte `fillStyle` egenskaben
- Vi **tegner** et rektangel startende øverst til venstre (0,0) på 200 pixels bredt og højt
✅ Canvas API fokuserer mest på 2D-former, men du kan også tegne 3D-elementer på en hjemmeside; til dette kan du bruge [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API).
✅ Canvas API'en fokuserer mest på 2D former, men du kan også tegne 3D elementer på et websted; til det kan du bruge [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API).
Du kan tegne mange forskellige ting med Canvas API som:
Du kan tegne alle mulige ting med Canvas API som:
- **Geometriske former**, vi har allerede vist, hvordan man tegner et rektangel, men der er meget mere, du kan tegne.
- **Tekst**, du kan tegne tekst med enhver skrifttype og farve, du ønsker.
- **Billeder**, du kan tegne et billede baseret på en billedfil som f.eks. en .jpg eller .png.
✅ Prøv det! Du ved, hvordan man tegner et rektangel, kan du tegne en cirkel på en side? Tag et kig på nogle interessante Canvas-tegninger på CodePen. Her er et [særdeles imponerende eksempel](https://codepen.io/dissimulate/pen/KrAwx).
- **Tekst**, du kan tegne tekst med enhver font og farve, du ønsker.
- **Billeder**, du kan tegne et billede baseret på en billedfil som f.eks. .jpg eller .png.
## Indlæs og tegn en billedfil
✅ Prøv det! Du ved, hvordan man tegner et rektangel, kan du tegne en cirkel på en side? Tag et kig på nogle interessante Canvas-tegninger på CodePen. Her er et [især imponerende eksempel](https://codepen.io/dissimulate/pen/KrAwx).
At tegne grundlæggende former er nyttigt for at komme i gang, men de fleste spil har brug for rigtige billeder! Figurer, baggrunde og teksturer er det, der giver spil deres visuelle appel. At indlæse og vise billeder på lærredet fungerer anderledes end at tegne geometriske former, men det er ligetil, når du først forstår processen.
### 🔄 **Pædagogisk check-in**
**Forståelse af Canvas grundprincipper**: Før vi går videre til billedeindlæsning, sikre dig at du kan:
- ✅ Forklare, hvordan canvas koordinatsystem adskiller sig fra matematiske koordinater
- ✅ Forstå tre-trins processen for canvas tegnehandlinger
- ✅ Identificere, hvad 2D rendering context tilbyder
- ✅ Beskrive, hvordan fillStyle og fillRect arbejder sammen
Vi skal oprette et `Image`-objekt, indlæse vores billedfil (dette sker asynkront, hvilket betyder "i baggrunden"), og derefter tegne det på lærredet, når det er klar. Denne tilgang sikrer, at dine billeder vises korrekt uden at blokere din applikation, mens de indlæses.
**Hurtig selvtest**: Hvordan ville du tegne en blå cirkel ved position (100, 50) med radius 25?
```javascript
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(100, 50, 25, 0, 2 * Math.PI);
ctx.fill();
```
### Grundlæggende billedindlæsning
**Canvas tegne-metoder du nu kender**:
- **fillRect()**: Tegner fyldte rektangler
- **fillStyle**: Sætter farver og mønstre
- **beginPath()**: Starter nye tegneveje
- **arc()**: Skaber cirkler og kurver
## Indlæs og tegn et billede
At tegne grundlæggende former er nyttigt til at komme i gang, men de fleste spil har brug for rigtige billeder! Sprites, baggrunde og teksturer er det, der giver spil visuel appel. Indlæsning og visning af billeder på canvas fungerer anderledes end at tegne geometriske former, men det er ligetil, når du forstår processen.
Vi skal oprette et `Image` objekt, indlæse vores billedfil (det sker asynkront, hvilket betyder "i baggrunden"), og derefter tegne det på canvas, når det er klar. Denne tilgang sikrer, at dine billeder vises korrekt uden at blokere din applikation, mens de indlæses.
```mermaid
sequenceDiagram
participant JS as JavaScript
participant Img as Billedobjekt
participant Server as Filer-server
participant Canvas as Canvas-kontekst
JS->>Img: new Image()
JS->>Img: Indstil src-ejendom
Img->>Server: Anmod om billedfil
Server->>Img: Returner billeddata
Img->>JS: Udløs onload-begivenhed
JS->>Canvas: drawImage(img, x, y)
Canvas->>Canvas: Render til skærm
Note over JS,Canvas: Asynkron indlæsning forhindrer UI-blokering
```
### Grundlæggende billedeindlæsning
```javascript
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// Image loaded and ready to be used
// Billedet er indlæst og klar til brug
console.log('Image loaded successfully!');
};
```
**Her er, hvad der sker i denne kode:**
- Vi **opretter** et helt nyt Image-objekt til at holde vores figur eller tekstur
- Vi **fortæller** det, hvilken billedfil der skal indlæses ved at angive kildebanen
- Vi **lytter** efter load-eventet, så vi ved præcis, hvornår billedet er klar til brug
**Det sker i denne kode:**
- Vi **opretter** et helt nyt Image-objekt til at holde vores sprite eller tekstur
- Vi **angiver** hvilken billedfil, der skal indlæses ved at sætte kilde-stien
- Vi **lytter** efter load-begivenheden, så vi ved præcis, hvornår billedet er klar til brug
### En bedre måde at indlæse billeder på
Her er en mere robust måde at håndtere billedindlæsning på, som professionelle udviklere ofte bruger. Vi pakker billedindlæsningen ind i en Promise-baseret funktion denne tilgang, der blev populær, da JavaScript Promises blev standard i ES6, gør din kode mere organiseret og håndterer fejl elegant:
Her er en mere robust måde at håndtere billedeindlæsning, som professionelle udviklere ofte bruger. Vi pakker billedeindlæsningen ind i en Promise-baseret funktion denne tilgang, som blev populær, da JavaScript Promises blev standard i ES6, gør din kode mere organiseret og håndterer fejl elegant:
```javascript
function loadAsset(path) {
@ -123,38 +242,38 @@ function loadAsset(path) {
});
}
// Modern usage with async/await
// Moderne brug med async/await
async function initializeGame() {
try {
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// Images are now ready to use
// Billeder er nu klar til brug
} catch (error) {
console.error('Failed to load game assets:', error);
}
}
```
**Hvad vi har gjort her:**
- **Pakket** al den billedindlæsningslogik ind i en Promise, så vi kan håndtere det bedre
**Her har vi gjort:**
- **Pakket** al billedeindlæsningslogikken ind i en Promise, så vi kan håndtere det bedre
- **Tilføjet** fejlhåndtering, der faktisk fortæller os, når noget går galt
- **Brugt** moderne async/await-syntaks, fordi det er så meget lettere at læse
- **Inkluderet** try/catch-blokke for elegant at håndtere eventuelle indlæsningsproblemer
- **Brugt** moderne async/await syntaks fordi den er så meget nemmere at læse
- **Inkluderet** try/catch blokke for at håndtere eventuelle indlæsningsproblemer pænt
Når dine billeder er indlæst, er det faktisk ret ligetil at tegne dem på lærredet:
Når dine billeder er indlæst, er det faktisk meget enkelt at tegne dem på canvas:
```javascript
async function renderGameScreen() {
try {
// Load game assets
// Indlæs spilressourcer
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// Get canvas and context
// Hent lærred og kontekst
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
// Draw images to specific positions
// Tegn billeder på specifikke positioner
ctx.drawImage(heroImg, canvas.width / 2, canvas.height / 2);
ctx.drawImage(monsterImg, 0, 0);
} catch (error) {
@ -163,32 +282,56 @@ async function renderGameScreen() {
}
```
**Lad os gennemgå dette trin for trin:**
- Vi **indlæser** både vores helte- og monsterbilleder i baggrunden ved hjælp af await
- Vi **henter** vores canvas-element og får den 2D-renderingskontekst, vi har brug for
- Vi **placerer** heltebilledet lige i midten ved hjælp af lidt hurtig koordinatmatematik
- Vi **placerer** monsterbilledet i øverste venstre hjørne for at starte vores fjendtlige formation
- Vi **fanger** eventuelle fejl, der måtte opstå under indlæsning eller rendering
**Lad os gennemgå det trin for trin:**
- Vi **indlæser** både vores helt og monsterbilleder i baggrunden ved hjælp af await
- Vi **henter** vores canvas element og får den 2D rendering context, vi skal bruge
- Vi **placerer** heltens billede lige i midten ved hjælp af lidt hurtig koordinat-matematik
- Vi **lægger** monsterbilledet i øverste venstre hjørne for at starte vores fjendeformation
- Vi **fanger** eventuelle fejl, der kan opstå under indlæsning eller rendering
```mermaid
flowchart TD
A[Indlæs Aktiver] --> B{Alle Billeder Indlæst?}
B -->|Nej| C[Vis Indlæsning]
B -->|Ja| D[Hent Canvas Kontekst]
C --> B
D --> E[Ryd Skærm]
E --> F[Tegn Baggrund]
F --> G[Tegn Fjendeformation]
G --> H[Tegn Helt Skib]
H --> I[Anvend Visuelle Effekter]
I --> J[Render Frame]
subgraph "Rendering Pipeline"
K[Asset Management]
L[Scene Composition]
M[Drawing Operations]
N[Frame Output]
end
style A fill:#e1f5fe
style J fill:#e8f5e8
style I fill:#fff3e0
```
## Nu er det tid til at begynde at bygge dit spil
Nu samler vi det hele for at skabe det visuelle fundament for dit rumspil. Du har en solid forståelse af canvas-grundprincipper og teknikker til billedindlæsning, så denne praktiske sektion vil guide dig gennem opbygningen af en komplet spilleskærm med korrekt placerede figurer.
Nu samler vi det hele for at skabe det visuelle fundament i dit rumspil. Du har en solid forståelse for canvas grundprincipper og billedeindlæsningsteknikker, så denne praktiske sektion vil guide dig gennem at bygge en komplet spilscreen med korrekt placerede sprites.
### Hvad skal bygges
Du skal bygge en webside med et Canvas-element. Det skal vise en sort skærm `1024*768`. Vi har givet dig to billeder:
Du skal bygge en webside med et Canvas-element. Det skal gengive en sort skærm `1024*768`. Vi har leveret to billeder til dig:
- Helteskib
![Helteskib](../../../../translated_images/player.dd24c1afa8c71e9b82b2958946d4bad13308681392d4b5ddcc61a0e818ef8088.da.png)
![Helteskib](../../../../translated_images/player.dd24c1afa8c71e9b.da.png)
- 5*5 monstre
![Monsterskib](../../../../translated_images/enemyShip.5df2a822c16650c2fb3c06652e8ec8120cdb9122a6de46b9a1a56d54db22657f.da.png)
![Monster skib](../../../../translated_images/enemyShip.5df2a822c16650c2.da.png)
### Anbefalede trin til at starte udviklingen
### Anbefalede trin for at starte udviklingen
Find de startfiler, der er oprettet til dig i undermappen `your-work`. Din projektstruktur bør indeholde:
Find startfilerne, som er oprettet for dig i undermappen `your-work`. Din projektstruktur bør indeholde:
```bash
your-work/
@ -200,46 +343,46 @@ your-work/
└── package.json
```
**Her er, hvad du arbejder med:**
- **Spilfigurer** ligger i mappen `assets/`, så alt forbliver organiseret
- **Din hoved-HTML-fil** opsætter canvas-elementet og gør alt klar
- **En JavaScript-fil**, hvor du skriver al din spilrenderingsmagi
- **En package.json**, der opsætter en udviklingsserver, så du kan teste lokalt
**Det arbejder du med:**
- **Spil sprites** ligger i `assets/` mappen, så alt holder sig organiseret
- **Din hoved HTML fil** opsætter canvas elementet og forbereder det hele
- **En JavaScript fil** hvor du skal skrive al din spil-renderingsmagi
- **En package.json** som opsætter en udviklingsserver, så du kan teste lokalt
Åbn denne mappe i Visual Studio Code for at begynde udviklingen. Du skal bruge et lokalt udviklingsmiljø med Visual Studio Code, NPM og Node.js installeret. Hvis du ikke har `npm` sat op på din computer, [sådan installerer du det](https://www.npmjs.com/get-npm).
Åbn denne mappe i Visual Studio Code for at begynde udviklingen. Du skal have et lokalt udviklingsmiljø med Visual Studio Code, NPM og Node.js installeret. Hvis du ikke har `npm` sat op på din computer, [her er hvordan du installerer det](https://www.npmjs.com/get-npm).
Start din udviklingsserver ved at navigere til mappen `your-work`:
Start din udviklingsserver ved at navigere til `your-work` mappen:
```bash
cd your-work
npm start
```
**Denne kommando gør nogle ret seje ting:**
**Denne kommando gør nogle ret fede ting:**
- **Starter** en lokal server på `http://localhost:5000`, så du kan teste dit spil
- **Serverer** alle dine filer korrekt, så din browser kan indlæse dem
- **Overvåger** dine filer for ændringer, så du kan udvikle problemfrit
- **Giver dig** et professionelt udviklingsmiljø til at teste alt
- **Serverer** alle dine filer korrekt, så din browser kan loade dem rigtigt
- **Holder øje med** dine filer for ændringer, så du kan udvikle glat
- **Giver dig** et professionelt udviklingsmiljø til at teste det hele
> 💡 **Bemærk**: Din browser vil vise en tom side i starten det er forventet! Når du tilføjer kode, skal du opdatere din browser for at se dine ændringer. Denne iterative udviklingsmetode ligner, hvordan NASA byggede Apollo-styringscomputeren testning af hver komponent, før den blev integreret i det større system.
> 💡 **Bemærk**: Din browser vil først vise en tom side det er forventet! Når du tilføjer kode, opdater browseren for at se dine ændringer. Denne iterative udviklingsstil minder om, hvordan NASA byggede Apollo vejledningscomputeren test hver komponent før de integrerede den i det større system.
### Tilføj kode
Tilføj den nødvendige kode til `your-work/app.js` for at fuldføre følgende opgaver:
Tilføj den nødvendige kode i `your-work/app.js` for at fuldføre følgende opgaver:
1. **Tegn et lærred med sort baggrund**
> 💡 **Sådan gør du**: Find TODO i `/app.js` og tilføj blot to linjer. Sæt `ctx.fillStyle` til sort, og brug derefter `ctx.fillRect()` startende ved (0,0) med dine lærredsdimensioner. Nemt!
1. **Tegn et canvas med sort baggrund**
> 💡 **Sådan gør du**: Find TODO i `/app.js` og tilføj bare to linjer. Sæt `ctx.fillStyle` til sort, brug så `ctx.fillRect()` startende ved (0,0) med dine canvas-dimensioner. Nemt!
2. **Indlæs spilteksturer**
> 💡 **Sådan gør du**: Brug `await loadAsset()` til at indlæse dine spiller- og fjendebilleder. Gem dem i variabler, så du kan bruge dem senere. Husk de vises ikke, før du faktisk tegner dem!
> 💡 **Sådan gør du**: Brug `await loadAsset()` til at indlæse dine spiller- og fjendebilleder. Gem dem i variabler, så du kan bruge dem senere. Husk de vises ikke, før du rent faktisk tegner dem!
3. **Tegn helteskib i midten-nederst**
> 💡 **Sådan gør du**: Brug `ctx.drawImage()` til at placere din helt. For x-koordinaten, prøv `canvas.width / 2 - 45` for at centrere det, og for y-koordinaten brug `canvas.height - canvas.height / 4` for at placere det i det nederste område.
3. **Tegn helteskibet i centrum-nederst position**
> 💡 **Sådan gør du**: Brug `ctx.drawImage()` til at placere din helt. For x-koordinaten prøv `canvas.width / 2 - 45` for at centrere, og for y-koordinaten brug `canvas.height - canvas.height / 4` for at placere det i nederste område.
4. **Tegn en 5×5 formation af fjendtlige skibe**
> 💡 **Sådan gør du**: Find funktionen `createEnemies` og opsæt en indlejret løkke. Du skal lave lidt matematik for afstand og placering, men bare rolig jeg viser dig præcis hvordan!
> 💡 **Sådan gør du**: Find `createEnemies` funktionen og opsæt en indlejret løkke. Du skal lave lidt matematik for afstand og placering, men bare rolig jeg viser dig præcist hvordan!
Først, fastlæg konstanter for korrekt layout af fjendtlige formationer:
Først, fastsæt konstanter for korrekt layout af fjendeformationen:
```javascript
const ENEMY_TOTAL = 5;
@ -249,13 +392,39 @@ const START_X = (canvas.width - FORMATION_WIDTH) / 2;
const STOP_X = START_X + FORMATION_WIDTH;
```
**Lad os bryde ned, hvad disse konstanter gør:**
**Sådan bryder vi disse konstanter ned:**
- Vi **sætter** 5 fjender pr. række og kolonne (et flot 5×5 gitter)
- Vi **definerer**, hvor meget plads der skal være mellem fjenderne, så de ikke ser trange ud
- Vi **beregner**, hvor bred vores hele formation vil være
- Vi **finder ud af**, hvor vi skal starte og stoppe, så formationen ser centreret ud
Derefter opretter du indlejrede løkker for at tegne fjendtlige formationer:
- Vi **definerer** hvor meget plads, der skal være mellem fjenderne, så de ikke ser klemt ud
- Vi **beregner** hvor bred hele formationen bliver
- Vi **finder ud af**, hvor vi skal starte og slutte, så formationen ser centreret ud
```mermaid
flowchart LR
A["Lærreds Bredde: 1024px"] --> B["Formationens Bredde: 490px"]
B --> C["Start X: 267px"]
C --> D["Fjernelse af Fjender: 98px"]
subgraph "5x5 Fjendeformation"
E["Række 1: Y=0"]
F["Række 2: Y=50"]
G["Række 3: Y=100"]
H["Række 4: Y=150"]
I["Række 5: Y=200"]
end
subgraph "Kolonneafstand"
J["Kolonne 1: X=267"]
K["Kolonne 2: X=365"]
L["Kolonne 3: X=463"]
M["Kolonne 4: X=561"]
N["Kolonne 5: X=659"]
end
style A fill:#e1f5fe
style B fill:#e8f5e8
style C fill:#fff3e0
```
Så laver du indlejrede løkker for at tegne fjendeformationen:
```javascript
for (let x = START_X; x < STOP_X; x += ENEMY_SPACING) {
@ -265,21 +434,40 @@ for (let x = START_X; x < STOP_X; x += ENEMY_SPACING) {
}
```
**Her er, hvad denne indlejrede løkke gør:**
- Den ydre løkke **bevæger sig** fra venstre til højre hen over vores formation
**Det gør denne indlejrede løkke:**
- Den ydre løkke **bevæger sig** fra venstre mod højre hen over vores formation
- Den indre løkke **går** fra top til bund for at skabe pæne rækker
- Vi **tegner** hver fjendtlig figur ved de præcise x,y-koordinater, vi har beregnet
- Alt forbliver **jævnt fordelt**, så det ser professionelt og organiseret ud
- Vi **tegner** hver fjendesprite på de præcise x,y koordinater vi har beregnet
- Alt holdes **jævnt fordelt**, så det ser professionelt og organiseret ud
### 🔄 **Pædagogisk check-in**
**Mestring af spil-rendering**: Tjek din forståelse af det komplette rendering system:
- ✅ Hvordan forhindrer asynkron billedeindlæsning UI-blokering under spilopstart?
- ✅ Hvorfor beregner vi fjendeformationens positioner med konstanter i stedet for at hardkode?
- ✅ Hvilken rolle spiller 2D rendering context i tegneoperationer?
- ✅ Hvordan skaber indlejrede løkker organiserede sprite-formationer?
**Performance overvejelser**: Dit spil demonstrerer nu:
- **Effektiv asset indlæsning**: Promise-baseret billedhåndtering
- **Organiseret rendering**: Strukturerede tegnehandlinger
- **Matematisk placering**: Beregnet spriteplacering
- **Fejlhåndtering**: Elegant håndtering af fejl
**Visuelle programmeringskoncepter**: Du har lært:
- **Koordinatsystemer**: Oversættelse af matematik til skærmpositioner
- **Sprite-administration**: Indlæsning og visning af spilstyringsgrafik
- **Formationsalgoritmer**: Matematiske mønstre til organiserede layouts
- **Async-operationer**: Moderne JavaScript for en glidende brugeroplevelse
## Resultat
Det færdige resultat bør se sådan ud:
Det færdige resultat burde se sådan ud:
![Sort skærm med en helt og 5*5 monstre](../../../../translated_images/partI-solution.36c53b48c9ffae2a5e15496b23b604ba5393433e4bf91608a7a0a020eb7a2691.da.png)
![Black screen with a hero and 5*5 monsters](../../../../translated_images/partI-solution.36c53b48c9ffae2a.da.png)
## Løsning
Prøv venligst at løse det selv først, men hvis du går i stå, kan du se en [løsning](../../../../6-space-game/2-drawing-to-canvas/solution/app.js).
Prøv venligst først at løse det selv, men hvis du går i stå, kan du kigge på en [løsning](../../../../6-space-game/2-drawing-to-canvas/solution/app.js)
---
@ -287,29 +475,150 @@ Prøv venligst at løse det selv først, men hvis du går i stå, kan du se en [
Brug Agent-tilstand til at fuldføre følgende udfordring:
**Beskrivelse:** Forbedr dit rumspil-lærred ved at tilføje visuelle effekter og interaktive elementer ved hjælp af de Canvas API-teknikker, du har lært.
**Beskrivelse:** Forbedr dit rumspils canvas ved at tilføje visuelle effekter og interaktive elementer ved hjælp af Canvas API-teknikker, du har lært.
**Opgave:** Opret en ny fil kaldet `enhanced-canvas.html` med et lærred, der viser animerede stjerner i baggrunden, en pulserende sundhedsbar for helteskibet og fjendtlige skibe, der langsomt bevæger sig nedad. Inkluder JavaScript-kode, der tegner blinkende stjerner ved hjælp af tilfældige positioner og opacitet, implementerer en sundhedsbar, der ændrer farve baseret på sundhedsniveau (grøn > gul > rød), og animerer fjendtlige skibe til at bevæge sig nedad på skærmen med forskellige hastigheder.
**Prompt:** Opret en ny fil kaldet `enhanced-canvas.html` med et canvas, der viser animerede stjerner i baggrunden, en pulserende helbredsindikator for helteskibet, og fjendtlige skibe, der langsomt bevæger sig nedad. Inkluder JavaScript-kode, der tegner blinkende stjerner ved brug af tilfældige positioner og opacitet, implementerer en helbredsindikator, der ændrer farve baseret på helbredsniveau (grøn > gul > rød), og animerer de fjendtlige skibe til at bevæge sig nedad på skærmen i forskellige hastigheder.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
Læs mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Du har lært om at tegne med den 2D-fokuserede Canvas API; tag et kig på [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API), og prøv at tegne et 3D-objekt.
Du har lært at tegne med det 2D-fokuserede Canvas API; tag et kig på [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API), og prøv at tegne et 3D-objekt.
## Quiz efter lektionen
## Quiz efter forelæsning
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/32)
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/32)
## Gennemgang & Selvstudie
## Gennemgang & Selvstudium
Lær mere om Canvas API ved at [læse om det](https://developer.mozilla.org/docs/Web/API/Canvas_API).
### ⚡ **Hvad du kan gøre de næste 5 minutter**
- [ ] Åbn browserkonsollen og opret et canvas-element med `document.createElement('canvas')`
- [ ] Prøv at tegne et rektangel ved hjælp af `fillRect()` på et canvas-kontekst
- [ ] Eksperimenter med forskellige farver ved hjælp af `fillStyle`-egenskaben
- [ ] Tegn en simpel cirkel med `arc()`-metoden
### 🎯 **Hvad du kan nå i denne time**
- [ ] Fuldfør quizzen efter lektionen og forstå canvas-grundlaget
- [ ] Opret en canvas-tegneapplikation med flere former og farver
- [ ] Implementer billedindlæsning og sprite-rendering til dit spil
- [ ] Byg en simpel animation, der flytter objekter hen over canvas
- [ ] Øv dig i canvas-transformationer som skalering, rotation og translation
### 📅 **Din ugentlige Canvas-rejse**
- [ ] Færdiggør rumspillet med polerede grafik og sprite-animationer
- [ ] Mest avancerede canvas-teknikker som gradienter, mønstre og composite
- [ ] Skab interaktive visualiseringer ved hjælp af canvas til datarepræsentation
- [ ] Lær om canvas-optimeringsteknikker for jævn ydeevne
- [ ] Byg en tegne- eller maleapplikation med forskellige værktøjer
- [ ] Udforsk kreative kodningsmønstre og generativ kunst med canvas
### 🌟 **Din månedlange grafik-mestre**
- [ ] Byg komplekse visuelle applikationer ved brug af Canvas 2D og WebGL
- [ ] Lær grafikprogrammeringskoncepter og shader-grundlag
- [ ] Bidrag til open source grafikbiblioteker og visualiseringsværktøjer
- [ ] Mestre performance-optimering til grafikintensive applikationer
- [ ] Skab undervisningsmateriale om canvas-programmering og computer-grafik
- [ ] Bliv ekspert i grafikprogrammering og hjælp andre med at skabe visuelle oplevelser
## 🎯 Din Canvas-grafikmesterskabs Tidslinje
```mermaid
timeline
title Canvas API Læringsforløb
section Canvas Grundlæggende (15 minutter)
Grundlæggende operationer: Elementreference
: Adgang til 2D-kontekst
: Koordinatsystem
: Enkel formtegning
section Tegneteknikker (20 minutter)
Grafiske primitiva: Firkantede og cirkler
: Farver og stilarter
: Tekstafbildning
: Sti-operationer
section Billedhåndtering (25 minutter)
Asset management: Oprettelse af billedelement
: Asynkrone indlæsningsmønstre
: Fejlbehandling
: Ydeevneoptimering
section Spilgrafik (30 minutter)
Sprite gengivelse: Positionsalgoritmer
: Formationsberegninger
: Sceneopsætning
: Frame-gengivelse
section Avancerede teknikker (40 minutter)
Visuelle effekter: Transformationer
: Animationer
: Lagdeling
: Tilstandsstyring
section Ydeevne (35 minutter)
Optimering: Effektiv tegning
: Hukommelsesstyring
: Frame-rate kontrol
: Asset caching
section Professionelle færdigheder (1 uge)
Produktionsgrafik: WebGL integration
: Canvas-biblioteker
: Spilmotorer
: Tværplatformsovervejelser
section Avanceret grafik (1 måned)
Specialiserede anvendelser: Datavisualisering
: Interaktiv kunst
: Realtidseffekter
: 3D-grafik
```
### 🛠️ Dit Canvas-grafikværktøjssæt Resumé
Efter at have gennemført denne lektion har du nu:
- **Canvas API-mesterskab**: Fuld forståelse af 2D-grafikprogrammering
- **Koordinatmatematik**: Præcise positionerings- og layoutalgoritmer
- **Asset Management**: Professionel billedindlæsning og fejlhåndtering
- **Rendering Pipeline**: Struktureret tilgang til scenekomposition
- **Spilgrafik**: Sprite-positionering og formationsberegning
- **Async Programmering**: Moderne JavaScript-mønstre til glidende ydeevne
- **Visuel Programmering**: Oversættelse af matematiske koncepter til skærmgrafik
**Anvendelser i den virkelige verden**: Dine Canvas-færdigheder kan direkte bruges til:
- **Datavisualisering**: Diagrammer, grafer og interaktive dashboards
- **Spiludvikling**: 2D-spil, simuleringer og interaktive oplevelser
- **Digital Kunst**: Kreativ kodning og generative kunstprojekter
- **UI/UX Design**: Tilpassede grafikker og interaktive elementer
- **Uddannelsessoftware**: Visuelle læringsværktøjer og simuleringer
- **Webapplikationer**: Dynamisk grafik og realtidsvisualiseringer
**Professionelle færdigheder du har opnået**: Du kan nu:
- **Bygge** skræddersyede grafikløsninger uden eksterne biblioteker
- **Optimere** renderingens ydeevne for glidende brugeroplevelser
- **Fejlsøge** komplekse visuelle problemer ved hjælp af browserens udviklerværktøjer
- **Designe** skalerbare grafiske systemer baseret på matematiske principper
- **Integrere** Canvas-grafik med moderne webapplikationsrammer
**Canvas API-metoder du mestrer**:
- **Elementadministration**: getElementById, getContext
- **Tegneoperationer**: fillRect, drawImage, fillStyle
- **Asset-indlæsning**: Billedobjekter, Promise-mønstre
- **Matematisk positionering**: Koordinatberegninger, formationsalgoritmer
**Næste niveau**: Du er klar til at tilføje animation, brugerinteraktion, kollisionsregistrering eller udforske WebGL til 3D-grafik!
🌟 **Præstation opnået**: Du har bygget et komplet spilsystem til rendering ved brug af grundlæggende Canvas API-teknikker!
## Opgave
[Leg med Canvas API](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog skal betragtes som den autoritative kilde. For vigtig information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,36 +1,103 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "4b1d441cfd31924084956000c0fee5a5",
"translation_date": "2025-10-23T22:11:34+00:00",
"original_hash": "039b4d8ce65f5edd82cf48d9c3e6728c",
"translation_date": "2026-01-06T23:36:17+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "da"
}
-->
# Byg et rumspil del 4: Tilføj en laser og registrer kollisioner
## Quiz før lektionen
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/35)
Tænk på øjeblikket i Star Wars, hvor Lukes proton-torpedoer ramte Dødsstjernens udstødningsport. Den præcise kollision ændrede galaksens skæbne! I spil fungerer kollisionsdetektion på samme måde - det afgør, hvornår objekter interagerer, og hvad der sker derefter.
I denne lektion vil du tilføje laser-våben til dit rumspil og implementere kollisionsdetektion. Ligesom NASAs missionplanlæggere beregner rumfartøjers baner for at undgå affald, vil du lære at registrere, når spilobjekter krydser hinanden. Vi bryder det ned i håndterbare trin, der bygger på hinanden.
Når du er færdig, vil du have et fungerende kampsystem, hvor lasere ødelægger fjender, og kollisioner udløser spilbegivenheder. De samme kollisionsprincipper bruges i alt fra fysiksimuleringer til interaktive webgrænseflader.
✅ Lav lidt research om det allerførste computerspil, der nogensinde er skrevet. Hvad var dets funktionalitet?
# Byg et rumspil Del 4: Tilføj en laser og opdag kollisioner
```mermaid
journey
title Din Kollision Registrerings Rejse
section Fysikkens Grundlag
Forstå rektangler: 3: Student
Lær skærings matematik: 4: Student
Forstå koordinatsystemer: 4: Student
section Spilmekanik
Implementer laser affyring: 4: Student
Tilføj objektets livscyklus: 5: Student
Opret kollision regler: 5: Student
section Systemintegration
Byg kollision registrering: 5: Student
Optimér ydeevne: 5: Student
Test interaktions systemer: 5: Student
```
## For-forelæsning quiz
[For-forelæsnings quiz](https://ff-quizzes.netlify.app/web/quiz/35)
Tænk på øjeblikket i Star Wars, hvor Lukes protontorpedoer rammer Dødsstjernens udstødningsport. Den præcise kollisionsregistrering ændrede galaksens skæbne! I spil fungerer kollisionsregistrering på samme måde den bestemmer, hvornår objekter interagerer, og hvad der sker næste gang.
I denne lektion tilføjer du laser-våben til dit rumspil og implementerer kollisionsregistrering. Ligesom NASAs missionplanlæggere beregner rumfartøjsbaner for at undgå rumskrot, lærer du at opdage, hvornår spilobjekter krydser hinandens veje. Vi opdeler det i håndterbare trin, der bygger ovenpå hinanden.
Ved lektionens afslutning har du et fungerende kampsystem, hvor lasere ødelægger fjender, og kollisioner udløser spilbegivenheder. De samme kollisionsprincipper anvendes i alt fra fysik-simuleringer til interaktive webløsninger.
```mermaid
mindmap
root((Kollisiondetektion))
Physics Concepts
Rectangle Grænser
Intersection Testning
Koordinatsystemer
Separation Logik
Game Objects
Laser Projektiler
Fjende Skibe
Hovedperson
Kollisionzoner
Lifecycle Management
Objekt Oprettelse
Bevægelses Opdateringer
Ødelæggelses Markering
Hukommelses Rydning
Event Systems
Tastatur Input
Kollision Begivenheder
Spiltilstands Ændringer
Audio/Visuelle Effekter
Performance
Effektive Algoritmer
Frame Rate Optimering
Hukommelses Håndtering
Rumlig Opdeling
```
✅ Lav en lille undersøgelse om det allerførste computerspil nogensinde skrevet. Hvad var dets funktionalitet?
## Kollisionsdetektion
## Kollisionsregistrering
Kollisionsdetektion fungerer som nærhedssensorerne på Apollo-månemodulet - det kontrollerer konstant afstande og udløser advarsler, når objekter kommer for tæt på. I spil afgør dette system, hvornår objekter interagerer, og hvad der skal ske derefter.
Kollisionsregistrering virker som nærhedssensorerne på Apollo-månemodulet det tjekker konstant afstande og udløser alarmer, når objekter kommer for tæt på hinanden. I spil afgør dette system, hvornår objekter interagerer, og hvad der skal ske næste gang.
Den tilgang, vi vil bruge, behandler hvert spilobjekt som en rektangel, ligesom lufttrafikstyringssystemer bruger forenklede geometriske former til at spore fly. Denne rektangelmetode kan virke simpel, men den er beregningsmæssigt effektiv og fungerer godt for de fleste spilsituationer.
Den tilgang, vi bruger, behandler hvert spilobjekt som et rektangel, på samme måde som flyveledelsessystemer bruger forenklede geometriske former til at spore fly. Denne rektangelmetode kan virke basal, men den er beregningseffektiv og fungerer godt i de fleste spilsituationer.
### Rektangelrepræsentation
Hvert spilobjekt har brug for koordinatgrænser, ligesom Mars Pathfinder-roveren kortlagde sin placering på Mars' overflade. Sådan definerer vi disse grænsekoordinater:
Hvert spilobjekt har brug for koordinatgrænser, ligesom Mars Pathfinder-roveren kortlagde sin position på Mars overflade. Sådan definerer vi disse grænsekoordinater:
```mermaid
flowchart TD
A["🎯 Spilobjekt"] --> B["📍 Position (x, y)"]
A --> C["📏 Dimensioner (bredde, højde)"]
B --> D["Top: y"]
B --> E["Venstre: x"]
C --> F["Bund: y + højde"]
C --> G["Højre: x + bredde"]
D --> H["🔲 Rektangel Grænser"]
E --> H
F --> H
G --> H
H --> I["Kollisionsdetektion Klar"]
style A fill:#e3f2fd
style H fill:#e8f5e8
style I fill:#fff3e0
```
```javascript
rectFromGameObject() {
return {
@ -43,15 +110,37 @@ rectFromGameObject() {
```
**Lad os bryde det ned:**
- **Øverste kant**: Det er bare, hvor dit objekt starter lodret (dets y-position)
- **Venstre kant**: Hvor det starter vandret (dets x-position)
- **Nederste kant**: Tilføj højden til y-positionen - nu ved du, hvor det slutter!
- **Højre kant**: Tilføj bredden til x-positionen - og du har den komplette grænse.
- **Topkant**: Det er bare, hvor dit objekt starter lodret (dets y-position)
- **Venstrekant**: Hvor det starter vandret (dets x-position)
- **Bundkant**: Læg højden til y-positionen nu ved du, hvor det slutter!
- **Højrekant**: Læg bredden til x-positionen og så har du hele grænsen
### Intersektionsalgoritme
### Krydsningsalgoritme
At registrere rektangelintersektioner bruger logik, der ligner den måde, Hubble-rumteleskopet afgør, om himmelobjekter overlapper i dets synsfelt. Algoritmen kontrollerer for adskillelse:
Registrering af rektangelkrydsning bruger logik, der ligner den måde Hubble-rygteleskopet afgør, om himmellegemer overlapper i sit synsfelt. Algoritmen tjekker for adskillelse:
```mermaid
flowchart LR
A["Rektangel 1"] --> B{"Adskillelsestests"}
C["Rektangel 2"] --> B
B --> D["R2 venstre > R1 højre?"]
B --> E["R2 højre < R1 venstre?"]
B --> F["R2 top > R1 bund?"]
B --> G["R2 bund < R1 top?"]
D --> H{"Noget Sandt?"}
E --> H
F --> H
G --> H
H -->|Ja| I["❌ Ingen Kollision"]
H -->|Nej| J["✅ Kollision Registreret"]
style B fill:#e3f2fd
style I fill:#ffebee
style J fill:#e8f5e8
```
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
@ -67,54 +156,99 @@ function intersectRect(r1, r2) {
- Er rektangel 2 helt under rektangel 1?
- Er rektangel 2 helt over rektangel 1?
Hvis ingen af disse betingelser er sande, må rektanglerne overlappe. Denne tilgang minder om, hvordan radaroperatører afgør, om to fly er på sikre afstande.
Hvis ingen af disse betingelser er sande, må rektanglerne overlappe. Denne tilgang spejler, hvordan radaroperatører vurderer, om to fly er på sikre afstande.
## Håndtering af objektlivscyklusser
Når en laser rammer en fjende, skal begge objekter fjernes fra spillet. Men at slette objekter midt i en løkke kan forårsage nedbrud - en lektie, der blev lært på den hårde måde i tidlige computersystemer som Apollo Guidance Computer. I stedet bruger vi en "mark for deletion"-tilgang, der sikkert fjerner objekter mellem frames.
Når en laser rammer en fjende, skal begge objekter fjernes fra spillet. Dog kan sletning midt i en løkke forårsage nedbrud en hårdt lært lektie fra tidlige computersystemer som Apollo Guidance Computer. I stedet bruger vi en "mærk til sletning"-metode, der sikkert fjerner objekter mellem frames.
Sådan markerer vi noget for fjernelse:
```mermaid
stateDiagram-v2
[*] --> Active: Objekt Oprettet
Active --> Collided: Kollision Registreret
Collided --> MarkedDead: Sæt død = sand
MarkedDead --> Filtered: Næste Frame
Filtered --> [*]: Objekt Fjernet
Active --> OutOfBounds: Forlader Skærm
OutOfBounds --> MarkedDead
note right of MarkedDead
Sikkert at fortsætte
nuværende frame
end note
note right of Filtered
Objekter fjernet
mellem frames
end note
```
Sådan markerer vi noget til fjernelse:
```javascript
// Mark object for removal
// Marker objekt til fjernelse
enemy.dead = true;
```
**Hvorfor denne tilgang fungerer:**
- Vi markerer objektet som "dødt", men sletter det ikke med det samme.
- Dette lader den aktuelle spilframe afslutte sikkert.
- Ingen nedbrud fra forsøg på at bruge noget, der allerede er væk!
**Hvorfor denne metode virker:**
- Vi markerer objektet som "dødt", men sletter det ikke med det samme
- Det lader den aktuelle spilframe afslutte sikkert
- Ingen nedbrud fra at forsøge at bruge noget, der allerede er væk!
Derefter filtrerer vi markerede objekter ud før næste render-cyklus:
Herefter filtreres markerede objekter væk før næste gengivelsescyklus:
```javascript
gameObjects = gameObjects.filter(go => !go.dead);
```
**Hvad denne filtrering gør:**
- Skaber en ny liste med kun de "levende" objekter.
- Smider alt ud, der er markeret som dødt.
- Holder dit spil kørende glat.
- Forhindrer hukommelsesopblæsning fra akkumulerede ødelagte objekter.
- Opretter en frisk liste med kun de "levende" objekter
- Kasserer alt, der er markeret som dødt
- Holder spillet kørende glat
- Forhindrer hukommelsesopsamling af ødelagte objekter
## Implementering af lasermekanik
Laserprojektiler i spil fungerer på samme princip som fotontorpedoer i Star Trek - de er diskrete objekter, der bevæger sig i lige linjer, indtil de rammer noget. Hver gang du trykker på mellemrumstasten, skabes et nyt laserobjekt, der bevæger sig hen over skærmen.
Laserprojektiler i spil fungerer efter samme princip som fotontorpedoer i Star Trek de er diskrete objekter, der bevæger sig i lige linjer, indtil de rammer noget. Hver tryk på mellemrumstasten skaber et nyt laserobjekt, der bevæger sig hen over skærmen.
For at få dette til at fungere skal vi koordinere nogle forskellige dele:
For at få det til at fungere, skal vi koordinere flere forskellige dele:
**Nøglekomponenter at implementere:**
- **Opret** laserobjekter, der spawner fra heltenes position.
- **Håndter** tastaturinput for at udløse laseroprettelse.
- **Administrer** lasernes bevægelse og livscyklus.
- **Implementer** visuel repræsentation for laserprojektilerne.
**Vigtige komponenter at implementere:**
- **Skabe** laserobjekter, der spawner ud fra heltenes position
- **Håndtere** tastaturinput for at udløse laseroprettelse
- **Styres** laserbevægelser og livscyklus
- **Implementere** visuel repræsentation for laserprojektilerne
## Implementering af skydehastighedskontrol
## Implementering af affyringshastighedskontrol
Ubegrænsede skydehastigheder ville overbelaste spilmotoren og gøre gameplayet for nemt. Rigtige våbensystemer står over for lignende begrænsninger - selv USS Enterprises phasere havde brug for tid til at genoplade mellem skud.
Ubegrænsede affyringshastigheder vil overbelaste spilmotoren og gøre spillet for let. Ægte våbensystemer har lignende begrænsninger selv USS Enterprises phasere skulle oplades mellem skud.
Vi implementerer et cooldown-system, der forhindrer hurtig affyring, mens det opretholder responsive kontroller:
Vi implementerer et cooldown-system, der forhindrer hurtig affyring, mens det bevarer responsive kontroller:
```mermaid
sequenceDiagram
participant Player
participant Weapon
participant Cooldown
participant Game
Player->>Weapon: Tryk på mellemrumstast
Weapon->>Cooldown: Tjek om kølet ned
alt Våben er klar
Cooldown->>Weapon: kølet = sandt
Weapon->>Game: Opret Laser
Weapon->>Cooldown: Start ny nedkøling
Cooldown->>Cooldown: kølet = falsk
Note over Cooldown: Vent 500ms
Cooldown->>Cooldown: kølet = sandt
else Våben er afkøling
Cooldown->>Weapon: kølet = falsk
Weapon->>Player: Ingen handling
end
```
```javascript
class Cooldown {
constructor(time) {
@ -132,42 +266,58 @@ class Weapon {
fire() {
if (!this.cooldown || this.cooldown.cool) {
// Create laser projectile
// Opret laserprojektil
this.cooldown = new Cooldown(500);
} else {
// Weapon is still cooling down
// Våbenet er stadig ved at køle ned
}
}
}
```
**Hvordan cooldown fungerer:**
- Når det oprettes, starter våbnet "varmt" (kan ikke skyde endnu).
- Efter timeout-perioden bliver det "køligt" (klar til at skyde).
- Før affyring kontrollerer vi: "Er våbnet køligt?"
- Dette forhindrer spamklik, mens det holder kontrollerne responsive.
**Sådan virker cooldown:**
- Når det bliver skabt, starter våbnet "varmt" (kan ikke affyre endnu)
- Efter timeout-perioden bliver det "køligt" (klar til at affyre)
- Før affyring tjekker vi: "Er våbnet køligt?"
- Det forhindrer spam-affyring og holder samtidig kontrollerne responsive
Henvis til lektion 1 i rumspilserien for at minde dig selv om cooldowns.
Se lektion 1 i rumspilsserien for at genopfriske cooldowns.
## Opbygning af kollisionssystemet
Du vil udvide din eksisterende rumspilkode for at skabe et kollisionsdetektionssystem. Ligesom den internationale rumstations automatiske kollisionsundgåelsessystem vil dit spil kontinuerligt overvåge objektpositioner og reagere på intersektioner.
Du skal udvide din eksisterende rumspilskode med et kollisionsdetekteringssystem. Ligesom Den Internationale Rumstations automatiske undgåelsessystem vil dit spil konstant overvåge objektpositioner og reagere på krydsninger.
Start med koden fra din tidligere lektion, og tilføj kollisionsdetektion med specifikke regler, der styrer objektinteraktioner.
Fra din tidligere lektionskode tilføjes kollisionsregistrering med specifikke regler, der styrer objektinteraktioner.
> 💡 **Pro Tip**: Laser-sprite er allerede inkluderet i din assets-mappe og refereret i din kode, klar til implementering.
> 💡 **Pro Tip**: Lasersprite er allerede inkluderet i dine assets-mapper og refereret i din kode, klar til implementering.
### Kollisionsregler at implementere
**Spilmekanik at tilføje:**
1. **Laser rammer fjende**: Fjendeobjektet ødelægges, når det rammes af et laserprojektil.
2. **Laser rammer skærmgrænse**: Laser fjernes, når den når skærmens øverste kant.
3. **Fjende og helt kollision**: Begge objekter ødelægges, når de krydser hinanden.
4. **Fjende når bunden**: Spillet er tabt, når fjender når skærmens bund.
**Spilmæssige mekanikker at tilføje:**
1. **Laser rammer fjende**: Fjendeobjektet ødelægges ved ramme af laserprojektilet
2. **Laser rammer skærmgrænse**: Laser fjernes, når den når skærmens topkant
3. **Fjende og helt kollision**: Begge objekter ødelægges ved krydsning
4. **Fjende når bunden**: Game over, når fjender når skærmens bund
### 🔄 **Pædagogisk Tjek-ind**
**Grundlag for Kollisionsregistrering**: Sørg for, at du forstår:
- ✅ Hvordan rektangelgrænser definerer kollisionszoner
- ✅ Hvorfor separationstest er mere effektiv end krydsningsberegning
- ✅ Vigtigheden af håndtering af objektlivscyklusser i spilloops
- ✅ Hvordan begivenhedsdrevne systemer koordinerer kollisionssvar
**Hurtig Selvevaluering**: Hvad ville ske, hvis du slettede objekter med det samme i stedet for at markere dem?
*Svar: Midt-loop sletning kunne forårsage nedbrud eller springe objekter over under iteration*
**Forståelse af fysik**: Nu mestrer du:
- **Koordinatsystemer**: Hvordan position og dimensioner skaber grænser
- **Krydsningslogik**: Matematiske principper bag kollisionsregistrering
- **Ydeevneoptimering**: Hvorfor effektive algoritmer betyder noget i realtidssystemer
- **Hukommelsesstyring**: Sikker håndtering af objektlivscyklusser for stabilitet
## Opsætning af dit udviklingsmiljø
Godt nyt - vi har allerede sat det meste af grundlaget op for dig! Alle dine spilressourcer og grundlæggende struktur venter i undermappen `your-work`, klar til at du kan tilføje de seje kollisionsfunktioner.
Gode nyheder vi har allerede sat det meste af fundamentet op for dig! Alle dine spilassets og grundlæggende struktur ligger klar i undermappen `your-work`, klar til at du kan tilføje fede kollisionsfunktioner.
### Projektstruktur
@ -181,12 +331,12 @@ Godt nyt - vi har allerede sat det meste af grundlaget op for dig! Alle dine spi
-| package.json
```
**Forstå filstrukturen:**
- **Indeholder** alle sprite-billeder, der er nødvendige for spilobjekterne.
- **Inkluderer** hoved-HTML-dokumentet og JavaScript-applikationsfilen.
- **Tilbyder** pakke-konfiguration til lokal udviklingsserver.
**Forståelse af filstrukturen:**
- **Indeholder** alle spritebilleder, der skal bruges til spilobjekter
- **Inkluderer** hoved-HTML-dokument og JavaScript-applikationsfil
- **Leverer** package-konfiguration til lokal udviklingsserver
### Start udviklingsserveren
### Start den lokale server
Naviger til din projektmappe og start den lokale server:
@ -195,21 +345,46 @@ cd your-work
npm start
```
**Denne kommandosekvens:**
- **Skifter** mappe til din arbejdende projektmappe.
- **Starter** en lokal HTTP-server på `http://localhost:5000`.
- **Serverer** dine spilfiler til test og udvikling.
- **Muliggør** live-udvikling med automatisk genindlæsning.
**Denne kommando-sekvens:**
- **Skifter** til din arbejdsprojektmappe
- **Starter** en lokal HTTP-server på `http://localhost:5000`
- **Serverer** dine spilkoder til test og udvikling
- **Muliggør** liveudvikling med automatisk genindlæsning
Åbn din browser og naviger til `http://localhost:5000` for at se din aktuelle spiltilstand med helten og fjenderne gengivet på skærmen.
Åbn din browser og naviger til `http://localhost:5000` for at se din nuværende spilstatus med helten og fjenderne vist på skærmen.
### Trin-for-trin implementering
Ligesom den systematiske tilgang NASA brugte til at programmere Voyager-rumfartøjet, vil vi implementere kollisionsdetektion metodisk og bygge hver komponent trin for trin.
Som den systematiske metode NASA brugte til at programmere Voyager-rumfartøjet, implementerer vi kollisionsdetektering metodisk, trin for trin.
#### 1. Tilføj rektangelkollisionsgrænser
```mermaid
flowchart TD
A["1. Rektangel Grænser"] --> B["2. Krydsningsdetektion"]
B --> C["3. Lasersystem"]
C --> D["4. Hændelseshåndtering"]
D --> E["5. Kollisionsregler"]
E --> F["6. Nedkølingssystem"]
G["Objektgrænser"] --> A
H["Fysikalgoritme"] --> B
I["Projektiloprettelse"] --> C
J["Tastaturinput"] --> D
K["Spillogik"] --> E
L["Hastighedsbegrænsning"] --> F
F --> M["🎮 Færdigt Spil"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
#### 1. Tilføj rektangel-kollisionsgrænser
Først skal vi lære vores spilobjekter at beskrive deres grænser. Tilføj denne metode til din `GameObject`-klasse:
Først lærer vi vores spilobjekter, hvordan de beskriver deres grænser. Tilføj denne metode til din `GameObject`-klasse:
```javascript
rectFromGameObject() {
@ -222,15 +397,15 @@ rectFromGameObject() {
}
```
**Denne metode opnår:**
- **Opretter** et rektangelobjekt med præcise grænsekoordinater.
- **Beregner** nederste og højre kanter ved hjælp af position plus dimensioner.
- **Returnerer** et objekt klar til kollisionsdetektionsalgoritmer.
- **Tilbyder** en standardiseret grænseflade for alle spilobjekter.
**Denne metode r:**
- **Opretter** et rektangelobjekt med præcise grænsekoordinater
- **Beregner** bund- og højrekant med position plus dimensioner
- **Returnerer** et objekt klar til kollisionsdetekteringsalgoritmer
- **Giver** et standardiseret interface til alle spilobjekter
#### 2. Implementer intersektionsdetektion
#### 2. Implementer krydsningsdetektion
Nu skal vi skabe vores kollisionsdetektiv - en funktion, der kan afgøre, hvornår to rektangler overlapper:
Lad os nu skabe vores kollisionsdetektiv en funktion, der kan afgøre, hvornår to rektangler overlapper:
```javascript
function intersectRect(r1, r2) {
@ -243,19 +418,19 @@ function intersectRect(r1, r2) {
}
```
**Denne algoritme fungerer ved:**
- **Tester** fire adskillelsesbetingelser mellem rektangler.
- **Returnerer** `false`, hvis nogen adskillelsesbetingelse er sand.
- **Indikerer** kollision, når ingen adskillelse eksisterer.
- **Bruger** negationslogik for effektiv intersektionstest.
**Algoritmen arbejder ved:**
- **Tester** fire adskillelsesbetingelser mellem rektangler
- **Returnerer** `false`, hvis nogen adskillelsesbetingelse er sand
- **Indikerer** kollision, når ingen adskillelse findes
- **Bruger** negationslogik for effektiv krydsningstest
#### 3. Implementer laserskyde-system
#### 3. Implementer lasers affyringssystem
Nu bliver det spændende! Lad os opsætte laserskyde-systemet.
Her bliver det spændende! Lad os sætte lasers affyringssystem op.
##### Meddelelseskonstanter
Først skal vi definere nogle meddelelsestyper, så forskellige dele af vores spil kan kommunikere med hinanden:
Først definerer vi nogle beskedtyper, så forskellige dele af spillet kan kommunikere:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
@ -263,14 +438,14 @@ COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
**Disse konstanter tilbyder:**
- **Standardiserer** begivenhedsnavne i hele applikationen.
- **Muliggør** konsistent kommunikation mellem spilsystemer.
- **Forhindrer** tastefejl i begivenhedshåndtering.
**Disse konstanter giver:**
- **Standardisering** af hændelsesnavne i hele applikationen
- **Muliggør** ensartet kommunikation mellem spilsystemer
- **Forebygger** tastefejl i hændelsesregistrering
##### Håndtering af tastaturinput
##### Tastaturinput håndtering
Tilføj mellemrumstast-detektion til din tastaturbegivenhedslytter:
Tilføj rumtast-registrering til dit nøglehændelseslyttere:
```javascript
} else if(evt.keyCode === 32) {
@ -278,14 +453,14 @@ Tilføj mellemrumstast-detektion til din tastaturbegivenhedslytter:
}
```
**Denne inputhåndtering:**
- **Registrerer** mellemrumstasttryk ved hjælp af keyCode 32.
- **Udsender** en standardiseret begivenhedsmeddelelse.
- **Muliggør** løsrevet skyde-logik.
**Denne inputhandler:**
- **Registrerer** rumtasttryk med keyCode 32
- **Sender** en standardiseret begivenhedsbesked
- **Muliggør** løs kobling af affyringslogik
##### Opsætning af begivenhedslytter
##### Event listener-opsætning
Registrer skydeadfærd i din `initGame()`-funktion:
Registrer affyringsadfærd i din `initGame()`-funktion:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
@ -295,12 +470,12 @@ eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
});
```
**Denne begivenhedslytter:**
- **Reagerer**mellemrumstast-begivenheder.
- **Kontrollerer** skydecooldown-status.
- **Udløser** laseroprettelse, når det er tilladt.
**Denne eventlistener:**
- **Reagerer** på rumtast-begivenheder
- **Tjekker** affyrings-cooldown-status
- **Udløser** laseroprettelse, når det er tilladt
Tilføj kollisionshåndtering for laser-fjende-interaktioner:
Tilføj kollisionshåndtering for laser-fjende interaktioner:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
@ -310,13 +485,13 @@ eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
```
**Denne kollisionshåndtering:**
- **Modtager** kollisionsbegivenhedsdata med begge objekter.
- **Markerer** begge objekter til fjernelse.
- **Sikrer** korrekt oprydning efter kollision.
- **Modtager** kollisionsdata med begge objekter
- **Markerer** begge objekter til fjernelse
- **Sikrer** korrekt oprydning efter kollision
#### 4. Opret Laser-klassen
Implementer et laserprojektil, der bevæger sig opad og administrerer sin egen livscyklus:
Implementer et laserprojektile, der bevæger sig opad og styrer sin egen livscyklus:
```javascript
class Laser extends GameObject {
@ -340,22 +515,22 @@ class Laser extends GameObject {
```
**Denne klasseimplementering:**
- **Udvider** GameObject for at arve grundlæggende funktionalitet.
- **Indstiller** passende dimensioner for laser-sprite.
- **Skaber** automatisk opadgående bevægelse ved hjælp af `setInterval()`.
- **Håndterer** selvdestruktion, når den når skærmens top.
- **Administrerer** sin egen animationstiming og oprydning.
- **Arver** fra GameObject for grundlæggende funktionalitet
- **Sætter** passende dimensioner til lasersprite
- **Opretter** automatisk opadgående bevægelse med `setInterval()`
- **Håndterer** selvdestruktion ved skærmens top
- **Styrer** sin egen animationstid og oprydning
#### 5. Implementer kollisionsdetektionssystem
#### 5. Implementer kollisionsdetekteringssystem
Opret en omfattende kollisionsdetektionsfunktion:
Opret en komplet kollisionsdetekteringsfunktion:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter(go => go.type === "Laser");
// Test laser-enemy collisions
// Test laser-fjende kollisioner
lasers.forEach((laser) => {
enemies.forEach((enemy) => {
if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
@ -367,22 +542,22 @@ function updateGameObjects() {
});
});
// Remove destroyed objects
// Fjern ødelagte objekter
gameObjects = gameObjects.filter(go => !go.dead);
}
```
**Dette kollisionssystem:**
- **Filtrerer** spilobjekter efter type for effektiv testning.
- **Tester** hver laser mod hver fjende for intersektioner.
- **Udsender** kollisionsbegivenheder, når intersektioner registreres.
- **Rydder** ødelagte objekter op efter kollisionsbehandling.
- **Filtrerer** spilobjekter efter type for effektiv test
- **Tester** hver laser mod hver fjende for krydsninger
- **Sender** kollisionsbegivenheder ved registrerede sammenstød
- **Rydder** op i ødelagte objekter efter kollisionsprocessering
> ⚠️ **Vigtigt**: Tilføj `updateGameObjects()` til din hovedspilsløkke i `window.onload` for at aktivere kollisionsdetektion.
> ⚠️ **Vigtigt**: Tilføj `updateGameObjects()` til din hovedspil-løkke i `window.onload` for at aktivere kollisionsregistrering.
#### 6. Tilføj cooldown-system til Hero-klassen
Forbedr Hero-klassen med skyde-mekanik og hastighedsbegrænsning:
Udvid Hero-klassen med affyringsmekanik og hastighedsbegrænsning:
```javascript
class Hero extends GameObject {
@ -414,30 +589,164 @@ class Hero extends GameObject {
}
```
**Forstå den forbedrede Hero-klasse:**
- **Initialiserer** cooldown-timeren til nul (klar til at skyde).
- **Opretter** laserobjekter placeret over helteskibet.
- **Indstiller** cooldown-periode for at forhindre hurtig affyring.
- **Reducerer** cooldown-timeren ved hjælp af intervalbaserede opdateringer.
- **Tilbyder** statuskontrol for affyring via `canFire()`-metoden.
### Test din implementering
Dit rumspil har nu komplette kollisionsdetektions- og kampmekanikker. 🚀 Test disse nye funktioner:
- **Naviger** med piletasterne for at verificere bevægelseskontroller.
- **Affyr lasere** med mellemrumstasten - bemærk, hvordan cooldown forhindrer spamklik.
- **Observer kollisioner**, når lasere rammer fjender, og udløser fjernelse.
- **Bekræft oprydning**, da ødelagte objekter forsvinder fra spillet.
Du har med succes implementeret et kollisionsdetektionssystem ved hjælp af de samme matematiske principper, der guider rumfartsnavigation og robotteknologi.
**Forståelse af den forbedrede Hero-klasse:**
- **Initialiserer** cooldown-timer til nul (klar til affyring)
- **Skaber** laserobjekter placeret over helteskibet
- **Sætter** cooldown-periode for at forhindre hurtig affyring
- **Reducerer** cooldown-timer med interval-baserede opdateringer
- **Tilbyder** affyringsstatuskontrol via `canFire()` metode
### 🔄 **Pædagogisk Tjek-ind**
**Komplet Systemforståelse**: Bekræft din mestring af kollisionssystemet:
- ✅ Hvordan gør rektangelgrænser effektiv kollisionsregistrering mulig?
- ✅ Hvorfor er håndtering af objektlivscyklusser kritisk for spilstabilitet?
- ✅ Hvordan forhindrer cooldown-systemet performanceproblemer?
- ✅ Hvilken rolle spiller begivenhedsdrevet arkitektur i kollisionshåndtering?
**Systemintegration**: Din kollisionsregistrering demonstrerer:
- **Matematisk præcision**: Rektangel-krydsningsalgoritmer
- **Ydeevneoptimering**: Effektive kollisionstestmønstre
- **Hukommelsesstyring**: Sikker objektoprettelse og -nedbrydning
- **Eventkoordination**: Løst koblet systemkommunikation
- **Realtidsbehandling**: Frame-baserede opdateringscyklusser
**Professionelle mønstre**: Du har implementeret:
- **Separation af bekymringer**: Fysik, gengivelse og input adskilt
- **Objektorienteret design**: Arv og polymorfi
- **State management**: Objektlivscyklus og spiltilstandssporing
- **Ydeevneoptimering**: Effektive algoritmer til realtimebrug
### Test af din implementering
Dit rumspil har nu komplet kollisionsregistrering og kampelementer. 🚀 Test de nye muligheder:
- **Navigér** med piletasterne for at bekræfte bevægelseskontrol
- **Affyr lasere** med mellemrumstasten bemærk hvordan cooldown forhindrer spam
- **Observer kollisioner**, når lasere rammer fjender og udløser fjernelse
- **Bekræft oprydning**, når ødelagte objekter forsvinder fra spillet
Du har med succes implementeret et kollisionsdetekteringssystem, der bruger samme matematiske principper som styrer rumfartøjsnavigation og robotik.
### ⚡ **Det kan du gøre på de næste 5 minutter**
- [ ] Åbn browserens DevTools og sæt breakpoint i din kollisionsdetektionsfunktion
- [ ] Prøv at ændre lasers hastighed eller fjendens bevægelse for at se kollisionseffekter
- [ ] Eksperimentér med forskellige cooldown-værdier for at teste affyringshastigheder
- [ ] Tilføj `console.log` udsagn for at spore kollisionsevents i realtid
### 🎯 **Hvad du kan opnå denne time**
- [ ] Gennemfør quizzen efter lektionen og forstå kollisiondetektionsalgoritmer
- [ ] Tilføj visuelle effekter som eksplosioner når kollisioner opstår
- [ ] Implementer forskellige typer projektiler med varierende egenskaber
- [ ] Skab power-ups der midlertidigt forbedrer spillerens evner
- [ ] Tilføj lydeffekter for at gøre kollisioner mere tilfredsstillende
### 📅 **Din uge-lange fysikprogrammering**
- [ ] Færdiggør det fulde rumspil med polerede kollisionssystemer
- [ ] Implementer avancerede kollisionsformer ud over rektangler (cirkler, polygoner)
- [ ] Tilføj partikeleffekter for realistiske eksplosioner
- [ ] Skab kompleks fjendeadfærd med kollisionsundgåelse
- [ ] Optimer kollisionsdetektion for bedre ydeevne med mange objekter
- [ ] Tilføj fysiksimulering såsom momentum og realistisk bevægelse
### 🌟 **Din månedslange mestring af spils fysik**
- [ ] Byg spil med avancerede fysikmotorer og realistiske simuleringer
- [ ] Lær 3D kollisionsdetektion og rumlig opdeling algoritmer
- [ ] Bidrag til open source fysikbiblioteker og spilmotorer
- [ ] Mestre performanceoptimering til grafikintensive applikationer
- [ ] Skab undervisningsindhold om spils fysik og kollisionsdetektion
- [ ] Byg en portefølje der viser avancerede fysikprogrammeringsfærdigheder
## 🎯 Din tidslinje for mestring af kollisionsdetektion
```mermaid
timeline
title Kollisionregistrering & Spilfysik Læringsfremskridt
section Grundlag (10 minutter)
Rectangle Math: Koordinatsystemer
: Grænseberegninger
: Positionssporing
: Dimensionstyring
section Algoritmedesign (20 minutter)
Intersection Logic: Separations-test
: Overlapdetektion
: Ydeevneoptimering
: Håndtering af kanttilfælde
section Spilimplementering (30 minutter)
Object Systems: Livscykelstyring
: Eventkoordination
: Tilstandssporing
: Hukommelsesrydning
section Interaktive Funktioner (40 minutter)
Combat Mechanics: Projektilsystemer
: Våbennedkøling
: Skadeberegning
: Visuel feedback
section Avanceret Fysik (50 minutter)
Real-time Systems: Frame rate optimering
: Rumlig opdeling
: Kollision respons
: Fysiksimulering
section Professionelle Teknikker (1 uge)
Game Engine Concepts: Komponentsystemer
: Fysik pipelines
: Ydelsesanalyse
: Tværplatformsoptimering
section Industri Anvendelser (1 måned)
Production Skills: Storskala optimering
: Team samarbejde
: Engine udvikling
: Platform implementering
```
### 🛠️ Dit spilfysikværktøjssæt resumé
Efter at have gennemført denne lektion, har du nu mestret:
- **Kollisionsmatematik**: Rektangelintersektionsalgoritmer og koordinatsystemer
- **Performanceoptimering**: Effektiv kollisionsdetektion til realtidsapplikationer
- **Objektlivscyklusstyring**: Sikker oprettelse, opdatering og destruktion
- **Event-drevet arkitektur**: Afkoblede systemer til kollisionsrespons
- **Spil-loop integration**: Opdateringer af fysik pr. frame og render-koordinering
- **Inputsystemer**: Reagerende kontroller med ratebegrænsning og feedback
- **Hukommelsesstyring**: Effektiv objektpooling og oprydningsstrategier
**Anvendelser i virkeligheden**: Dine kollisionsdetektionsfærdigheder bruges direkte til:
- **Interaktive simuleringer**: Videnskabelig modellering og undervisningsværktøjer
- **Brugerfladedesign**: Drag-and-drop interaktioner og touch-detektion
- **Datavisualisering**: Interaktive diagrammer og klikbare elementer
- **Mobiludvikling**: Touchgestusgenkendelse og kollisionshåndtering
- **Robotprogrammering**: Ruteplanlægning og forhindringsundgåelse
- **Computergrafik**: Ray tracing og rumlige algoritmer
**Professionelle færdigheder opnået**: Nu kan du:
- **Designe** effektive algoritmer til realtids kollisionsdetektion
- **Implementere** fysiksystemer der skalerer med objektkompleksitet
- **Debugge** komplekse interaktionssystemer ved hjælp af matematiske principper
- **Optimere** ydeevnen for forskellige hardware- og browsermuligheder
- **Arkitektere** vedligeholdelige spilsystemer med gennemprøvede designmønstre
**Konceptmæssige spils udviklingsfærdigheder mestret**:
- **Fysiksimulering**: Realtids kollisionsdetektion og respons
- **Performance engineering**: Optimerede algoritmer til interaktive applikationer
- **Event-systemer**: Afkoblede kommunikationssystemer mellem spilkomponenter
- **Objekthåndtering**: Effektive livscyklusmønstre til dynamisk indhold
- **Inputhåndtering**: Reagerende kontroller med passende feedback
**Næste niveau**: Du er klar til at udforske avancerede fysikmotorer som Matter.js, implementere 3D kollisionsdetektion eller bygge komplekse partikelsystemer!
🌟 **Opnåelse låst op**: Du har bygget et komplet fysikbaseret interaktionssystem med professionel kollisionsdetektion!
## GitHub Copilot Agent Challenge 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstanden til at løse denne udfordring:
**Beskrivelse:** Forbedr kollisionsdetektionssystemet ved at implementere power-ups, der spawner tilfældigt og giver midlertidige evner, når de samles op af helteskibet.
**Beskrivelse:** Forbedr kollisionsdetektionssystemet ved at implementere power-ups der spawner tilfældigt og giver midlertidige evner, når de samles op af helteskibet.
**Opgave:** Opret en PowerUp-klasse, der udvider GameObject, og implementer kollisionsdetektion mellem helten og power-ups. Tilføj mindst to typer power-ups: en, der øger skydehastigheden (reducerer cooldown), og en anden, der skaber et midlertidigt skjold. Inkluder spawn-logik, der skaber power-ups med tilfældige intervaller og positioner.
**Prompt:** Lav en PowerUp-klasse der udvider GameObject og implementer kollisionsdetektion mellem helten og power-ups. Tilføj mindst to typer power-ups: en som øger ildhastigheden (reducerer cooldown) og en anden som skaber et midlertidigt skjold. Inkluder spawn-logik der skaber power-ups på tilfældige intervaller og positioner.
---
@ -445,15 +754,15 @@ Brug Agent-tilstand til at fuldføre følgende udfordring:
## 🚀 Udfordring
Tilføj en eksplosion! Tag et kig på spilressourcerne i [Space Art repo](../../../../6-space-game/solution/spaceArt/readme.txt) og prøv at tilføje en eksplosion, når laseren rammer en alien.
Tilføj en eksplosion! Tag et kig på spillets assets i [the Space Art repo](../../../../6-space-game/solution/spaceArt/readme.txt) og prøv at tilføje en eksplosion når laseren rammer en alien
## Quiz efter lektionen
## Quiz efter forelæsning
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/36)
[Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/36)
## Gennemgang & Selvstudie
Eksperimenter med intervallerne i dit spil indtil videre. Hvad sker der, når du ændrer dem? Læs mere om [JavaScript timing events](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
Eksperimenter med intervallerne i dit spil indtil nu. Hvad sker der, når du ændrer dem? Læs mere om [JavaScript timing events](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Opgave
@ -461,5 +770,7 @@ Eksperimenter med intervallerne i dit spil indtil videre. Hvad sker der, når du
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på originalsproget bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,24 +1,88 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "d642759cf1542f554871f74956a59af9",
"translation_date": "2025-10-23T22:12:45+00:00",
"original_hash": "2ed9145a16cf576faa2a973dff84d099",
"translation_date": "2026-01-06T23:40:18+00:00",
"source_file": "6-space-game/5-keeping-score/README.md",
"language_code": "da"
}
-->
# Byg et Rumspil Del 5: Point og Liv
## Quiz før forelæsning
[Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/37)
Klar til at gøre dit rumspil til et rigtigt spil? Lad os tilføje point og håndtering af liv - de kerneelementer, der forvandlede tidlige arkadespil som Space Invaders fra simple demonstrationer til vanedannende underholdning. Det er her, dit spil bliver virkelig spilbart.
## Tegn tekst på skærmen - Din spils stemme
# Byg et rumspil Del 5: Point og liv
```mermaid
journey
title Din Spildesign Rejse
section Spiller Feedback
Forstå scoringspsykologi: 3: Student
Lær visuel kommunikation: 4: Student
Design belønningssystemer: 4: Student
section Teknisk Implementering
Canvas tekstgengivelse: 4: Student
Tilstandsadministration: 5: Student
Begivenhedsdrevne opdateringer: 5: Student
section Polering af Spil
Brugeroplevelsesdesign: 5: Student
Balancer udfordring og belønning: 5: Student
Skab engagerende gameplay: 5: Student
```
## Før-forelæsning quiz
[Før-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/37)
Klar til at få dit rumspil til at føles som et rigtigt spil? Lad os tilføje pointsystem og livsstyring - de kerne-mekanikker, der forvandlede tidlige arkadespil som Space Invaders fra simple demonstrationer til vanedannende underholdning. Her bliver dit spil virkelig spilbart.
```mermaid
mindmap
root((Spil Feedback Systemer))
Visual Communication
Text Rendering
Icon Display
Color Psychology
Layout Design
Scoring Mechanics
Point Values
Reward Timing
Progress Tracking
Achievement Systems
Life Management
Risk vs Reward
Player Agency
Difficulty Balance
Recovery Mechanics
User Experience
Immediate Feedback
Clear Information
Emotional Response
Engagement Loops
Implementation
Canvas API
State Management
Event Systems
Performance
```
## Tegn tekst på skærmen - dit spils stemme
For at vise din score skal vi lære, hvordan man gengiver tekst på lærredet. Metoden `fillText()` er dit primære værktøj til dette - det er den samme teknik, som blev brugt i klassiske arkadespil til at vise point og statusinformation.
For at vise din score skal vi lære at tegne tekst på canvas. Metoden `fillText()` er dit primære værktøj til dette - det samme teknik som blev brugt i klassiske arkadespil til at vise scores og statusinformation.
```mermaid
flowchart LR
A["📝 Tekstindhold"] --> B["🎨 Styling"]
B --> C["📍 Placering"]
C --> D["🖼️ Canvas Rendering"]
E["Skrifttype"] --> B
F["Skriftstørrelse"] --> B
G["Farve"] --> B
H["Justering"] --> B
I["X Koordinat"] --> C
J["Y Koordinat"] --> C
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
```
Du har fuld kontrol over tekstens udseende:
```javascript
@ -28,24 +92,74 @@ ctx.textAlign = "right";
ctx.fillText("show this on the screen", 0, 0);
```
✅ Dyk dybere ned i [tilføjelse af tekst til et lærred](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text) - du vil måske blive overrasket over, hvor kreativ du kan være med skrifttyper og styling!
✅ Dyk dybere ned i [at tilføje tekst til et canvas](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_text) - du vil måske blive overrasket over, hvor kreativ du kan være med fonte og styling!
## Liv - Mere end bare et tal
I spildesign repræsenterer et "liv" spillerens margin for fejl. Dette koncept går tilbage til flippermaskiner, hvor man fik flere kugler at spille med. I tidlige videospil som Asteroids gav liv spillerne tilladelse til at tage chancer og lære af deres fejl.
I spildesign repræsenterer et "liv" spillerens margin for fejl. Dette koncept går tilbage til flippermaskiner, hvor du fik flere bolde at spille med. I tidlige videospil som Asteroids gav liv spillerne tilladelse til at tage risici og lære af fejl.
Visuel repræsentation betyder meget - at vise skibsikoner i stedet for blot "Liv: 3" skaber øjeblikkelig visuel genkendelse, ligesom tidlige arkadekabinetter brugte ikonografi til at kommunikere på tværs af sprogbarrierer.
```mermaid
flowchart TD
A["🎮 Spillers Handling"] --> B{"Risikobedømmelse"}
B --> C["Høj Risiko, Høj Belønning"]
B --> D["Sikker Strategi"]
C --> E{"Resultat"}
D --> F["Stadig Fremskridt"]
E -->|Succes| G["🏆 Store Point"]
E -->|Fiasko| H["💔 Mist Liv"]
H --> I{"Liv Tilbage?"}
I -->|Ja| J["🔄 Prøv Igen"]
I -->|Nej| K["💀 Spil Slut"]
J --> B
G --> B
F --> B
style C fill:#ffebee
style D fill:#e8f5e8
style G fill:#e3f2fd
style H fill:#fff3e0
```
Visuel repræsentation betyder meget - at vise skibsikoner i stedet for bare "Liv: 3" skaber øjeblikkelig visuel genkendelse, ligesom tidlige arkadeskabe brugte ikonografi for at kommunikere på tværs af sprogbarrierer.
## Byg dit spils belønningssystem
Nu implementerer vi de kernefeedbacksystemer, der holder spillerne engagerede:
Nu implementerer vi de kerne feedbacksystemer, som holder spillere engagerede:
- **Pointsystem**: Hvert ødelagt fjendeskib giver 100 point (runde tal er lettere for spillere at beregne mentalt). Scoren vises nederst til venstre.
- **Livstæller**: Din helt starter med tre liv - en standard etableret af tidlige arkadespil for at balancere udfordring med spilbarhed. Hver kollision med en fjende koster et liv. Vi viser de resterende liv nederst til højre med skibsikoner ![livsbillede](../../../../translated_images/life.6fb9f50d53ee0413cd91aa411f7c296e10a1a6de5c4a4197c718b49bf7d63ebf.da.png).
```mermaid
sequenceDiagram
participant Player
participant GameEngine
participant ScoreSystem
participant LifeSystem
participant Display
Player->>GameEngine: Skyder Fjende
GameEngine->>ScoreSystem: Tildel Point
ScoreSystem->>ScoreSystem: +100 point
ScoreSystem->>Display: Opdater Score
Player->>GameEngine: Kolliderer med Fjende
GameEngine->>LifeSystem: Mist Liv
LifeSystem->>LifeSystem: -1 liv
LifeSystem->>Display: Opdater Liv
alt Liv > 0
LifeSystem->>Player: Fortsæt Med at Spille
else Liv = 0
LifeSystem->>GameEngine: Spillet Slut
end
```
- **Pointsystem**: Hver ødelagt fjendtlige skib giver 100 point (runde tal er lettere for spillere at regne mentalt). Scoren vises i nederste venstre hjørne.
- **Livstæller**: Din helt starter med tre liv - en standard etableret af tidlige arkadespil for at balancere udfordring med spilbarhed. Hver kollision med en fjende koster et liv. Vi viser resterende liv nederst til højre med skibsikoner ![liv billede](../../../../translated_images/life.6fb9f50d53ee0413.da.png).
## Lad os komme i gang!
Først skal du sætte din arbejdsplads op. Naviger til filerne i din `your-work` undermappe. Du bør se disse filer:
Først skal du sætte dit arbejdsområde op. Naviger til filerne i din `your-work` undermappe. Du skulle gerne se disse filer:
```bash
-| assets
@ -57,24 +171,49 @@ Først skal du sætte din arbejdsplads op. Naviger til filerne i din `your-work`
-| package.json
```
For at teste dit spil skal du starte udviklingsserveren fra mappen `your_work`:
For at teste dit spil skal du starte udviklingsserveren fra `your_work` mappen:
```bash
cd your-work
npm start
```
Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i din browser for at se dit spil. Test kontrollerne med piletasterne og prøv at skyde fjender for at verificere, at alt fungerer.
Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i din browser for at se dit spil. Test kontrollerne med piletasterne og prøv at skyde fjender for at sikre, at alt virker.
```mermaid
flowchart TD
A["1. Asset indlæsning"] --> B["2. Spilvariabler"]
B --> C["3. Kollisionsregistrering"]
C --> D["4. Helteforbedring"]
D --> E["5. Vise funktioner"]
E --> F["6. Begivenhedshåndterere"]
G["Livsikonbillede"] --> A
H["Score & Livsopfølgning"] --> B
I["Helte-fjende krydsninger"] --> C
J["Point- & livmetoder"] --> D
K["Tekst- & ikonrendering"] --> E
L["Belønning- & straflogik"] --> F
F --> M["🎮 Færdigt spil"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
### Tid til at kode!
1. **Hent de visuelle ressourcer, du skal bruge**. Kopier `life.png` ressourcen fra mappen `solution/assets/` til din `your-work` mappe. Tilføj derefter lifeImg til din window.onload funktion:
1. **Hent de visuelle ressourcer du skal bruge**. Kopiér `life.png` ressourcen fra `solution/assets/` mappen ind i din `your-work` mappe. Tilføj derefter lifeImg til din window.onload funktion:
```javascript
lifeImg = await loadTexture("assets/life.png");
```
1. Glem ikke at tilføje `lifeImg` til din liste over ressourcer:
1. Glem ikke at tilføje `lifeImg` til din ressourceliste:
```javascript
let heroImg,
@ -84,9 +223,9 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
eventEmitter = new EventEmitter();
```
2. **Opsæt dine spilvariabler**. Tilføj noget kode for at spore din samlede score (starter ved 0) og resterende liv (starter ved 3). Vi viser disse på skærmen, så spillerne altid ved, hvor de står.
2. **Opsæt dine spilvariabler**. Tilføj noget kode for at holde styr på din samlede score (starter på 0) og resterende liv (starter på 3). Vi viser disse på skærmen, så spillere altid ved, hvor de står.
3. **Implementer kollisionsdetektion**. Udvid din `updateGameObjects()` funktion til at opdage, når fjender kolliderer med din helt:
3. **Implementer kollisionsdetektion**. Udvid din `updateGameObjects()` funktion til at registrere, når fjender kolliderer med din helt:
```javascript
enemies.forEach(enemy => {
@ -97,8 +236,8 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
})
```
4. **Tilføj livs- og pointsporing til din helt**.
1. **Initialiser tællerne**. Under `this.cooldown = 0` i din `Hero` klasse, opsæt liv og point:
4. **Tilføj livs- og pointregistrering til din helt**.
1. **Initialiser tællerne**. Under `this.cooldown = 0` i din `Hero` klasse, sæt liv og point op:
```javascript
this.life = 3;
@ -132,16 +271,32 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
```
1. **Integrer alt i din spilsløjfe**. Tilføj disse funktioner til din window.onload funktion lige efter `updateGameObjects()`:
1. **Kobl det hele sammen i din spilloop**. Tilføj disse funktioner til din window.onload funktion lige efter `updateGameObjects()`:
```javascript
drawPoints();
drawLife();
```
1. **Implementer konsekvenser og belønninger i spillet**. Nu tilføjer vi de feedbacksystemer, der gør spillerens handlinger meningsfulde:
### 🔄 **Pædagogisk check-in**
**Forståelse af spildesign**: Før du implementerer konsekvenser, skal du sikre dig, at du forstår:
- ✅ Hvordan visuel feedback kommunikerer spillets tilstand til spillere
- ✅ Hvorfor konsekvent placering af UI-elementer forbedrer brugervenligheden
- ✅ Psykologien bag pointværdier og livsstyring
- ✅ Hvordan text rendering på canvas adskiller sig fra HTML tekst
**Hurtig selvtest**: Hvorfor bruger arkadespil typisk runde tal til pointværdier?
*Svar: Runde tal er lettere for spillere at regne mentalt og skaber tilfredsstillende psykologiske belønninger*
**Principper for brugeroplevelse**: Du anvender nu:
- **Visuel hierarki**: Vigtig information placeret fremtrædende
- **Umiddelbar feedback**: Opdatering i realtid på spillerhandlinger
- **Kognitiv belastning**: Enkel og klar informationspræsentation
- **Emotionelt design**: Ikoner og farver, der skaber spillerforbindelse
1. **Kollisioner koster liv**. Hver gang din helt støder ind i en fjende, skal du miste et liv.
1. **Implementer spilkonsekvenser og belønninger**. Nu tilføjer vi feedbacksystemerne, der gør spillerhandlinger meningsfulde:
1. **Kollisioner koster liv**. Hver gang din helt kolliderer med en fjende, mister du et liv.
Tilføj denne metode til din `Hero` klasse:
@ -154,9 +309,9 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
}
```
2. **At skyde fjender giver point**. Hvert vellykket skud giver 100 point, hvilket giver øjeblikkelig positiv feedback for præcise skud.
2. **At skyde fjender giver point**. Hvert vellykket skud giver 100 point og giver øjeblikkelig positiv feedback for præcis skydning.
Udvid din Hero klasse med denne metode til at øge point:
Udvid din Hero-klasse med denne increment metode:
```javascript
incrementPoints() {
@ -164,7 +319,7 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
}
```
Forbind nu disse funktioner til dine kollisionshændelser:
Kobl nu disse funktioner til dine kollisionshændelser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
@ -179,41 +334,177 @@ Dette kører en lokal server på `http://localhost:5000`. Åbn denne adresse i d
});
```
✅ Nysgerrig efter andre spil bygget med JavaScript og Canvas? Udforsk lidt - du vil måske blive overrasket over, hvad der er muligt!
✅ Nysgerrig efter andre spil bygget med JavaScript og Canvas? Gå på opdagelse - du vil måske blive overrasket over, hvad der er muligt!
Efter at have implementeret disse funktioner, test dit spil for at se det komplette feedbacksystem i aktion. Du bør se livsikoner nederst til højre, din score nederst til venstre, og se hvordan kollisioner reducerer liv, mens vellykkede skud øger din score.
Dit spil har nu de essentielle mekanikker, der gjorde tidlige arkadespil så fængslende - klare mål, øjeblikkelig feedback og meningsfulde konsekvenser for spillerens handlinger.
Dit spil har nu de essentielle mekanikker, der gjorde tidlige arkadespil så fængslende - klare mål, øjeblikkelig feedback og meningsfulde konsekvenser for spillerhandlinger.
### 🔄 **Pædagogisk check-in**
**Komplet spildesign system**: Bekræft din mestring af spiller-feedbacksystemer:
- ✅ Hvordan skaber pointsystemer motivation og engagement hos spillere?
- ✅ Hvorfor er visuel konsistens vigtig for brugergrænsefladedesign?
- ✅ Hvordan balancerer livssystemet udfordring med fastholdelse af spillere?
- ✅ Hvilken rolle spiller øjeblikkelig feedback i at skabe tilfredsstillende gameplay?
**Systemintegration**: Dit feedbacksystem demonstrerer:
- **Brugeroplevelsesdesign**: Klar visuel kommunikation og informationshierarki
- **Event-driven arkitektur**: Responsiv opdatering på spillerhandlinger
- **State management**: Sporing og visning af dynamiske spildata
- **Canvas mestring**: Tekstrendering og spriteplacering
- **Spilpsykologi**: Forståelse af spiller-motivation og engagement
**Professionelle mønstre**: Du har implementeret:
- **MVC arkitektur**: Adskillelse af spil-logik, data og præsentation
- **Observer-pattern**: Event-drevne opdateringer af spiltilstande
- **Komponentdesign**: Genanvendelige funktioner til rendering og logik
- **Performanceoptimering**: Effektiv rendering i spilloops
### ⚡ **Hvad du kan nå på 5 minutter**
- [ ] Eksperimenter med forskellige fontstørrelser og farver til scorevisning
- [ ] Prøv at ændre pointværdier og se hvordan det påvirker spiloplevelsen
- [ ] Tilføj console.log udsagn for at spore, hvornår point og liv ændres
- [ ] Test kanttilfælde som at løbe tør for liv eller opnå høje scores
### 🎯 **Hvad du kan nå i denne time**
- [ ] Færdiggør quizzen efter lektionen og forstå spilpsykologi
- [ ] Tilføj lydeffekter for point og tab af liv
- [ ] Implementer et highscore system med localStorage
- [ ] Opret forskellige pointværdier for forskellige fjendetyper
- [ ] Tilføj visuelle effekter som f.eks. skærmrystelse ved tab af liv
### 📅 **Din uge-lange spildesign rejse**
- [ ] Fuldfør det komplette rumspil med polerede feedbacksystemer
- [ ] Implementer avancerede pointsystemer som combo-multiplikatorer
- [ ] Tilføj achievements og låsbart indhold
- [ ] Skab sværhedsprogression og balanceringssystemer
- [ ] Design brugergrænseflader til menuer og game over-skærme
- [ ] Studer andre spil for at forstå engagementsmekanismer
### 🌟 **Din måneds-lange spiludviklingsmestring**
- [ ] Byg komplette spil med sofistikerede progression systemer
- [ ] Lær spil-analyse og måling af spilleradfærd
- [ ] Bidrag til open source spiludviklingsprojekter
- [ ] Mestring af avancerede spildesignmønstre og monetisering
- [ ] Skab undervisningsindhold om spildesign og brugeroplevelse
- [ ] Byg en portfolio, der viser spildesign og udviklingsevner
## 🎯 Din spildesign mestringstidslinje
```mermaid
timeline
title Game Design & Spillerfeedback Læringsforløb
section Grundlag (10 minutter)
Visuel Kommunikation: Tekstgengivelse
: Ikondesign
: Layoutprincipper
: Farvepsykologi
section Spillerpsykologi (20 minutter)
Motivationssystemer: Pointværdier
: Risiko vs belønning
: Fremdriftsfeedback
: Præstationsdesign
section Teknisk Implementering (30 minutter)
Canvas Mestring: Tekstpositionering
: Spritegengivelse
: Tilstandsadministration
: Ydelsesoptimering
section Spilbalance (40 minutter)
Sværhedsdesign: Livsadministration
: Scoringskurver
: Spillerfastholdelse
: Tilgængelighed
section Brugeroplevelse (50 minutter)
Interface Design: Informationshierarki
: Responsiv feedback
: Emotionelt design
: Brugervenlighedstest
section Avancerede Systemer (1 uge)
Spilmekanikker: Fremgangssystemer
: Analyseintegration
: Monetiseringsdesign
: Community-funktioner
section Branchefærdigheder (1 måned)
Professionel Udvikling: Team samarbejde
: Designdokumentation
: Spillerforskning
: Platformoptimering
```
### 🛠️ Dine spildesign værktøjer - resumé
Efter at have gennemført denne lektion mestrer du nu:
- **Spillerpsykologi**: Forståelse af motivation, risiko/belønning og engagementsløjfer
- **Visuel kommunikation**: Effektivt UI-design med tekst, ikoner og layout
- **Feedbacksystemer**: Respons i realtid på spillerhandlinger og spilevents
- **State management**: Effektiv sporing og visning af dynamiske spildata
- **Canvas tekstrendering**: Professionel tekstvisning med styling og placering
- **Eventsintegration**: Kobling af brugerhandlinger med meningsfulde spilkonsekvenser
- **Spilbalance**: Design af sværhedskurver og spillerprogression
**Anvendelser i praksis**: Dine spildesignfærdigheder gælder direkte til:
- **Brugergrænsefladedesign**: Skabelse af engagerende og intuitive interfaces
- **Produktudvikling**: Forståelse af brugermotivation og feedbacksløjfer
- **Undervisningsteknologi**: Gamification og læringsengagementssystemer
- **Datavisualisering**: Gøre kompleks information tilgængelig og engagerende
- **Mobil app-udvikling**: Fastholdelsesmekanik og brugeroplevelsesdesign
- **Marketingteknologi**: Forståelse af brugeradfærd og konverteringsoptimering
**Professionelle færdigheder erhvervet**: Du kan nu:
- **Designe** brugeroplevelser, der motiverer og engagerer brugere
- **Implementere** feedbacksystemer, der effektivt styrer brugeradfærd
- **Balancere** udfordring og tilgængelighed i interaktive systemer
- **Skabe** visuel kommunikation, der virker på tværs af brugergrupper
- **Analysere** brugeradfærd og iterere på designforbedringer
**Spiludviklingsbegreber mestre**:
- **Spillermotivation**: Forstå, hvad der driver engagement og fastholdelse
- **Visuelt design**: Skabe klare, attraktive og funktionelle interfaces
- **Systemintegration**: Forbinde flere spilsystemer til en sammenhængende oplevelse
- **Performanceoptimering**: Effektiv rendering og state management
- **Tilgængelighed**: Design til forskellige færdighedsniveauer og spillerbehov
**Næste niveau**: Du er klar til at udforske avancerede spildesignmønstre, implementere analyssystemer eller studere spilmonetisering og spillerfastholdelsesstrategier!
🌟 **Præstation Opnået**: Du har bygget et komplet spiller-feedbacksystem med professionelle spildesignprincipper!
---
## GitHub Copilot Agent Challenge 🚀
## GitHub Copilot Agent-udfordring 🚀
Brug Agent mode til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Forbedr rumspillets pointssystem ved at implementere en high score funktion med persistent lagring og bonuspoint-mekanik.
**Beskrivelse:** Forbedr rumspillets pointsystem ved at implementere et highscore-system med persistent lagring og bonuspointmekanikker.
**Opgave:** Opret et high score system, der gemmer spillerens bedste score i localStorage. Tilføj bonuspoint for konsekutive fjendedrab (kombosystem) og implementer forskellige pointværdier for forskellige fjendetyper. Inkluder en visuel indikator, når spilleren opnår en ny high score, og vis den aktuelle high score på spilskærmen.
**Prompt:** Opret et highscore-system, der gemmer spillerens bedste score i localStorage. Tilføj bonuspoint for på hinanden følgende fjendtdrab (combo-system) og implementer forskellige pointværdier for forskellige fjendetyper. Inkluder en visuel indikator, når spilleren opnår en ny highscore, og vis den aktuelle highscore på spilkærmen.
## 🚀 Udfordring
Du har nu et funktionelt spil med point og liv. Overvej hvilke yderligere funktioner der kunne forbedre spilleroplevelsen.
Du har nu et funktionelt spil med point og liv. Overvej hvilke ekstra funktioner der kunne forbedre spilleroplevelsen.
## Quiz efter forelæsning
## Efter-forelæsning quiz
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/38)
[Efter-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/38)
## Gennemgang & Selvstudie
Vil du udforske mere? Undersøg forskellige tilgange til spilscoring og livssystemer. Der findes fascinerende spilmotorer derude som [PlayFab](https://playfab.com), der håndterer scoring, ranglister og spillerudvikling. Hvordan kunne integration af noget lignende tage dit spil til næste niveau?
Vil du udforske mere? Undersøg forskellige tilgange til pointsystemer og livssystemer i spil. Der findes fascinerende spilengines som [PlayFab](https://playfab.com), der håndterer scoring, leaderboards og spillerprogression. Hvordan kunne integration af noget sådan løfte dit spil til næste niveau?
## Opgave
[Byg et Pointspil](assignment.md)
[Byg et pointsystem spil](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,42 +1,146 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "46bcdd9a0174031655a49bb062aa279c",
"translation_date": "2025-10-23T22:10:54+00:00",
"original_hash": "a4b78043f4d64bf3ee24e0689b8b391d",
"translation_date": "2026-01-06T23:38:57+00:00",
"source_file": "6-space-game/6-end-condition/README.md",
"language_code": "da"
}
-->
# Byg et Rumspil Del 6: Afslutning og Genstart
Ethvert godt spil har brug for klare afslutningsbetingelser og en glidende genstartsmekanisme. Du har allerede bygget et imponerende rumspil med bevægelse, kamp og pointgivning - nu er det tid til at tilføje de sidste brikker, der gør det komplet.
Dit spil kører i øjeblikket uendeligt, ligesom Voyager-sonderne, som NASA sendte ud i rummet i 1977 - og som stadig rejser gennem rummet årtier senere. Selvom det er fint til rumforskning, har spil brug for definerede slutpunkter for at skabe tilfredsstillende oplevelser.
I dag implementerer vi ordentlige vinder-/taberbetingelser og et genstartssystem. Når du er færdig med denne lektion, vil du have et poleret spil, som spillere kan fuldføre og spille igen, ligesom de klassiske arkadespil, der definerede mediet.
## Quiz før lektionen
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/39)
## Forståelse af spillets afslutningsbetingelser
Hvornår skal dit spil slutte? Dette fundamentale spørgsmål har formet spildesign siden den tidlige arkadeæra. Pac-Man slutter, når du bliver fanget af spøgelser eller rydder alle prikker, mens Space Invaders slutter, når rumvæsnerne når bunden eller du har ødelagt dem alle.
Som spilskaber definerer du selv sejr- og nederlagsbetingelserne. For vores rumspil er her nogle velprøvede tilgange, der skaber engagerende gameplay:
- **`N` fjendtlige skibe er blevet ødelagt**: Det er ret almindeligt, hvis du deler et spil op i forskellige niveauer, at du skal ødelægge `N` fjendtlige skibe for at fuldføre et niveau.
- **Dit skib er blevet ødelagt**: Der er bestemt spil, hvor du taber, hvis dit skib bliver ødelagt. En anden almindelig tilgang er at have et livssystem. Hver gang dit skib bliver ødelagt, mister du et liv. Når alle liv er tabt, taber du spillet.
- **Du har samlet `N` point**: En anden almindelig afslutningsbetingelse er at samle point. Hvordan du får point, er op til dig, men det er ret almindeligt at tildele point til forskellige aktiviteter som at ødelægge et fjendtligt skib eller måske samle genstande, som fjender *taber*, når de bliver ødelagt.
- **Fuldfør et niveau**: Dette kan involvere flere betingelser såsom `X` ødelagte fjendtlige skibe, `Y` samlede point eller måske, at en specifik genstand er blevet samlet.
## Implementering af genstartsfunktionalitet
Gode spil opmuntrer til genafspilning gennem glidende genstartsmekanismer. Når spillere fuldfører et spil (eller møder nederlag), vil de ofte gerne prøve igen med det samme - enten for at slå deres score eller forbedre deres præstation.
Tetris er et perfekt eksempel: Når dine blokke når toppen, kan du straks starte et nyt spil uden at navigere gennem komplekse menuer. Vi vil bygge et lignende genstartssystem, der renser spiltilstanden og hurtigt får spillerne tilbage i aktion.
# Byg et rumspil del 6: Afslutning og genstart
```mermaid
journey
title Din Spils Fuldførelsesrejse
section Slutbetingelser
Definer vinder/tab-tilstande: 3: Student
Implementer tilstandstjek: 4: Student
Håndter tilstandsovergange: 4: Student
section Spilleroplevelse
Design feedbacksystemer: 4: Student
Skab genstartsmekanikker: 5: Student
Poler brugergrænsefladen: 5: Student
section Systemintegration
Administrer spillets livscyklus: 5: Student
Håndter hukommelsesoprydning: 5: Student
Skab komplet oplevelse: 5: Student
```
Ethvert fantastisk spil har brug for klare slutbetingelser og en glidende genstartsmekanisme. Du har bygget et imponerende rumspil med bevægelse, kamp og scoring nu er det tid til at tilføje de sidste elementer, der får det til at føles komplet.
Dit spil kører i øjeblikket uendeligt, ligesom Voyager-sonderne, som NASA sendte afsted i 1977 stadig på rejse gennem rummet årtier senere. Mens det er fint til rumforskning, har spil brug for definerede slutpunkter for at skabe tilfredsstillende oplevelser.
I dag implementerer vi korrekte sejr/ nederlagsbetingelser og et genstartssystem. Ved afslutningen af denne lektion vil du have et poleret spil, som spillere kan gennemføre og spille igen, ligesom de klassiske arkadespil, der definerede mediet.
```mermaid
mindmap
root((Spilafslutning))
End Conditions
Victory States
Defeat Conditions
Progress Tracking
State Validation
Player Feedback
Visual Messages
Color Psychology
Clear Communication
Emotional Response
State Management
Game Loop Control
Memory Cleanup
Object Lifecycle
Event Handling
Restart Systems
Input Handling
State Reset
Fresh Initialization
User Experience
Polish Elements
Message Display
Smooth Transitions
Error Prevention
Accessibility
```
## Quiz inden forelæsningen
[Quiz inden forelæsningen](https://ff-quizzes.netlify.app/web/quiz/39)
## Forståelse af spillets slutbetingelser
Hvornår skal dit spil slutte? Dette grundlæggende spørgsmål har præget spildesign siden den tidlige arkade-æra. Pac-Man slutter, når du bliver fanget af spøgelser eller rydder alle prikker, mens Space Invaders slutter, når rumvæsnerne når bunden, eller du ødelægger dem alle.
Som spillets skaber definerer du sejrs- og nederlagsbetingelserne. For vores rumspil er her gennemprøvede tilgange, der skaber engagerende gameplay:
```mermaid
flowchart TD
A["🎮 Spilstart"] --> B{"Tjek betingelser"}
B --> C["Fjendetælling"]
B --> D["Heltes liv"]
B --> E["Pointgrænse"]
B --> F["Niveau fremskridt"]
C --> C1{"Fjender = 0?"}
D --> D1{"Liv = 0?"}
E --> E1{"Point ≥ Mål?"}
F --> F1{"Mål fuldført?"}
C1 -->|Ja| G["🏆 Sejr"]
D1 -->|Ja| H["💀 Nederlag"]
E1 -->|Ja| G
F1 -->|Ja| G
C1 -->|Nej| B
D1 -->|Nej| B
E1 -->|Nej| B
F1 -->|Nej| B
G --> I["🔄 Genstart mulighed"]
H --> I
style G fill:#e8f5e8
style H fill:#ffebee
style I fill:#e3f2fd
```
- **`N` fjendtlige skibe er blevet ødelagt**: Det er ganske almindeligt, hvis du opdeler et spil i forskellige niveauer, at du skal ødelægge `N` fjendtlige skibe for at gennemføre et niveau
- **Dit skib er blevet ødelagt**: Der findes uden tvivl spil, hvor du taber spillet, hvis dit skib ødelægges. En anden almindelig tilgang er, at du har et liv-system. Hver gang dit skib ødelægges, mister du et liv. Når alle liv er væk, taber du spillet.
- **Du har samlet `N` point**: En anden almindelig slutbetingelse er, at du samler point. Hvordan du får point, er op til dig, men det er ret almindeligt at tildele point for forskellige aktiviteter som at ødelægge et fjendtlige skib eller måske samle genstande, som *falder* når de ødelægges.
- **Gennemfør et niveau**: Dette kan involvere flere betingelser såsom `X` fjendtlige skibe ødelagt, `Y` point samlet, eller måske at en specifik genstand er blevet samlet.
## Implementering af spilgenstart-funktionalitet
Gode spil opfordrer til genafspilning gennem glidende genstartsmekanismer. Når spillere gennemfører et spil (eller møder nederlag), vil de ofte prøve igen med det samme — enten for at slå deres score eller forbedre deres præstation.
```mermaid
stateDiagram-v2
[*] --> Playing: Spil start
Playing --> Victory: Alle fjender ødelagt
Playing --> Defeat: Liv = 0
Victory --> MessageDisplay: Vis vindermeddelelse
Defeat --> MessageDisplay: Vis tabermeddelelse
MessageDisplay --> WaitingRestart: Tryk på Enter prompt
WaitingRestart --> Resetting: Enter-tast trykket
Resetting --> CleanupMemory: Ryd intervaller
CleanupMemory --> ClearEvents: Fjern lyttere
ClearEvents --> InitializeGame: Frisk start
InitializeGame --> Playing: Nyt spil begynder
note right of MessageDisplay
Farvekodet feedback:
Grøn = Sejr
Rød = Nederlag
end note
note right of Resetting
Fuldt statustilbageførsel
forhindrer hukommelseslækager
end note
```
Tetris eksemplificerer dette perfekt: når dine blokke når toppen, kan du straks starte et nyt spil uden at navigere gennem komplekse menuer. Vi vil bygge et lignende genstartssystem, der renser spillets tilstand og får spillerne hurtigt tilbage i aktion.
**Refleksion**: Tænk over de spil, du har spillet. Under hvilke betingelser slutter de, og hvordan bliver du opfordret til at genstarte? Hvad gør en genstartoplevelse glidende i stedet for frustrerende?
**Refleksion**: Tænk de spil, du har spillet. Under hvilke betingelser slutter de, og hvordan bliver du opfordret til at genstarte? Hvad gør en genstartoplevelse glidende versus frustrerende?
## Hvad du vil bygge
@ -44,16 +148,16 @@ Du vil implementere de sidste funktioner, der forvandler dit projekt til en komp
**Her er, hvad vi tilføjer i dag:**
1. **Sejrsbetingelse**: Skyd alle fjenderne og få en ordentlig fejring (det har du fortjent!)
2. **Nederlagsbetingelse**: Løb tør for liv og mød nederlaget med en taberskærm
3. **Genstartsmekanisme**: Tryk på Enter for straks at starte igen - fordi ét spil aldrig er nok
4. **Tilstandshåndtering**: Ren start hver gang - ingen resterende fjender eller mærkelige fejl fra sidste spil
1. **Sejrsbetingelse**: Nedkæmp alle fjender og få en ordentlig fejring (det har du fortjent!)
2. **Nederlagsbetingelse**: Løb tør for liv og stå ansigt til ansigt med nederlaget på en nederlagsskærm
3. **Genstartsmekanisme**: Tryk på Enter for at hoppe direkte tilbage for ét spil er aldrig nok
4. **Tilstandsstyring**: Ren tavle hver gang ingen resterende fjender eller mærkelige fejl fra sidste spil
## Kom godt i gang
## Kom i gang
Lad os forberede dit udviklingsmiljø. Du bør have alle dine rumspilsfiler fra de tidligere lektioner klar.
**Dit projekt bør se nogenlunde sådan ud:**
**Dit projekt skulle gerne se nogenlunde sådan ud:**
```bash
-| assets
@ -75,18 +179,43 @@ npm start
**Denne kommando:**
- Kører en lokal server på `http://localhost:5000`
- Serverer dine filer korrekt
- Opdaterer automatisk, når du foretager ændringer
Åbn `http://localhost:5000` i din browser og verificer, at dit spil kører. Du bør kunne bevæge dig, skyde og interagere med fjender. Når det er bekræftet, kan vi fortsætte med implementeringen.
> 💡 **Pro Tip**: For at undgå advarsler i Visual Studio Code, deklarer `gameLoopId` øverst i din fil som `let gameLoopId;` i stedet for at deklarere det inde i funktionen `window.onload`. Dette følger moderne JavaScript-praksis for variabeldeklaration.
- Server dine filer korrekt
- Opdaterer automatisk, når du laver ændringer
Åbn `http://localhost:5000` i din browser og bekræft, at dit spil kører. Du bør kunne bevæge dig, skyde og interagere med fjender. Når det er bekræftet, kan vi fortsætte med implementeringen.
> 💡 **Pro tip**: For at undgå advarsler i Visual Studio Code, deklarér `gameLoopId` øverst i din fil som `let gameLoopId;` i stedet for at deklarere den inde i `window.onload`-funktionen. Dette følger moderne JavaScript bedste praksis for variabeldeklaration.
```mermaid
flowchart TD
A["1. Tilstandsregistrering"] --> B["2. Begivenhedshåndteringer"]
B --> C["3. Beskedkonstanter"]
C --> D["4. Genstartskontroller"]
D --> E["5. Beskedvisning"]
E --> F["6. Nulstil system"]
G["isHeroDead()\nisEnemiesDead()"] --> A
H["Kollisionsevents\nSpilafslutningsbegivenheder"] --> B
I["GAME_END_WIN\nGAME_END_LOSS"] --> C
J["Enter-tast\nGenstartstrigger"] --> D
K["Sejr/ nederlag\nFarvekodet tekst"] --> E
L["Tilstandsrydning\nFrisk initialisering"] --> F
F --> M["🎮 Færdigt spil"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
```
## Implementeringstrin
### Trin 1: Opret funktioner til spore afslutningsbetingelser
### Trin 1: Opret funktioner til overvågning af slutbetingelser
Vi har brug for funktioner til at overvåge, hvornår spillet skal slutte. Ligesom sensorer på den internationale rumstation, der konstant overvåger kritiske systemer, vil disse funktioner løbende tjekke spiltilstanden.
Vi har brug for funktioner, der overvåger, hvornår spillet skal slutte. Ligesom sensorer på den Internationale Rumstation, der konstant overvåger kritiske systemer, vil disse funktioner hele tiden tjekke spillets tilstand.
```javascript
function isHeroDead() {
@ -99,17 +228,50 @@ function isEnemiesDead() {
}
```
**Her er, hvad der sker bag kulisserne:**
- **Tjekker**, om vores helt er løbet tør for liv (av!)
- **Tæller**, hvor mange fjender der stadig er i live
- **Returnerer** `true`, når slagmarken er ryddet for fjender
**Her sker der følgende under motorhjelmen:**
- **Tjekker** om vores helt er løbet tør for liv (av!)
- **Tæller** hvor mange fjender der stadig lever og kæmper
- **Returnerer** `true`, når slagmarken er fri for fjender
- **Bruger** simpel sand/falsk logik for at holde det enkelt
- **Filtrerer** gennem alle spilobjekter for at finde de overlevende
### Trin 2: Opdater hændelseshåndterere for afslutningsbetingelser
Nu vil vi forbinde disse betingelsestjek med spillets hændelsessystem. Hver gang en kollision opstår, vil spillet evaluere, om det udløser en afslutningsbetingelse. Dette skaber øjeblikkelig feedback for kritiske spilbegivenheder.
- **Filtrerer** gennem alle spils objekter for at finde overlevende
### Trin 2: Opdater event handlers for slutbetingelser
Nu forbinder vi disse betingelsestjek til spillets eventsystem. Hver gang en kollision opstår, vurderer spillet, om det udløser en slutbetingelse. Dette skaber øjeblikkelig feedback på kritiske begivenheder.
```mermaid
sequenceDiagram
participant Collision
participant GameLogic
participant Conditions
participant EventSystem
participant Display
Collision->>GameLogic: Laser rammer fjende
GameLogic->>GameLogic: Ødelæg objekter
GameLogic->>Conditions: Tjek isEnemiesDead()
alt Alle fjender besejret
Conditions->>EventSystem: Send GAME_END_WIN
EventSystem->>Display: Vis sejrsbesked
else Fjender tilbage
Conditions->>GameLogic: Fortsæt spil
end
Collision->>GameLogic: Fjende rammer helt
GameLogic->>GameLogic: Formindsk liv
GameLogic->>Conditions: Tjek isHeroDead()
alt Liv = 0
Conditions->>EventSystem: Send GAME_END_LOSS
EventSystem->>Display: Vis nederlagsbesked
else Liv tilbage
GameLogic->>Conditions: Tjek isEnemiesDead()
alt Alle fjender besejret
Conditions->>EventSystem: Send GAME_END_WIN
end
end
```
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
@ -126,7 +288,7 @@ eventEmitter.on(Messages.COLLISION_ENEMY_HERO, (_, { enemy }) => {
hero.decrementLife();
if (isHeroDead()) {
eventEmitter.emit(Messages.GAME_END_LOSS);
return; // loss before victory
return; // tab før sejr
}
if (isEnemiesDead()) {
eventEmitter.emit(Messages.GAME_END_WIN);
@ -142,15 +304,15 @@ eventEmitter.on(Messages.GAME_END_LOSS, () => {
});
```
**Hvad der sker her:**
- **Laser rammer fjende**: Begge forsvinder, du får point, og vi tjekker, om du har vundet
- **Fjende rammer dig**: Du mister et liv, og vi tjekker, om du stadig er i live
- **Smart rækkefølge**: Vi tjekker for nederlag først (ingen ønsker at vinde og tabe på samme tid!)
- **Øjeblikkelige reaktioner**: Så snart noget vigtigt sker, ved spillet det
**Her sker der:**
- **Laser rammer fjende**: Begge forsvinder, du får point, og vi tjekker om du har vundet
- **Fjende rammer dig**: Du mister et liv, og vi tjekker om du stadig er i live
- **Smart rækkefølge**: Vi tjekker nederlag først (ingen vil vinde og tabe samtidigt!)
- **Øjeblikkelig reaktion**: Så snart noget vigtigt sker, ved spillet det
### Trin 3: Tilføj nye beskedkonstanter
### Trin 3: Tilføj nye besked-konstanter
Du skal tilføje nye beskedtyper til dit `Messages` konstantobjekt. Disse konstanter hjælper med at opretholde konsistens og forhindre tastefejl i dit hændelsessystem.
Du skal tilføje nye besked-typer til dit `Messages`-konstants objekt. Disse konstanter hjælper med at bevare konsistens og forhindre stavefejl i dit eventsystem.
```javascript
GAME_END_LOSS: "GAME_END_LOSS",
@ -158,15 +320,15 @@ GAME_END_WIN: "GAME_END_WIN",
```
**I ovenstående har vi:**
- **Tilføjet** konstanter for spilafslutningsbegivenheder for at opretholde konsistens
- **Brugt** beskrivende navne, der tydeligt angiver begivenhedens formål
- **Fulgte** den eksisterende navngivningskonvention for beskedtyper
- **Tilføjet** konstanter til spillets slut-events for at bevare konsistens
- **Brugt** beskrivende navne, der tydeligt angiver eventets formål
- **Følger** den eksisterende navngivningskonvention for besked-typer
### Trin 4: Implementer genstartskontroller
Nu vil du tilføje tastaturkontroller, der giver spillere mulighed for at genstarte spillet. Enter-tasten er et naturligt valg, da den ofte er forbundet med at bekræfte handlinger og starte nye spil.
Nu tilføjer du tastaturkontroller, der gør det muligt for spillere at genstarte spillet. Enter-tasten er et naturligt valg, da den ofte forbindes med at bekræfte handlinger og starte nye spil.
**Tilføj detektion af Enter-tasten til din eksisterende keydown-hændelseslytter:**
**Tilføj Enter-tast registrering til din eksisterende keydown-event listener:**
```javascript
else if(evt.key === "Enter") {
@ -174,23 +336,23 @@ else if(evt.key === "Enter") {
}
```
**Tilføj den nye beskedkonstant:**
**Tilføj den nye besked-konstant:**
```javascript
KEY_EVENT_ENTER: "KEY_EVENT_ENTER",
```
**Hvad du skal vide:**
- **Udvider** dit eksisterende tastaturhændelseshåndteringssystem
- **Bruger** Enter-tasten som genstartstrigger for intuitiv brugeroplevelse
- **Udsender** en brugerdefineret hændelse, som andre dele af dit spil kan lytte efter
- **Opretholder** samme mønster som dine andre tastaturkontroller
**Det, du skal vide:**
- **Udvider** dit eksisterende tastaturhåndteringssystem
- **Bruger** Enter-tasten som genstartsudløser for intuitiv brugeroplevelse
- **Udsender** en brugerdefineret event, som andre dele af dit spil kan lytte på
- **Bevarer** samme mønster som dine øvrige tastaturkontroller
### Trin 5: Opret beskedvisningssystem
### Trin 5: Opret beskedsystemet til visning
Dit spil skal kommunikere resultater tydeligt til spillerne. Vi opretter et beskedsystem, der viser sejrs- og nederlagstilstande med farvekodet tekst, ligesom terminalgrænsefladerne på tidlige computersystemer, hvor grøn indikerede succes, og rød signalerede fejl.
Dit spil skal kommunikere resultater tydeligt til spillerne. Vi laver et beskedsystem, der viser sejrs- og nederlags-tilstande med farvekodet tekst, ligesom terminalinterfacer fra tidlige computersystemer, hvor grøn indikerede succes og rød tegnede fejl.
**Opret funktionen `displayMessage()`:**
**Lav funktionen `displayMessage()`:**
```javascript
function displayMessage(message, color = "red") {
@ -201,20 +363,20 @@ function displayMessage(message, color = "red") {
}
```
**Trin for trin, her er hvad der sker:**
- **Indstiller** skrifttype og størrelse for klar og læsbar tekst
- **Anvender** en farveparameter med "rød" som standard for advarsler
- **Centrerer** teksten horisontalt og vertikalt på lærredet
**Trin for trin sker der:**
- **Sætter** skrifttype og størrelse for klar, læsbar tekst
- **Anvender** en farveparameter med "rød" som standard til advarsler
- **Centrerer** teksten horisontalt og vertikalt på canvas
- **Bruger** moderne JavaScript standardparametre for fleksible farvevalg
- **Udnytter** canvas 2D-konteksten til direkte tekstgengivelse
- **Udnytter** canvas 2D-kontekst til direkte tekstrendering
**Opret funktionen `endGame()`:**
**Lav funktionen `endGame()`:**
```javascript
function endGame(win) {
clearInterval(gameLoopId);
// Set a delay to ensure any pending renders complete
// Sæt en forsinkelse for at sikre, at eventuelle ventende gengivelser fuldføres
setTimeout(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
@ -233,19 +395,35 @@ function endGame(win) {
}
```
**Hvad denne funktion gør:**
- **Fryser** alt på plads - ingen flere bevægelige skibe eller lasere
- **Tager** en lille pause (200ms) for at lade den sidste frame blive tegnet færdig
- **Rydder** skærmen og maler den sort for dramatisk effekt
**Dette gør funktionen:**
- **Fryser** alt på plads ingen flere bevægelige skibe eller lasere
- **Holder** en kort pause (200ms) for at lade sidste frame færdigtegne
- **Tømmer** skærmen og maler den sort for dramatisk effekt
- **Viser** forskellige beskeder for vindere og tabere
- **Farvekoder** nyhederne - grøn for godt, rød for... ja, ikke så godt
- **Fortæller** spillerne præcis, hvordan de kan hoppe tilbage i spillet
- **Farvekoder** nyhederne grøn for godt, rød for… ikke så godt
- **Fortæller** spillerne præcis, hvordan de hopper tilbage ind
### 🔄 **Pædagogisk status**
**Spil-tilstandshåndtering**: Før du implementerer reset-funktionalitet, skal du sikre dig at du forstår:
- ✅ Hvordan slutbetingelser skaber klare gameplay-mål
- ✅ Hvorfor visuel feedback er essentiel for spillerforståelse
- ✅ Vigtigheden af korrekt oprydning for at undgå hukommelseslækager
- ✅ Hvordan event-drevet arkitektur muliggør rene tilstandsovergange
### Trin 6: Implementer spilnulstillingsfunktionalitet
**Hurtig selvevaluering**: Hvad ville der ske, hvis du ikke fjernede event listeners under reset?
*Svar: Hukommelseslækager og duplikerede event handlers, der giver uforudsigelig opførsel*
Nulstillingssystemet skal fuldstændigt rydde den aktuelle spiltilstand og initialisere en ny spilsession. Dette sikrer, at spillerne får en ren start uden resterende data fra det tidligere spil.
**Spildesign-principper**: Du implementerer nu:
- **Klare mål**: Spillere ved præcist, hvad der definerer succes og fiasko
- **Øjeblikkelig feedback**: Spiltilstandsændringer kommunikeres straks
- **Brugerkontrol**: Spillere kan genstarte, når de er klar
- **Systempålidelighed**: Korrekt oprydning forhindrer fejl og ydelsesproblemer
**Opret funktionen `resetGame()`:**
### Trin 6: Implementer spil reset-funktionalitet
Reset-systemet skal fuldstændigt rydde op i den nuværende spiltilstand og initialisere en frisk spilsession. Dette sikrer, at spillere får en ren start uden resterende data fra det tidligere spil.
**Lav funktionen `resetGame()`:**
```javascript
function resetGame() {
@ -267,14 +445,14 @@ function resetGame() {
```
**Lad os forstå hver del:**
- **Tjekker**, om en spilsløjfe kører i øjeblikket, før den nulstilles
- **Rydder** den eksisterende spilsløjfe for at stoppe al nuværende spilaktivitet
- **Fjerner** alle hændelseslyttere for at forhindre hukommelseslækager
- **Reinitialiserer** spiltilstanden med friske objekter og variabler
- **Starter** en ny spilsløjfe med alle de essentielle spilfunktioner
- **Opretholder** samme 100ms interval for konsistent spilpræstation
- **Tjekker** om en spil-loop kører, før der resettes
- **Rydder** den eksisterende spil-loop for at stoppe al nuværende spilaktivitet
- **Fjerner** alle event listeners for at forhindre hukommelseslækager
- **Geninitialiserer** spiltilstanden med friske objekter og variabler
- **Starter** en ny spil-loop med alle essentielle spilfunktioner
- **Bevarer** samme 100ms interval for konsistent spilpræstation
**Tilføj Enter-tastens hændelseshåndtering til din `initGame()`-funktion:**
**Tilføj Enter-tast eventhandler til din `initGame()`-funktion:**
```javascript
eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
@ -282,7 +460,7 @@ eventEmitter.on(Messages.KEY_EVENT_ENTER, () => {
});
```
**Tilføj metoden `clear()` til din EventEmitter-klasse:**
**Tilføj `clear()`-metoden til din EventEmitter-klasse:**
```javascript
clear() {
@ -290,79 +468,217 @@ clear() {
}
```
**Vigtige punkter at huske:**
- **Forbinder** Enter-tastetryk med nulstillingsfunktionaliteten
- **Registrerer** denne hændelseslytter under spilinitialisering
- **Giver** en ren måde at fjerne alle hændelseslyttere ved nulstilling
- **Forhindrer** hukommelseslækager ved at rydde hændelseshåndterere mellem spil
- **Nulstiller** lyttere-objektet til en tom tilstand for frisk initialisering
**Vigtigste punkter at huske:**
- **Forbinder** Enter-tasttryk med reset-funktionaliteten
- **Registrerer** denne eventlistener under spilinitialiseringen
- **Giver** en klar måde at fjerne alle event listeners ved reset
- **Forebygger** hukommelseslækager ved at rydde event handlers mellem spil
- **Resetter** listeners-objektet til en tom tilstand for frisk initialisering
## Tillykke! 🎉
👽 💥 🚀 Du har med succes bygget et komplet spil fra bunden. Ligesom programmørerne, der skabte de første videospil i 1970'erne, har du forvandlet linjer af kode til en interaktiv oplevelse med ordentlige spilmekanikker og brugerfeedback. 🚀 💥 👽
👽 💥 🚀 Du har med succes bygget et komplet spil fra bunden. Ligesom programmørerne, der skabte de første videospil i 1970'erne, har du forvandlet linjer af kode til en interaktiv oplevelse med korrekte spilmekanikker og brugerfeedback. 🚀 💥 👽
**Du har opnået:**
- **Implementeret** komplette vinder- og taberbetingelser med brugerfeedback
- **Skabt** et glidende genstartssystem for kontinuerlig gameplay
- **Designet** klar visuel kommunikation for spiltilstande
- **Håndteret** komplekse spiltilstandsovergange og oprydning
- **Sammensat** alle komponenter til et sammenhængende, spilbart spil
- **Implementeret** komplette sejr- og nederlagsbetingelser med brugerfeedback
- **Skabt** et sømløst genstartssystem til kontinuerligt gameplay
- **Designet** klar visuel kommunikation af spiltilstande
- **Håndteret** komplekse spil-tilstandsovergange og oprydning
- **Samlet** alle komponenter til et sammenhængende, spilbart spil
### 🔄 **Pædagogisk status**
**Komplet spils udviklingssystem**: Fejr din mestring af hele spiludviklingscyklussen:
- ✅ Hvordan skaber slutbetingelser tilfredsstillende spilleroplevelser?
- ✅ Hvorfor er korrekt tilstands-styring kritisk for spillets stabilitet?
- ✅ Hvordan forbedrer visuel feedback spillerforståelse?
- ✅ Hvilken rolle spiller genstartssystemet for spillerfastholdelse?
**System-mestri**: Dit komplette spil demonstrerer:
- **Full-stack spildesign**: Fra grafik til input til tilstandsadministration
- **Professionel arkitektur**: Event-drevne systemer med korrekt oprydning
- **Brugeroplevelsesdesign**: Klar feedback og intuitive kontroller
- **Ydelsesoptimering**: Effektiv rendering og hukommelsesstyring
- **Polish og fuldstændighed**: Alle detaljer, der gør et spil færdigt
**Klar til branchen færdigheder**: Du har implementeret:
- **Spilloop-arkitektur**: Realtidssystemer med konsistent ydelse
- **Event-drevet programmering**: Decoupled systemer, der skalerer effektivt
- **Tilstandsstyring**: Komplekse datahåndteringer og livscyklusstyring
- **Brugerfladedesign**: Klar kommunikation og responsive kontroller
- **Test og fejlfinding**: Iterativ udvikling og problemløsning
### ⚡ **Hvad du kan gøre de næste 5 minutter**
- [ ] Spil dit komplette spil og test alle sejr- og nederlagsbetingelser
- [ ] Eksperimentér med forskellige parametre for slutbetingelser
- [ ] Prøv at tilføje console.log-udtryk for at spore spiltilstandsændringer
- [ ] Del dit spil med venner og indsamle feedback
### 🎯 **Hvad du kan nå på en time**
- [ ] Gennemfør quizzen efter lektionen og reflektér over din spiludviklingsrejse
- [ ] Tilføj lydeffekter for sejrs- og nederlagstilstande
- [ ] Implementer yderligere slutbetingelser som tidsbegrænsninger eller bonusmål
- [ ] Lav forskellige sværhedsgrader med varierende fjendetal
- [ ] Forfin den visuelle præsentation med bedre skrifttyper og farver
### 📅 **Din ugelange mestring af spiludvikling**
- [ ] Færdiggør det forbedrede rumspil med flere niveauer og progression
- [ ] Tilføj avancerede funktioner som power-ups, forskellige fjendetyper og specielle våben
- [ ] Lav et highscore-system med vedvarende lagring
- [ ] Design brugergrænseflader til menuer, indstillinger og spiloptioner
- [ ] Optimer performance til forskellige enheder og browsere
- [ ] Udgiv dit spil online og del det med fællesskabet
### 🌟 **Din månedslange karriere inden for spiludvikling**
- [ ] Byg flere komplette spil og udforsk forskellige genrer og mekanikker
- [ ] Lær avancerede spiludviklings-rammer som Phaser eller Three.js
- [ ] Bidrag til open source spiludviklingsprojekter
- [ ] Studer spildesignprincipper og spillerpsykologi
- [ ] Skab en portfolio, der viser dine færdigheder inden for spiludvikling
- [ ] Forbind dig med spiludviklingsfællesskabet og fortsæt med at lære
## 🎯 Din komplette tidslinje for mestring af spiludvikling
```mermaid
timeline
title Komplet Spiludviklings Læringsforløb
section Fundament (Lektionen 1-2)
Spilarkitektur: Projektstruktur
: Ressourcestyring
: Canvas grundlæggende
: Event systemer
section Interaktionssystemer (Lektionen 3-4)
Spillerkontrol: Input håndtering
: Bevægelsesmekanik
: Kollisiondetektion
: Fysik simulering
section Spilmekanikker (Lektion 5)
Feedbacksystemer: Pointsystemer
: Livsstyring
: Visuel kommunikation
: Spillermotivation
section Spilafslutning (Lektion 6)
Polering & Flow: Afslutningsbetingelser
: Tilstandsadministration
: Genstart systemer
: Brugeroplevelse
section Avancerede Funktioner (1 uge)
Forbedringsfærdigheder: Audio integration
: Visuelle effekter
: Niveau progression
: Ydelsesoptimering
section Professionel Udvikling (1 måned)
Brancheparathed: Framework mestring
: Team samarbejde
: Portfolio udvikling
: Fællesskabsengagement
section Karrierefremgang (3 måneder)
Specialisering: Avancerede spilmotorer
: Platform implementering
: Indtjeningsstrategier
: Branche netværk
```
### 🛠️ Din komplette opsummering af spiludviklingsværktøjer
Efter at have gennemført hele denne rumspilsserie, mestrer du nu:
- **Spilarkitektur**: Begivenhedsdrevne systemer, spilsløjfer og tilstandsstyring
- **Grafikprogrammering**: Canvas API, sprite-rendering og visuelle effekter
- **Inputsystemer**: Tastaturhåndtering, kollisiondetektion og responsive kontroller
- **Spildesign**: Spillerfeedback, fremskridtsystemer og engagerende mekanikker
- **Ydelsesoptimering**: Effektiv rendering, hukommelsesstyring og billedfrekvenskontrol
- **Brugeroplevelse**: Klar kommunikation, intuitive kontroller og detaljeret finish
- **Professionelle mønstre**: Ren kode, fejlfindingsteknikker og projektorganisation
**Anvendelser i den virkelige verden**: Dine spiludviklingsfærdigheder kan direkte bruges til:
- **Interaktive webapplikationer**: Dynamiske grænseflader og realtidssystemer
- **Datavisualisering**: Animerede diagrammer og interaktive grafik
- **Undervisningsteknologi**: Gamification og engagerende læringsoplevelser
- **Mobiludvikling**: Touch-baserede interaktioner og ydelsesoptimering
- **Simulationssoftware**: Fysikmotorer og realtidsmodellering
- **Kreative industrier**: Interaktiv kunst, underholdning og digitale oplevelser
**Opnåede professionelle færdigheder**: Du kan nu:
- **Designe arkitektur** for komplekse interaktive systemer fra bunden
- **Fejlsøge** realtidsapplikationer med systematiske metoder
- **Optimere** ydeevne for glidende brugeroplevelser
- **Designe** engagerende brugergrænseflader og interaktionsmønstre
- **Samarbejde** effektivt om tekniske projekter med korrekt kodeorganisation
**Mestring af spiludviklingskoncepter**:
- **Realtidssystemer**: Spilsløjfer, billedfrekvensstyring og ydeevne
- **Begivenhedsdrevet arkitektur**: Løse koblinger mellem systemer og beskedudveksling
- **Tilstandsstyring**: Kompleks datahåndtering og livscyklusstyring
- **Brugergrænsefladeprogrammering**: Canvas-grafik og responsivt design
- **Spildesignteori**: Spillerpsykologi og engagerende mekanikker
**Næste niveau**: Du er klar til at udforske avancerede spilrammer, 3D-grafik, multiplayer-systemer eller gå over til professionelle roller inden for spiludvikling!
🌟 **Præstation opnået**: Du har gennemført en fuld spiludviklingsrejse og bygget en interaktiv oplevelse i professionel kvalitet fra bunden!
**Velkommen til spiludviklingsfællesskabet!** 🎮✨
## GitHub Copilot Agent Challenge 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at løse følgende udfordring:
**Beskrivelse:** Forbedr rumspillet ved at implementere et niveauprogressionssystem med stigende sværhedsgrad og bonusfunktioner.
**Beskrivelse:** Forbedr rumspillet ved at implementere et niveau-fremgangssystem med stigende sværhedsgrad og bonusfunktioner.
**Opgave:** Opret et flerniveaus rumspilssystem, hvor hvert niveau har flere fjendtlige skibe med øget hastighed og sundhed. Tilføj en pointmultiplikator, der stiger med hvert niveau, og implementer power-ups (som hurtig skydning eller skjold), der tilfældigt dukker op, når fjender bliver ødelagt. Inkluder en niveauafslutningsbonus og vis det aktuelle niveau på skærmen sammen med den eksisterende score og liv.
**Prompt:** Lav et flerniveaus rumspilssystem, hvor hvert niveau har flere fjendtlige skibe med øget hastighed og sundhed. Tilføj en scoringsmultiplikator, der stiger med hvert niveau, og implementer power-ups (som hurtig ild eller skjold), der tilfældigt dukker op, når fjender ødelægges. Inkluder en bonus ved fuldførelse af niveau og vis det aktuelle niveau på skærmen sammen med den eksisterende score og liv.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Valgfri Forbedringsudfordring
## 🚀 Valgfri forbedringsudfordring
**Tilføj Lyd til Dit Spil**: Forbedr din spiloplevelse ved at implementere lydeffekter! Overvej at tilføje lyd til:
**Tilføj lyd til dit spil**: Forbedr din spilleoplevelse ved at implementere lydeffekter! Overvej at tilføje lyd for:
- **Laser skud**, når spilleren skyder
- **Fjendens ødelæggelse**, når skibe bliver ramt
- **Helteskade**, når spilleren bliver ramt
- **Sejrsmusik**, når spillet er vundet
- **Nederlagslyd**, når spillet er tabt
- **Laser-skud** når spilleren skyder
- **Fjendeødelæggelse** når skibe rammes
- **Helte-skade** når spilleren tager skade
- **Sejrs-musik** når spillet vindes
- **Nederlagslyd** når spillet tabes
**Eksempel på lydimplementering:**
```javascript
// Create audio objects
// Opret lydobjekter
const laserSound = new Audio('assets/laser.wav');
const explosionSound = new Audio('assets/explosion.wav');
// Play sounds during game events
// Afspil lyde under spilbegivenheder
function playLaserSound() {
laserSound.currentTime = 0; // Reset to beginning
laserSound.currentTime = 0; // Nulstil til begyndelsen
laserSound.play();
}
```
**Hvad du skal vide:**
**Det du skal vide:**
- **Opretter** Audio-objekter til forskellige lydeffekter
- **Nulstiller** `currentTime` for at tillade hurtige lydeffekter
- **Håndterer** browserens autoplay-politikker ved at udløse lyde fra brugerinteraktioner
- **Administrerer** lydstyrke og timing for bedre spiloplevelse
- **Nulstiller** `currentTime` for at tillade hurtig gentagelseslyd
- **Håndterer** browser-autoplay-politikker ved at aktivere lyde fra brugerinteraktioner
- **Styrer** lydstyrke og timing for bedre spiloplevelse
> 💡 **Læringsressource**: Udforsk denne [audio sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) for at lære mere om implementering af lyd i JavaScript-spil.
> 💡 **Læringsressource**: Udforsk denne [audio sandbox](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_audio_play) for at lære mere om at implementere lyd i JavaScript-spil.
## Quiz efter lektionen
## Quiz efter forelæsning
[Quiz efter lektionen](https://ff-quizzes.netlify.app/web/quiz/40)
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/40)
## Gennemgang & Selvstudie
## Revision og selvstudie
Din opgave er at skabe et nyt prøvespil, så udforsk nogle af de interessante spil derude for at se, hvilken type spil du kunne bygge.
Din opgave er at skabe et nyt eksempelspil, så undersøg nogle interessante spil derude for at se, hvilken slags spil du kunne bygge.
## Opgave
[Byg et prøvespil](assignment.md)
[Byg et eksempelspil](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets modersmål bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for eventuelle misforståelser eller fejltolkninger, der opstår ved brug af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -1,51 +1,96 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "5d259f6962464ad91e671083aa0398f4",
"translation_date": "2025-10-23T22:06:50+00:00",
"original_hash": "351678bece18f07d9daa987a881fb062",
"translation_date": "2026-01-06T23:50:11+00:00",
"source_file": "7-bank-project/1-template-route/README.md",
"language_code": "da"
}
-->
# Byg en Bankapp Del 1: HTML-skabeloner og Ruter i en Webapp
# Byg en bankapp del 1: HTML-skabeloner og ruter i en webapp
Da Apollo 11's navigationscomputer fløj til månen i 1969, måtte den skifte mellem forskellige programmer uden at genstarte hele systemet. Moderne webapplikationer fungerer på samme måde de ændrer, hvad du ser, uden at genindlæse alt fra bunden. Dette skaber den glatte, responsive oplevelse, som brugerne forventer i dag.
```mermaid
journey
title Din Bankapp Udviklingsrejse
section SPA Grundlæggende
Forstå single-page apps: 3: Student
Lær skabelonbegreber: 4: Student
Mestre DOM-manipulation: 4: Student
section Routing Systemer
Implementer klient-side routing: 4: Student
Håndter browserhistorik: 5: Student
Opret navigationssystemer: 5: Student
section Professionelle Mønstre
Byg modulær arkitektur: 5: Student
Anvend bedste praksis: 5: Student
Skab brugeroplevelser: 5: Student
```
Da Apollo 11's styringscomputer navigerede til månen i 1969, skulle den skifte mellem forskellige programmer uden at genstarte hele systemet. Moderne webapplikationer fungerer på samme måde de ændrer det, du ser, uden at genindlæse alt fra bunden. Det skaber den glatte, responsive oplevelse, brugere forventer i dag.
I modsætning til traditionelle hjemmesider, der genindlæser hele sider ved hver interaktion, opdaterer moderne webapps kun de dele, der skal ændres. Denne tilgang, ligesom hvordan mission control skifter mellem forskellige skærme, mens de opretholder konstant kommunikation, skaber den flydende oplevelse, vi er blevet vant til.
I modsætning til traditionelle hjemmesider, der genindlæser hele sider for hver interaktion, opdaterer moderne webapps kun de dele, der skal ændres. Denne tilgang, ligesom mission control, der skifter mellem forskellige skærmbilleder, mens de opretholder konstant kommunikation, skaber den flydende oplevelse, vi er blevet vant til.
Her er, hvad der gør forskellen så markant:
Her er, hvad der gør forskellen så dramatisk:
| Traditionelle Multi-Page Apps | Moderne Single-Page Apps |
|------------------------------|--------------------------|
| **Navigation** | Genindlæsning af hele siden for hver skærm | Øjeblikkelig indholdsskift |
| **Ydeevne** | Langsommere på grund af komplette HTML-downloads | Hurtigere med delvise opdateringer |
| Traditionelle multi-sides apps | Moderne enkelt-sides apps |
|-------------------------------|--------------------------|
| **Navigation** | Genindlæsning af hele siden for hvert skærmbillede | Øjeblikkelig skift af indhold |
| **Ydeevne** | Langsommere pga. fulde HTML-downloads | Hurtigere med delvise opdateringer |
| **Brugeroplevelse** | Forstyrrende sideblink | Glatte, app-lignende overgange |
| **Dataudveksling** | Svært mellem sider | Nem tilstandshåndtering |
| **Datadeling** | Vanskelig mellem sider | Let tilstandsstyring |
| **Udvikling** | Flere HTML-filer at vedligeholde | En enkelt HTML med dynamiske skabeloner |
**Forstå udviklingen:**
- **Traditionelle apps** kræver serveranmodninger for hver navigationshandling
- **Moderne SPAs** indlæses én gang og opdaterer indhold dynamisk med JavaScript
- **Brugerforventninger** favoriserer nu øjeblikkelige, problemfri interaktioner
- **Ydeevnefordele** inkluderer reduceret båndbredde og hurtigere svar
- **Traditionelle apps** kræver serveranmodninger ved hver navigation
- **Moderne SPAs** loader én gang og opdaterer indhold dynamisk med JavaScript
- **Brugerforventninger** favoriserer nu øjeblikkelige og sømløse interaktioner
- **Ydeevnefordele** inkluderer mindre båndbredde og hurtigere responstid
I denne lektion vil vi bygge en bankapp med flere skærme, der flyder sammen problemfrit. Ligesom hvordan forskere bruger modulære instrumenter, der kan omkonfigureres til forskellige eksperimenter, vil vi bruge HTML-skabeloner som genanvendelige komponenter, der kan vises efter behov.
I denne lektion bygger vi en bankapp med flere skærmbilleder, der flyder sammen problemfrit. Ligesom forskere bruger modulære instrumenter, der kan omkonfigureres til forskellige eksperimenter, bruger vi HTML-skabeloner som genanvendelige komponenter, der kan vises efter behov.
Du vil arbejde med HTML-skabeloner (genanvendelige skabeloner til forskellige skærme), JavaScript-routing (systemet, der skifter mellem skærme), og browserens historik-API (som holder tilbage-knappen fungerende som forventet). Dette er de samme grundlæggende teknikker, som bruges af frameworks som React, Vue og Angular.
Du vil arbejde med HTML-skabeloner (genanvendelige blueprints til forskellige skærme), JavaScript-ruting (systemet der skifter mellem skærmbilleder), og browserens history API (som sørger for, at tilbage-knappen fungerer som forventet). Dette er de samme grundlæggende teknikker, som bruges af frameworks som React, Vue og Angular.
Ved slutningen vil du have en fungerende bankapp, der demonstrerer professionelle principper for single-page applikationer.
Til sidst vil du have en fungerende bankapp, der demonstrerer professionelle principper for enkelt-sides applikationer.
## Quiz før lektionen
```mermaid
mindmap
root((Single-Page Applikationer))
Architecture
Template System
Client-side Routing
State Management
Event Handling
Templates
Genanvendelige Komponenter
Dynamisk Indhold
DOM Manipulation
Indholdsskift
Routing
URL-styring
History API
Navigationslogik
Browserintegration
User Experience
Hurtig Navigation
Glidende Overgange
Konsistent Tilstand
Moderne Interaktioner
Performance
Reducerede Serverforespørgsler
Hurtigere Sideovergange
Effektiv Ressourcebrug
Bedre Responsivitet
```
## For-forelæsning quiz
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/41)
[For-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/41)
### Hvad du skal bruge
### Hvad du får brug for
Vi skal bruge en lokal webserver til at teste vores bankapp bare rolig, det er nemmere, end det lyder! Hvis du ikke allerede har en opsat, skal du blot installere [Node.js](https://nodejs.org) og køre `npx lite-server` fra din projektmappe. Denne smarte kommando starter en lokal server og åbner automatisk din app i browseren.
Vi får brug for en lokal webserver til at teste vores bankapp bare rolig, det er nemmere end det lyder! Hvis du ikke allerede har en opsat, så installer bare [Node.js](https://nodejs.org) og kør `npx lite-server` fra din projektmappe. Denne smarte kommando starter en lokal server og åbner din app automatisk i browseren.
### Forberedelse
På din computer skal du oprette en mappe kaldet `bank` med en fil kaldet `index.html` indeni. Vi starter med denne HTML [boilerplate](https://en.wikipedia.org/wiki/Boilerplate_code):
På din computer opret en mappe med navnet `bank` og en fil inde i den, der hedder `index.html`. Vi starter med denne HTML [skabelon](https://en.wikipedia.org/wiki/Boilerplate_code):
```html
<!DOCTYPE html>
@ -61,14 +106,14 @@ På din computer skal du oprette en mappe kaldet `bank` med en fil kaldet `index
</html>
```
**Her er, hvad denne boilerplate giver:**
- **Etablerer** HTML5-dokumentstrukturen med korrekt DOCTYPE-deklaration
**Dette tilbyder skabelonen:**
- **Etablerer** HTML5 dokumentstruktur med korrekt DOCTYPE deklaration
- **Konfigurerer** tegnkodning som UTF-8 for international tekstunderstøttelse
- **Muliggør** responsivt design med viewport meta-tagget for mobilkompatibilitet
- **Muliggør** responsivt design med viewport meta-tag til mobilkompatibilitet
- **Sætter** en beskrivende titel, der vises i browserfanen
- **Skaber** en ren body-sektion, hvor vi bygger vores applikation
- **Opretter** en ren body-sektion, hvor vi bygger vores applikation
> 📁 **Projektstruktur Forhåndsvisning**
> 📁 **Projektstrukturoversigt**
>
> **Ved slutningen af denne lektion vil dit projekt indeholde:**
> ```
@ -79,42 +124,59 @@ På din computer skal du oprette en mappe kaldet `bank` med en fil kaldet `index
> ```
>
> **Filansvar:**
> - **index.html**: Indeholder alle skabeloner og giver appens struktur
> - **app.js**: Håndterer routing, navigation og skabelonstyring
> - **Skabeloner**: Definerer UI for login, dashboard og andre skærme
> - **index.html**: Indeholder alle skabeloner og strukturen til appen
> - **app.js**: Håndterer ruting, navigation og skabelonstyring
> - **Skabeloner**: Definerer UI til login, dashboard og andre skærme
---
## HTML-skabeloner
Skabeloner løser et grundlæggende problem i webudvikling. Da Gutenberg opfandt den bevægelige type trykning i 1440'erne, indså han, at i stedet for at udskære hele sider, kunne han skabe genanvendelige bogstavblokke og arrangere dem efter behov. HTML-skabeloner fungerer på samme princip i stedet for at skabe separate HTML-filer for hver skærm, definerer du genanvendelige strukturer, der kan vises, når det er nødvendigt.
Skabeloner løser et grundlæggende problem i webudvikling. Da Gutenberg opfandt det bevægelige typesystem i 1440erne, indså han, at i stedet for at udskære hele sider kunne han lave genanvendelige bogstavblokke og arrangere dem efter behov. HTML-skabeloner fungerer på samme princip i stedet for at skabe separate HTML-filer til hver skærm definerer du genanvendelige strukturer, som kan vises efter behov.
Tænk på skabeloner som tegninger for forskellige dele af din app. Ligesom en arkitekt skaber én tegning og bruger den flere gange i stedet for at tegne identiske rum igen, skaber vi skabeloner én gang og bruger dem efter behov. Browseren holder disse skabeloner skjult, indtil JavaScript aktiverer dem.
```mermaid
flowchart TD
A["📋 Skabelon Definition"] --> B["💬 Skjult i DOM"]
B --> C["🔍 JavaScript Finder Skabelon"]
C --> D["📋 Klon Skabelon Indhold"]
D --> E["🔗 Vedhæft til Synligt DOM"]
E --> F["👁️ Bruger Ser Indhold"]
G["Login Skabelon"] --> A
H["Dashboard Skabelon"] --> A
I["Fremtidige Skabeloner"] --> A
style A fill:#e3f2fd
style D fill:#e8f5e8
style F fill:#fff3e0
style B fill:#f3e5f5
```
Tænk på skabeloner som blueprints for forskellige dele af din app. Ligesom en arkitekt skaber ét blueprint og bruger det flere gange i stedet for at tegne identiske rum flere gange, laver vi skabeloner én gang og instansierer dem efter behov. Browseren holder disse skabeloner skjulte, indtil JavaScript aktiverer dem.
Hvis du vil oprette flere skærme til en webside, kunne en løsning være at oprette en HTML-fil for hver skærm, du vil vise. Men denne løsning har nogle ulemper:
Hvis du vil lave flere skærme til en webside, kunne en løsning være at lave en HTML-fil for hver skærm, du vil vise. Denne løsning har dog nogle ulemper:
- Du skal genindlæse hele HTML'en, når du skifter skærm, hvilket kan være langsomt.
- Det er svært at dele data mellem de forskellige skærme.
En anden tilgang er kun at have én HTML-fil og definere flere [HTML-skabeloner](https://developer.mozilla.org/docs/Web/HTML/Element/template) ved hjælp af `<template>`-elementet. En skabelon er en genanvendelig HTML-blok, der ikke vises af browseren og skal instansieres ved runtime ved hjælp af JavaScript.
En anden tilgang er at have kun én HTML-fil og definere flere [HTML-skabeloner](https://developer.mozilla.org/docs/Web/HTML/Element/template) ved hjælp af `<template>` elementet. En skabelon er et genanvendeligt HTML-blok, som ikke vises af browseren, og som skal instansieres under kørslen ved hjælp af JavaScript.
### Lad os bygge det
Vi skal oprette en bankapp med to hovedskærme: en login-side og et dashboard. Først skal vi tilføje et pladsholder-element til vores HTML-body her vil alle vores forskellige skærme blive vist:
Vi skal lave en bankapp med to hovedskærme: en login-side og et dashboard. Først tilføjer vi et pladsholder-element til vores HTML body her vil alle vores forskellige skærme fremkomme:
```html
<div id="app">Loading...</div>
```
**Forstå denne pladsholder:**
- **Skaber** en container med ID'et "app", hvor alle skærme vil blive vist
- **Viser** en indlæsningsbesked, indtil JavaScript initialiserer den første skærm
- **Giver** et enkelt monteringspunkt for vores dynamiske indhold
- **Muliggør** nem målretning fra JavaScript ved hjælp af `document.getElementById()`
- **Opretter** en container med ID'et "app", hvor alle skærme vil blive vist
- **Viser** en loading-besked indtil JavaScript initialiserer den første skærm
- **Giver** ét monteringspunkt for vores dynamiske indhold
- **Muliggør** nem adgang fra JavaScript med `document.getElementById()`
> 💡 **Tip**: Da indholdet af dette element vil blive erstattet, kan vi indsætte en indlæsningsbesked eller indikator, der vises, mens appen indlæses.
> 💡 **Tips**: Da indholdet i dette element vil blive udskiftet, kan vi indsætte en loading-besked eller indikator, som vises, mens appen loader.
Dernæst tilføjer vi HTML-skabelonen for login-siden nedenfor. Indtil videre vil vi kun indsætte en titel og en sektion, der indeholder et link, som vi vil bruge til navigation.
Dernæst tilføjer vi nedenfor HTML-skabelonen til login-siden. Lige nu indsætter vi kun en titel og en sektion med et link, som vi vil bruge til navigationen.
```html
<template id="login">
@ -126,16 +188,16 @@ Dernæst tilføjer vi HTML-skabelonen for login-siden nedenfor. Indtil videre vi
```
**Opdeling af denne login-skabelon:**
- **Definerer** en skabelon med det unikke identifikator "login" til JavaScript-målretning
- **Definerer** en skabelon med det unikke ID "login" til JavaScript-målretning
- **Indeholder** en hovedoverskrift, der etablerer appens branding
- **Indeholder** et semantisk `<section>`-element til gruppering af relateret indhold
- **Giver** et navigationslink, der vil føre brugerne til dashboardet
- **Indholder** et semantisk `<section>` element til gruppering af relateret indhold
- **Giver** et navigationslink, der sender brugerne til dashboardet
Derefter tilføjer vi en anden HTML-skabelon til dashboard-siden. Denne side vil indeholde forskellige sektioner:
tilføjer vi en anden HTML-skabelon til dashboard-siden. Denne side indeholder forskellige sektioner:
- En header med en titel og et logout-link
- Et header med en titel og et logout-link
- Den aktuelle saldo på bankkontoen
- En liste over transaktioner, vist i en tabel
- En liste over transaktioner vist i en tabel
```html
<template id="dashboard">
@ -162,73 +224,88 @@ Derefter tilføjer vi en anden HTML-skabelon til dashboard-siden. Denne side vil
</template>
```
**Lad os forstå hver del af dette dashboard:**
- **Strukturerer** siden med et semantisk `<header>`-element, der indeholder navigation
- **Viser** appens titel konsekvent på tværs af skærme for branding
- **Giver** et logout-link, der rer tilbage til login-skærmen
**Lad os forstå hver del af dashboardet:**
- **Strukturerer** siden med et semantisk `<header>` element, der indeholder navigation
- **Viser** app-titlen konsekvent på tværs af skærme for branding
- **Giver** et logout-link, der ruter tilbage til login-skærmen
- **Viser** den aktuelle kontosaldo i en dedikeret sektion
- **Organiserer** transaktionsdata ved hjælp af en korrekt struktureret HTML-tabel
- **Definerer** tabeloverskrifter for kolonnerne Dato, Objekt og Beløb
- **Efterlader** tabelkroppen tom for dynamisk indholdsindsprøjtning senere
- **Definerer** tabelhoveder for Dato, Objekt og Beløb
- **Lader** tabelkroppen være tom til dynamisk indhold senere
> 💡 **Tips**: Når du laver HTML-skabeloner, hvis du vil se, hvordan det vil se ud, kan du kommentere `<template>` og `</template>` linjerne ud ved at omslutte dem med `<!-- -->`.
> 💡 **Tip**: Når du opretter HTML-skabeloner, hvis du vil se, hvordan det vil se ud, kan du kommentere `<template>` og `</template>` linjerne ved at omslutte dem med `<!-- -->`.
### 🔄 **Pædagogisk tjek**
**Forståelse af skabelonsystemet**: Inden du implementerer JavaScript, sørg for at forstå:
- ✅ Hvordan skabeloner adskiller sig fra almindelige HTML-elementer
- ✅ Hvorfor skabeloner forbliver skjulte, indtil de aktiveres af JavaScript
- ✅ Vigtigheden af semantisk HTML-struktur i skabeloner
- ✅ Hvordan skabeloner muliggør genanvendelige UI-komponenter
✅ Hvorfor tror du, vi bruger `id`-attributter på skabelonerne? Kunne vi bruge noget andet som klasser?
**Hurtig selv-test**: Hvad sker der, hvis du fjerner `<template>` tags omkring dit HTML?
*Svar: Indholdet bliver synligt med det samme og mister sin skabelonfunktion*
**Arkitekturfordele**: Skabeloner giver:
- **Genanvendelighed**: Én definition, flere instanser
- **Ydeevne**: Ingen redundante HTML-parsninger
- **Vedligeholdelighed**: Centraliseret UI-struktur
- **Fleksibilitet**: Dynamisk indholdsskift
✅ Hvorfor tror du, vi bruger `id` attributter på skabelonerne? Kunne vi bruge noget andet som klasser?
## Gør skabeloner levende med JavaScript
Nu skal vi gøre vores skabeloner funktionelle. Ligesom hvordan en 3D-printer tager en digital tegning og skaber et fysisk objekt, tager JavaScript vores skjulte skabeloner og skaber synlige, interaktive elementer, som brugerne kan se og bruge.
Nu skal vi gøre vores skabeloner funktionelle. Ligesom en 3D-printer tager en digital blueprint og skaber et fysisk objekt, tager JavaScript vores skjulte skabeloner og laver synlige, interaktive elementer, som brugerne kan se og bruge.
Processen følger tre konsekvente trin, der danner grundlaget for moderne webudvikling. Når du forstår dette mønster, vil du genkende det på tværs af mange frameworks og biblioteker.
Processen følger tre konsekvente trin, der udgør fundamentet for moderne webudvikling. Når du forstår dette mønster, vil du genkende det i mange frameworks og biblioteker.
Hvis du prøver din nuværende HTML-fil i en browser, vil du se, at den sidder fast med at vise `Loading...`. Det skyldes, at vi skal tilføje noget JavaScript-kode for at instantiere og vise HTML-skabelonerne.
Hvis du prøver din nuværende HTML-fil i en browser, vil du se, at den sidder fast i at vise `Loading...`. Det er fordi vi skal tilføje noget JavaScript-kode for at instantiere og vise HTML-skabelonerne.
Instantiationen af en skabelon udføres normalt i 3 trin:
At instantiere en skabelon gøres normalt i 3 trin:
1. Hent skabelonelementet i DOM'en, for eksempel ved hjælp af [`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById).
2. Klon skabelonelementet ved hjælp af [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode).
3. Tilføj det til DOM'en under et synligt element, for eksempel ved hjælp af [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild).
1. Find skabelonelementet i DOMen, for eksempel ved hjælp af [`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById).
2. Klon skabelonelementet, brug [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode).
3. Vedhæft det til DOM under et synligt element, for eksempel ved hjælp af [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild).
```mermaid
flowchart TD
A[🔍 Step 1: Find Template] --> B[📋 Step 2: Clone Template]
B --> C[🔗 Step 3: Attach to DOM]
A[🔍 Trin 1: Find Skabelon] --> B[📋 Trin 2: Klon Skabelon]
B --> C[🔗 Trin 3: Tilføj til DOM]
A1["document.getElementById('login')"] --> A
B1["template.content.cloneNode(true)"] --> B
C1["app.appendChild(view)"] --> C
C --> D[👁️ Template Visible to User]
C --> D[👁️ Skabelon Synlig for Bruger]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
```
**Visuel opdeling af processen:**
- **Trin 1** lokaliserer det skjulte skabelonelement i DOM-strukturen
- **Trin 2** skaber en fungerende kopi, der sikkert kan ændres
- **Trin 3** indsætter kopien i det synlige sideområde
- **Resultat** er en funktionel skærm, som brugerne kan interagere med
- **Trin 1** finder den skjulte skabelon i DOM-strukturen
- **Trin 2** laver en arbejdskopi, som kan ændres uden påvirkning af originalen
- **Trin 3** indsætter kopien i det synlige område på siden
- **Resultatet** er en funktionel skærm, som brugerne kan interagere med
✅ Hvorfor skal vi klone skabelonen, før vi tilføjer den til DOM'en? Hvad tror du, der ville ske, hvis vi sprang dette trin over?
✅ Hvorfor skal vi klone skabelonen før vi vedhæfter den til DOMen? Hvad tror du ville ske, hvis vi sprang dette trin over?
### Opgave
Opret en ny fil kaldet `app.js` i din projektmappe og importer den fil i `<head>`-sektionen af din HTML:
Opret en ny fil med navnet `app.js` i din projektmappe og importer den fil i `<head>` sektionen i din HTML:
```html
<script src="app.js" defer></script>
```
**Forstå denne script-import:**
- **Linker** JavaScript-filen til vores HTML-dokument
- **Bruger** attributten `defer` for at sikre, at scriptet kører efter HTML-parsning er fuldført
- **Forbinder** JavaScript-filen til vores HTML-dokument
- **Bruger** `defer` attributten for at sikre, at scriptet kører efter HTML-parsning er færdig
- **Muliggør** adgang til alle DOM-elementer, da de er fuldt indlæst før scriptudførelse
- **Følger** moderne bedste praksis for scriptindlæsning og ydeevne
- **Følger** moderne bedste praksis for script-loading og ydeevne
Nu i `app.js` opretter vi en ny funktion `updateRoute`:
Nu i `app.js` laver vi en ny funktion `updateRoute`:
```js
function updateRoute(templateId) {
@ -240,56 +317,71 @@ function updateRoute(templateId) {
}
```
**Trin for trin, her er hvad der sker:**
- **Lokaliserer** skabelonelementet ved hjælp af dets unikke ID
- **Skaber** en dyb kopi af skabelonens indhold ved hjælp af `cloneNode(true)`
- **Finder** app-containeren, hvor indholdet vil blive vist
- **Rydder** eksisterende indhold fra app-containeren
- **Indsætter** det klonede skabelonindhold i den synlige DOM
**Trin for trin hvad der sker:**
- **Finder** skabelonelementet ved hjælp af dets unikke ID
- **Laver** en dyb kopi af skabelonens indhold med `cloneNode(true)`
- **Finder** app-containeren, hvor indholdet skal vises
- **Fjerner** eksisterende indhold i app-containeren
- **Indsætter** den klonede skabelon i den synlige DOM
Nu kalder vi denne funktion med en af skabelonerne og ser på resultatet.
Kald derefter denne funktion med en af skabelonerne og se resultatet.
```js
updateRoute('login');
```
**Hvad denne funktionskald opnår:**
- **Aktiverer** login-skabelonen ved at sende dens ID som en parameter
- **Demonstrerer** hvordan man programmatisk skifter mellem forskellige app-skærme
- **Viser** login-skærmen i stedet for "Loading..."-beskeden
**Hvad dette funktionskald opnår:**
- **Aktiverer** login-skabelonen ved at give dens ID som parameter
- **Demonstrerer** hvordan man programmæssigt skifter mellem forskellige app-skærme
- **Viser** login-skærmen i stedet for "Loading..." beskeden
✅ Hvad er formålet med denne kode `app.innerHTML = '';`? Hvad sker der uden den?
✅ Hvad er formålet med koden `app.innerHTML = '';`? Hvad sker uden den?
## Oprettelse af ruter
## Opret ruter
Routing handler grundlæggende om at forbinde URL'er til det rigtige indhold. Tænk på, hvordan tidlige telefonoperatører brugte omstillingsborde til at forbinde opkald de tog en indgående anmodning og dirigerede den til den korrekte destination. Web-routing fungerer på samme måde, idet den tager en URL-anmodning og bestemmer, hvilket indhold der skal vises.
Traditionelt håndterede webservere dette ved at levere forskellige HTML-filer til forskellige URL'er. Da vi bygger en single-page app, skal vi selv håndtere denne routing med JavaScript. Denne tilgang giver os mere kontrol over brugeroplevelsen og ydeevnen.
Routing handler grundlæggende om at forbinde URLer med det rette indhold. Forestil dig, hvordan tidlige telefonoperatører brugte koblingsanlæg for at forbinde opkald de tog en indkommende anmodning og rute den til det korrekte sted. Webruting fungerer på samme måde, hvor en URL-anmodning bestemmer, hvilket indhold der vises.
```mermaid
flowchart LR
A["🌐 URL Path<br/>/dashboard"] --> B["🗺️ Routes Object<br/>Lookup"]
B --> C["🎯 Template ID<br/>'dashboard'"]
C --> D["📄 Find Template<br/>getElementById"]
D --> E["👁️ Display Screen<br/>Clone & Append"]
A["🌐 URL-sti<br/>/dashboard"] --> B["🗺️ Ruteobjekt<br/>Opslag"]
B --> C["🎯 Skabelon-ID<br/>'dashboard'"]
C --> D["📌 Find skabelon<br/>getElementById"]
D --> E["👁️ Vis skærm<br/>Klon & tilføj"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /unknown"] --> I["❌ Not Found"]
I --> J["🔄 Redirect to /login"]
H["📍 /unknown"] --> I["❌ Ikke fundet"]
I --> J["🔄 Omdiriger til /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
Traditionelt håndterede webservere dette ved at servere forskellige HTML-filer til forskellige URLer. Da vi bygger en enkelt-sides app, skal vi selv håndtere ruterne med JavaScript. Denne tilgang giver os mere kontrol over brugeroplevelsen og ydeevnen.
```mermaid
flowchart LR
A["🌐 URL-sti<br/>/dashboard"] --> B["🗺️ Rute-objekt<br/>Opslag"]
B --> C["🎯 Skabelon-ID<br/>'dashboard'"]
C --> D["📄 Find skabelon<br/>getElementById"]
D --> E["👁️ Vis skærm<br/>Klon & tilføj"]
F["📍 /login"] --> G["🎯 'login'"]
H["📍 /ukendt"] --> I["❌ Ikke fundet"]
I --> J["🔄 Omdiriger til /login"]
style B fill:#e3f2fd
style E fill:#e8f5e8
style I fill:#ffebee
style J fill:#fff3e0
```
**Forstå routing-flowet:**
- **URL-ændringer** udløser en opslag i vores rute-konfiguration
- **Gyldige ruter** kortlægges til specifikke skabelon-ID'er til rendering
- **Ugyldige ruter** udløser fallback-adfærd for at forhindre brudte tilstande
- **Skabelon-rendering** følger den tre-trins proces, vi har lært tidligere
- **URL-ændringer** udløser opslag i vores rute-konfiguration
- **Gyldige ruter** knyttes til specifikke skabelon-IDer til gengivelse
- **Ugyldige ruter** udløser fallback-adfærd for at undgå fejltilstande
- **Gengivelse af skabelon** følger den tretrinsproces, vi lærte tidligere
Når vi taler om en webapp, kalder vi *Routing* intentionen om at kortlægge **URL'er** til specifikke skærme, der skal vises. På en hjemmeside med flere HTML-filer sker dette automatisk, da filstierne afspejles i URL'en. For eksempel med disse filer i din projektmappe:
Når man taler om en webapp, kalder vi *Routing* intentionen om at kortlægge **URLer** til specifikke skærmbilleder, der skal vises. På en hjemmeside med flere HTML-filer gøres dette automatisk, idet filstier afspejles i URLen. For eksempel med disse filer i din projektmappe:
```
mywebsite/index.html
@ -297,7 +389,7 @@ mywebsite/login.html
mywebsite/admin/index.html
```
Hvis du opretter en webserver med `mywebsite` som rod, vil URL-kortlægningen være:
Hvis du opretter en webserver med `mywebsite` som rod, bliver URL-kortlægningen:
```
https://site.com --> mywebsite/index.html
@ -305,11 +397,11 @@ https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
Men for vores webapp bruger vi en enkelt HTML-fil, der indeholder alle skærme, så denne standardadfærd vil ikke hjælpe os. Vi skal manuelt oprette dette kort og opdatere den viste skabelon ved hjælp af JavaScript.
Men i vores webapp bruger vi en enkelt HTML-fil, der indeholder alle skærme, så denne standardadfærd hjælper os ikke. Vi skal skabe dette kort manuelt og opdatere den viste skabelon med JavaScript.
### Opgave
Vi vil bruge et simpelt objekt til at implementere et [kort](https://en.wikipedia.org/wiki/Associative_array) mellem URL-stier og vores skabeloner. Tilføj dette objekt øverst i din `app.js`-fil.
Vi bruger et simpelt objekt til at implementere et [kort](https://en.wikipedia.org/wiki/Associative_array) mellem URL-stier og vores skabeloner. Tilføj dette objekt øverst i din `app.js` fil.
```js
const routes = {
@ -319,12 +411,11 @@ const routes = {
```
**Forstå denne rute-konfiguration:**
- **Definerer** en kortlægning mellem URL-stier og skabelonidentifikatorer
- **Bruger** objekt-syntaks, hvor nøgler er URL-stier, og værdier indeholder skabeloninformation
- **Definerer** en kortlægning mellem URL-stier og skabelon-ider
- **Bruger** objekt-syntaks, hvor nøgler er URL-stier og værdier indeholder skabeloninformation
- **Muliggør** nem opslag af, hvilken skabelon der skal vises for en given URL
- **Giver** en skalerbar struktur til at tilføje nye ruter i fremtiden
Nu skal vi ændre funktionen `updateRoute` lidt. I stedet for direkte at sende `templateId` som et argument, vil vi hente det ved først at kigge på den aktuelle URL og derefter bruge vores kort til at få den tilsvarende skabelon-ID-værdi. Vi kan bruge [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) til kun at få stisektionen fra URL'en.
Lad os nu ændre lidt på funktionen `updateRoute`. I stedet for at sende `templateId` direkte som et argument, vil vi hente det ved først at kigge på den aktuelle URL og derefter bruge vores kort til at få den tilsvarende template ID-værdi. Vi kan bruge [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) til kun at få paths-delen fra URL'en.
```js
function updateRoute() {
@ -339,50 +430,100 @@ function updateRoute() {
}
```
**Opdeling af, hvad der sker her:**
- **Ekstraherer** den aktuelle sti fra browserens URL ved hjælp af `window.location.pathname`
- **Slår op** i den tilsvarende rute-konfiguration i vores rute-objekt
- **Henter** skabelon-ID'et fra rute-konfigurationen
- **Følger** den samme skabelon-renderingsproces som før
- **Skaber** et dynamisk system, der reagerer på URL-ændringer
**Opdelt hvad der sker her:**
- **Ekstraherer** det aktuelle path fra browserens URL ved hjælp af `window.location.pathname`
- **Slår op** den tilsvarende rute-konfiguration i vores routes-objekt op
- **Henter** template ID fra rute-konfigurationen
- **Følger** samme skabelonrendereringsproces som før
- **Opretter** et dynamisk system, der reagerer på URL-ændringer
Her har vi koblet de ruter, vi har erklæret, til deres tilsvarende template. Du kan prøve selv at ændre URL'en manuelt i din browser for at se, at det virker korrekt.
Her har vi kortlagt de ruter, vi har erklæret, til den tilsvarende skabelon. Du kan prøve, at det fungerer korrekt ved manuelt at ændre URL'en i din browser.
✅ Hvad sker der, hvis du indtaster en ukendt sti i URL'en? Hvordan kan vi løse dette?
✅ Hvad sker der, hvis du indtaster et ukendt path i URL'en? Hvordan kunne vi løse det?
## Tilføjelse af navigation
## Tilføjelse af Navigation
Når routing er etableret, skal brugerne have en måde at navigere gennem appen på. Traditionelle websites genindlæser hele sider, når man klikker på links, men vi ønsker at opdatere både URL'en og indholdet uden sideopdateringer. Dette skaber en mere glidende oplevelse, der minder om, hvordan desktop-applikationer skifter mellem forskellige visninger.
Med routing på plads har brugerne brug for en måde at navigere rundt i appen på. Traditionelle hjemmesider genindlæser hele sider, når der klikkes på links, men vi ønsker at opdatere både URL og indhold uden at genindlæse siden. Det skaber en glattere oplevelse, som minder om hvordan desktop-applikationer skifter mellem forskellige visninger.
Vi skal koordinere to ting: opdatere browserens URL, så brugerne kan bogmærke sider og dele links, og vise det relevante indhold. Når dette implementeres korrekt, skaber det den sømløse navigation, som brugerne forventer af moderne applikationer.
Vi skal koordinere to ting: opdatere browserens URL, så brugerne kan bogmærke sider og dele links, og vise det passende indhold. Når det implementeres korrekt, skaber det den sømløse navigation, brugerne forventer af moderne applikationer.
> 🏗️ **Arkitekturindsigt**: Navigationens systemkomponenter
```mermaid
sequenceDiagram
participant User
participant Browser
participant App
participant Template
User->>Browser: Klikker på "Log ind" link
Browser->>App: onclick begivenhed udløst
App->>App: preventDefault() & naviger('/dashboard')
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL opdateres til /dashboard
App->>App: updateRoute() kaldt
App->>Template: Find & klon dashboard skabelon
Template->>App: Returner klonet indhold
App->>Browser: Erstat app indhold med skabelon
Browser->>User: Vis dashboard skærm
Note over User,Template: Bruger klikker browser tilbage knap
User->>Browser: Klikker tilbage knap
Browser->>Browser: Historik går tilbage til /login
Browser->>App: popstate begivenhed udløst
App->>App: updateRoute() kaldt automatisk
App->>Template: Find & klon login skabelon
Template->>App: Returner klonet indhold
App->>Browser: Erstat app indhold med skabelon
Browser->>User: Vis login skærm
```
### 🔄 **Pædagogisk Check-in**
**Single-Page Application Arkitektur**: Bekræft din forståelse af hele systemet:
- ✅ Hvordan adskiller klient-side routing sig fra traditionel server-side routing?
- ✅ Hvorfor er History API vigtigt for korrekt SPA-navigation?
- ✅ Hvordan muliggør templates dynamisk indhold uden sideopdateringer?
- ✅ Hvilken rolle spiller event-håndtering for at opfange navigation?
**Systemintegration**: Din SPA demonstrerer:
- **Template-styring**: Genanvendelige UI-komponenter med dynamisk indhold
- **Klient-side Routing**: URL-styring uden serverforespørgsler
- **Event-drevet Arkitektur**: Responsiv navigation og brugerinteraktioner
- **Browserintegration**: Korrekt historik og bagud/forud-knap support
- **Performance-optimering**: Hurtige overgange og reduceret serverbelastning
**Professionelle Mønstre**: Du har implementeret:
- **Model-View Separation**: Templates adskilt fra applikationslogik
- **State Management**: URL-tilstand synkroniseret med vist indhold
- **Progressiv Forbedring**: JavaScript forbedrer grundlæggende HTML-funktionalitet
- **Brugeroplevelse**: Glat, app-lignende navigation uden sideopdateringer
> <20> **Arkitekturindsigt**: Navigation Systemkomponenter
>
> **Hvad du bygger:**
> - **🔄 URL-håndtering**: Opdaterer browserens adressefelt uden sideopdateringer
> - **📋 Templatesystem**: Skifter dynamisk indhold baseret på den aktuelle rute
> - **📚 Historikintegration**: Bevarer browserens tilbage/frem-knap funktionalitet
> - **🛡️ Fejlhåndtering**: Elegante løsninger for ugyldige eller manglende ruter
> **Det du bygger:**
> - **🔄 URL-styring**: Opdaterer browserens adresselinje uden sideopdateringer
> - **📋 Template-system**: Udskifter indhold dynamisk baseret på aktuel rute
> - **📚 Historik-integration**: Vedligeholder browserens bagud/forud-knap funktionalitet
> - **🛡️ Fejlhåndtering**: Smidige fallback-mekanismer til ugyldige eller manglende ruter
>
> **Hvordan komponenterne arbejder sammen:**
> - **Lytter** til navigationsevents (klik, historikændringer)
> - **Opdaterer** URL'en ved hjælp af History API
> - **Render** den passende template for den nye rute
> - **Bevarer** en glidende brugeroplevelse hele vejen igennem
> **Hvordan komponenter arbejder sammen:**
> - **Lytter** efter navigationsevents (klik, historikændringer)
> - **Opdaterer** URL med History API
> - **Renderer** den passende template for den nye rute
> - **Opretholder** en sømløs brugeroplevelse hele vejen igennem
Næste skridt for vores app er at tilføje muligheden for at navigere mellem sider uden manuelt at ændre URL'en. Dette indebærer to ting:
Det næste skridt for vores app er at tilføje muligheden for navigation mellem sider uden at skulle ændre URL'en manuelt. Det indebærer to ting:
1. Opdatering af den aktuelle URL
2. Opdatering af den viste template baseret på den nye URL
Vi har allerede taget os af den anden del med funktionen `updateRoute`, så vi skal finde ud af, hvordan vi opdaterer den aktuelle URL.
Vi har allerede håndteret det andet med `updateRoute` funktionen, så vi skal finde ud af hvordan vi opdaterer den aktuelle URL.
Vi skal bruge JavaScript og mere specifikt [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState), som gør det muligt at opdatere URL'en og oprette en ny post i browserens historik uden at genindlæse HTML'en.
Vi bliver nødt til at bruge JavaScript og mere specifikt [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState), som tillader at opdatere URL'en og tilføje en ny post i browserens historik, uden at genindlæse HTML'en.
> ⚠️ **Vigtig Bemærkning**: Selvom HTML-anker-elementet [`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a) kan bruges alene til at oprette hyperlinks til forskellige URL'er, vil det som standard få browseren til at genindlæse HTML'en. Det er nødvendigt at forhindre denne adfærd, når man håndterer routing med brugerdefineret JavaScript, ved at bruge funktionen preventDefault() på klik-eventet.
> ⚠️ **Vigtig Note**: Mens HTML-anker elementet [`<a href>`](https://developer.mozilla.org/docs/Web/HTML/Element/a) kan bruges til at lave hyperlinks til forskellige URL'er, vil det som standard få browseren til at genindlæse HTML'en. Det er nødvendigt at forhindre denne opførsel, når man håndterer routing med brugerdefineret JavaScript, ved at bruge preventDefault() funktionen på klik-eventet.
### Opgave
Lad os oprette en ny funktion, som vi kan bruge til at navigere i vores app:
Lad os oprette en ny funktion, vi kan bruge til navigation i vores app:
```js
function navigate(path) {
@ -393,13 +534,13 @@ function navigate(path) {
**Forståelse af denne navigationsfunktion:**
- **Opdaterer** browserens URL til den nye sti ved hjælp af `history.pushState`
- **Tilføjer** en ny post til browserens historikstack for korrekt tilbage/frem-knap support
- **Aktiverer** funktionen `updateRoute()` for at vise den tilsvarende template
- **Bevarer** single-page app oplevelsen uden sideopdateringer
- **Tilføjer** en ny post til browserens historikhistorik for korrekt bagud/forud-knap support
- **Aktiverer** `updateRoute()` funktionen for at vise den tilsvarende template
- **Opretholder** single-page app-oplevelsen uden sidereloads
Denne metode opdaterer først den aktuelle URL baseret på den givne sti og derefter opdaterer templaten. Egenskaben `window.location.origin` returnerer URL-roden, hvilket gør det muligt for os at rekonstruere en komplet URL fra en given sti.
Denne metode opdaterer først den aktuelle URL baseret på den givne sti, derefter opdateres templaten. Egenskaben `window.location.origin` returnerer rod-URL'en, så vi kan rekonstruere en komplet URL ud fra en given sti.
Nu hvor vi har denne funktion, kan vi tage os af problemet, vi har, hvis en sti ikke matcher nogen defineret rute. Vi vil ændre funktionen `updateRoute` ved at tilføje en fallback til en af de eksisterende ruter, hvis vi ikke kan finde et match.
Nu hvor vi har denne funktion, kan vi tage hånd om problemet, hvis et path ikke matcher nogen defineret rute. Vi vil modificere `updateRoute` funktionen ved at tilføje et fallback til en af de eksisterende ruter, hvis vi ikke kan finde et match.
```js
function updateRoute() {
@ -419,14 +560,14 @@ function updateRoute() {
```
**Vigtige punkter at huske:**
- **Kontrollerer** om der findes en rute for den aktuelle sti
- **Tjekker** om en rute eksisterer for den aktuelle sti
- **Omdirigerer** til login-siden, når en ugyldig rute tilgås
- **Tilbyder** en fallback-mekanisme, der forhindrer brudt navigation
- **Sikrer** at brugerne altid ser en gyldig skærm, selv med forkerte URL'er
- **Sikrer** en fallback-mekanisme, der forhindrer brudt navigation
- **Sørger for** at brugerne altid ser en gyldig skærm, selv med forkerte URL'er
Hvis en rute ikke kan findes, vil vi nu omdirigere til `login`-siden.
Hvis en rute ikke kan findes, omdirigerer vi nu til `login`-siden.
Nu skal vi oprette en funktion til at hente URL'en, når et link bliver klikket, og forhindre browserens standard linkadfærd:
Lad os nu oprette en funktion til at hente URL'en, når et link klikkes på, og forhindre browserens standard linkadfærd:
```js
function onLinkClick(event) {
@ -435,11 +576,11 @@ function onLinkClick(event) {
}
```
**Opdeling af denne klik-håndtering:**
- **Forhindrer** browserens standard linkadfærd ved hjælp af `preventDefault()`
**Opdelt denne klikhåndtering:**
- **Forhindrer** browserens standard linkadfærd ved brug af `preventDefault()`
- **Ekstraherer** destinations-URL'en fra det klikkede linkelement
- **Kalder** vores brugerdefinerede navigationsfunktion i stedet for at genindlæse siden
- **Bevarer** den glidende single-page applikationsoplevelse
- **Kalder** vores brugerdefinerede navigate-funktion i stedet for at genindlæse siden
- **Opretholder** den glatte single-page app-oplevelse
```html
<a href="/dashboard" onclick="onLinkClick(event)">Login</a>
@ -449,21 +590,21 @@ function onLinkClick(event) {
**Hvad denne onclick-binding opnår:**
- **Forbinder** hvert link til vores brugerdefinerede navigationssystem
- **Sender** klik-eventet til vores `onLinkClick`-funktion til behandling
- **Muliggør** glidende navigation uden sideopdateringer
- **Bevarer** korrekt URL-struktur, som brugerne kan bogmærke eller dele
- **Sender** klik-eventet til vores `onLinkClick` funktion til behandling
- **Muliggør** glat navigation uden sidereloads
- **Opretholder** korrekt URL-struktur, som brugerne kan bogmærke eller dele
Attributten [`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) binder `click`-eventet til JavaScript-kode, her kaldet til funktionen `navigate()`.
[`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) attributten binder `click` eventet til JavaScript-kode, her kaldet til `navigate()` funktionen.
Prøv at klikke på disse links, du burde nu kunne navigere mellem de forskellige skærme i din app.
Prøv at klikke på disse links, du burde nu kunne navigere mellem de forskellige skærmbilleder i din app.
Metoden `history.pushState` er en del af HTML5-standarden og implementeret i [alle moderne browsere](https://caniuse.com/?search=pushState). Hvis du bygger en webapp til ældre browsere, er der et trick, du kan bruge i stedet for dette API: ved at bruge et [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) før stien kan du implementere routing, der fungerer med almindelig anker-navigation og ikke genindlæser siden, da dens formål var at skabe interne links inden for en side.
`history.pushState` metoden er en del af HTML5-standarden og implementeret i [alle moderne browsere](https://caniuse.com/?search=pushState). Hvis du bygger en webapp til ældre browsere, er der et trick, du kan bruge i stedet for denne API: Ved at bruge en [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) før path'en kan du implementere routing, der virker med almindelig anker-navigation og ikke genindlæser siden, da det oprindeligt var tiltænkt at skabe interne links i en side.
## Få tilbage- og frem-knapperne til at fungere
## Få Tilbage- og Fremad-Knapperne til at Virke
Tilbage- og frem-knapperne er fundamentale for web-browsing, ligesom NASA-missionens kontrolenheder kan gennemgå tidligere systemtilstande under rummissioner. Brugere forventer, at disse knapper fungerer, og når de ikke gør det, bryder det den forventede browsingoplevelse.
Tilbage- og fremad-knapperne er fundamentale for webbrowsing, ligesom NASA-mission controllers kan gennemse tidligere systemtilstande under rumfart. Brugere forventer, at disse knapper virker, og hvis de ikke gør, bryder det den forventede browsingoplevelse.
Vores single-page app har brug for yderligere konfiguration for at understøtte dette. Browseren opretholder en historikstack (som vi har tilføjet til med `history.pushState`), men når brugerne navigerer gennem denne historik, skal vores app reagere ved at opdatere det viste indhold tilsvarende.
Vores single-page app har brug for ekstra konfiguration for at understøtte dette. Browseren opretholder en historikstak (som vi har tilføjet til med `history.pushState`), men når brugerne navigerer gennem denne historik, skal vores app reagere ved at opdatere det viste indhold tilsvarende.
```mermaid
sequenceDiagram
@ -472,47 +613,46 @@ sequenceDiagram
participant App
participant Template
User->>Browser: Clicks "Login" link
Browser->>App: onclick event triggered
User->>Browser: Klikker på "Log ind" link
Browser->>App: onclick begivenhed udløst
App->>App: preventDefault() & navigate('/dashboard')
App->>Browser: history.pushState('/dashboard')
Browser->>Browser: URL updates to /dashboard
App->>App: updateRoute() called
App->>Template: Find & clone dashboard template
Template->>App: Return cloned content
App->>Browser: Replace app content with template
Browser->>User: Display dashboard screen
Browser->>Browser: URL opdateres til /dashboard
App->>App: updateRoute() kaldt
App->>Template: Find & klon dashboard skabelon
Template->>App: Returner klonet indhold
App->>Browser: Erstat app-indhold med skabelon
Browser->>User: Vis dashboard skærm
Note over User,Template: User clicks browser back button
Note over User,Template: Brugeren klikker på browserens tilbage-knap
User->>Browser: Clicks back button
Browser->>Browser: History moves back to /login
Browser->>App: popstate event fired
App->>App: updateRoute() called automatically
App->>Template: Find & clone login template
Template->>App: Return cloned content
App->>Browser: Replace app content with template
Browser->>User: Display login screen
User->>Browser: Klikker på tilbage-knap
Browser->>Browser: Historik går tilbage til /login
Browser->>App: popstate begivenhed udløst
App->>App: updateRoute() kaldt automatisk
App->>Template: Find & klon login skabelon
Template->>App: Returner klonet indhold
App->>Browser: Erstat app-indhold med skabelon
Browser->>User: Vis login skærm
```
**Vigtige interaktionspunkter:**
- **Brugerhandlinger** udløser navigation gennem klik eller browserknapper
- **Appen opfanger** linkklik for at forhindre sideopdateringer
- **History API** styrer URL-ændringer og browserens historikstack
- **Templates** giver indholdsstrukturen for hver skærm
- **Event listeners** sikrer, at appen reagerer på alle navigationstyper
- **App opfanger** linkklik for at forhindre sidereloads
- **History API** styrer URL-ændringer og browserens historikstak
- **Templates** tilbyder indholdsstruktur for hver skærm
- **Event-lyttere** sikrer appens reaktion på alle navigationstyper
Brugen af `history.pushState` skaber nye poster i browserens navigationshistorik. Du kan kontrollere dette ved at holde *tilbage-knappen* på din browser, det skulle vise noget som dette:
Brug af `history.pushState` skaber nye poster i browserens navigationshistorik. Du kan tjekke det ved at holde *tilbage-knappen* på din browser nede; den skulle vise noget som dette:
![Skærmbillede af navigationshistorik](../../../../translated_images/history.7fdabbafa521e06455b738d3dafa3ff41d3071deae60ead8c7e0844b9ed987d8.da.png)
![Screenshot of navigation history](../../../../translated_images/history.7fdabbafa521e064.da.png)
Hvis du prøver at klikke tilbage-knappen et par gange, vil du se, at den aktuelle URL ændrer sig, og historikken opdateres, men den samme template bliver ved med at blive vist.
Hvis du prøver at klikke tilbage-knappen et par gange, vil du se, at den aktuelle URL ændres, og historikken opdateres, men den samme template bliver ved med at blive vist.
Det skyldes, at applikationen ikke ved, at vi skal kalde `updateRoute()` hver gang historikken ændrer sig. Hvis du kigger på dokumentationen for [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState), kan du se, at hvis tilstanden ændrer sig - hvilket betyder, at vi er flyttet til en anden URL - udløses eventet [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event). Vi vil bruge dette til at løse problemet.
Det skyldes, at applikationen ikke ved, at vi skal kalde `updateRoute()` hver gang historikken ændres. Hvis du kigger på [`history.pushState` dokumentationen](https://developer.mozilla.org/docs/Web/API/History/pushState), kan du se, at hvis tilstanden ændres altså vi bevæger os til en anden URL bliver [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event) eventet udløst. Det bruger vi til at løse problemet.
### Opgave
For at sikre, at den viste template opdateres, når browserens historik ændres, vil vi tilføje en ny funktion, der kalder `updateRoute()`. Vi gør dette nederst i vores `app.js`-fil:
For at sikre at den viste template opdateres, når browserens historik ændres, tilknytter vi en ny funktion, der kalder `updateRoute()`. Det gør vi nederst i vores `app.js` fil:
```js
window.onpopstate = () => updateRoute();
@ -520,63 +660,173 @@ updateRoute();
```
**Forståelse af denne historikintegration:**
- **Lytter** til `popstate`-events, der opstår, når brugere navigerer med browserknapper
- **Bruger** en arrow-funktion for kortfattet event handler-syntaks
- **Kalder** `updateRoute()` automatisk, hver gang historiktilstanden ændrer sig
- **Lytter** efter `popstate` events, der opstår, når brugere navigerer via browserknapper
- **Bruger** en arrow-funktion for kortere event-handler syntaks
- **Kalder** automatisk `updateRoute()` hver gang historiktilstanden ændres
- **Initialiserer** appen ved at kalde `updateRoute()` når siden først indlæses
- **Sikrer** at den korrekte template vises uanset, hvordan brugerne navigerer
- **Sikrer** korrekt templates vises uanset hvordan brugere navigerer
> 💡 **Pro Tip**: Vi brugte en [arrow-funktion](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions) her til at erklære vores `popstate` event handler for kortfattethed, men en almindelig funktion ville fungere på samme måde.
> 💡 **Pro Tip**: Vi brugte en [arrow-funktion](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions) her til at deklarere vores `popstate` event-handler for kortfattethed, men en almindelig funktion ville fungere på samme måde.
Her er en video om arrow-funktioner:
Her er en opfriskningsvideo om arrow-funktioner:
[![Arrow Functions](https://img.youtube.com/vi/OP6eEbOj2sc/0.jpg)](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions")
> 🎥 Klik på billedet ovenfor for en video om arrow-funktioner.
Prøv nu at bruge tilbage- og frem-knapperne i din browser, og kontroller, at den viste rute opdateres korrekt denne gang.
Prøv nu at bruge browserens tilbage- og fremad-knapper, og tjek at den viste rute opdateres korrekt denne gang.
### ⚡ **Hvad du kan nå på de næste 5 minutter**
- [ ] Test navigationen i din bankapp med browserens tilbage/frem-knapper
- [ ] Prøv at taste forskellige URL'er manuelt i adresselinjen for at teste routing
- [ ] Åbn browserens DevTools og undersøg, hvordan templates kopieres ind i DOM
- [ ] Eksperimenter med at tilføje console.log-udtryk for at spore routing flow
### 🎯 **Hvad du kan opnå på denne time**
- [ ] Gennemfør quizzen efter lektionen og forstå SPA-arkitekturkoncepter
- [ ] Tilføj CSS-styling, så dine bankapp-templates ser professionelle ud
- [ ] Implementer 404-fejlside-udfordringen med korrekt fejlhåndtering
- [ ] Opret credits-side-udfordringen med ekstra routingfunktionalitet
- [ ] Tilføj loading-tilstande og overgange mellem templater
### 📅 **Din uge-lange SPA-udviklingsrejse**
- [ ] Byg hele bankappen med formularer, datastyring og persistens
- [ ] Tilføj avancerede routingfunktioner som ruteparametre og indlejrede ruter
- [ ] Implementer navigation guards og routing baseret på autentifikation
- [ ] Skab genanvendelige template-komponenter og et komponentbibliotek
- [ ] Tilføj animationer og overgange for en glattere brugeroplevelse
- [ ] Deploy din SPA til en hosting-platform og konfigurer routing korrekt
### 🌟 **Din måned-lange frontend arkitektur mestring**
- [ ] Byg komplekse SPAs med moderne frameworks som React, Vue eller Angular
- [ ] Lær avancerede state management mønstre og biblioteker
- [ ] Mestring af build-værktøjer og udviklingsworkflows til SPA-udvikling
- [ ] Implementer Progressive Web App funktioner og offline funktionalitet
- [ ] Studér performanceoptimeringsteknikker til store SPAs
- [ ] Bidrag til open source SPA-projekter og del din viden
## 🎯 Din Single-Page Application Mastery Tidslinje
```mermaid
timeline
title SPA-udvikling & moderne webarkitektur læringsforløb
section Grundlag (20 minutter)
Skabelonsystemer: HTML skabelonelementer
: DOM-manipulation
: Indholdskloning
: Dynamisk rendering
section Routing grundlæggende (30 minutter)
Klientside-navigation: URL-håndtering
: History API
: Routekortlægning
: Event-håndtering
section Brugeroplevelse (40 minutter)
Navigationsfinpudsning: Browserintegration
: Tilbageknap-understøttelse
: Fejlhåndtering
: Glidende overgange
section Arkitektur mønstre (50 minutter)
Professionelle SPAs: Komponentsystemer
: Tilstandsstyring
: Ydeevneoptimering
: Fejlgrænser
section Avancerede teknikker (1 uge)
Framework integration: React Router
: Vue Router
: Angular Router
: Tilstandsbiblioteker
section Produktionsfærdigheder (1 måned)
Enterprise-udvikling: Build-systemer
: Teststrategier
: Deployments-pipelines
: Ydeevneovervågning
```
### 🛠️ Din SPA-udviklingsværktøjskasse opsummering
Efter at have fuldført denne lektion har du nu mestret:
- **Templatearkitektur**: Genanvendelige HTML-komponenter med dynamisk indholds-rendering
- **Klient-side Routing**: URL-styring og navigation uden sideopdateringer
- **Browserintegration**: History API brug og bagud/forud knap support
- **Event-drevne Systemer**: Navigation og brugerinteraktionsstyring
- **DOM-manipulation**: Template-kloning, indholdsskift og elementhåndtering
- **Fejlhåndtering**: Smidige fallback-løsninger til ugyldige ruter og manglende indhold
- **Performance-mønstre**: Effektiv indlæsning og rendering af indhold
**Virkelige Anvendelser**: Dine SPA-udviklingsfærdigheder gælder direkte for:
- **Moderne Webapplikationer**: React, Vue, Angular og andre framework-udviklinger
- **Progressive Web Apps**: Offline-kapable applikationer med app-lignende oplevelser
- **Enterprise Dashboards**: Kompleks forretningssoftware med flere visninger
- **E-handelsplatforme**: Produktkataloger, indkøbskurve og checkout flows
- **Indholdsstyring**: Dynamisk oprettelse og redigering af indhold
- **Mobiludvikling**: Hybrid-apps baseret på web-teknologier
**Professionelle Kompetencer Opnået**: Du kan nu:
- **Arkitekter** enkelt-sides applikationer med korrekt adskillelse af bekymringer
- **Implementer** klient-side routing systemer, der skalerer med applikationens kompleksitet
- **Fejlret** komplekse navigationsflows ved hjælp af browserens udviklerværktøjer
- **Optimer** applikationsydelse gennem effektiv skabelonstyring
- **Design** brugeroplevelser, der føles native og responsive
**Frontend Udviklingskoncepter Mestret**:
- **Komponentarkitektur**: Genanvendelige UI-mønstre og skabelonsystemer
- **Tilstandssynkronisering**: URL-tilstandshåndtering og browserhistorik
- **Hændelsesdrevet programmering**: Håndtering af brugerinteraktioner og navigation
- **Ydelsesoptimering**: Effektiv DOM-manipulation og indholdsindlæsning
- **Brugeroplevelsesdesign**: Glidende overgange og intuitiv navigation
**Næste Niveau**: Du er klar til at udforske moderne frontend-frameworks, avanceret tilstandshåndtering eller bygge komplekse enterprise-applikationer!
🌟 **Bedrift Opnået**: Du har opbygget et professionelt grundlag for enkelt-sides applikationer med moderne webarkitektur mønstre!
---
## GitHub Copilot Agent Challenge 🚀
## GitHub Copilot Agent Udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand til at gennemføre følgende udfordring:
**Beskrivelse:** Forbedr bankappen ved at implementere fejlhåndtering og en 404-sidetemplate for ugyldige ruter, hvilket forbedrer brugeroplevelsen, når der navigeres til ikke-eksisterende sider.
**Beskrivelse:** Forbedr bankappen ved at implementere fejlhåndtering og en 404-side skabelon til ugyldige ruter, og forbedr brugeroplevelsen, når man navigerer til ikke-eksisterende sider.
**Opgave:** Opret en ny HTML-template med id'et "not-found", der viser en brugervenlig 404-fejlside med styling. Rediger derefter JavaScript-routing-logikken til at vise denne template, når brugere navigerer til ugyldige URL'er, og tilføj en "Gå til Hjem"-knap, der navigerer tilbage til login-siden.
**Prompt:** Opret en ny HTML-skabelon med id "not-found", der viser en brugervenlig 404-fejlside med styling. Ændr derefter JavaScript routing-logikken, så denne skabelon vises, når brugere navigerer til ugyldige URL'er, og tilføj en "Gå Hjem" knap, der navigerer tilbage til login-siden.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring
Tilføj en ny template og rute for en tredje side, der viser credits for denne app.
Tilføj en ny skabelon og rute til en tredje side, der viser kreditterne for denne app.
**Udfordringsmål:**
- **Opret** en ny HTML-template med passende indholdsstruktur
- **Opret** en ny HTML-skabelon med passende indholdsstruktur
- **Tilføj** den nye rute til din rute-konfigurationsobjekt
- **Inkluder** navigationslinks til og fra credits-siden
- **Inkluder** navigationslinks til og fra kredit-siden
- **Test** at al navigation fungerer korrekt med browserhistorik
## Quiz efter forelæsning
[Quiz efter forelæsning](https://ff-quizzes.netlify.app/web/quiz/42)
## Gennemgang & Selvstudie
## Review & Selvstudie
Routing er en af de overraskende vanskelige dele af webudvikling, især når nettet bevæger sig fra sideopdateringsadfærd til Single Page Application sideopdateringer. Læs lidt om [hvordan Azure Static Web App-tjenesten](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon) håndterer routing. Kan du forklare, hvorfor nogle af de beslutninger, der er beskrevet i det dokument, er nødvendige?
Routing er en af de overraskende vanskelige dele af webudvikling, især efterhånden som nettet bevæger sig fra sideopdateringer til enkelt-sides applikationers sideopdateringer. Læs lidt om [hvordan Azure Static Web App servicen](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon) håndterer routing. Kan du forklare, hvorfor nogle af beslutningerne beskrevet i det dokument er nødvendige?
**Yderligere læringsressourcer:**
- **Udforsk** hvordan populære frameworks som React Router og Vue Router implementerer klient-side routing
- **Undersøg** forskellene mellem hash-baseret routing og History API routing
- **Lær** om server-side rendering (SSR) og hvordan det påvirker routing-strategier
- **Undersøg** forskellene mellem hash-baseret routing og history API routing
- **Lær** om server-side rendering (SSR) og hvordan det påvirker routingstrategier
- **Undersøg** hvordan Progressive Web Apps (PWAs) håndterer routing og navigation
## Opgave
[Forbedr routing](assignment.md)
[Forbedr routingen](assignment.md)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi stræber efter nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For vigtig information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,112 +1,236 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "32bd800759c3e943c38ad9ae6e1f51e0",
"translation_date": "2025-10-23T22:04:43+00:00",
"original_hash": "b807b09df716dc48a2b750835bf8e933",
"translation_date": "2026-01-06T23:53:55+00:00",
"source_file": "7-bank-project/4-state-management/README.md",
"language_code": "da"
}
-->
# Byg en bankapp del 4: Koncepter inden for tilstandshåndtering
# Byg en Bankapp Del 4: Begreber om Tilstandsstyring
## Quiz før lektionen
## ⚡ Hvad Du Kan Nå på de Næste 5 Minutter
[Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/47)
**Hurtigstart for Travle Udviklere**
```mermaid
flowchart LR
A[⚡ 5 minutter] --> B[Diagnostiser tilstandsproblemer]
B --> C[Opret centralt tilstandsobjekt]
C --> D[Tilføj updateState-funktion]
D --> E[Se øjeblikkelige forbedringer]
```
- **Minut 1**: Test det nuværende tilstandsproblem - log ind, opdater siden, bemærk udlogning
- **Minut 2**: Erstat `let account = null` med `let state = { account: null }`
- **Minut 3**: Opret en simpel `updateState()` funktion til kontrollerede opdateringer
- **Minut 4**: Opdater én funktion til at bruge det nye mønster
- **Minut 5**: Test den forbedrede forudsigelighed og fejlsøgningsmulighed
**Hurtig Diagnostisk Test**:
```javascript
// Før: Spredt tilstand
let account = null; // Mistet ved opdatering!
// Efter: Centraliseret tilstand
let state = Object.freeze({ account: null }); // Kontrolleret og sporbar!
```
**Hvorfor det er vigtigt**: På 5 minutter vil du opleve overgangen fra kaotisk tilstandsstyring til forudsigelige, fejlfri mønstre. Det er fundamentet, der gør komplekse applikationer vedligeholdelsesvenlige.
## 🗺️ Din Læringsrejse Gennem Mesterskabet i Tilstandsstyring
```mermaid
journey
title Fra spredt tilstand til professionel arkitektur
section Diagnosticering af problemer
Identificer tilstands-tab problemer: 3: You
Forstå spredte opdateringer: 4: You
Genkend arkitekturbehov: 6: You
section Centralisering af kontrol
Opret samlet tilstandsobjekt: 5: You
Implementer kontrollerede opdateringer: 7: You
Tilføj uforanderlige mønstre: 8: You
section Tilføjelse af persistens
Implementer localStorage: 6: You
Håndter serialisering: 7: You
Skab sessionskontinuitet: 9: You
section Balancering af friskhed
Adresse datældning: 5: You
Byg opfriskningssystemer: 8: You
Opnå optimal balance: 9: You
```
**Din Målsætning**: Ved slutningen af denne lektion vil du have bygget et professionelt tilstandsstyringssystem, der håndterer vedvarende data, friskhed og forudsigelige opdateringer de samme mønstre som bruges i produktionsapplikationer.
## Forud for Forelæsning Quiz
[Forud for forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/47)
## Introduktion
Tilstandshåndtering er som navigationssystemet på Voyager-rumfartøjet når alt fungerer glat, bemærker du knap nok, at det er der. Men når tingene går galt, kan det være forskellen mellem at nå interstellart rum og at drive fortabt i det kosmiske tomrum. Inden for webudvikling repræsenterer tilstand alt, hvad din applikation skal huske: brugerens loginstatus, formulardata, navigationshistorik og midlertidige grænsefladetilstande.
Tilstandsstyring er som navigationssystemet på Voyager-rumskibet når alt fungerer glat, bemærker man det næsten ikke. Men når noget går galt, er det forskellen mellem at nå det interstellare rum og at drive tabt i det kosmiske tomrum. I webudvikling repræsenterer tilstand alt, hvad din applikation skal huske: bruger-loginstatus, formularoplysninger, navigationshistorik og midlertidige grænsefladestatusser.
Efterhånden som din bankapp er udviklet fra en simpel loginformular til en mere sofistikeret applikation, har du sandsynligvis stødt på nogle almindelige udfordringer. Opdater siden, og brugerne bliver uventet logget ud. Luk browseren, og al fremgang forsvinder. Fejlsøg et problem, og du leder gennem flere funktioner, der alle ændrer de samme data på forskellige måder.
Efterhånden som din bankapp har udviklet sig fra en simpel loginformular til en mere sofistikeret applikation, har du sandsynligvis stødt på nogle almindelige udfordringer. Opdater siden, og brugere bliver uventet logget ud. Luk browseren, og alt fremskridt forsvinder. Fejlsøg et problem, og du leder gennem flere funktioner, der alle ændrer de samme data på forskellige måder.
Dette er ikke tegn på dårlig kodning det er de naturlige vækstproblemer, der opstår, når applikationer når en vis kompleksitet. Hver udvikler står over for disse udfordringer, når deres apps går fra "proof of concept" til "produktionsklar."
Disse er ikke tegn på dårlig kodning de er de naturlige vækstsmerter, der opstår, når applikationer når et vist kompleksitetsniveau. Hver udvikler møder disse udfordringer, når deres apps bevæger sig fra "proof of concept" til "klar til produktion."
I denne lektion implementerer vi et centraliseret tilstandshåndteringssystem, der forvandler din bankapp til en pålidelig, professionel applikation. Du lærer at håndtere dataflow forudsigeligt, bevare brugersessioner korrekt og skabe den glatte brugeroplevelse, som moderne webapplikationer kræver.
I denne lektion vil vi implementere et centraliseret tilstandsstyringssystem, der forvandler din bankapp til en pålidelig, professionel applikation. Du vil lære at styre dataflow forudsigeligt, vedvarende brugersessioner passende og skabe en glat brugeroplevelse, som moderne webapplikationer kræver.
## Forudsætninger
Før du dykker ned i tilstandshåndteringskoncepter, skal du have din udviklingsmiljø korrekt opsat og din bankapps fundament på plads. Denne lektion bygger direkte på koncepterne og koden fra de tidligere dele af denne serie.
Før du dykker ned i tilstandsstyringsbegreber, skal du have dit udviklingsmiljø korrekt sat op og fundamentet for din bankapp på plads. Denne lektion bygger direkte videre på koncepterne og koden fra tidligere dele af denne serie.
Sørg for, at du har følgende komponenter klar, før du fortsætter:
Sørg for at have følgende komponenter klar, inden du fortsætter:
**Påkrævet opsætning:**
- Fuldfør [datahentningslektionen](../3-data/README.md) - din app skal kunne indlæse og vise kontodata korrekt
**Nødvendig Opsætning:**
- Gennemfør [datahentningslektionen](../3-data/README.md) din app bør kunne indlæse og vise kontodata korrekt
- Installer [Node.js](https://nodejs.org) på dit system for at køre backend-API'en
- Start [server-API'en](../api/README.md) lokalt for at håndtere kontodataoperationer
**Test af dit miljø:**
**Test dit Miljø:**
Bekræft, at din API-server kører korrekt ved at udføre denne kommando i en terminal:
Bekræft at din API-server kører korrekt ved at køre denne kommando i et terminalvindue:
```sh
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
# -> skal returnere "Bank API v1.0.0" som resultat
```
**Hvad denne kommando gør:**
- **Sender** en GET-anmodning til din lokale API-server
- **Tester** forbindelsen og bekræfter, at serveren svarer
- **Returnerer** API-versionens oplysninger, hvis alt fungerer korrekt
- **Sender** en GET-forespørgsel til din lokale API-server
- **Tester** forbindelsen og bekræfter at serveren svarer
- **Returnerer** API-versionen, hvis alt fungerer korrekt
## 🧠 Oversigt over Tilstandsstyringsarkitektur
```mermaid
mindmap
root((State Management))
Nuværende Problemer
Sessions Tab
Problemer ved Sideopdatering
Browser Luknings Indvirkning
Problemer med Variabel Reset
Spredte Opdateringer
Flere Ændringspunkter
Fejlfinding Udfordringer
Uforudsigelig Adfærd
Ufuldstændig Oprydning
Logout Tilstandsproblemer
Hukommelses Lækager
Sikkerheds Bekymringer
Centraliserede Løsninger
Enheds Tilstands Objekt
Enkelt Sandhedskilde
Forudsigelig Struktur
Skalerbar Fundament
Kontrollerede Opdateringer
Uforanderlige Mønstre
Object.freeze Brug
Funktionsbaserede Ændringer
Tilstands Sporing
Historik Håndtering
Fejlfindings Synlighed
Ændrings Revision
Vedvarende Strategier
localStorage Integration
Sessions Kontinuitet
JSON Serialisering
Automatisk Synkronisering
Data Friskhed
Server Opdatering
Håndtering af Uaktuelle Data
Balancerings Optimering
Lager Optimering
Minimal Data
Ydeevne Fokus
Sikkerheds Overvejelser
```
**Kerneprincip**: Professionel tilstandsstyring balancerer forudsigelighed, vedholdenhed og ydeevne for at skabe pålidelige brugeroplevelser, der kan skalere fra simple interaktioner til komplekse applikationsarbejdsgange.
---
## Diagnosticering af de nuværende tilstandsproblemer
## Diagnostisering af Nuværende Tilstandsproblemer
Som Sherlock Holmes, der undersøger en gerningssted, skal vi forstå præcis, hvad der sker i vores nuværende implementering, før vi kan løse mysteriet om forsvindende brugersessioner.
Som Sherlock Holmes, der undersøger en gerningssted, skal vi forstå præcis hvad der foregår i vores nuværende implementering, før vi kan løse mysteriet om forsvindende brugersessioner.
Lad os udføre et simpelt eksperiment, der afslører de underliggende udfordringer med tilstandshåndtering:
Lad os lave et simpelt eksperiment, der afslører de underliggende tilstandsstyringsudfordringer:
**🧪 Prøv denne diagnostiske test:**
1. Log ind på din bankapp og naviger til dashboardet
2. Opdater browserens side
3. Observer, hvad der sker med din loginstatus
**🧪 Prøv Denne Diagnostiske Test:**
1. Log ind i din bankapp og gå til dashboardet
2. Opdater browseren
3. Observer hvad der sker med din loginstatus
Hvis du bliver omdirigeret tilbage til login-skærmen, har du opdaget det klassiske problem med tilstandspersistens. Denne adfærd opstår, fordi vores nuværende implementering gemmer brugerdata i JavaScript-variabler, der nulstilles ved hver sideindlæsning.
Hvis du bliver sendt tilbage til login-skærmen, har du opdaget det klassiske problem med tilstandsvedholdenhed. Denne adfærd sker, fordi vores nuværende implementering gemmer brugerdata i JavaScript-variabler, som nulstilles ved hver sideindlæsning.
**Problemer med den nuværende implementering:**
**Nuværende Implementeringsproblemer:**
Den simple `account`-variabel fra vores [tidligere lektion](../3-data/README.md) skaber tre væsentlige problemer, der påvirker både brugeroplevelse og kodevedligeholdelse:
Den simple `account` variabel fra vores [tidligere lektion](../3-data/README.md) skaber tre væsentlige problemer, der påvirker både brugeroplevelse og kodevedligeholdelse:
| Problem | Teknisk årsag | Brugerpåvirkning |
| Problem | Teknisk Årsag | Brugerpåvirkning |
|---------|---------------|------------------|
| **Sessionstab** | Sideopdatering rydder JavaScript-variabler | Brugere skal ofte logge ind igen |
| **Spredte opdateringer** | Flere funktioner ændrer tilstand direkte | Fejlsøgning bliver mere og mere vanskelig |
| **Ufuldstændig oprydning** | Logout rydder ikke alle tilstandsreferencer | Potentielle sikkerheds- og privatlivsproblemer |
| **Sessions-tab** | Sideopdatering nulstiller JavaScript-variabler | Brugere skal ofte logge ind igen |
| **Spredte Opdateringer** | Flere funktioner ændrer tilstanden direkte | Fejlsøgning bliver mere og mere vanskelig |
| **Ufultstændig Oprydning** | Logout sletter ikke alle tilstandsreferencer | Potentielle sikkerheds- og privatlivsproblemer |
**Den arkitektoniske udfordring:**
**Den Arkitektoniske Udfordring:**
Ligesom Titanics opdelte design, der virkede robust, indtil flere rum blev oversvømmet samtidig, vil det ikke løse de underliggende arkitektoniske problemer at rette disse problemer individuelt. Vi har brug for en omfattende løsning til tilstandshåndtering.
Som Titanics afdelingsopdeling, der virkede robust indtil flere skibsrum pludselig blev oversvømmet, vil det ikke løse disse problemer blot at rette dem individuelt. Vi har brug for en omfattende tilstandsstyringsløsning.
> 💡 **Hvad prøver vi egentlig at opnå her?**
[Tilstandshåndtering](https://en.wikipedia.org/wiki/State_management) handler i virkeligheden om at løse to grundlæggende gåder:
1. **Hvor er mine data?**: At holde styr på, hvilke oplysninger vi har, og hvor de kommer fra
2. **Er alle på samme side?**: At sikre, at det, brugerne ser, stemmer overens med, hvad der faktisk sker
**Vores handlingsplan:**
I stedet for at jagte vores egen hale, vil vi oprette et **centraliseret tilstandshåndteringssystem**. Tænk på det som at have én virkelig organiseret person, der har styr på alle de vigtige ting:
![Skema, der viser dataflowet mellem HTML, brugerhandlinger og tilstand](../../../../translated_images/data-flow.fa2354e0908fecc89b488010dedf4871418a992edffa17e73441d257add18da4.da.png)
**Forståelse af dette dataflow:**
[Tilstandsstyring](https://en.wikipedia.org/wiki/State_management) handler i bund og grund om at løse to fundamentale gåder:
1. **Hvor er mine data?**: Holde styr på hvilken information vi har og hvor den kommer fra
2. **Er alle på samme side?**: Sikre at det brugerne ser stemmer overens med hvad der rent faktisk sker
**Vores Spilplan:**
I stedet for at løbe i ring, vil vi skabe et **centraliseret tilstandsstyringssystem**. Tænk på det som én virkelig organiseret person, der har ansvaret for alt det vigtige:
![Schema showing the data flows between the HTML, user actions and state](../../../../translated_images/data-flow.fa2354e0908fecc8.da.png)
```mermaid
flowchart TD
A[Brugerhandling] --> B[Hændelseshåndtering]
B --> C[updateState Funktion]
C --> D{Tilstandsvalidering}
D -->|Gyldig| E[Opret Ny Tilstand]
D -->|Ugyldig| F[Fejlhåndtering]
E --> G[Object.freeze]
G --> H[Opdater localStorage]
H --> I[Ud­løs UI-opdatering]
I --> J[Bruger Ser Ændringer]
F --> K[Bruger Ser Fejl]
subgraph "Lag for tilstandsstyring"
C
E
G
end
subgraph "Persistence Layer"
H
L[localStorage]
H -.-> L
end
```
**Forstå dette dataflow:**
- **Centraliserer** al applikationstilstand ét sted
- **Dirigerer** alle tilstandsændringer gennem kontrollerede funktioner
- **Sikrer**, at brugergrænsefladen forbliver synkroniseret med den aktuelle tilstand
- **Giver** et klart, forudsigeligt mønster for datastyring
- **Router** alle tilstandsændringer gennem kontrollerede funktioner
- **Sikrer** at brugergrænsefladen forbliver synkroniseret med den aktuelle tilstand
- **Tilbyder** et klart, forudsigeligt mønster til datastyring
> 💡 **Professionel indsigt**: Denne lektion fokuserer på grundlæggende koncepter. For komplekse applikationer tilbyder biblioteker som [Redux](https://redux.js.org) mere avancerede funktioner til tilstandshåndtering. At forstå disse kerneprincipper vil hjælpe dig med at mestre ethvert tilstandshåndteringsbibliotek.
> 💡 **Professionel indsigt**: Denne lektion fokuserer på grundlæggende begreber. For komplekse applikationer tilbyder biblioteker som [Redux](https://redux.js.org) mere avancerede tilstandsstyringsfunktioner. At forstå disse kerneprincipper hjælper dig med at mestre ethvert tilstandsstyringsbibliotek.
> ⚠️ **Avanceret emne**: Vi dækker ikke automatiske UI-opdateringer, der udløses af tilstandsændringer, da dette involverer [Reaktiv programmering](https://en.wikipedia.org/wiki/Reactive_programming)-koncepter. Overvej dette som et fremragende næste skridt i din læringsrejse!
> ⚠️ **Avanceret Emne**: Vi kommer ikke til at dække automatiske UI-opdateringer udløst af tilstandsændringer, da dette involverer [Reaktiv Programmering](https://en.wikipedia.org/wiki/Reactive_programming) koncepter. Overvej dette som et fremragende næste skridt på din læringsrejse!
### Opgave: Centraliser tilstandsstruktur
### Opgave: Centraliser Tilstandsstruktur
Lad os begynde at transformere vores spredte tilstandshåndtering til et centraliseret system. Dette første skridt etablerer fundamentet for alle de forbedringer, der følger.
Lad os begynde at omdanne vores spredte tilstandsstyring til et centraliseret system. Dette første skridt lægger fundamentet for alle efterfølgende forbedringer.
**Trin 1: Opret et centraliseret tilstandsobjekt**
**Trin 1: Opret et Centraliseret Tilstandsobjekt**
Erstat den simple `account`-deklaration:
Erstat den simple `account` deklaration:
```js
let account = null;
@ -120,17 +244,17 @@ let state = {
};
```
**Hvorfor denne ændring er vigtig:**
- **Centraliserer** alle applikationsdata ét sted
- **Forbereder** strukturen til at tilføje flere tilstandsegenskaber senere
- **Skaber** en klar grænse mellem tilstand og andre variabler
- **Etablerer** et mønster, der skalerer, efterhånden som din app vokser
**Her er hvorfor denne ændring er vigtig:**
- **Centraliserer** al applikationsdata ét sted
- **Forbereder** strukturen til at tilføje flere tilstands-egenskaber senere
- **Skaber** en klar afgrænsning mellem tilstand og andre variabler
- **Etablerer** et mønster der skalerer efterhånden som din app vokser
**Trin 2: Opdater tilstandsadgangsmønstre**
**Trin 2: Opdater Tilgangsmønstre til Tilstand**
Opdater dine funktioner til at bruge den nye tilstandsstruktur:
**I `register()` og `login()` funktionerne**, erstat:
**I `register()`- og `login()`-funktionerne**, erstat:
```js
account = ...
```
@ -140,60 +264,92 @@ Med:
state.account = ...
```
**I `updateDashboard()` funktionen**, tilføj denne linje øverst:
**I `updateDashboard()`-funktionen**, tilføj denne linje øverst:
```js
const account = state.account;
```
**Hvad disse opdateringer opnår:**
- **Bevarer** eksisterende funktionalitet, mens strukturen forbedres
- **Forbereder** din kode til mere sofistikeret tilstandshåndtering
- **Skaber** konsistente mønstre for adgang til tilstandsdata
- **Opretholder** eksisterende funktionalitet mens strukturen forbedres
- **Forbereder** din kode til mere sofistikeret tilstandsstyring
- **Skaber** konsekvente mønstre for at tilgå tilstandsdata
- **Etablerer** fundamentet for centraliserede tilstandsopdateringer
> 💡 **Bemærk**: Denne refaktorering løser ikke straks vores problemer, men det skaber det essentielle fundament for de kraftfulde forbedringer, der kommer!
> 💡 **Note**: Denne refaktorering løser ikke straks vores problemer, men skaber det essentielle fundament for de kraftfulde forbedringer, der følger!
### 🎯 Pædagogisk Tjek-ind: Centraliseringsprincipper
**Pause og Reflektér**: Du har netop implementeret fundamentet for centraliseret tilstandsstyring. Det er en afgørende arkitektonisk beslutning.
**Hurtig Selv-vurdering**:
- Kan du forklare, hvorfor centralisering af tilstand i ét objekt er bedre end spredte variabler?
- Hvad ville ske, hvis du glemte at opdatere en funktion til at bruge `state.account`?
- Hvordan forbereder dette mønster din kode til mere avancerede funktioner?
## Implementering af kontrollerede tilstandsopdateringer
**Virkelighedsforbindelse**: Centraliseringsmønstret du har lært er fundamentet for moderne frameworks som Redux, Vuex og React Context. Du bygger samme arkitektoniske tænkning, som bruges i store applikationer.
Med vores tilstand centraliseret er næste skridt at etablere kontrollerede mekanismer til dataændringer. Denne tilgang sikrer forudsigelige tilstandsændringer og lettere fejlsøgning.
**Udfordrende spørgsmål**: Hvis du skulle tilføje brugerpræferencer (tema, sprog) til din app, hvor ville du så tilføje dem i tilstandsstrukturen? Hvordan ville det kunne skaleres?
Det grundlæggende princip minder om lufttrafikkontrol: i stedet for at tillade flere funktioner at ændre tilstanden uafhængigt, vil vi kanalisere alle ændringer gennem en enkelt, kontrolleret funktion. Dette mønster giver klar kontrol over, hvornår og hvordan data ændres.
## Implementering af Kontrollerede Tilstandsopdateringer
**Uforanderlig tilstandshåndtering:**
Med vores tilstand centraliseret går næste skridt ud på at etablere kontrollerede mekanismer for dataændringer. Denne tilgang sikrer forudsigelige tilstandsændringer og lettere fejlsøgning.
Vi vil behandle vores `state`-objekt som [*uforanderligt*](https://en.wikipedia.org/wiki/Immutable_object), hvilket betyder, at vi aldrig ændrer det direkte. I stedet skaber hver ændring et nyt tilstandsobjekt med de opdaterede data.
Kerneprincippet minder om lufttrafikstyring: i stedet for at lade flere funktioner ændre tilstanden uafhængigt, kanaliserer vi alle ændringer gennem én kontrolleret funktion. Dette mønster giver klart overblik over hvornår og hvordan data ændres.
Selvom denne tilgang måske oprindeligt virker ineffektiv sammenlignet med direkte ændringer, giver den betydelige fordele for fejlsøgning, testning og opretholdelse af applikationens forudsigelighed.
**Immutable Tilstandsstyring:**
**Fordele ved uforanderlig tilstandshåndtering:**
Vi behandler vores `state` objekt som [*immutable*](https://en.wikipedia.org/wiki/Immutable_object), hvilket betyder, at vi aldrig ændrer det direkte. I stedet skaber hver ændring et nyt tilstandsobjekt med de opdaterede data.
Selvom denne tilgang i starten kan virke ineffektiv sammenlignet med direkte ændringer, giver den store fordele for fejlsøgning, testning og opretholdelse af applikationens forudsigelighed.
**Fordele ved immutable tilstandsstyring:**
| Fordel | Beskrivelse | Indvirkning |
|--------|-------------|-------------|
|--------|-------------|------------|
| **Forudsigelighed** | Ændringer sker kun gennem kontrollerede funktioner | Nemmere at fejlsøge og teste |
| **Historiksporing** | Hver tilstandsændring skaber et nyt objekt | Muliggør fortryd/omgør funktionalitet |
| **Forebyggelse af bivirkninger** | Ingen utilsigtede ændringer | Forhindrer mystiske fejl |
| **Ydelsesoptimering** | Let at opdage, når tilstanden faktisk ændrede sig | Muliggør effektive UI-opdateringer |
| **Historiksporing** | Hver tilstandsændring skaber et nyt objekt | Muliggør fortryd/redo funktionalitet |
| **Forebyggelse af Sideeffekter** | Ingen utilsigtede ændringer | Forhindrer mystiske fejl |
| **Ydelsesoptimering** | Let at opdage virkelig tilstandsændringer | Muliggør effektiv UI-opdatering |
**JavaScript-uforanderlighed med `Object.freeze()`:**
**JavaScript Immutability med `Object.freeze()`:**
JavaScript tilbyder [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) for at forhindre ændringer af objekter:
JavaScript leverer [`Object.freeze()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) til at forhindre objektændringer:
```js
const immutableState = Object.freeze({ account: userData });
// Any attempt to modify immutableState will throw an error
// Ethvert forsøg på at ændre immutableState vil forårsage en fejl
```
**Hvad der sker her:**
**Fordeling af hvad der sker her:**
- **Forhindrer** direkte egenskabstildelinger eller sletninger
- **Kaster** undtagelser, hvis der forsøges på ændringer
- **Sikrer**, at tilstandsændringer skal gå gennem kontrollerede funktioner
- **Skaber** en klar kontrakt for, hvordan tilstanden kan opdateres
> 💡 **Dybdegående**: Læs om forskellen mellem *overfladiske* og *dybe* uforanderlige objekter i [MDN-dokumentationen](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze). At forstå denne forskel er afgørende for komplekse tilstandsstrukturer.
- **Kaster** fejl hvis der forsøges at ændre
- **Sikrer** at tilstandsændringer skal gå gennem kontrollerede funktioner
- **Skaber** en klar kontrakt for hvordan tilstand kan opdateres
> 💡 **Dybdegående**: Lær om forskellen mellem *shallow* og *deep* immutable objekter i [MDN-dokumentationen](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze). At forstå denne forskel er afgørende for komplekse tilstandsstrukturer.
```mermaid
stateDiagram-v2
[*] --> StateV1: Starttilstand
StateV1 --> StateV2: updateState('konto', nyeData)
StateV2 --> StateV3: updateState('konto', andenOpdatering)
StateV3 --> StateV4: updateState('præferencer', brugerIndstillinger)
note right of StateV1
Object.freeze()
Uforanderlig
Fejlfinderbar
end note
note right of StateV2
Nyt objekt oprettet
Tidligere tilstand bevaret
Forudsigelige ændringer
end note
```
### Opgave
Lad os oprette en ny `updateState()`-funktion:
Lad os skabe en ny `updateState()` funktion:
```js
function updateState(property, newData) {
@ -204,9 +360,9 @@ function updateState(property, newData) {
}
```
I denne funktion opretter vi et nyt tilstandsobjekt og kopierer data fra den tidligere tilstand ved hjælp af [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Derefter overskriver vi en bestemt egenskab i tilstandsobjektet med de nye data ved hjælp af [bracket notation](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` til tildeling. Til sidst låser vi objektet for at forhindre ændringer ved hjælp af `Object.freeze()`. Vi har kun egenskaben `account` gemt i tilstanden for nu, men med denne tilgang kan du tilføje så mange egenskaber, som du har brug for i tilstanden.
I denne funktion opretter vi et nyt tilstandsobjekt og kopierer data fra den forrige tilstand ved hjælp af [*spread (`...`) operatoren*](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals). Derefter overskriver vi en specifik egenskab i tilstandsobjektet med de nye data ved brug af [braketsyntaksen](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties) `[property]` til tildeling. Til sidst låser vi objektet for at forhindre ændringer med `Object.freeze()`. Vi har lige nu kun `account` egenskaben gemt i tilstanden, men med denne tilgang kan du tilføje så mange egenskaber som nødvendigt i tilstanden.
Vi opdaterer også initialiseringen af `state` for at sikre, at den oprindelige tilstand også er låst:
Vi opdaterer også `state` initialiseringen for at sikre, at den oprindelige tilstand også fryses:
```js
let state = Object.freeze({
@ -214,19 +370,19 @@ let state = Object.freeze({
});
```
Derefter opdaterer vi `register`-funktionen ved at erstatte tildelingen `state.account = result;` med:
Dernæst opdaterer du `register` funktionen ved at erstatte tildelingen `state.account = result;` med:
```js
updateState('account', result);
```
Gør det samme med `login`-funktionen, og erstat `state.account = data;` med:
Gør det samme i `login` funktionen, hvor `state.account = data;` erstattes med:
```js
updateState('account', data);
```
Vi benytter også lejligheden til at løse problemet med, at kontodata ikke ryddes, når brugeren klikker på *Logout*.
Vi benytter nu chancen til at løse problemet med, at kontodata ikke bliver ryddet, når brugeren klikker på *Logout*.
Opret en ny funktion `logout()`:
@ -237,73 +393,91 @@ function logout() {
}
```
I `updateDashboard()` skal du erstatte omdirigeringen `return navigate('/login');` med `return logout();`.
I `updateDashboard()` erstatter du omdirigeringen `return navigate('/login');` med `return logout()`;
Prøv at registrere en ny konto, logge ud og logge ind igen for at kontrollere, at alt stadig fungerer korrekt.
Prøv at registrere en ny konto, logge ud og igen ind for at sikre, at alt stadig fungerer korrekt.
> Tip: Du kan se alle tilstandsændringer ved at tilføje `console.log(state)` nederst i `updateState()` og åbne konsollen i din browsers udviklingsværktøjer.
> Tip: du kan se alle tilstandsændringer ved at tilføje `console.log(state)` nederst i `updateState()` og åbne konsollen i din browsers udviklingsværktøjer.
## Implementering af datapersistens
## Implementering af Datapersistens
Problemet med sessionstab, som vi identificerede tidligere, kræver en persistensløsning, der opretholder brugertilstanden på tværs af browsersessioner. Dette forvandler vores applikation fra en midlertidig oplevelse til et pålideligt, professionelt værktøj.
Det sessions tab-problem vi tidligere identificerede kræver en vedvarende løsning, der bevarer brugertilstanden på tværs af browsersessioner. Dette forvandler vores applikation fra en midlertidig oplevelse til et pålideligt, professionelt værktøj.
Tænk på, hvordan atomure opretholder præcis tid, selv under strømafbrydelser, ved at gemme kritisk tilstand i ikke-flygtig hukommelse. På samme måde har webapplikationer brug for persistente lagringsmekanismer for at bevare essentielle brugerdata på tværs af browsersessioner og sideopdateringer.
Tænk på hvordan atomure bevarer præcis tid selv gennem strømafbrydelser ved at lagre kritisk tilstand i ikke-flygtig hukommelse. På samme måde har webapplikationer brug for vedvarende lagringsmekanismer for at bevare vigtig brugerdata på tværs af browsersessioner og sideopdateringer.
**Strategiske spørgsmål for datapersistens:**
**Strategiske Spørgsmål for Datapersistens:**
Før du implementerer persistens, skal du overveje disse kritiske faktorer:
Før implementering af vedholdenhed, overvej disse kritiske faktorer:
| Spørgsmål | Bankapp-kontekst | Beslutningspåvirkning |
|-----------|------------------|-----------------------|
| **Er dataene følsomme?** | Kontosaldo, transaktionshistorik | Vælg sikre lagringsmetoder |
| **Hvor længe skal det opbevares?** | Loginstatus vs. midlertidige UI-præferencer | Vælg passende lagringsvarighed |
| **Har serveren brug for det?** | Autentificeringstokens vs. UI-indstillinger | Bestem delingskrav |
| Spørgsmål | Bankapp Kontekst | Beslutningens Indvirkning |
|-----------|------------------|--------------------------|
| **Er data følsomt?** | Kontosaldo, transaktionshistorik | Vælg sikre lagringsmetoder |
| **Hvor længe skal det vedvare?** | Login-tilstand vs. midlertidige UI-præferencer | Vælg passende lagringsvarighed |
| **Har serveren brug for det?** | Autentificeringstokens vs. UI-indstillinger | Bestem delingsbehov |
**Browserlagringsmuligheder:**
Moderne browsere tilbyder flere lagringsmekanismer, hver designet til forskellige anvendelser:
Moderne browsere tilbyder flere lagringsmekanismer, hver designet til forskellige anvendelsestilfælde:
**Primære lagrings-API'er:**
1. **[`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage)**: Persistent [Key/Value-lagring](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)
- **Bevarer** data på tværs af browsersessioner på ubestemt tid
- **Overlever** genstart af browseren og computeren
- **Afgrænset** til det specifikke websteds domæne
- **Perfekt** til brugerpræferencer og loginstatus
1. **[`localStorage`](https://developer.mozilla.org/docs/Web/API/Window/localStorage)**: Vedvarende [Nøgle/Værdi-lagring](https://en.wikipedia.org/wiki/Key%E2%80%93value_database)
- **Vedvarer** data på tværs af browsersessioner på ubestemt tid
- **Overlever** browsergenstarter og computergenstarter
- **Afgrænset** til det specifikke webstedsdomæne
- **Perfekt** til brugerpræferencer og login-tilstande
2. **[`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage)**: Midlertidig sessionlagring
2. **[`sessionStorage`](https://developer.mozilla.org/docs/Web/API/Window/sessionStorage)**: Midlertidig sessionslagring
- **Fungerer** identisk med localStorage under aktive sessioner
- **Rydder** automatisk, når browserfanen lukkes
- **Ideel** til midlertidige data, der ikke bør bevares
- **Rydder** automatisk op, når browsertab lukkes
- **Ideel** til midlertidige data, der ikke skal vedvare
3. **[HTTP-cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies)**: Server-delt lagring
- **Sendes automatisk** med hver serveranmodning
- **Perfekt** til [autentificering](https://en.wikipedia.org/wiki/Authentication) tokens
- **Begrænset** i størrelse og kan påvirke ydeevnen
3. **[HTTP Cookies](https://developer.mozilla.org/docs/Web/HTTP/Cookies)**: Server-delt lagring
- **Sendes automatisk** med hver serverforespørgsel
- **Perfekte** til [autentificerings](https://en.wikipedia.org/wiki/Authentication)tokens
- **Begrænsede** i størrelse og kan påvirke ydeevnen
**Krav til dataserialisering:**
Både `localStorage` og `sessionStorage` gemmer kun [strenge](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String):
```js
// Convert objects to JSON strings for storage
// Konverter objekter til JSON-strenge til lagring
const accountData = { user: 'john', balance: 150 };
localStorage.setItem('account', JSON.stringify(accountData));
// Parse JSON strings back to objects when retrieving
// Fortolk JSON-strenge tilbage til objekter ved hentning
const savedAccount = JSON.parse(localStorage.getItem('account'));
```
**Forståelse af serialisering:**
- **Konverterer** JavaScript-objekter til JSON-strenge ved hjælp af [`JSON.stringify()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
- **Genskaber** objekter fra JSON ved hjælp af [`JSON.parse()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)
- **Håndterer** komplekse indlejrede objekter og arrays automatisk
- **Fejler** på funktioner, udefinerede værdier og cirkulære referencer
- **Genskaber** objekter fra JSON ved at bruge [`JSON.parse()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)
- **Håndterer** komplekse, indlejrede objekter og arrays automatisk
- **Fejler** på funktioner, undefined værdier og cirkulære referencer
> 💡 **Avanceret mulighed**: For komplekse offline-applikationer med store datasæt, overvej [`IndexedDB` API](https://developer.mozilla.org/docs/Web/API/IndexedDB_API). Det tilbyder en fuld klient-side database, men kræver en mere kompleks implementering.
### Opgave: Implementer localStorage Persistens
```mermaid
quadrantChart
title Browser Lageringsmuligheder
x-axis Lav Kompleksitet --> Høj Kompleksitet
y-axis Kort Varighed --> Lang Varighed
quadrant-1 Professionelle Værktøjer
quadrant-2 Enkel Persistens
quadrant-3 Midlertidig Lagering
quadrant-4 Avancerede Systemer
localStorage: [0.3, 0.8]
sessionStorage: [0.2, 0.2]
HTTP Cookies: [0.6, 0.7]
IndexedDB: [0.9, 0.9]
Memory Variables: [0.1, 0.1]
```
### Opgave: Implementer localStorage-Vedvarende lagring
Lad os implementere persistent lagring, så brugerne forbliver logget ind, indtil de eksplicit logger ud. Vi bruger `localStorage` til at gemme kontodata på tværs af browsersessioner.
Lad os implementere vedvarende lagring, så brugere forbliver logget ind, indtil de eksplicit logger ud. Vi vil bruge `localStorage` til at gemme kontodata på tværs af browsersessioner.
**Trin 1: Definer lagringskonfiguration**
@ -311,29 +485,29 @@ Lad os implementere persistent lagring, så brugerne forbliver logget ind, indti
const storageKey = 'savedAccount';
```
**Hvad denne konstant tilbyder:**
- **Skaber** en konsistent identifikator for vores lagrede data
- **Forebygger** tastefejl i referencer til lagringsnøgler
- **Gør** det nemt at ændre lagringsnøglen, hvis nødvendigt
- **Følger** bedste praksis for vedligeholdelig kode
**Hvad denne konstant leverer:**
- **Opretter** et konsistent id for vores gemte data
- **Forhindrer** tastefejl i lagringsnøglehenvisninger
- **Gør det nemt** at ændre lagringsnøglen om nødvendigt
- **Følger** bedste praksis for vedligeholdelsesvenlig kode
**Trin 2: Tilføj automatisk persistens**
**Trin 2: Tilføj automatisk vedvarende lagring**
Tilføj denne linje i slutningen af funktionen `updateState()`:
Tilføj denne linje i slutningen af `updateState()` funktionen:
```js
localStorage.setItem(storageKey, JSON.stringify(state.account));
```
**Hvad der sker her:**
- **Konverterer** kontoobjektet til en JSON-streng for lagring
- **Gemmer** data ved hjælp af vores konsistente lagringsnøgle
- **Udføres** automatisk, når der sker ændringer i tilstanden
- **Sikrer** at lagrede data altid er synkroniseret med den aktuelle tilstand
**Gennemgang af hvad der sker her:**
- **Konverterer** kontobjektet til en JSON-streng til lagring
- **Gemmer** data ved at bruge vores konsistente lagringsnøgle
- **Eksekverer** automatisk, når der sker ændringer i tilstanden
- **Sikrer** at gemte data altid er synkroniseret med den aktuelle tilstand
> 💡 **Arkitektonisk fordel**: Fordi vi centraliserede alle tilstandsopdateringer gennem `updateState()`, krævede det kun én linje kode at tilføje persistens. Dette viser styrken ved gode arkitektoniske beslutninger!
> 💡 **Arkitekturfordel**: Fordi vi centraliserede alle tilstandsopdateringer via `updateState()`, krævede tilføjelse af vedvarende lagring kun én kode linje. Dette demonstrerer styrken i gode arkitekturvalg!
**Trin 3: Gendan tilstand ved app-opstart**
**Trin 3: Gendan tilstand ved app-load**
Opret en initialiseringsfunktion til at gendanne gemte data:
@ -344,7 +518,7 @@ function init() {
updateState('account', JSON.parse(savedAccount));
}
// Our previous initialization code
// Vores tidligere initialiseringskode
window.onpopstate = () => updateRoute();
updateRoute();
}
@ -353,47 +527,61 @@ init();
```
**Forståelse af initialiseringsprocessen:**
- **Henter** eventuelle tidligere gemte kontodata fra localStorage
- **Henter** tidligere gemte kontodata fra localStorage
- **Parser** JSON-strengen tilbage til et JavaScript-objekt
- **Opdaterer** tilstanden ved hjælp af vores kontrollerede opdateringsfunktion
- **Opdaterer** tilstanden via vores kontrollerede opdateringsfunktion
- **Gendanner** brugerens session automatisk ved sideindlæsning
- **Udføres** før ruteopdateringer for at sikre, at tilstanden er tilgængelig
- **Eksekverer** før ruteopdateringer for at sikre at tilstanden er tilgængelig
**Trin 4: Optimer standardruten**
Opdater standardruten for at udnytte persistens:
Opdater standardruten for at udnytte vedvarende lager:
I `updateRoute()`, erstat:
```js
// Replace: return navigate('/login');
// Erstat: returner naviger('/login');
return navigate('/dashboard');
```
**Hvorfor denne ændring giver mening:**
- **Udnytter** vores nye persistenssystem effektivt
- **Tillader** dashboardet at håndtere autentificeringskontroller
- **Omdirigerer** automatisk til login, hvis der ikke findes en gemt session
- **Skaber** en mere problemfri brugeroplevelse
- **Udnytter** vores nye vedvarende system effektivt
- **Tillader** dashboardet at håndtere autentificeringskontrol
- **Omdirigerer** automatisk til login, hvis der ikke findes nogen gemt session
- **Skaber** en mere sammenhængende brugeroplevelse
**Test din implementering:**
**Test af din implementering:**
1. Log ind på din bankapp
2. Opdater browserens side
3. Bekræft, at du forbliver logget ind og på dashboardet
2. Opdater browser-siden
3. Bekræft at du stadig er logget ind og på dashboardet
4. Luk og genåbn din browser
5. Naviger tilbage til din app og bekræft, at du stadig er logget ind
5. Naviger tilbage til din app og bekræft at du stadig er logget ind
🎉 **Præstation opnået**: Du har med succes implementeret vedvarende tilstandsadministration! Din app opfører sig nu som en professionel webapplikation.
🎉 **Mål opnået**: Du har med succes implementeret persistent tilstandsstyring! Din app opfører sig nu som en professionel webapplikation.
### 🎯 Pædagogisk status: Arkitektur for persistence
## Balancering af persistens med dataaktualitet
**Arkitekturforståelse**: Du har implementeret et sofistikeret persistence-lag, som balancerer brugeroplevelse med kompleksitet i datastyring.
Vores persistenssystem opretholder brugersessioner, men introducerer en ny udfordring: dataforældelse. Når flere brugere eller applikationer ændrer de samme serverdata, bliver lokal cache-information forældet.
**Nøglekoncepter mestret**:
- **JSON-serialisering**: Konvertering af komplekse objekter til lagringsvenlige strenge
- **Automatisk synkronisering**: Tilstandsændringer udløser vedvarende lagring
- **Sessionsgendannelse**: Apps kan genskabe brugerens kontekst efter afbrydelser
- **Centraliseret persistence**: Én opdateringsfunktion håndterer al lagring
Denne situation minder om viking-navigatører, der både stolede på gemte stjernekort og aktuelle observationer af himmellegemer. Kortene gav konsistens, men navigatørerne havde brug for friske observationer for at tage højde for ændrede forhold. På samme måde har vores applikation brug for både persistent brugertilstand og aktuelle serverdata.
**Branchens forbindelse**: Dette persistence-mønster er grundlæggende i Progressive Web Apps (PWA), offline-first applikationer og moderne mobile weboplevelser. Du bygger funktionsdygtigheder på produktionsniveau.
**🧪 Opdagelse af problemet med dataaktualitet:**
**Refleksionsspørgsmål**: Hvordan ville du ændre dette system for at håndtere flere brugerkonti på samme enhed? Overvej privatlivs- og sikkerhedsmæssige konsekvenser.
1. Log ind på dashboardet med kontoen `test`
## Balancering af persistence med datafriskhed
Vores persistence-system opretholder med succes bruger-sessioner, men introducerer en ny udfordring: forældet data. Når flere brugere eller applikationer ændrer samme serverdata, bliver lokalt cachelagret information forældet.
Denne situation minder om vikingernes navigatører, som stolede både på lagrede stjernekort og aktuelle himmelske observationer. Kortene gav konsistens, men navigatørerne havde brug for nye observationer for at tage højde for ændrede forhold. På samme måde har vores applikation både brug for vedvarende bruger-tilstand og aktuelle serverdata.
**🧪 Opdagelse af problemet med datafriskhed:**
1. Log ind på dashboardet med `test` kontoen
2. Kør denne kommando i en terminal for at simulere en transaktion fra en anden kilde:
```sh
@ -404,28 +592,44 @@ curl --request POST \
```
3. Opdater din dashboard-side i browseren
4. Observer, om du ser den nye transaktion
4. Observer om du ser den nye transaktion
**Hvad denne test viser:**
- **Viser** hvordan localStorage kan blive "forældet" (udateret)
- **Simulerer** virkelige scenarier, hvor dataændringer sker uden for din app
- **Afslører** spændingen mellem persistens og dataaktualitet
**Hvad denne test demonstrerer:**
- **Viser** hvordan lokal lagring kan blive "forældet" (udateret)
- **Simulerer** virkelige scenarier, hvor data ændres uden for din app
- **Afslører** spændingen mellem persistence og datafriskhed
**Udfordringen med dataforældelse:**
**Udfordringen ved datagammelhed:**
| Problem | Årsag | Brugerpåvirkning |
|---------|-------|------------------|
| **Forældede data** | localStorage udløber aldrig automatisk | Brugere ser uaktuelle oplysninger |
| **Serverændringer** | Andre apps/brugere ændrer de samme data | Inkonsistente visninger på tværs af platforme |
| **Cache vs. virkelighed** | Lokal cache matcher ikke servertilstanden | Dårlig brugeroplevelse og forvirring |
| **Forældet data** | localStorage udløber aldrig automatisk | Brugere ser forældet information |
| **Serverændringer** | Andre apps/brugere ændrer samme data | Uoverensstemmende visninger på tværs af platforme |
| **Cache vs. virkelighed** | Lokal cache matcher ikke servertilstand | Dårlig brugeroplevelse og forvirring |
**Løsningsstrategi:**
Vi implementerer et "opdater ved indlæsning"-mønster, der balancerer fordelene ved persistens med behovet for friske data. Denne tilgang opretholder den glatte brugeroplevelse, samtidig med at den sikrer dataens nøjagtighed.
### Opgave: Implementer dataopdateringssystem
Vi implementerer et "opdater ved indlæsning" mønster, som balancerer fordelene ved persistence med behovet for frisk data. Denne tilgang bevarer en glidende brugeroplevelse samtidig med at sikre datanøjagtighed.
```mermaid
sequenceDiagram
participant U as Bruger
participant A as App
participant L as localStorage
participant S as Server
U->>A: Åbner app
A->>L: Indlæs gemt tilstand
L-->>A: Returner cachede data
A->>U: Vis UI med det samme
A->>S: Hent frisk data
S-->>A: Returner aktuelle data
A->>L: Opdater cache
A->>U: Opdater UI med frisk data
```
### Opgave: Implementer datafriskheds-system
Vi opretter et system, der automatisk henter friske data fra serveren, mens vi opretholder fordelene ved vores persistente tilstandsstyring.
Vi laver et system, som automatisk henter frisk data fra serveren, mens vi bevarer fordelene ved vores vedvarende tilstandsadministration.
**Trin 1: Opret konto-dataopdaterer**
@ -446,14 +650,14 @@ async function updateAccountData() {
```
**Forståelse af denne funktions logik:**
- **Kontrollerer** om en bruger i øjeblikket er logget ind (state.account eksisterer)
- **Omdirigerer** til logout, hvis der ikke findes en gyldig session
- **Henter** friske kontodata fra serveren ved hjælp af den eksisterende `getAccount()`-funktion
- **Håndterer** serverfejl elegant ved at logge ud ugyldige sessioner
- **Opdaterer** tilstanden med friske data ved hjælp af vores kontrollerede opdateringssystem
- **Udløser** automatisk localStorage-persistens gennem funktionen `updateState()`
- **Tjekker** om en bruger er logget ind (state.account findes)
- **Omdirigerer** til logout, hvis ingen gyldig session findes
- **Henter** frisk kontodata fra serveren ved brug af eksisterende `getAccount()` funktion
- **Håndterer** serverfejl yndefuldt ved at logge ud ugyldige sessioner
- **Opdaterer** tilstanden med frisk data via vores kontrollerede opdateringssystem
- **Udvikler** automatisk localStorage-persistence via `updateState()` funktionen
**Trin 2: Opret dashboard-opdateringshåndtering**
**Trin 2: Opret dashboard-opdateringshandler**
```js
async function refresh() {
@ -463,14 +667,14 @@ async function refresh() {
```
**Hvad denne opdateringsfunktion opnår:**
- **Koordinerer** dataopdatering og UI-opdateringsprocessen
- **Venter**, at friske data indlæses, før displayet opdateres
- **Sikrer** at dashboardet viser de mest aktuelle oplysninger
- **Opretholder** en ren adskillelse mellem datastyring og UI-opdateringer
- **Koordinerer** dataopdaterings- og UI-opdateringsprocessen
- **Venter** at frisk data er hentet, før display opdateres
- **Sikrer** at dashboardet viser den mest aktuelle information
- **Opretholder** ren adskillelse mellem datastyring og UI-opdateringer
**Trin 3: Integrer med rutesystemet**
Opdater din rutekonfiguration til automatisk at udløse opdatering:
Opdater din rutekonfiguration for automatisk at udløse opdatering:
```js
const routes = {
@ -479,55 +683,110 @@ const routes = {
};
```
**Hvordan denne integration fungerer:**
- **Udfører** opdateringsfunktionen hver gang dashboard-ruten indlæses
- **Sikrer** at friske data altid vises, når brugere navigerer til dashboardet
- **Opretholder** den eksisterende rutestruktur, mens dataaktualitet tilføjes
- **Tilbyder** et konsistent mønster for rutespecifik initialisering
**Sådan virker denne integration:**
- **Kører** opdateringsfunktionen hver gang dashboard-ruten indlæses
- **Sikrer** at frisk data altid vises, når brugere navigerer til dashboardet
- **Bevarer** eksisterende rutestruktur mens der tilføjes datafriskhed
- **Leverer** et konsistent mønster for rutespecifik initialisering
**Test dit dataopdateringssystem:**
**Test dit datafriskhedssystem:**
1. Log ind på din bankapp
2. Kør curl-kommandoen fra tidligere for at oprette en ny transaktion
3. Opdater din dashboard-side eller naviger væk og tilbage
4. Bekræft, at den nye transaktion vises med det samme
4. Bekræft at den nye transaktion vises straks
🎉 **Perfekt balance opnået**: Din app kombinerer nu en glidende oplevelse med vedvarende tilstand og nøjagtigheden af frisk serverdata!
## 📈 Din tidslinje for tilstandsadministration-mesterskab
```mermaid
timeline
title Professionel State Management Rejse
section Problemerkenkendelse
State Issues Diagnosis
: Identificer sessionstab problemer
: Forstå spredte opdateringsproblemer
: Genkend arkitektoniske behov
section Arkitekturgrundlag
Centralized State Design
: Skab enhedsstat objekter
: Implementer kontrollerede opdateringsmønstre
: Etabler uforanderlige principper
Forudsigelige Opdateringer
: Mestre Object.freeze() brug
: Byg debug-venlige systemer
: Skab skalerbare mønstre
section Vedholdenhedsekspertise
localStorage Integration
: Håndter JSON-serialisering
: Implementer automatisk synkronisering
: Skab sessionskontinuitet
Datagenshedsbalance
: Håndter forældelsesudfordringer
: Byg opfriskningsmekanismer
: Optimer ydeevne vs nøjagtighed
section Professionelle Mønstre
Produktionsklare Systemer
: Implementer fejlhåndtering
: Skab vedligeholdelige arkitekturer
: Følg branchens bedste praksis
Avancerede Kapaciteter
: Klar til rammeværksintegration
: Forberedt på komplekse state behov
: Fundament for realtidsfunktioner
```
**🎓 Eksamenstrin**: Du har med succes bygget et komplet tilstandsadministrationssystem ved hjælp af de samme principper, der driver Redux, Vuex og andre professionelle tilstandsbiblioteker. Disse mønstre skalerer fra simple apps til virksomhedsapplikationer.
🎉 **Perfekt balance opnået**: Din app kombinerer nu den glatte oplevelse af persistent tilstand med nøjagtigheden af friske serverdata!
**🔄 Næste niveau færdigheder**:
- Klar til at mestre tilstandsadministrationsrammer (Redux, Zustand, Pinia)
- Forberedt på at implementere realtidsfunktioner med WebSockets
- Udstyret til at bygge offline-først Progressive Web Apps
- Grundlag lagt for avancerede mønstre som tilstandsmaskiner og observatører
## GitHub Copilot Agent-udfordring 🚀
Brug Agent-tilstand til at fuldføre følgende udfordring:
Brug Agent-tilstand for at løse følgende udfordring:
**Beskrivelse:** Implementer et omfattende tilstandsstyringssystem med fortryd/gendan-funktionalitet til bankappen. Denne udfordring vil hjælpe dig med at øve avancerede tilstandsstyringskoncepter, herunder historiksporing af tilstand, uforanderlige opdateringer og synkronisering af brugergrænsefladen.
**Beskrivelse:** Implementer et omfattende tilstandsadministrationssystem med fortryd/annuller-funktionalitet til bankappen. Denne udfordring hjælper dig med at øve avancerede tilstandsadministrationskoncepter, herunder tilstandshistoriksporing, immutabel opdatering og synkronisering af brugergrænsefladen.
**Prompt:** Opret et forbedret tilstandsstyringssystem, der inkluderer: 1) En historik-array, der sporer alle tidligere tilstande, 2) Fortryd og gendan funktioner, der kan vende tilbage til tidligere tilstande, 3) UI-knapper til fortryd/gendan operationer på dashboardet, 4) En maksimal historikgrænse på 10 tilstande for at forhindre hukommelsesproblemer, og 5) Korrekt oprydning af historik, når brugeren logger ud. Sørg for, at fortryd/gendan-funktionaliteten fungerer med ændringer i kontosaldo og vedvarer på tværs af browseropdateringer.
**Prompt:** Skab et forbedret tilstandsadministrationssystem, der inkluderer: 1) Et tilstandshistorik-array, som sporer alle tidligere tilstande, 2) Fortryd- og annuller-funktioner, der kan gendanne tidligere tilstande, 3) UI-knapper til fortryd/annuller-operationer på dashboardet, 4) En maksimum historikgrænse på 10 tilstande for at forhindre hukommelsesproblemer, og 5) Korrekt oprydning af historik, når brugeren logger ud. Sørg for, at fortryd/annuller funktionaliteten fungerer med kontoens saldoændringer og vedvarer på tværs af sides opdateringer.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## 🚀 Udfordring: Lagringsoptimering
## 🚀 Udfordring: Optimering af lagring
Din implementering håndterer nu brugersessioner, dataopdatering og tilstandsstyring effektivt. Overvej dog, om vores nuværende tilgang optimalt balancerer lagringseffektivitet med funktionalitet.
Din implementering håndterer nu effektivt brugersessioner, dataopdatering og tilstandsadministration. Overvej dog, om vores nuværende tilgang optimalt balancerer lagringseffektivitet med funktionalitet.
Ligesom skakmestre, der skelner mellem essentielle brikker og ofre, kræver effektiv tilstandsstyring identifikation af, hvilke data der skal bevares, og hvilke der altid skal hentes friske fra serveren.
Ligesom skakmestre, der skelner mellem essentielle brikker og ofrende bønder, kræver effektiv tilstandsadministration, at man identificerer, hvilke data der skal vedvare, versus hvilke der altid bør være friske fra serveren.
**Optimeringsanalyse:**
Evaluer din nuværende localStorage-implementering og overvej disse strategiske spørgsmål:
- Hvad er det minimale information, der kræves for at opretholde brugerautentifikation?
- Hvilke data ændrer sig ofte nok til, at lokal caching giver lille fordel?
- Hvordan kan lagringsoptimering forbedre ydeevnen uden at forringe brugeroplevelsen?
- Hvad er de minimale oplysninger, der kræves for at opretholde brugerautentificering?
- Hvilke data ændrer sig hyppigt nok til, at lokal caching har lille fordel?
- Hvordan kan optimering af lagring forbedre ydeevnen uden at forringe brugeroplevelsen?
Denne type arkitekturanalyse adskiller erfarne udviklere, som betragter både funktionalitet og effektivitet i deres løsninger.
**Implementeringsstrategi:**
- **Identificer** de essentielle data, der skal bevares (sandsynligvis kun brugeridentifikation)
- **Modificer** din localStorage-implementering til kun at gemme kritiske sessionsdata
- **Sørg for** at friske data altid hentes fra serveren ved dashboardbesøg
- **Test** at din optimerede tilgang opretholder den samme brugeroplevelse
- **Identificer** de essentielle data, som skal vedvare (sandsynligvis kun brugeridentifikation)
- **Ændr** din localStorage-implementering til kun at gemme kritiske sessionsdata
- **Sørg for** at frisk data altid hentes fra serveren ved dashboardbesøg
- **Test** at din optimerede tilgang opretholder samme brugeroplevelse
**Avanceret overvejelse:**
- **Sammenlign** kompromiserne mellem at gemme fulde kontodata vs. kun autentifikationstokens
- **Dokumenter** dine beslutninger og begrundelser for fremtidige teammedlemmer
- **Sammenlign** fordele og ulemper ved at gemme fulde kontodata versus kun autentificeringstokens
- **Dokumenter** dine beslutninger og begrundelser til fremtidige teammedlemmer
Denne udfordring vil hjælpe dig med at tænke som en professionel udvikler, der overvejer både brugeroplevelse og applikationseffektivitet. Tag dig tid til at eksperimentere med forskellige tilgange!
Denne udfordring hjælper dig med at tænke som en professionel udvikler, som både tager hensyn til brugeroplevelse og applikationseffektivitet. Tag dig tid til at eksperimentere med forskellige tilgange!
## Quiz efter forelæsning
@ -535,13 +794,15 @@ Denne udfordring vil hjælpe dig med at tænke som en professionel udvikler, der
## Opgave
[Implementer "Tilføj transaktion"-dialog](assignment.md)
[Implementer dialogen "Tilføj transaktion"](assignment.md)
Her er et eksempelresultat efter at have fuldført opgaven:
Her er et eksempel på resultat efter opgaven er gennemført:
![Skærmbillede, der viser et eksempel på "Tilføj transaktion"-dialog](../../../../translated_images/dialog.93bba104afeb79f12f65ebf8f521c5d64e179c40b791c49c242cf15f7e7fab15.da.png)
![Screenshot viser et eksempel på dialogen "Tilføj transaktion"](../../../../translated_images/dialog.93bba104afeb79f1.da.png)
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiske oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -112,7 +112,7 @@ Se [server-API-dokumentationen](../api/README.md) for:
**Forventet resultat:**
Efter at have fuldført denne opgave, bør din bankapp have en fuldt funktionel "Tilføj Transaktion"-funktion, der ser professionel ud og fungerer perfekt:
![Skærmbillede, der viser et eksempel på "Tilføj transaktion"-dialog](../../../../translated_images/dialog.93bba104afeb79f12f65ebf8f521c5d64e179c40b791c49c242cf15f7e7fab15.da.png)
![Skærmbillede, der viser et eksempel på "Tilføj transaktion"-dialog](../../../../translated_images/dialog.93bba104afeb79f1.da.png)
## Test af din implementering

@ -11,7 +11,7 @@ CO_OP_TRANSLATOR_METADATA:
I dette projekt lærer du, hvordan du bygger en fiktiv bank. Disse lektioner inkluderer instruktioner om, hvordan du opbygger en webapp med layout og ruter, bygger formularer, håndterer tilstand og henter data fra en API, hvorfra du kan hente bankens data.
| ![Screen1](../../../translated_images/screen1.baccbba0f1f93364672eb250d2fbd21574bb1caf79a2155022dc098a741cbdfe.da.png) | ![Screen2](../../../translated_images/screen2.123c82a831a1d14ab2061994be2fa5de9cec1ce651047217d326d4773a6348e4.da.png) |
| ![Screen1](../../../translated_images/screen1.baccbba0f1f93364.da.png) | ![Screen2](../../../translated_images/screen2.123c82a831a1d14a.da.png) |
|--------------------------------|--------------------------------|
## Lektioner

@ -1,385 +1,639 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "cfd4a15974168ca426d50c67682ab9d4",
"translation_date": "2025-10-23T21:54:24+00:00",
"original_hash": "a9a3bcc037a447e2d8994d99e871cd9f",
"translation_date": "2026-01-06T23:34:50+00:00",
"source_file": "8-code-editor/1-using-a-code-editor/README.md",
"language_code": "da"
}
-->
# Brug af en kodeeditor: Mestering af VSCode.dev
# Brug af en kodeeditor: Mestring af VSCode.dev
Kan du huske i *The Matrix*, da Neo skulle tilslutte sig en enorm computerterminal for at få adgang til den digitale verden? Nutidens webudviklingsværktøjer er det stik modsatte utroligt kraftfulde funktioner tilgængelige hvor som helst. VSCode.dev er en browserbaseret kodeeditor, der bringer professionelle udviklingsværktøjer til enhver enhed med internetforbindelse.
Kan du huske i *The Matrix*, da Neo skulle tilslutte sig en kæmpe computerterminal for at få adgang til den digitale verden? Nutidens webudviklingsværktøjer er det modsatte utroligt kraftfulde muligheder, der er tilgængelige fra hvor som helst. VSCode.dev er en browserbaseret kodeeditor, der bringer professionelle udviklingsværktøjer til enhver enhed med internetforbindelse.
Ligesom trykpressen gjorde bøger tilgængelige for alle, ikke kun munke i klostre, demokratiserer VSCode.dev kodning. Du kan arbejde på projekter fra en bibliotekskomputer, et skolelaboratorium eller hvor som helst, du har adgang til en browser. Ingen installationer, ingen "jeg har brug for min specifikke opsætning"-begrænsninger.
Ligesom bogtrykkerkunsten gjorde bøger tilgængelige for alle, ikke kun skrivere i klostre, demokratiserer VSCode.dev kodning. Du kan arbejde på projekter fra en bibliotekscomputer, et skollaboratorium eller hvor som helst, du har browseradgang. Ingen installationer, ingen "jeg har brug for min specifikke opsætning"-begrænsninger.
Ved slutningen af denne lektion vil du forstå, hvordan du navigerer i VSCode.dev, åbner GitHub-repositorier direkte i din browser og bruger Git til versionskontrol færdigheder, som professionelle udviklere stoler på dagligt.
Ved slutningen af denne lektion vil du forstå, hvordan du navigerer i VSCode.dev, åbner GitHub-repositorier direkte i din browser og bruger Git til versionskontrol alle færdigheder, som professionelle udviklere dagligt er afhængige af.
## ⚡ Hvad du kan gøre på de næste 5 minutter
**Hurtigstart for travle udviklere**
```mermaid
flowchart LR
A[⚡ 5 minutter] --> B[Besøg vscode.dev]
B --> C[Forbind GitHub-konto]
C --> D[Åbn et hvilket som helst repository]
D --> E[Begynd at redigere med det samme]
```
- **Minut 1**: Gå til [vscode.dev](https://vscode.dev) - ingen installation nødvendig
- **Minut 2**: Log ind med GitHub for at forbinde dine repositories
- **Minut 3**: Prøv URL-tricket: skift `github.com` til `vscode.dev/github` i enhver repo-URL
- **Minut 4**: Opret en ny fil og se, hvordan syntaksfremhævning virker automatisk
- **Minut 5**: Foretag en ændring og commit den via panelet Kildekontrol
**Hurtig test-URL**:
```
# Transform this:
github.com/microsoft/Web-Dev-For-Beginners
# Into this:
vscode.dev/github/microsoft/Web-Dev-For-Beginners
```
**Hvorfor det er vigtigt**: På 5 minutter vil du opleve friheden ved at kode overalt med professionelle værktøjer. Dette repræsenterer fremtidens udvikling tilgængelig, kraftfuld og øjeblikkelig.
## 🗺️ Din læringsrejse gennem cloud-baseret udvikling
```mermaid
journey
title Fra lokal opsætning til cloud-udviklingsmestring
section Forstå platformen
Oplev webbaseret redigering: 4: You
Forbind til GitHub-økosystemet: 6: You
Mestre navigering i grænsefladen: 7: You
section Færdigheder i filhåndtering
Opret og organiser filer: 5: You
Rediger med syntaksfremhævning: 7: You
Naviger i projektstrukturer: 8: You
section Mestring af versionsstyring
Forstå Git-integration: 6: You
Øv commit-arbejdsgange: 8: You
Mestre samarbejdsmønstre: 9: You
section Professionel tilpasning
Installer kraftfulde udvidelser: 7: You
Konfigurer udviklingsmiljø: 8: You
Byg personlige arbejdsgange: 9: You
```
**Dit destinationsmål**: Ved slutningen af denne lektion vil du have mestret et professionelt cloud-udviklingsmiljø, der fungerer fra enhver enhed, så du kan kode med de samme værktøjer, som udviklere i store tech-virksomheder bruger.
## Hvad du vil lære
Når vi har gennemgået dette sammen, vil du kunne:
Efter at vi har gennemgået dette sammen, vil du kunne:
- Navigere i VSCode.dev som om det var dit andet hjem finde alt, hvad du har brug for, uden at fare vild
- Åbne ethvert GitHub-repository i din browser og begynde at redigere med det samme (det her er ret magisk!)
- Bruge Git til at spore dine ændringer og gemme dine fremskridt som en professionel
- Forbedre din editor med udvidelser, der gør kodning hurtigere og sjovere
- Navigere i VSCode.dev som var det dit andet hjem finde alt, du har brug for uden at gå tabt
- Åbne ethvert GitHub-repositorium i din browser og begynde at redigere med det samme (det her er ret magisk!)
- Bruge Git til at spore dine ændringer og gemme dit fremskridt som en professionel
- Boost din editor med extensions, der gør kodning hurtigere og sjovere
- Oprette og organisere projektfiler med selvtillid
## Hvad du skal bruge
Kravene er enkle:
Kravene er ligetil:
- En gratis [GitHub-konto](https://github.com) (vi guider dig gennem oprettelsen, hvis nødvendigt)
- Grundlæggende kendskab til webbrowsere
- GitHub Basics-lektionen giver nyttig baggrund, men er ikke nødvendig
> 💡 **Ny til GitHub?** Oprettelse af en konto er gratis og tager minutter. Ligesom et bibliotekskort giver dig adgang til bøger verden over, åbner en GitHub-konto dørene til kode repositories på internettet.
- En gratis [GitHub-konto](https://github.com) (vi guider dig gennem oprettelsen, hvis det er nødvendigt)
- Grundlæggende kendskab til webbrowser
- Lektionen "GitHub Basics" giver nyttig baggrund, selvom det ikke er essentielt
## 🧠 Overblik over cloud-udviklingsøkosystemet
> 💡 **Ny på GitHub?** Det er gratis at oprette en konto og tager kun få minutter. Ligesom et lånerkort til biblioteket giver dig adgang til bøger verden over, åbner en GitHub-konto dørene til kode-repositorier på internettet.
```mermaid
mindmap
root((VSCode.dev Mastery))
Platform Fordele
Tilgængelighed
Enheds-uafhængighed
Ingen Installation Krævet
Øjeblikkelige Opdateringer
Universel Adgang
Integration
GitHub Forbindelse
Repository Synkronisering
Indstillinger Vedvarende
Samarbejde Klar
Udviklingsworkflow
Filhåndtering
Projektstruktur
Syntaks Fremhævning
Multi-fane Redigering
Autosave Funktioner
Versionskontrol
Git Integration
Commit Workflows
Grenstyring
Ændringssporing
Tilpasningskraft
Udvidelses Økosystem
Produktivitetsværktøjer
Sprogunderstøttelse
Tema Muligheder
Tilpassede Genveje
Miljøopsætning
Personlige Præferencer
Arbejdsområde Konfiguration
Værktøjsintegration
Workflow Optimering
Professionelle Færdigheder
Industri Standarder
Versionskontrol
Kodekvalitet
Samarbejde
Dokumentation
Karriereparathed
Fjernarbejde
Cloud Udvikling
Team Projekter
Open Source
```
**Kerneprincip**: Cloud-baserede udviklingsmiljøer repræsenterer fremtiden for kodning de tilbyder professionelle værktøjer, der er tilgængelige, samarbejdsorienterede og platformuafhængige.
## Hvorfor webbaserede kodeeditorer er vigtige
Før internettet kunne forskere på forskellige universiteter ikke nemt dele forskning. Så kom ARPANET i 1960'erne, som forbandt computere over afstande. Webbaserede kodeeditorer følger samme princip de gør kraftfulde værktøjer tilgængelige uanset din fysiske placering eller enhed.
Før internettet kunne forskere på forskellige universiteter ikke nemt dele forskning. Så kom ARPANET i 1960erne, som forbandt computere på tværs af afstande. Webbaserede kodeeditorer følger samme princip at gøre kraftfulde værktøjer tilgængelige uanset din fysiske placering eller enhed.
En kodeeditor fungerer som dit udviklingsarbejdsområde, hvor du skriver, redigerer og organiserer kodefiler. I modsætning til simple teksteditorer tilbyder professionelle kodeeditorer syntaksfremhævning, fejldetektion og projektstyringsfunktioner.
En kodeeditor fungerer som dit udviklingsarbejdsrum, hvor du skriver, redigerer og organiserer kodefiler. I modsætning til simple teksteditorer tilbyder professionelle kodeeditorer syntaksfremhævning, fejldetektion og projektstyringsfunktioner.
VSCode.dev bringer disse funktioner til din browser:
VSCode.dev bringer disse muligheder til din browser:
**Fordele ved webbaseret redigering:**
| Funktion | Beskrivelse | Praktisk fordel |
|----------|-------------|-----------------|
| **Platformuafhængighed** | Fungerer på enhver enhed med en browser | Arbejd problemfrit fra forskellige computere |
|---------|-------------|----------------|
| **Platform-uafhængighed** | Kører på enhver enhed med en browser | Arbejd problemfrit fra forskellige computere |
| **Ingen installation nødvendig** | Adgang via en web-URL | Omgå softwareinstallationsbegrænsninger |
| **Automatiske opdateringer** | Kører altid den nyeste version | Få adgang til nye funktioner uden manuelle opdateringer |
| **Automatiske opdateringer** | Kører altid den nyeste version | Få nye funktioner uden manuel opdatering |
| **Repository-integration** | Direkte forbindelse til GitHub | Rediger kode uden lokal filhåndtering |
**Praktiske implikationer:**
- Arbejd kontinuitet på tværs af forskellige miljøer
- Konsistent grænseflade uanset operativsystem
- Arbejdsfortsættelse på tværs af forskellige miljøer
- Konsistent interface uanset operativsystem
- Øjeblikkelige samarbejdsmuligheder
- Reducerede krav til lokal lagring
- Reduceret krav til lokal lagring
## Udforskning af VSCode.dev
Ligesom Marie Curies laboratorium indeholdt sofistikeret udstyr i et relativt enkelt rum, pakker VSCode.dev professionelle udviklingsværktøjer ind i en browsergrænseflade. Denne webapplikation tilbyder samme kernefunktionalitet som desktop-kodeeditorer.
Ligesom Marie Curies laboratorium indeholdt sofistikeret udstyr på et relativt enkelt sted, pakker VSCode.dev professionelle udviklingsværktøjer ind i en browsergrænseflade. Denne webapplikation tilbyder den samme kernefunktionalitet som desktop kodeeditorer.
Start med at navigere til [vscode.dev](https://vscode.dev) i din browser. Grænsefladen indlæses uden downloads eller systeminstallationer en direkte anvendelse af cloud computing-principper.
Begynd ved at gå til [vscode.dev](https://vscode.dev) i din browser. Interfacet indlæses uden downloads eller systeminstallationer en direkte anvendelse af cloud computing-principper.
### Tilslutning af din GitHub-konto
### Forbind din GitHub-konto
Ligesom Alexander Graham Bells telefon forbandt fjerne steder, forbinder tilknytning af din GitHub-konto VSCode.dev med dine kode-repositorier. Når du bliver bedt om at logge ind med GitHub, anbefales det at acceptere denne forbindelse.
Ligesom Alexander Graham Bells telefon forbandt fjerne steder, forbinder det at linke din GitHub-konto VSCode.dev med dine kode repositories. Når du bliver bedt om at logge på med GitHub, anbefales det at acceptere denne forbindelse.
**GitHub-integration giver:**
- Direkte adgang til dine repositorier inden for editoren
- Synkroniserede indstillinger og udvidelser på tværs af enheder
- Direkte adgang til dine repositories i editoren
- Synkroniserede indstillinger og extensions på tværs af enheder
- Strømlinet gemmearbejdsgang til GitHub
- Personlig udviklingsmiljø
- Personligt udviklingsmiljø
### Lær dit nye arbejdsområde at kende
Når alt er indlæst, vil du se et smukt rent arbejdsområde, der er designet til at holde dig fokuseret på det, der betyder noget din kode!
Når alt er indlæst, vil du se et smukt rent arbejdsområde designet til at holde dig fokuseret på det, der betyder noget din kode!
![Standard VSCode.dev-grænseflade](../../../../translated_images/default-vscode-dev.5d06881d65c1b3234ce50cd9ed3b0028e6031ad5f5b441bcbed96bfa6311f6d0.da.png)
![Default VSCode.dev interface](../../../../translated_images/default-vscode-dev.5d06881d65c1b323.da.png)
**Her er din rundtur i nabolaget:**
- **Aktivitetslinje** (den stribe til venstre): Din hovednavigation med Explorer 📁, Søg 🔍, Versionskontrol 🌿, Udvidelser 🧩 og Indstillinger ⚙️
- **Sidebar** (panelet ved siden af): Ændres for at vise dig relevant info baseret på, hvad du har valgt
- **Editorområde** (det store rum i midten): Her sker magien dit primære kodningsområde
**Her er din rundvisning i nabolaget:**
- **Aktivitetslinje** (den stribe til venstre): Din primære navigation med Explorer 📁, Søg 🔍, Kildekontrol 🌿, Extensions 🧩 og Indstillinger ⚙️
- **Sidebar** (panelet ved siden af): Skifter for at vise relevant info baseret på hvad du har valgt
- **Editorområde** (det store område i midten): Her sker magien dit primære kodningsområde
**Tag et øjeblik til at udforske:**
- Klik rundt på ikonerne i aktivitetslinjen og se, hvad hver enkelt gør
- Bemærk, hvordan sidepanelet opdateres for at vise forskellig information ret smart, ikke?
- Explorer-visningen (📁) er sandsynligvis der, hvor du vil bruge mest tid, så bliv komfortabel med den
- Klik på ikonerne i Aktivitetslinjen og se, hvad hver gør
- Bemærk hvordan sidepanelet opdateres til at vise forskellig information ret fedt, ikke?
- Explorer-visningen (📁) er nok hvor du vil bruge mest tid, så bliv godt tilpas med den
```mermaid
flowchart TB
subgraph "VSCode.dev Interface Arkitektur"
A[Aktivitetslinje] --> B[Explorer 📁]
A --> C[Søg 🔍]
A --> D[Kildekontrol 🌿]
A --> E[Udvidelser 🧩]
A --> F[Indstillinger ⚙️]
B --> G[Filtræ]
C --> H[Find & Erstat]
D --> I[Git Status]
E --> J[Udvidelsesmarked]
F --> K[Konfiguration]
L[Sidepanel] --> M[Kontekstpanel]
N[Editorområde] --> O[Kodefiler]
P[Terminal/Output] --> Q[Kommandolinje]
end
```
## Åbning af GitHub-repositorier
Før internettet var forskere nødt til fysisk at rejse til biblioteker for at få adgang til dokumenter. GitHub-repositorier fungerer på samme måde de er samlinger af kode, der er gemt eksternt. VSCode.dev eliminerer det traditionelle trin med at downloade repositorier til din lokale maskine, før du redigerer.
Før internettet skulle forskere fysisk rejse til biblioteker for at få adgang til dokumenter. GitHub-repositorier fungerer på samme måde de er samlinger af kode, der er lagret eksternt. VSCode.dev eliminerer det traditionelle trin med at downloade repositories til din lokale maskine før redigering.
Denne funktion gør det muligt at få øjeblikkelig adgang til ethvert offentligt repository for visning, redigering eller bidrag. Her er to metoder til at åbne repositorier:
Denne funktion muliggør øjeblikkelig adgang til ethvert offentligt repository til visning, redigering eller bidrag. Her er to metoder til at åbne repositories:
### Metode 1: Den klik-og-peg måde
### Metode 1: Peg-og-klik-metoden
Dette er perfekt, når du starter fra bunden i VSCode.dev og vil åbne et specifikt repository. Det er ligetil og brugervenligt:
Den er perfekt, når du starter frisk i VSCode.dev og vil åbne et specifikt repository. Den er ligetil og begyndervenlig:
**Sådan gør du:**
1. Gå til [vscode.dev](https://vscode.dev), hvis du ikke allerede er der
2. Find knappen "Open Remote Repository" på velkomstskærmen og klik på den
![Åbn fjernrepository](../../../../translated_images/open-remote-repository.bd9c2598b8949e7fc283cdfc8f4050c6205a7c7c6d3f78c4b135115d037d6fa2.da.png)
![Open remote repository](../../../../translated_images/open-remote-repository.bd9c2598b8949e7f.da.png)
3. Indsæt en hvilken som helst GitHub-repository-URL (prøv denne: `https://github.com/microsoft/Web-Dev-For-Beginners`)
4. Tryk på Enter og se magien ske!
3. Indsæt en hvilken som helst GitHub repository URL (prøv denne: `https://github.com/microsoft/Web-Dev-For-Beginners`)
4. Tryk Enter og se magien ske!
**Pro tip - Genvejen til Command Palette:**
**Pro tip - Genvej til Kommandopalletten:**
Vil du føle dig som en kodningsmester? Prøv denne tastaturgenvej: Ctrl+Shift+P (eller Cmd+Shift+P på Mac) for at åbne Command Palette:
Vil du føle dig som en kode-troldmand? Prøv denne tastaturgenvej: Ctrl+Shift+P (eller Cmd+Shift+P på Mac) for at åbne Kommandopalletten:
![Command Palette](../../../../translated_images/palette-menu.4946174e07f426226afcdad707d19b8d5150e41591c751c45b5dee213affef91.da.png)
![Command Palette](../../../../translated_images/palette-menu.4946174e07f42622.da.png)
**Command Palette er som en søgemaskine for alt, hvad du kan gøre:**
- Skriv "open remote", og den finder repository-åbneren for dig
- Den husker repositorier, du har åbnet for nylig (super praktisk!)
- Når du vænner dig til den, vil du føle, at du koder med lynets hast
- Det er i bund og grund VSCode.dev's version af "Hey Siri, men for kodning"
**Kommandopalletten er som en søgemaskine til alt, hvad du kan gøre:**
- Skriv "open remote" og den finder repository-åbneren for dig
- Den husker repositories, du har åbnet for nylig (super praktisk!)
- Når du bliver vant til den, vil du føle, du koder lynhurtigt
- Det er grundlæggende VSCode.dev's version af "Hey Siri, men til kodning"
### Metode 2: URL-modifikationsteknik
### Metode 2: URL-ændringsteknikken
Ligesom hvordan HTTP og HTTPS bruger forskellige protokoller, mens de bevarer samme domænestruktur, bruger VSCode.dev et URL-mønster, der afspejler GitHubs adresseringssystem. Enhver GitHub-repository-URL kan ændres til at åbne direkte i VSCode.dev.
Ligesom HTTP og HTTPS bruger forskellige protokoller, men bevarer samme domænestruktur, bruger VSCode.dev et URL-mønster, der spejler GitHubs adresseringssystem. Enhver GitHub-repository-URL kan ændres, så den åbnes direkte i VSCode.dev.
**URL-transformation mønster:**
**URL-omdannelsesmønster:**
| Repositorytype | GitHub URL | VSCode.dev URL |
|----------------|------------|----------------|
| Repository-type | GitHub-URL | VSCode.dev-URL |
|----------------|---------------------|----------------|
| **Offentligt repository** | `github.com/microsoft/Web-Dev-For-Beginners` | `vscode.dev/github/microsoft/Web-Dev-For-Beginners` |
| **Personligt projekt** | `github.com/dit-brugernavn/min-projekt` | `vscode.dev/github/dit-brugernavn/min-projekt` |
| **Ethvert tilgængeligt repo** | `github.com/deres-brugernavn/fantastisk-repo` | `vscode.dev/github/deres-brugernavn/fantastisk-repo` |
| **Personligt projekt** | `github.com/your-username/my-project` | `vscode.dev/github/your-username/my-project` |
| **Ethvert tilgængeligt repo** | `github.com/their-username/awesome-repo` | `vscode.dev/github/their-username/awesome-repo` |
**Implementering:**
- Erstat `github.com` med `vscode.dev/github`
- Bevar alle andre URL-komponenter uændret
- Fungerer med ethvert offentligt tilgængeligt repository
- Giver øjeblikkelig adgang til redigering
- Bevar alle øvrige URL-komponenter uændrede
- Virker med ethvert offentligt tilgængeligt repository
- Giver øjeblikkelig redigeringsadgang
> 💡 **Livsændrende tip**: Bogmærk VSCode.dev-versionerne af dine yndlingsrepositorier. Jeg har bogmærker som "Rediger min portefølje" og "Ret dokumentation", der tager mig direkte til redigeringstilstand!
> 💡 **Livsændrende tip**: Bogmærk VSCode.dev-versionerne af dine yndlingsrepositories. Jeg har bogmærker som "Edit My Portfolio" og "Fix Documentation", som tager mig direkte til redigeringstilstand!
**Hvilken metode skal du bruge?**
- **Grænseflademetoden**: God, når du udforsker eller ikke kan huske de præcise repository-navne
- **Interface-metoden**: God, når du udforsker eller ikke kan huske præcise repository-navne
- **URL-tricket**: Perfekt til lynhurtig adgang, når du ved præcis, hvor du skal hen
### 🎯 Pædagogisk check-in: Adgang til cloud-udvikling
**Pause og refleksion**: Du har netop lært to måder at få adgang til koderepositorier via en webbrowser. Det repræsenterer et fundamentalt skift i, hvordan udvikling fungerer.
**Hurtig selvevaluering**:
- Kan du forklare, hvorfor webbaseret redigering eliminerer den traditionelle "opsætning af udviklingsmiljø"?
- Hvilke fordele giver URL-ændringsteknikken i forhold til lokal git-kloning?
- Hvordan ændrer denne tilgang måden, du måske bidrager til open source-projekter på?
**Virkelighedsforbindelse**: Store virksomheder som GitHub, GitLab og Replit har bygget deres udviklingsplatforme omkring disse cloud-first-principper. Du lærer de samme arbejdsgange, som professionelle udviklingsteams verden over bruger.
**Udfordrende spørgsmål**: Hvordan kan cloud-baseret udvikling ændre måden, kodning undervises på i skoler? Overvej enhedskrav, softwarehåndtering og muligheder for samarbejde.
## Arbejde med filer og projekter
Nu hvor du har åbnet et repository, lad os begynde at bygge! VSCode.dev giver dig alt, hvad du behøver for at oprette, redigere og organisere dine kodefiler. Tænk på det som dit digitale værksted hvert værktøj er lige der, hvor du har brug for det.
Nu hvor du har et repository åbent, lad os begynde at bygge! VSCode.dev giver dig alt, hvad du behøver for at oprette, redigere og organisere dine kodefiler. Tænk på det som dit digitale værksted hvert værktøj er lige, hvor du har brug for det.
Lad os dykke ned i de daglige opgaver, der vil udgøre størstedelen af din kodningsarbejdsgang.
### Oprettelse af nye filer
Ligesom organisering af tegninger på en arkitekts kontor følger filoprettelse i VSCode.dev en struktureret tilgang. Systemet understøtter alle standard webudviklingsfiltyper.
Ligesom at organisere blåtryk på en arkitekts kontor følger filoprettelse i VSCode.dev en struktureret fremgangsmåde. Systemet understøtter alle standardfiltyper til webudvikling.
**Filoprettelsesproces:**
1. Naviger til den ønskede mappe i Explorer-sidepanelet
2. Hold musen over mappenavnet for at afsløre ikonet "Ny fil" (📄+)
3. Indtast filnavnet inklusive den passende filtype (`style.css`, `script.js`, `index.html`)
4. Tryk Enter for at oprette filen
1. Naviger til den ønskede mappe i Explorer sidepanelet
2. Hold musen over mappenavnet for at afsløre "Ny fil"-ikonet (📄+)
3. Indtast filnavnet inklusive den korrekte filendelse (`style.css`, `script.js`, `index.html`)
4. Tryk Enter for at oprette filen
![Oprettelse af en ny fil](../../../../translated_images/create-new-file.2814e609c2af9aeb6c6fd53156c503ac91c3d538f9cac63073b2dd4a7631f183.da.png)
![Creating a new file](../../../../translated_images/create-new-file.2814e609c2af9aeb.da.png)
**Navngivningskonventioner:**
- Brug beskrivende navne, der angiver filens formål
- Inkluder filtyper for korrekt syntaksfremhævning
- Følg konsistente navngivningsmønstre i hele projektet
- Inkluder filendelser for korrekt syntaksfremhævning
- Følg konsistente navngivningsmønstre på tværs af projekter
- Brug små bogstaver og bindestreger i stedet for mellemrum
### Redigering og gemning af filer
Her begynder det sjove! VSCode.dev's editor er fyldt med nyttige funktioner, der gør kodning glat og intuitiv. Det er som at have en virkelig smart skriveassistent, men for kode.
Her begynder det sjove! VSCode.devs editor er fyldt med hjælpsomme funktioner, der får kodning til at føles glat og intuitiv. Det er som at have en virkelig klog skriveassistent, men til kode.
**Din redigeringsarbejdsgang:**
1. Klik på en hvilken som helst fil i Explorer for at åbne den i hovedområdet
2. Begynd at skrive og se, hvordan VSCode.dev hjælper dig med farver, forslag og fejlspotting
3. Gem dit arbejde med Ctrl+S (Windows/Linux) eller Cmd+S (Mac) selvom det også gemmer automatisk!
1. Klik på en fil i Explorer for at åbne den i hovedområdet
2. Begynd at skrive og se, hvordan VSCode.dev hjælper dig med farver, forslag og fejlregistrering
3. Gem dit arbejde med Ctrl+S (Windows/Linux) eller Cmd+S (Mac) selvom det også autosaves!
![Redigering af filer i VSCode.dev](../../../../translated_images/edit-a-file.52c0ee665ef19f08119d62d63f395dfefddc0a4deb9268d73bfe791f52c5807a.da.png)
![Editing files in VSCode.dev](../../../../translated_images/edit-a-file.52c0ee665ef19f08.da.png)
**De seje ting, der sker, mens du koder:**
**De seje ting, der sker under kodning:**
- Din kode bliver smukt farvekodet, så den er nem at læse
- VSCode.dev foreslår fuldførelser, mens du skriver (som autokorrektur, men meget smartere)
- VSCode.dev foreslår færdiggørelser, mens du skriver (ligesom autokorrektion, men meget smartere)
- Den fanger tastefejl og fejl, før du overhovedet gemmer
- Du kan have flere filer åbne i faner, ligesom i en browser
- Alt gemmes automatisk i baggrunden
> ⚠️ **Hurtigt tip**: Selvom auto-save har din ryg, er det stadig en god vane at trykke på Ctrl+S eller Cmd+S. Det gemmer alt med det samme og aktiverer nogle ekstra nyttige funktioner som fejlkontrol.
> ⚠️ **Hurtigt tip**: Selvom autosave sørger for dit rygdækning, er det stadig god vane at trykke Ctrl+S eller Cmd+S. Det gemmer alt med det samme og udløser ekstra nyttige funktioner som fejltjek.
### Versionskontrol med Git
Ligesom arkæologer skaber detaljerede optegnelser over udgravningslag, sporer Git ændringer i din kode over tid. Dette system bevarer projektets historie og giver dig mulighed for at vende tilbage til tidligere versioner, når det er nødvendigt. VSCode.dev inkluderer integreret Git-funktionalitet.
Ligesom arkæologer laver detaljerede optegnelser over udgravningslag, sporer Git ændringer i din kode over tid. Systemet bevarer projektets historie og gør det muligt at rulle tilbage til tidligere versioner, når det er nødvendigt. VSCode.dev inkluderer integreret Git-funktionalitet.
**Source Control-grænseflade:**
**Kildekontrolinterface:**
1. Få adgang til Source Control-panelet via 🌿-ikonet i aktivitetslinjen
2. Ændrede filer vises i sektionen "Changes"
1. Åbn panelet Kildekontrol via 🌿-ikonet i Aktivitetslinjen
2. Ændrede filer vises i sektionen "Ændringer"
3. Farvekodning angiver ændringstyper: grøn for tilføjelser, rød for sletninger
![Visning af ændringer i Source Control](../../../../translated_images/working-tree.c58eec08e6335c79cc708c0c220c0b7fea61514bd3c7fb7471905a864aceac7c.da.png)
![Viewing changes in Source Control](../../../../translated_images/working-tree.c58eec08e6335c79.da.png)
**Gem dit arbejde (commit-arbejdsgang):**
```mermaid
flowchart TD
A[Make changes to files] --> B[View changes in Source Control]
B --> C[Stage changes by clicking +]
C --> D[Write descriptive commit message]
D --> E[Click checkmark to commit]
E --> F[Changes pushed to GitHub]
```
**Her er din trin-for-trin proces:**
- Klik på "+"-ikonet ved siden af de filer, du vil gemme (dette "stager" dem)
- Dobbelttjek, at du er tilfreds med alle dine staged ændringer
- Skriv en kort note, der forklarer, hvad du gjorde (dette er din "commit-besked")
- Klik på checkmark-knappen for at gemme alt til GitHub
- Hvis du fortryder noget, giver fortrydelsesikonet dig mulighed for at kassere ændringer
**Skriv gode commit-beskeder (det er lettere, end du tror!):**
- Beskriv bare, hvad du gjorde, som "Tilføj kontaktformular" eller "Ret ødelagt navigation"
- Hold det kort og præcist tænk tweet-længde, ikke essay
A[Foretag ændringer i filer] --> B[Se ændringer i Kildekontrol]
B --> C[Forbered ændringer ved at klikke på +]
C --> D[Skriv beskrivende commit-besked]
D --> E[Klik på fluebenet for at committe]
E --> F[Ændringer sendt til GitHub]
```
```mermaid
stateDiagram-v2
[*] --> Modified: Rediger filer
Modified --> Staged: Klik + for at stage
Staged --> Modified: Klik - for at unstaging
Staged --> Committed: Tilføj besked & commit
Committed --> [*]: Synkroniser til GitHub
state Committed {
[*] --> LocalCommit
LocalCommit --> RemotePush: Auto-synkronisering
}
```
**Her er din trin-for-trin proces:**
- Klik på "+"-ikonet ved siden af filer, du vil gemme (dette "stager" dem)
- Dobbelttjek, at du er tilfreds med alle dine iscenesatte ændringer
- Skriv en kort note, der forklarer, hvad du har gjort (det er din "commit-besked")
- Klik på flueben-knappen for at gemme alt til GitHub
- Hvis du ombestemmer dig, kan fortryd-ikonet lade dig kassere ændringer
**At skrive gode commit-beskeder (det er nemmere, end du tror!):**
- Beskriv blot, hvad du har gjort, som "Tilføj kontaktformular" eller "Ret ødelagt navigation"
- Hold det kort og godt tænk tweet-længde, ikke essay
- Start med handlingsord som "Tilføj", "Ret", "Opdater" eller "Fjern"
- **Gode eksempler**: "Tilføj responsiv navigationsmenu", "Ret mobil layoutproblemer", "Opdater farver for bedre tilgængelighed"
- **Gode eksempler**: "Tilføj responsivt navigationsmenu", "Ret mobil-layoutfejl", "Opdater farver for bedre tilgængelighed"
> 💡 **Hurtig navigationstip**: Brug hamburger-menuen (☰) øverst til venstre for at hoppe tilbage til dit GitHub-repository og se dine committed ændringer online. Det er som en portal mellem dit redigeringsmiljø og dit projekts hjem på GitHub!
> 💡 **Hurtig navigations-tip**: Brug hamburger-menuen (☰) øverst til venstre for hurtigt at springe tilbage til dit GitHub-repositorium og se dine committed ændringer online. Det er som en portal mellem dit redigeringsmiljø og dit projekts hjem på GitHub!
## Forbedring af funktionalitet med udvidelser
Ligesom en håndværkers værksted indeholder specialværktøjer til forskellige opgaver, kan VSCode.dev tilpasses med udvidelser, der tilføjer specifikke funktioner. Disse community-udviklede plugins adresserer almindelige udviklingsbehov som kodeformatering, live preview og forbedret Git-integration.
Ligesom en håndværkers værksted indeholder specialiserede værktøjer til forskellige opgaver, kan VSCode.dev tilpasses med udvidelser, der tilføjer specifikke funktioner. Disse plugins udviklet af fællesskabet løser almindelige udviklingsbehov som kodeformatering, live preview og forbedret Git-integration.
Udvidelsesmarkedet huser tusindvis af gratis værktøjer skabt af udviklere verden over. Hver udvidelse løser specifikke arbejdsgangsudfordringer, hvilket giver dig mulighed for at bygge et personligt udviklingsmiljø, der passer til dine specifikke behov og præferencer.
Udvidelsesmarkedspladsen rummer tusindvis af gratis værktøjer skabt af udviklere verden over. Hver udvidelse løser særlige workflow-udfordringer og giver dig mulighed for at bygge et personligt udviklingsmiljø, der passer til dine specifikke behov og præferencer.
```mermaid
mindmap
root((Udvidelsesøkosystem))
Grundlæggende Kategorier
Produktivitet
Live Server
Auto Rename Tag
Bracket Pair Colorizer
GitLens
Kode Kvalitet
Prettier
ESLint
Spell Checker
Error Lens
Sprog Understøttelse
HTML CSS Support
JavaScript ES6
Python Extension
Markdown Preview
Temaer & UI
Dark+ Modern
Material Icon Theme
Peacock
Rainbow Brackets
Oplevelsesmetoder
Populære Ranglister
Download Antal
Brugervurderinger
Seneste Opdateringer
Fællesskabsanmeldelser
Anbefalinger
Workspace Forslag
Sprog-baseret
Workflow-specifik
Team Standarder
```
### Find dine perfekte udvidelser
Udvidelsesmarkedet er virkelig godt organiseret, så du ikke farer vild, når du leder efter det, du har brug for. Det er designet til at hjælpe dig med at finde både specifikke værktøjer og spændende ting, du ikke engang vidste eksisterede!
Udvidelsesmarkedspladsen er virkelig godt organiseret, så du ikke mister overblikket, når du leder efter, hvad du har brug for. Den er designet til at hjælpe dig med at opdage både specifikke værktøjer og spændende ting, du måske ikke engang vidste eksisterede!
**Sådan kommer du til markedet:**
**Sådan kommer du til markedspladsen:**
1. Klik på Udvidelsesikonet (🧩) i aktivitetslinjen
2. Gennemse eller søg efter noget specifikt
3. Klik på alt, der ser interessant ud, for at lære mere om det
1. Klik på Udvidelses-ikonet (🧩) i Aktivitetslinjen
2. Gå på opdagelse eller søg efter noget specifikt
3. Klik på alt, der ser interessant ud for at lære mere om det
![Udvidelsesmarkedets grænseflade](../../../../translated_images/extensions.eca0e0c7f59a10b5c88be7fe24b3e32cca6b6058b35a49026c3a9d80b1813b7c.da.png)
![Extension marketplace interface](../../../../translated_images/extensions.eca0e0c7f59a10b5.da.png)
**Hvad du vil se derinde:**
**Det vil du se derinde:**
| Sektion | Hvad er der indeni | Hvorfor det er nyttigt |
|---------|---------------------|------------------------|
| **Installeret** | Udvidelser, du allerede har tilføjet | Dit personlige kodningsværktøjssæt |
| **Populær** | Favoritter blandt udviklere | Hvad de fleste udviklere sværger til |
| **Anbefalet** | Smarte forslag til dit projekt | VSCode.dev's nyttige anbefalinger |
| Sektion | Hvad den indeholder | Hvorfor det er nyttigt |
|----------|---------|----------|
| **Installerede** | Udvidelser du allerede har tilføjet | Dit personlige kodningsværktøjssæt |
| **Populære** | Favoritterne blandt brugerne | Hvad de fleste udviklere sværger til |
| **Anbefalede** | Smarte forslag til dit projekt | VSCode.devs hjælpsomme anbefalinger |
**Hvad gør browsing nemt:**
- Hver udvidelse viser bedømmelser, antal downloads og anmeldelser fra rigtige brugere
- Du får skærmbilleder og klare beskrivelser af, hvad hver enkelt gør
- Alt er tydeligt markeret med kompatibilitetsinformation
**Det, der gør browsing nemt:**
- Hver udvidelse viser vurderinger, antal downloads og ægte brugeranmeldelser
- Du får screenshots og klare beskrivelser af, hvad hver enkelt gør
- Alt er tydeligt markeret med kompatibilitetsinfo
- Lignende udvidelser foreslås, så du kan sammenligne muligheder
### Installation af udvidelser (Det er super nemt!)
### Installation af udvidelser (det er super nemt!)
At tilføje nye funktioner til din editor er lige så enkelt som at klikke på en knap. Udvidelser installeres på få sekunder og begynder at fungere med det samme ingen genstart, ingen ventetid.
At tilføje nye kræfter til din editor er så simpelt som at klikke på en knap. Udvidelser installeres på få sekunder og virker med det samme ingen genstart, ingen ventetid.
**Sådan gør du:**
1. Søg efter det, du har brug for (prøv at søge efter "live server" eller "prettier")
2. Klik på en, der ser interessant ud, for at se flere detaljer
3. Læs om, hvad den gør, og tjek bedømmelserne
1. Søg efter det, du vil have (prøv at søge på "live server" eller "prettier")
2. Klik på en, der ser god ud, for at se flere detaljer
3. Læs om, hvad den gør, og tjek vurderingerne
4. Tryk på den blå "Installér"-knap, og så er du færdig!
![Installing extensions](../../../../8-code-editor/images/install-extension.gif)
**Hvad sker der bag kulisserne:**
- Udvidelsen downloades og konfigureres automatisk
- Nye funktioner vises straks i din grænseflade
- Alt begynder at fungere med det samme (seriøst, det r så hurtigt!)
- Hvis du er logget ind, synkroniseres udvidelsen til alle dine enheder
**Hvad der sker bag kulisserne:**
- Udvidelsen downloader og sættes op automatisk
- Nye funktioner dukker straks op i din grænseflade
- Alt begynder at fungere med det samme (seriøst, det er så hurtigt!)
- Hvis du er logget ind, synkroniserer udvidelsen til alle dine enheder
**Nogle udvidelser, jeg vil anbefale at starte med:**
- **Live Server**: Se din hjemmeside opdatere i realtid, mens du koder (denne er magisk!)
- **Prettier**: Gør din kode automatisk ren og professionel
- **Auto Rename Tag**: Ændr én HTML-tag, og dens partner opdateres også
- **Live Server**: Se dit website opdatere i realtid, mens du koder (den er magisk!)
- **Prettier**: Gør din kode ren og professionel automatisk
- **Auto Rename Tag**: Ændr et HTML-tag, og dets partner opdateres også
- **Bracket Pair Colorizer**: Farvekoder dine parenteser, så du aldrig farer vild
- **GitLens**: Forbedrer dine Git-funktioner med masser af nyttige oplysninger
- **GitLens**: Forstærker dine Git-funktioner med masser af nyttige oplysninger
### Tilpasning af dine udvidelser
De fleste udvidelser har indstillinger, du kan justere, så de fungerer præcis, som du ønsker. Tænk på det som at justere sædet og spejlene i en bil alle har deres præferencer!
De fleste udvidelser kommer med indstillinger, som du kan justere, så de fungerer præcis, som du vil have det. Tænk på det som at justere sædet og spejlene i en bil alle har deres præferencer!
**Sådan justerer du udvidelsesindstillinger:**
**Sådan tilpasser du udvidelsesindstillinger:**
1. Find din installerede udvidelse i Udvidelsespanelet
2. Kig efter det lille tandhjulsikon (⚙️) ved siden af dens navn, og klik på det
3. Vælg "Udvidelsesindstillinger" fra dropdown-menuen
4. Juster indstillingerne, indtil de passer perfekt til din arbejdsgang
1. Find din installerede udvidelse i Udvidelses-panelet
2. Kig efter det lille tandhjulsikon (⚙️) ved siden af dens navn og klik på det
3. Vælg "Extension Settings" i dropdown-menuen
4. Juster ting, indtil det passer perfekt til dit workflow
![Customizing extension settings](../../../../translated_images/extension-settings.21c752ae4f4cdb78a867f140ccd0680e04619d0c44bb4afb26373e54b829d934.da.png)
![Customizing extension settings](../../../../translated_images/extension-settings.21c752ae4f4cdb78.da.png)
**Almindelige ting, du måske vil justere:**
- Hvordan din kode bliver formateret (tabs vs mellemrum, linjelængde osv.)
- Hvordan din kode formateres (tabs vs mellemrum, linjelængde osv.)
- Hvilke tastaturgenveje der udløser forskellige handlinger
- Hvilke filtyper udvidelsen skal fungere med
- Tænd/sluk specifikke funktioner for at holde tingene overskuelige
- Hvilke filtyper udvidelsen skal arbejde med
- Slå specifikke funktioner til eller fra for at holde tingene ryddelige
### Organisering af dine udvidelser
Når du opdager flere spændende udvidelser, vil du gerne holde din samling ryddelig og velfungerende. VSCode.dev gør det virkelig nemt at administrere.
Efterhånden som du opdager flere spændende udvidelser, vil du gerne holde din samling pæn og kørende problemfrit. VSCode.dev gør det virkelig nemt at administrere.
**Dine muligheder for udvidelsesadministration:**
| Hvad du kan gøre | Hvornår det er nyttigt | Pro Tip |
| Hvad du kan gøre | Hvornår det er nyttigt | Pro-tip |
|--------|---------|----------|
| **Deaktiver** | Teste om en udvidelse forårsager problemer | Bedre end at afinstallere, hvis du måske vil have den tilbage |
| **Deaktiver** | Når du tester, om en udvidelse skaber problemer | Bedre end at afinstallere, hvis du måske vil have den tilbage igen |
| **Afinstaller** | Fjerne udvidelser, du ikke har brug for | Holder dit miljø rent og hurtigt |
| **Opdater** | Få de nyeste funktioner og fejlrettelser | Sker normalt automatisk, men det er værd at tjekke |
**Sådan administrerer jeg mine udvidelser:**
- Hver tredje måned gennemgår jeg, hvad jeg har installeret, og fjerner alt, jeg ikke bruger
**Sådan plejer jeg at administrere udvidelser:**
- Hver par måneder går jeg igennem, hvad jeg har installeret og fjerner det, jeg ikke bruger
- Jeg holder udvidelser opdateret, så jeg får de nyeste forbedringer og sikkerhedsrettelser
- Hvis noget virker langsomt, deaktiverer jeg midlertidigt udvidelser for at se, om en af dem er årsagen
- Jeg læser opdateringsnoterne, når udvidelser får større opdateringer nogle gange er der spændende nye funktioner!
- Hvis noget virker langsomt, deaktiverer jeg midlertidigt udvidelser for at se, om en af dem er synderen
- Jeg læser opdateringsnoterne, når udvidelser får større opdateringer nogle gange er der fede nye funktioner!
> ⚠️ **Performance-tip**: Udvidelser er fantastiske, men for mange kan gøre systemet langsomt. Fokuser på dem, der virkelig gør dit liv nemmere, og vær ikke bange for at afinstallere dem, du aldrig bruger.
### 🎯 Pædagogisk status: Tilpasning af udviklingsmiljø
**Forståelse af arkitektur**: Du har lært at tilpasse et professionelt udviklingsmiljø ved hjælp af udvidelser skabt af fællesskabet. Dette afspejler, hvordan store udviklingsteams bygger standardiserede værktøjskæder.
**Nøglebegreber mestret**:
- **Opdagelse af udvidelser**: Find værktøjer, der løser specifikke udviklingsudfordringer
- **Konfiguration af miljø**: Tilpas værktøjer til personlige eller teampræferencer
- **Optimering af ydeevne**: Balancering mellem funktionalitet og systemets ydeevne
- **Fællesskabssamarbejde**: Udnyttelse af værktøjer skabt af det globale udviklerfællesskab
**Industriel forbindelse**: Udvidelsesøkosystemer driver store udviklingsplatforme som VS Code, Chrome DevTools og moderne IDEer. At forstå, hvordan man vurderer, installerer og konfigurerer udvidelser, er essentielt for professionelle udviklings-workflows.
**Refleksionsspørgsmål**: Hvordan vil du gribe opbygningen af et standardiseret udviklingsmiljø an for et team på 10 udviklere? Overvej konsistens, performance og individuelle præferencer.
## 📈 Din tidslinje for mestring af cloud-udvikling
```mermaid
timeline
title Professionel Cloud Udviklingsrejse
section Platform Fundamenter
Cloud Udviklingsforståelse
: Mestre web-baserede redigeringskoncepter
: Forbinde GitHub integrationsmønstre
: Navigere professionelle grænseflader
section Workflow Mestring
Fil- & Projektstyring
: Oprette organiserede projektstrukturer
: Mestre fordelene ved syntaksfremhævning
: Håndtere arbejdsflows med flere filer
Versionskontrol Integration
: Forstå Git visualisering
: Øve commit-besked standarder
: Mestre ændringssporing workflows
section Miljø Tilpasning
Udvidelsesøkosystem
: Opdage produktivitetsudvidelser
: Konfigurere udviklingspræferencer
: Optimere ydeevne vs funktionalitet
Professionel Opsætning
: Bygge konsistente workflows
: Oprette genanvendelige konfigurationer
: Etablere teamstandarder
section Brancheberedskab
Cloud-Første Udvikling
: Mestre fjernudviklingspraksis
: Forstå samarbejdsworkflows
: Opbygge platformuafhængige færdigheder
Professionelle Praksisser
: Følge industristandarder
: Oprette vedligeholdbare workflows
: Forberede til teammiljøer
```
**🎓 Uddannelsesmilepæl**: Du har med succes mestret cloud-baseret udvikling ved hjælp af de samme værktøjer og workflows, som professionelle udviklere i store tech-virksomheder bruger. Disse færdigheder repræsenterer fremtiden for softwareudvikling.
> ⚠️ **Ydelsestip**: Udvidelser er fantastiske, men for mange kan gøre tingene langsomme. Fokuser på dem, der virkelig gør dit liv lettere, og vær ikke bange for at afinstallere dem, du aldrig bruger.
**🔄 Næste niveau evner**:
- Klar til at udforske avancerede cloud-udviklingsplatforme (Codespaces, GitPod)
- Forberedt på at arbejde i distribuerede udviklingsteams
- Udstyret til at bidrage til open source-projekter globalt
- Fundament lagt for moderne DevOps og kontinuerlig integration praksis
## GitHub Copilot Agent Challenge 🚀
## GitHub Copilot Agent Udfordring 🚀
Ligesom den strukturerede tilgang NASA bruger til rumfartsmissioner, involverer denne udfordring systematisk anvendelse af VSCode.dev-færdigheder i en komplet arbejdsgang.
Ligesom den strukturerede tilgang NASA bruger til rummissioner, indebærer denne udfordring systematisk anvendelse af VSCode.dev-færdigheder i et komplet workflow-scenarie.
**Mål:** Demonstrer færdigheder med VSCode.dev ved at etablere en omfattende webudviklingsarbejdsgang.
**Mål:** Demonstrer færdigheder med VSCode.dev ved at etablere et omfattende webudviklingsworkflow.
**Projektkrav:** Brug Agent mode assistance til at fuldføre disse opgaver:
1. Fork en eksisterende repository eller opret en ny
2. Etabler en funktionel projektstruktur med HTML-, CSS- og JavaScript-filer
3. Installer og konfigurer tre udvidelser, der forbedrer udviklingen
4. Øv versionskontrol med beskrivende commit-beskeder
5. Eksperimentér med oprettelse og ændring af feature branches
6. Dokumentér processen og læringer i en README.md-fil
**Projektkrav:** Brug agent-tilstand assistenten til at fuldføre disse opgaver:
1. Fork et eksisterende repository eller opret et nyt
2. Etablér en funktionel projektstruktur med HTML-, CSS- og JavaScript-filer
3. Installer og konfigurer tre udviklingsforbedrende udvidelser
4. Øv versionsstyring med beskrivende commit-beskeder
5. Eksperimenter med oprettelse og ændring af feature-branches
6. Dokumenter processen og læringen i en README.md-fil
Denne øvelse samler alle VSCode.dev-koncepterne i en praktisk arbejdsgang, der kan anvendes på fremtidige udviklingsprojekter.
Denne øvelse samler alle VSCode.dev-konceptet i et praktisk workflow, der kan anvendes i fremtidige udviklingsprojekter.
Læs mere om [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
r mere om [agent-tilstand](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) her.
## Opgave
Tid til at teste disse færdigheder i praksis! Jeg har et praktisk projekt, der lader dig øve alt, vi har dækket: [Opret et CV-websted ved hjælp af VSCode.dev](./assignment.md)
Tid til at teste disse færdigheder i praksis! Jeg har et hands-on projekt, som lader dig øve alt, vi har gennemgået: [Lav et CV-website med VSCode.dev](./assignment.md)
Denne opgave guider dig gennem opbygningen af et professionelt CV-websted helt i din browser. Du vil bruge alle de VSCode.dev-funktioner, vi har udforsket, og når du er færdig, har du både et flot websted og solid selvtillid i din nye arbejdsgang.
Denne opgave guider dig igennem at bygge et professionelt CV-website helt i din browser. Du vil bruge alle de VSCode.dev-funktioner, vi har udforsket, og til slut har du både et flot website og stærk tillid til dit nye workflow.
## Fortsæt med at udforske og udvikle dine færdigheder
## Bliv ved med at udforske og udvikle dine færdigheder
Du har nu et solidt fundament, men der er så meget mere spændende at opdage! Her er nogle ressourcer og idéer til at tage dine VSCode.dev-færdigheder til det næste niveau:
Du har nu et solidt fundament, men der er så meget mere fedt at opdage! Her er nogle ressourcer og idéer til at tage dine VSCode.dev-færdigheder til næste niveau:
**Officielle dokumenter, der er værd at bogmærke:**
**Officielle dokumenter, du bør bogmærke:**
- [VSCode Web Dokumentation](https://code.visualstudio.com/docs/editor/vscode-web?WT.mc_id=academic-0000-alfredodeza) Den komplette guide til browserbaseret redigering
- [GitHub Codespaces](https://docs.github.com/en/codespaces) Når du vil have endnu mere kraft i skyen
**Spændende funktioner at eksperimentere med næste gang:**
- **Tastaturgenveje**: Lær de tastkombinationer, der får dig til at føle dig som en kode-ninja
- **Arbejdsområder**: Opsæt forskellige miljøer til forskellige typer projekter
**Seje funktioner at eksperimentere med næste gang:**
- **Tastaturgenveje**: Lær de tastekombinationer, der får dig til at føle dig som en kodningsninja
- **Workspace-indstillinger**: Opsæt forskellige miljøer til forskellige typer projekter
- **Multi-root Workspaces**: Arbejd på flere repositories samtidig (super praktisk!)
- **Terminalintegration**: Få adgang til kommandolinjeværktøjer direkte i din browser
**Idéer til øvelse:**
- Deltag i nogle open-source-projekter og bidrag ved hjælp af VSCode.dev det er en fantastisk måde at give tilbage!
- Prøv forskellige udvidelser for at finde din perfekte opsætning
- Opret projektskabeloner til de typer af websteder, du oftest bygger
- Øv Git-arbejdsgange som branching og merging disse færdigheder er guld værd i teamprojekter
- Hop med på nogle open source-projekter og bidrag med VSCode.dev det er en god måde at give tilbage på!
- Prøv forskellige udvidelser for at finde dit perfekte setup
- Opret projekt-skabeloner til de typer sites, du bygger mest
- Øv Git-workflows som branching og merging disse færdigheder er guld værd i teamprojekter
---
**Du har mestret browserbaseret udvikling!** 🎉 Ligesom opfindelsen af bærbare instrumenter gjorde det muligt for forskere at udføre forskning på fjerntliggende steder, gør VSCode.dev professionel kodning mulig fra enhver internetforbundet enhed.
**Du har mestret browser-baseret udvikling!** 🎉 Ligesom opfindelsen af bærbare instrumenter gjorde det muligt for videnskabsfolk at lave research i fjerne områder, muliggør VSCode.dev professionel kodning fra enhver enhed med internetforbindelse.
Disse færdigheder afspejler aktuelle industripraksisser mange professionelle udviklere bruger cloud-baserede udviklingsmiljøer for deres fleksibilitet og tilgængelighed. Du har lært en arbejdsgang, der skalerer fra individuelle projekter til store team-samarbejder.
Disse færdigheder afspejler nutidens industripraksis mange professionelle udviklere bruger cloudbaserede udviklingsmiljøer for deres fleksibilitet og tilgængelighed. Du har lært et workflow, der skalerer fra individuelle projekter til store teamsamarbejder.
Anvend disse teknikker på dit næste udviklingsprojekt! 🚀
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal det bemærkes, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For kritiske oplysninger anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->

@ -78,7 +78,7 @@ Da VSCode.dev kræver mindst én fil for at åbne et repository, opretter vi vor
4. **Skriv** en commit-besked: "Tilføj indledende HTML-struktur"
5. **Klik** på "Commit new file" for at gemme dine ændringer
![Oprettelse af indledende fil på GitHub](../../../../translated_images/new-file-github.com.c886796d800e8056561829a181be1382c5303da9d902d8b2dd82b68a4806e21f.da.png)
![Oprettelse af indledende fil på GitHub](../../../../translated_images/new-file-github.com.c886796d800e8056.da.png)
**Dette opnår den indledende opsætning:**
- **Etablerer** korrekt HTML5-dokumentstruktur med semantiske elementer
@ -104,7 +104,7 @@ Nu hvor fundamentet for dit repository er etableret, lad os skifte til VSCode.de
**Succesindikator**: Du bør se dine projektfiler i Explorer-sidepanelet og `index.html` tilgængelig for redigering i hovededitorområdet.
![Projekt indlæst i VSCode.dev](../../../../translated_images/project-on-vscode.dev.e79815a9a95ee7feac72ebe5c941c91279716be37c575dbdbf2f43bea2c7d8b6.da.png)
![Projekt indlæst i VSCode.dev](../../../../translated_images/project-on-vscode.dev.e79815a9a95ee7fe.da.png)
**Hvad du vil se i grænsefladen:**
- **Explorer-sidepanel**: **Viser** dine repository-filer og mappestruktur
@ -448,7 +448,7 @@ Udvidelser forbedrer din udviklingsoplevelse ved at give live preview-muligheder
**Umiddelbare resultater efter installation:**
Når CodeSwing er installeret, vil du se et live preview af din CV-hjemmeside vises i editoren. Dette giver dig mulighed for at se præcis, hvordan din side ser ud, mens du foretager ændringer.
![CodeSwing-udvidelse viser live preview](../../../../translated_images/after-codeswing-extension-pb.0ebddddcf73b550994947a9084e35e2836c713ae13839d49628e3c764c1cfe83.da.png)
![CodeSwing-udvidelse viser live preview](../../../../translated_images/after-codeswing-extension-pb.0ebddddcf73b5509.da.png)
**Forstå den forbedrede grænseflade:**
- **Split view**: **Viser** din kode på den ene side og live preview på den anden

File diff suppressed because it is too large Load Diff

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "8b9d0562ea649b6012d1a67acc630681",
"translation_date": "2025-10-24T08:54:28+00:00",
"original_hash": "fea3a0fceb8ad86fd640c09cf63a2aac",
"translation_date": "2026-01-06T23:20:14+00:00",
"source_file": "README.md",
"language_code": "da"
}
@ -17,26 +17,38 @@ CO_OP_TRANSLATOR_METADATA:
[![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-Beginners.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/network/)
[![GitHub stars](https://img.shields.io/github/stars/microsoft/Web-Dev-For-Beginners.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/stargazers/)
[![](https://dcbadge.vercel.app/api/server/ByRwuEEgH4)](https://discord.gg/zxKYvhSnVp?WT.mc_id=academic-000002-leestott)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
# Webudvikling for begyndere - Et kursus
# Webudvikling for begyndere - Et læseplan
Lær det grundlæggende i webudvikling med vores 12-ugers omfattende kursus fra Microsoft Cloud Advocates. Hver af de 24 lektioner dykker ned i JavaScript, CSS og HTML gennem praktiske projekter som terrarier, browserudvidelser og rumspil. Deltag i quizzer, diskussioner og praktiske opgaver. Forbedr dine færdigheder og optimer din viden med vores effektive projektbaserede undervisningsmetode. Start din koderejse i dag!
Lær det grundlæggende i webudvikling med vores 12-ugers omfattende kursus af Microsoft Cloud Advocates. Hver af de 24 lektioner dykker ned i JavaScript, CSS og HTML gennem praktiske projekter som terrarier, browserudvidelser og rumspil. Deltag i quizzer, diskussioner og praktiske opgaver. Forbedr dine færdigheder og optimer din viden med vores effektive projektbaserede undervisningsmetode. Start din kodningsrejse i dag!
Deltag i Azure AI Foundry Discord-fællesskabet
[![Microsoft Azure AI Foundry Discord](https://dcbadge.limes.pink/api/server/ByRwuEEgH4)](https://discord.com/invite/ByRwuEEgH4)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
Følg disse trin for at komme i gang med at bruge ressourcerne:
1. **Fork repository**: Klik på [![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/fork)
2. **Clone repository**: `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
3. [**Deltag i Azure AI Foundry Discord og mød eksperter og andre udviklere**](https://discord.com/invite/ByRwuEEgH4)
Følg disse trin for at komme i gang med at bruge disse ressourcer:
1. **Fork Repository**: Klik på [![GitHub forks](https://img.shields.io/github/forks/microsoft/Web-Dev-For-beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/Web-Dev-For-Beginners/fork)
2. **Klon Repository**: `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
3. [**Deltag i Azure AI Foundry Discord og mød eksperter og medudviklere**](https://discord.com/invite/ByRwuEEgH4)
### 🌐 Flersproget support
#### Understøttet via GitHub Action (Automatisk & Altid opdateret)
#### Understøttet via GitHub Action (Automatiseret & Altid Opdateret)
[Arabic](../ar/README.md) | [Bengali](../bn/README.md) | [Bulgarian](../bg/README.md) | [Burmese (Myanmar)](../my/README.md) | [Chinese (Simplified)](../zh/README.md) | [Chinese (Traditional, Hong Kong)](../hk/README.md) | [Chinese (Traditional, Macau)](../mo/README.md) | [Chinese (Traditional, Taiwan)](../tw/README.md) | [Croatian](../hr/README.md) | [Czech](../cs/README.md) | [Danish](./README.md) | [Dutch](../nl/README.md) | [Estonian](../et/README.md) | [Finnish](../fi/README.md) | [French](../fr/README.md) | [German](../de/README.md) | [Greek](../el/README.md) | [Hebrew](../he/README.md) | [Hindi](../hi/README.md) | [Hungarian](../hu/README.md) | [Indonesian](../id/README.md) | [Italian](../it/README.md) | [Japanese](../ja/README.md) | [Korean](../ko/README.md) | [Lithuanian](../lt/README.md) | [Malay](../ms/README.md) | [Marathi](../mr/README.md) | [Nepali](../ne/README.md) | [Norwegian](../no/README.md) | [Persian (Farsi)](../fa/README.md) | [Polish](../pl/README.md) | [Portuguese (Brazil)](../br/README.md) | [Portuguese (Portugal)](../pt/README.md) | [Punjabi (Gurmukhi)](../pa/README.md) | [Romanian](../ro/README.md) | [Russian](../ru/README.md) | [Serbian (Cyrillic)](../sr/README.md) | [Slovak](../sk/README.md) | [Slovenian](../sl/README.md) | [Spanish](../es/README.md) | [Swahili](../sw/README.md) | [Swedish](../sv/README.md) | [Tagalog (Filipino)](../tl/README.md) | [Tamil](../ta/README.md) | [Thai](../th/README.md) | [Turkish](../tr/README.md) | [Ukrainian](../uk/README.md) | [Urdu](../ur/README.md) | [Vietnamese](../vi/README.md)
<!-- CO-OP TRANSLATOR LANGUAGES TABLE START -->
[Arabic](../ar/README.md) | [Bengali](../bn/README.md) | [Bulgarian](../bg/README.md) | [Burmese (Myanmar)](../my/README.md) | [Chinese (Simplified)](../zh/README.md) | [Chinese (Traditional, Hong Kong)](../hk/README.md) | [Chinese (Traditional, Macau)](../mo/README.md) | [Chinese (Traditional, Taiwan)](../tw/README.md) | [Croatian](../hr/README.md) | [Czech](../cs/README.md) | [Danish](./README.md) | [Dutch](../nl/README.md) | [Estonian](../et/README.md) | [Finnish](../fi/README.md) | [French](../fr/README.md) | [German](../de/README.md) | [Greek](../el/README.md) | [Hebrew](../he/README.md) | [Hindi](../hi/README.md) | [Hungarian](../hu/README.md) | [Indonesian](../id/README.md) | [Italian](../it/README.md) | [Japanese](../ja/README.md) | [Kannada](../kn/README.md) | [Korean](../ko/README.md) | [Lithuanian](../lt/README.md) | [Malay](../ms/README.md) | [Malayalam](../ml/README.md) | [Marathi](../mr/README.md) | [Nepali](../ne/README.md) | [Nigerian Pidgin](../pcm/README.md) | [Norwegian](../no/README.md) | [Persian (Farsi)](../fa/README.md) | [Polish](../pl/README.md) | [Portuguese (Brazil)](../br/README.md) | [Portuguese (Portugal)](../pt/README.md) | [Punjabi (Gurmukhi)](../pa/README.md) | [Romanian](../ro/README.md) | [Russian](../ru/README.md) | [Serbian (Cyrillic)](../sr/README.md) | [Slovak](../sk/README.md) | [Slovenian](../sl/README.md) | [Spanish](../es/README.md) | [Swahili](../sw/README.md) | [Swedish](../sv/README.md) | [Tagalog (Filipino)](../tl/README.md) | [Tamil](../ta/README.md) | [Telugu](../te/README.md) | [Thai](../th/README.md) | [Turkish](../tr/README.md) | [Ukrainian](../uk/README.md) | [Urdu](../ur/README.md) | [Vietnamese](../vi/README.md)
> **Foretrækker du at klone lokalt?**
> Dette repository inkluderer 50+ sprogoversættelser, som markant øger downloadstørrelsen. For at klone uden oversættelser, brug sparse checkout:
> ```bash
> git clone --filter=blob:none --sparse https://github.com/microsoft/Web-Dev-For-Beginners.git
> cd Web-Dev-For-Beginners
> git sparse-checkout set --no-cone '/*' '!translations' '!translated_images'
> ```
> Dette giver dig alt, hvad du behøver for at gennemføre kurset med en meget hurtigere download.
<!-- CO-OP TRANSLATOR LANGUAGES TABLE END -->
**Hvis du ønsker yderligere oversættelser, er understøttede sprog listet [her](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
@ -44,34 +56,35 @@ Følg disse trin for at komme i gang med at bruge ressourcerne:
#### 🧑‍🎓 _Er du studerende?_
Besøg [**Student Hub-siden**](https://docs.microsoft.com/learn/student-hub/?WT.mc_id=academic-77807-sagibbon), hvor du finder ressourcer for begyndere, studenterpakker og endda måder at få en gratis certifikatvoucher. Dette er siden, du vil bogmærke og tjekke fra tid til anden, da vi skifter indhold månedligt.
Besøg [**Student Hub-siden**](https://docs.microsoft.com/learn/student-hub/?WT.mc_id=academic-77807-sagibbon) hvor du vil finde begynderguider, studentepakker og endda måder at få en gratis certifikatkupon på. Dette er siden, du vil bogmærke og tjekke fra tid til anden, da vi månedligt udskifter indhold.
### 📣 Meddelelse - Nye GitHub Copilot Agent mode-udfordringer at fuldføre!
### 📣 Meddelelse - Nye GitHub Copilot Agent-udfordringer til at gennemføre!
Ny udfordring tilføjet, kig efter "GitHub Copilot Agent Challenge 🚀" i de fleste kapitler. Det er en ny udfordring for dig at fuldføre ved hjælp af GitHub Copilot og Agent mode. Hvis du ikke har brugt Agent mode før, er det i stand til ikke kun at generere tekst, men kan også oprette og redigere filer, køre kommandoer og mere.
Ny udfordring tilføjet, kig efter "GitHub Copilot Agent Challenge 🚀" i de fleste kapitler. Det er en ny udfordring, du skal gennemføre ved brug af GitHub Copilot og Agent-tilstand. Hvis du ikke har brugt Agent-tilstand før, kan den ikke kun generere tekst, men også oprette og redigere filer, køre kommandoer og mere.
### 📣 Meddelelse - _Nyt projekt at bygge ved hjælp af Generativ AI_
### 📣 Meddelelse - _Nyt projekt at bygge med Generativ AI_
Nyt AI-assistentprojekt lige tilføjet, tjek det ud [projekt](./09-chat-project/README.md)
Nyt AI-assistentprojekt lige tilføjet, tjek det ud [projekt](./9-chat-project/README.md)
### 📣 Meddelelse - _Nyt pensum_ om Generativ AI for JavaScript er netop blevet udgivet
### 📣 Meddelelse - _Ny læseplan_ om Generativ AI for JavaScript er netop udgivet
Gå ikke glip af vores nye pensum om Generativ AI!
Gå ikke glip af vores nye Generativ AI-læseplan!
Besøg [https://aka.ms/genai-js-course](https://aka.ms/genai-js-course) for at komme i gang!
![Baggrund](../../translated_images/background.148a8d43afde57303419a663f50daf586681bc2fabf833f66ef6954073983c66.da.png)
![Background](../../translated_images/background.148a8d43afde5730.da.png)
- Lektioner der dækker alt fra grundlæggende til RAG.
- Interager med historiske personer ved hjælp af GenAI og vores ledsagerapp.
- Sjov og engagerende fortælling, du kommer til at tidsrejse!
- Lektioner, der dækker alt fra grundlæggende til RAG.
- Interager med historiske figurer ved hjælp af GenAI og vores ledsager-app.
- Sjov og engagerende fortælling, du vil rejse i tiden!
![character](../../translated_images/character.5c0dd8e067ffd693.da.png)
![karakter](../../translated_images/character.5c0dd8e067ffd693c16e2c5b7412ab075a2215ce31f998305639fa3a05e14fbe.da.png)
Hver lektion inkluderer en opgave, en videnstest og en udfordring, der guider dig i at lære emner som:
Hver lektion inkluderer en opgave, en vidensprøve og en udfordring, der guider dig gennem læring af emner som:
- Prompting og prompt engineering
- Tekst- og billedapp-generering
- Søgeapps
- Tekst- og billedapp-generation
- Søg-apps
Besøg [https://aka.ms/genai-js-course](../../[https:/aka.ms/genai-js-course) for at komme i gang!
@ -79,182 +92,189 @@ Besøg [https://aka.ms/genai-js-course](../../[https:/aka.ms/genai-js-course) fo
## 🌱 Kom godt i gang
> **Lærere**, vi har [inkluderet nogle forslag](for-teachers.md) til, hvordan man kan bruge dette pensum. Vi vil meget gerne høre din feedback [i vores diskussionsforum](https://github.com/microsoft/Web-Dev-For-Beginners/discussions/categories/teacher-corner)!
> **Lærere**, vi har [inkluderet nogle forslag](for-teachers.md) til, hvordan dette læseplan kan bruges. Vi vil meget gerne have din feedback [i vores diskussionsforum](https://github.com/microsoft/Web-Dev-For-Beginners/discussions/categories/teacher-corner)!
**[Lærende](https://aka.ms/student-page/?WT.mc_id=academic-77807-sagibbon)**, for hver lektion, start med en quiz før lektionen og fortsæt med at læse undervisningsmaterialet, fuldføre de forskellige aktiviteter og tjek din forståelse med quizzen efter lektionen.
**[Elever](https://aka.ms/student-page/?WT.mc_id=academic-77807-sagibbon)**, for hver lektion start med en quiz før forelæsningen og fortsæt med at læse forelæsningsmaterialet, gennemføre de forskellige aktiviteter og tjek din forståelse med quizzen efter forelæsningen.
For at forbedre din læringsoplevelse, forbind med dine medstuderende for at arbejde på projekterne sammen! Diskussioner opfordres i vores [diskussionsforum](https://github.com/microsoft/Web-Dev-For-Beginners/discussions), hvor vores team af moderatorer vil være tilgængelige for at besvare dine spørgsmål.
For at forbedre din læringsoplevelse, forbind dig med dine medstuderende for at arbejde på projekterne sammen! Diskussioner opfordres i vores [diskussionsforum](https://github.com/microsoft/Web-Dev-For-Beginners/discussions) hvor vores team af moderatorer vil være tilgængelige for at besvare dine spørgsmål.
For at udvide din uddannelse anbefaler vi stærkt at udforske [Microsoft Learn](https://learn.microsoft.com/users/wirelesslife/collections/p1ddcy5jwy0jkm?WT.mc_id=academic-77807-sagibbon) for yderligere studiematerialer.
For at udvide din uddannelse anbefaler vi kraftigt at udforske [Microsoft Learn](https://learn.microsoft.com/users/wirelesslife/collections/p1ddcy5jwy0jkm?WT.mc_id=academic-77807-sagibbon) for yderligere studiematerialer.
### 📋 Opsætning af dit miljø
### 📋 Opsæt dit miljø
Dette pensum har et udviklingsmiljø klar til brug! Når du begynder, kan du vælge at køre pensum i en [Codespace](https://github.com/features/codespaces/) (_et browserbaseret miljø, hvor ingen installationer er nødvendige_), eller lokalt på din computer ved hjælp af en teksteditor som [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon).
Dette læseplan har et udviklingsmiljø klar til brug! Når du kommer i gang, kan du vælge at køre læseplanen i en [Codespace](https://github.com/features/codespaces/) (_et browserbaseret miljø uden behov for installation_), eller lokalt på din computer ved brug af en teksteditor som [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon).
#### Opret dit repository
For at du nemt kan gemme dit arbejde, anbefales det, at du opretter din egen kopi af dette repository. Du kan gøre dette ved at klikke på knappen **Use this template** øverst på siden. Dette vil oprette et nyt repository i din GitHub-konto med en kopi af pensum.
For at du nemt kan gemme dit arbejde, anbefales det at du opretter en egen kopi af dette repository. Det kan du gøre ved at klikke på knappen **Use this template** øverst på siden. Det vil oprette et nyt repository på din GitHub-konto med en kopi af læseplanen.
Følg disse trin:
1. **Fork repository**: Klik på "Fork"-knappen øverst til højre på denne side.
2. **Clone repository**: `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
1. **Fork Repository**: Klik på "Fork" knappen øverst til højre på denne side.
2. **Klon Repository**: `git clone https://github.com/microsoft/Web-Dev-For-Beginners.git`
#### Kør pensum i en Codespace
#### Kør læseplanen i en Codespace
I din kopi af dette repository, som du har oprettet, klik på **Code**-knappen og vælg **Open with Codespaces**. Dette vil oprette en ny Codespace, som du kan arbejde i.
I din kopi af dette repository som du har oprettet, klik på **Code** knappen og vælg **Open with Codespaces**. Det vil oprette en ny Codespace til dig at arbejde i.
![Codespace](../../translated_images/createcodespace.0238bbf4d7a8d955fa8fa7f7b6602a3cb6499a24708fbee589f83211c5a613b7.da.png)
![Codespace](../../translated_images/createcodespace.0238bbf4d7a8d955.da.png)
#### Kør pensum lokalt på din computer
#### Kør læseplanen lokalt på din computer
For at køre dette pensum lokalt på din computer, skal du bruge en teksteditor, en browser og et kommandolinjeværktøj. Vores første lektion, [Introduktion til programmeringssprog og værktøjer](../../1-getting-started-lessons/1-intro-to-programming-languages), vil guide dig gennem forskellige muligheder for hvert af disse værktøjer, så du kan vælge det, der passer bedst til dig.
For at køre dette læseplan lokalt på din computer skal du bruge en teksteditor, en browser og et kommandolinjeværktøj. Vores første lektion, [Introduktion til programmeringssprog og værktøjer](../../1-getting-started-lessons/1-intro-to-programming-languages), vil guide dig gennem forskellige muligheder for hver af disse værktøjer, så du kan vælge det, der passer bedst til dig.
Vores anbefaling er at bruge [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) som din editor, som også har en indbygget [Terminal](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon). Du kan downloade Visual Studio Code [her](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon).
Vi anbefaler at bruge [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) som editor, der også har et indbygget [Terminal](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon). Du kan downloade Visual Studio Code [her](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon).
1. Klon dit repository til din computer. Du kan gøre dette ved at klikke på **Code**-knappen og kopiere URL'en:
[CodeSpace](./images/createcodespace.png)
1. Klon dit repository til din computer. Det kan du gøre ved at klikke på **Code** knappen og kopiere URLen:
Derefter åbner du [Terminal](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon) i [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) og kører følgende kommando, hvor `<your-repository-url>` erstattes med den URL, du lige har kopieret:
[CodeSpace](./images/createcodespace.png)
Åbn derefter [Terminal](https://code.visualstudio.com/docs/terminal/basics/?WT.mc_id=academic-77807-sagibbon) i [Visual Studio Code](https://code.visualstudio.com/?WT.mc_id=academic-77807-sagibbon) og kør følgende kommando, hvor du udskifter `<your-repository-url>` med den URL, du netop har kopieret:
```bash
git clone <your-repository-url>
```
2. Åbn mappen i Visual Studio Code. Du kan gøre dette ved at klikke på **File** > **Open Folder** og vælge den mappe, du lige har klonet.
2. Åbn mappen i Visual Studio Code. Det kan du gøre ved at klikke på **File** > **Open Folder** og vælge den mappe, du netop har klonet.
> Anbefalede Visual Studio Code-udvidelser:
>
> * [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) - for at forhåndsvise HTML-sider i Visual Studio Code
> * [Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot&WT.mc_id=academic-77807-sagibbon) - for at hjælpe dig med at skrive kode hurtigere
> * [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) - til at forhåndsvise HTML-sider inden for Visual Studio Code
> * [Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot&WT.mc_id=academic-77807-sagibbon) - hjælper dig med at skrive kode hurtigere
## 📂 Hver lektion inkluderer:
- valgfri sketchnote
- valgfri supplerende video
- opvarmningsquiz før lektionen
- skriftlig lektion
- quiz som opvarmning før lektionen
- skreven lektion
- for projektbaserede lektioner, trin-for-trin vejledninger til at bygge projektet
- videnschecks
- videnskontroller
- en udfordring
- supplerende læsning
- opgave
- [quiz efter lektionen](https://ff-quizzes.netlify.app/web/)
> **En note om quizzer**: Alle quizzer findes i Quiz-app-mappen, i alt 48 quizzer med tre spørgsmål hver. De er tilgængelige [her](https://ff-quizzes.netlify.app/web/), og quiz-appen kan køres lokalt eller implementeres på Azure; følg instruktionerne i `quiz-app`-mappen.
> **En note om quizzer**: Alle quizzer er indeholdt i Quiz-app mappen, i alt 48 quizzer med tre spørgsmål hver. De er tilgængelige [her](https://ff-quizzes.netlify.app/web/) quiz appen kan køres lokalt eller deployes til Azure; følg instruktionerne i `quiz-app` mappen.
## 🗃️ Lektioner
| | Projekt Navn | Underviste Koncepter | Læringsmål | Linket Lektion | Forfatter |
| :-: | :------------------------------------------------------: | :--------------------------------------------------------------------: | ----------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------: | :---------------------: |
| 01 | Kom godt i gang | Introduktion til programmering og værktøjer til faget | Lær de grundlæggende principper bag de fleste programmeringssprog og om software, der hjælper professionelle udviklere med deres arbejde | [Introduktion til programmeringssprog og værktøjer til faget](./1-getting-started-lessons/1-intro-to-programming-languages/README.md) | Jasmine |
| 02 | Kom godt i gang | Grundlæggende om GitHub, herunder arbejde i et team | Hvordan man bruger GitHub i dit projekt, og hvordan man samarbejder med andre om en kodebase | [Introduktion til GitHub](./1-getting-started-lessons/2-github-basics/README.md) | Floor |
| 03 | Kom godt i gang | Tilgængelighed | Lær det grundlæggende om webtilgængelighed | [Grundlæggende om tilgængelighed](./1-getting-started-lessons/3-accessibility/README.md) | Christopher |
| 04 | JS Grundlæggende | JavaScript Datatyper | Det grundlæggende om JavaScript datatyper | [Datatyper](./2-js-basics/1-data-types/README.md) | Jasmine |
| 05 | JS Grundlæggende | Funktioner og metoder | Lær om funktioner og metoder til at styre en applikations logikflow | [Funktioner og metoder](./2-js-basics/2-functions-methods/README.md) | Jasmine og Christopher |
| 06 | JS Grundlæggende | At træffe beslutninger med JS | Lær hvordan man skaber betingelser i din kode ved hjælp af beslutningsmetoder | [At træffe beslutninger](./2-js-basics/3-making-decisions/README.md) | Jasmine |
| 07 | JS Grundlæggende | Arrays og loops | Arbejd med data ved hjælp af arrays og loops i JavaScript | [Arrays og loops](./2-js-basics/4-arrays-loops/README.md) | Jasmine |
| 08 | [Terrarium](./3-terrarium/solution/README.md) | HTML i praksis | Byg HTML for at skabe et online terrarium med fokus på at opbygge et layout | [Introduktion til HTML](./3-terrarium/1-intro-to-html/README.md) | Jen |
| 09 | [Terrarium](./3-terrarium/solution/README.md) | CSS i praksis | Byg CSS for at style det online terrarium med fokus på det grundlæggende i CSS, herunder at gøre siden responsiv | [Introduktion til CSS](./3-terrarium/2-intro-to-css/README.md) | Jen |
| 10 | [Terrarium](./3-terrarium/solution/README.md) | JavaScript Closures, DOM-manipulation | Byg JavaScript for at få terrariet til at fungere som en drag/drop-grænseflade med fokus på closures og DOM-manipulation | [JavaScript Closures, DOM-manipulation](./3-terrarium/3-intro-to-DOM-and-closures/README.md) | Jen |
| 11 | [Typing Game](./4-typing-game/solution/README.md) | Byg et tastaturspil | Lær hvordan man bruger tastaturbegivenheder til at styre logikken i din JavaScript-app | [Event-drevet programmering](./4-typing-game/typing-game/README.md) | Christopher |
| 12 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Arbejde med browsere | Lær hvordan browsere fungerer, deres historie, og hvordan man opbygger de første elementer i en browserudvidelse | [Om browsere](./5-browser-extension/1-about-browsers/README.md) | Jen |
| 13 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Bygning af en formular, kald af en API og lagring af variabler i lokal storage | Byg JavaScript-elementerne i din browserudvidelse for at kalde en API ved hjælp af variabler, der er gemt i lokal storage | [APIs, formularer og lokal storage](./5-browser-extension/2-forms-browsers-local-storage/README.md) | Jen |
| 14 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Baggrundsprocesser i browseren, webydelse | Brug browserens baggrundsprocesser til at administrere udvidelsens ikon; lær om webydelse og nogle optimeringer | [Baggrundsopgaver og ydeevne](./5-browser-extension/3-background-tasks-and-performance/README.md) | Jen |
| 15 | [Space Game](./6-space-game/solution/README.md) | Mere avanceret spiludvikling med JavaScript | Lær om arv ved hjælp af både klasser og komposition samt Pub/Sub-mønsteret som forberedelse til at bygge et spil | [Introduktion til avanceret spiludvikling](./6-space-game/1-introduction/README.md) | Chris |
| 16 | [Space Game](./6-space-game/solution/README.md) | Tegning på canvas | Lær om Canvas API, der bruges til at tegne elementer på en skærm | [Tegning på canvas](./6-space-game/2-drawing-to-canvas/README.md) | Chris |
| 17 | [Space Game](./6-space-game/solution/README.md) | Flytte elementer rundt på skærmen | Opdag hvordan elementer kan få bevægelse ved hjælp af kartesiske koordinater og Canvas API | [Flytte elementer rundt](./6-space-game/3-moving-elements-around/README.md) | Chris |
| 18 | [Space Game](./6-space-game/solution/README.md) | Kollisionsdetektion | Få elementer til at kollidere og reagere på hinanden ved hjælp af tastetryk og tilføj en cooldown-funktion for at sikre spillets ydeevne | [Kollisionsdetektion](./6-space-game/4-collision-detection/README.md) | Chris |
| 19 | [Space Game](./6-space-game/solution/README.md) | Holde styr på point | Udfør matematiske beregninger baseret på spillets status og ydeevne | [Holde styr på point](./6-space-game/5-keeping-score/README.md) | Chris |
| 20 | [Space Game](./6-space-game/solution/README.md) | Afslutte og genstarte spillet | Lær om at afslutte og genstarte spillet, herunder oprydning af aktiver og nulstilling af variabelværdier | [Afslutningsbetingelse](./6-space-game/6-end-condition/README.md) | Chris |
| 21 | [Banking App](./7-bank-project/solution/README.md) | HTML-skabeloner og ruter i en webapp | Lær hvordan man opretter strukturen for en multipages hjemmesides arkitektur ved hjælp af routing og HTML-skabeloner | [HTML-skabeloner og ruter](./7-bank-project/1-template-route/README.md) | Yohan |
| 22 | [Banking App](./7-bank-project/solution/README.md) | Byg en login- og registreringsformular | Lær om at bygge formularer og håndtere valideringsrutiner | [Formularer](./7-bank-project/2-forms/README.md) | Yohan |
| 23 | [Banking App](./7-bank-project/solution/README.md) | Metoder til at hente og bruge data | Hvordan data flyder ind og ud af din app, hvordan man henter det, gemmer det og bortskaffer det | [Data](./7-bank-project/3-data/README.md) | Yohan |
| 24 | [Banking App](./7-bank-project/solution/README.md) | Koncepter for tilstandsadministration | Lær hvordan din app bevarer tilstand, og hvordan man administrerer det programmæssigt | [Tilstandsadministration](./7-bank-project/4-state-management/README.md) | Yohan |
| 25 | [Browser/VScode Code](../../8-code-editor) | Arbejde med VScode | Lær hvordan man bruger en kodeeditor | [Brug VScode Code Editor](./8-code-editor/1-using-a-code-editor/README.md) | Chris |
| 26 | [AI Assistants](./9-chat-project/README.md) | Arbejde med AI | Lær hvordan man bygger sin egen AI-assistent | [AI Assistent projekt](./9-chat-project/README.md) | Chris |
| | Projektnavn | Koncepter undervist | Læringsmål | Linket lektion | Forfatter |
| :-: | :----------------------------------------------------: | :-----------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------------------------------------------: | :-----------------------: |
| 01 | Kom godt i gang | Introduktion til programmering og værktøjer | Lær de grundlæggende elementer bag de fleste programmeringssprog og om software, der hjælper professionelle udviklere | [Introduktion til programmeringssprog og værktøjer](./1-getting-started-lessons/1-intro-to-programming-languages/README.md) | Jasmine |
| 02 | Kom godt i gang | Grundlæggende GitHub, inkl. arbejde med et team | Hvordan man bruger GitHub i sit projekt, og hvordan man samarbejder med andre om en kodebase | [Introduktion til GitHub](./1-getting-started-lessons/2-github-basics/README.md) | Floor |
| 03 | Kom godt i gang | Tilgængelighed | Lær det grundlæggende om webtilgængelighed | [Grundlæggende tilgængelighed](./1-getting-started-lessons/3-accessibility/README.md) | Christopher |
| 04 | JS Basics | JavaScript-datatyper | Det grundlæggende om JavaScript-datatyper | [Datatyper](./2-js-basics/1-data-types/README.md) | Jasmine |
| 05 | JS Basics | Funktioner og metoder | Lær om funktioner og metoder til at styre en applikations logik | [Funktioner og metoder](./2-js-basics/2-functions-methods/README.md) | Jasmine og Christopher |
| 06 | JS Basics | Beslutningstagning med JS | Lær hvordan man skaber betingelser i sin kode ved brug af beslutningstagning | [Beslutningstagning](./2-js-basics/3-making-decisions/README.md) | Jasmine |
| 07 | JS Basics | Arrays og løkker | Arbejd med data ved brug af arrays og løkker i JavaScript | [Arrays og løkker](./2-js-basics/4-arrays-loops/README.md) | Jasmine |
| 08 | [Terrarium](./3-terrarium/solution/README.md) | HTML i praksis | Byg HTMLen til at skabe et online terrarium med fokus på at bygge et layout | [Introduktion til HTML](./3-terrarium/1-intro-to-html/README.md) | Jen |
| 09 | [Terrarium](./3-terrarium/solution/README.md) | CSS i praksis | Byg CSSen til at style det online terrarium med fokus på CSSs grundlæggende, inkl. at gøre siden responsiv | [Introduktion til CSS](./3-terrarium/2-intro-to-css/README.md) | Jen |
| 10 | [Terrarium](./3-terrarium/solution/README.md) | JavaScript closures, DOM-manipulation | Byg JavaScripten til at gøre terrariet funktionelt som et drag/drop interface med fokus på closures og DOM-manipulation | [JavaScript closures, DOM-manipulation](./3-terrarium/3-intro-to-DOM-and-closures/README.md) | Jen |
| 11 | [Typing Game](./4-typing-game/solution/README.md) | Byg et skrive-spil | Lær hvordan man bruger tastaturhændelser til at styre logikken i din JavaScript-app | [Begivenhedsdrevet programmering](./4-typing-game/typing-game/README.md) | Christopher |
| 12 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Arbejde med browsere | Lær hvordan browsere fungerer, deres historie og hvordan man skaber de første elementer af en browserudvidelse | [Om browsere](./5-browser-extension/1-about-browsers/README.md) | Jen |
| 13 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Byg en formular, kald et API og gem variabler i lokal lagring | Byg JavaScript-elementerne i din browserudvidelse for at kalde et API vha. variabler gemt i lokal lagring | [APIer, formularer og lokal lagring](./5-browser-extension/2-forms-browsers-local-storage/README.md) | Jen |
| 14 | [Green Browser Extension](./5-browser-extension/solution/README.md) | Baggrundsprocesser i browseren, webperformance | Brug browserens baggrundsprocesser til at styre udvidelsens ikon; lær om webperformance og optimeringer | [Baggrundsopgaver og performance](./5-browser-extension/3-background-tasks-and-performance/README.md) | Jen |
| 15 | [Space Game](./6-space-game/solution/README.md) | Mere avanceret spiludvikling med JavaScript | Lær om arv ved brug af både klasser og komposition og Pub/Sub-mønsteret som forberedelse til at bygge et spil | [Introduktion til avanceret spiludvikling](./6-space-game/1-introduction/README.md) | Chris |
| 16 | [Space Game](./6-space-game/solution/README.md) | Tegning på canvas | Lær om Canvas APIet, der bruges til at tegne elementer på en skærm | [Tegning på canvas](./6-space-game/2-drawing-to-canvas/README.md) | Chris |
| 17 | [Space Game](./6-space-game/solution/README.md) | Flyt elementer rundt på skærmen | Opdag hvordan elementer kan bevæge sig vha. kartesiske koordinater og Canvas APIet | [Flyt elementer rundt](./6-space-game/3-moving-elements-around/README.md) | Chris |
| 18 | [Space Game](./6-space-game/solution/README.md) | Kollisiondetektion | Få elementer til at kollidere og reagere på hinanden ved brug af tastetryk og indfør en cooldown-funktion for spillets ydeevne | [Kollisiondetektion](./6-space-game/4-collision-detection/README.md) | Chris |
| 19 | [Space Game](./6-space-game/solution/README.md) | Hold styr på point | Udfør matematiske beregninger baseret på spillets status og ydeevne | [Hold styr på point](./6-space-game/5-keeping-score/README.md) | Chris |
| 20 | [Space Game](./6-space-game/solution/README.md) | Afslut og genstart spillet | Lær om at afslutte og genstarte spillet, inklusiv oprydning af ressourcer og nulstilling af variabelværdier | [Afslutningsbetingelsen](./6-space-game/6-end-condition/README.md) | Chris |
| 21 | [Banking App](./7-bank-project/solution/README.md) | HTML-skabeloner og ruter i en webapp | Lær hvordan man skaber arkitekturen for et multipage-websted ved brug af routing og HTML-skabeloner | [HTML-skabeloner og ruter](./7-bank-project/1-template-route/README.md) | Yohan |
| 22 | [Banking App](./7-bank-project/solution/README.md) | Byg en login- og registreringsformular | Lær om at bygge formularer og håndtere valideringsrutiner | [Formularer](./7-bank-project/2-forms/README.md) | Yohan |
| 23 | [Banking App](./7-bank-project/solution/README.md) | Metoder til at hente og bruge data | Hvordan data flyder ind og ud af din app, hvordan man henter, gemmer og bortskaffer det | [Data](./7-bank-project/3-data/README.md) | Yohan |
| 24 | [Banking App](./7-bank-project/solution/README.md) | Begreber inden for tilstandsstyring | Lær hvordan din app bevarer tilstand, og hvordan det styres programmatisk | [Tilstandsstyring](./7-bank-project/4-state-management/README.md) | Yohan |
| 25 | [Browser/VScode Code](../../8-code-editor) | Arbejde med VScode | Lær hvordan man bruger en kodeeditor | [Brug af VScode Code Editor](./8-code-editor/1-using-a-code-editor/README.md) | Chris |
| 26 | [AI Assistants](./9-chat-project/README.md) | Arbejde med AI | Lær hvordan du bygger din egen AI-assistent | [AI Assistant projekt](./9-chat-project/README.md) | Chris |
## 🏫 Pædagogik
Vores pensum er designet med to centrale pædagogiske principper i tankerne:
Vores pensum er designet med to nøglepædagogiske principper for øje:
* projektbaseret læring
* hyppige quizzer
Programmet underviser i grundlæggende JavaScript, HTML og CSS samt de nyeste værktøjer og teknikker, som nutidens webudviklere bruger. Studerende får mulighed for at opbygge praktisk erfaring ved at bygge et tastaturspil, et virtuelt terrarium, en miljøvenlig browserudvidelse, et spil i stil med Space Invaders og en bankapp til virksomheder. Ved afslutningen af serien vil de studerende have opnået en solid forståelse af webudvikling.
Programmet underviser i grundlæggende JavaScript, HTML og CSS samt de nyeste værktøjer og teknikker, som nutidens webudviklere bruger. Studerende får mulighed for at opnå praktisk erfaring ved at bygge et skrive-spil, et virtuelt terrarium, en miljøvenlig browserudvidelse, et rum-invader stil spil og en bankapp til virksomheder. Ved slutningen af serien vil studerende have opnået solid forståelse for webudvikling.
> 🎓 Du kan tage de første par lektioner i dette pensum som en [Learn Path](https://docs.microsoft.com/learn/paths/web-development-101/?WT.mc_id=academic-77807-sagibbon) på Microsoft Learn!
> 🎓 Du kan tage de første par lektioner i dette pensum som en [læringssti](https://docs.microsoft.com/learn/paths/web-development-101/?WT.mc_id=academic-77807-sagibbon) på Microsoft Learn!
Ved at sikre, at indholdet er i overensstemmelse med projekter, bliver processen mere engagerende for de studerende, og begreberne vil blive bedre fastholdt. Vi har også skrevet flere introduktionslektioner i JavaScript-grundlæggende for at introducere begreber, parret med en video fra "[Beginners Series to: JavaScript](https://channel9.msdn.com/Series/Beginners-Series-to-JavaScript/?WT.mc_id=academic-77807-sagibbon)" samlingen af videotutorials, hvor nogle af forfatterne har bidraget til dette pensum.
Ved at sikre, at indholdet knytter sig til projekter, bliver processen mere engagerende for eleverne, og fastholdelsen af koncepter vil blive forbedret. Vi har også skrevet flere startlektioner i JavaScript-basics for at introducere koncepter, parret med en video fra "[Beginners Series to: JavaScript](https://channel9.msdn.com/Series/Beginners-Series-to-JavaScript/?WT.mc_id=academic-77807-sagibbon)" samlingen af videotutorials, hvor nogle af forfatterne har bidraget til dette pensum.
Derudover sætter en lav-stress quiz før en klasse intentionen hos den studerende mod at lære et emne, mens en anden quiz efter klassen sikrer yderligere fastholdelse. Dette pensum er designet til at være fleksibelt og sjovt og kan tages helt eller delvist. Projekterne starter små og bliver gradvist mere komplekse ved slutningen af den 12-ugers cyklus.
Derudover sætter en quiz med lav indsats før en lektion elevens intention mod at lære et emne, mens en anden quiz efter lektion sikrer yderligere fastholdelse. Dette pensum er designet til at være fleksibelt og sjovt og kan gennemføres helt eller delvist. Projekterne starter småt og bliver gradvist mere komplekse i løbet af den 12-ugers cyklus.
Mens vi bevidst har undgået at introducere JavaScript-frameworks for at koncentrere os om de grundlæggende færdigheder, der er nødvendige som webudvikler, før vi adopterer et framework, ville et godt næste skridt efter at have gennemført dette pensum være at lære om Node.js via en anden samling af videoer: "[Beginner Series to: Node.js](https://channel9.msdn.com/Series/Beginners-Series-to-Nodejs/?WT.mc_id=academic-77807-sagibbon)".
Selvom vi bevidst har undgået at indføre JavaScript-rammeværk for at koncentrere os om de grundlæggende færdigheder, der er nødvendige som webudvikler, før man tager et rammeværk i brug, vil et godt næste skridt efter at have gennemført dette pensum være at lære om Node.js via en anden samling videoer: "[Beginner Series to: Node.js](https://channel9.msdn.com/Series/Beginners-Series-to-Nodejs/?WT.mc_id=academic-77807-sagibbon)".
> Besøg vores [Code of Conduct](CODE_OF_CONDUCT.md) og [Contributing](CONTRIBUTING.md) retningslinjer. Vi værdsætter din konstruktive feedback!
> Besøg vores [Adfærdskodeks](CODE_OF_CONDUCT.md) og [Bidrag](CONTRIBUTING.md) retningslinjer. Vi sætter pris på din konstruktive feedback!
## 🧭 Offline adgang
Du kan køre denne dokumentation offline ved hjælp af [Docsify](https://docsify.js.org/#/). Fork denne repo, [installer Docsify](https://docsify.js.org/#/quickstart) på din lokale maskine, og skriv derefter `docsify serve` i rodmappen af denne repo. Hjemmesiden vil blive serveret på port 3000 på din localhost: `localhost:3000`.
Du kan køre denne dokumentation offline ved at bruge [Docsify](https://docsify.js.org/#/). Fork dette repo, [installer Docsify](https://docsify.js.org/#/quickstart) på din lokale maskine, og skriv derefter `docsify serve` i rodmappen af dette repo. Webstedet vil blive serveret på port 3000 på din lokale computer: `localhost:3000`.
## 📘 PDF
En PDF af alle lektionerne kan findes [her](https://microsoft.github.io/Web-Dev-For-Beginners/pdf/readme.pdf).
En PDF med alle lektionerne kan findes [her](https://microsoft.github.io/Web-Dev-For-Beginners/pdf/readme.pdf).
## 🎒 Andre kurser
Vores team producerer andre kurser! Tjek:
Vores team producerer andre kurser! Se dem her:
<!-- CO-OP TRANSLATOR OTHER COURSES START -->
### Azure / Edge / MCP / Agents
[![AZD for Beginners](https://img.shields.io/badge/AZD%20for%20Beginners-0078D4?style=for-the-badge&labelColor=E5E7EB&color=0078D4)](https://github.com/microsoft/AZD-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Edge AI for Beginners](https://img.shields.io/badge/Edge%20AI%20for%20Beginners-00B8E4?style=for-the-badge&labelColor=E5E7EB&color=00B8E4)](https://github.com/microsoft/edgeai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![MCP for Beginners](https://img.shields.io/badge/MCP%20for%20Beginners-009688?style=for-the-badge&labelColor=E5E7EB&color=009688)](https://github.com/microsoft/mcp-for-beginners?WT.mc_id=academic-105485-koreyst)
[![AI Agents for Beginners](https://img.shields.io/badge/AI%20Agents%20for%20Beginners-00C49A?style=for-the-badge&labelColor=E5E7EB&color=00C49A)](https://github.com/microsoft/ai-agents-for-beginners?WT.mc_id=academic-105485-koreyst)
### LangChain
[![LangChain4j for Beginners](https://img.shields.io/badge/LangChain4j%20for%20Beginners-22C55E?style=for-the-badge&&labelColor=E5E7EB&color=0553D6)](https://aka.ms/langchain4j-for-beginners)
[![LangChain.js for Beginners](https://img.shields.io/badge/LangChain.js%20for%20Beginners-22C55E?style=for-the-badge&labelColor=E5E7EB&color=0553D6)](https://aka.ms/langchainjs-for-beginners?WT.mc_id=m365-94501-dwahlin)
---
### Generativ AI-serie
[![Generative AI for Beginners](https://img.shields.io/badge/Generative%20AI%20for%20Beginners-8B5CF6?style=for-the-badge&labelColor=E5E7EB&color=8B5CF6)](https://github.com/microsoft/generative-ai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Generative AI (.NET)](https://img.shields.io/badge/Generative%20AI%20(.NET)-9333EA?style=for-the-badge&labelColor=E5E7EB&color=9333EA)](https://github.com/microsoft/Generative-AI-for-beginners-dotnet?WT.mc_id=academic-105485-koreyst)
[![Generative AI (Java)](https://img.shields.io/badge/Generative%20AI%20(Java)-C084FC?style=for-the-badge&labelColor=E5E7EB&color=C084FC)](https://github.com/microsoft/generative-ai-for-beginners-java?WT.mc_id=academic-105485-koreyst)
[![Generative AI (JavaScript)](https://img.shields.io/badge/Generative%20AI%20(JavaScript)-E879F9?style=for-the-badge&labelColor=E5E7EB&color=E879F9)](https://github.com/microsoft/generative-ai-with-javascript?WT.mc_id=academic-105485-koreyst)
### Azure / Edge / MCP / Agenter
[![AZD for Beginners](https://img.shields.io/badge/AZD%20for%20Beginners-0078D4?style=for-the-badge&labelColor=E5E7EB&color=0078D4)](https://github.com/microsoft/AZD-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Edge AI for Beginners](https://img.shields.io/badge/Edge%20AI%20for%20Beginners-00B8E4?style=for-the-badge&labelColor=E5E7EB&color=00B8E4)](https://github.com/microsoft/edgeai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![MCP for Beginners](https://img.shields.io/badge/MCP%20for%20Beginners-009688?style=for-the-badge&labelColor=E5E7EB&color=009688)](https://github.com/microsoft/mcp-for-beginners?WT.mc_id=academic-105485-koreyst)
[![AI Agents for Beginners](https://img.shields.io/badge/AI%20Agents%20for%20Beginners-00C49A?style=for-the-badge&labelColor=E5E7EB&color=00C49A)](https://github.com/microsoft/ai-agents-for-beginners?WT.mc_id=academic-105485-koreyst)
---
### Grundlæggende læring
[![ML for Beginners](https://img.shields.io/badge/ML%20for%20Beginners-22C55E?style=for-the-badge&labelColor=E5E7EB&color=22C55E)](https://aka.ms/ml-beginners?WT.mc_id=academic-105485-koreyst)
[![Data Science for Beginners](https://img.shields.io/badge/Data%20Science%20for%20Beginners-84CC16?style=for-the-badge&labelColor=E5E7EB&color=84CC16)](https://aka.ms/datascience-beginners?WT.mc_id=academic-105485-koreyst)
[![AI for Beginners](https://img.shields.io/badge/AI%20for%20Beginners-A3E635?style=for-the-badge&labelColor=E5E7EB&color=A3E635)](https://aka.ms/ai-beginners?WT.mc_id=academic-105485-koreyst)
[![Cybersecurity for Beginners](https://img.shields.io/badge/Cybersecurity%20for%20Beginners-F97316?style=for-the-badge&labelColor=E5E7EB&color=F97316)](https://github.com/microsoft/Security-101?WT.mc_id=academic-96948-sayoung)
[![Web Dev for Beginners](https://img.shields.io/badge/Web%20Dev%20for%20Beginners-EC4899?style=for-the-badge&labelColor=E5E7EB&color=EC4899)](https://aka.ms/webdev-beginners?WT.mc_id=academic-105485-koreyst)
[![IoT for Beginners](https://img.shields.io/badge/IoT%20for%20Beginners-14B8A6?style=for-the-badge&labelColor=E5E7EB&color=14B8A6)](https://aka.ms/iot-beginners?WT.mc_id=academic-105485-koreyst)
[![XR Development for Beginners](https://img.shields.io/badge/XR%20Development%20for%20Beginners-38BDF8?style=for-the-badge&labelColor=E5E7EB&color=38BDF8)](https://github.com/microsoft/xr-development-for-beginners?WT.mc_id=academic-105485-koreyst)
### Generativ AI Serie
[![Generative AI for Beginners](https://img.shields.io/badge/Generative%20AI%20for%20Beginners-8B5CF6?style=for-the-badge&labelColor=E5E7EB&color=8B5CF6)](https://github.com/microsoft/generative-ai-for-beginners?WT.mc_id=academic-105485-koreyst)
[![Generative AI (.NET)](https://img.shields.io/badge/Generative%20AI%20(.NET)-9333EA?style=for-the-badge&labelColor=E5E7EB&color=9333EA)](https://github.com/microsoft/Generative-AI-for-beginners-dotnet?WT.mc_id=academic-105485-koreyst)
[![Generative AI (Java)](https://img.shields.io/badge/Generative%20AI%20(Java)-C084FC?style=for-the-badge&labelColor=E5E7EB&color=C084FC)](https://github.com/microsoft/generative-ai-for-beginners-java?WT.mc_id=academic-105485-koreyst)
[![Generative AI (JavaScript)](https://img.shields.io/badge/Generative%20AI%20(JavaScript)-E879F9?style=for-the-badge&labelColor=E5E7EB&color=E879F9)](https://github.com/microsoft/generative-ai-with-javascript?WT.mc_id=academic-105485-koreyst)
---
### Kernelæring
[![ML for Beginners](https://img.shields.io/badge/ML%20for%20Beginners-22C55E?style=for-the-badge&labelColor=E5E7EB&color=22C55E)](https://aka.ms/ml-beginners?WT.mc_id=academic-105485-koreyst)
[![Data Science for Beginners](https://img.shields.io/badge/Data%20Science%20for%20Beginners-84CC16?style=for-the-badge&labelColor=E5E7EB&color=84CC16)](https://aka.ms/datascience-beginners?WT.mc_id=academic-105485-koreyst)
[![AI for Beginners](https://img.shields.io/badge/AI%20for%20Beginners-A3E635?style=for-the-badge&labelColor=E5E7EB&color=A3E635)](https://aka.ms/ai-beginners?WT.mc_id=academic-105485-koreyst)
[![Cybersecurity for Beginners](https://img.shields.io/badge/Cybersecurity%20for%20Beginners-F97316?style=for-the-badge&labelColor=E5E7EB&color=F97316)](https://github.com/microsoft/Security-101?WT.mc_id=academic-96948-sayoung)
[![Web Dev for Beginners](https://img.shields.io/badge/Web%20Dev%20for%20Beginners-EC4899?style=for-the-badge&labelColor=E5E7EB&color=EC4899)](https://aka.ms/webdev-beginners?WT.mc_id=academic-105485-koreyst)
[![IoT for Beginners](https://img.shields.io/badge/IoT%20for%20Beginners-14B8A6?style=for-the-badge&labelColor=E5E7EB&color=14B8A6)](https://aka.ms/iot-beginners?WT.mc_id=academic-105485-koreyst)
[![XR Development for Beginners](https://img.shields.io/badge/XR%20Development%20for%20Beginners-38BDF8?style=for-the-badge&labelColor=E5E7EB&color=38BDF8)](https://github.com/microsoft/xr-development-for-beginners?WT.mc_id=academic-105485-koreyst)
### Copilot-serie
[![Copilot for AI Paired Programming](https://img.shields.io/badge/Copilot%20for%20AI%20Paired%20Programming-FACC15?style=for-the-badge&labelColor=E5E7EB&color=FACC15)](https://aka.ms/GitHubCopilotAI?WT.mc_id=academic-105485-koreyst)
[![Copilot for C#/.NET](https://img.shields.io/badge/Copilot%20for%20C%23/.NET-FBBF24?style=for-the-badge&labelColor=E5E7EB&color=FBBF24)](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers?WT.mc_id=academic-105485-koreyst)
[![Copilot Adventure](https://img.shields.io/badge/Copilot%20Adventure-FDE68A?style=for-the-badge&labelColor=E5E7EB&color=FDE68A)](https://github.com/microsoft/CopilotAdventures?WT.mc_id=academic-105485-koreyst)
---
### Copilot Serie
[![Copilot for AI Paired Programming](https://img.shields.io/badge/Copilot%20for%20AI%20Paired%20Programming-FACC15?style=for-the-badge&labelColor=E5E7EB&color=FACC15)](https://aka.ms/GitHubCopilotAI?WT.mc_id=academic-105485-koreyst)
[![Copilot for C#/.NET](https://img.shields.io/badge/Copilot%20for%20C%23/.NET-FBBF24?style=for-the-badge&labelColor=E5E7EB&color=FBBF24)](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers?WT.mc_id=academic-105485-koreyst)
[![Copilot Adventure](https://img.shields.io/badge/Copilot%20Adventure-FDE68A?style=for-the-badge&labelColor=E5E7EB&color=FDE68A)](https://github.com/microsoft/CopilotAdventures?WT.mc_id=academic-105485-koreyst)
<!-- CO-OP TRANSLATOR OTHER COURSES END -->
## Få hjælp
## Få hjælp
Hvis du sidder fast eller har spørgsmål om at bygge AI-apps, kan du deltage i:
Hvis du sidder fast eller har spørgsmål om at bygge AI-apps. Deltag med andre elever og erfarne udviklere i diskussioner om MCP. Det er et støttende fællesskab, hvor spørgsmål er velkomne, og viden deles frit.
[![Azure AI Foundry Discord](https://img.shields.io/badge/Discord-Azure_AI_Foundry_Community_Discord-blue?style=for-the-badge&logo=discord&color=5865f2&logoColor=fff)](https://aka.ms/foundry/discord)
[![Microsoft Foundry Discord](https://dcbadge.limes.pink/api/server/nTYy5BXMWG)](https://discord.gg/nTYy5BXMWG)
Hvis du har produktfeedback eller støder på fejl under udviklingen, besøg:
Hvis du har produktfeedback eller fejl under udviklingen, besøg:
[![Azure AI Foundry Developer Forum](https://img.shields.io/badge/GitHub-Azure_AI_Foundry_Developer_Forum-blue?style=for-the-badge&logo=github&color=000000&logoColor=fff)](https://aka.ms/foundry/forum)
[![Microsoft Foundry Developer Forum](https://img.shields.io/badge/GitHub-Microsoft_Foundry_Developer_Forum-blue?style=for-the-badge&logo=github&color=000000&logoColor=fff)](https://aka.ms/foundry/forum)
## Licens
## Licens
Dette repository er licenseret under MIT-licensen. Se [LICENSE](../../LICENSE)-filen for mere information.
Dette repository er licenseret under MIT-licensen. Se [LICENSE](../../LICENSE)-filen for mere information.
---
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi er ikke ansvarlige for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**Ansvarsfraskrivelse**:
Dette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi stræber efter nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets modersmål bør betragtes som den autoritative kilde. For kritiske oplysninger anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
Loading…
Cancel
Save