From c07df985f1466751eff45c881c3ac5460dcbdb22 Mon Sep 17 00:00:00 2001 From: Travis Briggs Date: Mon, 22 Jul 2024 18:57:50 -0700 Subject: [PATCH 01/33] Update article.md Remove non-inclusive language. --- 1-js/01-getting-started/1-intro/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 2f4f518f3..a58d5d725 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -73,7 +73,7 @@ Examples of such restrictions include: This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and must contain special JavaScript code that handles it. We'll cover that in the tutorial. This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com`, for example, and steal information from there. -- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is crippled. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation. +- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is severely limited. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation. ![](limitations.svg) From 5f40ec4eff039a06a9886161b1bf5b0b45260e2f Mon Sep 17 00:00:00 2001 From: ockley Date: Fri, 6 Mar 2026 13:13:33 +0100 Subject: [PATCH 02/33] Oversat til dansk --- .../2-dictionary-tostring/solution.md | 20 +-- .../2-dictionary-tostring/task.md | 20 +-- .../3-compare-calls/solution.md | 8 +- .../3-compare-calls/task.md | 8 +- .../04-prototype-methods/article.md | 160 +++++++++--------- 5 files changed, 108 insertions(+), 108 deletions(-) diff --git a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md index f3c9cf0e5..234388dfb 100644 --- a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md +++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md @@ -1,31 +1,31 @@ -The method can take all enumerable keys using `Object.keys` and output their list. +Metoden kan tage alle enumerable (tælbare) nøgler ved hjælp af `Object.keys` og outputte deres liste. -To make `toString` non-enumerable, let's define it using a property descriptor. The syntax of `Object.create` allows us to provide an object with property descriptors as the second argument. +For at gøre `toString` non-enumerable, lad os definere den ved hjælp af en egenskabsbeskrivelse. Syntaksen for `Object.create` giver os mulighed for at give et objekt med egenskabsbeskrivelser som anden argument. ```js run *!* let dictionary = Object.create(null, { - toString: { // define toString property - value() { // the value is a function + toString: { // definer egenskaben toString + value() { // value er en funktion return Object.keys(this).join(); } } }); */!* -dictionary.apple = "Apple"; +dictionary.apple = "Æble"; dictionary.__proto__ = "test"; -// apple and __proto__ is in the loop +// Kun apple og __proto__ er i loopet for(let key in dictionary) { - alert(key); // "apple", then "__proto__" + alert(key); // "apple" og "__proto__" } -// comma-separated list of properties by toString +// kommasepareret liste af egenskaber fra toString alert(dictionary); // "apple,__proto__" ``` -When we create a property using a descriptor, its flags are `false` by default. So in the code above, `dictionary.toString` is non-enumerable. +Når vi opretter en egenskab ved hjælp af en egenskabsbeskrivelser, er dens flag som standard sat til `false`. Så i koden ovenfor er `dictionary.toString` non-enumerable. -See the chapter [](info:property-descriptors) for review. +Se kapitlet [](info:property-descriptors) for en gennemgang. diff --git a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md index 0d831f2cc..82521674e 100644 --- a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md +++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md @@ -2,30 +2,30 @@ importance: 5 --- -# Add toString to the dictionary +# Tilføj toString til ordbogen -There's an object `dictionary`, created as `Object.create(null)`, to store any `key/value` pairs. +Der er et objekt `dictionary`, oprettet som `Object.create(null)`, til at gemme enhver `key/value` par. -Add method `dictionary.toString()` into it, that should return a comma-delimited list of keys. Your `toString` should not show up in `for..in` over the object. +Tilføj metoden `dictionary.toString()` til det, som skal returnere en komma-separeret liste af nøgler. Din `toString` bør ikke vises i `for..in` over objektet. -Here's how it should work: +Den bør virke sådan her: ```js let dictionary = Object.create(null); *!* -// your code to add dictionary.toString method +// din kode til at tilføje dictionary.toString metoden */!* -// add some data -dictionary.apple = "Apple"; +// tilføj nogle data +dictionary.apple = "æble"; dictionary.__proto__ = "test"; // __proto__ is a regular property key here -// only apple and __proto__ are in the loop +// Kun apple og __proto__ er i loopet for(let key in dictionary) { - alert(key); // "apple", then "__proto__" + alert(key); // "apple" og "__proto__" } -// your toString in action +// din toString i aktion alert(dictionary); // "apple,__proto__" ``` diff --git a/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md index 90d3118bf..995c79f2c 100644 --- a/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md +++ b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md @@ -1,7 +1,7 @@ -The first call has `this == rabbit`, the other ones have `this` equal to `Rabbit.prototype`, because it's actually the object before the dot. +Det første kald har `this == rabbit`, de andre har `this` lig med `Rabbit.prototype`, fordi det faktisk er objektet før punktummet. -So only the first call shows `Rabbit`, other ones show `undefined`: +Så kun det første kald viser `Rabbit`, andre viser `undefined`: ```js run function Rabbit(name) { @@ -11,9 +11,9 @@ Rabbit.prototype.sayHi = function() { alert( this.name ); } -let rabbit = new Rabbit("Rabbit"); +let rabbit = new Rabbit("Kanin"); -rabbit.sayHi(); // Rabbit +rabbit.sayHi(); // Kanin Rabbit.prototype.sayHi(); // undefined Object.getPrototypeOf(rabbit).sayHi(); // undefined rabbit.__proto__.sayHi(); // undefined diff --git a/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md index 09bb7f1ed..672883d46 100644 --- a/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md +++ b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# The difference between calls +# Forskellen mellem kald -Let's create a new `rabbit` object: +Lad os oprette et nyt `rabbit` objekt: ```js function Rabbit(name) { @@ -14,10 +14,10 @@ Rabbit.prototype.sayHi = function() { alert(this.name); }; -let rabbit = new Rabbit("Rabbit"); +let rabbit = new Rabbit("Kanin"); ``` -These calls do the same thing or not? +Gør disse kald det samme eller gør de ikke? ```js rabbit.sayHi(); diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index 9c5f1eb3d..3b54e6a91 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -1,31 +1,31 @@ -# Prototype methods, objects without __proto__ +# Prototype metoder, objekter uden __proto__ -In the first chapter of this section, we mentioned that there are modern methods to setup a prototype. +I det første kapitel i denne sektion nævnte vi, at der er moderne metoder til at sætte en prototype. -Setting or reading the prototype with `obj.__proto__` is considered outdated and somewhat deprecated (moved to the so-called "Annex B" of the JavaScript standard, meant for browsers only). +At sætte eller læse en prototype med `obj.__proto__` er betragtet som forældet og noget deprecated (flyttet til såkaldte "Annex B" i JavaScript-standarden, kun tilgængelig i browser-motorer). -The modern methods to get/set a prototype are: +De moderne metoder til at få/sætte en prototype er: -- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`. -- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto`. +- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returnerer `[[Prototype]]` af `obj`. +- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sætter `[[Prototype]]` af `obj` til `proto`. -The only usage of `__proto__`, that's not frowned upon, is as a property when creating a new object: `{ __proto__: ... }`. +Den eneste brug af `__proto__`, der ikke rynkes på panden af er, som en en egenskab når der oprettes et nyt objekt: `{ __proto__: ... }`. -Although, there's a special method for this too: +Selvom der egentlig er en speciel metode for dette også: -- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors. +- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- opretter et tomt objekt med given `proto` som `[[Prototype]]` og valgfrie beskrivelser af egenskaber. -For instance: +For eksempel for at oprette et objekt med `animal` som prototype, kan vi bruge: ```js run let animal = { eats: true }; -// create a new object with animal as a prototype +// opret et nyt objekt med animal som prototype *!* -let rabbit = Object.create(animal); // same as {__proto__: animal} +let rabbit = Object.create(animal); // samme som {__proto__: animal} */!* alert(rabbit.eats); // true @@ -35,13 +35,13 @@ alert(Object.getPrototypeOf(rabbit) === animal); // true */!* *!* -Object.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {} +Object.setPrototypeOf(rabbit, {}); // Ændring af rabbits prototype til {} */!* ``` -The `Object.create` method is a bit more powerful, as it has an optional second argument: property descriptors. +Metoden `Object.create` er lidt mere kraftfuld, da den giver mulighed for at tilføje egenskaber til det nye objekt. Det er en valgfri anden parameter: egenskabsbeskrivelser. -We can provide additional properties to the new object there, like this: +Vi kan tilføje yderligere egenskaber til det nye objekt der, som dette: ```js run let animal = { @@ -57,9 +57,9 @@ let rabbit = Object.create(animal, { alert(rabbit.jumps); // true ``` -The descriptors are in the same format as described in the chapter . +Muligheden for beskrivelser er det samme format som beskrevet i kapitlet . -We can use `Object.create` to perform an object cloning more powerful than copying properties in `for..in`: +Vi kan bruge `Object.create` til at udføre en kloning af et objekt der er mere effektiv end at kopiere de enkelte egenskaber i et `for..in` loop: ```js let clone = Object.create( @@ -67,125 +67,125 @@ let clone = Object.create( ); ``` -This call makes a truly exact copy of `obj`, including all properties: enumerable and non-enumerable, data properties and setters/getters -- everything, and with the right `[[Prototype]]`. +Dette kald laver en helt præcis kopi af `obj`, inklusiv alle egenskaber: både enumerable og ikke-enumerable, data egenskaber og setters/getters -- alt, og med den rigtige `[[Prototype]]`. -## Brief history +## Et lille blik på historien bag -There're so many ways to manage `[[Prototype]]`. How did that happen? Why? +Der er SÅ mange måder at håndtere `[[Prototype]]`. Hvordan kunne det dog komme dertil? -That's for historical reasons. +Det er ganske enkelt en række historiske årsdager. -The prototypal inheritance was in the language since its dawn, but the ways to manage it evolved over time. +Nedarvning ved prototype har været muligt siden sprogets start, men måderne at håndtere den har udviklet sig over tid. -- The `prototype` property of a constructor function has worked since very ancient times. It's the oldest way to create objects with a given prototype. -- Later, in the year 2012, `Object.create` appeared in the standard. It gave the ability to create objects with a given prototype, but did not provide the ability to get/set it. Some browsers implemented the non-standard `__proto__` accessor that allowed the user to get/set a prototype at any time, to give more flexibility to developers. -- Later, in the year 2015, `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, to perform the same functionality as `__proto__`. As `__proto__` was de-facto implemented everywhere, it was kind-of deprecated and made its way to the Annex B of the standard, that is: optional for non-browser environments. -- Later, in the year 2022, it was officially allowed to use `__proto__` in object literals `{...}` (moved out of Annex B), but not as a getter/setter `obj.__proto__` (still in Annex B). +- Egenskaben `prototype` som led i en konstruktørfunktion har virket helt fra begyndelsen. Det er den ældste måde at oprette objekter med en given prototype. +- Senere, i 2012, blev `Object.create` vedtaget som standard. Den gav mulighed for at oprette objekter med en given prototype, men gav ikke mulighed for at bruge get/set på den. Nogle browsere implementerede den ikke-standard `__proto__` tilgang, som tillod brugeren at bruge get/set på en prototype uden for konstruktøren, for at give mere fleksibilitet til udviklere. +- Senere, i 2015, blev `Object.setPrototypeOf` og `Object.getPrototypeOf` tilføjet til standarden, for at udføre samme funktionalitet som `__proto__`. Da `__proto__` var de-facto implementeret overalt, blev det delvis deprecated og bragt til Annex B af standarden, altså valgfrit for ikke-browser miljøer. +- Senere, i 2022, blev det officielt tilladt at bruge `__proto__` i objekt-literaler `{...}` (flyttet ud af Annex B), men ikke som en getter/setter `obj.__proto__` (stadig i Annex B). -Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? +Hvorfor var `__proto__` erstattet af funktionerne `getPrototypeOf/setPrototypeOf`? -Why was `__proto__` partially rehabilitated and its usage allowed in `{...}`, but not as a getter/setter? +Hvorfor var `__proto__` delvist genindført så det er tilladt i objekt-literaler `{...}`, men ikke som en getter/setter `obj.__proto__`? -That's an interesting question, requiring us to understand why `__proto__` is bad. +Det er et interessant spørgsmål, der kræver, at vi forstår hvorfor `__proto__` er dårlig. -And soon we'll get the answer. +Og snart vil vi få svaret. -```warn header="Don't change `[[Prototype]]` on existing objects if speed matters" -Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time and don't modify it anymore: `rabbit` inherits from `animal`, and that is not going to change. +```warn header="Lad være med at ændre `[[Prototype]]` på eksisterende objekter hvis hastighed er vigtig" +Teknisk set kan vi oprette can get/set på en `[[Prototype]]` når som helst. Men i praksis sker det næsten altid kun ved oprettelsen af objektet. Ofte sætter vi kun `[[Prototype]]` én gang ved oprettelsen af objektet og undlader efterfølgende at ændre den ikke igen: `rabbit` nedarver fra `animal`, og det ændres ikke. -And JavaScript engines are highly optimized for this. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation as it breaks internal optimizations for object property access operations. So avoid it unless you know what you're doing, or JavaScript speed totally doesn't matter for you. +JavaScript-motorer er højst optimerede til dette. Ændring af en prototype "on-the-fly" med `Object.setPrototypeOf` eller `obj.__proto__=` er en meget langsom operation, da den bryder interne optimeringer for objektegenskabstilgange. Undgå derfor at ændre prototypen, medmindre du ved hvad du gør, eller JavaScript-hastighed ikke spiller en rolle for dig. ``` -## "Very plain" objects [#very-plain] +## "Meget simple" objekter [#very-plain] -As we know, objects can be used as associative arrays to store key/value pairs. +Som vi ved kan objekter bruges som associative arrays til at gemme nøgle/værdi-par. -...But if we try to store *user-provided* keys in it (for instance, a user-entered dictionary), we can see an interesting glitch: all keys work fine except `"__proto__"`. +...Men hvis vi forsøger at gemme *brugerdefinerede* nøgler i det (for eksempel en brugerdefineret ordbog), kan vi se et interessant glitch: alle nøgler virker fint, bortset fra `"__proto__"`. -Check out the example: +Så for eksempel nedenfor: ```js run let obj = {}; -let key = prompt("What's the key?", "__proto__"); -obj[key] = "some value"; +let key = prompt("Hvad er nøglen?", "__proto__"); +obj[key] = "en værdi"; -alert(obj[key]); // [object Object], not "some value"! +alert(obj[key]); // [object Object], ikke "en værdi"! ``` -Here, if the user types in `__proto__`, the assignment in line 4 is ignored! +Her vil linje 4 ignoreres, hvis brugeren skriver `__proto__` i prompten! -That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why assigning a string to `__proto__` is ignored. +Det er umiddelbart overraskende, men egentlig meget logisk, når vi husker på at egenskaben `__proto__` er lidt speciel: Den skal enten være et objekt eller `null`. En streng kan ikke blive en prototype. Derfor ignoreres tildeling af en streng til `__proto__`. -But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! +Det var var jo ikke vores *plan* at den skulle opføre sig på den måde, vel? Vi vil gerne gemme nøgle/værdi-par, og nøglen kaldt `"__proto__"` blev ikke korrekt gemt. Så det er en fejl! -Here the consequences are not terrible. But in other cases we may be storing objects instead of strings in `obj`, and then the prototype will indeed be changed. As a result, the execution will go wrong in totally unexpected ways. +Her er konsekvenserne ikke så farlige. Men i andre tilfælde kan vi gemme objekter i stedet for strenge i `obj`, og så vil prototypen faktisk blive ændret. Som resultat vil eksekveringen gå galt på helt uventede måder. -What's worse -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side. +Det værre -- ofte tænker udviklere ikke over denne mulighed overhovedet. Det gør sådanne fejl svære at opdage og endda omvandles til sikkerhedsproblemer, især når JavaScript bruges på server-side. -Unexpected things also may happen when assigning to `obj.toString`, as it's a built-in object method. +Uventede ting kan også ske når vi tildeler til `obj.toString`, da det er en indbygget objektmetode. -How can we avoid this problem? +Hvordan kan vi undgå dette problem? -First, we can just switch to using `Map` for storage instead of plain objects, then everything's fine: +Først og fremmest kan vi skifte til at bruge `Map` for lagring i stedet for almindelige objekter, så alt er i orden: ```js run let map = new Map(); -let key = prompt("What's the key?", "__proto__"); -map.set(key, "some value"); +let key = prompt("Hvad er nøglen?", "__proto__"); +map.set(key, "en værdi"); -alert(map.get(key)); // "some value" (as intended) +alert(map.get(key)); // "en værdi" (som det var meningen) ``` -...But `Object` syntax is often more appealing, as it's more concise. +...Men `Object` syntaksen appelerer mere til de fleste - og den er kortere. -Fortunately, we *can* use objects, because language creators gave thought to that problem long ago. +Heldigvis *kan* vi bruge objekter fordi udviklerne af sproget har tænkt over dette problem for længe siden. -As we know, `__proto__` is not a property of an object, but an accessor property of `Object.prototype`: +Som vi ved er `__proto__` ikke i sig selv en egenskab af et objekt, men en accessor-egenskab af `Object.prototype`: ![](object-prototype-2.svg) -So, if `obj.__proto__` is read or set, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. +Så, hvis `obj.__proto__` læses eller sættes vil dens respektive getter/setter kaldes fra dens prototype, som så får eller sætter værdien af `[[Prototype]]`. -As it was said in the beginning of this tutorial section: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. +Som det blev sagt i begyndelsen: `__proto__` er en måde at tilgå `[[Prototype]]`, det er ikke selve objektet `[[Prototype]]`. -Now, if we intend to use an object as an associative array and be free of such problems, we can do it with a little trick: +Så, hvis vi planlægger at bruge et objekt som et associativt array og være fri for sådanne problemer, kan vi gøre det med et lille trick: ```js run *!* let obj = Object.create(null); -// or: obj = { __proto__: null } +// eller: obj = { __proto__: null } */!* -let key = prompt("What's the key?", "__proto__"); -obj[key] = "some value"; +let key = prompt("Hvad er nøglen?", "__proto__"); +obj[key] = "en værdi"; -alert(obj[key]); // "some value" +alert(obj[key]); // "en værdi" ``` -`Object.create(null)` creates an empty object without a prototype (`[[Prototype]]` is `null`): +`Object.create(null)` opretter et tomt objekt uden en prototype (`[[Prototype]]` er `null`): ![](object-prototype-null.svg) -So, there is no inherited getter/setter for `__proto__`. Now it is processed as a regular data property, so the example above works right. +På denne måde er der ingen arvede getter/setter for `__proto__`. Nu bliver det behandlet som en almindelig dataegenskab, så eksemplet ovenfor fungerer korrekt. -We can call such objects "very plain" or "pure dictionary" objects, because they are even simpler than the regular plain object `{...}`. +Vi kan kalde sådanne objekter "helt rene", fordi de er endnu mere simple end regulære rene objekter `{...}`. -A downside is that such objects lack any built-in object methods, e.g. `toString`: +En ulempe er, at sådanne objekter mangler alle indbyggede objektmetoder, f.eks. `toString`: ```js run *!* let obj = Object.create(null); */!* -alert(obj); // Error (no toString) +alert(obj); // Fejl (ingen toString) ``` -...But that's usually fine for associative arrays. +...men det er normalt ikke et problem for associative arrays. -Note that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects: +Bemærk, at de fleste objektrelaterede metoder er `Object.something(...)`, som `Object.keys(obj)` -- de er ikke i prototype, så de vil fortsat fungere på denne slags objekter: ```js run @@ -196,28 +196,28 @@ chineseDictionary.bye = "再见"; alert(Object.keys(chineseDictionary)); // hello,bye ``` -## Summary +## Opsummering -- To create an object with the given prototype, use: +- For at oprette et objekt med den givne prototype, brug: - - literal syntax: `{ __proto__: ... }`, allows to specify multiple properties - - or [Object.create(proto[, descriptors])](mdn:js/Object/create), allows to specify property descriptors. + - literal syntax: `{ __proto__: ... }`, tillader at specificere flere egenskaber + - eller [Object.create(proto[, descriptors])](mdn:js/Object/create), tillader at specificere egenskabsbeskrivelser. - The `Object.create` provides an easy way to shallow-copy an object with all descriptors: + `Object.create` giver en let måde at lave en flad kopi af et objekt med alle beskrivelser: ```js let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); ``` -- Modern methods to get/set the prototype are: +- Moderne metoder til at få eller sætte en prototype er: - - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). - - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). + - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returnerer `[[Prototype]]` of `obj` (samme som `__proto__` getter). + - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sætter `[[Prototype]]` of `obj` til `proto` (samme som `__proto__` setter). -- Getting/setting the prototype using the built-in `__proto__` getter/setter isn't recommended, it's now in the Annex B of the specification. +- At få eller sætte en prototype ved hjælp af de indbyggede `__proto__` getter/setter anbefales ikke, da det nu er i Annex B af specifikationen. -- We also covered prototype-less objects, created with `Object.create(null)` or `{__proto__: null}`. +- Vi dækkede også objekter uden prototype, oprettet med `Object.create(null)` eller `{__proto__: null}`. - These objects are used as dictionaries, to store any (possibly user-generated) keys. + Disse objekter bruges som "dictionary" (ordbøger) til at gemme enhver (muligvis bruger-genereret) nøgle. - Normally, objects inherit built-in methods and `__proto__` getter/setter from `Object.prototype`, making corresponding keys "occupied" and potentially causing side effects. With `null` prototype, objects are truly empty. + Normalt arver objekter indbyggede metoder og `__proto__` getter/setter fra `Object.prototype`, hvilket gør de tilsvarende nøgler "optaget" og potentielt forårsager sideeffekter. Med `null` prototype er objekterne virkelig tomme. From d7531fecfbc385f56980738b2229392d8ae8fe1c Mon Sep 17 00:00:00 2001 From: ockley Date: Fri, 6 Mar 2026 14:04:05 +0100 Subject: [PATCH 03/33] oversat til dansk --- .../01-class/1-rewrite-to-class/task.md | 6 +- 1-js/09-classes/01-class/article.md | 236 +++++++++--------- 1-js/09-classes/index.md | 2 +- 3 files changed, 122 insertions(+), 122 deletions(-) diff --git a/1-js/09-classes/01-class/1-rewrite-to-class/task.md b/1-js/09-classes/01-class/1-rewrite-to-class/task.md index 4477de679..5c7012767 100644 --- a/1-js/09-classes/01-class/1-rewrite-to-class/task.md +++ b/1-js/09-classes/01-class/1-rewrite-to-class/task.md @@ -2,8 +2,8 @@ importance: 5 --- -# Rewrite to class +# Omskriv til en klasse -The `Clock` class (see the sandbox) is written in functional style. Rewrite it in the "class" syntax. +Klassen `Clock` (se i sandkassen) er skrevet med funktioner. Omskriv den til "class" syntaks. -P.S. The clock ticks in the console, open it to see. +P.S. Klokken tikker i konsollen, åbn den for at se. diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md index 135d24929..c0501fe7a 100644 --- a/1-js/09-classes/01-class/article.md +++ b/1-js/09-classes/01-class/article.md @@ -1,19 +1,19 @@ -# Class basic syntax +# Klasser: Grundlæggende syntaks ```quote author="Wikipedia" -In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). +I objekt-orienteret programmering, er en *klasse* (class) en udvidelig program-kodetemplate til at skabe objekter, der giver initiale værdier for tilstand (medlemsvariable) og implementeringer af adfærd (medlemsfunktioner eller metoder). ``` -In practice, we often need to create many objects of the same kind, like users, or goods or whatever. +I praksis har vi ofte brug for at skabe mange objekter af samme type, som f.eks. brugere eller varer eller hvad som helst. -As we already know from the chapter , `new function` can help with that. +Som vi allerede ved fra kapitlet , kan `new function` hjælpe med det. -But in the modern JavaScript, there's a more advanced "class" construct, that introduces great new features which are useful for object-oriented programming. +Men i moderne JavaScript, er der en mere avanceret "class" konstruktion, der introducerer store nye funktioner, som er nyttige for objekt-orienteret programmering. -## The "class" syntax +## Syntaks for "class" -The basic syntax is: +Den grundlæggende syntaks er: ```js class MyClass { // class methods @@ -25,11 +25,11 @@ class MyClass { } ``` -Then use `new MyClass()` to create a new object with all the listed methods. +Derefter bruges `new MyClass()` til at oprette et nyt objekt med alle de opførte metoder. -The `constructor()` method is called automatically by `new`, so we can initialize the object there. +Metoden `constructor()` kaldes automatisk af `new`, så vi kan initialisere objektet der. -For example: +For eksempel: ```js run class User { @@ -45,32 +45,32 @@ class User { } // Usage: -let user = new User("John"); +let user = new User("Karsten"); user.sayHi(); ``` -When `new User("John")` is called: -1. A new object is created. -2. The `constructor` runs with the given argument and assigns it to `this.name`. +Når `new User("Karsten")` kaldes, sker der følgende: +1. Et nyt objekt oprettes. +2. Metoden `constructor` kører med det givne argument og tildeler det til `this.name`. -...Then we can call object methods, such as `user.sayHi()`. +...efter det kan vi kalde objektets metoder, såsom `user.sayHi()`. -```warn header="No comma between class methods" -A common pitfall for novice developers is to put a comma between class methods, which would result in a syntax error. +```warn header="Ingen komma mellem class metoder" +En kendt fejl i begyndelsen er at putte kommaer mellem class metoder - det vil føre tilen syntaksfejl. -The notation here is not to be confused with object literals. Within the class, no commas are required. +Den notation her er ikke at forveksle med object literals. Inden for class, er kommaer ikke nødvendige. ``` -## What is a class? +## Hvad er en class? -So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think. +Så, hvad er `class` egentlig for en størrelse? Det er faktisk ikke så nyt et koncept på sprogniveau, som man måske skulle forestille sig. -Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects. +Lad os prøve at pakke det ud lidt ad gangen. Det vil hjælpe med at forstå de mere komplekse aspekter. -In JavaScript, a class is a kind of function. +I JavaScript er en klasse en slags funktion. -Here, take a look: +Lad os se på følgende: ```js run class User { @@ -78,24 +78,24 @@ class User { sayHi() { alert(this.name); } } -// proof: User is a function +// Bevis: User er en function *!* alert(typeof User); // function */!* ``` -What `class User {...}` construct really does is: +Det konstruktionen `class User {...}` i virkeligheden gør er: -1. Creates a function named `User`, that becomes the result of the class declaration. The function code is taken from the `constructor` method (assumed empty if we don't write such method). -2. Stores class methods, such as `sayHi`, in `User.prototype`. +1. Opretter en funktion kaldet `User`, som bliver resultatet af class-deklarationen. Funktionens kode bliver taget fra `constructor`-metoden (antaget tom, hvis vi ikke skriver en sådan). +2. Gemmer class-metoder, såsom `sayHi`, i `User.prototype`. -After `new User` object is created, when we call its method, it's taken from the prototype, just as described in the chapter . So the object has access to class methods. +Efter at et `new User`-objekt er oprettet, når vi kalder dens metode, bliver den taget fra prototype, ligesom beskrevet i kapitlet . Så har objektet adgang til class-metoder. -We can illustrate the result of `class User` declaration as: +Vi kan illustrere resultatet af `class User`-deklarationen som: ![](class-user.svg) -Here's the code to introspect it: +Her er koden til at kigge nærmere på det: ```js run class User { @@ -103,50 +103,50 @@ class User { sayHi() { alert(this.name); } } -// class is a function +// class er en function alert(typeof User); // function -// ...or, more precisely, the constructor method +// ...eller, mere præcist, konstruktøren alert(User === User.prototype.constructor); // true -// The methods are in User.prototype, e.g: -alert(User.prototype.sayHi); // the code of the sayHi method +// Metoderne findes i User.prototype, f. eks. sayHi: +alert(User.prototype.sayHi); // her er koden for metoden sayHi -// there are exactly two methods in the prototype +// der er præcis to metoder i prototypen alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi ``` -## Not just a syntactic sugar +## Det er ikke kun en syntactic sugar -Sometimes people say that `class` is a "syntactic sugar" (syntax that is designed to make things easier to read, but doesn't introduce anything new), because we could actually declare the same thing without using the `class` keyword at all: +Du vil høre folk sige at `class` er en "syntactic sugar" (syntaks der er designet til at gøre ting lettere at læse, men ikke introducerer noget nyt), fordi vi faktisk kunne erklære det samme uden at bruge `class`-nøgleordet: ```js run -// rewriting class User in pure functions +// omskrivning af class User til kun at bruge funktioner -// 1. Create constructor function +// 1. Opret constructor function User(name) { this.name = name; } -// a function prototype has "constructor" property by default, -// so we don't need to create it +// En function prototype har egenskaben "constructor" som standard, +// så vi behøver ikke at oprette den -// 2. Add the method to prototype +// 2. Tilføj metoden til prototype User.prototype.sayHi = function() { alert(this.name); }; -// Usage: -let user = new User("John"); +// Brug: +let user = new User("Karsten"); user.sayHi(); ``` -The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntactic sugar to define a constructor together with its prototype methods. +Resultatet af denne definition er omkring det samme. Så, der er faktisk grunde til at `class` kan betragtes som en syntactic sugar til at definere en constructor sammen med dens prototype metoder. -Still, there are important differences. +Alligevel, der er vigtige forskelle. -1. First, a function created by `class` is labelled by a special internal property `[[IsClassConstructor]]: true`. So it's not entirely the same as creating it manually. +1. Først, en funktion oprettet af `class` er mærket med en speciel intern egenskab `[[IsClassConstructor]]: true`. Så det er ikke helt det samme som at oprette den manuelt. - The language checks for that property in a variety of places. For example, unlike a regular function, it must be called with `new`: + SProget tjekker for denne egenskab i en række sammenhænge. For eksempel, i modsætning til en almindelig funktion, skal den kaldes med `new`: ```js run class User { @@ -157,7 +157,7 @@ Still, there are important differences. User(); // Error: Class constructor User cannot be invoked without 'new' ``` - Also, a string representation of a class constructor in most JavaScript engines starts with the "class..." + Derudover vil en repræsentation af klassen som en streng i de fleste JavaScript-motorer starte med "class..." ```js run class User { @@ -166,23 +166,23 @@ Still, there are important differences. alert(User); // class User { ... } ``` - There are other differences, we'll see them soon. + Der er også andre forskelle som vi snart vil se. -2. Class methods are non-enumerable. - A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`. +2. Class metoder er ikke-enumerable. + En class definition sætter flaget `enumerable` til `false` for alle metoder i dens `"prototype"`. - That's good, because if we `for..in` over an object, we usually don't want its class methods. + Det er godt, fordi hvis vi bruger `for..in` på et objekt, vil vi normalt ikke vil have dets class metoder. -3. Classes always `use strict`. - All code inside the class construct is automatically in strict mode. +3. Class bruger altid `use strict`. + Al kode inde i en class konstruktion er automatisk sat til strict mode. -Besides, `class` syntax brings many other features that we'll explore later. +Der er også andre features ved `class` syntaksen som vi kigger på senere. ## Class Expression -Just like functions, classes can be defined inside another expression, passed around, returned, assigned, etc. +På samme måde som funktioner, kan classes defineres indeni en andet udtryk (expression), videregives, returneres, tildeles osv. -Here's an example of a class expression: +Her er et eksempel på en class expression: ```js let User = class { @@ -192,29 +192,29 @@ let User = class { }; ``` -Similar to Named Function Expressions, class expressions may have a name. +På samme måde som "Named Function Expressions", kan class udtryk også have et navn. -If a class expression has a name, it's visible inside the class only: +Hvis et class udtryk har et navn, er det kun synligt inden for klassen selv: ```js run // "Named Class Expression" -// (no such term in the spec, but that's similar to Named Function Expression) +// (der er ikke sådan et navn i specifikationen, men det er det samme som med Named Function Expression) let User = class *!*MyClass*/!* { sayHi() { - alert(MyClass); // MyClass name is visible only inside the class + alert(MyClass); // Navnet MyClass er kun synligt inden for klassen selv } }; -new User().sayHi(); // works, shows MyClass definition +new User().sayHi(); // virker, viser MyClass definition -alert(MyClass); // error, MyClass name isn't visible outside of the class +alert(MyClass); // fejl, navnet MyClass er ikke synligt uden for klassen ``` -We can even make classes dynamically "on-demand", like this: +Vi kan endda oprette klasser dynamisk "on-demand", som dette: ```js run function makeClass(phrase) { - // declare a class and return it + // deklarerer en klasse og returner den return class { sayHi() { alert(phrase); @@ -222,24 +222,24 @@ function makeClass(phrase) { }; } -// Create a new class -let User = makeClass("Hello"); +// Opret en ny klasse +let User = makeClass("Hej!"); -new User().sayHi(); // Hello +new User().sayHi(); // Hej! ``` ## Getters/setters -Just like literal objects, classes may include getters/setters, computed properties etc. +På samme måde som med objekter kan klasser have getters og setters, computed properties osv. -Here's an example for `user.name` implemented using `get/set`: +Her er et eksempel for `user.name` implementeret ved hjælp af `get/set`: ```js run class User { constructor(name) { - // invokes the setter + // aktiverer dens setter this.name = name; } @@ -253,7 +253,7 @@ class User { set name(value) { */!* if (value.length < 4) { - alert("Name is too short."); + alert("Navnet er for kort."); return; } this._name = value; @@ -261,17 +261,17 @@ class User { } -let user = new User("John"); -alert(user.name); // John +let user = new User("Karsten"); +alert(user.name); // Karsten -user = new User(""); // Name is too short. +user = new User("Bo"); // Navnet er for kort. ``` -Technically, such class declaration works by creating getters and setters in `User.prototype`. +Teknisk set virker sådanne deklarationer ved at oprette getter og setter i `User.prototype`. ## Computed names [...] -Here's an example with a computed method name using brackets `[...]`: +Her er et eksempel hvor navnet på metoden er udregnet ved hjælp af brackets `[...]`: ```js run class User { @@ -279,7 +279,7 @@ class User { *!* ['say' + 'Hi']() { */!* - alert("Hello"); + alert("Hej"); } } @@ -287,71 +287,71 @@ class User { new User().sayHi(); ``` -Such features are easy to remember, as they resemble that of literal objects. +Nogle muligheder er nemme at huske, da de minder om dem fra normale objekter. ## Class fields -```warn header="Old browsers may need a polyfill" -Class fields are a recent addition to the language. +```warn header="Ældre browsere kan have brug for et polyfill" +Class fields er en relativ ny feature i JavaScript. ``` -Previously, our classes only had methods. +Tidligere havde klasser kun metoder. -"Class fields" is a syntax that allows to add any properties. +"Class fields" er en syntaks der tillader os at tilføje vilkårlige egenskaber. -For instance, let's add `name` property to `class User`: +For eksempel, lad os tilføje egenskaben `name` til `class User`: ```js run class User { *!* - name = "John"; + name = "Karsten"; */!* sayHi() { - alert(`Hello, ${this.name}!`); + alert(`Hej ${this.name}!`); } } -new User().sayHi(); // Hello, John! +new User().sayHi(); // Hej Karsten! ``` -So, we just write " = " in the declaration, and that's it. +Så vi skriver simpelthen " = " i deklarationen, og det er det. -The important difference of class fields is that they are set on individual objects, not `User.prototype`: +En vigtig forskel mellem class fields og almindelige metoder er, at de er sat på individuelle objekter, ikke `User.prototype`: ```js run class User { *!* - name = "John"; + name = "Karsten"; */!* } let user = new User(); -alert(user.name); // John +alert(user.name); // Karsten alert(User.prototype.name); // undefined ``` -We can also assign values using more complex expressions and function calls: +Vi kan også tildele værdier ved hjælp af mere komplekse udtryk og funktionskald: ```js run class User { *!* - name = prompt("Name, please?", "John"); + name = prompt("Dit navn, tak?", "Karsten"); */!* } let user = new User(); -alert(user.name); // John +alert(user.name); // Karsten ``` -### Making bound methods with class fields +### Gør bundne metoder til class fields -As demonstrated in the chapter functions in JavaScript have a dynamic `this`. It depends on the context of the call. +Som demonstreret i kapitlet har funktioner i JavaScript en dynamisk `this`. Det afhænger af konteksten for kaldet. -So if an object method is passed around and called in another context, `this` won't be a reference to its object any more. +Så hvis en objektmetode bliver videregivet og kaldt i en anden kontekst, vil `this` ikke længere være en reference til objektet. -For instance, this code will show `undefined`: +For eksempel vil denne kode vise `undefined`: ```js run class Button { @@ -364,21 +364,21 @@ class Button { } } -let button = new Button("hello"); +let button = new Button("hej"); *!* setTimeout(button.click, 1000); // undefined */!* ``` -The problem is called "losing `this`". +Problemet kaldes populært at den "mister `this`" ("losing `this`"). -There are two approaches to fixing it, as discussed in the chapter : +Der er to tilgange til at fikse det, som vi diskuterede i kapitlet : -1. Pass a wrapper-function, such as `setTimeout(() => button.click(), 1000)`. -2. Bind the method to object, e.g. in the constructor. +1. Videregiv en wrapper-funktion, i stil med `setTimeout(() => button.click(), 1000)`. +2. Bind metoden til objektet, f.eks. i constructor. -Class fields provide another, quite elegant syntax: +Class fields leverer en anden, ganske elegant syntaks: ```js run class Button { @@ -392,37 +392,37 @@ class Button { */!* } -let button = new Button("hello"); +let button = new Button("hej"); -setTimeout(button.click, 1000); // hello +setTimeout(button.click, 1000); // hej ``` -The class field `click = () => {...}` is created on a per-object basis, there's a separate function for each `Button` object, with `this` inside it referencing that object. We can pass `button.click` around anywhere, and the value of `this` will always be correct. +class field `click = () => {...}` oprettes på individuelle objekter, så der er en seperat funktion for hver `Button`-objekt, med `this` inde i den, der refererer til det objekt. Vi kan overføre `button.click` hvor som helst, og værdien af `this` vil altid være korrekt. -That's especially useful in browser environment, for event listeners. +Det er især nyttigt i browser-miljøet, for event-listeners. -## Summary +## Opsummering -The basic class syntax looks like this: +Den grundlæggende syntaks for klasser ser sådan ud: ```js class MyClass { - prop = value; // property + prop = value; // egenskab (class field) constructor(...) { // constructor // ... } - method(...) {} // method + method(...) {} // metode - get something(...) {} // getter method - set something(...) {} // setter method + get something(...) {} // getter metode + set something(...) {} // setter metode - [Symbol.iterator]() {} // method with computed name (symbol here) + [Symbol.iterator]() {} // metode med beregnet navn (symbol her) // ... } ``` -`MyClass` is technically a function (the one that we provide as `constructor`), while methods, getters and setters are written to `MyClass.prototype`. +`MyClass` er teknisk set en function (den vi leverer som `constructor`), mens metoder, getters og setters skrives i `MyClass.prototype`. -In the next chapters we'll learn more about classes, including inheritance and other features. +I de næste kapitler vil vi lære mere om klasser, nedarvning og andre funktioner. diff --git a/1-js/09-classes/index.md b/1-js/09-classes/index.md index 87846ef6b..70a9e4cd4 100644 --- a/1-js/09-classes/index.md +++ b/1-js/09-classes/index.md @@ -1 +1 @@ -# Classes +# Klasser From eda3795e21f1934c4b5f104fe589062ddaff072d Mon Sep 17 00:00:00 2001 From: ockley Date: Sat, 7 Mar 2026 14:54:46 +0100 Subject: [PATCH 04/33] Oversat til dansk --- .../1-class-constructor-error/solution.md | 8 +- .../1-class-constructor-error/task.md | 8 +- .../source.view/index.html | 7 +- .../2-clock-class-extended/task.md | 10 +- .../animal-rabbit-extends.svg | 107 ++++- .../02-class-inheritance/article.md | 376 +++++++++--------- .../class-inheritance-rabbit-animal-2.svg | 90 ++++- .../rabbit-animal-independent-animal.svg | 63 ++- .../rabbit-animal-independent-rabbit.svg | 61 ++- 9 files changed, 521 insertions(+), 209 deletions(-) diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md index 4711e4827..cd5e25d7a 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md @@ -1,6 +1,6 @@ -That's because the child constructor must call `super()`. +Det er fordi at konstruktøren skal kalde `super()`. -Here's the corrected code: +Her er den tilrettede kode: ```js run class Animal { @@ -21,7 +21,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // ok now +let rabbit = new Rabbit("Hvid kanin"); // ok nu */!* -alert(rabbit.name); // White Rabbit +alert(rabbit.name); // Hvid kanin ``` diff --git a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md index 380a4720b..827a37175 100644 --- a/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md +++ b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md @@ -2,11 +2,11 @@ importance: 5 --- -# Error creating an instance +# Fejl ved oprettelse af instans -Here's the code with `Rabbit` extending `Animal`. +Her er koden hvor `Rabbit` udvider `Animal`. -Unfortunately, `Rabbit` objects can't be created. What's wrong? Fix it. +Uheldigvis kan `Rabbit`-objekter ikke oprettes. Hvad er galt? Ret det. ```js run class Animal { @@ -24,7 +24,7 @@ class Rabbit extends Animal { } *!* -let rabbit = new Rabbit("White Rabbit"); // Error: this is not defined +let rabbit = new Rabbit("Hvid kanin"); // Fejl: this is not defined */!* alert(rabbit.name); ``` diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html index c0609858b..b0c0ab456 100644 --- a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html @@ -1,13 +1,12 @@ - + From d82d85f9b938b51e94142ac0205321272ced9d78 Mon Sep 17 00:00:00 2001 From: ockley Date: Fri, 20 Mar 2026 15:51:22 +0100 Subject: [PATCH 12/33] oversat til dansk --- .../1-finally-or-code-after/solution.md | 24 +- .../1-finally-or-code-after/task.md | 22 +- 1-js/10-error-handling/1-try-catch/article.md | 391 +++++++++--------- .../1-try-catch/try-catch-flow.svg | 92 ++++- 4 files changed, 309 insertions(+), 220 deletions(-) diff --git a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md index ec0dabc9a..20f367344 100644 --- a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md +++ b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md @@ -1,47 +1,47 @@ -The difference becomes obvious when we look at the code inside a function. +Forskellen bliver tydelig, når vi kigger på koden inde i en funktion. -The behavior is different if there's a "jump out" of `try...catch`. +Det er forskelligt, hvis der er et "spring ud" af `try...catch`. -For instance, when there's a `return` inside `try...catch`. The `finally` clause works in case of *any* exit from `try...catch`, even via the `return` statement: right after `try...catch` is done, but before the calling code gets the control. +For eksempel, når der er en `return` inde i `try...catch`. `finally`-klausulen virker i tilfældet af *hvilken som helst* afslutning fra `try...catch`, selv via `return`-sætningen: lige efter `try...catch` er færdig, men før den kaldende kode får kontrollen. ```js run function f() { try { alert('start'); *!* - return "result"; + return "resultat"; */!* } catch (err) { /// ... } finally { - alert('cleanup!'); + alert('oprydning!'); } } -f(); // cleanup! +f(); // oprydning! ``` -...Or when there's a `throw`, like here: +...eller hvis der er en `throw`, som her: ```js run function f() { try { alert('start'); - throw new Error("an error"); + throw new Error("en fejl"); } catch (err) { // ... - if("can't handle the error") { + if("kan ikke håndtere fejlen") { *!* throw err; */!* } } finally { - alert('cleanup!') + alert('oprydning!') } } -f(); // cleanup! +f(); // oprydning! ``` -It's `finally` that guarantees the cleanup here. If we just put the code at the end of `f`, it wouldn't run in these situations. +Det er `finally` der garanterer oprydningen her. Hvis vi bare sætter koden ved slutningen af `f`, ville den ikke køre i disse situationer. diff --git a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md index b6dc81326..c5663cbac 100644 --- a/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md +++ b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md @@ -4,35 +4,35 @@ importance: 5 # Finally or just the code? -Compare the two code fragments. +Sammenlign disse to kodefragmenter. -1. The first one uses `finally` to execute the code after `try...catch`: +1. Den første bruger `finally` til at køre koden efter `try...catch`: ```js try { - work work + arbejd' arbejd' } catch (err) { - handle errors + håndter fejl } finally { *!* - cleanup the working space + ryd op på arbejdspladsen */!* } ``` -2. The second fragment puts the cleaning right after `try...catch`: +2. Den anden placerer oprydningen lige efter `try...catch`: ```js try { - work work + arbejd' arbejd' } catch (err) { - handle errors + håndter fejl } *!* - cleanup the working space + ryd op på arbejdspladsen */!* ``` -We definitely need the cleanup after the work, doesn't matter if there was an error or not. +Vi skal selvfølgelig have oprydningen efter arbejdet, uanset om der var en fejl eller ej. -Is there an advantage here in using `finally` or both code fragments are equal? If there is such an advantage, then give an example when it matters. +Er der en fordel i at bruge `finally` eller er de to kodefragmenter ens? Hvis der er en sådan fordel, så giv et eksempel på, når det betyder noget. diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md index cad2e1a3e..a8bf2b7ba 100644 --- a/1-js/10-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -1,98 +1,98 @@ -# Error handling, "try...catch" +# Håndtering af fejl, "try...catch" -No matter how great we are at programming, sometimes our scripts have errors. They may occur because of our mistakes, an unexpected user input, an erroneous server response, and for a thousand other reasons. +Uanset hvor gode vi er til programmering, så har vores scripts periodisk fejl. De kan opstå på grund af vores fejl, et uventet brugerinput, et fejlbehæftet server svar, og for tusind andre grunde. -Usually, a script "dies" (immediately stops) in case of an error, printing it to console. +Normalt dør et script (stoppes øjeblikkeligt) når en fejl opstår, og fejlen udskrives til konsollen. -But there's a syntax construct `try...catch` that allows us to "catch" errors so the script can, instead of dying, do something more reasonable. +Men der er en konstruktion `try...catch` som tillader os at "fange" fejl, så scriptet kan gøre noget mere fornuftigt end at dø. -## The "try...catch" syntax +## Syntaksen "try...catch" -The `try...catch` construct has two main blocks: `try`, and then `catch`: +Konstruktionen `try...catch` har to hovedblokke: `try`, og derefter `catch`: ```js try { - // code... + // kode... } catch (err) { - // error handling + // fejlhåndtering } ``` -It works like this: +Det virker sådan: -1. First, the code in `try {...}` is executed. -2. If there were no errors, then `catch (err)` is ignored: the execution reaches the end of `try` and goes on, skipping `catch`. -3. If an error occurs, then the `try` execution is stopped, and control flows to the beginning of `catch (err)`. The `err` variable (we can use any name for it) will contain an error object with details about what happened. +1. Først vil koden i `try {...}` blive kørt. +2. Hvis der ikke er nogen fejl, så ignoreres `catch (err)`: eksekveringen når til slutningen af `try` og går videre, springer over `catch`. +3. Hvis en fejl opstår, så stoppes `try`-eksekveringen, og kontrol flyttes til begyndelsen af `catch (err)`. Den `err`-variabel (vi kan bruge et hvilket som helst navn) vil indeholde et fejlobjekt med detaljer om, hvad der skete. ![](try-catch-flow.svg) -So, an error inside the `try {...}` block does not kill the script -- we have a chance to handle it in `catch`. +Så, hvis der sker en fejl inde i `try {...}` blokken, så dør scriptet ikke -- vi har en chance til at håndtere den i `catch`. -Let's look at some examples. +Lad os se på et par eksempler. -- An errorless example: shows `alert` `(1)` and `(2)`: +- Et eksempel uden fejl: viser `alert` `(1)` og `(2)`: ```js run try { - alert('Start of try runs'); // *!*(1) <--*/!* + alert('Start på "try runs"'); // *!*(1) <--*/!* - // ...no errors here + // ...ingen fejl her - alert('End of try runs'); // *!*(2) <--*/!* + alert('Slut på "try runs"'); // *!*(2) <--*/!* } catch (err) { - alert('Catch is ignored, because there are no errors'); // (3) + alert('Catch ignoreres fordi der ikke var fejl'); // (3) } ``` -- An example with an error: shows `(1)` and `(3)`: +- Et eksempel med en fejl: viser `(1)` og `(3)`: ```js run try { - alert('Start of try runs'); // *!*(1) <--*/!* + alert('Start på "try runs"'); // *!*(1) <--*/!* *!* - lalala; // error, variable is not defined! + lalala; // fejl, variable is not defined! */!* - alert('End of try (never reached)'); // (2) + alert('Slut på "try runs"'); // (2) } catch (err) { - alert(`Error has occurred!`); // *!*(3) <--*/!* + alert(`Der skete en fejl!`); // *!*(3) <--*/!* } ``` -````warn header="`try...catch` only works for runtime errors" -For `try...catch` to work, the code must be runnable. In other words, it should be valid JavaScript. +````warn header="`try...catch` virker kun for runtime errors" +For at `try...catch` skal virke skal koden kunne køre. Med anre ord skal det være gyldig (valid) JavaScript. -It won't work if the code is syntactically wrong, for instance it has unmatched curly braces: +Den virker ikke hvis syntaksen er forkert. For eksempel hvis der er rod med parenteser: ```js run try { {{{{{{{{{{{{ } catch (err) { - alert("The engine can't understand this code, it's invalid"); + alert("Motoren forstår ikke denne kode, den er ugyldig"); } ``` -The JavaScript engine first reads the code, and then runs it. The errors that occur on the reading phase are called "parse-time" errors and are unrecoverable (from inside that code). That's because the engine can't understand the code. +JavaScript-motoren læser koden først, og derefter kører den. De fejl, der opstår under læsningen, kaldes "parse-time" fejl og er unrecoverable (fra inde i koden). Det er fordi motoren ikke kan forstå koden. -So, `try...catch` can only handle errors that occur in valid code. Such errors are called "runtime errors" or, sometimes, "exceptions". -```` +Så, `try...catch` kan kun håndtere fejl, der opstår i gyldig kode. Sådanne fejl kaldes "runtime errors" eller, nogle gange, "exceptions". +``` -````warn header="`try...catch` works synchronously" -If an exception happens in "scheduled" code, like in `setTimeout`, then `try...catch` won't catch it: +````warn header="`try...catch` virker synkront" +Hvis en exception opstår i "planlagt" kode, som i `setTimeout`, så vil `try...catch` ikke fange den: ```js run try { @@ -100,112 +100,112 @@ try { noSuchVariable; // script will die here }, 1000); } catch (err) { - alert( "won't work" ); + alert( "det her virker ikke" ); } ``` -That's because the function itself is executed later, when the engine has already left the `try...catch` construct. +Det er fordi at selve funktionen først afvikles senere - det har motoren allerede forladt `try...catch` konstruktionen. -To catch an exception inside a scheduled function, `try...catch` must be inside that function: +For at fange en exception inde i en planlagt funktion, skal `try...catch` være inde i selve funktionen: ```js run setTimeout(function() { try { - noSuchVariable; // try...catch handles the error! + noSuchVariable; // try...catch håndterer fejlen! } catch { - alert( "error is caught here!" ); + alert( "fejlen fanges her!" ); } }, 1000); ``` ```` -## Error object +## Error objekt -When an error occurs, JavaScript generates an object containing the details about it. The object is then passed as an argument to `catch`: +Når der opstår en fejl, genererer JavaScript et objekt, der indeholder detaljerne om fejlen. Objektet sendes derefter som et argument til `catch`: ```js try { // ... -} catch (err) { // <-- the "error object", could use another word instead of err +} catch (err) { // <-- her er "error objektet", du kan sagtens bruge et andet navn istedet for err // ... } ``` -For all built-in errors, the error object has two main properties: +For alle indbyggede fejl har error objektet to hovedegenskaber: `name` -: Error name. For instance, for an undefined variable that's `"ReferenceError"`. +: Navnet på fejlen. Hvis det for eksempel er en ikke-defineret variabel, så indeholder den `"ReferenceError"`. `message` -: Textual message about error details. +: En tekst der beskriver fejlen. -There are other non-standard properties available in most environments. One of most widely used and supported is: +Der er andre ikke-standard egenskaber tilgængelige i de fleste miljøer. En af de mest brugte og understøttede er: `stack` -: Current call stack: a string with information about the sequence of nested calls that led to the error. Used for debugging purposes. +: Aktuel kaldestak: en streng med information om sekvensen af indlejrede kald, der ledte til fejlen. Bruges til fejlfinding. -For instance: +For eksempel: ```js run untrusted try { *!* - lalala; // error, variable is not defined! + lalala; // fejl, variablen er ikke defineret! */!* } catch (err) { alert(err.name); // ReferenceError alert(err.message); // lalala is not defined alert(err.stack); // ReferenceError: lalala is not defined at (...call stack) - // Can also show an error as a whole - // The error is converted to string as "name: message" + // Kan også vise en fejl som et samlet output + // Fejlen konverteres til en streng som "name: message" alert(err); // ReferenceError: lalala is not defined } ``` -## Optional "catch" binding +## Frivillig "catch" binding [recent browser=new] -If we don't need error details, `catch` may omit it: +Hvis vi ikke behøver detaljer om fejlen, kan `catch` udelade den: ```js try { // ... -} catch { // <-- without (err) +} catch { // <-- uden (err) // ... } ``` -## Using "try...catch" +## Brug af "try...catch" -Let's explore a real-life use case of `try...catch`. +Lad os udforske brugsscenarier for `try...catch`. -As we already know, JavaScript supports the [JSON.parse(str)](mdn:js/JSON/parse) method to read JSON-encoded values. +Som vi allerede ved så understøtter JavaScript metoden [JSON.parse(str)](mdn:js/JSON/parse) til at læse JSON værdier. -Usually it's used to decode data received over the network, from the server or another source. +Normalt bruges den til at afkode data der er modtaget over netværket, fra serveren eller anden kilde. -We receive it and call `JSON.parse` like this: +Vi modtager det og kalder `JSON.parse` sådan her: ```js run let json = '{"name":"John", "age": 30}'; // data from the server *!* -let user = JSON.parse(json); // convert the text representation to JS object +let user = JSON.parse(json); // konverterer tekstrepræsentation til JS-objekt */!* -// now user is an object with properties from the string +// nu er user et objekt med egenskaber fra strengen alert( user.name ); // John alert( user.age ); // 30 ``` -You can find more detailed information about JSON in the chapter. +Du kan finde mere detaljeret information om JSON i kapitlet . -**If `json` is malformed, `JSON.parse` generates an error, so the script "dies".** +**Hvis `json` indeholder fejl vil `JSON.parse` generere en fejl, så scriptet "dør".** -Should we be satisfied with that? Of course not! +Skal vi stille os tilfreds med det? Nej, selvfølgelig ikke! -This way, if something's wrong with the data, the visitor will never know that (unless they open the developer console). And people really don't like when something "just dies" without any error message. +På denne måde, hvis der er noget galt med data, vil besøgende aldrig vide det (medmindre de åbner konsollen). Og folk kan ikke li', når noget "bare dør" uden nogen fejlbesked. -Let's use `try...catch` to handle the error: +Lad os bruge `try...catch` til at håndtere fejlen: ```js run let json = "{ bad json }"; @@ -213,74 +213,74 @@ let json = "{ bad json }"; try { *!* - let user = JSON.parse(json); // <-- when an error occurs... + let user = JSON.parse(json); // <-- når en fejl opstår... */!* - alert( user.name ); // doesn't work + alert( user.name ); // virker ikke } catch (err) { *!* - // ...the execution jumps here - alert( "Our apologies, the data has errors, we'll try to request it one more time." ); + // ...hopper afviklingen her til + alert( "Desværre indeholder vores data fejl, vi vil forsøge at anmode om den en gang til." ); alert( err.name ); alert( err.message ); */!* } ``` -Here we use the `catch` block only to show the message, but we can do much more: send a new network request, suggest an alternative to the visitor, send information about the error to a logging facility, ... . All much better than just dying. +Her bruger vi kun `catch` blokken til at vise en besked, men vi kan gøre meget andet: sende et nyt netværkskald, foreslå et alternativ til besøgende, send information om fejlen til en log, ... alle muligheder der er meget bedre end bare at dø. -## Throwing our own errors +## Kaste vores egne fejl -What if `json` is syntactically correct, but doesn't have a required `name` property? +Hvad hvis `json` er syntactisk korrekt, men ikke har en påkrævet `name` egenskab? -Like this: +Som dette: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ufuldstændige data try { - let user = JSON.parse(json); // <-- no errors + let user = JSON.parse(json); // <-- ingen fejl *!* - alert( user.name ); // no name! + alert( user.name ); // intet navn! */!* } catch (err) { - alert( "doesn't execute" ); + alert( "afvikles ikke" ); } ``` -Here `JSON.parse` runs normally, but the absence of `name` is actually an error for us. +Her kører `JSON.parse` normalt, men fraværet af `name` er faktisk en fejl for os. -To unify error handling, we'll use the `throw` operator. +For at ensrette fejlhåndteringen, vil vi bruge `throw` operatoren. ### "Throw" operator -The `throw` operator generates an error. +Operatoren `throw` genererer en fejl. -The syntax is: +Syntaksen er: ```js throw ``` -Technically, we can use anything as an error object. That may be even a primitive, like a number or a string, but it's better to use objects, preferably with `name` and `message` properties (to stay somewhat compatible with built-in errors). +Teknisk set kan vi kaste hvad som helst som et error objekt. Det kan endda være en primitiv, som et tal eller en streng, men det er bedre at bruge objekter, især med `name` og `message` egenskaber (for at forblive nogenlunde kompatibel med de indbyggede fejl). -JavaScript has many built-in constructors for standard errors: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` and others. We can use them to create error objects as well. +JavaScript har mange indbyggede konstruktører for standardfejl: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` og andre. Vi kan bruge dem til at oprette fejlobjekter. -Their syntax is: +Deres syntaks er: ```js let error = new Error(message); -// or +// eller let error = new SyntaxError(message); let error = new ReferenceError(message); // ... ``` -For built-in errors (not for any objects, just for errors), the `name` property is exactly the name of the constructor. And `message` is taken from the argument. +For indbyggede fejl (ikke for objekter generelt, kun for fejl) er `name` egenskaben præcis det samme som navnet på konstruktøren, og `message` er taget fra argumentet. -For instance: +For eksempel: ```js run let error = new Error("Things happen o_O"); @@ -289,7 +289,7 @@ alert(error.name); // Error alert(error.message); // Things happen o_O ``` -Let's see what kind of error `JSON.parse` generates: +Lad os se hvilken fejl `JSON.parse` genererer, når den ikke kan læse data: ```js run try { @@ -302,14 +302,14 @@ try { } ``` -As we can see, that's a `SyntaxError`. +Som vi kan se er det en `SyntaxError`. -And in our case, the absence of `name` is an error, as users must have a `name`. +Og i vores tilfælde er fraværet af `name` en fejl, da brugere skal have et `name`. -So let's throw it: +Så lad os kaste den fejl, og håndtere den i `catch`: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ufuldstændige data try { @@ -317,55 +317,55 @@ try { if (!user.name) { *!* - throw new SyntaxError("Incomplete data: no name"); // (*) + throw new SyntaxError("Ufuldstændige data: name mangler"); // (*) */!* } alert( user.name ); } catch (err) { - alert( "JSON Error: " + err.message ); // JSON Error: Incomplete data: no name + alert( "JSON Error: " + err.message ); // JSON Error: Ufuldstændige data: name mangler } ``` -In the line `(*)`, the `throw` operator generates a `SyntaxError` with the given `message`, the same way as JavaScript would generate it itself. The execution of `try` immediately stops and the control flow jumps into `catch`. +I linjen med `(*)`, genererer `throw` operatoren en `SyntaxError` med den angivne `message`, på samme måde som JavaScript ville generere det selv. Afviklingen af `try` stopper med det samme og kontrollen af flower overgår til `catch`. -Now `catch` became a single place for all error handling: both for `JSON.parse` and other cases. +Nu bliver `catch` det eneste sted for al fejlhåndtering: både for `JSON.parse` og for andre scenarier. ## Rethrowing -In the example above we use `try...catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try {...}` block? Like a programming error (variable is not defined) or something else, not just this "incorrect data" thing. +I eksemplet ovenfor bruger vi `try...catch` til at håndtere forkert data i JSON filen. Men er det muligt at *en anden uventet fejl* opstår inden for `try {...}` blokken? Som en programmeringsfejl (variabel er ikke defineret) eller noget andet, ikke bare denne "forkerte data" ting. -For example: +For eksempel: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ufuldstændige data try { - user = JSON.parse(json); // <-- forgot to put "let" before user + user = JSON.parse(json); // <-- glemte at putte "let" foran user // ... } catch (err) { - alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined - // (no JSON Error actually) + alert("JSON Fejl: " + err); // JSON Fejl: ReferenceError: user is not defined + // (faktisk ikke en JSON fejl) } ``` -Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a bug may be discovered that leads to terrible hacks. +Selvfølgelig, alt er muligt! Programmører laver fejl. Selv i open-source værktøjer der er brugt af millioner i årtier -- pludselig kan en fejl blive opdaget, der fører til frygtelige hacks. -In our case, `try...catch` is placed to catch "incorrect data" errors. But by its nature, `catch` gets *all* errors from `try`. Here it gets an unexpected error, but still shows the same `"JSON Error"` message. That's wrong and also makes the code more difficult to debug. +I vores tilfælde, `try...catch` er placeret for at fange "forkerte data" fejl. Men selve konstrulktionen dikterer at `catch` får *alle* fejl fra `try`. Her får det en uventet fejl, men viser stadig den samme `"JSON Error"` besked. Det er forkert og gør også koden sværere at debugge. -To avoid such problems, we can employ the "rethrowing" technique. The rule is simple: +For at undgå sådanne problemer, kan vi bruge "rethrowing" teknikken. Reglen er simpel: -**Catch should only process errors that it knows and "rethrow" all others.** +**Catch skal kun behandle fejl, den kender, og "rethrow" alle andre.** -The "rethrowing" technique can be explained in more detail as: +Teknikken "rethrowing" kan forklares lidt mere detaljeret som: -1. Catch gets all errors. -2. In the `catch (err) {...}` block we analyze the error object `err`. -3. If we don't know how to handle it, we do `throw err`. +1. Opfang alle fejl. +2. I blokken `catch (err) {...}` analyserer vi error objektet. +3. Hvis vi ikke ved, hvordan vi skal håndtere den, kaster vi den med `throw err`. -Usually, we can check the error type using the `instanceof` operator: +Normalt kan vi tjekke fejltypen ved hjælp af `instanceof` operatoren: ```js run try { @@ -374,27 +374,27 @@ try { *!* if (err instanceof ReferenceError) { */!* - alert('ReferenceError'); // "ReferenceError" for accessing an undefined variable + alert('ReferenceError'); // "ReferenceError" for at tilgå en variabel der er undefineret } } ``` -We can also get the error class name from `err.name` property. All native errors have it. Another option is to read `err.constructor.name`. +Vi kan også få fejlens navn fra `err.name` egenskaben. Alle indbyggede fejl har den. En anden option er at læse `err.constructor.name`. -In the code below, we use rethrowing so that `catch` only handles `SyntaxError`: +I koden nedenfor, vi bruger rethrowing så `catch` kun håndterer `SyntaxError`: ```js run -let json = '{ "age": 30 }'; // incomplete data +let json = '{ "age": 30 }'; // ufuldstændige data try { let user = JSON.parse(json); if (!user.name) { - throw new SyntaxError("Incomplete data: no name"); + throw new SyntaxError("Ufuldstændige data: name mangler"); } *!* - blabla(); // unexpected error + blabla(); // uventet fejl */!* alert( user.name ); @@ -403,7 +403,7 @@ try { *!* if (err instanceof SyntaxError) { - alert( "JSON Error: " + err.message ); + alert( "JSON fejl: " + err.message ); } else { throw err; // rethrow (*) } @@ -412,11 +412,11 @@ try { } ``` -The error throwing on line `(*)` from inside `catch` block "falls out" of `try...catch` and can be either caught by an outer `try...catch` construct (if it exists), or it kills the script. +Fejlkastningen på linjen med `(*)` inde i `catch` blokken "falder ud" af `try...catch` og kan enten blive fanget af en ydre `try...catch` construct (hvis den eksisterer), eller også dør scriptet. -So the `catch` block actually handles only errors that it knows how to deal with and "skips" all others. +Så `catch` blokken håndterer faktisk kun fejl, den ved hvordan den skal håndtere, og "dropper" alle andre. -The example below demonstrates how such errors can be caught by one more level of `try...catch`: +Eksemplet nedenfor viser, hvordan sådanne fejl kan blive fanget af et ekstra niveau af `try...catch`: ```js run function readData() { @@ -425,13 +425,13 @@ function readData() { try { // ... *!* - blabla(); // error! + blabla(); // fejl! */!* } catch (err) { // ... if (!(err instanceof SyntaxError)) { *!* - throw err; // rethrow (don't know how to deal with it) + throw err; // rethrow (ved ikke hvordan man skal håndtere det) */!* } } @@ -441,42 +441,42 @@ try { readData(); } catch (err) { *!* - alert( "External catch got: " + err ); // caught it! + alert( "Ydre catch modtog: " + err ); // fangede den! */!* } ``` -Here `readData` only knows how to handle `SyntaxError`, while the outer `try...catch` knows how to handle everything. +Her ved `readData` kun hvordan den skal håndtere `SyntaxError`, mens den ydre `try...catch` ved hvordan den skal håndtere alt. ## try...catch...finally -Wait, that's not all. +vent, der er mere endnu. -The `try...catch` construct may have one more code clause: `finally`. +Konstruktionen `try...catch` har en såkaldt klausul mere, nemlig `finally`. -If it exists, it runs in all cases: +Hvis den eksisterer, kører den i alle tilfælde: -- after `try`, if there were no errors, -- after `catch`, if there were errors. +- efter `try`, hvis der ikke var nogen fejl, +- efter `catch`, hvis der var fejl. -The extended syntax looks like this: +Den udvidede syntaks ser sådan ud: ```js *!*try*/!* { - ... try to execute the code ... + ... prøv at afvikle noget kode ... } *!*catch*/!* (err) { - ... handle errors ... + ... håndter fejl ... } *!*finally*/!* { - ... execute always ... + ... kører altid til sidst ... } ``` -Try running this code: +Prøv at køre denne kode for at se, hvordan det fungerer: ```js run try { alert( 'try' ); - if (confirm('Make an error?')) BAD_CODE(); + if (confirm('Skal der laves en fejl?')) BAD_CODE(); } catch (err) { alert( 'catch' ); } finally { @@ -484,27 +484,27 @@ try { } ``` -The code has two ways of execution: +Koden har to måder at blive afviklet på: -1. If you answer "Yes" to "Make an error?", then `try -> catch -> finally`. -2. If you say "No", then `try -> finally`. +1. Hvis du svarer "Ja" til "Skal der laves en fejl?", så `try -> catch -> finally`. +2. Hvis du svarer "Nej", så `try -> finally`. -The `finally` clause is often used when we start doing something and want to finalize it in any case of outcome. +Klausulen `finally` bruges ofte, når vi starter noget og vil have det afsluttet uanset resultatet. -For instance, we want to measure the time that a Fibonacci numbers function `fib(n)` takes. Naturally, we can start measuring before it runs and finish afterwards. But what if there's an error during the function call? In particular, the implementation of `fib(n)` in the code below returns an error for negative or non-integer numbers. +For eksempel, vi vil måle tiden, det tager at køre en Fibonacci tal funktion `fib(n)`. Naturligvis kan vi starte måling før den kører og afslutte efterfølgende. Men hvad hvis der er en fejl under funktionskaldet? Især implementationen af `fib(n)` i koden nedenfor returnerer en fejl for negative eller ikke-heltalstal. -The `finally` clause is a great place to finish the measurements no matter what. +`finally` klausulen er et godt sted at afslutte målingerne uanset hvad. -Here `finally` guarantees that the time will be measured correctly in both situations -- in case of a successful execution of `fib` and in case of an error in it: +Her garanterer `finally` at tiden vil blive målt korrekt i begge situationer -- i tilfælde af en succesfuld udførelse af `fib` og i tilfælde af en fejl i den: ```js run -let num = +prompt("Enter a positive integer number?", 35) +let num = +prompt("Skriv et positivt heltal?", 35) let diff, result; function fib(n) { if (n < 0 || Math.trunc(n) != n) { - throw new Error("Must not be negative, and also an integer."); + throw new Error("Tallet må ikke være negativt, og det skal være et heltal."); } return n <= 1 ? n : fib(n - 1) + fib(n - 2); } @@ -521,26 +521,26 @@ try { } */!* -alert(result || "error occurred"); +alert(result || "der skete en fejl"); -alert( `execution took ${diff}ms` ); +alert( `udførelsen tog ${diff}ms` ); ``` -You can check by running the code with entering `35` into `prompt` -- it executes normally, `finally` after `try`. And then enter `-1` -- there will be an immediate error, and the execution will take `0ms`. Both measurements are done correctly. +Du kan nu køre koden med at indtaste `35` i `prompt` -- den kører normalt, `finally` efter `try`. Prøv bagefter at indtaste `-1` -- der vil umiddelbart være en fejl, og udførelsen vil tage `0ms`. Begge målinger er udført korrekt. -In other words, the function may finish with `return` or `throw`, that doesn't matter. The `finally` clause executes in both cases. +Med andre ord, funktionen kan slutte med `return` eller `throw`, det spiller ingen rolle. Klausulen `finally` udføres i begge tilfælde. -```smart header="Variables are local inside `try...catch...finally`" -Please note that `result` and `diff` variables in the code above are declared *before* `try...catch`. +```smart header="Variable er lokale inde i `try...catch...finally`" +Bemærk at `result` og `diff` variablene i koden ovenfor er deklareret *før* `try...catch`. -Otherwise, if we declared `let` in `try` block, it would only be visible inside of it. +Ellers, hvis vi deklarerede `let` i `try` blokken, ville den kun være synlig inden for den. ``` -````smart header="`finally` and `return`" -The `finally` clause works for *any* exit from `try...catch`. That includes an explicit `return`. +````smart header="`finally` og `return`" +Klausulen `finally` virker for *alle* udgange fra `try...catch`. Det indbefatter også en eksplicit `return`. -In the example below, there's a `return` in `try`. In this case, `finally` is executed just before the control returns to the outer code. +I eksemplet nedenfor, er der en `return` i `try`. I dette tilfælde, udføres `finally` lige før kontrol returnerer til den ydre kode. ```js run function func() { @@ -559,40 +559,40 @@ function func() { } } -alert( func() ); // first works alert from finally, and then this one +alert( func() ); // først kommer alert fra finally, og derefter kommer denne der kalder func() ``` ```` ````smart header="`try...finally`" -The `try...finally` construct, without `catch` clause, is also useful. We apply it when we don't want to handle errors here (let them fall through), but want to be sure that processes that we started are finalized. +Konstruktionen `try...finally` uden klausulen `catch` kan også være brugbar. Den bruger vi, hvis vi ikke vil håndtere fejl her (lad dem falde gennem), men vil være sikker på, at processer, vi har startet, bliver afsluttet. ```js function func() { - // start doing something that needs completion (like measurements) + // start noget der skal afsluttes (som målinger, eller en databaseforbindelse, eller noget andet) try { // ... } finally { - // complete that thing even if all dies + // førdiggør eller ryd op lige meget om det fejler eller ej } } ``` -In the code above, an error inside `try` always falls out, because there's no `catch`. But `finally` works before the execution flow leaves the function. +I koden ovenfor vil en fejl inde `try` altid falde ud, fordi der ikke er en `catch`. Men `finally` virker før afviklingsflowet forlader funktionen. ```` ## Global catch -```warn header="Environment-specific" -The information from this section is not a part of the core JavaScript. +```warn header="Miljøspecifikt" +Informationen i denne sektion er ikke en del af selve JavaScript sproget. ``` -Let's imagine we've got a fatal error outside of `try...catch`, and the script died. Like a programming error or some other terrible thing. +Lad os forestille os, at vi får en fatal fejl uden for `try...catch`, og scriptet dør. Som en programmeringsfejl eller noget andet skrækkeligt. -Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages), etc. +Er der en måde at reagere på sådanne tilfælde? Vi vil måske logge fejlen, vise noget til brugeren (normalt ser de ikke fejlbeskeder), etc. -There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to the special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property, that will run in case of an uncaught error. +Der er ingen i specifikationen, men miljøer ofte leverer det, fordi det er virkelig nyttigt. For eksempel har Node.js [`process.on("uncaughtException")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) for det. Og i browseren kan vi tildele en funktion til den specielle [window.onerror](mdn:api/GlobalEventHandlers/onerror) egenskab, som vil køre i tilfælde af en ikke-fanget fejl. -The syntax: +Syntaksen er: ```js window.onerror = function(message, url, line, col, error) { @@ -601,18 +601,18 @@ window.onerror = function(message, url, line, col, error) { ``` `message` -: Error message. +: Fejlmeddelelsen. `url` -: URL of the script where error happened. +: URL for det script hvor fejlen opstod. `line`, `col` -: Line and column numbers where error happened. +: Linje og kolonne numre hvor fejlen opstod. `error` -: Error object. +: Error objekt. -For instance: +For eksempel, denne kode har en fejl, og den vil blive fanget af `window.onerror`: ```html run untrusted refresh height=1 ``` -The role of the global handler `window.onerror` is usually not to recover the script execution -- that's probably impossible in case of programming errors, but to send the error message to developers. - -There are also web-services that provide error-logging for such cases, like or . +Rollen for den globale håndtering med `window.onerror` er normalt ikke for at genskabe scripteksekveringen -- det er sandsynligvis umuligt i tilfælde af programmeringsfejl, men for at sende fejlmeddelelsen til udviklerne. -They work like this: +Der er også web-tjenester, der leverer fejllogning for sådanne tilfælde, som eller . -1. We register at the service and get a piece of JS (or a script URL) from them to insert on pages. -2. That JS script sets a custom `window.onerror` function. -3. When an error occurs, it sends a network request about it to the service. -4. We can log in to the service web interface and see errors. +De virker sådan her: +1.Vi registrerer os hos services og får et stykke JS-kode (eller en URL til et script) fra dem til at indsætte på sider. +2. Dette JS-script sætter en brugerdefineret `window.onerror` funktion. +3. Når der sker en fejl, sender den en network request omkring det til tjenesten. +4. Vi kan så logge ind på tjenestens webinterface og se fejl. -## Summary +## Opsummering -The `try...catch` construct allows to handle runtime errors. It literally allows to "try" running the code and "catch" errors that may occur in it. +Konstruktionen `try...catch` tillade os at håndtere runtime-fejl. Det tillader os bogstaveligt talt at "prøve" at køre koden og "fange" fejl, der kan opstå i den. -The syntax is: +Syntaksen er: ```js try { - // run this code + // kør denne kode } catch (err) { - // if an error happened, then jump here - // err is the error object + // hvis der sker en fejl, så hop hertil + // err er et objekt der indeholder fejlinformationen } finally { - // do in any case after try/catch + // gør dette uanset om der sker en fejl eller ej } ``` -There may be no `catch` section or no `finally`, so shorter constructs `try...catch` and `try...finally` are also valid. +Der behøver ikke at være en `catch` klausul eller en `finally` klausul, så kortere konstruktioner `try...catch` og `try...finally` er også gyldige. -Error objects have following properties: +Error objekter har følgende egenskaber: -- `message` -- the human-readable error message. -- `name` -- the string with error name (error constructor name). -- `stack` (non-standard, but well-supported) -- the stack at the moment of error creation. +- `message` -- fejlmeddelelsen. +- `name` -- strengen med fejlnavnet (konstruktoren navn). +- `stack` (ikke-standard, men ret udbredt) -- stakken ved øjeblikket for fejl oprettelsen. -If an error object is not needed, we can omit it by using `catch {` instead of `catch (err) {`. +Hvis vi ikke behøver et error object, kan du udelade det ved at bruge `catch {` i stedet for `catch (err) {`. -We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter. +Vi kan også generere vores egne fejl ved hjælp af `throw` operatoren. Teknisk set kan argumentet for `throw` være hvad som helst, men normalt er det et error object, der nedarver fra den indbyggede `Error` klasse. Mere om at udvide fejl i næste kapitel. -*Rethrowing* is a very important pattern of error handling: a `catch` block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn't know. +*Rethrowing* er en meget vigtig mønster for fejlhåndtering: en `catch` blok forventes typisk at vide, hvordan den skal håndtere den specifikke fejltype, så den bør kaste fejlen igen, hvis den ikke kender til den. -Even if we don't have `try...catch`, most environments allow us to setup a "global" error handler to catch errors that "fall out". In-browser, that's `window.onerror`. +Selv hvis vi ikke har `try...catch`, tillader de fleste miljøer os at opsætte en "global" fejlhåndtering for at fange fejl, der "falder ud". I browseren er det `window.onerror`. diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg index 2c0d71348..336936c37 100644 --- a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg +++ b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg @@ -1 +1,91 @@ -BeginNo ErrorsAn error occured in the codeIgnore catch blockIgnore the rest of tryExecute catch blocktry { }// code... \ No newline at end of file + + + + + + + + + + + + Start + + + + + + + + + + + + + Ingen fejl + + + + + + En fejl skete i koden + + + + + + + Ignorer catch blok + + + Ignorer resten af try + + + Eksekver catch blokken + + + try { + + } + + + // code... + + + + \ No newline at end of file From e284bdb9ed9a97e6e7324614955aeb72fdc04d65 Mon Sep 17 00:00:00 2001 From: ockley Date: Fri, 20 Mar 2026 16:54:05 +0100 Subject: [PATCH 13/33] ovesat til dansk --- .../1-format-error/solution.md | 4 +- .../2-custom-errors/1-format-error/task.md | 14 +- .../2-custom-errors/article.md | 148 +++++++++--------- 1-js/10-error-handling/index.md | 2 +- 4 files changed, 84 insertions(+), 84 deletions(-) diff --git a/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md index 754e68f9a..57109386d 100644 --- a/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md +++ b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md @@ -6,9 +6,9 @@ class FormatError extends SyntaxError { } } -let err = new FormatError("formatting error"); +let err = new FormatError("formattingsfejl"); -alert( err.message ); // formatting error +alert( err.message ); // formatteringsfejl alert( err.name ); // FormatError alert( err.stack ); // stack diff --git a/1-js/10-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md index 2c8e910fc..8886e2cb1 100644 --- a/1-js/10-error-handling/2-custom-errors/1-format-error/task.md +++ b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md @@ -2,21 +2,21 @@ importance: 5 --- -# Inherit from SyntaxError +# Nedarv fra SyntaxError -Create a class `FormatError` that inherits from the built-in `SyntaxError` class. +Opret en klasse `FormatError` som nedarver fra den indbyggede `SyntaxError`-klasse. -It should support `message`, `name` and `stack` properties. +Den bør understøtte `message`, `name` og `stack` egenskaber. -Usage example: +Brugseksempel: ```js -let err = new FormatError("formatting error"); +let err = new FormatError("formatteringsfejl"); -alert( err.message ); // formatting error +alert( err.message ); // formatteringsfejl alert( err.name ); // FormatError alert( err.stack ); // stack alert( err instanceof FormatError ); // true -alert( err instanceof SyntaxError ); // true (because inherits from SyntaxError) +alert( err instanceof SyntaxError ); // true (fordi den nedarver fra SyntaxError) ``` diff --git a/1-js/10-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md index d28b07439..40531977d 100644 --- a/1-js/10-error-handling/2-custom-errors/article.md +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -1,42 +1,42 @@ -# Custom errors, extending Error +# Brugerdefinerede fejl, udvidelse af Error -When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on. +Når vi udvikler noget, har vi ofte brug for egne fejlklasser til at reflektere specifikke ting, der kan gå galt i vores opgaver. For fejl i netværksoperationer kan vi have brug for `HttpError`, for databaseoperationer `DbError`, for søgeoperationer `NotFoundError` og så videre. -Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have a `statusCode` property with a value like `404` or `403` or `500`. +Vores fejl bør understøtte grundlæggende fejl egenskaber som `message`, `name` og meget gerne `stack`. Men de kan også have andre egenskaber af deres egen type, f.eks. `HttpError` objekter kan have en `statusCode` egenskab med en værdi som `404` eller `403` eller `500`. -JavaScript allows to use `throw` with any argument, so technically our custom error classes don't need to inherit from `Error`. But if we inherit, then it becomes possible to use `obj instanceof Error` to identify error objects. So it's better to inherit from it. +JavaScript tillader at bruge `throw` med ethvert argument, så teknisk set behøver vores custom error klasser ikke at arve fra `Error`. Men hvis vi arver, så bliver det muligt at bruge `obj instanceof Error` til at identificere fejl objekter. Så det er bedre at arve fra den. -As the application grows, our own errors naturally form a hierarchy. For instance, `HttpTimeoutError` may inherit from `HttpError`, and so on. +Efterhånden som applikationen vokser, vil vores egne fejl naturligvis danne en hierarki. For eksempel kan `HttpTimeoutError` arve fra `HttpError`, og så videre. -## Extending Error +## Udvidelse af Error -As an example, let's consider a function `readUser(json)` that should read JSON with user data. +Som et eksempel, lad os overveje en funktion `readUser(json)` som skal læse JSON med brugerdata. -Here's an example of how a valid `json` may look: +Her er et eksempel på, hvordan et gyldigt `json` kan se ud: ```js let json = `{ "name": "John", "age": 30 }`; ``` -Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`. But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, it may not have `name` and `age` properties that are essential for our users. +Internt bruger vi `JSON.parse`. Hvis den modtager fejlformateret `json`, så kaster den `SyntaxError`. Men selv hvis `json` er syntaktisk korrekt, betyder det jo ikke nødvendigvis, at det er en gyldig bruger? Det kan mangle de nødvendige data. For eksempel kan det ikke have `name` og `age` egenskaber, som er afgørende for vores brugere. -Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field. +Vores funktion `readUser(json)` vil ikke kun læse JSON, men også tjekke ("validere") dataene. Hvis der mangler at blive udfyldt påkrævede felter, eller det er formatet er forkert, så er det en fejl. Og det er ikke en `SyntaxError`, fordi dataene er syntaktisk korrekte, men en anden type af fejl. Vi vil kalde det `ValidationError` og oprette en klasse til det. En fejl af denne type bør også bære informationen om det fejlbehagende felt. -Our `ValidationError` class should inherit from the `Error` class. +Vores `ValidationError` klasse bør arve fra `Error` klassen. -The `Error` class is built-in, but here's its approximate code so we can understand what we're extending: +Klassen `Error` er indbygget, men her er dens omtrentlige kode, så vi kan forstå, hvad vi udvider: ```js -// The "pseudocode" for the built-in Error class defined by JavaScript itself +// "pseudocode" for den indbyggede Error klasse defineret af JavaScript selv class Error { constructor(message) { this.message = message; - this.name = "Error"; // (different names for different built-in error classes) - this.stack = ; // non-standard, but most environments support it + this.name = "Error"; // (forskellige navne for forskellige indbyggede fejltyper) + this.stack = ; // ikke-standard, men de fleste miljøer understøtter det } } ``` -Now let's inherit `ValidationError` from it and try it in action: +Lad nu `ValidationError` udvide denne `Error` og prøve at bruge den: ```js run *!* @@ -49,23 +49,23 @@ class ValidationError extends Error { } function test() { - throw new ValidationError("Whoops!"); + throw new ValidationError("Ups!"); } try { test(); } catch(err) { - alert(err.message); // Whoops! + alert(err.message); // Ups! alert(err.name); // ValidationError - alert(err.stack); // a list of nested calls with line numbers for each + alert(err.stack); // en liste af indlejrede kald med linjenumre for hvor fejlen opstod } ``` -Please note: in the line `(1)` we call the parent constructor. JavaScript requires us to call `super` in the child constructor, so that's obligatory. The parent constructor sets the `message` property. +Bemærk: i linjen `(1)` kalder vi 'parent constructor'. JavaScript kræver at vi kalder `super` i 'child constructor', så det er obligatorisk. Forældrekonstruktøren sætter `message` egenskaben. -The parent constructor also sets the `name` property to `"Error"`, so in the line `(2)` we reset it to the right value. +Forældrekonstruktøren sætter også `name` egenskaben til `"Error"`, så i linjen `(2)` nulstiller vi den til den rigtige værdi. -Let's try to use it in `readUser(json)`: +Lad os prøve at bruge den i `readUser(json)`: ```js run class ValidationError extends Error { @@ -80,52 +80,52 @@ function readUser(json) { let user = JSON.parse(json); if (!user.age) { - throw new ValidationError("No field: age"); + throw new ValidationError("Mangler feltet: age"); } if (!user.name) { - throw new ValidationError("No field: name"); + throw new ValidationError("Mangler feltet: name"); } return user; } -// Working example with try..catch +// Eksempel med try..catch try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No field: name + alert("Ugyldige data: " + err.message); // Ugyldige data: Mangler feltet: name */!* } else if (err instanceof SyntaxError) { // (*) - alert("JSON Syntax Error: " + err.message); + alert("JSON Syntaksfejl: " + err.message); } else { - throw err; // unknown error, rethrow it (**) + throw err; // ukendt fejl, kast den videre (**) } } ``` -The `try..catch` block in the code above handles both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse`. +Blokken `try..catch` i koden ovenfor åndterer både vores `ValidationError` og den indbyggede `SyntaxError` fra `JSON.parse`. -Please take a look at how we use `instanceof` to check for the specific error type in the line `(*)`. +Se hvordan vi gør brug af `instanceof` til at tjekke for den specifikke fejltype i linjen `(*)`. -We could also look at `err.name`, like this: +Vi kan også kigge på `err.name`, sådan her: ```js // ... -// instead of (err instanceof SyntaxError) +// i stedet for (err instanceof SyntaxError) } else if (err.name == "SyntaxError") { // (*) // ... ``` -The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof. +`instanceof` versionen er meget bedre, fordi vi i fremtiden måske vil udvide `ValidationError`, lave undertyper af den i stil med `PropertyRequiredError`. Et tjek med `instanceof` vil stadig virke for nedarvede klasser. På den måde er det fremtidssikret. -Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (caused by a typo in the code or other unknown reasons) should fall through. +Det er også vigtigt, at hvis `catch` møder en ukendt fejl, så kaster den den videre i linjen `(**)`. Denne `catch` blok ved kun hvordan vi håndterer validerings- og syntaksfejl. Alt andet (sket ved en fejl i koden eller andre ukendte årsager) skal falde igennem. -## Further inheritance +## Videre nedarvning -The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age` instead of a number). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing. +Klassen `ValidationError` er meget generisk - mange ting kan gå galt. Egenskaben kan mangle eller den kan være i et forkert format (som en strengværdi for `age` i stedet for et tal). Lad os lave en mere konkret klasse `PropertyRequiredError`, præcist til at håndtere manglende egenskaber. Den vil bære yderligere information om den egenskab, der mangler. ```js run class ValidationError extends Error { @@ -138,7 +138,7 @@ class ValidationError extends Error { *!* class PropertyRequiredError extends ValidationError { constructor(property) { - super("No property: " + property); + super("Mangler egenskab: " + property); this.name = "PropertyRequiredError"; this.property = property; } @@ -159,32 +159,32 @@ function readUser(json) { return user; } -// Working example with try..catch +// Eksempel med try..catch try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No property: name + alert("Ugyldige data: " + err.message); // Ugyldige data: Mangler egenskab: name alert(err.name); // PropertyRequiredError alert(err.property); // name */!* } else if (err instanceof SyntaxError) { - alert("JSON Syntax Error: " + err.message); + alert("JSON Syntaksfejl: " + err.message); } else { - throw err; // unknown error, rethrow it + throw err; // ukendt fejl, kast den videre } } ``` -The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor. +Den nye klasse `PropertyRequiredError` er nem at bruge: vi behøver bare at videregive egenskabens navn: `new PropertyRequiredError(property)`. Den læsevenlige `message` bliver skabt i konstruktøren. -Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` in every custom error class. We can avoid it by making our own "basic error" class that assigns `this.name = this.constructor.name`. And then inherit all our custom errors from it. +Bemærk at `this.name` in `PropertyRequiredError` constructor er igen tildelt manuelt. Det kan blive en smule besværligt -- at tildele `this.name = ` i hver custom error klasse. Vi kan undgå det ved at lave vores egen "basic error" klasse, der tildele `this.name = this.constructor.name`. Og så nedarve alle vores custom errors fra den. -Let's call it `MyError`. +Lad os kalde det `MyError`. -Here's the code with `MyError` and other custom error classes, simplified: +Her er koden med `MyError` og andre custom error klasser, forenklet: ```js run class MyError extends Error { @@ -200,56 +200,56 @@ class ValidationError extends MyError { } class PropertyRequiredError extends ValidationError { constructor(property) { - super("No property: " + property); + super("Ingen egenskab: " + property); this.property = property; } } -// name is correct +// name er korrekt indstillet af MyError alert( new PropertyRequiredError("field").name ); // PropertyRequiredError ``` -Now custom errors are much shorter, especially `ValidationError`, as we got rid of the `"this.name = ..."` line in the constructor. +Nu er brugerdefinerede fejl meget kortere, især `ValidationError`, da vi har fjernet linjen `"this.name = ..."` i constructor. -## Wrapping exceptions +## Indpakning af undtagelser (expeptions) -The purpose of the function `readUser` in the code above is "to read the user data". There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but in the future `readUser` function may grow and probably generate other kinds of errors. +Meningen med funktionen `readUser` i koden ovenfor er "at læse brugerdata". Der kan opstå forskellige slags fejl i den proces. Som det er nu har vi `SyntaxError` og `ValidationError`, men i en fremtidig `readUser` funktion kan det vokse og måske generere andre typer fejl. -The code which calls `readUser` should handle these errors. Right now it uses multiple `if`s in the `catch` block, that check the class and handle known errors and rethrow the unknown ones. +Koden der kalder `readUser` bør håndtere disse fejl. Lige nu bruger den flere `if` inde i `catch` blokken, der tjekker klassen og håndterer kendte fejl og kaster de ukendte videre. -The scheme is like this: +Skemaet er sådan her: ```js try { ... - readUser() // the potential error source + readUser() // den potentielle fejlkilde ... } catch (err) { if (err instanceof ValidationError) { - // handle validation errors + // håndter valideringsfejl } else if (err instanceof SyntaxError) { - // handle syntax errors + // håndter syntaksfejl } else { - throw err; // unknown error, rethrow it + throw err; // ukendt fejl, kast den videre } } ``` -In the code above we can see two types of errors, but there can be more. +I koden ovenfor kan vi se to typer af fejl, men der kan være flere. -If the `readUser` function generates several kinds of errors, then we should ask ourselves: do we really want to check for all error types one-by-one every time? +Hvis `readUser`-funktionen genererer flere typer af fejl, så bør vi spørge os selv: vil vi virkelig have lyst til at skulle tjekke for alle fejltyper - en efter en - hver gang? -Often the answer is "No": we'd like to be "one level above all that". We just want to know if there was a "data reading error" -- why exactly it happened is often irrelevant (the error message describes it). Or, even better, we'd like to have a way to get the error details, but only if we need to. +Ofte er svaret "Nej": vi vil gerne være "et niveau over alt det". Vi vil bare have at vide om der var en "data læsningsfejl" -- hvorfor det præcist skete er ofte irrelevant (fejlmeddelelsen beskriver det). Eller, endnu bedre, vi vil gerne have en måde at få detaljerne om fejlen på ... men kun hvis vi har brug for dem. -The technique that we describe here is called "wrapping exceptions". +Den teknik vi beskriver her kaldes "wrapping exceptions". -1. We'll make a new class `ReadError` to represent a generic "data reading" error. -2. The function `readUser` will catch data reading errors that occur inside it, such as `ValidationError` and `SyntaxError`, and generate a `ReadError` instead. -3. The `ReadError` object will keep the reference to the original error in its `cause` property. +1. Vi laver en ny klasse `ReadError` til at repræsentere en generisk "data læsning" fejl. +2. Funktionen `readUser` vil fange data læsningsfejl, der opstår inden for den, såsom `ValidationError` og `SyntaxError`, og generere en `ReadError` i stedet. +3. `ReadError`-objektet vil gemme referencen til den originale fejl i sin `cause`-egenskab. -Then the code that calls `readUser` will only have to check for `ReadError`, not for every kind of data reading errors. And if it needs more details of an error, it can check its `cause` property. +Således vil koden, der kalder `readUser`, kun behøve at tjekke for `ReadError`, ikke for hver enkelt type data læsningsfejl. Og hvis den har brug for flere detaljer om en fejl, kan den tjekke dens `cause`-egenskab. -Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`: +Her er koden, der definerer `ReadError` og demonstrerer dens brug i `readUser` og `try..catch`: ```js run class ReadError extends Error { @@ -281,7 +281,7 @@ function readUser(json) { } catch (err) { *!* if (err instanceof SyntaxError) { - throw new ReadError("Syntax Error", err); + throw new ReadError("Syntaksfejl", err); } else { throw err; } @@ -293,7 +293,7 @@ function readUser(json) { } catch (err) { *!* if (err instanceof ValidationError) { - throw new ReadError("Validation Error", err); + throw new ReadError("Valideringsfejl", err); } else { throw err; } @@ -317,14 +317,14 @@ try { } ``` -In the code above, `readUser` works exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual). +I koden ovenfor fungerer `readUser` præcis som beskrevet -- fanger syntaks- og valideringsfejl og kaster `ReadError`-fejl i stedet (ukendte fejl kastes videre som normalt). -So the outer code checks `instanceof ReadError` and that's it. No need to list all possible error types. +Så den ydre kode tjekker `instanceof ReadError` og det er det. Ingen grund til at gennemgå alle mulige fejltyper. -The approach is called "wrapping exceptions", because we take "low level" exceptions and "wrap" them into `ReadError` that is more abstract. It is widely used in object-oriented programming. +Den kaldes "wrapping exceptions", fordi den tager "low level" undtagelser og "pakker" dem ind i `ReadError` der er mere abstrakt. Denne teknik er meget brugt i objektorienteret programmering. -## Summary +## Opsummering -- We can inherit from `Error` and other built-in error classes normally. We just need to take care of the `name` property and don't forget to call `super`. -- We can use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from a 3rd-party library and there's no easy way to get its class. Then `name` property can be used for such checks. -- Wrapping exceptions is a widespread technique: a function handles low-level exceptions and creates higher-level errors instead of various low-level ones. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required. +- Vi kan nedarve fra `Error` og andre indbyggede fejltyper på normal vis. Vi skal bare tage huske på `name`-egenskaben og ikke glemme at kalde `super`. +- Vi kan bruge `instanceof` til at tjekke for bestemte fejl. Det virker også med nedarvning. Men nogle gange har vi et fejlobjekt, der kommer fra en 3.parts bibliotek, hvor der måske ikke er en enkel måde at få dens klasse. Her kan `name`-egenskaben bruges til sådanne tjek. +- Wrapping exceptions er en almindelig teknik: en funktion håndterer lav-niveau undtagelser og opretter højere-niveau fejl i stedet for forskellige lav-niveau fejl. Lav-niveau undtagelser bliver nogle gange til egenskaber på det objekt, som `err.cause` i eksemplerne ovenfor, men det er ikke strengt nødvendigt. diff --git a/1-js/10-error-handling/index.md b/1-js/10-error-handling/index.md index face61c6e..aa2db89c7 100644 --- a/1-js/10-error-handling/index.md +++ b/1-js/10-error-handling/index.md @@ -1 +1 @@ -# Error handling +# Håndtering af fejl From eeff34d0de0583bded2b41152e1f727fc54fc652 Mon Sep 17 00:00:00 2001 From: ockley Date: Sat, 21 Mar 2026 10:16:47 +0100 Subject: [PATCH 14/33] oversat --- 1-js/11-async/01-callbacks/article.md | 148 +++++++++---------- 1-js/11-async/01-callbacks/callback-hell.svg | 111 +++++++++++++- 2 files changed, 183 insertions(+), 76 deletions(-) diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md index 57115a909..754a3e42c 100644 --- a/1-js/11-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -1,68 +1,66 @@ +# Introduktion: callbacks +```warn header="Vi bruger browser metoder i eksemplerne her" +For at demonstrere brugen af callbacks, promises og andre abstrakte koncepter, vil vi bruge nogle browser metoder: specifikt, indlæsning af scripts og udførelse af simple manipulationer af dokumentet. -# Introduction: callbacks +Hvis du ikke er fortrolig med disse metoder, og brugen af dem i eksemplerne er forvirrende, kan du med fordel læse et par kapitler fra den [næste del](/document) af tutorialen. -```warn header="We use browser methods in examples here" -To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods: specifically, loading scripts and performing simple document manipulations. - -If you're not familiar with these methods, and their usage in the examples is confusing, you may want to read a few chapters from the [next part](/document) of the tutorial. - -Although, we'll try to make things clear anyway. There won't be anything really complex browser-wise. +Vi vil dog forsøge at gøre tingene så simple og klare som muligt. Der vil ikke være noget virkelig komplekst browser-baseret. ``` -Many functions are provided by JavaScript host environments that allow you to schedule *asynchronous* actions. In other words, actions that we initiate now, but they finish later. +Mange funktioner leveret af JavaScript host miljøer tillader dig at planlægge *asynkrone* handlinger. Med andre ord, handlinger som vi starter nu, men de afsluttes senere. -For instance, one such function is the `setTimeout` function. +For eksempel, en sådan funktion er `setTimeout` funktionen. -There are other real-world examples of asynchronous actions, e.g. loading scripts and modules (we'll cover them in later chapters). +Der er andre eksempler på asynkrone handlinger i den virkelige verden, f.eks. indlæsning af scripts og moduler (vi vil dække dem i senere kapitler). -Take a look at the function `loadScript(src)`, that loads a script with the given `src`: +Tag et kig på funktionen `loadScript(src)`, som loader et script med det givne `src`: ```js function loadScript(src) { - // creates a - - - + } + + function showCircle(cx, cy, radius) { + let div = document.createElement('div'); + div.style.width = 0; + div.style.height = 0; + div.style.left = cx + 'px'; + div.style.top = cy + 'px'; + div.className = 'circle'; + document.body.append(div); + + return new Promise((resolve) => { + setTimeout(() => { + div.style.width = radius * 2 + 'px'; + div.style.height = radius * 2 + 'px'; + + div.addEventListener('transitionend', function handler() { + div.removeEventListener('transitionend', handler); + resolve(div); + }); + }, 0); + }); + } + + diff --git a/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md index 7860a71dc..919e3a89d 100644 --- a/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md +++ b/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md @@ -1,15 +1,15 @@ -# Animated circle with promise +# Animeret cirkel med promise -Rewrite the `showCircle` function in the solution of the task so that it returns a promise instead of accepting a callback. +Omskriv funktionen `showCircle` i løsningen fra opgaven så den returnerer et promise i stedet for at acceptere en callback. -The new usage: +Den nye brug: ```js showCircle(150, 150, 100).then(div => { div.classList.add('message-ball'); - div.append("Hello, world!"); + div.append("Hej, verden!"); }); ``` -Take the solution of the task as the base. +Brug løsningen fra som udgangspunkt. diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md index 66d9538fc..aff25bac1 100644 --- a/1-js/11-async/02-promise-basics/article.md +++ b/1-js/11-async/02-promise-basics/article.md @@ -1,92 +1,92 @@ # Promise -Imagine that you're a top singer, and fans ask day and night for your upcoming song. +Forestil dig at du er en kendt sanger og fans spørger dag og nat efter din kommende sang. -To get some relief, you promise to send it to them when it's published. You give your fans a list. They can fill in their email addresses, so that when the song becomes available, all subscribed parties instantly receive it. And even if something goes very wrong, say, a fire in the studio, so that you can't publish the song, they will still be notified. +For at få noget ro til at indspille, lover du at sende den til dem, når den er udgivet. Du giver dine fans en liste. De kan udfylde deres e-mailadresser, så når sangen bliver tilgængelig, modtager alle abonnerede parter den øjeblikkeligt. Og selv hvis noget går meget galt, f.eks. en brand i studiet, så du ikke kan udgive sangen, vil de stadig blive underrettet. -Everyone is happy: you, because the people don't crowd you anymore, and fans, because they won't miss the song. +Alle er glade: du, fordi folk ikke længere presser dig, og dine fans, fordi de ikke kommer til at misse sangen. -This is a real-life analogy for things we often have in programming: +Dette er en analogi fra virkeligheden for en situation vi ofte har i programmering: -1. A "producing code" that does something and takes time. For instance, some code that loads the data over a network. That's a "singer". -2. A "consuming code" that wants the result of the "producing code" once it's ready. Many functions may need that result. These are the "fans". -3. A *promise* is a special JavaScript object that links the "producing code" and the "consuming code" together. In terms of our analogy: this is the "subscription list". The "producing code" takes whatever time it needs to produce the promised result, and the "promise" makes that result available to all of the subscribed code when it's ready. +1. En "producerende kode" der gør noget der tager tid. For eksempel koder der henter data over et netværk - det er "sangeren". +2. En "konsumerende kode" der vil have resultatet af den "producerende kode" når den er klar. Mange funktioner kan have brug for dette resultat - disse er "fans". +3. Et *promise* (på dansk et "løfte") er et specielt JavaScript-objekt, der forbinder "producerende kode" og "konsumerende kode". I vores analogi er dette "abonnementslisten". "Producerende kode" tager den tid det kræver for at producere det lovede resultat, og "promise" gør dette resultat tilgængeligt for alle der har skrevet sig på listen, når den er klar. -The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But it's fine to begin with. +Analogien er ikke helt nøjagtig, fordi JavaScript-promises er mere komplekse end en simpel abonnementsliste: de har ekstra funktioner og begrænsninger. Men det er fint nok til at begynde med. -The constructor syntax for a promise object is: +Konstruktør-syntaksen for et promise-objekt er: ```js let promise = new Promise(function(resolve, reject) { - // executor (the producing code, "singer") + // udfører (den producerende kode, "sanger") }); ``` -The function passed to `new Promise` is called the *executor*. When `new Promise` is created, the executor runs automatically. It contains the producing code which should eventually produce the result. In terms of the analogy above: the executor is the "singer". +Den funktion der gives til `new Promise` kaldes *executor* (i stil med udfører på dansk). Når `new Promise` er oprettet, køres executor'en automatisk. Den indeholder den producerende kode, som bør producere resultatet i sidste ende. I termer af vores analogi: executor'en er "sangeren". -Its arguments `resolve` and `reject` are callbacks provided by JavaScript itself. Our code is only inside the executor. +Dens argumenter `resolve` og `reject` er callbacks leveret af JavaScript selv. Vores kode er kun inde i executor'en. -When the executor obtains the result, be it soon or late, doesn't matter, it should call one of these callbacks: +Når executor'en får resultatet, uanset hvor hurtigt eller langsomt, spiller det ingen rolle, så bør den kalde en af disse callbacks: -- `resolve(value)` — if the job is finished successfully, with result `value`. -- `reject(error)` — if an error has occurred, `error` is the error object. +- `resolve(value)` — hvis jobbet er færdigt med succes, med resultat `value`. +- `reject(error)` — hvis der er opstået en fejl, `error` er fejl-objektet. -So to summarize: the executor runs automatically and attempts to perform a job. When it is finished with the attempt, it calls `resolve` if it was successful or `reject` if there was an error. +Så for at opsummere: Executor'en kører automatisk og forsøger at udføre et job. Når det er færdigt med forsøget, kalder den `resolve` hvis det lykkedes eller `reject` hvis der var en fejl. -The `promise` object returned by the `new Promise` constructor has these internal properties: +Objektet `promise` som returneres af `new Promise` constructor har disse interne egenskaber: -- `state` — initially `"pending"`, then changes to either `"fulfilled"` when `resolve` is called or `"rejected"` when `reject` is called. -- `result` — initially `undefined`, then changes to `value` when `resolve(value)` is called or `error` when `reject(error)` is called. +- `state` — starter med at være `"pending"`, men ændres til enten `"fulfilled"` når `resolve` kaldes eller `"rejected"` når `reject` kaldes. +- `result` — starter med at være `undefined`, men ændres til `value` når `resolve(value)` kaldes eller `error` når `reject(error)` kaldes. -So the executor eventually moves `promise` to one of these states: +Så executor'en bevæger `promise` til en af disse tilstande: ![](promise-resolve-reject.svg) -Later we'll see how "fans" can subscribe to these changes. +Senere vil vi se, hvordan "fans" kan abonnere på disse ændringer. -Here's an example of a promise constructor and a simple executor function with "producing code" that takes time (via `setTimeout`): +Her er et eksempel på en promise konstruktør og en simpel executor-funktion med "producerende kode" der tager tid (via `setTimeout`): ```js let promise = new Promise(function(resolve, reject) { - // the function is executed automatically when the promise is constructed + // funktionen udføres automatisk når promise'et er konstrueret - // after 1 second signal that the job is done with the result "done" + // efter 1 sekund signalerer at jobbet er færdigt med resultatet "done" setTimeout(() => *!*resolve("done")*/!*, 1000); }); ``` -We can see two things by running the code above: +Vi kan se to ting ved at køre koden ovenfor: -1. The executor is called automatically and immediately (by `new Promise`). -2. The executor receives two arguments: `resolve` and `reject`. These functions are pre-defined by the JavaScript engine, so we don't need to create them. We should only call one of them when ready. +1. Executor'en kaldes automatisk og umiddelbart (af `new Promise`). +2. Executor'en modtager to argumenter: `resolve` og `reject`. Disse funktioner er foruddefinerede af JavaScript-motoren, så vi behøver ikke at oprette dem. Vi bør kun kalde en af dem når vi er klar. - After one second of "processing", the executor calls `resolve("done")` to produce the result. This changes the state of the `promise` object: + Efter et sekund af "behandling", kalder executor'en `resolve("done")` for at producere resultatet. Dette ændrer tilstanden på `promise`-objektet til `"fulfilled"` og sætter `result` til `"done"`: ![](promise-resolve-1.svg) -That was an example of a successful job completion, a "fulfilled promise". +Det var et eksempel på et succesfuldt job, et "fuldført løfte". -And now an example of the executor rejecting the promise with an error: +Og nu et eksempel på executor'en afviser løftet med en fejl: ```js let promise = new Promise(function(resolve, reject) { - // after 1 second signal that the job is finished with an error + // efter 1 sekund signalerer at jobbet er færdigt med en fejl setTimeout(() => *!*reject(new Error("Whoops!"))*/!*, 1000); }); ``` -The call to `reject(...)` moves the promise object to `"rejected"` state: +Kaldet til `reject(...)` flytter promise objektet til `"rejected"` state: ![](promise-reject-1.svg) -To summarize, the executor should perform a job (usually something that takes time) and then call `resolve` or `reject` to change the state of the corresponding promise object. +For at opsummere, executor udfører et job (ofte noget der tager tid) og kalder så `resolve` eller `reject` for at ændre tilstanden på det tilsvarende promise-objekt. -A promise that is either resolved or rejected is called "settled", as opposed to an initially "pending" promise. +Et løfte der enten opfyldes eller afvises kaldes for "settled", modsat den oprindelige tilstand "pending". -````smart header="There can be only a single result or an error" -The executor should call only one `resolve` or one `reject`. Any state change is final. +````smart header="Der kan der kun være et enkelt resultat eller en fejl" +Executor'en bør kun kalde en enkelt `resolve` eller en enkelt `reject`. Enhver ændring af state er endelig. -All further calls of `resolve` and `reject` are ignored: +Alle efterfølgende kald af `resolve` og `reject` ignoreres: ```js let promise = new Promise(function(resolve, reject) { @@ -94,95 +94,95 @@ let promise = new Promise(function(resolve, reject) { resolve("done"); */!* - reject(new Error("…")); // ignored - setTimeout(() => resolve("…")); // ignored + reject(new Error("…")); // ignoreret + setTimeout(() => resolve("…")); // ignoreret }); ``` -The idea is that a job done by the executor may have only one result or an error. +Idéen er at når et job er færdigt, kan det kun have ét resultat eller en fejl. -Also, `resolve`/`reject` expect only one argument (or none) and will ignore additional arguments. +Derudover arbejder `resolve`/`reject` med kun ét argument (eller intet) og vil ignorere yderligere argumenter. ```` -```smart header="Reject with `Error` objects" -In case something goes wrong, the executor should call `reject`. That can be done with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent. +```smart header="Afvis med `Error` objekter" +I det tilfælde at noget går galt, bør executor'en kalde `reject`. Det kan gøres med enhver type af argument (ligesom `resolve`). Men det er anbefalet at bruge `Error` objekter (eller objekter der nedarver fra `Error`). Årsagen til det vil snart blive klart. ``` -````smart header="Immediately calling `resolve`/`reject`" -In practice, an executor usually does something asynchronously and calls `resolve`/`reject` after some time, but it doesn't have to. We also can call `resolve` or `reject` immediately, like this: +````smart header="Umiddelbart kald af `resolve`/`reject`" +I praksis udfører en executor en job asynkront og kalder `resolve`/`reject` efter noget tid, men det behøver ikke at være tilfældet. Vi kan også kalde `resolve` eller `reject` umiddelbart, som her: ```js let promise = new Promise(function(resolve, reject) { - // not taking our time to do the job - resolve(123); // immediately give the result: 123 + // det tager ikke nogen tid at gøre jobbet + resolve(123); // giv resultatet med det samme: 123 }); ``` -For instance, this might happen when we start to do a job but then see that everything has already been completed and cached. +Det kan ske, hvis vi starter et job, men derefter ser, at alt allerede er fuldført og cached. -That's fine. We immediately have a resolved promise. +Det er helt fint. Så har vi bare umiddelbart et resolved promise. ```` -```smart header="The `state` and `result` are internal" -The properties `state` and `result` of the Promise object are internal. We can't directly access them. We can use the methods `.then`/`.catch`/`.finally` for that. They are described below. +```smart header="`state` og `result` er interne" +Egenskaberne `state` og `result` fra Promise objektet er interne. Vi kan ikke tilgå dem direkte. Vi kan bruge metoderne `.then`/`.catch`/`.finally` til det. De er beskrevet nedenfor. ``` -## Consumers: then, catch +## Forbrugerne: then, catch -A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using the methods `.then` and `.catch`. +Et Promise objekt fungerer som en link mellem executor (den "producerende kode" eller "sanger") og de forbrugerfunktioner (de "fans"), som vil modtage resultatet eller fejlen. Forbrugerfunktioner kan registrere sig (abonnere) ved hjælp af metoderne `.then` og `.catch`. ### then -The most important, fundamental one is `.then`. +Det vigtigste (og fundamentale) er `.then`. -The syntax is: +Syntaksen er: ```js promise.then( - function(result) { *!*/* handle a successful result */*/!* }, - function(error) { *!*/* handle an error */*/!* } + function(result) { *!*/* håndter et succesfuldt resultat */*/!* }, + function(error) { *!*/* håndter en fejl */*/!* } ); ``` -The first argument of `.then` is a function that runs when the promise is resolved and receives the result. +Det første argument af `.then` er en funktion der kører når løftet opfyldes og vi modtager et resultat. -The second argument of `.then` is a function that runs when the promise is rejected and receives the error. +Det andet argument af `.then` er en funktion der kører når løftet afvises og vi modtager en fejl. -For instance, here's a reaction to a successfully resolved promise: +Her er et eksempel på en reaktion til et succesfuldt løft: ```js run let promise = new Promise(function(resolve, reject) { setTimeout(() => resolve("done!"), 1000); }); -// resolve runs the first function in .then +// resolve kører den første funktion i .then promise.then( *!* - result => alert(result), // shows "done!" after 1 second + result => alert(result), // viser "done!" efter 1 sekund */!* - error => alert(error) // doesn't run + error => alert(error) // kører ikke ); ``` -The first function was executed. +Den første funktion blev eksekveret. -And in the case of a rejection, the second one: +Og i tilfælde af en afvisning, den anden funktion: ```js run let promise = new Promise(function(resolve, reject) { - setTimeout(() => reject(new Error("Whoops!")), 1000); + setTimeout(() => reject(new Error("Ups!")), 1000); }); -// reject runs the second function in .then +// reject kører den anden funktion i .then promise.then( - result => alert(result), // doesn't run + result => alert(result), // kører ikke *!* - error => alert(error) // shows "Error: Whoops!" after 1 second + error => alert(error) // viser "Error: Ups!" efter 1 sekund */!* ); ``` -If we're interested only in successful completions, then we can provide only one function argument to `.then`: +Hvis vi kun er interesseret i succesfulde afslutninger, så kan vi nøjes med kun at give et argument til `.then`: ```js run let promise = new Promise(resolve => { @@ -190,125 +190,125 @@ let promise = new Promise(resolve => { }); *!* -promise.then(alert); // shows "done!" after 1 second +promise.then(alert); // viser "done!" efter 1 sekund */!* ``` ### catch -If we're interested only in errors, then we can use `null` as the first argument: `.then(null, errorHandlingFunction)`. Or we can use `.catch(errorHandlingFunction)`, which is exactly the same: +Hvis vi kun er interesseret i fejl, så kan vi bruge `null` som det første argument: `.then(null, errorHandlingFunction)`. Eller vi kan bruge `.catch(errorHandlingFunction)`, som er præcis det samme: ```js run let promise = new Promise((resolve, reject) => { - setTimeout(() => reject(new Error("Whoops!")), 1000); + setTimeout(() => reject(new Error("Ups!")), 1000); }); *!* // .catch(f) is the same as promise.then(null, f) -promise.catch(alert); // shows "Error: Whoops!" after 1 second +promise.catch(alert); // viser "Error: Ups!" efter 1 sekund */!* ``` -The call `.catch(f)` is a complete analog of `.then(null, f)`, it's just a shorthand. +Klausulen `.catch(f)` er fuldstændig det samme som `.then(null, f)`. -## Cleanup: finally +## Oprydning: finally -Just like there's a `finally` clause in a regular `try {...} catch {...}`, there's `finally` in promises. +Ligesom der er en `finally` klausul i en `try {...} catch {...}`, er der også en `finally` i promises. -The call `.finally(f)` is similar to `.then(f, f)` in the sense that `f` runs always, when the promise is settled: be it resolve or reject. +Kaldet til `.finally(f)` minder om `.then(f, f)` i den forstand at `f` altid kører, når promise er blevet afsluttet: enten resolve eller reject. -The idea of `finally` is to set up a handler for performing cleanup/finalizing after the previous operations are complete. +Idéen med `finally` er at opstille håndtering der rydder op eller færdiggører processen efter de tidligere operationer er fuldført. -E.g. stopping loading indicators, closing no longer needed connections, etc. +Det kan være ting som at stoppe loading indicators, lukke forbindelser der ikke længere er nødvendige, etc. -Think of it as a party finisher. Irresepective of whether a party was good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it. +Tænk på det som en oprydder efter festen. Det er ligegyldigt om festen var god eller dårlig, hvor mange venner der var med oev. Vi skal stadig (eller i det mindste bør vi stadig) rydde op efter den. -The code may look like this: +Koden kan se sådan ud: ```js new Promise((resolve, reject) => { - /* do something that takes time, and then call resolve or maybe reject */ + /* gør noget der tager tid, og kald derefter resolve eller reject */ }) *!* - // runs when the promise is settled, doesn't matter successfully or not + // køres når promise er blevet afsluttet, det spiller ingen rolle om det lykkes eller ej .finally(() => stop loading indicator) - // so the loading indicator is always stopped before we go on + // Kode der gør at loading indikatoren er altid stoppet før vi går videre */!* .then(result => show result, err => show error) ``` -Please note that `finally(f)` isn't exactly an alias of `then(f,f)` though. +Bemærk at `finally(f)` ikke helt er det samme som `then(f,f)`. -There are important differences: +Der er nogle vigtig forskelle: -1. A `finally` handler has no arguments. In `finally` we don't know whether the promise is successful or not. That's all right, as our task is usually to perform "general" finalizing procedures. +1. En `finally` handler har ingen argumenter. I `finally` ved vi ikke om løftet er opfyldt eller ej. Det er ok, da vores opgave her ofter er at udføre "generelle" opgaver for at afslutte processen. - Please take a look at the example above: as you can see, the `finally` handler has no arguments, and the promise outcome is handled by the next handler. -2. A `finally` handler "passes through" the result or error to the next suitable handler. + Tag et kig på eksemplet ovenfor: som du kan se, har `finally` handler ingen argumenter, og promise udfaldet håndteres af den næste handler. +2. En `finally` handler "sender information igennem" til den næste egnede handler - hvad end det er et result eller error. - For instance, here the result is passed through `finally` to `then`: + Her sendes resultatet for eksempel gennem `finally` til `then`: ```js run new Promise((resolve, reject) => { setTimeout(() => resolve("value"), 2000); }) - .finally(() => alert("Promise ready")) // triggers first - .then(result => alert(result)); // <-- .then shows "value" + .finally(() => alert("Promise klart")) // trigger først + .then(result => alert(result)); // <-- .then viser "value" ``` - As you can see, the `value` returned by the first promise is passed through `finally` to the next `then`. + Som du kan se sendes værdien `value` der returneres fra det første promise til `finally` og gennem den videre til den næste `then`. - That's very convenient, because `finally` is not meant to process a promise result. As said, it's a place to do generic cleanup, no matter what the outcome was. + Det er meget praktisk fordi `finally` er ikke sat i verden for at behandling resultatet af dit promise. Som sagt, er det et sted til generel oprydning, uanset hvad udfaldet var. - And here's an example of an error, for us to see how it's passed through `finally` to `catch`: + He rer et eksempel med en fejl, for os at vise hvordan den bliver sendt gennem `finally` til `catch`: ```js run new Promise((resolve, reject) => { throw new Error("error"); }) - .finally(() => alert("Promise ready")) // triggers first - .catch(err => alert(err)); // <-- .catch shows the error + .finally(() => alert("Promise klart")) // trigger først + .catch(err => alert(err)); // <-- .catch viser fejlen ``` -3. A `finally` handler also shouldn't return anything. If it does, the returned value is silently ignored. +3. En `finally` handler skal heller ikke returnere noget. Hvis den gør, ignoreres den returnerede værdi stille. - The only exception to this rule is when a `finally` handler throws an error. Then this error goes to the next handler, instead of any previous outcome. + Den eneste undtagelse til den regel er, når en `finally` handler smider en fejl. Så er det denne fejl der bliver sendt til den næste handler, istedet for et tidligere resultat. -To summarize: +For at opsummere: -- A `finally` handler doesn't get the outcome of the previous handler (it has no arguments). This outcome is passed through instead, to the next suitable handler. -- If a `finally` handler returns something, it's ignored. -- When `finally` throws an error, then the execution goes to the nearest error handler. +- En `finally` handler får ikke det resultat fra den forrige handler (den har ingen argumenter). Dette resultat bliver i stedet sendt videre til den næste egnede handler. +- Hvis en `finally` handler returnerer noget, ignoreres det. +- Når `finally` kaster en fejl, går udførelsen til den nærmeste fejlhandler. -These features are helpful and make things work just the right way if we use `finally` how it's supposed to be used: for generic cleanup procedures. +Disse funktioner er hjælpsome og gør at tingene fungerer på den rigtige måde, hvis vi bruger `finally` som det er ment: til generelle oprydningsprocedurer. -````smart header="We can attach handlers to settled promises" -If a promise is pending, `.then/catch/finally` handlers wait for its outcome. +````smart header="Vi kan tilføje handlers til afsluttede promises" +Hvis et promise står som pending, `.then/catch/finally` vil handlers vente på dets resultat. -Sometimes, it might be that a promise is already settled when we add a handler to it. +Der kan være tilfælde, vore et løfte allerede er afsluttet, når handleren bliver tilføjet. -In such case, these handlers just run immediately: +I sådanne tilfælde vil disse handlers bare køre med det samme: ```js run -// the promise becomes resolved immediately upon creation +// Denne promise bliver sat til resolved med det samme den oprettes let promise = new Promise(resolve => resolve("done!")); -promise.then(alert); // done! (shows up right now) +promise.then(alert); // done! (Vises med det samme) ``` -Note that this makes promises more powerful than the real life "subscription list" scenario. If the singer has already released their song and then a person signs up on the subscription list, they probably won't receive that song. Subscriptions in real life must be done prior to the event. +Bemærk at dette gør promises mere kraftfulde end scenariet med "subscription list" i det virkelige liv. Hvis en sanger forsynlig har frigivet deres sang og en person tilmelder sig på abonnementslisten efterfølgende, vil de sandsynligvis ikke modtage den sang der allerede er udkommet. Abonnementer i det virkelige liv skal foretages før begivenheden for at virke. -Promises are more flexible. We can add handlers any time: if the result is already there, they just execute. +Promises er mere fleksible. Vi kan tilføje handlers når som helst: hvis resultatet allerede er der, så udføres de bare. ```` -## Example: loadScript [#loadscript] +## Eksempel: loadScript [#loadscript] -Next, let's see more practical examples of how promises can help us write asynchronous code. +Lad os nu se et par praktiske eksempler på hvordan promises kan hjælpe os med at skrive asynkron kode. -We've got the `loadScript` function for loading a script from the previous chapter. +Vi har `loadScript` funktionen der henter et script fra det forrige kapitel. -Here's the callback-based variant, just to remind us of it: +Her er den callback-baserede variant, for lige at genopfriske hukommelsen: ```js function loadScript(src, callback) { @@ -322,9 +322,9 @@ function loadScript(src, callback) { } ``` -Let's rewrite it using Promises. +Lad os omskrive den til at gøre brug af Promises. -The new function `loadScript` will not require a callback. Instead, it will create and return a Promise object that resolves when the loading is complete. The outer code can add handlers (subscribing functions) to it using `.then`: +Den nye funktion `loadScript` kræver ikke en callback. I stedet vil den oprette og returnere et Promise-objekt, der løser sig, når indlæsningen er fuldført. Den ydre kode kan tilføje handlers (abonnentfunktioner) til det ved hjælp af `.then`: ```js run function loadScript(src) { @@ -340,25 +340,25 @@ function loadScript(src) { } ``` -Usage: +Den bruges således: ```js run let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"); promise.then( - script => alert(`${script.src} is loaded!`), - error => alert(`Error: ${error.message}`) + script => alert(`${script.src} er hentet!`), + error => alert(`Fejl: ${error.message}`) ); -promise.then(script => alert('Another handler...')); +promise.then(script => alert('Endnu en handler...')); ``` -We can immediately see a few benefits over the callback-based pattern: +Vi kan med det samme se et par fordele i forhold til det callback-baserede mønster: | Promises | Callbacks | |----------|-----------| -| Promises allow us to do things in the natural order. First, we run `loadScript(script)`, and `.then` we write what to do with the result. | We must have a `callback` function at our disposal when calling `loadScript(script, callback)`. In other words, we must know what to do with the result *before* `loadScript` is called. | -| We can call `.then` on a Promise as many times as we want. Each time, we're adding a new "fan", a new subscribing function, to the "subscription list". More about this in the next chapter: [](info:promise-chaining). | There can be only one callback. | +| Promises tillader os at gøre tingene i den naturlige rækkefølge. Først kører vi `loadScript(script)`, og `.then` skriver vi, hvad vi vil gøre med resultatet. | Vi skal have en `callback`-funktion til rådighed, når vi kalder `loadScript(script, callback)`. Med andre ord skal vi vide, hvad vi vil gøre med resultatet *før* `loadScript` kaldes. | +| Vi kan kalde `.then` på et Promise så mange gange som vi vil. Hver gang tilføjer vi en ny "fan", en ny abonnentfunktion, til "abonnementslisten". Mere om dette i næste kapitel: [](info:promise-chaining). | Der kan kun være én callback. | -So promises give us better code flow and flexibility. But there's more. We'll see that in the next chapters. +Så promises giver os bedre kodeflow og fleksibilitet. Men der er mere. Det vil vi se nærmere på i næste kapitler. From 3e9f9badd9491fad11b510a918ec5f8bf1e59977 Mon Sep 17 00:00:00 2001 From: ockley Date: Mon, 23 Mar 2026 15:20:57 +0100 Subject: [PATCH 16/33] Oversat til dansk --- .../01-then-vs-catch/solution.md | 10 +- .../01-then-vs-catch/task.md | 2 +- 1-js/11-async/03-promise-chaining/article.md | 167 +++++++++--------- .../03-promise-chaining/getMessage.js | 2 +- .../promise-handler-variants.svg | 117 +++++++++++- 5 files changed, 206 insertions(+), 92 deletions(-) diff --git a/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md index bdd1c643b..060a2cdcd 100644 --- a/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md +++ b/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md @@ -1,6 +1,6 @@ -The short answer is: **no, they are not equal**: +Det korte svar er: **nej, de er ikke ens**: -The difference is that if an error happens in `f1`, then it is handled by `.catch` here: +Forskellen er, at hvis en fejl opstår i `f1`, så håndteres den af `.catch` her: ```js run promise @@ -8,13 +8,13 @@ promise .catch(f2); ``` -...But not here: +... men ikke her: ```js run promise .then(f1, f2); ``` -That's because an error is passed down the chain, and in the second code piece there's no chain below `f1`. +Det er fordi at fejl bliver sendt ned gennem kæden, og i det andet kodeeksempel er der ingen kæde under `f1`. -In other words, `.then` passes results/errors to the next `.then/catch`. So in the first example, there's a `catch` below, and in the second one there isn't, so the error is unhandled. +Med andre ord, `.then` sender resultater/fejl videre til den næste `.then/catch`. Så i det første eksempel er der en `catch` nedenfor, og i det andet er der ikke, så fejlen håndteres ikke. diff --git a/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md index cefca60aa..b3ca4ba43 100644 --- a/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md +++ b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md @@ -1,6 +1,6 @@ # Promise: then versus catch -Are these code fragments equal? In other words, do they behave the same way in any circumstances, for any handler functions? +Er disse to fragmenter af kode ens? Med andre ord, gør de det samme i enhver sammenhæng, for enhver handler funktion? ```js promise.then(f1).catch(f2); diff --git a/1-js/11-async/03-promise-chaining/article.md b/1-js/11-async/03-promise-chaining/article.md index a33ca258c..281b668b8 100644 --- a/1-js/11-async/03-promise-chaining/article.md +++ b/1-js/11-async/03-promise-chaining/article.md @@ -1,13 +1,13 @@ -# Promises chaining +# Sammenkædning af promises (chaining) -Let's return to the problem mentioned in the chapter : we have a sequence of asynchronous tasks to be performed one after another — for instance, loading scripts. How can we code it well? +Lad os vende tilbage til problemet nævnt i kapitlet : vi har en sekvens af asynkrone opgaver, der skal udføres efter hinanden — for eksempel, indlæsning af scripts. Hvordan kan vi kode det godt? -Promises provide a couple of recipes to do that. +Promises leverer et par metoder til at gøre det. -In this chapter we cover promise chaining. +I dette kapitel dækker vi promise chaining. -It looks like this: +Det ser ud som dette: ```js run new Promise(function(resolve, reject) { @@ -32,23 +32,23 @@ new Promise(function(resolve, reject) { }); ``` -The idea is that the result is passed through the chain of `.then` handlers. +Idéen her er, at resultatet sendes gennem en kæde af `.then`-håndteringer. -Here the flow is: -1. The initial promise resolves in 1 second `(*)`, -2. Then the `.then` handler is called `(**)`, which in turn creates a new promise (resolved with `2` value). -3. The next `then` `(***)` gets the result of the previous one, processes it (doubles) and passes it to the next handler. -4. ...and so on. +Her er flowet: +1. Det indledende promise løser sig efter 1 sekund `(*)`, +2. Derefter kaldes `.then` handleren `(**)`, der derefter opretter et nyt promise (løst med værdien `2`). +3. Det næste `then` `(***)` får resultatet fra den forrige, behandler det (dobbelt) og sender det videre til den næste handler. +4. ...og sådan videre. -As the result is passed along the chain of handlers, we can see a sequence of `alert` calls: `1` -> `2` -> `4`. +Efterhånden som resultatet sendes gennem kæden af håndteringer, kan vi se en sekvens af `alert`-kald: `1` -> `2` -> `4`. ![](promise-then-chain.svg) -The whole thing works, because every call to a `.then` returns a new promise, so that we can call the next `.then` on it. +Dette virker, fordi hvert kald til en `.then` returnerer et nyt promise, så vi kan kalde næste `.then` på det. -When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it. +Når en handler returnerer en værdi, bliver den til resultatet af det promise, så næste `.then` kaldes med den. -**A classic newbie error: technically we can also add many `.then` to a single promise. This is not chaining.** +**En klassisk begynderfejl: teknisk set kan vi også tilføje mange `.then` til et enkelt promise. Dette er ikke chaining.** For example: ```js run @@ -72,23 +72,23 @@ promise.then(function(result) { }); ``` -What we did here is just adding several handlers to one promise. They don't pass the result to each other; instead they process it independently. +Det vi har gjort her er blot at tilføje flere håndteringer til et enkelt promise. De sender ikke resultatet videre til hinanden; de behandler det uafhængigt. -Here's the picture (compare it with the chaining above): +Her er et billede (til sammenligning med kæden ovenfor): ![](promise-then-many.svg) -All `.then` on the same promise get the same result -- the result of that promise. So in the code above all `alert` show the same: `1`. +Alle `.then` der direkte er tilknyttet det samme promise får det samme resultat -- resultatet af det promise. Så i koden ovenfor viser alle `alert` den samme værdi: `1`. -In practice we rarely need multiple handlers for one promise. Chaining is used much more often. +I praksis har vi sjældent brug for flere håndteringer for et enkelt promise. Kæden bruges meget mere ofte. -## Returning promises +## Returnering af promises -A handler, used in `.then(handler)` may create and return a promise. +En handler brugt i `.then(handler)` må oprette og returnere et promise. -In that case further handlers wait until it settles, and then get its result. +I det tilfælde venter næste håndteringer på, at det promise bliver løst, og derefter får de dets resultat. -For instance: +For eksempel: ```js run new Promise(function(resolve, reject) { @@ -120,15 +120,15 @@ new Promise(function(resolve, reject) { }); ``` -Here the first `.then` shows `1` and returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result * 2`) is passed on to the handler of the second `.then`. That handler is in the line `(**)`, it shows `2` and does the same thing. +Her viser den første `.then` `1` og returnerer `new Promise(…)` i linjen `(*)`. Efter et sekund løser den sig, og resultatet (argumentet for `resolve`, her er det `result * 2`) sendes videre til håndtereren for den anden `.then`. Den håndterer er i linjen `(**)`, den viser `2` og gør det samme. -So the output is the same as in the previous example: 1 -> 2 -> 4, but now with 1 second delay between `alert` calls. +Så outputtet er det samme som i det forrige eksempel: 1 -> 2 -> 4, men nu med 1 sekund forsinkelse mellem `alert`-kald. -Returning promises allows us to build chains of asynchronous actions. +Returnering af promises tillader os at bygge kæder af asynchronous handlinger. -## Example: loadScript +## Eksempel: loadScript -Let's use this feature with the promisified `loadScript`, defined in the [previous chapter](info:promise-basics#loadscript), to load scripts one by one, in sequence: +Lad os bruge den mulighed i vores "promisificerede" `loadScript`, som vi oprettede i [sidste kapitel](info:promise-basics#loadscript), til at hente scripts et ad gangen, i sekvens: ```js run loadScript("/article/promise-chaining/one.js") @@ -139,22 +139,22 @@ loadScript("/article/promise-chaining/one.js") return loadScript("/article/promise-chaining/three.js"); }) .then(function(script) { - // use functions declared in scripts - // to show that they indeed loaded + // udfør funktionerne deklareret i de hentede scripts + // for at vise, at de rent faktisk er hentet one(); two(); three(); }); ``` -This code can be made bit shorter with arrow functions: +Denne kode kan gøres lidt kortere med arrow funktioner: ```js run loadScript("/article/promise-chaining/one.js") .then(script => loadScript("/article/promise-chaining/two.js")) .then(script => loadScript("/article/promise-chaining/three.js")) .then(script => { - // scripts are loaded, we can use functions declared there + // scripts er alle hentet. Nu kan vi bruge funktionerne one(); two(); three(); @@ -162,17 +162,17 @@ loadScript("/article/promise-chaining/one.js") ``` -Here each `loadScript` call returns a promise, and the next `.then` runs when it resolves. Then it initiates the loading of the next script. So scripts are loaded one after another. +Her returnerer hvert kald til `loadScript` et promise og den næste `.then` kører, når det løses (resolve). Derefter initialiseres hentning af det næste script. Så scripts er hentet en efter en. -We can add more asynchronous actions to the chain. Please note that the code is still "flat" — it grows down, not to the right. There are no signs of the "pyramid of doom". +Vi kan tilføje endnu flere asynkrone handlinger til kæden. Bemærk, at koden stadig er "flad" — den vokser ned, ikke til højre. Der er ingen tegn på "pyramid of doom". -Technically, we could add `.then` directly to each `loadScript`, like this: +Teknisk set kunne vi tilføje `.then` direkte til hvert `loadScript`, som dette: ```js run loadScript("/article/promise-chaining/one.js").then(script1 => { loadScript("/article/promise-chaining/two.js").then(script2 => { loadScript("/article/promise-chaining/three.js").then(script3 => { - // this function has access to variables script1, script2 and script3 + // denne funktion har adgang til variablene script1, script2 and script3 one(); two(); three(); @@ -181,19 +181,19 @@ loadScript("/article/promise-chaining/one.js").then(script1 => { }); ``` -This code does the same: loads 3 scripts in sequence. But it "grows to the right". So we have the same problem as with callbacks. +Denne kode gør det samme: henter 3 scripts i sekvens. Men den "vokser til højre". Så vi har det samme problem som med callbacks. -People who start to use promises sometimes don't know about chaining, so they write it this way. Generally, chaining is preferred. +Folk der starter med at bruge promises ved ikke altid om chaining, så de skriver det på denne måde. Generelt set er chaining at foretrække. -Sometimes it's ok to write `.then` directly, because the nested function has access to the outer scope. In the example above the most nested callback has access to all variables `script1`, `script2`, `script3`. But that's an exception rather than a rule. +Nogle gange er det i orden at skrive `.then` direkte, fordi den indlejrede funktion har adgang til det ydre scope. I eksemplet ovenfor har den mest indlejrede callback adgang til alle variabler `script1`, `script2`, `script3`. Men det er mere en undtagelse end en regel. ````smart header="Thenables" -To be precise, a handler may return not exactly a promise, but a so-called "thenable" object - an arbitrary object that has a method `.then`. It will be treated the same way as a promise. +For at være præcis, kan en handler returnerer ikke nødvendigvis et promise, men et såkaldt "thenable" objekt - et vilkårligt objekt som har en metode `.then`. Det vil blive behandlet på samme måde som en promise. -The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have an extended set of methods, but also be compatible with native promises, because they implement `.then`. +Ideen er, at 3rd-party biblioteker kan implementere deres egne "promise-kompatible" objekter. De kan have et udvidet sæt af metoder, men også være kompatible med native promises, fordi de implementerer `.then`. -Here's an example of a thenable object: +Her er et eksempel på et thenable objekt: ```js run class Thenable { @@ -201,8 +201,8 @@ class Thenable { this.num = num; } then(resolve, reject) { - alert(resolve); // function() { native code } - // resolve with this.num*2 after the 1 second + alert(resolve); // function() { native kode } + // resolve med this.num*2 efter 1 sekund setTimeout(() => resolve(this.num * 2), 1000); // (**) } } @@ -213,70 +213,69 @@ new Promise(resolve => resolve(1)) return new Thenable(result); // (*) */!* }) - .then(alert); // shows 2 after 1000ms + .then(alert); // vis 2 efter 1000ms ``` -JavaScript checks the object returned by the `.then` handler in line `(*)`: if it has a callable method named `then`, then it calls that method providing native functions `resolve`, `reject` as arguments (similar to an executor) and waits until one of them is called. In the example above `resolve(2)` is called after 1 second `(**)`. Then the result is passed further down the chain. - -This feature allows us to integrate custom objects with promise chains without having to inherit from `Promise`. -```` +JavaScript tjekker objektet der returneres af `.then` handleren i linje `(*)`: Hvis den har en kaldbar metode kaldet `then`, så kalder den den metode og giver native funktioner `resolve`, `reject` som argumenter (ligner en executor) og venter indtil en af dem bliver kaldt. I eksemplet ovenfor bliver `resolve(2)` kaldt efter 1 sekund `(**)`. Derefter sendes resultatet videre ned ad kæden. +Denne feature tillader os at integrere custom objekter med promise kæder uden at skulle arve fra `Promise`. +``` -## Bigger example: fetch +## Et større eksempel: fetch -In frontend programming, promises are often used for network requests. So let's see an extended example of that. +I frontend programmering bruges promises ofte til forespørgsler over netværket. Så lad os se på et eksempel på det. -We'll use the [fetch](info:fetch) method to load the information about the user from the remote server. It has a lot of optional parameters covered in [separate chapters](info:fetch), but the basic syntax is quite simple: +Vi vil bruge metoden [fetch](info:fetch) til at indlæse informationen om brugeren fra den eksterne server. Den har en masse valgfrie parametre dækket i [separate kapitler](info:fetch), men den grundlæggende syntaks er ganske enkel: ```js let promise = fetch(url); ``` -This makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but *before the full response is downloaded*. +Dette opretter en netværksforespørgsel til `url` og returnerer en promise. Promise'en løser med et `response`-objekt, når den eksterne server svarer med headers, men *før hele svaret er downloadet*. -To read the full response, we should call the method `response.text()`: it returns a promise that resolves when the full text is downloaded from the remote server, with that text as a result. +For at læse hele svaret, bør vi kalde metoden `response.text()`: den returnerer en promise, der løser, når hele teksten er downloadet fra den eksterne server, med den tekst som resultat. -The code below makes a request to `user.json` and loads its text from the server: +Koden nedenfor laver en forespørgsel til `user.json` og indlæser dens tekst fra serveren: ```js run fetch('/article/promise-chaining/user.json') - // .then below runs when the remote server responds + // .then nedenfor kører når remote serveren svarer .then(function(response) { - // response.text() returns a new promise that resolves with the full response text - // when it loads + // response.text() returnerer et nyt promise der løses med den fulde tekst af det eksterne fil, + // når den er indlæst return response.text(); }) .then(function(text) { - // ...and here's the content of the remote file + // ... og her er indholdet af det eksterne fil alert(text); // {"name": "iliakan", "isAdmin": true} }); ``` -The `response` object returned from `fetch` also includes the method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient, so let's switch to it. +`response` objektet der returneres fra `fetch` indeholder også metoden `response.json()` som læser de eksterne data og parser dem som JSON. I vores tilfælde er det endnu mere praktisk, så lad os skifte til det. -We'll also use arrow functions for brevity: +Vi vil også bruge arrow funktioner for at gøre koden kortere: ```js run -// same as above, but response.json() parses the remote content as JSON +// samme som ovenfor, men response.json() oversætter det hentede indhold som JSON fetch('/article/promise-chaining/user.json') .then(response => response.json()) - .then(user => alert(user.name)); // iliakan, got user name + .then(user => alert(user.name)); // iliakan, tog user.name ``` -Now let's do something with the loaded user. +Lad os gøre noget med den hentede bruger. -For instance, we can make one more request to GitHub, load the user profile and show the avatar: +For eksempel, vi kan lave et kald mere til GitHub, hente brugerens profil og vise en avatar: ```js run -// Make a request for user.json +// Opret en forespørgsel på user.json fetch('/article/promise-chaining/user.json') - // Load it as json + // Hent det ind som JSON .then(response => response.json()) - // Make a request to GitHub + // Lav en forespørgsel til GitHub .then(user => fetch(`https://api.github.com/users/${user.name}`)) - // Load the response as json + // Hent svaret som JSON .then(response => response.json()) - // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it) + // Vis avatar billedet (githubUser.avatar_url) i 3 sekunder (måske animér det) .then(githubUser => { let img = document.createElement('img'); img.src = githubUser.avatar_url; @@ -287,13 +286,13 @@ fetch('/article/promise-chaining/user.json') }); ``` -The code works; see comments about the details. However, there's a potential problem in it, a typical error for those who begin to use promises. +Koden virker; se eventuelt kommentarer for flere detaljer. Men, der er et potentielt problem - en typisk fejl for dem, der begynder at bruge promises. -Look at the line `(*)`: how can we do something *after* the avatar has finished showing and gets removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way. +Kig på linjen `(*)`: hvordan kan vi gøre noget *efter* at avataren er færdig med at blive vist og fjernet? Hvis vi for eksempel vil vise muligheder for at redigere brugeren eller noget i den stil. Som det er nu, er det ikke muligt. -To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing. +For at kunne udvide kæden, må vi returnere en promise, der løses, når avataren er færdig med at blive vist. -Like this: +Som dette: ```js run fetch('/article/promise-chaining/user.json') @@ -315,15 +314,15 @@ fetch('/article/promise-chaining/user.json') */!* }, 3000); })) - // triggers after 3 seconds - .then(githubUser => alert(`Finished showing ${githubUser.name}`)); + // trigger efter 3 sekunder + .then(githubUser => alert(`Færdig med at vise ${githubUser.name}`)); ``` -That is, the `.then` handler in line `(*)` now returns `new Promise`, that becomes settled only after the call of `resolve(githubUser)` in `setTimeout` `(**)`. The next `.then` in the chain will wait for that. +Nu vil `.then` handleren i linje `(*)` returnere et `new Promise`, der først afsluttes efter kaldet til `resolve(githubUser)` i `setTimeout` `(**)`. Det næste `.then` i kæden vil vente på det. -As a good practice, an asynchronous action should always return a promise. That makes it possible to plan actions after it; even if we don't plan to extend the chain now, we may need it later. +Det er god praksis at en asynkron handling altid bør returnere et promise. Det gør det muligt at planlægge handlinger efter den; selv hvis vi ikke planlægger at udvide kæden nu, kan vi have brug for det senere. -Finally, we can split the code into reusable functions: +Endelig kan vi splitte koden op i funktioner der kan genbruges: ```js run function loadJson(url) { @@ -349,18 +348,18 @@ function showAvatar(githubUser) { }); } -// Use them: +// Brug dem således: loadJson('/article/promise-chaining/user.json') .then(user => loadGithubUser(user.name)) .then(showAvatar) - .then(githubUser => alert(`Finished showing ${githubUser.name}`)); + .then(githubUser => alert(`Færdig med at vise ${githubUser.name}`)); // ... ``` -## Summary +## Opsummering -If a `.then` (or `catch/finally`, doesn't matter) handler returns a promise, the rest of the chain waits until it settles. When it does, its result (or error) is passed further. +Hvis en `.then` (eller `catch/finally`, for den sags skyld) handler returnerer et promise, vil resten af kæden vente indtil den bliver løst. Når det sker, videregives resultatet (eller fejlen) videre. -Here's a full picture: +Her er et overbliksbillede: ![](promise-handler-variants.svg) diff --git a/1-js/11-async/03-promise-chaining/getMessage.js b/1-js/11-async/03-promise-chaining/getMessage.js index 6c5893433..be48fdd50 100644 --- a/1-js/11-async/03-promise-chaining/getMessage.js +++ b/1-js/11-async/03-promise-chaining/getMessage.js @@ -1,3 +1,3 @@ function getMessage() { - return "Hello, world!"; + return "Hej, verden!"; } diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants.svg b/1-js/11-async/03-promise-chaining/promise-handler-variants.svg index 664a4dbeb..749215f2c 100644 --- a/1-js/11-async/03-promise-chaining/promise-handler-variants.svg +++ b/1-js/11-async/03-promise-chaining/promise-handler-variants.svg @@ -1 +1,116 @@ -return valuereturn promisethrow errorstate: "fulfilled" result: valuestate: "rejected" result: error...with the result of the new promise...state: "pending" result: undefinedthe call of .then(handler) always returns a promise:if handler ends with…that promise settles with: \ No newline at end of file + + + + + + + + + + + + + return value + + + return promise + + + throw error + + + + state: "fulfilled" + + result: value + + + + state: "rejected" + + result: error + + + + + + + ... med resultatet + + af et nyt promise... + + + + state: "pending" + + result: undefined + + + kaldet til .then(handler) returnerer altid et promise: + + + hvis handler ender med … + + + vil promise afslutte med: + + + + \ No newline at end of file From 5b035f030c32238de62552cee112cddfd5f274c0 Mon Sep 17 00:00:00 2001 From: ockley Date: Tue, 24 Mar 2026 11:03:55 +0100 Subject: [PATCH 17/33] Oversat --- .../01-error-async/solution.md | 8 +- .../01-error-async/task.md | 6 +- .../04-promise-error-handling/article.md | 132 +++++++++--------- 3 files changed, 73 insertions(+), 73 deletions(-) diff --git a/1-js/11-async/04-promise-error-handling/01-error-async/solution.md b/1-js/11-async/04-promise-error-handling/01-error-async/solution.md index 0d43f55e0..a736bfa4d 100644 --- a/1-js/11-async/04-promise-error-handling/01-error-async/solution.md +++ b/1-js/11-async/04-promise-error-handling/01-error-async/solution.md @@ -1,13 +1,13 @@ -The answer is: **no, it won't**: +Svaret er: **nej, det vil ikke**: ```js run new Promise(function(resolve, reject) { setTimeout(() => { - throw new Error("Whoops!"); + throw new Error("Ups!"); }, 1000); }).catch(alert); ``` -As said in the chapter, there's an "implicit `try..catch`" around the function code. So all synchronous errors are handled. +Som sagt tidligere i kapitlet er der en "implicit `try..catch`" omkring funktionens kode. Det betyder at alle synkrone fejl håndteres. -But here the error is generated not while the executor is running, but later. So the promise can't handle it. +Men her genereres fejlen ikke mens udføreren kører, men senere. Så promise kan ikke håndtere den. diff --git a/1-js/11-async/04-promise-error-handling/01-error-async/task.md b/1-js/11-async/04-promise-error-handling/01-error-async/task.md index bafc47ce9..a57301665 100644 --- a/1-js/11-async/04-promise-error-handling/01-error-async/task.md +++ b/1-js/11-async/04-promise-error-handling/01-error-async/task.md @@ -1,11 +1,11 @@ -# Error in setTimeout +# Fejl i setTimeout -What do you think? Will the `.catch` trigger? Explain your answer. +Hvad tror du? Vil `.catch` blive udløst? Forklar dit svar. ```js new Promise(function(resolve, reject) { setTimeout(() => { - throw new Error("Whoops!"); + throw new Error("Ups!"); }, 1000); }).catch(alert); ``` diff --git a/1-js/11-async/04-promise-error-handling/article.md b/1-js/11-async/04-promise-error-handling/article.md index c5b4206ab..10bd36267 100644 --- a/1-js/11-async/04-promise-error-handling/article.md +++ b/1-js/11-async/04-promise-error-handling/article.md @@ -1,21 +1,21 @@ -# Error handling with promises +# Håndtering af fejl med promises -Promise chains are great at error handling. When a promise rejects, the control jumps to the closest rejection handler. That's very convenient in practice. +Kæder af promise er gode til fejlbehandling. Når et promise afvises, springer kontrollen til den nærmeste afvisningshåndtering. Det er meget praktisk i praksis. -For instance, in the code below the URL to `fetch` is wrong (no such site) and `.catch` handles the error: +For eksempel, i koden nedenfor er URL'en til `fetch` forkert (ingen sådan side) og `.catch` håndterer fejlen: ```js run *!* -fetch('https://no-such-server.blabla') // rejects +fetch('https://no-such-server.blabla') // afviser */!* .then(response => response.json()) - .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary) + .catch(err => alert(err)) // TypeError: failed to fetch (teksten kan variere) ``` -As you can see, the `.catch` doesn't have to be immediate. It may appear after one or maybe several `.then`. +Som du kan se behøver `.catch` ikke at komme umiddelbart efter. Den kan sagtens placeres efter en eller flere `.then`. -Or, maybe, everything is all right with the site, but the response is not valid JSON. The easiest way to catch all errors is to append `.catch` to the end of chain: +Eller, måske er alt i orden med siden, men svaret er ikke gyldigt JSON. Den nemmeste måde at fange alle fejl er at tilføje `.catch` til slutningen af kæden: ```js run fetch('/article/promise-chaining/user.json') @@ -38,168 +38,168 @@ fetch('/article/promise-chaining/user.json') */!* ``` -Normally, such `.catch` doesn't trigger at all. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it. +Normalt vil sådan en `.catch` slet ikke blive aktiveret. Men, hvis en af dine promises afvises (et netværksproblem, ugyldig json osv.), så vil den fange det. ## Implicit try..catch -The code of a promise executor and promise handlers has an "invisible `try..catch`" around it. If an exception happens, it gets caught and treated as a rejection. +Koden fra en promise udførerer og promise handlere har en "usynlig `try..catch`" omkring den. Hvis der sker en exception, vil den blive opfanget og behandlet som en afvisning. -For instance, this code: +Se for eksempel denne kode: ```js run new Promise((resolve, reject) => { *!* - throw new Error("Whoops!"); + throw new Error("Ups!"); */!* -}).catch(alert); // Error: Whoops! +}).catch(alert); // Error: Ups! ``` -...Works exactly the same as this: +...Virker præcis på samme måde som denne: ```js run new Promise((resolve, reject) => { *!* - reject(new Error("Whoops!")); + reject(new Error("Ups!")); */!* -}).catch(alert); // Error: Whoops! +}).catch(alert); // Error: Ups! ``` -The "invisible `try..catch`" around the executor automatically catches the error and turns it into rejected promise. +Den "usynlige `try..catch`" omkring udførerenopfanger automatisk fejl og omdanner dem til afviste løfter. -This happens not only in the executor function, but in its handlers as well. If we `throw` inside a `.then` handler, that means a rejected promise, so the control jumps to the nearest error handler. +Det sker ikke kun i udførerfunktionen, men også i dens handlere. Hvis vi bruger `throw` indeni en `.then`-handler, betyder det en afvisning af løftet, og kontrollen overgives til den nærmeste fejlhåndterer. -Here's an example: +Her er et eksempel: ```js run new Promise((resolve, reject) => { resolve("ok"); }).then((result) => { *!* - throw new Error("Whoops!"); // rejects the promise + throw new Error("Ups!"); // afviser løftet */!* -}).catch(alert); // Error: Whoops! +}).catch(alert); // Error: Ups! ``` -This happens for all errors, not just those caused by the `throw` statement. For example, a programming error: +Dette sker for alle fejl, ikke kun de der er forårsaget af `throw`-sætningen. For eksempel, en programmeringsfejl: ```js run new Promise((resolve, reject) => { resolve("ok"); }).then((result) => { *!* - blabla(); // no such function + blabla(); // funktionen findes ikke */!* -}).catch(alert); // ReferenceError: blabla is not defined +}).catch(alert); // ReferenceError: blabla er ikke defineret ``` -The final `.catch` not only catches explicit rejections, but also accidental errors in the handlers above. +Den endelige `.catch` fanger ikke kun eksplicitte afvigelser, men også tilfældige fejl i de håndteringer, der kommer før den. ## Rethrowing -As we already noticed, `.catch` at the end of the chain is similar to `try..catch`. We may have as many `.then` handlers as we want, and then use a single `.catch` at the end to handle errors in all of them. +Som vi allerede har bemærket ligner en `.catch` i slutningen af kæden `try..catch`. Vi kan have så mange `.then` håndteringer som vi vil, og så bruge en enkelt `.catch` i slutningen for at håndtere fejl i alle af dem. -In a regular `try..catch` we can analyze the error and maybe rethrow it if it can't be handled. The same thing is possible for promises. +I en normal `try..catch` kan vi analysere fejlen og måske kaste den igen, hvis den ikke kan håndteres. Det samme er muligt for promises. -If we `throw` inside `.catch`, then the control goes to the next closest error handler. And if we handle the error and finish normally, then it continues to the next closest successful `.then` handler. +Hvis vi bruger `throw` indeni `.catch`, så går kontrollen til den næste tætteste fejl-handler. Og hvis vi håndterer fejlen og afslutter normalt, så fortsætter det til den næste tætteste succesfulde `.then` handler. -In the example below the `.catch` successfully handles the error: +I eksemplet nedenfor håndterer `.catch` fejlen succesfuldt: ```js run // the execution: catch -> then new Promise((resolve, reject) => { - throw new Error("Whoops!"); + throw new Error("Ups!"); }).catch(function(error) { - alert("The error is handled, continue normally"); + alert("Fejlen er håndteret, fortsæt normalt"); -}).then(() => alert("Next successful handler runs")); +}).then(() => alert("Næste succesfulde handler kører")); ``` -Here the `.catch` block finishes normally. So the next successful `.then` handler is called. +Her afsluttes `.catch` blokken normalt. Så den næste succesfulde `.then` handler kaldes. -In the example below we see the other situation with `.catch`. The handler `(*)` catches the error and just can't handle it (e.g. it only knows how to handle `URIError`), so it throws it again: +I eksemplet nedenfor ser vi den anden situation med `.catch`. Handleren `(*)` fanger fejlen og kan ikke håndtere den (f.eks. den kun ved hvordan man håndterer `URIError`), så den kaster den igen: ```js run // the execution: catch -> catch new Promise((resolve, reject) => { - throw new Error("Whoops!"); + throw new Error("Ups!"); }).catch(function(error) { // (*) if (error instanceof URIError) { - // handle it + // håndter fejlen } else { - alert("Can't handle such error"); + alert("Kan ikke håndtere sådan en fejl"); *!* - throw error; // throwing this or another error jumps to the next catch + throw error; // kaster fejlen videre */!* } }).then(function() { - /* doesn't run here */ + /* denne handler kører ikke her */ }).catch(error => { // (**) - alert(`The unknown error has occurred: ${error}`); - // don't return anything => execution goes the normal way + alert(`En ukendt fejl er opstået: ${error}`); + // returnerer ikke noget => udførelse foregår normalt }); ``` -The execution jumps from the first `.catch` `(*)` to the next one `(**)` down the chain. +Udførelse foregår fra den første `.catch` `(*)` til den næste `(**)` ned ad kæden. -## Unhandled rejections +## Afvisninger der ikke håndteres -What happens when an error is not handled? For instance, we forgot to append `.catch` to the end of the chain, like here: +Hvad sker der når en fejl ikke håndteres? For eksempel, vi har glemt at tilføje `.catch` til slutningen af kæden, som her: ```js untrusted run refresh new Promise(function() { - noSuchFunction(); // Error here (no such function) + noSuchFunction(); // Fejl her (funktionen findes ikke) }) .then(() => { - // successful promise handlers, one or more - }); // without .catch at the end! + // håndtering af succesfuld promise, en eller flere + }); // men uden .catch i slutningen! ``` -In case of an error, the promise becomes rejected, and the execution should jump to the closest rejection handler. But there is none. So the error gets "stuck". There's no code to handle it. +I tilfælde af en fejl, bliver løftet afvist, og udførelsen vil prøve at hoppe til den nærmeste håndtering af afvisninger - men der er ingen. Så fejlen bliver "fastlåst". Der er ingen kode til at håndtere den. -In practice, just like with regular unhandled errors in code, it means that something has gone terribly wrong. +I praksis, ligesom med regulære uhåndterede fejl i kode, betyder det, at noget er gået frygteligt galt. -What happens when a regular error occurs and is not caught by `try..catch`? The script dies with a message in the console. A similar thing happens with unhandled promise rejections. +Hvad sker der når en regulær fejl opstår og ikke bliver fanget af `try..catch`? Scriptet dør med en besked i konsollen. Et lignende noget sker med uhåndterede promise-afvisninger. -The JavaScript engine tracks such rejections and generates a global error in that case. You can see it in the console if you run the example above. +JavaScript-motoren sporer sådanne afvisninger og genererer en global fejl i det tilfælde. Du kan se det i konsollen, hvis du kører eksemplet ovenfor. -In the browser we can catch such errors using the event `unhandledrejection`: +I browseren kan vi fange sådanne fejl ved hjælp af begivenheden `unhandledrejection`: ```js run *!* window.addEventListener('unhandledrejection', function(event) { - // the event object has two special properties: - alert(event.promise); // [object Promise] - the promise that generated the error - alert(event.reason); // Error: Whoops! - the unhandled error object + // et event objekt har to specielle egenskaber: + alert(event.promise); // [object Promise] - det promise der genererede fejlen + alert(event.reason); // Error: Ups! - det error objekt der ikke er behandlet }); */!* new Promise(function() { - throw new Error("Whoops!"); -}); // no catch to handle the error + throw new Error("Ups!"); +}); // ingen cathc til at opfange fejlen ``` -The event is the part of the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections). +Event'et er en del af [HTML standarden](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections). -If an error occurs, and there's no `.catch`, the `unhandledrejection` handler triggers, and gets the `event` object with the information about the error, so we can do something. +Hvis der sker en fejl og der ikke er en `.catch`, vil handleren `unhandledrejection` blive udløst og modtage et `event` objekt med information om fejlen, så vi kan handle på det. -Usually such errors are unrecoverable, so our best way out is to inform the user about the problem and probably report the incident to the server. +Normalt er det ikke muligt at komme sig over sådanne fejl, så vores bedste måde at håndtere dem på er at informere brugeren om problemet og sandsynligvis rapportere hændelsen til serveren. -In non-browser environments like Node.js there are other ways to track unhandled errors. +I ikke-browser miljøer som Node.js findes der andre måder at spore uhåndterede fejl på. -## Summary +## Opsummering -- `.catch` handles errors in promises of all kinds: be it a `reject()` call, or an error thrown in a handler. -- `.then` also catches errors in the same manner, if given the second argument (which is the error handler). -- We should place `.catch` exactly in places where we want to handle errors and know how to handle them. The handler should analyze errors (custom error classes help) and rethrow unknown ones (maybe they are programming mistakes). -- It's ok not to use `.catch` at all, if there's no way to recover from an error. -- In any case we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments) to track unhandled errors and inform the user (and probably our server) about them, so that our app never "just dies". +- `.catch` håndterer fejl i alle slags promises: det kan være en `reject()` der kaldes eller en fejl der kastes med `throw`. +- `.then` kan også opfange fejl på samme måde, hvis den får et andet argument (som er fejlhåndteringen). +- Vi bør placere `.catch` præcis på de steder vi vil håndtere fejlene og ved hvordan de skal behandles. Den handler der skal skal analysere fejlen (brugerdefinerede fejl kan hjælpe) og kaste fejl den ikke kender videre (rethrow) da det kan være programmeringsfejl. +- Det er ok slet ikke at bruge `.catch` hvis der ikke er nogen måde at afhjælpe fejl. +- I alle tilfælde bør vi have en `unhandledrejection` event handler (for browsere og tilsvarende for andre miljøer) til at opsnappe fejl der ikke håndteres og informere brugeren (og muligvis serveren) om dem, så vores app ikke bare "dør uden grund". From f0438247a759c7de31deefd5ab1948f0bee18c3e Mon Sep 17 00:00:00 2001 From: ockley Date: Tue, 24 Mar 2026 21:03:56 +0100 Subject: [PATCH 18/33] Oversat til dansk --- 1-js/11-async/05-promise-api/article.md | 162 ++++++++++++------------ 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/1-js/11-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md index 7be84ce2c..b19af2139 100644 --- a/1-js/11-async/05-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -1,40 +1,40 @@ # Promise API -There are 6 static methods in the `Promise` class. We'll quickly cover their use cases here. +Der er 6 statiske metoder i `Promise` klassen. Vi gennemløber dem alle kort her. ## Promise.all -Let's say we want many promises to execute in parallel and wait until all of them are ready. +Lad os sige, vi vil have mange promises til at køre parallelt og vente til alle er klar. -For instance, download several URLs in parallel and process the content once they are all done. +Det kunnme for eksempel være at downloade flere URL'er og behandle indholdet når de alle er færdigindlæst. -That's what `Promise.all` is for. +Det er det `Promise.all` er til. -The syntax is: +Syntaksen er: ```js let promise = Promise.all(iterable); ``` -`Promise.all` takes an iterable (usually, an array of promises) and returns a new promise. +`Promise.all` tager et itererbart objekt (ofte et array af promises) og returnerer et nyt promise. -The new promise resolves when all listed promises are resolved, and the array of their results becomes its result. +Det nye promise løser sig når alle de listede promises er løst, og arrayet af deres resultater bliver dets result. -For instance, the `Promise.all` below settles after 3 seconds, and then its result is an array `[1, 2, 3]`: +For eksempel vil `Promise.all` nedenfor blive færdig efter 3 sekunder, og dens result er et array med indholdet `[1, 2, 3]`: ```js run Promise.all([ new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1 new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2 new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3 -]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member +]).then(alert); // 1,2,3 når løfterne er klare: hver promise bidrager med et array-element ``` -Please note that the order of the resulting array members is the same as in its source promises. Even though the first promise takes the longest time to resolve, it's still first in the array of results. +Bemærk at rækkefølgen for de resulterende array-elementer er den samme som i de oprindelige promises. Selvom den første promise tager længst tid at løse, er den stadig først i arrayet af resultater. -A common trick is to map an array of job data into an array of promises, and then wrap that into `Promise.all`. +Et meget udbredt tricket er at mappe et array af job-data til et array af promises, og så omslutte det i `Promise.all`. -For instance, if we have an array of URLs, we can fetch them all like this: +For eksempel, hvis vi har et array af URLs, kan vi hente dem alle sådan: ```js run let urls = [ @@ -43,17 +43,17 @@ let urls = [ 'https://api.github.com/users/jeresig' ]; -// map every url to the promise of the fetch +// map hver url til et promise fra fetch let requests = urls.map(url => fetch(url)); -// Promise.all waits until all jobs are resolved +// Promise.all venter til alle job er løst Promise.all(requests) .then(responses => responses.forEach( response => alert(`${response.url}: ${response.status}`) )); ``` -A bigger example with fetching user information for an array of GitHub users by their names (we could fetch an array of goods by their ids, the logic is identical): +Et større eksempel med hentning af brugerinformation for et array af GitHub-brugere efter deres navne (vi kunne hente et array af varer efter deres id, logikken er identisk): ```js run let names = ['iliakan', 'remy', 'jeresig']; @@ -62,47 +62,47 @@ let requests = names.map(name => fetch(`https://api.github.com/users/${name}`)); Promise.all(requests) .then(responses => { - // all responses are resolved successfully + // alle respons er løst for(let response of responses) { - alert(`${response.url}: ${response.status}`); // shows 200 for every url + alert(`${response.url}: ${response.status}`); // viser 200 for hver url } return responses; }) - // map array of responses into an array of response.json() to read their content + // map array af response til et array af response.json() til at læse deres indhold .then(responses => Promise.all(responses.map(r => r.json()))) - // all JSON answers are parsed: "users" is the array of them + // alle svar er oversat fra JSON: "users" objektet indeholder deres navn i "name" egenskaben .then(users => users.forEach(user => alert(user.name))); ``` -**If any of the promises is rejected, the promise returned by `Promise.all` immediately rejects with that error.** +**Hvis et af løfterne bliver afvist, bliver det promise der returneres af `Promise.all` umiddelbart afvist med den fejl.** -For instance: +For eksempel: ```js run Promise.all([ new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), *!* - new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ups!")), 2000)), */!* new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).catch(alert); // Error: Whoops! +]).catch(alert); // Error: Ups! ``` -Here the second promise rejects in two seconds. That leads to an immediate rejection of `Promise.all`, so `.catch` executes: the rejection error becomes the outcome of the entire `Promise.all`. +Her bliver det andet promise afvist efter to sekunder. Det fører til en øjeblikkelig afvisning af `Promise.all`, så `.catch` eksekveres: fejlen bliver til resultatet af hele `Promise.all`. -```warn header="In case of an error, other promises are ignored" -If one promise rejects, `Promise.all` immediately rejects, completely forgetting about the other ones in the list. Their results are ignored. +```warn header="I tilfælde af en fejl, ignoreres andre promises" +Hvis et af løfterne bliver afvist, bliver det promise der returneres af `Promise.all` umiddelbart afvist med den fejl. Deres resultater ignoreres. -For example, if there are multiple `fetch` calls, like in the example above, and one fails, the others will still continue to execute, but `Promise.all` won't watch them anymore. They will probably settle, but their results will be ignored. +For eksempel, hvis der er flere `fetch` kald, som i eksemplet ovenfor, og ét af dem mislykkes, vil de andre stadig fortsætte med at køre, men `Promise.all` vil ikke længere holde øje med dem. De vil sandsynligvis slutte, men deres resultater vil blive ignoreret. -`Promise.all` does nothing to cancel them, as there's no concept of "cancellation" in promises. In [another chapter](info:fetch-abort) we'll cover `AbortController` that can help with that, but it's not a part of the Promise API. +`Promise.all` gør intet for at annullere dem, da der ikke er en koncept om "annullering" i promises. I [et andet kapitel](info:fetch-abort) vil vi dække `AbortController` som kan afhjælpe det, men det er ikke en del af Promise API'et. ``` -````smart header="`Promise.all(iterable)` allows non-promise \"regular\" values in `iterable`" -Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's passed to the resulting array "as is". +````smart header="`Promise.all(itererbar)` tillader ikke-promise \"regulære\" værdier i `itererbar`" +Normalt accepterer, `Promise.all(...)` et itererbart objekt (ofte et array) med promises. Men, hvis nogle af disse objekter ikke er promises, bliver de overført til det resulterende array "som det er". -For instance, here the results are `[1, 2, 3]`: +Her er resultatet for eksempel `[1, 2, 3]`: ```js run Promise.all([ @@ -114,31 +114,31 @@ Promise.all([ ]).then(alert); // 1, 2, 3 ``` -So we are able to pass ready values to `Promise.all` where convenient. +Så vi er i stand til at overføre eksisterende værdier til `Promise.all` hvor det er praktisk. ```` ## Promise.allSettled [recent browser="new"] -`Promise.all` rejects as a whole if any promise rejects. That's good for "all or nothing" cases, when we need *all* results successful to proceed: +`Promise.all` fejler helt hvis bare ét af dets løfter afvises. Det er godt for "alt eller ingen" situationer, hvor vi har brug for at *alle* resultater er succesfulde for at fortsætte: ```js Promise.all([ fetch('/template.html'), fetch('/style.css'), fetch('/data.json') -]).then(render); // render method needs results of all fetches +]).then(render); // render metoden behøver resultaterne fra alle fetch kald ``` -`Promise.allSettled` just waits for all promises to settle, regardless of the result. The resulting array has: +`Promise.allSettled` venter på at alle løfter bliver løst, uanset resultatet. Det resulterende array har: -- `{status:"fulfilled", value:result}` for successful responses, -- `{status:"rejected", reason:error}` for errors. +- `{status:"fulfilled", value:result}` for succesfuldde løfter, og +- `{status:"rejected", reason:error}` for fejl. -For example, we'd like to fetch the information about multiple users. Even if one request fails, we're still interested in the others. +Det kunne for eksempel være, at vi gerne vil hente information om flere brugere. Selvom en forespørgsel mislykkes, er vi stadig interesseret i de andre. -Let's use `Promise.allSettled`: +Lad os bruge `Promise.allSettled`: ```js run let urls = [ @@ -160,7 +160,7 @@ Promise.allSettled(urls.map(url => fetch(url))) }); ``` -The `results` in the line `(*)` above will be: +`results` i linjen med `(*)` ovenfor vil være: ```js [ {status: 'fulfilled', value: ...response...}, @@ -169,11 +169,11 @@ The `results` in the line `(*)` above will be: ] ``` -So for each promise we get its status and `value/error`. +Så for hvert løfte får vi dens status og `value/error`. ### Polyfill -If the browser doesn't support `Promise.allSettled`, it's easy to polyfill: +Hvis browseren ikke understøtter `Promise.allSettled`, er det nemt at lave en polyfill: ```js if (!Promise.allSettled) { @@ -188,23 +188,23 @@ if (!Promise.allSettled) { } ``` -In this code, `promises.map` takes input values, turns them into promises (just in case a non-promise was passed) with `p => Promise.resolve(p)`, and then adds `.then` handler to every one. +I denne kode tager `promises.map` input værdierne og omdanner dem til promises (for en sikkerheds skyld, hvis der blev leveret et ikke-promise) med `p => Promise.resolve(p)`. Derefter tilføjes en `.then` til hver af dem. -That handler turns a successful result `value` into `{status:'fulfilled', value}`, and an error `reason` into `{status:'rejected', reason}`. That's exactly the format of `Promise.allSettled`. +Denne handler omdanner et succesfuldt resultats `value` til objektet `{status:'fulfilled', value}`, og en error `reason` om til `{status:'rejected', reason}`. Det er præcis det format, som `Promise.allSettled` forventes at levere. -Now we can use `Promise.allSettled` to get the results of *all* given promises, even if some of them reject. +Nu kan vi bruge `Promise.allSettled` til at give resultatet fra at hente resultaterne af *alle* givne promises, selvom nogle af dem afvises. ## Promise.race -Similar to `Promise.all`, but waits only for the first settled promise and gets its result (or error). +Minder om `Promise.all`, men venter kun på den første løste promise og får dens resultat (eller fejl). -The syntax is: +Syntaksen er: ```js -let promise = Promise.race(iterable); +let promise = Promise.race(itererbar); ``` -For instance, here the result will be `1`: +For eksempel vil resultatet her være `1`: ```js run Promise.race([ @@ -214,65 +214,65 @@ Promise.race([ ]).then(alert); // 1 ``` -The first promise here was fastest, so it became the result. After the first settled promise "wins the race", all further results/errors are ignored. +Det første promise her har hurtigst, så den bliver resultatet. Efter det første løste promise "vinder kapløbet" bliver alle de andre resultater og fejl ignoreret. ## Promise.any -Similar to `Promise.race`, but waits only for the first fulfilled promise and gets its result. If all of the given promises are rejected, then the returned promise is rejected with [`AggregateError`](mdn:js/AggregateError) - a special error object that stores all promise errors in its `errors` property. +Minder om `Promise.race`, men venter kun på den første *opfyldte* promise og får dens resultat. Hvis alle de givne promises er afvist, så er det returnerede promise afvist med [`AggregateError`](mdn:js/AggregateError) - et specielt error-objekt, der gemmer alle promise-fejl i sin `errors`-egenskab. -The syntax is: +Syntaksen er: ```js -let promise = Promise.any(iterable); +let promise = Promise.any(itererbar); ``` -For instance, here the result will be `1`: +For eksempel vil resultatet her være `1`: ```js run Promise.any([ - new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)), + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ups!")), 1000)), new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)), new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) ]).then(alert); // 1 ``` -The first promise here was fastest, but it was rejected, so the second promise became the result. After the first fulfilled promise "wins the race", all further results are ignored. +Det første promise her har hurtigst, men det blev afvist, så det andet promise blev resultatet. Efter det første opfyldte promise "vinder kapløbet", bliver alle de andre resultater ignoreret. -Here's an example when all promises fail: +Her er et eksempel når alle promises fejler: ```js run Promise.any([ new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ouch!")), 1000)), - new Promise((resolve, reject) => setTimeout(() => reject(new Error("Error!")), 2000)) + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Fejl!")), 2000)) ]).catch(error => { console.log(error.constructor.name); // AggregateError console.log(error.errors[0]); // Error: Ouch! - console.log(error.errors[1]); // Error: Error! + console.log(error.errors[1]); // Error: Fejl! }); ``` -As you can see, error objects for failed promises are available in the `errors` property of the `AggregateError` object. +som du kan se, error objekter for fejlede løfter er tilgængelige i `errors`-egenskaben af `AggregateError`-objektet. ## Promise.resolve/reject -Methods `Promise.resolve` and `Promise.reject` are rarely needed in modern code, because `async/await` syntax (we'll cover it [a bit later](info:async-await)) makes them somewhat obsolete. +Metoderne `Promise.resolve` og `Promise.reject` er sjældent nødvendige i moderne kode, fordi `async/await` syntaksen (vi møder dem [lige om lidt](info:async-await)) gør dem lidt overflødige. -We cover them here for completeness and for those who can't use `async/await` for some reason. +Vi dækker dem her for fuldkommenhedens skyld og for de som ikke kan/vil bruge `async/await` af en eller anden grund. ### Promise.resolve -`Promise.resolve(value)` creates a resolved promise with the result `value`. +`Promise.resolve(value)` opretter et opfyldt promise med resultatet `value`. -Same as: +Det samme som: ```js let promise = new Promise(resolve => resolve(value)); ``` -The method is used for compatibility, when a function is expected to return a promise. +Metoden bruges til kompatibilitet, når en funktion forventes at returnere et promise. -For example, the `loadCached` function below fetches a URL and remembers (caches) its content. For future calls with the same URL it immediately gets the previous content from cache, but uses `Promise.resolve` to make a promise of it, so the returned value is always a promise: +For eksempel, funktionen `loadCached` nedenfor henter en URL og husker (cacher) dens indhold. For fremtidige opkald med samme URL får den øjeblikkeligt det tidligere indhold fra cachen, men bruger `Promise.resolve` til at lave et promise af det, så det returnerede værdi altid er et promise: ```js let cache = new Map(); @@ -293,31 +293,31 @@ function loadCached(url) { } ``` -We can write `loadCached(url).then(…)`, because the function is guaranteed to return a promise. We can always use `.then` after `loadCached`. That's the purpose of `Promise.resolve` in the line `(*)`. +Vi kan skrive `loadCached(url).then(…)`, fordi funktionen er garanteret til at returnere et promise. Vi kan altid bruge `.then` efter `loadCached`. Det er formålet med `Promise.resolve` i linjen `(*)`. ### Promise.reject -`Promise.reject(error)` creates a rejected promise with `error`. +`Promise.reject(error)` opretter et afvist promise med `error`. -Same as: +Det samme som: ```js let promise = new Promise((resolve, reject) => reject(error)); ``` -In practice, this method is almost never used. +I praksis bliver denne metode næsten aldrig brugt. -## Summary +## Opsummering -There are 6 static methods of `Promise` class: +Der er 6 statiske metoder i `Promise`-klassen: -1. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, it becomes the error of `Promise.all`, and all other results are ignored. -2. `Promise.allSettled(promises)` (recently added method) -- waits for all promises to settle and returns their results as an array of objects with: - - `status`: `"fulfilled"` or `"rejected"` - - `value` (if fulfilled) or `reason` (if rejected). -3. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome. -4. `Promise.any(promises)` (recently added method) -- waits for the first promise to fulfill, and its result becomes the outcome. If all of the given promises are rejected, [`AggregateError`](mdn:js/AggregateError) becomes the error of `Promise.any`. -5. `Promise.resolve(value)` -- makes a resolved promise with the given value. -6. `Promise.reject(error)` -- makes a rejected promise with the given error. +1. `Promise.all(promises)` -- venter på at alle promises bliver opfyldt og returnerer en array med deres resultater. Hvis en af de givne promises bliver afvist, bliver det til fejlen i `Promise.all`, og alle andre resultater ignoreres. +2. `Promise.allSettled(promises)` (nylig tilføjet metode) -- venter på at alle promises bliver løst og returnerer deres resultater som en array af objekter med: + - `status`: `"fulfilled"` eller `"rejected"` + - `value` (hvis opfyldt) eller `reason` (hvis afvist). +3. `Promise.race(promises)` -- venter på at det første promise bliver løst (opfyldt eller afvist), og dets resultat/fejl bliver resultatet. +4. `Promise.any(promises)` -- venter på at det første promise bliver opfyldt, og dets resultaat bliver resultatet. Hvis alle de givne promises bliver afvist, bliver [`AggregateError`](mdn:js/AggregateError) til fejlen i `Promise.any`. +5. `Promise.resolve(value)` -- opretter et opfyldt promise med det givne værdi. +6. `Promise.reject(error)` -- opretter et afvist promise med det givne fejl. -Of all these, `Promise.all` is probably the most common in practice. +Af alle disse er `Promise.all` nok den mest brugte i praksis. From 5a036fa8b492622e0d222b73de68e634f8956b44 Mon Sep 17 00:00:00 2001 From: ockley Date: Tue, 24 Mar 2026 22:08:33 +0100 Subject: [PATCH 19/33] oversat til dansk --- 1-js/11-async/06-promisify/article.md | 73 ++++++++++++++------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md index 855678e5b..2292d979c 100644 --- a/1-js/11-async/06-promisify/article.md +++ b/1-js/11-async/06-promisify/article.md @@ -1,12 +1,12 @@ -# Promisification +# Promisificering -"Promisification" is a long word for a simple transformation. It's the conversion of a function that accepts a callback into a function that returns a promise. +"Promisificering" er et langt ord for en simpel transformation. Det er konverteringen af en function, der accepterer en callback, til en function, der returnerer et promise. -Such transformations are often required in real-life, as many functions and libraries are callback-based. But promises are more convenient, so it makes sense to promisify them. +Sådanne transformationer er ofte nødvendige i det virkelige liv, da mange funktioner og biblioteker er callback-baserede. Men promises er mere praktiske, så det giver mening at promisificere dem. -For better understanding, let's see an example. +For bedre at forstå det så lad os se et ekssempel. -For instance, we have `loadScript(src, callback)` from the chapter . +Her er for eksempel `loadScript(src, callback)` fra kapitlet . ```js run function loadScript(src, callback) { @@ -19,19 +19,19 @@ function loadScript(src, callback) { document.head.append(script); } -// usage: +// brug: // loadScript('path/script.js', (err, script) => {...}) ``` -The function loads a script with the given `src`, and then calls `callback(err)` in case of an error, or `callback(null, script)` in case of successful loading. That's a widespread agreement for using callbacks, we saw it before. +Funktionen henter et script der gives med `src`, og så kalder den `callback(err)` i tilfælde af en fejl, eller `callback(null, script)` i tilfælde af succesfuld indlæsning. Det er en almindelig overenskomst for brug af callbacks, som vi har set før. -Let's promisify it. +Lad os promisificere det. -We'll make a new function `loadScriptPromise(src)`, that does the same (loads the script), but returns a promise instead of using callbacks. +Vi vil lave en ny function `loadScriptPromise(src)`, som gør det samme (henter scriptet), men returnerer et promise i stedet for at bruge callbacks. -In other words, we pass it only `src` (no `callback`) and get a promise in return, that resolves with `script` when the load is successful, and rejects with the error otherwise. +Med andre ord, vi sender kun `src` (ingen `callback`) og får et promise tilbage, som løser sig med `script` når indlæsningen er successful, og afvises med fejlen ellers. -Here it is: +Her er det: ```js let loadScriptPromise = function(src) { return new Promise((resolve, reject) => { @@ -42,23 +42,23 @@ let loadScriptPromise = function(src) { }); }; -// usage: +// brug: // loadScriptPromise('path/script.js').then(...) ``` -As we can see, the new function is a wrapper around the original `loadScript` function. It calls it providing its own callback that translates to promise `resolve/reject`. +Som vi kan se er den nye function en wrapper omkring den originale `loadScript` function. Den kalder den og giver sin egen callback, som oversætter til et promise med `resolve/reject`. -Now `loadScriptPromise` fits well in promise-based code. If we like promises more than callbacks (and soon we'll see more reasons for that), then we will use it instead. +Nu passer `loadScriptPromise` ind i promise-baseret kode. Hvis vi kan lide promises bedre end callbacks (og senere vil vi se flere grunde for det), så kan vi bruge den i stedet. -In practice we may need to promisify more than one function, so it makes sense to use a helper. +I praksis vil vi måske have brug for at promisificere flere funktioner, så det giver mening at bruge en hjælper funktion. -We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function. +Vi vil kalde den `promisify(f)`: den accepterer en function `f` der skal promisificeres og returnerer en wrapper function. ```js function promisify(f) { - return function (...args) { // return a wrapper-function (*) + return function (...args) { // returner en wrapper-funktion (*) return new Promise((resolve, reject) => { - function callback(err, result) { // our custom callback for f (**) + function callback(err, result) { // vores brugerdefinerede callback til f (**) if (err) { reject(err); } else { @@ -66,9 +66,9 @@ function promisify(f) { } } - args.push(callback); // append our custom callback to the end of f arguments + args.push(callback); // tilføj vores brugerdefinerede callback til slutningen af f's argumenter - f.call(this, ...args); // call the original function + f.call(this, ...args); // kald den originale funktion }); }; } @@ -78,29 +78,30 @@ let loadScriptPromise = promisify(loadScript); loadScriptPromise(...).then(...); ``` -The code may look a bit complex, but it's essentially the same that we wrote above, while promisifying `loadScript` function. +Koden kan se lidt kompleks ud, men det er grundlæggende det samme, som vi skrev ovenfor, da vi promisificerede `loadScript` funktionen. -A call to `promisify(f)` returns a wrapper around `f` `(*)`. That wrapper returns a promise and forwards the call to the original `f`, tracking the result in the custom callback `(**)`. +Et kald til `promisify(f)` returnerer en wrapper omkring `f` `(*)`. Den wrapper returnerer et promise og videreformidler kaldet til den originale `f`, som sporer resultatet i den brugerdefinerede callback `(**)`. -Here, `promisify` assumes that the original function expects a callback with exactly two arguments `(err, result)`. That's what we encounter most often. Then our custom callback is in exactly the right format, and `promisify` works great for such a case. +Her regner `promisify` med at den originale funktion forventer en callback med præcis to argumenter `(err, result)`. Det er det, vi oftest støder på. Så vores brugerdefinerede callback er i præcis det rigtige format, og `promisify` virker godt for sådanne tilfælde. -But what if the original `f` expects a callback with more arguments `callback(err, res1, res2, ...)`? +Men hvad hvis den originale `f` forventer en callback med flere argumenter `callback(err, res1, res2, ...)`? -We can improve our helper. Let's make a more advanced version of `promisify`. +Vi kan forbedre vores hjælper. Lad os lave en mere avanceret version af `promisify`. -- When called as `promisify(f)` it should work similar to the version above. -- When called as `promisify(f, true)`, it should return the promise that resolves with the array of callback results. That's exactly for callbacks with many arguments. +- Når den kaldes som `promisify(f)` bør den fungere ligesom versionen ovenfor. +- Når den kaldes som `promisify(f, true)`, bør den returnere det promise, der løser sig med arrayet af callback-resultater. Det er netop til brug for callbacks med mange argumenter. ```js -// promisify(f, true) to get array of results +// promisify(f, true) for at få et array af resultater function promisify(f, manyArgs = false) { return function (...args) { return new Promise((resolve, reject) => { - function *!*callback(err, ...results*/!*) { // our custom callback for f + function *!*callback(err, ...results*/!*) { // vores brugerdefinrerede callback for f if (err) { reject(err); } else { - // resolve with all callback results if manyArgs is specified + // resolve med array af alle callback-resultater hvis manyArgs er specificeret, + // ellers resolve med det første resultat *!*resolve(manyArgs ? results : results[0]);*/!* } } @@ -117,16 +118,16 @@ f = promisify(f, true); f(...).then(arrayOfResults => ..., err => ...); ``` -As you can see it's essentially the same as above, but `resolve` is called with only one or all arguments depending on whether `manyArgs` is truthy. +Som du kan se er det stort set det samme som ovenfor, men `resolve` kaldes med kun ét eller alle argumenter afhængigt af om `manyArgs` er sand. -For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper. +For mere eksotiske callback-formater, som dem uden `err` overhovedet: `callback(result)`, kan vi promisificere sådanne funktioner manuelt uden at bruge hjælperen. -There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that. +Der er også moduler med lidt mere fleksible promisification-funktioner, f.eks. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). I Node.js er der en indbygget `util.promisify`-funktion til det. ```smart -Promisification is a great approach, especially when you use `async/await` (covered later in the chapter ), but not a total replacement for callbacks. +Promisificering er at god tilgang, særligt hvis du bruger `async/await` (som vi viser senere i kapitlet ), men ikke ment som en total erstatning for callbacks. -Remember, a promise may have only one result, but a callback may technically be called many times. +Husk, at et promise kun kan have ét resultat, mens en callback teknisk set kan kaldes mange gange. -So promisification is only meant for functions that call the callback once. Further calls will be ignored. +Så promisificering er kun ment til funktioner, der kalder callback'en én gang. Yderligere kald vil blive ignoreret. ``` From b0fdbd3fbc8d2509839b4a07007b08d838504197 Mon Sep 17 00:00:00 2001 From: ockley Date: Tue, 24 Mar 2026 22:34:27 +0100 Subject: [PATCH 20/33] Oversat til dansk --- 1-js/11-async/07-microtask-queue/article.md | 90 +++++++++---------- .../07-microtask-queue/promiseQueue.svg | 82 ++++++++++++++++- 2 files changed, 126 insertions(+), 46 deletions(-) diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md index 014dd93c0..47cbe0d24 100644 --- a/1-js/11-async/07-microtask-queue/article.md +++ b/1-js/11-async/07-microtask-queue/article.md @@ -1,112 +1,112 @@ # Microtasks -Promise handlers `.then`/`.catch`/`.finally` are always asynchronous. +Promise handlers `.then`/`.catch`/`.finally` er altid asynkrone. -Even when a Promise is immediately resolved, the code on the lines *below* `.then`/`.catch`/`.finally` will still execute before these handlers. +Selv når et promise løses umiddelbart, vil koden på linjerne *under* `.then`/`.catch`/`.finally` stadig køre før disse handlers. -Here's a demo: +Her er et eksempel: ```js run let promise = Promise.resolve(); -promise.then(() => alert("promise done!")); +promise.then(() => alert("promise færdig!")); -alert("code finished"); // this alert shows first +alert("kode færdig"); // denne kode vises først ``` -If you run it, you see `code finished` first, and then `promise done!`. +Hvis du kører det, ser du `kode færdig` først, og så `promise færdig!`. -That's strange, because the promise is definitely done from the beginning. +Det er mærkeligt, fordi promise er helt sikkert færdig fra starten. -Why did the `.then` trigger afterwards? What's going on? +Hvorfor aktiverede `.then` efterfølgende? Hvad sker der? ## Microtasks queue -Asynchronous tasks need proper management. For that, the ECMA standard specifies an internal queue `PromiseJobs`, more often referred to as the "microtask queue" (V8 term). +Asynkrone opgaver har brug for korrekt håndtering. Til det formål specificerer ECMA-standarden en intern kø `PromiseJobs`, som ofte refereres til som "microtask queue" (V8 term). -As stated in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues): +Som angivet i [specifikationen](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues): -- The queue is first-in-first-out: tasks enqueued first are run first. -- Execution of a task is initiated only when nothing else is running. +- Køen er first-in-first-out: opgaver, der er sat i køen først, køres først. +- Udførelse af en opgave initieres kun, når intet andet er i gang. -Or, to put it more simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue; they are not executed yet. When the JavaScript engine becomes free from the current code, it takes a task from the queue and executes it. +Eller, sagt på en simpel måde, når et promise er færdigt, bliver dets `.then/catch/finally` handlers sat i køen; de bliver ikke udført endnu. Når JavaScript-motoren bliver ledig fra den nuværende kode, tager den en opgave fra køen og udfører den. -That's why "code finished" in the example above shows first. +Det er derfor "kode færdig" i eksemplet ovenfor vises først. ![](promiseQueue.svg) -Promise handlers always go through this internal queue. +Promise handlers går altid gennem denne interne kø. -If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, then executed when the current code is complete and previously queued handlers are finished. +Hvis der er en kæde med flere `.then/catch/finally`, så udføres hver enkelt asynkront. Det vil sige, at de først sættes i køen, og derefter udføres, når den nuværende kode er færdig og tidligere satte handlers er færdige. -**What if the order matters for us? How can we make `code finished` appear after `promise done`?** +**Hvad hvis rækkefølgen er vigtig for os? Hvordan kan vi få `kode færdig` til at vises efter `promise færdig`?** -Easy, just put it into the queue with `.then`: +Simpelthen ved at putte det i køen med `.then`: ```js run Promise.resolve() - .then(() => alert("promise done!")) - .then(() => alert("code finished")); + .then(() => alert("promise færdig!")) + .then(() => alert("kode færdig")); ``` -Now the order is as intended. +Nu er rækkefølgen som ønsket. -## Unhandled rejection +## Uhåndteret afvisning -Remember the `unhandledrejection` event from the article ? +Husker du `unhandledrejection` eventen fra artiklen ? -Now we can see exactly how JavaScript finds out that there was an unhandled rejection. +Nu kan vi se præcis hvordan JavaScript finder ud af, at der er en uhåndteret afvisning. -**An "unhandled rejection" occurs when a promise error is not handled at the end of the microtask queue.** +**En "uhåndteret afvisning" opstår, når en promise fejl ikke håndteres i slutningen af microtask-køen.** -Normally, if we expect an error, we add `.catch` to the promise chain to handle it: +Normalt, hvis vi forventer en fejl, tilføjer vi `.catch` til promise-kæden for at håndtere den: ```js run -let promise = Promise.reject(new Error("Promise Failed!")); +let promise = Promise.reject(new Error("Promise fejlet!")); *!* -promise.catch(err => alert('caught')); +promise.catch(err => alert('fanget')); */!* -// doesn't run: error handled +// kører ikke: fejlen er håndteret window.addEventListener('unhandledrejection', event => alert(event.reason)); ``` -But if we forget to add `.catch`, then, after the microtask queue is empty, the engine triggers the event: +Men hvis vi glemmer at tilføje `.catch`, så udløser motoren eventet, efter at microtask-køen er tom, og den ser, at der er en promise i "afvist" tilstand: ```js run -let promise = Promise.reject(new Error("Promise Failed!")); +let promise = Promise.reject(new Error("Promise fejlet!")); -// Promise Failed! +// Promise fejlet! window.addEventListener('unhandledrejection', event => alert(event.reason)); ``` -What if we handle the error later? Like this: +Hvad hvis vi håndterer fejlen senere? Sådan her: ```js run -let promise = Promise.reject(new Error("Promise Failed!")); +let promise = Promise.reject(new Error("Promise fejlet!")); *!* -setTimeout(() => promise.catch(err => alert('caught')), 1000); +setTimeout(() => promise.catch(err => alert('fanget')), 1000); */!* -// Error: Promise Failed! +// Error: Promise fejlet! window.addEventListener('unhandledrejection', event => alert(event.reason)); ``` -Now, if we run it, we'll see `Promise Failed!` first and then `caught`. +Hvis vi kører koden vil vi se `Promise fejlet!` først og derefter `fanget`. -If we didn't know about the microtasks queue, we could wonder: "Why did `unhandledrejection` handler run? We did catch and handle the error!" +Hvis vi ikke kendte til microtasks-køen, kunne vi undre os: "Hvorfor kørte `unhandledrejection` handleren? Vi fangede og håndterede fejlen!" -But now we understand that `unhandledrejection` is generated when the microtask queue is complete: the engine examines promises and, if any of them is in the "rejected" state, then the event triggers. +Men nu forstår vi, at `unhandledrejection` genereres, når microtask-køen er tom: motoren undersøger promises, og hvis en af dem er i "rejected" tilstand, så udløser eventet. -In the example above, `.catch` added by `setTimeout` also triggers. But it does so later, after `unhandledrejection` has already occurred, so it doesn't change anything. +I eksemplet ovenfor kan vi se et `.catch` blive tilføjet af `setTimeout` som også udløses - men det sker senere. På det tidspunkt er `unhandledrejection` allerede opstået, så det ændrer ikke på noget. -## Summary +## Opsummering -Promise handling is always asynchronous, as all promise actions pass through the internal "promise jobs" queue, also called "microtask queue" (V8 term). +Håndtering af promises er altid asynkron, da alle promise-handlinger går gennem den interne "promise jobs" kø, også kaldet "microtask kø" (V8 term). -So `.then/catch/finally` handlers are always called after the current code is finished. +Så `.then/catch/finally` handlers kaldes altid efter, at den nuværende kode er færdig. -If we need to guarantee that a piece of code is executed after `.then/catch/finally`, we can add it into a chained `.then` call. +Hvis vi har brug for at garantere, at en del af koden bliver udført efter `.then/catch/finally`, kan vi tilføje den til en kædet `.then` kald. -In most Javascript engines, including browsers and Node.js, the concept of microtasks is closely tied with the "event loop" and "macrotasks". As these have no direct relation to promises, they are covered in another part of the tutorial, in the article . +I de fleste Javascript motorer, inklusiv browsere og Node.js, er konceptet microtasks tæt forbundet med "event loop" og "macrotasks". Da disse ikke har en direkte relation til promises, er de behandlet i en anden del af tutorialen, i artiklen . diff --git a/1-js/11-async/07-microtask-queue/promiseQueue.svg b/1-js/11-async/07-microtask-queue/promiseQueue.svg index c802c44a0..fbc635c79 100644 --- a/1-js/11-async/07-microtask-queue/promiseQueue.svg +++ b/1-js/11-async/07-microtask-queue/promiseQueue.svg @@ -1 +1,81 @@ -promise . then ( handler ); ... alert ( "code finished" );handler enqueuedqueued handler runsscript execution finished \ No newline at end of file + + + + + + + + + + promise + . + then + ( + handler + ); + ... + alert + ( + "kode færdig" + ); + + + + handler sat i kø + + + handlere i kø afvikles + + + + + + + script udførsel færdig + + + + + \ No newline at end of file From b78ad5b9faa0f195caaba0ffe2f16999e6097177 Mon Sep 17 00:00:00 2001 From: ockley Date: Wed, 25 Mar 2026 00:31:19 +0100 Subject: [PATCH 21/33] Oversat til dansk --- .../01-rewrite-async/solution.md | 14 +- .../08-async-await/01-rewrite-async/task.md | 4 +- .../02-rewrite-async-2/solution.md | 16 +- .../08-async-await/02-rewrite-async-2/task.md | 14 +- .../03-async-from-regular/solution.md | 7 +- .../03-async-from-regular/task.md | 12 +- .../04-promise-all-failure/solution.md | 48 +++--- .../04-promise-all-failure/task.md | 38 ++--- 1-js/11-async/08-async-await/article.md | 144 +++++++++--------- 9 files changed, 149 insertions(+), 148 deletions(-) diff --git a/1-js/11-async/08-async-await/01-rewrite-async/solution.md b/1-js/11-async/08-async-await/01-rewrite-async/solution.md index 3337ef3c4..c429b310b 100644 --- a/1-js/11-async/08-async-await/01-rewrite-async/solution.md +++ b/1-js/11-async/08-async-await/01-rewrite-async/solution.md @@ -1,5 +1,5 @@ -The notes are below the code: +Noterne er under koden: ```js run async function loadJson(url) { // (1) @@ -17,11 +17,11 @@ loadJson('https://javascript.info/no-such-user.json') .catch(alert); // Error: 404 (4) ``` -Notes: +Noter: -1. The function `loadJson` becomes `async`. -2. All `.then` inside are replaced with `await`. -3. We can `return response.json()` instead of awaiting for it, like this: +1. Funktionen `loadJson` bliver `async`. +2. Alle `.then` indeni erstattes med `await`. +3. Vi kan bruge `return response.json()` i stedet for at vente på det, som dette: ```js if (response.status == 200) { @@ -29,5 +29,5 @@ Notes: } ``` - Then the outer code would have to `await` for that promise to resolve. In our case it doesn't matter. -4. The error thrown from `loadJson` is handled by `.catch`. We can't use `await loadJson(…)` there, because we're not in an `async` function. + Den ydre kode vil være nødt til at vente med `await` på at det promise løses. I vores tilfælde spiller det ikke en rolle. +4. Fejlen kastet fra `loadJson` håndteres af `.catch`. Vi kan ikke bruge `await loadJson(…)` der, fordi vi ikke er i en `async` funktion. diff --git a/1-js/11-async/08-async-await/01-rewrite-async/task.md b/1-js/11-async/08-async-await/01-rewrite-async/task.md index 0c31737da..5d057483d 100644 --- a/1-js/11-async/08-async-await/01-rewrite-async/task.md +++ b/1-js/11-async/08-async-await/01-rewrite-async/task.md @@ -1,7 +1,7 @@ -# Rewrite using async/await +# Omskriv ved brug af async/await -Rewrite this example code from the chapter using `async/await` instead of `.then/catch`: +Omskriv dette eksempelkode fra kapitlet ved hjælp af `async/await` i stedet for `.then/catch`: ```js run function loadJson(url) { diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md index aa462dbf7..8826237d4 100644 --- a/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md +++ b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md @@ -1,5 +1,5 @@ -There are no tricks here. Just replace `.catch` with `try..catch` inside `demoGithubUser` and add `async/await` where needed: +Der er ikke nogen tricks her. Bare erstat `.catch` med `try..catch` inde i `demoGithubUser` og tilføj `async/await` hvor det er nødvendigt: ```js run class HttpError extends Error { @@ -19,29 +19,29 @@ async function loadJson(url) { } } -// Ask for a user name until github returns a valid user +// Spørg efter et brugernavn indtil github returnerer en gyldig bruger async function demoGithubUser() { let user; while(true) { - let name = prompt("Enter a name?", "iliakan"); + let name = prompt("Skriv et brugernavn?", "iliakan"); try { user = await loadJson(`https://api.github.com/users/${name}`); - break; // no error, exit loop + break; // ingen fejl, hop ud af loopet } catch(err) { if (err instanceof HttpError && err.response.status == 404) { - // loop continues after the alert - alert("No such user, please reenter."); + // loop fortsætter efter alert + alert("Brugeren findes ikke. Indtast et nyt brugernavn."); } else { - // unknown error, rethrow + // ukendt fejl, rethrow throw err; } } } - alert(`Full name: ${user.name}.`); + alert(`Fulde navn: ${user.name}.`); return user; } diff --git a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md index 13d625d2a..39708ee30 100644 --- a/1-js/11-async/08-async-await/02-rewrite-async-2/task.md +++ b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md @@ -1,9 +1,9 @@ -# Rewrite "rethrow" with async/await +# Omskriv "rethrow" med async/await -Below you can find the "rethrow" example. Rewrite it using `async/await` instead of `.then/catch`. +Nedenfor ser du "rethrow" eksemplet. Omskriv det ved hjælp af `async/await` i stedet for `.then/catch`. -And get rid of the recursion in favour of a loop in `demoGithubUser`: with `async/await` that becomes easy to do. +Og erstat rekursionen med et loop i `demoGithubUser`: med `async/await` bliver det nemt at gøre. ```js run class HttpError extends Error { @@ -25,18 +25,18 @@ function loadJson(url) { }); } -// Ask for a user name until github returns a valid user +// Spørg efter et brugernavn indtil github returnerer en gyldig bruger function demoGithubUser() { - let name = prompt("Enter a name?", "iliakan"); + let name = prompt("Skriv et brugernavn?", "iliakan"); return loadJson(`https://api.github.com/users/${name}`) .then(user => { - alert(`Full name: ${user.name}.`); + alert(`Fulde navn: ${user.name}.`); return user; }) .catch(err => { if (err instanceof HttpError && err.response.status == 404) { - alert("No such user, please reenter."); + alert("Brugeren findes ikke. Indtast et nyt brugernavn."); return demoGithubUser(); } else { throw err; diff --git a/1-js/11-async/08-async-await/03-async-from-regular/solution.md b/1-js/11-async/08-async-await/03-async-from-regular/solution.md index 7e2ab597e..ce5151d42 100644 --- a/1-js/11-async/08-async-await/03-async-from-regular/solution.md +++ b/1-js/11-async/08-async-await/03-async-from-regular/solution.md @@ -1,7 +1,8 @@ -That's the case when knowing how it works inside is helpful. +Det er her hvor det er godt at vide hvordan det virker inde i motorrummet. + +Du kan bare behandle `async` kald som et promise og tilføje `.then` til det: -Just treat `async` call as promise and attach `.then` to it: ```js run async function wait() { await new Promise(resolve => setTimeout(resolve, 1000)); @@ -10,7 +11,7 @@ async function wait() { } function f() { - // shows 10 after 1 second + // viser 10 efter 1 sekund *!* wait().then(result => alert(result)); */!* diff --git a/1-js/11-async/08-async-await/03-async-from-regular/task.md b/1-js/11-async/08-async-await/03-async-from-regular/task.md index ca7c186ff..e6f97e303 100644 --- a/1-js/11-async/08-async-await/03-async-from-regular/task.md +++ b/1-js/11-async/08-async-await/03-async-from-regular/task.md @@ -1,7 +1,7 @@ -# Call async from non-async +# Kald async fra ikke-async -We have a "regular" function called `f`. How can you call the `async` function `wait()` and use its result inside of `f`? +Vi har en "normal" function kaldet `f`. Hvordan kan du kalde den `async` function `wait()` og bruge dens resultat inde i `f`? ```js async function wait() { @@ -11,10 +11,10 @@ async function wait() { } function f() { - // ...what should you write here? - // we need to call async wait() and wait to get 10 - // remember, we can't use "await" + // ... hvad skal vi skrive her? + // vi har brug for at kalde den asynkrone wait() og vente på at få 10 + // husk, vi kan ikke bruge "await" } ``` -P.S. The task is technically very simple, but the question is quite common for developers new to async/await. +P.S. Opgaven er teknisk set meget simpel, men spørgsmålet er ganske almindeligt for udviklere, der er nye i async/await. diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md index 9fda8e000..134d1adc9 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/solution.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/solution.md @@ -1,52 +1,52 @@ -The root of the problem is that `Promise.all` immediately rejects when one of its promises rejects, but it do nothing to cancel the other promises. +Roden til problemet er, at `Promise.all` umiddelbart afviser, når en af dens promises afviser, men den gør intet for at annullere de andre promises. -In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error.Meanwhile, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught, we can see it in the console. +I vores tilfælde fejler den anden forespørgsel, så `Promise.all` afviser, og `try...catch` blokken fanger denne fejl. Mens de andre promises ikke er påvirket - de fortsætter uafhængigt deres eksekvering. I vores tilfælde kaster den tredje forespørgsel en fejl selv efter et stykke tid. Og den fejl bliver aldrig fanget, vi kan se den i konsollen. -The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash. +Problemet er især farligt i server-side miljøer, såsom Node.js, hvor en ikke-fanget fejl kan forårsage, at processen går ned. -How to fix it? +Hvordan fikser vi det? -An ideal solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors. +En idéel løsning ville være at annullere alle uafsluttede forespørgsler, når en af dem fejler. På denne måde undgår vi eventuelle fejl. -However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call. +Men den dårlige nyheder er, at servicekald (såsom `database.query`) ofte er implementeret af en 3rd-parts bibliotek, som ikke understøtter annullering. Så der er ingen måde at annullere et kald. -As an alternative, we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored. +Som et alternativ kan vi skrive vores egen wrapper omkring `Promise.all` som tilføjer en custom `then/catch` handler til hver promise for at spore dem: resultaterne samles og, hvis en fejl opstår, ignoreres alle efterfølgende promises. ```js function customPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; let resultsCount = 0; - let hasError = false; // we'll set it to true upon first error + let hasError = false; // vi sætter den til true ved første fejl vi møder promises.forEach((promise, index) => { promise .then(result => { - if (hasError) return; // ignore the promise if already errored + if (hasError) return; // ignorer promise hvis den allerede er fejlet results[index] = result; resultsCount++; if (resultsCount === promises.length) { - resolve(results); // when all results are ready - successs + resolve(results); // når alle resultater er klar - succes } }) .catch(error => { - if (hasError) return; // ignore the promise if already errored - hasError = true; // wops, error! - reject(error); // fail with rejection + if (hasError) return; // ignorer promise hvis den allerede er fejlet + hasError = true; // ups, fejl! + reject(error); // fejl med reject }); }); }); } ``` -This approach has an issue of its own - it's often undesirable to `disconnect()` when queries are still in the process. +Denne tilgang har sine egne udfordringer - det er ofte uønsket at kalde `disconnect()` når der stadig er forespørgsler i processen. -It may be important that all queries complete, especially if some of them make important updates. +Det kan være vigtigt at alle forespørgsler gennemføres, især hvis nogle af dem indeholder vigtige opdateringer. -So we should wait until all promises are settled before going further with the execution and eventually disconnecting. +Så vi bør vente indtil alle promise er afsluttet, før vi går videre med eksekveringen og til sidst frakobler. -Here's another implementation. It behaves similar to `Promise.all` - also resolves with the first error, but waits until all promises are settled. +Her er en anden implementering. Den opfører sig i stil med `Promise.all` - den resolver også ved den første fejl, men venter indtil alle promise er afsluttet. ```js function customPromiseAllWait(promises) { @@ -80,16 +80,16 @@ function customPromiseAllWait(promises) { } ``` -Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed. +Nu vil `await customPromiseAllWait(...)` tilbageholde udførelsen indtil alle forespørgsler er behandlet. Hvis der opstår en fejl, vil den blive fanget i `try...catch` blokken, og vi kan være sikre på, at alle forespørgsler er afsluttet, før vi går videre. -This is a more reliable approach, as it guarantees a predictable execution flow. +Dette er en mere pålidelig tilgang, da den garanterer et forudsigeligt eksekveringsflow. -Lastly, if we'd like to process all errors, we can use either use `Promise.allSettled` or write a wrapper around it to gathers all errors in a single [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) object and rejects with it. +Til sidst, hvis vi vil behandle alle fejl, kan vi bruge enten `Promise.allSettled` eller skrive en wrapper omkring for at samle alle fejl i et enkelt [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) objekt og afvise med det. ```js -// wait for all promises to settle -// return results if no errors -// throw AggregateError with all errors if any +// vent på at alle promise er afsluttet +// returner resultater hvis ingen fejl +// kast AggregateError med alle fejl hvis nogen function allOrAggregateError(promises) { return Promise.allSettled(promises).then(results => { const errors = []; @@ -104,7 +104,7 @@ function allOrAggregateError(promises) { }); if (errors.length > 0) { - throw new AggregateError(errors, 'One or more promises failed'); + throw new AggregateError(errors, 'En eller flere promises fejlede'); } return values; diff --git a/1-js/11-async/08-async-await/04-promise-all-failure/task.md b/1-js/11-async/08-async-await/04-promise-all-failure/task.md index 74571c43e..6b6b4773d 100644 --- a/1-js/11-async/08-async-await/04-promise-all-failure/task.md +++ b/1-js/11-async/08-async-await/04-promise-all-failure/task.md @@ -1,17 +1,17 @@ -# Dangerous Promise.all +# Farlig Promise.all -`Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services. +`Promise.all` er en fantastisk måde at køre flere operationer parallelt. Det er især nyttigt når vi har brug for at lave flere forespørgsler til forskellige services samtidigt. -However, there's a hidden danger. We'll see an example in this task and explore how to avoid it. +Men, der er en skjult fare. Vi vil se et eksempel i denne opgave og udforske, hvordan man undgår den. -Let's say we have a connection to a remote service, such as a database. +Lad os sige, vi har en forbindelse til en ekstern service, såsom en database. -There're two functions: `connect()` and `disconnect()`. +Der er to funktioner: `connect()` og `disconnect()`. -When connected, we can send requests using `database.query(...)` - an async function which usually returns the result but also may throw an error. +Når den er forbundet, kan vi sende forespørgsler ved hjælp af `database.query(...)` - en async function, som normalt returnerer resultatet, men også kan kaste en fejl. -Here's a simple implementation: +Her er en simpel implementering af det: ```js let database; @@ -28,21 +28,21 @@ function disconnect() { database = null; } -// intended usage: +// beregnet brug: // connect() // ... -// database.query(true) to emulate a successful call -// database.query(false) to emulate a failed call +// database.query(true) for at emulere et succesfuldt kald +// database.query(false) for at emulere et mislykket kald // ... // disconnect() ``` -Now here's the problem. +Se her er problemet. -We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect: +Vi skrev koden til at forbinde og sende 3 forespørgsler parallelt (alle tager forskellig tid, f.eks. 100, 200 og 300 ms), for derefter at frakoble igen: ```js -// Helper function to call async function `fn` after `ms` milliseconds +// Hjælperfunktion til at kalde async funktion `fn` efter `ms` millisekunder function delay(fn, ms) { return new Promise((resolve, reject) => { setTimeout(() => fn().then(resolve, reject), ms); @@ -54,8 +54,8 @@ async function run() { try { await Promise.all([ - // these 3 parallel jobs take different time: 100, 200 and 300 ms - // we use the `delay` helper to achieve this effect + // disse 3 paralelle jobs tager forskellig tid: 100, 200 og 300 ms + // vi bruger `delay` hjælperen for at opnå denne effekt *!* delay(() => database.query(true), 100), delay(() => database.query(false), 200), @@ -63,7 +63,7 @@ async function run() { */!* ]); } catch(error) { - console.log('Error handled (or was it?)'); + console.log('Fejl håndteret (eller er den?)'); } disconnect(); @@ -72,8 +72,8 @@ async function run() { run(); ``` -Two of these queries happen to be unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try..catch` block. +To af disse forespørgsler viser sig at fejle, men vi var smarte nok til at wrappe `Promise.all` kaldet i en `try..catch` block. -However, this doesn't help! This script actually leads to an uncaught error in console! +Men lige meget hvad, så hjælper det ikke! Dette script fører faktisk til en ikke-fanget fejl i konsollen! -Why? How to avoid it? \ No newline at end of file +Hvorfor? Hvordan undgår man det? \ No newline at end of file diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md index e679b1c4c..c83b794ac 100644 --- a/1-js/11-async/08-async-await/article.md +++ b/1-js/11-async/08-async-await/article.md @@ -1,10 +1,10 @@ # Async/await -There's a special syntax to work with promises in a more comfortable fashion, called "async/await". It's surprisingly easy to understand and use. +Der er en speciel syntaks der bruges til at arbejde med promises på en mere komfortabel måde, kaldet "async/await". Den er overraskende let at forstå og bruge. ## Async functions -Let's start with the `async` keyword. It can be placed before a function, like this: +Lad os starte med nøgleordet `async`. Det kan placeres før en function, sådan her: ```js async function f() { @@ -12,9 +12,9 @@ async function f() { } ``` -The word "async" before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically. +Ordet "async" før en function betyder en simpel ting: en function returnerer altid en promise. Andre værdier er automatisk pakket ind i en løst (resolved) promise. -For instance, this function returns a resolved promise with the result of `1`; let's test it: +For eksempel, denne funktion returnerer et løst promise med resultatet `1`; lad os teste den: ```js run async function f() { @@ -24,7 +24,7 @@ async function f() { f().then(alert); // 1 ``` -...We could explicitly return a promise, which would be the same: +... vi kunne også eksplicit returnere et promise, hvilket ville være det samme: ```js run async function f() { @@ -34,20 +34,20 @@ async function f() { f().then(alert); // 1 ``` -So, `async` ensures that the function returns a promise, and wraps non-promises in it. Simple enough, right? But not only that. There's another keyword, `await`, that works only inside `async` functions, and it's pretty cool. +Så `async` sikrer at funktionen returnerer et promise, og pakker ikke-promise værdier ind i et løst promise. Enkelt nok, ikke? Men ikke kun det. Der er endnu et nøgleord, `await`, som kun virker inde i `async` funktioner, og det er ret sejt. ## Await -The syntax: +Syntaksen er: ```js -// works only inside async functions +// virker kun inde i async funktioner let value = await promise; ``` -The keyword `await` makes JavaScript wait until that promise settles and returns its result. +Nøgleordet `await` gør at JavaScript venter indtil det promise er løst og returnerer dets resultat. -Here's an example with a promise that resolves in 1 second: +Her er et eksempel med et promise, der løses om 1 sekund: ```js run async function f() { @@ -56,23 +56,23 @@ async function f() { }); *!* - let result = await promise; // wait until the promise resolves (*) + let result = await promise; // vent indtil promise løses (*) */!* - alert(result); // "done!" + alert(result); // "færdig!" } f(); ``` -The function execution "pauses" at the line `(*)` and resumes when the promise settles, with `result` becoming its result. So the code above shows "done!" in one second. +Udførelsen af funktionen "sættes på pause" ved linjen `(*)` og fortsætter når promise løses, med `result` som dens resultat. Koden ovenfor vil vise "færdig!" efter 1 sekund. -Let's emphasize: `await` literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn't cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc. +Lad os understrege: `await` udsætter eksekvering af funktionen indtil promise løses og fortsætter med det modtagne resultat. Det koster ikke nogen CPU ressourcer, fordi JavaScript motoren kan udføre andre job i mellemtiden: udføre andre scripts, håndtere events etc. -It's just a more elegant syntax of getting the promise result than `promise.then`. And, it's easier to read and write. +Det er bare en mere elegant syntaks for at få resultatet af et promise end `promise.then`. Og det er lettere at læse og skrive. -````warn header="Can't use `await` in regular functions" -If we try to use `await` in a non-async function, there would be a syntax error: +````warn header="Du kan ikke bruge `await` i regulære funktioner" +HVis vi prøver at bruge `await` i en ikke-async funktion, vil der være en syntaxfejl: ```js run function f() { @@ -83,32 +83,32 @@ function f() { } ``` -We may get this error if we forget to put `async` before a function. As stated earlier, `await` only works inside an `async` function. +Vi får den fejl hvis vi glemmer at tilføje `async` før en funktion. Som nævnt tidligere, virker `await` kun inde i en `async` funktion. ```` -Let's take the `showAvatar()` example from the chapter and rewrite it using `async/await`: +Lad os tage `showAvatar()` eksemplet fra kapitlet og omskrive det ved hjælp af `async/await`: -1. We'll need to replace `.then` calls with `await`. -2. Also we should make the function `async` for them to work. +1. Vi vil skulle erstatte `.then` kald med `await`. +2. Desuden bør vi gøre funktionen `async` for at de kan fungere. ```js run async function showAvatar() { - // read our JSON + // læs JSON filen let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); - // read github user + // læs github bruger let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); let githubUser = await githubResponse.json(); - // show the avatar + // vis avatar let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); - // wait 3 seconds + // vent 3 sekunder await new Promise((resolve, reject) => setTimeout(resolve, 3000)); img.remove(); @@ -119,24 +119,24 @@ async function showAvatar() { showAvatar(); ``` -Pretty clean and easy to read, right? Much better than before. +Ret rent og nemt at læse, ikke? Meget bedre end før. -````smart header="Modern browsers allow top-level `await` in modules" -In modern browsers, `await` on top level works just fine, when we're inside a module. We'll cover modules in article . +````smart header="Moderne browsere tillader top-level `await` i moduler" +I moderne browsere vil `await` på top level fungere helt fint, når vi er inde i et modul. Vi vil dække moduler i artiklen . -For instance: +For eksempel: ```js run module -// we assume this code runs at top level, inside a module +// vi tager udgangspunkt i, at denne kode kører på top level, inde i et modul let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); console.log(user); ``` -If we're not using modules, or [older browsers](https://caniuse.com/mdn-javascript_operators_await_top_level) must be supported, there's a universal recipe: wrapping into an anonymous async function. +Hvis vi ikke bruger moduler, eller [ældre browsere](https://caniuse.com/mdn-javascript_operators_await_top_level) skal understøttes, er der en universel opskrift: pak det hele ind i en anonym async funktion. -Like this: +Sådan her: ```js (async () => { @@ -148,10 +148,10 @@ Like this: ```` -````smart header="`await` accepts \"thenables\"" -Like `promise.then`, `await` allows us to use thenable objects (those with a callable `then` method). The idea is that a third-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use it with `await`. +````smart header="`await` accepterer \"thenables\"" +Ligesom `promise.then` vil `await` tillade os at bruge *thenable* objekter (dem med en meode `then` der kan kaldes). Idéen med dette er at 3de-parts objekter ikke nødvendigvis er et promise, men promise-kompatible: hvis det understøtter `.then`, er det godt nok til at bruge sammen med `await`. -Here's a demo `Thenable` class; the `await` below accepts its instances: +Her er en demo `Thenable` klasse; `await` nedenfor accepterer udgaver af den: ```js run class Thenable { @@ -160,13 +160,13 @@ class Thenable { } then(resolve, reject) { alert(resolve); - // resolve with this.num*2 after 1000ms + // resolve med this.num*2 efter 1000ms setTimeout(() => resolve(this.num * 2), 1000); // (*) } } async function f() { - // waits for 1 second, then result becomes 2 + // venter i 1 sekund, hvorefter resultatet bliver 2 let result = await new Thenable(1); alert(result); } @@ -174,11 +174,11 @@ async function f() { f(); ``` -If `await` gets a non-promise object with `.then`, it calls that method providing the built-in functions `resolve` and `reject` as arguments (just as it does for a regular `Promise` executor). Then `await` waits until one of them is called (in the example above it happens in the line `(*)`) and then proceeds with the result. +Hvis `await` får et ikke-promise object med `.then`, det kalder den metode og giver de indbyggede funktioner `resolve` og `reject` som argumenter (ligesom det gør for en almindelig `Promise` executor). Derefter venter `await` indtil en af dem bliver kaldt (i eksemplet ovenfor sker det i linjen `(*)`) og fortsætter derefter med resultatet. ```` -````smart header="Async class methods" -To declare an async class method, just prepend it with `async`: +````smart header="Async klasse metoder" +For at deklarere en async klasse metode, tilføj `async` før metoden: ```js run class Waiter { @@ -191,38 +191,38 @@ class Waiter { new Waiter() .wait() - .then(alert); // 1 (this is the same as (result => alert(result))) + .then(alert); // 1 (det er det samme som (result => alert(result))) ``` -The meaning is the same: it ensures that the returned value is a promise and enables `await`. +Meningen er den samme: det sikrer at den returnerede værdi er et promise og aktiverer `await`. ```` -## Error handling +## Håndtering af fejl -If a promise resolves normally, then `await promise` returns the result. But in the case of a rejection, it throws the error, just as if there were a `throw` statement at that line. +Hvis et promise løses normalt, så returnerer `await promise` resultatet. Men i tilfældet af en afvisning, kaster det en fejl, som om der var en `throw`-sætning på linjen. -This code: +Denne kode: ```js async function f() { *!* - await Promise.reject(new Error("Whoops!")); + await Promise.reject(new Error("Ups!")); */!* } ``` -...is the same as this: +... er det samme som: ```js async function f() { *!* - throw new Error("Whoops!"); + throw new Error("Ups!"); */!* } ``` -In real situations, the promise may take some time before it rejects. In that case there will be a delay before `await` throws an error. +I virkelige situationer vil et promise tage noget tid før det afvises. I det tilfælde vil der være en forsinkelse før `await` kaster en fejl. -We can catch that error using `try..catch`, the same way as a regular `throw`: +Vi kan opsnappe den fejl ved hjælp af `try..catch`, på samme måde som en almindelig `throw`: ```js run async function f() { @@ -239,7 +239,7 @@ async function f() { f(); ``` -In the case of an error, the control jumps to the `catch` block. We can also wrap multiple lines: +I tilfælde af en fejl, springer kontrollen over til `catch`-blokken. Vi kan også wrappe flere linjer i `try..catch` for at fange fejl i flere `await`: ```js run async function f() { @@ -248,7 +248,7 @@ async function f() { let response = await fetch('/no-user-here'); let user = await response.json(); } catch(err) { - // catches errors both in fetch and response.json + // fanger fejler i både fetch og response.json alert(err); } } @@ -256,33 +256,33 @@ async function f() { f(); ``` -If we don't have `try..catch`, then the promise generated by the call of the async function `f()` becomes rejected. We can append `.catch` to handle it: +Hvis vi ikke har `try..catch`, så vil promise genreret ved kaldet af den asynkrone funktion `f()` blive afvist. Vi kan tilføje `.catch` for at håndtere det: ```js run async function f() { let response = await fetch('http://no-such-url'); } -// f() becomes a rejected promise +// f() bliver et afvist promise *!* -f().catch(alert); // TypeError: failed to fetch // (*) +f().catch(alert); // TypeError: fejlede ved fetch // (*) */!* ``` -If we forget to add `.catch` there, then we get an unhandled promise error (viewable in the console). We can catch such errors using a global `unhandledrejection` event handler as described in the chapter . +Hvis vi glemmer at tilføje `.catch`, så får vi en ikke-håndteret promise-fejl (synlig i konsollen). Vi kan fange sådanne fejl ved hjælp af en global `unhandledrejection`-begivenhedshåndtering som beskrevet i kapitlet . -```smart header="`async/await` and `promise.then/catch`" -When we use `async/await`, we rarely need `.then`, because `await` handles the waiting for us. And we can use a regular `try..catch` instead of `.catch`. That's usually (but not always) more convenient. +```smart header="`async/await` og `promise.then/catch`" +Når vi bruger `async/await`, bruger vi sjældent `.then`, fordi `await` håndterer ventningen for os. Og vi kan bruge en almindelig `try..catch` i stedet for `.catch`. Det er ofte (men ikke altid) mere praktisk. -But at the top level of the code, when we're outside any `async` function, we're syntactically unable to use `await`, so it's a normal practice to add `.then/catch` to handle the final result or falling-through error, like in the line `(*)` of the example above. +Men på den øverste niveau af koden, når vi er uden for enhver `async` funktion, er vi syntaktisk ude af stand til at bruge `await`, så det er en normal praksis at tilføje `.then/catch` for at håndtere det endelige resultat eller fejlen, som i linjen `(*)` i eksemplet ovenfor. ``` -````smart header="`async/await` works well with `Promise.all`" -When we need to wait for multiple promises, we can wrap them in `Promise.all` and then `await`: +````smart header="`async/await` virker godt med `Promise.all`" +Når vi har brug for at vente på flere promises, kan vi pakke dem ind i `Promise.all` og derefter `await`: ```js -// wait for the array of results +// vent på arrayet af resultater let results = await Promise.all([ fetch(url1), fetch(url2), @@ -290,22 +290,22 @@ let results = await Promise.all([ ]); ``` -In the case of an error, it propagates as usual, from the failed promise to `Promise.all`, and then becomes an exception that we can catch using `try..catch` around the call. +I tilfælde af en fejl, propagerer den som sædvanligt, fra det mislykkede promise til `Promise.all`, og så bliver det til en exception, som vi kan fange ved hjælp af `try..catch` omkring kaldet. ```` -## Summary +## Opsummering -The `async` keyword before a function has two effects: +Nøgleordet `async` før en funktion har to effekter: -1. Makes it always return a promise. -2. Allows `await` to be used in it. +1. Gør det til en funktion, der altid returnerer et promise. +2. Tillader `await` at blive brugt i den. -The `await` keyword before a promise makes JavaScript wait until that promise settles, and then: +Nøgleordet `await` før et promise gør JavaScript til at vente indtil det promise løses, og derefter: -1. If it's an error, an exception is generated — same as if `throw error` were called at that very place. -2. Otherwise, it returns the result. +1. Hvis det er en fejl, genereres en exception — samme som hvis `throw error` blev kaldt på det sted. +2. Ellers returnerer det resultatet. -Together they provide a great framework to write asynchronous code that is easy to both read and write. +Sammen udgør de en fremragende framework til at skrive asynkron kode, som er let at læse og skrive. -With `async/await` we rarely need to write `promise.then/catch`, but we still shouldn't forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also `Promise.all` is nice when we are waiting for many tasks simultaneously. +Med `async/await` behøver vi sjældent at skrive `promise.then/catch`, men vi bør stadig ikke glemme, at de er baseret på promises, fordi nogle gange (f.eks. i den yderste scope) er vi nødt til at bruge dem. Derudover er `Promise.all` ret smart når vi venter på mange opgaver samtidigt. From 99ec7569c9a83796aa6d85d76497c3d15e20cb45 Mon Sep 17 00:00:00 2001 From: ockley Date: Thu, 26 Mar 2026 07:40:05 +0100 Subject: [PATCH 22/33] Opdater til dansk --- .../01-pseudo-random-generator/solution.md | 5 +- .../01-pseudo-random-generator/task.md | 18 +- .../1-generators/article.md | 220 +++++++++--------- 1-js/12-generators-iterators/index.md | 2 +- 4 files changed, 123 insertions(+), 122 deletions(-) diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md index 4355d0cfc..e7822df6f 100644 --- a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md @@ -16,7 +16,7 @@ alert(generator.next().value); // 282475249 alert(generator.next().value); // 1622650073 ``` -Please note, the same can be done with a regular function, like this: +Bemærk, at det samme kan gøres med en normal funktion, som her: ```js run function pseudoRandom(seed) { @@ -35,4 +35,5 @@ alert(generator()); // 282475249 alert(generator()); // 1622650073 ``` -That also works. But then we lose ability to iterate with `for..of` and to use generator composition, that may be useful elsewhere. +Det vil også virke. Men vi mister muligheden for at iterere med `for..of` og at bruge generator komposition, som kan være brugbaret andre steder. + diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md index e7c251ad3..2d7096c0d 100644 --- a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md @@ -1,29 +1,29 @@ # Pseudo-random generator -There are many areas where we need random data. +Der findes mange situationer, hvor vi har brug for tilfældige data. -One of them is testing. We may need random data: text, numbers, etc. to test things out well. +En af dem er testning. Vi kan have brug for tilfældige data: tekst, tal osv. for at teste funktioner grundigt. -In JavaScript, we could use `Math.random()`. But if something goes wrong, we'd like to be able to repeat the test, using exactly the same data. +I JavaScript kan vi bruge `Math.random()`. Men hvis noget går galt, vil vi gerne være i stand til at kunne gentage testen ved hjælp af præcis de samme data. -For that, so called "seeded pseudo-random generators" are used. They take a "seed", the first value, and then generate the next ones using a formula so that the same seed yields the same sequence, and hence the whole flow is easily reproducible. We only need to remember the seed to repeat it. +Til dette kan såkaldte "seeded pseudo-random generators" bruges. De tager en "seed", den første værdi, og genererer derefter de næste ved hjælp af en formel, så den samme seed giver den samme sekvens, og dermed er hele flowet nemt reproducibelt. Vi behøver kun at huske seed'en for at gentage den. -An example of such formula, that generates somewhat uniformly distributed values: +Et eksempel på en sådan formel, som genererer nogle lidt uniformt fordelte værdier: ``` next = previous * 16807 % 2147483647 ``` -If we use `1` as the seed, the values will be: +Hvis vi bruger `1` som seed, vil værdierne være: 1. `16807` 2. `282475249` 3. `1622650073` -4. ...and so on... +4. ...og så videre... -The task is to create a generator function `pseudoRandom(seed)` that takes `seed` and creates the generator with this formula. +Opgaven er at oprette en generatorfunktion `pseudoRandom(seed)` som tager `seed` og opretter generator med denne formel. -Usage example: +Brugseksempel: ```js let generator = pseudoRandom(1); diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 55f6bf903..b117030fe 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -1,14 +1,14 @@ -# Generators +# Generatorer -Regular functions return only one, single value (or nothing). +Normale funktioner returnerer kun en enkelt værdi (eller intet). -Generators can return ("yield") multiple values, one after another, on-demand. They work great with [iterables](info:iterable), allowing to create data streams with ease. +Generatorer kan returnere ("yield") flere værdier, en efter en, på anmodning. De fungerer godt med [iterable](info:iterable) og gør det nemt at oprette datastrømme. -## Generator functions +## Generatorfunktioner -To create a generator, we need a special syntax construct: `function*`, so-called "generator function". +For at oprette en generator har vi brug for en speciel syntaks: `function*`, såkaldt "generatorfunktion". -It looks like this: +Det ser sådan ud: ```js function* generateSequence() { @@ -18,7 +18,7 @@ function* generateSequence() { } ``` -Generator functions behave differently from regular ones. When such function is called, it doesn't run its code. Instead it returns a special object, called "generator object", to manage the execution. +Generatorfunktioner opfører sig anderledes end normale funktioner. Når en sådan funktion kaldes, kører dens kode ikke. I stedet returnerer den et specielt objekt, kaldet et "generator objekt", der håndterer eksekveringen. Here, take a look: @@ -29,24 +29,24 @@ function* generateSequence() { return 3; } -// "generator function" creates "generator object" +// "generatorfunktionen" opretter et "generator objekt" let generator = generateSequence(); *!* alert(generator); // [object Generator] */!* ``` -The function code execution hasn't started yet: +Funktionens kode er ikke startet endnu, og den er klar til at blive kørt. Det er det, der adskiller generatorer fra normale funktioner: de starter ikke, når de kaldes. I stedet returnerer de et objekt, der kan bruges til at styre eksekveringen. ![](generateSequence-1.svg) -The main method of a generator is `next()`. When called, it runs the execution until the nearest `yield ` statement (`value` can be omitted, then it's `undefined`). Then the function execution pauses, and the yielded `value` is returned to the outer code. +Hovedmetoden for en generator er `next()`. Når den kaldes, kører den eksekveringen indtil den nærmeste `yield ` sætning (`value` kan udelades, da den så er `undefined`). Derefter pauser funktionens eksekvering, og den `value` der gives ved yield returneres til det ydre kode. -The result of `next()` is always an object with two properties: -- `value`: the yielded value. -- `done`: `true` if the function code has finished, otherwise `false`. +Resultatet af `next()` er altid et objekt med to egenskaber: +- `value`: værdien som er *yielded*. +- `done`: `true` hvis funktionens kode er færdig, ellers `false`. -For instance, here we create the generator and get its first yielded value: +Her opretter vi en generator og får dens første yielded værdi: ```js run function* generateSequence() { @@ -64,11 +64,11 @@ let one = generator.next(); alert(JSON.stringify(one)); // {value: 1, done: false} ``` -As of now, we got the first value only, and the function execution is on the second line: +Som det står nu har vi kun fået den første værdi, og funktionens eksekvering er på den anden linje: `yield 2`. Det er derfor `done: false` og `value: 1`. Generatoren er ikke færdig, og den har returneret `1` som det første resultat. ![](generateSequence-2.svg) -Let's call `generator.next()` again. It resumes the code execution and returns the next `yield`: +Lad os kalde `generator.next()` igen. Det genoptager eksekvering af funktionens kode og returnerer den næste `yield`: ```js let two = generator.next(); @@ -78,7 +78,7 @@ alert(JSON.stringify(two)); // {value: 2, done: false} ![](generateSequence-3.svg) -And, if we call it a third time, the execution reaches the `return` statement that finishes the function: +Og, hvis vu kalder den for tredje gang, vil eksekveringen nå `return`-sætningen, som afslutter funktionen. ```js let three = generator.next(); @@ -88,21 +88,21 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} ![](generateSequence-4.svg) -Now the generator is done. We should see it from `done:true` and process `value:3` as the final result. +Nu er generatoren færdig. Vi ser det med værdierne `done:true` og `value:3` som det endelige resultat. New calls to `generator.next()` don't make sense any more. If we do them, they return the same object: `{done: true}`. -```smart header="`function* f(…)` or `function *f(…)`?" -Both syntaxes are correct. +```smart header="`function* f(…)` eller `function *f(…)`?" +Begge syntakser er korrekte. -But usually the first syntax is preferred, as the star `*` denotes that it's a generator function, it describes the kind, not the name, so it should stick with the `function` keyword. +Men den første syntaks er normalt foretrukket. Stjernen `*` indikerer at det er en generatorfunktion, den beskriver typen, ikke navnet, så den bør fastholde sig ved `function`-nøgleordet. ``` -## Generators are iterable +## Generatororer er itererbare -As you probably already guessed looking at the `next()` method, generators are [iterable](info:iterable). +Som du nok allerede har gættet på, når du kigger på metoden `next()`, er generatorer [itererbare](info:iterable). -We can loop over their values using `for..of`: +Vi kan loope over deres værdier ved hjælp af `for..of`: ```js run function* generateSequence() { @@ -118,11 +118,11 @@ for(let value of generator) { } ``` -Looks a lot nicer than calling `.next().value`, right? +Det ser pænere ud end at kalde `.next().value`, ikke? -...But please note: the example above shows `1`, then `2`, and that's all. It doesn't show `3`! +...Men husk: eksemplet ovenfor viser `1`, så `2`, og det er alt. Det viser ikke `3`! -It's because `for..of` iteration ignores the last `value`, when `done: true`. So, if we want all results to be shown by `for..of`, we must return them with `yield`: +Det er fordi `for..of` iteration ignorerer den sidste `value`, når `done: true`. Så, hvis vi vil have alle resultater vist af `for..of`, skal vi returnere dem med `yield` i stedet for `return`: ```js run function* generateSequence() { @@ -140,7 +140,7 @@ for(let value of generator) { } ``` -As generators are iterable, we can call all related functionality, e.g. the spread syntax `...`: +Da generatorer er itererbare, kan vi kalde al relateret funktionalitet, f.eks. spread-syntaksen `...`: ```js run function* generateSequence() { @@ -154,30 +154,30 @@ let sequence = [0, ...generateSequence()]; alert(sequence); // 0, 1, 2, 3 ``` -In the code above, `...generateSequence()` turns the iterable generator object into an array of items (read more about the spread syntax in the chapter [](info:rest-parameters-spread#spread-syntax)) +I koden ovenfor omdannes `...generateSequence()` det det itererbare objekt til et array af elementer (læs mere om spread-syntaksen i kapitlet [](info:rest-parameters-spread#spread-syntax)) -## Using generators for iterables +## Brug generatorer med itererbare objekter -Some time ago, in the chapter [](info:iterable) we created an iterable `range` object that returns values `from..to`. +For noget tid siden, i kapitlet [](info:iterable) oprettede vi et itererbart `range`-objekt, der returnerer værdier `from..to`. -Here, let's remember the code: +Her, lad os lige huske koden: ```js run let range = { from: 1, to: 5, - // for..of range calls this method once in the very beginning + // for..of range kalder denne metode en gang i begyndelsen [Symbol.iterator]() { - // ...it returns the iterator object: - // onward, for..of works only with that object, asking it for next values + // ...den returnerer iterator-objektet: + // efterfølgende vil for..of kun virke med det objekt, og kun når det spørges om næste værdier return { current: this.from, last: this.to, - // next() is called on each iteration by the for..of loop + // next() bliver kaldt ved hver iteration af for..of-løkken next() { - // it should return the value as an object {done:.., value :...} + // den skal returnere værdien som et objekt {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -188,20 +188,20 @@ let range = { } }; -// iteration over range returns numbers from range.from to range.to +// iteration over range returnerer tal fra range.from til range.to alert([...range]); // 1,2,3,4,5 ``` -We can use a generator function for iteration by providing it as `Symbol.iterator`. +Vi kan bruge en generatorfunktion til at iterere ved at levere den som `Symbol.iterator`. -Here's the same `range`, but much more compact: +Her er den samme `range`, men mere kompakt: ```js run let range = { from: 1, to: 5, - *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*() + *[Symbol.iterator]() { // Kort måde at skrive [Symbol.iterator]: function*() for(let value = this.from; value <= this.to; value++) { yield value; } @@ -211,25 +211,25 @@ let range = { alert( [...range] ); // 1,2,3,4,5 ``` -That works, because `range[Symbol.iterator]()` now returns a generator, and generator methods are exactly what `for..of` expects: -- it has a `.next()` method -- that returns values in the form `{value: ..., done: true/false}` +Det virker fordi `range[Symbol.iterator]()` nu returnerer en generator, og generatormetoder er netop det, `for..of` forventer: +- den har en `.next()`-metode +- som returnerer værdier i formen `{value: ..., done: true/false}` -That's not a coincidence, of course. Generators were added to JavaScript language with iterators in mind, to implement them easily. +Det er selvfølgelig ikke en tilfældighed. Generatorer blev tilføjet til JavaScript-sproget med iteratorer i tankerne, for at kunne implementere dem nemt. -The variant with a generator is much more concise than the original iterable code of `range`, and keeps the same functionality. +Versionen med en generator er meget mere kompakt end den originale iterable kode for `range`, og beholder samme funktionalitet. -```smart header="Generators may generate values forever" -In the examples above we generated finite sequences, but we can also make a generator that yields values forever. For instance, an unending sequence of pseudo-random numbers. +```smart header="Generatorer kan generere værdier for evigt" +I eksemplet ovenfor genererede vi endelige sekvenser, men vi kan også lave en generator, der producerer værdier for evigt. For eksempel en uendelig sekvens af pseudo-tilfældige tal. -That surely would require a `break` (or `return`) in `for..of` over such generator. Otherwise, the loop would repeat forever and hang. +Det vil selvfølgelig kræve en `break` (eller `return`) i `for..of` over sådan en generator. Ellers ville løkken gentage for evigt og låse systemet. ``` -## Generator composition +## Generator komposition -Generator composition is a special feature of generators that allows to transparently "embed" generators in each other. +Generator komposition er en speciel feature ved generatorer der tillader os at indlejre generatorer indeni andre generatorer. -For instance, we have a function that generates a sequence of numbers: +Her har vi for eksempel en funktion, der genererer en række tal: ```js function* generateSequence(start, end) { @@ -237,18 +237,18 @@ function* generateSequence(start, end) { } ``` -Now we'd like to reuse it to generate a more complex sequence: -- first, digits `0..9` (with character codes 48..57), -- followed by uppercase alphabet letters `A..Z` (character codes 65..90) -- followed by lowercase alphabet letters `a..z` (character codes 97..122) +Nu vil vi gerne genbruge den til at at generere en mere kompleks sekvens: +- først, tal `0..9` (med karakterkoderne 48..57), +- efterfulgt af alfabet med store bogstaver `A..Z` (karakterkoderne 65..90) +- efterfulgt af små bogstaver `a..z` (karakterkoderne 97..122) -We can use this sequence e.g. to create passwords by selecting characters from it (could add syntax characters as well), but let's generate it first. +Vi kan bruge denne sekvens til f.eks. at skabe passwords ved at vælge tegn fra den (kunne tilføje syntaks-tegn også), men lad os generere den først. -In a regular function, to combine results from multiple other functions, we call them, store the results, and then join at the end. +I en normal funktion, for at kombinere resultater fra flere andre funktioner, kalder vi dem, gemmer resultaterne og sammenkæder dem til sidst. -For generators, there's a special `yield*` syntax to "embed" (compose) one generator into another. +For generatorer, er der en speciel `yield*`-syntaks til at "indlejre" (sammensætte) en generator i en anden. -The composed generator: +Den komponerede generator: ```js run function* generateSequence(start, end) { @@ -279,9 +279,9 @@ for(let code of generatePasswordCodes()) { alert(str); // 0..9A..Za..z ``` -The `yield*` directive *delegates* the execution to another generator. This term means that `yield* gen` iterates over the generator `gen` and transparently forwards its yields outside. As if the values were yielded by the outer generator. +Direktivet `yield*` *delegerer* udførelsen til en anden generator. Dette udtryk betyder, at `yield* gen` itererer over generator `gen` og videreformidler dens yields uden for. Som om værdierne blev produceret af den ydre generator. -The result is the same as if we inlined the code from nested generators: +Resultatet er det samme som hvis vi inkoorporerede koden fra den indlejrede generatorer: ```js run function* generateSequence(start, end) { @@ -312,22 +312,22 @@ for(let code of generateAlphaNum()) { alert(str); // 0..9A..Za..z ``` -A generator composition is a natural way to insert a flow of one generator into another. It doesn't use extra memory to store intermediate results. +En generator komposition er en naturlig måde at indsætte ef flow fra en generator i en anden. Den bruger ikke ekstra hukommelse til at gemme mellemresultater. -## "yield" is a two-way street +## "yield" virker begge veje -Until this moment, generators were similar to iterable objects, with a special syntax to generate values. But in fact they are much more powerful and flexible. +Op til nu har generatorer fungeret som iterable objekter, med en speciel syntaks til at generere værdier. Men faktisk er de meget mere kraftfulde og fleksible. -That's because `yield` is a two-way street: it not only returns the result to the outside, but also can pass the value inside the generator. +Det skyldes, at `yield` kan køre i begge retninger: den returnerer ikke kun resultatet til udvikleren, men kan også sende en værdi ind i generatoren. -To do so, we should call `generator.next(arg)`, with an argument. That argument becomes the result of `yield`. +For at gøre det, skal vi kalde `generator.next(arg)`, med et argument. Det argument bliver resultatet af `yield`. -Let's see an example: +Lad os se et eksempel: ```js run function* gen() { *!* - // Pass a question to the outer code and wait for an answer + // Send et spørgsmål til det ydre kode og vent på et svar let result = yield "2 + 2 = ?"; // (*) */!* @@ -336,29 +336,29 @@ function* gen() { let generator = gen(); -let question = generator.next().value; // <-- yield returns the value +let question = generator.next().value; // <-- yield returnerer værdien -generator.next(4); // --> pass the result into the generator +generator.next(4); // --> sender resultatet til generatoren ``` ![](genYield2.svg) -1. The first call `generator.next()` should be always made without an argument (the argument is ignored if passed). It starts the execution and returns the result of the first `yield "2+2=?"`. At this point the generator pauses the execution, while staying on the line `(*)`. -2. Then, as shown at the picture above, the result of `yield` gets into the `question` variable in the calling code. -3. On `generator.next(4)`, the generator resumes, and `4` gets in as the result: `let result = 4`. +1. Det første kald til `generator.next()` skal altid laves uden et argument (det argument ignoreres hvis det sendes). Det starter eksekveringen og returnerer resultatet af den første `yield "2+2=?"`. På dette tidspunkt pauser generatoren eksekveringen, mens den bliver på linjen `(*)`. +2. Derefter, som vist på billedet ovenfor, kommer resultatet af `yield` ind i `question` variablen i det ydre kode. +3. På `generator.next(4)`, genstartes generatoren, og `4` kommer ind som resultatet: `let result = 4`. -Please note, the outer code does not have to immediately call `next(4)`. It may take time. That's not a problem: the generator will wait. +Bemærk venligst, at det ydre kode ikke behøver at kalde `next(4)` umiddelbart. Det kan tage tid. Det er ikke et problem: generatoren vil vente. -For instance: +For eksempel: ```js -// resume the generator after some time +// fortsæt generatoren efter noget tid setTimeout(() => generator.next(4), 1000); ``` -As we can see, unlike regular functions, a generator and the calling code can exchange results by passing values in `next/yield`. +Som vi kan se så, modsat normale funktioner, kan en generator og det ydre kode bytte resultater ved at sende værdier i `next/yield`. -To make things more obvious, here's another example, with more calls: +For at gøre tingene mere klart, her er et andet eksempel med flere kald: ```js run function* gen() { @@ -380,36 +380,36 @@ alert( generator.next(4).value ); // "3 * 3 = ?" alert( generator.next(9).done ); // true ``` -The execution picture: +Et billede over udførelsen: ![](genYield2-2.svg) -1. The first `.next()` starts the execution... It reaches the first `yield`. -2. The result is returned to the outer code. -3. The second `.next(4)` passes `4` back to the generator as the result of the first `yield`, and resumes the execution. -4. ...It reaches the second `yield`, that becomes the result of the generator call. -5. The third `next(9)` passes `9` into the generator as the result of the second `yield` and resumes the execution that reaches the end of the function, so `done: true`. +1. Det første `.next()` starter udførelsen... Den rammer det første `yield`. +2. Resultatet returneres til det ydre kode. +3. Det andet `.next(4)` sender `4` tilbage til generatoren som resultatet af det første `yield`, og genstarter eksekveringen. +4. ...Det rammer det andet `yield`, som bliver resultatet af generatorkaldet. +5. Det tredje `next(9)` sender `9` ind i generatoren som resultatet af det andet `yield` og genstarter eksekveringen, der når slutningen af funktionen, så `done: true`. -It's like a "ping-pong" game. Each `next(value)` (excluding the first one) passes a value into the generator, that becomes the result of the current `yield`, and then gets back the result of the next `yield`. +Det er som et "ping-pong" spil. Hvert `next(value)` (undtagen det første) sender en værdi ind i generatoren, som bliver resultatet af den nuværende `yield`, og får så tilbage resultatet af den næste `yield`. ## generator.throw -As we observed in the examples above, the outer code may pass a value into the generator, as the result of `yield`. +Som vi har set i eksemplerne ovenfor, kan den ydre kode sende en værdi ind i generatoren som resultatet af `yield`. -...But it can also initiate (throw) an error there. That's natural, as an error is a kind of result. +... men det kan også udløse (throw) en fejl der. Det er egentligt naturligt, da en fejl jo er en slags resultat. -To pass an error into a `yield`, we should call `generator.throw(err)`. In that case, the `err` is thrown in the line with that `yield`. +For at sende en fejl til en `yield`, skal vi kalde `generator.throw(err)`. I det tilfælde bliver `err` kastet i linjen med den pågældende `yield`. -For instance, here the yield of `"2 + 2 = ?"` leads to an error: +Her er for eksempel et yield med `"2 + 2 = ?"` der leder til en fejl: ```js run function* gen() { try { let result = yield "2 + 2 = ?"; // (1) - alert("The execution does not reach here, because the exception is thrown above"); + alert("Udførelsen når ikke her til fordi en fejl er kastet ovenfor"); } catch(e) { - alert(e); // shows the error + alert(e); // vis fejlen } } @@ -418,19 +418,19 @@ let generator = gen(); let question = generator.next().value; *!* -generator.throw(new Error("The answer is not found in my database")); // (2) +generator.throw(new Error("Svaret findes ikke i min database")); // (2) */!* ``` -The error, thrown into the generator at line `(2)` leads to an exception in line `(1)` with `yield`. In the example above, `try..catch` catches it and shows it. +Fejlen smides in i generatoren på linjen `(2)`, hvilket fører til en fejl i linjen `(1)` med `yield`. I eksemplet ovenfor fanger `try..catch` fejlen og viser den. -If we don't catch it, then just like any exception, it "falls out" the generator into the calling code. +Hvis vi ikke fanger den, så "falder den ud" som enhver anden fejl - ud af generatoren til det ydre kode. -The current line of the calling code is the line with `generator.throw`, labelled as `(2)`. So we can catch it here, like this: +Den aktuelle linje i det ydre kode er linjen med `generator.throw`, mærket som `(2)`. Så vi kan fange den her således: ```js run function* generate() { - let result = yield "2 + 2 = ?"; // Error in this line + let result = yield "2 + 2 = ?"; // Fejl i denne linje } let generator = generate(); @@ -439,18 +439,18 @@ let question = generator.next().value; *!* try { - generator.throw(new Error("The answer is not found in my database")); + generator.throw(new Error("Svaret findes ikke i min database")); } catch(e) { - alert(e); // shows the error + alert(e); // vis fejlen } */!* ``` -If we don't catch the error there, then, as usual, it falls through to the outer calling code (if any) and, if uncaught, kills the script. +HVis vi ikke fanger fejlen her så vil den, som sædvanlig, falde igennem til den ydre kaldende kode, hvis der er en. Hvis den ikke fanges her, vil scriptet dø. ## generator.return -`generator.return(value)` finishes the generator execution and return the given `value`. +`generator.return(value)` afslutter eksekveringen af generatoren og returnerer den givne `value`. ```js function* gen() { @@ -466,18 +466,18 @@ g.return('foo'); // { value: "foo", done: true } g.next(); // { value: undefined, done: true } ``` -If we again use `generator.return()` in a completed generator, it will return that value again ([MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return)). +Hvis vi bruger `generator.return()` i en færdig generator, vil den returnere den givne værdi igen ([MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return)). -Often we don't use it, as most of time we want to get all returning values, but it can be useful when we want to stop generator in a specific condition. +Ofte bruger vi det ikke, da vi ofte vil have alle de returnerede værdier, men det kan være nyttigt, når vi vil stoppe generatoren i en bestemt tilstand. -## Summary +## Opsummering -- Generators are created by generator functions `function* f(…) {…}`. -- Inside generators (only) there exists a `yield` operator. -- The outer code and the generator may exchange results via `next/yield` calls. +- Generatorer oprettes af generator funktioner `function* f(…) {…}`. +- Inde i generatorer (og kun generatorer) kan der eksistere en `yield` operator. +- Det ydre kode og generatoren kan udveksle resultater via `next/yield` kald. -In modern JavaScript, generators are rarely used. But sometimes they come in handy, because the ability of a function to exchange data with the calling code during the execution is quite unique. And, surely, they are great for making iterable objects. +I moderne JavaScript bruges generatorer sjældent. De kan dog være praktiske, fordi de giver en funktion evnen til at udveksle data med den ydre kode under eksekveringen, hvilket er ganske unik. Og, så de er fantastiske til at oprette iterable objekter. -Also, in the next chapter we'll learn async generators, which are used to read streams of asynchronously generated data (e.g paginated fetches over a network) in `for await ... of` loops. +I næste kapitel vil vi lære om async generatorer. De bruges til at læse streams af asynkront genererede data (f.eks. paginerede fetches over et netværk) i `for await ... of` loops. -In web-programming we often work with streamed data, so that's another very important use case. +I web-programmering arbejdes der ofte med streamed data, så det er et andet meget vigtig scenarie. diff --git a/1-js/12-generators-iterators/index.md b/1-js/12-generators-iterators/index.md index ccc909d1a..c5058386a 100644 --- a/1-js/12-generators-iterators/index.md +++ b/1-js/12-generators-iterators/index.md @@ -1,2 +1,2 @@ -# Generators, advanced iteration +# Generators, avanceret iteration From 4395ce60eb8635dd46488baa3c629da81eb3c261 Mon Sep 17 00:00:00 2001 From: ockley Date: Sat, 28 Mar 2026 12:27:39 +0100 Subject: [PATCH 23/33] oversat til dansk --- .../2-async-iterators-generators/article.md | 251 +++++++++--------- .../2-async-iterators-generators/head.html | 8 +- 2 files changed, 130 insertions(+), 129 deletions(-) diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/article.md b/1-js/12-generators-iterators/2-async-iterators-generators/article.md index d4e9f7861..68c750afa 100644 --- a/1-js/12-generators-iterators/2-async-iterators-generators/article.md +++ b/1-js/12-generators-iterators/2-async-iterators-generators/article.md @@ -1,15 +1,15 @@ -# Async iteration and generators +# Asynkron iteration og generatorer -Asynchronous iteration allow us to iterate over data that comes asynchronously, on-demand. Like, for instance, when we download something chunk-by-chunk over a network. And asynchronous generators make it even more convenient. +Asynkron iteration tillader os at iterere over data, der kommer asynkront, på baggrund af en forespørgsel. Det kan f.eks. være hvis vi downloader noget bid for bid over et netværk. Her er asynkrone generatorer praktiske. -Let's see a simple example first, to grasp the syntax, and then review a real-life use case. +Lad os starte med et simpelt eksempel, for at forstå syntaksen. Dererefter gennemgår vi et mere realistisk brugsscenarie. -## Recall iterables +## Hvad var itererbare objekter? -Let's recall the topic about iterables. +Lad os først minde os om itererbare objekter. -The idea is that we have an object, such as `range` here: +Idéen er, at vi har et objekt, såsom `range` her: ```js let range = { from: 1, @@ -17,17 +17,17 @@ let range = { }; ``` -...And we'd like to use `for..of` loop on it, such as `for(value of range)`, to get values from `1` to `5`. +... og vil gerne bruge `for..of` loop på det, såsom `for(value of range)`, for at få værdier fra `1` til `5`. -In other words, we want to add an *iteration ability* to the object. +Med andre ord, vi vil gerne tilføje en *itereringsmulighed* til objektet. -That can be implemented using a special method with the name `Symbol.iterator`: +Det kan implementeres ved hjælp af en speciel metode med navnet `Symbol.iterator`: -- This method is called in by the `for..of` construct when the loop is started, and it should return an object with the `next` method. -- For each iteration, the `next()` method is invoked for the next value. -- The `next()` should return a value in the form `{done: true/false, value:}`, where `done:true` means the end of the loop. +- Denne metode er kaldt af `for..of` konstruktøren når loopen startes, og den skal returnere et objekt med `next` metoden. +- For hver iteration kaldes `next()` metoden for at få den næste værdi. +- Metoden `next()` skal returnere en værdi med formen `{done: true/false, value:}`, hvor `done:true` betyder slutningen på loopet. -Here's an implementation for the iterable `range`: +Her er en implementation til en itererbar `range`: ```js run let range = { @@ -35,14 +35,14 @@ let range = { to: 5, *!* - [Symbol.iterator]() { // called once, in the beginning of for..of + [Symbol.iterator]() { // kaldes én gang når for..of starter */!* return { current: this.from, last: this.to, *!* - next() { // called every iteration, to get the next value + next() { // kaldes ved hver iteration, for at få den næste værdi */!* if (this.current <= this.last) { return { done: false, value: this.current++ }; @@ -55,29 +55,29 @@ let range = { }; for(let value of range) { - alert(value); // 1 then 2, then 3, then 4, then 5 + alert(value); // 1 så 2, så 3, så 4, så 5 } ``` -If anything is unclear, please visit the chapter [](info:iterable), it gives all the details about regular iterables. +Hvis noget af dette er uklart, så besøg kapitlet [](info:iterable), for at læse mere om iterable. -## Async iterables +## Asynkrone itererbare objekter -Asynchronous iteration is needed when values come asynchronously: after `setTimeout` or another kind of delay. +Asynkron iteration er nødvendig når værdier kommer asynkront: efter `setTimeout` eller andre typer af forsinkelse. -The most common case is that the object needs to make a network request to deliver the next value, we'll see a real-life example of it a bit later. +Det mest almindelige tilfælde er, når objektet skal lave en netværksforespørgsel for at levere den næste værdi. Det vil vi se et eksempel lidt senere. -To make an object iterable asynchronously: +For at gøre et objekt itererbart og asynkront, skal vi gøre følgende: -1. Use `Symbol.asyncIterator` instead of `Symbol.iterator`. -2. The `next()` method should return a promise (to be fulfilled with the next value). - - The `async` keyword handles it, we can simply make `async next()`. -3. To iterate over such an object, we should use a `for await (let item of iterable)` loop. - - Note the `await` word. +1. Brug `Symbol.asyncIterator` i stedet for `Symbol.iterator`. +2. Metoden `next()` vil returnere et promise (om at blive opfyldt med den næstkommende værdi). + - Nøgleordet `async` håndterer den interne logik. Vi kan simpelthen kalde `async next()`. +3. For at iterere over sådan et objekt, skal vi bruge en `for await (let item of iterable)` loop. + - Bemærk `await` i loopet. -As a starting example, let's make an iterable `range` object, similar like the one before, but now it will return values asynchronously, one per second. +Til en start, så lad os lave et itererbart`range` objekt, ligesom det før, men nu vil det returnere værdier asynkront, en per sekund. -All we need to do is to perform a few replacements in the code above: +Alt vi behøver at gøre er at skrive et par ændringer i koden ovenfor: ```js run let range = { @@ -96,7 +96,7 @@ let range = { */!* *!* - // note: we can use "await" inside the async next: + // bemærk: vi kan nu bruge "await" fordi det sker i async next: await new Promise(resolve => setTimeout(resolve, 1000)); // (3) */!* @@ -121,43 +121,43 @@ let range = { })() ``` -As we can see, the structure is similar to regular iterators: +Som vi kan se, er strukturen den samme som for regulære iterators: -1. To make an object asynchronously iterable, it must have a method `Symbol.asyncIterator` `(1)`. -2. This method must return the object with `next()` method returning a promise `(2)`. -3. The `next()` method doesn't have to be `async`, it may be a regular method returning a promise, but `async` allows us to use `await`, so that's convenient. Here we just delay for a second `(3)`. -4. To iterate, we use `for await(let value of range)` `(4)`, namely add "await" after "for". It calls `range[Symbol.asyncIterator]()` once, and then its `next()` for values. +1. For at gøre et objekt asynkront itererbart, skal det have metoden `Symbol.asyncIterator` `(1)`. +2. Denne metode skal returnere objektet med en `next()` metode, der returnerer et promise `(2)`. +3. Metoden `next()` behøver ikke at være `async`, den kan være en regulær metode, der returnerer et promise. Men `async` gør det muligt at bruge `await`, hvilket er praktisk. Her forsinkes det med en sekund `(3)`. +4. For at iterere, bruger vi `for await(let value of range)` `(4)` - husk at skrive "await" efter "for". Det kalder `range[Symbol.asyncIterator]()` én gang, og derefter dens `next()` for at få værdier. -Here's a small table with the differences: +Her er en tabel med forskellene: -| | Iterators | Async iterators | +| | Iteratorer | Async iteratorer | |-------|-----------|-----------------| -| Object method to provide iterator | `Symbol.iterator` | `Symbol.asyncIterator` | -| `next()` return value is | any value | `Promise` | -| to loop, use | `for..of` | `for await..of` | +| Objekt metode der skal gives til iteratoren | `Symbol.iterator` | `Symbol.asyncIterator` | +| `next()` returnerer værdi som | alle typer | `Promise` | +| til loop, brug | `for..of` | `for await..of` | -````warn header="The spread syntax `...` doesn't work asynchronously" -Features that require regular, synchronous iterators, don't work with asynchronous ones. +````warn header="Spread syntaksen `...` virker ikke asynkront" +Features der kræver regulære, synkrone iteratorer, virker ikke med asynkrone iteratorer. -For instance, a spread syntax won't work: +For eksempel vil spread syntaksen ikke virke: ```js -alert( [...range] ); // Error, no Symbol.iterator +alert( [...range] ); // Fejl, ingen Symbol.iterator ``` -That's natural, as it expects to find `Symbol.iterator`, not `Symbol.asyncIterator`. +Det er naturligt, da det forventer at finde `Symbol.iterator`, ikke `Symbol.asyncIterator`. -It's also the case for `for..of`: the syntax without `await` needs `Symbol.iterator`. +Det er også tilfældet for `for..of`: syntaksen uden `await` behøver `Symbol.iterator`. ```` -## Recall generators +## Kan du huske generatorer? -Now let's recall generators, as they allow to make iteration code much shorter. Most of the time, when we'd like to make an iterable, we'll use generators. +Las os nu genkalde os generatorer, da de tillader os at iterere med mindre kode. For det meste, når vi kan tænke os at arbejde med itererbare objekter, vil vi bruge generatorer. -For sheer simplicity, omitting some important stuff, they are "functions that generate (yield) values". They are explained in detail in the chapter [](info:generators). +Simpelt sagt, og med udeladelse af nogle vigtige detaljer, er de "funktioner, der genererer (yield) værdier". De er forklaret i detaljer i kapitlet [](info:generators). -Generators are labelled with `function*` (note the star) and use `yield` to generate a value, then we can use `for..of` to loop over them. +Generatorer noteres med `function*` (bemærk stjernen) og bruger `yield` til at generere en værdi, og derefter kan vi bruge `for..of` til at loope over dem. -This example generates a sequence of values from `start` to `end`: +Dette eksempel genererer en sekvens af værdier fra `start` til `end`: ```js run function* generateSequence(start, end) { @@ -167,11 +167,11 @@ function* generateSequence(start, end) { } for(let value of generateSequence(1, 5)) { - alert(value); // 1, then 2, then 3, then 4, then 5 + alert(value); // 1, så 2, så 3, så 4, så 5 } ``` -As we already know, to make an object iterable, we should add `Symbol.iterator` to it. +Som vi allerede ved, skal vi tilføje `Symbol.iterator` til et objekt for at gøre det itererbart. ```js let range = { @@ -179,20 +179,20 @@ let range = { to: 5, *!* [Symbol.iterator]() { - return + return ; } */!* } ``` -A common practice for `Symbol.iterator` is to return a generator, it makes the code shorter, as you can see: +Det er normal praksis for `Symbol.iterator` at returnere en generator, det gør koden kortere, som du kan se: ```js run let range = { from: 1, to: 5, - *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*() + *[Symbol.iterator]() { // kort skriveform for [Symbol.iterator]: function*() for(let value = this.from; value <= this.to; value++) { yield value; } @@ -200,25 +200,25 @@ let range = { }; for(let value of range) { - alert(value); // 1, then 2, then 3, then 4, then 5 + alert(value); // 1, så 2, så 3, så 4, så 5 } ``` -Please see the chapter [](info:generators) if you'd like more details. +Husk at se kapitlet [](info:generators) for flere detaljer. -In regular generators we can't use `await`. All values must come synchronously, as required by the `for..of` construct. +I regulære generatorer kan vi ikke bruge `await`. Alle værdier skal komme synkront - som krævet af `for..of` konstruktionen. -What if we'd like to generate values asynchronously? From network requests, for instance. +Hvad hvis vi vil generere værdier asynkront? Fra netværksforespørgsler, for eksempel. -Let's switch to asynchronous generators to make it possible. +Lad os skifte til asynchronous generators for at gøre det muligt. -## Async generators (finally) +## Async generatorer (endelig!) -For most practical applications, when we'd like to make an object that asynchronously generates a sequence of values, we can use an asynchronous generator. +I praksis er det ofte sådan, at når vi have et objekt, der skal generere værdier asynkront, kan vi bruge en asynkron generator. -The syntax is simple: prepend `function*` with `async`. That makes the generator asynchronous. +Syntaksen er simpel: Sæt `async` foran `function*`. Det gør generatoren asynkron. -And then use `for await (...)` to iterate over it, like this: +Brug derefter `for await (...)` til at iterere over det, i stil med dette: ```js run *!*async*/!* function* generateSequence(start, end) { @@ -226,7 +226,7 @@ And then use `for await (...)` to iterate over it, like this: for (let i = start; i <= end; i++) { *!* - // Wow, can use await! + // Wow, vi kan bruge await! await new Promise(resolve => setTimeout(resolve, 1000)); */!* @@ -239,47 +239,47 @@ And then use `for await (...)` to iterate over it, like this: let generator = generateSequence(1, 5); for *!*await*/!* (let value of generator) { - alert(value); // 1, then 2, then 3, then 4, then 5 (with delay between) + alert(value); // 1, så 2, så 3, så 4, så 5 (med forsinkelse mellem) } })(); ``` -As the generator is asynchronous, we can use `await` inside it, rely on promises, perform network requests and so on. +Nu, hvor generatoren er asynkron, kan vi bruge `await` inde i den, stole på promises, udføre netværksforespørgsler og så videre. -````smart header="Under-the-hood difference" -Technically, if you're an advanced reader who remembers the details about generators, there's an internal difference. +````smart header="forskellen under motorhjelmen" +Teknisk set, hvis du er en avanceret læser, der husker detaljerne om generatorer, er der en intern forskel. -For async generators, the `generator.next()` method is asynchronous, it returns promises. +For async generatorer, er `generator.next()`-metoden asynkron, og den returnerer promises. -In a regular generator we'd use `result = generator.next()` to get values. In an async generator, we should add `await`, like this: + en normal generator bruger vi `result = generator.next()` til at få værdier. I en async generator bør vi tilføje `await`, som dette: ```js result = await generator.next(); // result = {value: ..., done: true/false} ``` -That's why async generators work with `for await...of`. +Det er derfor at generatorer virker med `for await...of`. ```` -### Async iterable range +### Asynkron itererbare range -Regular generators can be used as `Symbol.iterator` to make the iteration code shorter. +Regulære generatorer kan bruges som `Symbol.iterator` for at gøre koden for iteration kortere. -Similar to that, async generators can be used as `Symbol.asyncIterator` to implement the asynchronous iteration. +På samme måde kan async generatorer bruges som `Symbol.asyncIterator` for at implementere asynkron iteration. -For instance, we can make the `range` object generate values asynchronously, once per second, by replacing synchronous `Symbol.iterator` with asynchronous `Symbol.asyncIterator`: +For eksempel kan vi få `range` objektet til at generere værdier asynkront, en gang pr. sekund, ved at erstatte den synkrone `Symbol.iterator` med den asynkrone `Symbol.asyncIterator`: ```js run let range = { from: 1, to: 5, - // this line is same as [Symbol.asyncIterator]: async function*() { + // den linje er den samme som [Symbol.asyncIterator]: async function*() { *!* async *[Symbol.asyncIterator]() { */!* for(let value = this.from; value <= this.to; value++) { - // make a pause between values, wait for something + // lav en pause mellem værdier, vent på noget asynkront await new Promise(resolve => setTimeout(resolve, 1000)); yield value; @@ -290,47 +290,47 @@ let range = { (async () => { for *!*await*/!* (let value of range) { - alert(value); // 1, then 2, then 3, then 4, then 5 + alert(value); // 1, så 2, så 3, så 4, så 5 } })(); ``` -Now values come with a delay of 1 second between them. +Nu vil værdier komme med en forsinkelse på 1 sekund mellem dem. ```smart -Technically, we can add both `Symbol.iterator` and `Symbol.asyncIterator` to the object, so it's both synchronously (`for..of`) and asynchronously (`for await..of`) iterable. +Teknisk set, vi kan tilføje både `Symbol.iterator` og `Symbol.asyncIterator` til objektet, så det er både synkront (`for..of`) og asynkront (`for await..of`) itererbart. -In practice though, that would be a weird thing to do. +I praksis er det dog noget mærkeligt noget at gøre. 🤔 ``` -## Real-life example: paginated data +## Realistisk eksempel: paginerede data -So far we've seen basic examples, to gain understanding. Now let's review a real-life use case. +Indtil nu har vi set på grundlæggende eksempler, for at få forståelse. Nu lad os gennemgå et realistisk brugsscenarie. -There are many online services that deliver paginated data. For instance, when we need a list of users, a request returns a pre-defined count (e.g. 100 users) - "one page", and provides a URL to the next page. +Der er mange online services der leverer paginerede data. For eksempel, når vi har brug for en liste over brugere, returnerer en forespørgsel et foruddefineret antal (f.eks. 100 brugere) - "en side", og leverer en URL til den næste side. -This pattern is very common. It's not about users, but just about anything. +Dette mønster er meget almindeligt. Det handler ikke kun om brugere, men om alt muligt. -For instance, GitHub allows us to retrieve commits in the same, paginated fashion: +For eksempel tillader GitHub os at hente commits på samme, paginerede måde: -- We should make a request to `fetch` in the form `https://api.github.com/repos//commits`. -- It responds with a JSON of 30 commits, and also provides a link to the next page in the `Link` header. -- Then we can use that link for the next request, to get more commits, and so on. +- Vi skal bruge `fetch` til at oprette en forespørgsel med formen `https://api.github.com/repos//commits`. +- Der svares med en JSON af 30 commits, og leverer også et link til den næste side i `Link`-hovedet. +- Derefter kan vi bruge det link til den næste forespørgsel, for at få flere commits, og så videre. -For our code, we'd like to have a simpler way to get commits. +For vores kode, vil vi gerne have en enkel måde at hente commits. -Let's make a function `fetchCommits(repo)` that gets commits for us, making requests whenever needed. And let it care about all pagination stuff. For us it'll be a simple async iteration `for await..of`. +Lad os oprette en funktion `fetchCommits(repo)` som henter commits for os, og laver forespørgsler når det er nødvendigt. Funktionen skal også sørge for al pagineringslogik. For os vil det være en simpel asynkron iteration `for await..of`. -So the usage will be like this: +Så brug af metoden vil se sådan ud: ```js for await (let commit of fetchCommits("username/repository")) { - // process commit + // behandling af commit } ``` -Here's such function, implemented as async generator: +Her er sådan en funktion, implementeret som async generator: ```js async function* fetchCommits(repo) { @@ -338,36 +338,36 @@ async function* fetchCommits(repo) { while (url) { const response = await fetch(url, { // (1) - headers: {'User-Agent': 'Our script'}, // github needs any user-agent header + headers: {'User-Agent': 'Our script'}, // github behøver en user-agent header }); - const body = await response.json(); // (2) response is JSON (array of commits) + const body = await response.json(); // (2) response er JSON (array af commits) - // (3) the URL of the next page is in the headers, extract it + // (3) URL til næste side er i headers - udtræk den let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/); nextPage = nextPage?.[1]; url = nextPage; - for(let commit of body) { // (4) yield commits one by one, until the page ends + for(let commit of body) { // (4) yield commits en ad gangen, indtil siden er færdig yield commit; } } } ``` -More explanations about how it works: +Forklaret lidt mere i detaljer kan man sige: -1. We use the browser [fetch](info:fetch) method to download the commits. +1. Vi bruger browserens metode [fetch](info:fetch) til at downloade commits. - - The initial URL is `https://api.github.com/repos//commits`, and the next page will be in the `Link` header of the response. - - The `fetch` method allows us to supply authorization and other headers if needed -- here GitHub requires `User-Agent`. -2. The commits are returned in JSON format. -3. We should get the next page URL from the `Link` header of the response. It has a special format, so we use a regular expression for that (we will learn this feature in [Regular expressions](info:regular-expressions)). - - The next page URL may look like `https://api.github.com/repositories/93253246/commits?page=2`. It's generated by GitHub itself. -4. Then we yield the received commits one by one, and when they finish, the next `while(url)` iteration will trigger, making one more request. + - Den indledende URL er `https://api.github.com/repos//commits`, og de næste sider vil være i `Link`-headeren af svaret. + - Metoden `fetch` tillader os at medsende authorization og andre headers hvis det er nødvendigt -- her kræver GitHub `User-Agent`. +2. De resturnerede commits er returneret i JSON format. +3. Vi skal trække URL'en til den næste side ud af `Link`-headeren af svaret. Den har et speciel format, så vi bruger en regular expressions for at finde den. Vi vil lære denne funktion i [Regular expressions](info:regular-expressions). + - URL til den næste sider ser ud i stil med dette: `https://api.github.com/repositories/93253246/commits?page=2`. Den er genereret af GitHub selv. +4. Derefter bruger vi yield til at modtage commits en ad gangen, indtil siden er færdig. Når de er færdige, så trigger vi den næste `while(url)` iteration, hvilket vil resultere i endnu en forespørgsel. -An example of use (shows commit authors in console): +Et eksempel på brug (viser commit forfattere i console): ```js run (async () => { @@ -378,40 +378,41 @@ An example of use (shows commit authors in console): console.log(commit.author.login); - if (++count == 100) { // let's stop at 100 commits + if (++count == 100) { // lad os stoppe ved 100 commits break; } } })(); -// Note: If you are running this in an external sandbox, you'll need to paste here the function fetchCommits described above +// Bemærk: Hvis du kører dette i dit eget miljø, vil du også skulle indsætte funktionen fetchCommits beskrevet ovenfor ``` -That's just what we wanted. +Det er lige hvad vi havde brug for. -The internal mechanics of paginated requests is invisible from the outside. For us it's just an async generator that returns commits. +De interne mekanismer af paginerede anmodninger er usynlige for brugeren af funktionen. For os er det bare en async generator, der returnerer commits. -## Summary +## Opsummering -Regular iterators and generators work fine with the data that doesn't take time to generate. +Regulære iteratorer og generatorer arbejder fint med data, der ikke tager tid at generere. -When we expect the data to come asynchronously, with delays, their async counterparts can be used, and `for await..of` instead of `for..of`. +Når vi forventer data at komme asynkront, med forsinkelser, kan deres async modstørrelser bruges, og `for await..of` i stedet for `for..of`. -Syntax differences between async and regular iterators: +Syntaxforskelle mellem async og regulære iteratorer: -| | Iterable | Async Iterable | +| | Iteratorer | Async iteratorer | |-------|-----------|-----------------| -| Method to provide iterator | `Symbol.iterator` | `Symbol.asyncIterator` | -| `next()` return value is | `{value:…, done: true/false}` | `Promise` that resolves to `{value:…, done: true/false}` | +| Objekt metode der skal gives til iteratoren | `Symbol.iterator` | `Symbol.asyncIterator` | +| `next()` returnerer værdi som | `{value:…, done: true/false}` | `Promise` that resolves to `{value:…, done: true/false}` | +| til loop, brug | `for..of` | `for await..of` | -Syntax differences between async and regular generators: +Syntaksforskelle mellem async og regulære generatorer: -| | Generators | Async generators | +| | Generatorer | Async generatorer | |-------|-----------|-----------------| -| Declaration | `function*` | `async function*` | -| `next()` return value is | `{value:…, done: true/false}` | `Promise` that resolves to `{value:…, done: true/false}` | +| Deklarering | `function*` | `async function*` | +| `next()` returnerer værdi som | `{value:…, done: true/false}` | `Promise` der løser til `{value:…, done: true/false}` | -In web-development we often meet streams of data, when it flows chunk-by-chunk. For instance, downloading or uploading a big file. +I web-udvikling møder vi ofte strømme af data, der sendes bid for bid. For eksempel, når man downloader eller uploader en stor fil. -We can use async generators to process such data. It's also noteworthy that in some environments, like in browsers, there's also another API called Streams, that provides special interfaces to work with such streams, to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere). +Vi kan bruge asynkrone generatorer til at behandle sådanne data. Det er måske også værd at vide, at i nogle miljøer, som f.eks. i browsere, findes der også en anden API kaldet Streams, som tilbyder specielle grænseflader til at arbejde med sådanne strømme, til at transformere dataene og til at sende dem fra en strøm til en anden (f.eks. download fra et sted og umiddelbart sendelse et andet sted). diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/head.html b/1-js/12-generators-iterators/2-async-iterators-generators/head.html index 03f21e2bd..7559ff29a 100644 --- a/1-js/12-generators-iterators/2-async-iterators-generators/head.html +++ b/1-js/12-generators-iterators/2-async-iterators-generators/head.html @@ -4,18 +4,18 @@ while (url) { const response = await fetch(url, { - headers: {'User-Agent': 'Our script'}, // github requires user-agent header + headers: {'User-Agent': 'Our script'}, // github kræver en user-agent header }); - const body = await response.json(); // parses response as JSON (array of commits) + const body = await response.json(); // oversætter svaret som JSON (array af commits) - // the URL of the next page is in the headers, extract it + // URL'en for den næste side er i headerne - træk dem ud let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/); nextPage = nextPage?.[1]; url = nextPage; - // yield commits one by one, when they finish - fetch a new page url + // yield commits en ad gangen. Når det er færdigt? ... fetch en ny sides url for(let commit of body) { yield commit; } From 5ee27c18630a1c8c75c7142cc1c0ef062ca2da5f Mon Sep 17 00:00:00 2001 From: ockley Date: Sat, 28 Mar 2026 16:08:09 +0100 Subject: [PATCH 24/33] oversat til dansk --- 1-js/13-modules/01-modules-intro/article.md | 316 +++++++++--------- .../01-modules-intro/say.view/say.js | 2 +- 1-js/13-modules/index.md | 2 +- 3 files changed, 160 insertions(+), 160 deletions(-) diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md index 5ad70d151..dacf97fad 100644 --- a/1-js/13-modules/01-modules-intro/article.md +++ b/1-js/13-modules/01-modules-intro/article.md @@ -1,108 +1,108 @@ -# Modules, introduction +# Moduler, introduktion -As our application grows bigger, we want to split it into multiple files, so called "modules". A module may contain a class or a library of functions for a specific purpose. +Efterhånden som vores applikation vokser sig større, stiger behovet for at splitte den op i flere filer, såkaldte "moduler". Et modul kan indeholde en klasse eller et bibliotek af funktioner til et specifikt formål. -For a long time, JavaScript existed without a language-level module syntax. That wasn't a problem, because initially scripts were small and simple, so there was no need. +I mange år levede JavaScript uden nogen syntaks for moduler på sprogniveau. Det var ikke det helt store problem, da scripts ofte var små og simple - så der var ikke det store behov for det. -But eventually scripts became more and more complex, so the community invented a variety of ways to organize code into modules, special libraries to load modules on demand. +Men i takt med at scripts blev mere og mere komplekse, opfandt fællesskabet en række måder at organisere kode i moduler, samt specielle biblioteker til at indlæse moduler efter behov. -To name some (for historical reasons): +For at nævne nogle (af historiske grunde, da de ikke længere er så relevante): -- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- one of the most ancient module systems, initially implemented by the library [require.js](https://requirejs.org/). -- [CommonJS](https://wiki.commonjs.org/wiki/Modules/1.1) -- the module system created for Node.js server. -- [UMD](https://github.com/umdjs/umd) -- one more module system, suggested as a universal one, compatible with AMD and CommonJS. +- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- en af de helt gamle modulsystemer, oprindeligt implementeret af biblioteket [require.js](https://requirejs.org/). +- [CommonJS](https://wiki.commonjs.org/wiki/Modules/1.1) -- et modulsystem skal til Node.js server. +- [UMD](https://github.com/umdjs/umd) -- endnu et modulsystem, foreslået som et universelt system og kompatibelt med AMD og CommonJS. -Now these all slowly became a part of history, but we still can find them in old scripts. +Nu bliver alle disse langsomt en del af historien, men vi kan stadig finde dem i gamle scripts. -The language-level module system appeared in the standard in 2015, gradually evolved since then, and is now supported by all major browsers and in Node.js. So we'll study the modern JavaScript modules from now on. +Moduler på sprogniveau så dagens lys i standarden i 2015. Den har gradvist udviklet sig siden og er nu understøttet i alle større browsere og i Node.js - så vil vil studere disse moderne moduler implementeret i JavaScript og lade de andre systemer i fred. -## What is a module? +## Hvad er et modul? -A module is just a file. One script is one module. As simple as that. +Et modul er bare en fil. Et script er ét modul. Det er så simpelt som det kan være. -Modules can load each other and use special directives `export` and `import` to interchange functionality, call functions of one module from another one: +Moduler kan hente hinanden og bruge specielle direktiver `export` og `import` til at bytte funktionalitet, kalde funktioner fra et modul i et andet: -- `export` keyword labels variables and functions that should be accessible from outside the current module. -- `import` allows the import of functionality from other modules. +- `export` nøgleordet mærker variabler og funktioner, der skal være tilgængelige fra uden for det nuværende modul. +- `import` tillader import af funktionalitet fra andre moduler. -For instance, if we have a file `sayHi.js` exporting a function: +For eksempel, hvis vi har en fil `sayHi.js` der eksporterer en funktion: ```js // 📁 sayHi.js export function sayHi(user) { - alert(`Hello, ${user}!`); + alert(`Hej, ${user}!`); } ``` -...Then another file may import and use it: +...Så kan en anden fil importere og bruge den: ```js // 📁 main.js import {sayHi} from './sayHi.js'; alert(sayHi); // function... -sayHi('John'); // Hello, John! +sayHi('John'); // Hej, John! ``` -The `import` directive loads the module by path `./sayHi.js` relative to the current file, and assigns exported function `sayHi` to the corresponding variable. +Direktivet `import` henter modulet med stien `./sayHi.js` relativt til den nuværende fil. Den tildeler den eksporterede funktion `sayHi` til den tilsvarende variabel skrevet i de krøllede parenteser. -Let's run the example in-browser. +Lad os køre eksemplet i browseren. -As modules support special keywords and features, we must tell the browser that a script should be treated as a module, by using the attribute ` ``` -### Module-level scope +### Scope på modul-niveau -Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. +Hvert modul har sit eget top-level scope. Med andre ord, top-level variable og funktioner fra et modul er ikke synlige i andre scripts. -In the example below, two scripts are imported, and `hello.js` tries to use `user` variable declared in `user.js`. It fails, because it's a separate module (you'll see the error in the console): +I eksemplet nedenfor importeres to scripts, og `hello.js` forsøger at bruge `user` variablen deklareret i `user.js`. Det mislykkes, fordi det er et separat modul (du vil se fejlen i konsollen): [codetabs src="scopes" height="140" current="index.html"] -Modules should `export` what they want to be accessible from outside and `import` what they need. +Moduler skal bruge `export` til at fortælle, hvad der skal være tilgængeligt udenfor modulet og selv bruge `import` til det der er brug for udefra. -- `user.js` should export the `user` variable. -- `hello.js` should import it from `user.js` module. +- `user.js` bør eksportere variablen `user`. +- `hello.js` bør importere den fra `user.js` modulet. -In other words, with modules we use import/export instead of relying on global variables. +Med andre ord: Med moduler bruger vi import/export i stedet for at stole på globale variabler. -This is the correct variant: +Her er den korrekte udgave af koden før: [codetabs src="scopes-working" height="140" current="hello.js"] -In the browser, if we talk about HTML pages, independent top-level scope also exists for each ` @@ -114,45 +114,45 @@ Here are two scripts on the same page, both `type="module"`. They don't see each ``` ```smart -In the browser, we can make a variable window-level global by explicitly assigning it to a `window` property, e.g. `window.user = "John"`. +I browseren, kan vi lave en variabel window-level global ved at eksplicit tildele den til en `window` egenskab, f.eks. `window.user = "John"`. -Then all scripts will see it, both with `type="module"` and without it. +Derefter vil alle scripts se den, både med `type="module"` og uden for det. -That said, making such global variables is frowned upon. Please try to avoid them. +Det skal dog understreges, at den måde at oprette globale variable på "giver panderynker" hos andre udviklere. Så prøv at undgå det med mindre at det er nødvendigt. ``` -### A module code is evaluated only the first time when imported +### Koden i et modul evalueres kun den første gang det importeres -If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers. +Hvis det samme modulet importeres af flere andre moduler, evalueres dens kode kun én gang, når det importeres første gang. Derefter gives dens eksporteringer til alle yderligere importører. -The one-time evaluation has important consequences, that we should be aware of. +Den ene evaluering har vigtige konsekvenser, som vi bør være opmærksomme på. -Let's see a couple of examples. +Lad os se et par eksempler. -First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time: +Først, hvis det at eksekvere koden i et modul medfører sideeffekter, f.eks. visning af en besked, så vil import af det samme modul flere gange kun udløse sideeffekten én gang -- første gang: ```js // 📁 alert.js -alert("Module is evaluated!"); +alert("Modulet er evalueret!"); ``` ```js -// Import the same module from different files +// Importer det samme modul fra forskellige filer // 📁 1.js -import `./alert.js`; // Module is evaluated! +import `./alert.js`; // Modulet er evalueret! // 📁 2.js -import `./alert.js`; // (shows nothing) +import `./alert.js`; // (viser intet) ``` -The second import shows nothing, because the module has already been evaluated. +Den anden gang modulet importeres, vises intet, fordi modulet allerede er blevet evalueret. -There's a rule: top-level module code should be used for initialization, creation of module-specific internal data structures. If we need to make something callable multiple times - we should export it as a function, like we did with `sayHi` above. +Der er en regel: top-level modul-kode skal bruges til initialisering, oprettelse af modulspecifikke insterne datastrukture. Hvis vi har behov for at gøre noget kaldbart flere gange - bør vi eksportere det som en funktion, som vi gjorde med `sayHi` ovenfor. -Now, let's consider a deeper example. +Lad os nu se et mere komplekst eksempel. -Let's say, a module exports an object: +Lad os sige, at et modul eksporterer et objekt: ```js // 📁 admin.js @@ -161,9 +161,9 @@ export let admin = { }; ``` -If this module is imported from multiple files, the module is only evaluated the first time, `admin` object is created, and then passed to all further importers. +Hvis dette modul importeres fra flere filer, evalueres modulet kun første gang, `admin` objektet oprettes, og derefter sendes det til alle yderligere importører. -All importers get exactly the one and only `admin` object: +Alle importører får præcis det samme `admin` objekt: ```js // 📁 1.js @@ -175,38 +175,38 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* -// Both 1.js and 2.js reference the same admin object -// Changes made in 1.js are visible in 2.js +// Både 1.js og 2.js refererer til samme admin objekt +// Ændringer lavet i 1.js er synlige i 2.js */!* ``` -As you can see, when `1.js` changes the `name` property in the imported `admin`, then `2.js` can see the new `admin.name`. +Som du kan se, når `1.js` ændrer `name` egenskaben i det importerede `admin`, så kan `2.js` se den nye `admin.name`. -That's exactly because the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other importers will see that. +Det er netop fordi modulet kun bliver eksekveret én gang. Eksporteringer genereres, og derefter deles de mellem importørerne, så hvis noget ændrer `admin` objektet, vil andre importører se det. -**Such behavior is actually very convenient, because it allows us to *configure* modules.** +**Sådan adfærd er faktisk meget praktisk, fordi den tillader os at *konfigurere* moduler.** -In other words, a module can provide a generic functionality that needs a setup. E.g. authentication needs credentials. Then it can export a configuration object expecting the outer code to assign to it. +Med andre ord kan moduler levere generisk funktionalitet, der har brug for opsætning - f.eks. noget der kræver autentifikation. Så kan det eksportere et konfigurationsobjekt, som det forventes, at den ydre kode tildele det. -Here's the classical pattern: -1. A module exports some means of configuration, e.g. a configuration object. -2. On the first import we initialize it, write to its properties. The top-level application script may do that. -3. Further imports use the module. +Her er det klassiske mønster for at konfigurere moduler: +1. Et modul eksporterer nogle metoder til konfiguration, f.eks. et konfigurationsobjekt. +2. Ved første import initialiserer vi det og skriver til dets egenskaber. Det kan et top-level applikationsscript f.eks. gøre. +3. Yderligere importeringer bruger modulet. -For instance, the `admin.js` module may provide certain functionality (e.g. authentication), but expect the credentials to come into the `config` object from outside: +For eksempel kan `admin.js` modulet levere bestemte funktionaliteter (f.eks. autentifikation), men forvente, at legitimationsoplysningerne kommer ind i `config` objektet udefra: ```js // 📁 admin.js export let config = { }; export function sayHi() { - alert(`Ready to serve, ${config.user}!`); + alert(`Jeg er klar, ${config.user}!`); } ``` -Here, `admin.js` exports the `config` object (initially empty, but may have default properties too). +Her eksporterer, `admin.js` selve objektet `config` (Her er det helt tomt, men det kan også indeholde standard egenskaber). -Then in `init.js`, the first script of our app, we import `config` from it and set `config.user`: +Så i `init.js`, det første script i vores app, importerer vi `config` og sætter `config.user`: ```js // 📁 init.js @@ -214,38 +214,38 @@ import {config} from './admin.js'; config.user = "Pete"; ``` -...Now the module `admin.js` is configured. +...Nu er modulet `admin.js` konfigureret. -Further importers can call it, and it correctly shows the current user: +Yderligere importører kan kalde det, og det viser korrekt den nuværende bruger: ```js // 📁 another.js import {sayHi} from './admin.js'; -sayHi(); // Ready to serve, *!*Pete*/!*! +sayHi(); // Jeg er klar, *!*Pete*/!*! ``` ### import.meta -The object `import.meta` contains the information about the current module. +Objektet `import.meta` indeholder informationen om det nuværende modul. -Its content depends on the environment. In the browser, it contains the URL of the script, or a current webpage URL if inside HTML: +Dets indhold afhænger af miljøet. I browseren indeholder det URL'en for scriptet, eller en nuværende webpage URL hvis det er inde i HTML: ```html run height=0 ``` -### In a module, "this" is undefined +### I et modul har "this" værdien undefined -That's kind of a minor feature, but for completeness we should mention it. +Det er måske en mindre ting, men for komplethedens skyld bør vi nævne det. -In a module, top-level `this` is undefined. +I et modul er `this` undefined. -Compare it to non-module scripts, where `this` is a global object: +Sammenlign det med scripts der ikke er sat som moduler, hvor `this` er det globale objekt: ```html run height=0 ``` -## Browser-specific features +## Browserspecifikke features -There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. +Der er også flere browser-specifikke forskelle på scripts med `type="module"` i forhold til regulære scripts. -You may want to skip this section for now if you're reading for the first time, or if you don't use JavaScript in a browser. +Du kan vælge at springe denne sektion over for nu, hvis du læser om JavaScript for første gang, eller hvis du ikke bruger JavaScript i en browser. -### Module scripts are deferred +### Scripts der er moduler er deferred -Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts. +Når du sætter et script som et modul, er det *altid* deferred. Så har det samme effekt som `defer` attributten (beskrevet i kapitlet [](info:script-async-defer)), for både eksterne og inline scripts. -In other words: -- downloading external module scripts ` -Compare to regular script below: +Sammelignet med et regulært script nedenfor: ``` -Please note: the second script actually runs before the first! So we'll see `undefined` first, and then `object`. +Bemærk: Det andet script afvikles faktisk før det første! Så vi ser`undefined` først, efterfulgt af `object`. -That's because modules are deferred, so we wait for the document to be processed. The regular script runs immediately, so we see its output first. +Det er fordi moduler er deferred, så vi venter på, at dokumentet bliver processeret. Det regulære script kører med det samme, så vi ser dens output først. -When using modules, we should be aware that the HTML page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put "loading indicators", or otherwise ensure that the visitor won't be confused by that. +Når man bruger moduler, bør man være opmærksom på, at HTML-siden vises, mens den indlæses, og JavaScript-moduler først kører efter, så brugeren måske ser siden før JavaScript-applikationen er klar. Noget funktionalitet virker muligvis endnu ikke. Vi bør tilføje "loading indicators", eller på anden måde sikre, at besøgende ikke bliver forvirret af det. -### Async works on inline scripts +### Async virker på inline scripts -For non-module scripts, the `async` attribute only works on external scripts. Async scripts run immediately when ready, independently of other scripts or the HTML document. +For scripts der ikke er moduler, virker `async` attributten kun på eksterne scripts. Async scripts kører med det samme, når de er klare, uafhængigt af andre scripts eller HTML-dokumentet. -For module scripts, it works on inline scripts as well. +For modul-scripts, virker det også på inline scripts. -For example, the inline script below has `async`, so it doesn't wait for anything. +For eksempel har inline scriptet nedenfor `async`, så det venter ikke på noget. -It performs the import (fetches `./analytics.js`) and runs when ready, even if the HTML document is not finished yet, or if other scripts are still pending. +Det udfører importen (henter `./analytics.js`) og kører når det er klart, selv hvis HTML-dokumentet ikke er færdigt endnu, eller hvis andre scripts stadig afventer. -That's good for functionality that doesn't depend on anything, like counters, ads, document-level event listeners. +Det er godt for funktionalitet, der ikke afhænger af noget, som tællere, reklamer, og dokumentniveau event listeners. ```html - - + + ``` -### External scripts +### Eksterne scripts -External scripts that have `type="module"` are different in two aspects: +Eksterne scripts der har `type="module"` er forskellige i to aspekter: -1. External scripts with the same `src` run only once: +1. Eksterne scripts med samme `src` kører kun én gang: ```html - + ``` -2. External scripts that are fetched from another origin (e.g. another site) require [CORS](mdn:Web/HTTP/CORS) headers, as described in the chapter . In other words, if a module script is fetched from another origin, the remote server must supply a header `Access-Control-Allow-Origin` allowing the fetch. +2. Eksterne scripts der er hentet fra en anden såkaldt *origin* (f.eks. en anden hjemmeside) kræver [CORS](mdn:Web/HTTP/CORS) headers, som beskrevet i kapitlet . Med andre ord, hvis et modul-script er hentet fra en anden origin, skal den eksterne server tilbyde en header `Access-Control-Allow-Origin` som tillader fetch. ```html - - + + ``` - That ensures better security by default. + Dette sikrer bedre sikkerhed som standard. -### No "bare" modules allowed +### Ingen "bare" modules er tilladt -In the browser, `import` must get either a relative or absolute URL. Modules without any path are called "bare" modules. Such modules are not allowed in `import`. +I browseren skal `import` modtage en relative eller absolut URL. Moduler uden nogen sti kaldes "bare" modules. Sådanne moduler er ikke tilladt i `import`. -For instance, this `import` is invalid: +Denne `import` er derfor ugyldig: ```js import {sayHi} from 'sayHi'; // Error, "bare" module -// the module must have a path, e.g. './sayHi.js' or wherever the module is +// modulet skal have en sti, f.eks. './sayHi.js' der peger på hvor modulet er ``` -Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have their own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. +Nogle miljøer, som f.eks. Node.js eller bundle tools, tillader "bare" modules, uden nogen sti, da de har deres egne måder at finde moduler og hooks til at justere dem. Men browsere understøtter ikke "bare" modules endnu. -### Compatibility, "nomodule" +### Kompatibilitet, "nomodule" -Old browsers do not understand `type="module"`. Scripts of an unknown type are just ignored. For them, it's possible to provide a fallback using the `nomodule` attribute: +Gamel browsere forstår ikke `type="module"`. Scripts der har en ukendt type ignoreres. For dem er det muligt at give en fallback ved hjælp af `nomodule` attributten, som fortæller browseren at det script kun skal køres hvis den ikke forstår `type="module"`.: ```html run ``` ## Build tools -In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. +I virkelige projekter bliver moduler bundlet sammen med et specielt værktøj såsom [Vite](https://vite.dev/) og distribueret til produktionsserveren. -One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. +Der er flere fordele ved at bruge bundlere. De giver mere kontrol over hvordan moduler løses, hvilket tillader "bare" moduler og meget mere, som f.eks. CSS/HTML moduler. -Build tools do the following: +Build tools gør følgende: -1. Take a "main" module, the one intended to be put in ` ``` -That said, native modules are also usable. So we won't be using Webpack here: you can configure it later. +Med det sagt er native modules også brugbare. Vi vil ikke bruge Vite her: du kan konfigurere det senere. -## Summary +## Opsummering -To summarize, the core concepts are: +For at opsummere, er de vigtigste ting at huske om moduler: -1. A module is a file. To make `import/export` work, browsers need ` - + diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js index cff234b7c..534b559af 100644 --- a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js @@ -1,11 +1,11 @@ export function hi() { - alert(`Hello`); + alert(`Hej`); } export function bye() { - alert(`Bye`); + alert(`Farvel`); } -export default function() { - alert("Module loaded (export default)!"); +export default function () { + alert("Modulet er hentet (export default)!"); } From 17fc3213e612eee0f459b75532108243102fa20e Mon Sep 17 00:00:00 2001 From: ockley Date: Tue, 28 Apr 2026 12:05:04 +0200 Subject: [PATCH 27/33] Oversat til dansk Co-authored-by: Copilot --- .../01-browser-environment/article.md | 102 +++++++++--------- 2-ui/1-document/index.md | 4 +- 2-ui/index.md | 4 +- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index eedc28fb3..87cf65e75 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,113 +1,113 @@ -# Browser environment, specs +# Browser miljø, specs -The JavaScript language was initially created for web browsers. Since then, it has evolved into a language with many uses and platforms. +JavaScript blev oprindelig skabt for webbrowsere. Siden da har det udviklet sig til et sprog med mange brugsområder og platforme. -A platform may be a browser, or a web-server or another *host*, or even a "smart" coffee machine if it can run JavaScript. Each of these provides platform-specific functionality. The JavaScript specification calls that a *host environment*. +En platform kan være en browser, eller en web-server eller en anden *host*. Den kan endda en "smart" kaffemaskine hvis den kan køre JavaScript. Hver af disse leverer platform-specifik funktionalitet. JavaScript specifikationen kalder det en *host miljø*. -A host environment provides its own objects and functions in addition to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +Et host miljø leverer dets egne objekter og funktioner i tilføjelse til sprogkernen. Webbrowsere giver et middel til at kontrollere web sider. Node.js leverer server-side funktioner, og så videre. -Here's a bird's-eye view of what we have when JavaScript runs in a web browser: +Her er et simpelt billede af, hvad vi har, når JavaScript kører i en web browser: ![](windowObjects.svg) -There's a "root" object called `window`. It has two roles: +Der er et "root" objekt kaldet `window`. Det har to roller: -1. First, it is a global object for JavaScript code, as described in the chapter . -2. Second, it represents the "browser window" and provides methods to control it. +1. Det er et globalt objekt for JavaScript kode, som beskrevet i kapitlet . +2. Det repræsenterer "browser vinduet" og leverer metoder til at kontrollere det. -For instance, we can use it as a global object: +Vi kan for eksempel bruge det som et globalt objekt, for at definere en global funktion: -```js run global +```js Kør globalt function sayHi() { - alert("Hello"); + alert("Hej!"); } -// global functions are methods of the global object: +// globale funktioner er metoder i det globale objekt: window.sayHi(); ``` -And we can use it as a browser window, to show the window height: +Og vi kan bruge det som et browser vindue, for at vise vinduets højde: ```js run -alert(window.innerHeight); // inner window height +alert(window.innerHeight); // indre højde på browser vinduet ``` -There are more window-specific methods and properties, which we'll cover later. +Der er flere window-specifikke metoder og egenskaber, som vi vil dække senere. ## DOM (Document Object Model) -The Document Object Model, or DOM for short, represents all page content as objects that can be modified. +Document Object Model, ofte bare kaldt DOM, repræsenterer alt indhold på siden der kan ændres. -The `document` object is the main "entry point" to the page. We can change or create anything on the page using it. +Objektet `document` er "hoveddøren" til siden. Vi kan ændre eller oprette noget på siden ved hjælp af det. -For instance: +For eksempel, for at ændre baggrundsfarven på siden, kan vi bruge `document.body.style`: ```js run -// change the background color to red +// gør baggrundsfarven rød document.body.style.background = "red"; -// change it back after 1 second +// nulstil den efter 1 sekund setTimeout(() => document.body.style.background = "", 1000); ``` -Here, we used `document.body.style`, but there's much, much more. Properties and methods are described in the specification: [DOM Living Standard](https://dom.spec.whatwg.org). +Her bruger vi `document.body.style`, men der er mange andre muligheder. Egenskaber og metoder er beskrevet i specificationen for: [DOM Living Standard](https://dom.spec.whatwg.org). -```smart header="DOM is not only for browsers" -The DOM specification explains the structure of a document and provides objects to manipulate it. There are non-browser instruments that use DOM too. +```smart header="DOM er ikke kun for browseren" +DOM specifikationen forklarer strukturen af et dokument og leverer objekter til at manipulere det. Der er ikke-browser instrumenter som også bruger DOM. -For instance, server-side scripts that download HTML pages and process them can also use the DOM. They may support only a part of the specification though. +For eksempel, server-side scripts som downloader HTML sider og behandler dem kan også bruge DOM. De kan måske kun supportere en del af specifikationen. ``` -```smart header="CSSOM for styling" -There's also a separate specification, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) for CSS rules and stylesheets, that explains how they are represented as objects, and how to read and write them. +```smart header="CSSOM til styling" +Der er også en seperat specifikation, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) for CSS-regler og stylesheets, som forklarer, hvordan de repræsenteres som objekter, og hvordan man læser og skriver dem. -The CSSOM is used together with the DOM when we modify style rules for the document. In practice though, the CSSOM is rarely required, because we rarely need to modify CSS rules from JavaScript (usually we just add/remove CSS classes, not modify their CSS rules), but that's also possible. +CSSOM bruges sammen med DOM, når vi ændrer style-regler for dokumentet. I praksis er CSSOM dog sjældent nødvendigt, fordi vi sjælden har brug for at ændre CSS-regler fra JavaScript. For det meste tilføjer eller fjerner vi bare CSS-klasser. Kun i sjældne tilfælde ændrer vi CSS-regler, men det er muligt. ``` ## BOM (Browser Object Model) -The Browser Object Model (BOM) represents additional objects provided by the browser (host environment) for working with everything except the document. +Browser Object Model (BOM) repræsenterer yderligere objekter leveret af browseren (hostmiljøet) til at arbejde med alt undtagen dokumentet. -For instance: +For eksempel: -- The [navigator](mdn:api/Window/navigator) object provides background information about the browser and the operating system. There are many properties, but the two most widely known are: `navigator.userAgent` -- about the current browser, and `navigator.platform` -- about the platform (can help to differentiate between Windows/Linux/Mac etc). -- The [location](mdn:api/Window/location) object allows us to read the current URL and can redirect the browser to a new one. +- Objektet [navigator](mdn:api/Window/navigator) leverer baggrundsinformation om browseren ogopretaivsystemet. Det indeholder mange egenskaber, men de to mest kendte er: `navigator.userAgent` -- den aktuelle browser, og `navigator.platform` -- platformen der kører browseren (kan hjælpe med at differentiere mellem Windows/Linux/Mac etc). +- Objektet [location](mdn:api/Window/location) tillader dig at læse den nuværende URL og kan omdirigere browseren til en ny URL. -Here's how we can use the `location` object: +Her er hvordan vi kan bruge `location`-objektet til at vise den nuværende URL og omdirigere browseren til Wikipedia, hvis brugeren bekræfter det: ```js run -alert(location.href); // shows current URL +alert(location.href); // Vis den nuværende URL if (confirm("Go to Wikipedia?")) { - location.href = "https://wikipedia.org"; // redirect the browser to another URL + location.href = "https://wikipedia.org"; // omdirigere browseren til en ny URL } ``` -The functions `alert/confirm/prompt` are also a part of the BOM: they are not directly related to the document, but represent pure browser methods for communicating with the user. +Funktionerne `alert/confirm/prompt` er også en del af BOM: de er ikke direkte relaterede til dokumentet, men repræsenterer rene browsermetoder til kommunikation med brugeren. -```smart header="Specifications" -The BOM is a part of the general [HTML specification](https://html.spec.whatwg.org). +```smart header="Specifikationer" +BOM er en del af den generelle [HTML specifikation](https://html.spec.whatwg.org). -Yes, you heard that right. The HTML spec at is not only about the "HTML language" (tags, attributes), but also covers a bunch of objects, methods, and browser-specific DOM extensions. That's "HTML in broad terms". Also, some parts have additional specs listed at . +Ja, du hørte rigtigt. HTML specifikationen fra handler ikke kun om "HTML sproget" (tags, attributter, etc), men dækker også over en stor bunke objekter, metoder og browser-specifikke DOM-udvidelser. Det er "HTML i bred betydning". Desuden har nogle dele yderligere specifikationer opført andre steder på . ``` -## Summary +## Opsummering -Talking about standards, we have: +Når vi taler om standarder, så har vi: -DOM specification -: Describes the document structure, manipulations, and events, see . +DOM specifikationen +: Beskriver strukturen af et dokument, manipulation af den og hændelser (events), se . -CSSOM specification -: Describes stylesheets and style rules, manipulations with them, and their binding to documents, see . +CSSOM specifikation +: Beskriver stylesheet og regler for styling, manipulation af dem, og deres binding til dokumenter, se . -HTML specification -: Describes the HTML language (e.g. tags) and also the BOM (browser object model) -- various browser functions: `setTimeout`, `alert`, `location` and so on, see . It takes the DOM specification and extends it with many additional properties and methods. +HTML specifikation +: Beskriver HTML sproget (f.eks. tags) og også BOM (browser object model) -- forskellige browserfunktioner: `setTimeout`, `alert`, `location` og så videre, se . Den tager DOM specifikationen og udvider den med mange yderligere egenskaber og metoder. -Additionally, some classes are described separately at . +Ud over disse specifikationer er der også flere klasser beskrevet separat på . -Please note these links, as there's so much to learn that it's impossible to cover everything and remember it all. +Gem disse links, da der er så meget at lære og det er umuligt at dække eller huske det hele. -When you'd like to read about a property or a method, the Mozilla manual at is also a nice resource, but the corresponding spec may be better: it's more complex and longer to read, but will make your fundamental knowledge sound and complete. +Når du vil læse om en egenskab eller en metode, er Mozilla manualen på også en god ressource. Du kan starte med den, men den tilsvarende spec kan nogle gange være bedre for den øvede. Den er mere kompleks og længere at læse, men vil gøre din fundamentale viden komplet. -To find something, it's often convenient to use an internet search "WHATWG [term]" or "MDN [term]", e.g , . +For at finde noget, er det ofte praktisk at bruge en internet søgning "WHATWG [term]" eller "MDN [term]", f.eks , . -Now, we'll get down to learning the DOM, because the document plays the central role in the UI. +Nu er det tid til at lære DOM nærmere at kende, fordi dokumentet spiller en central rolle i UI. \ No newline at end of file diff --git a/2-ui/1-document/index.md b/2-ui/1-document/index.md index 1b60cdf2c..22907c912 100644 --- a/2-ui/1-document/index.md +++ b/2-ui/1-document/index.md @@ -1,3 +1,3 @@ -# Document +# Dokumentet -Here we'll learn to manipulate a web-page using JavaScript. +Her vil vi lære at manipulere en web-side ved hjælp af JavaScript. diff --git a/2-ui/index.md b/2-ui/index.md index d94378cf3..e0d45ed1f 100644 --- a/2-ui/index.md +++ b/2-ui/index.md @@ -1,3 +1,3 @@ -# Browser: Document, Events, Interfaces +# Browser: Dokumenter, Hændelser, brugergrænseflader -Learning how to manage the browser page: add elements, manipulate their size and position, dynamically create interfaces and interact with the visitor. +Lær hvordan man håndterer browserens side: tilføj elementer, manipuler deres størrelse og position, opret grænseflader dynamisk og interager med brugeren. From a0a033c573995e423b56542dad8b88e08454ee51 Mon Sep 17 00:00:00 2001 From: ockley Date: Thu, 30 Apr 2026 11:43:06 +0200 Subject: [PATCH 28/33] Oversat til dansk Co-authored-by: Copilot --- 2-ui/1-document/02-dom-nodes/article.md | 188 ++++++++++++------------ 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index f7f2be91d..721da37ae 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -4,35 +4,35 @@ libs: --- -# DOM tree +# DOM træet -The backbone of an HTML document is tags. +Rygraden i et HTML dokument er tags. -According to the Document Object Model (DOM), every HTML tag is an object. Nested tags are "children" of the enclosing one. The text inside a tag is an object as well. +Ifølge Document Object Model (DOM) er alle HTML tag objekter. Indlejrede tags kaldes "Børn" ("children" på engelsk)af det omkransende tag. Fri tekst inde i et tag er også objekter. -All these objects are accessible using JavaScript, and we can use them to modify the page. +Alle disse objekter er tilgængelige ved hjælp af JavaScript, og vi kan bruge dem til at ændre siden. -For example, `document.body` is the object representing the `` tag. +For eksempel er `document.body` objektet, der repræsenterer `` tagget. -Running this code will make the `` red for 3 seconds: +Hvis vi kører denne kode, vil `` blive rød i 3 sekunder: ```js run -document.body.style.background = 'red'; // make the background red +document.body.style.background = 'red'; // gør baggrunden rød -setTimeout(() => document.body.style.background = '', 3000); // return back +setTimeout(() => document.body.style.background = '', 3000); // vend tilbage ``` -Here we used `style.background` to change the background color of `document.body`, but there are many other properties, such as: +Her bruger vi `style.background` til at ændre baggrundsfarven på `document.body`, men der er mange andre egenskaber, såsom: -- `innerHTML` -- HTML contents of the node. -- `offsetWidth` -- the node width (in pixels) -- ...and so on. +- `innerHTML` -- HTML-indholdet af noden. +- `offsetWidth` -- nodens bredde (i pixels) +- ...og så videre. -Soon we'll learn more ways to manipulate the DOM, but first we need to know about its structure. +Snart vil vi lære flere måder at manipulere DOM'en på, men først skal vi kende dens struktur. -## An example of the DOM +## Et eksempel på DOM'en -Let's start with the following simple document: +Lad os starte med følgende simple dokument: ```html run no-beautify @@ -46,7 +46,7 @@ Let's start with the following simple document: ``` -The DOM represents HTML as a tree structure of tags. Here's how it looks: +DOM'en repræsenterer HTML dokumentet som en træstruktur af tags. Her er hvordan det ser ud:
@@ -57,31 +57,31 @@ drawHtmlTree(node1, 'div.domtree', 690, 320); ```online -On the picture above, you can click on element nodes and their children will open/collapse. +På billedet ovenfor kan du klikke på elementer og deres børn vil åbne/lukke. ``` -Every tree node is an object. +Hver node i træet er et objekt. -Tags are *element nodes* (or just elements) and form the tree structure: `` is at the root, then `` and `` are its children, etc. +Tags er *element-noder* (eller bare elementer) og danner træstrukturer: `` er på roden, så `` og `` er dens børn, etc. -The text inside elements forms *text nodes*, labelled as `#text`. A text node contains only a string. It may not have children and is always a leaf of the tree. +Teksten inde i elementer danner *tekst noder*, mærket som `#text`. En text node indeholder kun en streng. Den må ikke have børn og kan ses som et blad (leaf på engelsk) i træet. -For instance, the `` tag has the text `"About elk"`. +For eksempel har `<title>` tagget teksten `"About elk"`. -Please note the special characters in text nodes: +Bemærk de specielle tegn i text noder: -- a newline: `↵` (in JavaScript known as `\n`) -- a space: `␣` +- en ny linie: `↵` (i JavaScript ofte skrevet som `\n`) +- et mellemrum: `␣` -Spaces and newlines are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the `<head>` tag contains some spaces before `<title>`, and that text becomes a `#text` node (it contains a newline and some spaces only). +Mellemrum og linjeskift er gyldelige tegn, ligesom bogstaver og cifre. De danner text noder og bliver en del af DOM'en. Således bliver teksten i eksemplet ovenfor, som indeholder mellemrum før `<title>`, til en `#text` node (den indeholder en ny linie og nogle mellemrum). -There are only two top-level exclusions: -1. Spaces and newlines before `<head>` are ignored for historical reasons. -2. If we put something after `</body>`, then that is automatically moved inside the `body`, at the end, as the HTML spec requires that all content must be inside `<body>`. So there can't be any spaces after `</body>`. +Der er kun to top-niveau undtagelser: +1. Mellemrum og linjeskift før `<head>` ignoreres af historiske grunde. +2. Hvis vi putter noget efter `</body>`, så flyttes det automatisk ind i `body`, ved enden, da HTML specifikationen kræver, at alt indhold skal være inde i `<body>`. Så der kan ikke være nogle mellemrum efter `</body>`. -In other cases everything's straightforward -- if there are spaces (just like any character) in the document, then they become text nodes in the DOM, and if we remove them, then there won't be any. +I alle andre tilfælde er det lige ud af landevejen - hvis der er mellemrum (ligesom ethvert tegn) i dokumentet, så bliver de til tekst noder i DOM'en, og hvis vi fjerner dem, så vil der ikke være nogle. -Here are no space-only text nodes: +Her er et eksempel på en DOM med tekst noder uden mellemrum eller linjeskift: ```html no-beautify <!DOCTYPE HTML> @@ -96,21 +96,21 @@ let node2 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node2, 'div.domtree', 690, 210); </script> -```smart header="Spaces at string start/end and space-only text nodes are usually hidden in tools" -Browser tools (to be covered soon) that work with DOM usually do not show spaces at the start/end of the text and empty text nodes (line-breaks) between tags. +```smart header="Mellemrum ved start/slut af tekststreng og tekst noder der kun indeholder mellemrum skjules ofte i værktøjer" +Browser værktøjer (som vil blive dækket snart) der arbejder med DOM'en viser ofte ikke mellemrum ved start/end af teksten og tomme tekst noder (linjeskift) mellem tags. -Developer tools save screen space this way. +Det er en måde for udviklerværktøjet at spare på pladsen. -On further DOM pictures we'll sometimes omit them when they are irrelevant. Such spaces usually do not affect how the document is displayed. +Ved fremtidige DOM billeder vil vi ofte udelade dem, når de er irrelevante. Sådanne mellemrum påvirker normalt ikke, hvordan dokumentet vises. ``` -## Autocorrection +## Autokorrektur -If the browser encounters malformed HTML, it automatically corrects it when making the DOM. +Hvis browseren møder fejlformateret HTML, korrigere den automatisk når DOM'en oprettes. -For instance, the top tag is always `<html>`. Even if it doesn't exist in the document, it will exist in the DOM, because the browser will create it. The same goes for `<body>`. +For eksempel er første tag altid `<html>`. Selv hvis det ikke eksisterer i dokumentet, vil det eksistere i DOM'en, fordi browseren vil oprette det. Det samme gælder for `<body>`. -As an example, if the HTML file is the single word `"Hello"`, the browser will wrap it into `<html>` and `<body>`, and add the required `<head>`, and the DOM will be: +Som et eksempel, hvis HTML-filen er det ene ord `"Hello"`, vil browseren pakke det ind i `<html>` og `<body>`, og tilføje det nødvendige `<head>`, og DOM'en vil være: <div class="domtree"></div> @@ -121,9 +121,9 @@ let node3 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node3, 'div.domtree', 690, 150); </script> -While generating the DOM, browsers automatically process errors in the document, close tags and so on. +Når DOM'en genereres, korrigere browseren automatisk fejl i dokumentet, lukker tags og så videre. -A document with unclosed tags: +Et dokument med åbne tags: ```html no-beautify <p>Hello @@ -132,7 +132,7 @@ A document with unclosed tags: <li>Dad ``` -...will become a normal DOM as the browser reads tags and restores the missing parts: +...vil blive til et normalt DOM træ, da browseren læser tags og gendanner de manglende dele: <div class="domtree"></div> @@ -142,16 +142,16 @@ let node4 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node4, 'div.domtree', 690, 360); </script> -````warn header="Tables always have `<tbody>`" -An interesting "special case" is tables. By DOM specification they must have `<tbody>` tag, but HTML text may omit it. Then the browser creates `<tbody>` in the DOM automatically. +````warn header="Tabeller har altid `<tbody>`" +Et interessant "særtilfælde" er ved tabeller. Ifølge DOM specifikationen skal de have `<tbody>` tag, men HTML tekst kan udelade det. Derfor opretter browseren `<tbody>` i DOM'en automatisk. -For the HTML: +For HTML der ser sådan ud: ```html no-beautify <table id="table"><tr><td>1</td></tr></table> ``` -DOM-structure will be: +vil DOM-strukturen være: <div class="domtree"></div> <script> @@ -160,14 +160,14 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType": drawHtmlTree(node5, 'div.domtree', 600, 200); </script> -You see? The `<tbody>` appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises. +Kan du se det? Tagget `<tbody>` skabes ud af det blå. Det er værd at have i baghovedet, når vi arbejder med tabeller for at undgå overraskelser. ```` -## Other node types +## Andre typer af noder -There are some other node types besides elements and text nodes. +Der findes et par andre noder end elementer og tekstnoder. -For example, comments: +For eksempel, kommentarer: ```html <!DOCTYPE HTML> @@ -193,90 +193,90 @@ let node6 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node6, 'div.domtree', 690, 500); </script> -We can see here a new tree node type -- *comment node*, labeled as `#comment`, between two text nodes. +Her ser vi en ny type node -- *kommentar node*, mærket som `#comment`, imellem to tekstnoder. -We may think -- why is a comment added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree. +Vi kan tænke -- hvorfor bliver en kommentar tilføjet til DOM'en? Det påvirker ikke den visuelle repræsentation på nogen måde. Men der er en regel -- hvis noget er i HTML, så skal det også være i DOM-træet. -**Everything in HTML, even comments, becomes a part of the DOM.** +**Alt i HTML, også kommentarer, bliver en del af DOM'en.** -Even the `<!DOCTYPE...>` directive at the very beginning of HTML is also a DOM node. It's in the DOM tree right before `<html>`. Few people know about that. We are not going to touch that node, we even don't draw it on diagrams, but it's there. +Selv `<!DOCTYPE...>`-direktivet i begyndelsen af HTML er også en DOM-node. Den er i DOM-træet lige før `<html>`. Få mennesker ved det. Vi kommer ikke til at røre den node, vi tegner den heller ikke på diagrammer, men den er der. -The `document` object that represents the whole document is, formally, a DOM node as well. +Objektet `document` der repræsenterer hele dokumentet er formelt set også en DOM node. -There are [12 node types](https://dom.spec.whatwg.org/#node). In practice we usually work with 4 of them: +Der er [12 typer af noder](https://dom.spec.whatwg.org/#node). I praksis arbejder vi mest med fire af dem: -1. `document` -- the "entry point" into DOM. -2. element nodes -- HTML-tags, the tree building blocks. -3. text nodes -- contain text. -4. comments -- sometimes we can put information there, it won't be shown, but JS can read it from the DOM. +1. `document` -- "hoveddøren" ind til DOM. +2. element noder -- HTML-tags, byggeblokkene til selve træset. +3. tekst noder -- indeholder tekst. +4. kommentarer -- Vi kan placere information der, det vil ikke blive vist, men JS kan læse det fra DOM'en. -## See it for yourself +## Test det selv -To see the DOM structure in real-time, try [Live DOM Viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Just type in the document, and it will show up as a DOM at an instant. +For at se DOM-strukturen i realtid, kan du prøve at bruge [Live DOM Viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Skriv blot i dokumentet, og det vil blive vist som en DOM med det samme. -Another way to explore the DOM is to use the browser developer tools. Actually, that's what we use when developing. +En anden måde at udforske DOM'en er at bruge browserens udviklerværktøjer. Faktisk er det, vi bruger, når vi udvikler. -To do so, open the web page [elk.html](elk.html), turn on the browser developer tools and switch to the Elements tab. +For at gøre det, åbn web siden [elk.html](elk.html), slå browserens udviklerværktøjer til og skift til Elements-fanen. -It should look like this: +Det burde se ud i stil med dette: ![](elk.svg) -You can see the DOM, click on elements, see their details and so on. +Du kan se din DOM, klikke på elementer for at se detaljer etc. -Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no "blank" (space only) text nodes at all. That's fine, because most of the time we are interested in element nodes. +Bemærk, at DOM strukturen i udviklerværktøjer er forenklet. Tekstnoder vises kun som tekst. Og der er ingen "tomme" (kun mellemrum) tekstnoder overhovedet. Det er i orden, fordi de fleste gange er vi kun interesseret i elementnoder. -Clicking the <span class="devtools" style="background-position:-328px -124px"></span> button in the left-upper corner allows us to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it. +At klikke på <span class="devtools" style="background-position:-328px -124px"></span> knappen i det øverste venstre hjørne giver os mulighed for at vælge en node fra siden ved hjælp af en mus og "inspicere" den (rulle ned til den i Elements-fanen). Dette virker godt, når vi har en meget stor HTML-side (og derfor en tilsvarende stor DOM) og gerne vil se placeringen af en bestemt element i den. -Another way to do it would be just right-clicking on a webpage and selecting "Inspect" in the context menu. +En anden måde at gøre det på ville være at højreklikke på en web side og vælge "Inspect" i kontekstmenuen. ![](inspect.svg) -At the right part of the tools there are the following subtabs: -- **Styles** -- we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited in-place, including the dimensions/margins/paddings of the box below. -- **Computed** -- to see CSS applied to the element by property: for each property we can see a rule that gives it (including CSS inheritance and such). -- **Event Listeners** -- to see event listeners attached to DOM elements (we'll cover them in the next part of the tutorial). -- ...and so on. +I højre side af værktøjerne er følgende underfaner: +- **Styles** -- vi kan se CSS påført til det aktuelle element regel for regel, inklusive indbyggede regler (grå). Næsten alt kan redigeres "på stedet", herunder dimensioner/margin/padding af boksen. +- **Computed** -- for at se CSS påført til elementet efter egenskab: for hver egenskab kan vi se de regler som giver den sit udseende (inklusiv CSS arv mm.). +- **Event Listeners** -- for at se hvilke hændelser der lyttes efter på DOM elementer (de bliver dækket i den næste del af tutorialen). +... og flere endnu, som jeg ikke vil dække i denne del. -The best way to study them is to click around. Most values are editable in-place. +Den bedste måde at studere dem er at klikke rundt. De fleste værdier er redigerbare in-place. -## Interaction with console +## Interaktion med konsollen -As we work the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console. +Når vi arbejder med DOM'en, vil vi ofte bruge JavaScript i den sammenhæng: Hente et element og afvikle noget kode der modificerer det og sende det tilbage for at se resultatet. Her er et par tips til at bevæge dig mellem Elements-fanen og konsollen. -For the start: +Start med: -1. Select the first `<li>` in the Elements tab. -2. Press `key:Esc` -- it will open console right below the Elements tab. +1. Vælg det første `<li>` i Elements-fanen. +2. Tryk `key:Esc` -- det vil åbne konsollen lige under Elements-fanen. -Now the last selected element is available as `$0`, the previously selected is `$1` etc. +Nu er det sidst valgte element tilgængeligt som `$0`, det tidligere valgte er `$1` etc. -We can run commands on them. For instance, `$0.style.background = 'red'` makes the selected list item red, like this: +Nu kan vi køre kommandoer som f. eks. `$0.style.background = 'red'` for at gøre det valgte listeelement rødt: ![](domconsole0.svg) -That's how to get a node from Elements in Console. +Sådan kan du referere direkte til en node fra Elements i konsollen. -There's also a road back. If there's a variable referencing a DOM node, then we can use the command `inspect(node)` in Console to see it in the Elements pane. +Der er også en vej tilbage. Hvis der er en variabel der refererer til en DOM-node, så kan vi bruge kommandoen `inspect(node)` i konsollen for at se den i Elements-fanen. -Or we can just output the DOM node in the console and explore "in-place", like `document.body` below: +Eller vi kan bare outputte DOM-noden i konsollen og udforske den "på stedet", som `document.body` nedenfor: ![](domconsole1.svg) -That's for debugging purposes of course. From the next chapter on we'll access and modify DOM using JavaScript. +Dette er selvfølgelig kun til debugging. Fra og med næste kapitel vil vi se nærmere på, hvordan vi arbejder med DOM'en ved hjælp af JavaScript. -The browser developer tools are a great help in development: we can explore the DOM, try things and see what goes wrong. +Browserens udviklingsværktøjer er en stor hjælp i udviklingen: vi kan udforske DOM'en, prøve ting og se, hvad der går galt. -## Summary +## Opsummering -An HTML/XML document is represented inside the browser as the DOM tree. +Et HTML/XML dokument er repræsenteret inde i browseren som et DOM-træ. -- Tags become element nodes and form the structure. -- Text becomes text nodes. -- ...etc, everything in HTML has its place in DOM, even comments. +- Tags bliver til elementnoder og former selve strukturen. +- Tekst bliver til tekstnoder. +- ...etc, alt i HTML har sin plads i DOM, også kommentarer. -We can use developer tools to inspect DOM and modify it manually. +Vi kan bruge udviklerværktøjer til at inpicere DOM'en og modificere den manuelt. -Here we covered the basics, the most used and important actions to start with. There's an extensive documentation about Chrome Developer Tools at <https://developers.google.com/web/tools/chrome-devtools>. The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest. +Vi har dækket de grundlæggende koncepter, de mest brugte og vigtige handlinger for at komme i gang. Der er en omfattende dokumentation om Chrome Developer Tools på <https://developers.google.com/web/tools/chrome-devtools>. Den bedste måde at lære værktøjerne på er at klikke her og der, læse menuerne: de fleste muligheder er åbenlyse. Senere, når du føler dig mere tryg, kan du læse dokumentationen og opdage resten. -DOM nodes have properties and methods that allow us to travel between them, modify them, move around the page, and more. We'll get down to them in the next chapters. +DOM-noder har egenskaber og metoder, der tillader os at rejse mellem dem, modificere dem, flytte rundt på siden og mere. Vi vil komme ind på dem i de næste kapitler. From 92378317833cafb28258fc1087c5a62ea4e8a553 Mon Sep 17 00:00:00 2001 From: ockley <vestergaard.karsten@gmail.com> Date: Thu, 30 Apr 2026 13:23:59 +0200 Subject: [PATCH 29/33] Oversat til dansk --- .../1-dom-children/solution.md | 16 +- .../03-dom-navigation/1-dom-children/task.md | 18 +- .../3-navigation-links-which-null/solution.md | 8 +- .../3-navigation-links-which-null/task.md | 8 +- .../4-select-diagonal-cells/solution.md | 2 +- .../4-select-diagonal-cells/task.md | 10 +- 2-ui/1-document/03-dom-navigation/article.md | 218 +++++++++--------- .../03-dom-navigation/dom-links-elements.svg | 105 ++++++++- .../03-dom-navigation/dom-links.svg | 98 +++++++- 9 files changed, 341 insertions(+), 142 deletions(-) diff --git a/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md b/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md index decfa62c7..66fa68b9b 100644 --- a/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md +++ b/2-ui/1-document/03-dom-navigation/1-dom-children/solution.md @@ -1,27 +1,27 @@ -There are many ways, for instance: +Der er mange måder at gå til det på. Det kunne for eksempel være: -The `<div>` DOM node: +`<div>` DOM noden: ```js document.body.firstElementChild -// or +// eller document.body.children[0] -// or (the first node is space, so we take 2nd) +// eller (den første node er et mellemrum, så vi tager den anden) document.body.childNodes[1] ``` -The `<ul>` DOM node: +`<ul>` DOM noden: ```js document.body.lastElementChild -// or +// eller document.body.children[1] ``` -The second `<li>` (with Pete): +Den anden `<li>` (med Mikkel): ```js -// get <ul>, and then get its last element child +// hent <ul>, og derefter hent det sidste barn der er et element document.body.lastElementChild.lastElementChild ``` diff --git a/2-ui/1-document/03-dom-navigation/1-dom-children/task.md b/2-ui/1-document/03-dom-navigation/1-dom-children/task.md index d97f2748a..9979594f5 100644 --- a/2-ui/1-document/03-dom-navigation/1-dom-children/task.md +++ b/2-ui/1-document/03-dom-navigation/1-dom-children/task.md @@ -2,23 +2,23 @@ importance: 5 --- -# DOM children +# DOM børn -Look at this page: +Se på følgende side: ```html <html> <body> - <div>Users:</div> + <div>Brugere:</div> <ul> - <li>John</li> - <li>Pete</li> + <li>Lisbeth</li> + <li>Mikkel</li> </ul> </body> </html> ``` -For each of the following, give at least one way of how to access them: -- The `<div>` DOM node? -- The `<ul>` DOM node? -- The second `<li>` (with Pete)? +For hver af de følgende, giv mindst en måde at tilgå dem: +- `<div>` DOM noden? +- `<ul>` DOM noden? +- Den anden `<li>` (med Mikkel)? diff --git a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md index d76936320..56774df7b 100644 --- a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md +++ b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md @@ -1,6 +1,6 @@ -1. Yes, true. The element `elem.lastChild` is always the last one, it has no `nextSibling`. -2. No, wrong, because `elem.children[0]` is the first child *among elements*. But there may exist non-element nodes before it. So `previousSibling` may be a text node. +1. Ja, det er sandt. Elementet `elem.lastChild` er altid det sidste - det har ikke nogen `nextSibling`. +2. Nej, det er ikke sandt, fordi `elem.children[0]` er det første barn *mellem elementer*. Men der kan eksistere ikke-elementer før det. Så `previousSibling` kan være en tekstnode. -Please note: for both cases if there are no children, then there will be an error. +Bemærk: for begge tilfælde, hvis der ikke er nogen børn, vil der være en fejl. -If there are no children, `elem.lastChild` is `null`, so we can't access `elem.lastChild.nextSibling`. And the collection `elem.children` is empty (like an empty array `[]`). +Hvis der ikke er nogen børn, er `elem.lastChild` `null`, så vi kan ikke tilgå `elem.lastChild.nextSibling`. Og samlingen `elem.children` er tom (ligesom et tomt array `[]`). diff --git a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md index 235e83a0c..6ba9d7fb2 100644 --- a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md +++ b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# The sibling question +# Spørgsmålet om søskende -If `elem` -- is an arbitrary DOM element node... +Hvis `elem` -- er en tilfølglig DOM element node... -- Is it true that `elem.lastChild.nextSibling` is always `null`? -- Is it true that `elem.children[0].previousSibling` is always `null` ? +- Er det sandt at `elem.lastChild.nextSibling` altid er `null`? +- Er det sandt at `elem.children[0].previousSibling` altid er `null`? diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md index f2aa86302..3a937beb9 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md @@ -1 +1 @@ -We'll be using `rows` and `cells` properties to access diagonal table cells. +Vi vil bruge `rows` og `cells` egenskaberne til at tilgå diagonale tabelceller. diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md index 23be59fc1..23063dcec 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# Select all diagonal cells +# Vælg alle diagonale celler -Write the code to paint all diagonal table cells in red. +Skriv koden der maler alle diagonale tabelceller røde. -You'll need to get all diagonal `<td>` from the `<table>` and paint them using the code: +Du skal hente alle diagonale `<td>` fra `<table>` og male dem ved hjælp af koden: ```js -// td should be the reference to the table cell +// td skal være referencen til tabelcellen td.style.backgroundColor = 'red'; ``` -The result should be: +resulatet skal være dette: [iframe src="solution" height=180] diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index b5f03098c..aa0af9a27 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -5,37 +5,37 @@ libs: --- -# Walking the DOM +# Gennemløbe DOM'en -The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object. +DOM'en tillader os at gøre alt med dens elementer og deres indhold, men først skal vi "nå det" tilsvarende DOM-objekt. -All operations on the DOM start with the `document` object. That's the main "entry point" to DOM. From it we can access any node. +Alle operationer på DOM'en starter med `document`-objektet. Det er "hoveddøren" til din DOM. Fra det kan vi tilgå enhver node. -Here's a picture of links that allow for travel between DOM nodes: +Her er et billede af links, der tillader rejse mellem DOM-noder: ![](dom-links.svg) -Let's discuss them in more detail. +Lad os diskutere dem lidt i detaljer. -## On top: documentElement and body +## I toppen: documentElement og body -The topmost tree nodes are available directly as `document` properties: +De øverste trænoder er tilgængelige direkte som `document`-egenskaber: `<html>` = `document.documentElement` -: The topmost document node is `document.documentElement`. That's the DOM node of the `<html>` tag. +: Den øverste document node er `document.documentElement`. Det er DOM-noden for `<html>` tagget. `<body>` = `document.body` -: Another widely used DOM node is the `<body>` element -- `document.body`. +: En anden meget brugt DOM-node er `<body>`-elementet -- `document.body`. `<head>` = `document.head` -: The `<head>` tag is available as `document.head`. +: Tagget `<head>` er tilgængeligt som `document.head`. -````warn header="There's a catch: `document.body` can be `null`" -A script cannot access an element that doesn't exist at the moment of running. +````warn header="Der er en hage: `document.body` kan være `null`" +Et script kan ikke tilgå et element, der ikke eksisterer ved det øjeblik, hvor scriptet kører. -In particular, if a script is inside `<head>`, then `document.body` is unavailable, because the browser did not read it yet. +I særdeleshed, hvis et script er indeni `<head>`, så er `document.body` ikke tilgængeligt, fordi browseren har endnu ikke læst det. -So, in the example below the first `alert` shows `null`: +Så, i eksemplet nedenfor viser den første `alert` `null`: ```html run <html> @@ -43,7 +43,7 @@ So, in the example below the first `alert` shows `null`: <head> <script> *!* - alert( "From HEAD: " + document.body ); // null, there's no <body> yet + alert( "Fra HEAD: " + document.body ); // null, der er ikke noget <body> endnu */!* </script> </head> @@ -51,7 +51,7 @@ So, in the example below the first `alert` shows `null`: <body> <script> - alert( "From BODY: " + document.body ); // HTMLBodyElement, now it exists + alert( "Fra BODY: " + document.body ); // HTMLBodyElement, findes nu </script> </body> @@ -59,23 +59,23 @@ So, in the example below the first `alert` shows `null`: ``` ```` -```smart header="In the DOM world `null` means \"doesn't exist\"" -In the DOM, the `null` value means "doesn't exist" or "no such node". +```smart header="På DOM sprog betyder `null` at det \"ikke eksisterer\"" +Hvis du møder `null` i DOM'en, betyder det "eksisterer ikke" eller "ingen sådan node". ``` ## Children: childNodes, firstChild, lastChild -There are two terms that we'll use from now on: +Der er to termer vi vil bruge fra nu af: -- **Child nodes (or children)** -- elements that are direct children. In other words, they are nested exactly in the given one. For instance, `<head>` and `<body>` are children of `<html>` element. -- **Descendants** -- all elements that are nested in the given one, including children, their children and so on. +- **Child nodes (eller children/børn)** -- elementer der er direkte børn af et element. For eksempel er `<head>` og `<body>` børn af `<html>` elementet. +- **Descendants** (eller efterkommere) -- alle elementer der er indlejret i et givent element, inklusivt deres bør og børn af børn osv. -For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text nodes): +For eksempelt har `<body>` her `<div>` og `<ul>` som børn (og nogle tomme tekstnoder): ```html run <html> <body> - <div>Begin</div> + <div>Start</div> <ul> <li> @@ -86,22 +86,22 @@ For instance, here `<body>` has children `<div>` and `<ul>` (and few blank text </html> ``` -...And descendants of `<body>` are not only direct children `<div>`, `<ul>` but also more deeply nested elements, such as `<li>` (a child of `<ul>`) and `<b>` (a child of `<li>`) -- the entire subtree. +... og efterkommere af `<body>` er ikke kun børnene `<div>`, `<ul>` men også elementer der ligger dybere indlejret, såsom `<li>` (barn af `<ul>`) og `<b>` (et barn af `<li>`) -- hele den del af træet. -**The `childNodes` collection lists all child nodes, including text nodes.** +**Samlingen `childNodes` lister alle child nodes, inklusiv tekstnoder.** -The example below shows children of `document.body`: +Eksemplet nedenfor viser børn af `document.body`: ```html run <html> <body> - <div>Begin</div> + <div>Start</div> <ul> <li>Information</li> </ul> - <div>End</div> + <div>Slut</div> <script> *!* @@ -110,81 +110,81 @@ The example below shows children of `document.body`: } */!* </script> - ...more stuff... + ...mere her... </body> </html> ``` -Please note an interesting detail here. If we run the example above, the last element shown is `<script>`. In fact, the document has more stuff below, but at the moment of the script execution the browser did not read it yet, so the script doesn't see it. +Bemærk en interessant detalje her. Hvis vi kører eksemplet ovenfor, er det sidste element vist `<script>`. Faktisk har dokumentet mere indhold nedenfor, men på det tidspunkt hvor scriptet kører har browseren ikke læst det endnu, så scriptet ser det ikke. -**Properties `firstChild` and `lastChild` give fast access to the first and last children.** +**Egenskaberne `firstChild` og `lastChild` giver hurtig adgang til første og sidste barn.** -They are just shorthands. If there exist child nodes, then the following is always true: +De er bare hurtige genveje. Hvis der findes børn, så er det altid sandt: ```js elem.childNodes[0] === elem.firstChild elem.childNodes[elem.childNodes.length - 1] === elem.lastChild ``` -There's also a special function `elem.hasChildNodes()` to check whether there are any child nodes. +Der er også en særlig funktion `elem.hasChildNodes()` til at tjekke om der er nogle børn. -### DOM collections +### DOM samlinger -As we can see, `childNodes` looks like an array. But actually it's not an array, but rather a *collection* -- a special array-like iterable object. +Som vi kan se så ligner `childNodes` et array. Men det er faktisk ikke et array, men en *collection* (samling) -- et særligt array-lignende objekt. -There are two important consequences: +Det har derfor to vigtige konsekvenser for os: -1. We can use `for..of` to iterate over it: +1. Vi kan bruge `for..of` til at iterere over den: ```js for (let node of document.body.childNodes) { - alert(node); // shows all nodes from the collection + alert(node); // viser alle noder fra samlingen } ``` - That's because it's iterable (provides the `Symbol.iterator` property, as required). + Det er fordi den er itererbar (leverer egenskaben `Symbol.iterator`, som det kræves). -2. Array methods won't work, because it's not an array: +2. Array metode virker ikke fordi det ikke er et array: ```js run - alert(document.body.childNodes.filter); // undefined (there's no filter method!) + alert(document.body.childNodes.filter); // undefined (der er ikke nogen filter-metode!) ``` -The first thing is nice. The second is tolerable, because we can use `Array.from` to create a "real" array from the collection, if we want array methods: +Det første er skønt. Det andet er håndterbart, fordi vi kan bruge `Array.from` til at skabe en "rigtig" array fra samlingen, hvis vi vil have array-metoder: ```js run alert( Array.from(document.body.childNodes).filter ); // function ``` -```warn header="DOM collections are read-only" -DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only. +```warn header="DOM samlinger er kun til at læse fra" +DOM samlinger, eller i det hele taget -- *alle* egenskaber til navigation som vi taler om i dette kapitel er kun til at læse fra. -We can't replace a child by something else by assigning `childNodes[i] = ...`. +Vi kan ikke erstatte et barn med noget andet ved at tildele `childNodes[i] = ...`. -Changing DOM needs other methods. We will see them in the next chapter. +At ændre DOM kræver andre metoder. Vi vil se dem i næste kapitel. ``` -```warn header="DOM collections are live" -Almost all DOM collections with minor exceptions are *live*. In other words, they reflect the current state of DOM. +```warn header="DOM samlinger er live" +Næsten alle DOM samlinger, med enkelte undtagelser, er *live*. Med andre ord, de afspejler den nuværende tilstand af din DOM. -If we keep a reference to `elem.childNodes`, and add/remove nodes into DOM, then they appear in the collection automatically. +Hvis vi holder en reference til `elem.childNodes`, og tilføjer/fjerner noder til DOM, så vises de automatisk i samlingen. ``` -````warn header="Don't use `for..in` to loop over collections" -Collections are iterable using `for..of`. Sometimes people try to use `for..in` for that. +````warn header="Brug ikke `for..in` til at loope over samlinger" +Samlinger er itererbare ved brug af `for..of`. Nogle gange prøver folk at bruge `for..in` til det. -Please, don't. The `for..in` loop iterates over all enumerable properties. And collections have some "extra" rarely used properties that we usually do not want to get: +Lad være med det. Et `for..in`-loop itererer over alle enumerable egenskaber. Og samlinger har nogle "ekstra" sjældent brugte egenskaber, som vi normalt ikke vil have: ```html run <body> <script> - // shows 0, 1, length, item, values and more. + // viser 0, 1, length, item, values and more. for (let prop in document.body.childNodes) alert(prop); </script> </body> ```` -## Siblings and the parent +## Søskende og forældre (siblings og parents) -*Siblings* are nodes that are children of the same parent. +*Siblings* er noder som er børn af samme forælder. -For instance, here `<head>` and `<body>` are siblings: +For eksempel så er `<head>` og `<body>` søskende her: ```html <html> @@ -192,75 +192,75 @@ For instance, here `<head>` and `<body>` are siblings: </html> ``` -- `<body>` is said to be the "next" or "right" sibling of `<head>`, -- `<head>` is said to be the "previous" or "left" sibling of `<body>`. +- `<body>` bliver kaldt den "næste" eller "højre" søskende af `<head>`, +- `<head>` bliver kaldt den "forrige" eller "venstre" søskende af `<body>`. -The next sibling is in `nextSibling` property, and the previous one - in `previousSibling`. +Den næste søskende er kan hentes med egenskaben `nextSibling`, og den forrige - med `previousSibling`. -The parent is available as `parentNode`. +Forældren er tilgængelig som `parentNode`. -For example: +For eksempel: ```js run -// parent of <body> is <html> +// forælder til <body> er <html> alert( document.body.parentNode === document.documentElement ); // true -// after <head> goes <body> +// efter <head> kommer <body> alert( document.head.nextSibling ); // HTMLBodyElement -// before <body> goes <head> +// før <body> kommer <head> alert( document.body.previousSibling ); // HTMLHeadElement ``` -## Element-only navigation +## Navigation af elementer alene -Navigation properties listed above refer to *all* nodes. For instance, in `childNodes` we can see both text nodes, element nodes, and even comment nodes if they exist. +Egenskaberne til navigation som set ovenfor refererer til *alle* noder. Vi kan for eksempel med `childNodes` se både tekstnoder, elementnoder, og endda kommentar noder hvis de eksisterer. -But for many tasks we don't want text or comment nodes. We want to manipulate element nodes that represent tags and form the structure of the page. +Men i mange tilfælde har vi ikke lyst til at arbejde med tekst- eller kommentar noder. Vi vil gerne manipulere elementnoder, som repræsenterer HTML-tags og udgør strukturen af siden. -So let's see more navigation links that only take *element nodes* into account: +Så lad os se flere navigations links, som kun tager *element noder* i betragtning: ![](dom-links-elements.svg) -The links are similar to those given above, just with `Element` word inside: +De minder om dem vi har set tidligere, de har bare fået ordet `Element` tilføjet: -- `children` -- only those children that are element nodes. -- `firstElementChild`, `lastElementChild` -- first and last element children. -- `previousElementSibling`, `nextElementSibling` -- neighbor elements. -- `parentElement` -- parent element. +- `children` -- kun de børn der er elementnoder. +- `firstElementChild`, `lastElementChild` -- første og sidste barn der er elementnode. +- `previousElementSibling`, `nextElementSibling` -- naboelementer. +- `parentElement` -- forældre-element. -````smart header="Why `parentElement`? Can the parent be *not* an element?" -The `parentElement` property returns the "element" parent, while `parentNode` returns "any node" parent. These properties are usually the same: they both get the parent. +````smart header="Why `parentElement`? Kan en forælder *undgå* at være et element?" +Egenskaben `parentElement` returnerer "element" forælderen, mens `parentNode` returnerer "alle typer af node" forælder. Disse egenskaber der normalt de samme: de henter begge forælderen. -With the one exception of `document.documentElement`: +Der er dog én undtagelse med `document.documentElement`: ```js run alert( document.documentElement.parentNode ); // document alert( document.documentElement.parentElement ); // null ``` -The reason is that the root node `document.documentElement` (`<html>`) has `document` as its parent. But `document` is not an element node, so `parentNode` returns it and `parentElement` does not. +Grunden til dette er at roden `document.documentElement` (`<html>`) har `document` som sin forælder. Men `document` er ikke en element node så `parentNode` returner den mens `parentElement` ikke gør. -This detail may be useful when we want to travel up from an arbitrary element `elem` to `<html>`, but not to the `document`: +Den lille detalje kan være brugbar, hvis vi vil "rejse" op gennem træet fra et vilkårligt element `elem` til `<html>`, men ikke til `document`: ```js -while(elem = elem.parentElement) { // go up till <html> +while(elem = elem.parentElement) { // gå op til <html> alert( elem ); } ``` ```` -Let's modify one of the examples above: replace `childNodes` with `children`. Now it shows only elements: +Lad os modificere et af eksemplerne ovenfor: erstat `childNodes` med `children`. Nu viser det kun elementerne, og ignorerer tekstnoderne: ```html run <html> <body> - <div>Begin</div> + <div>Start</div> <ul> <li>Information</li> </ul> - <div>End</div> + <div>Slut</div> <script> *!* @@ -274,60 +274,60 @@ Let's modify one of the examples above: replace `childNodes` with `children`. No </html> ``` -## More links: tables [#dom-navigation-tables] +## Flere links: tabeller [#dom-navigation-tables] -Till now we described the basic navigation properties. +Indtil nu har vi beskrevet de grundlæggende navigationsegenskaber. -Certain types of DOM elements may provide additional properties, specific to their type, for convenience. +Bestemte typer af DOM-elementer kan levere yderligere egenskaber, specifikke for deres type, for nemhedens skyld. -Tables are a great example of that, and represent a particularly important case: +Tabeller er et godt eksempel, og de repræsenterer en særlig vigtig case: -**The `<table>`** element supports (in addition to the given above) these properties: -- `table.rows` -- the collection of `<tr>` elements of the table. -- `table.caption/tHead/tFoot` -- references to elements `<caption>`, `<thead>`, `<tfoot>`. -- `table.tBodies` -- the collection of `<tbody>` elements (can be many according to the standard, but there will always be at least one -- even if it is not in the source HTML, the browser will put it in the DOM). +**`<table>`** elementet undestøtter (ud over det vi har lært) disse egenskaber: +- `table.rows` -- en samling af `<tr>` elementer fra tabellen. +- `table.caption/tHead/tFoot` -- referencer til elementerne `<caption>`, `<thead>`, `<tfoot>`. +- `table.tBodies` -- samlingen af `<tbody>` elementer (kan være mange ifølge standarden, men der vil altid være mindst en -- selvom den ikke er i din egen kilde HTML, vil browseren sætte den i DOM). -**`<thead>`, `<tfoot>`, `<tbody>`** elements provide the `rows` property: -- `tbody.rows` -- the collection of `<tr>` inside. +**`<thead>`, `<tfoot>`, `<tbody>`** elementer leverer egenskaben `rows`: +- `tbody.rows` -- samlingen af `<tr>` elementer indeni. **`<tr>`:** -- `tr.cells` -- the collection of `<td>` and `<th>` cells inside the given `<tr>`. -- `tr.sectionRowIndex` -- the position (index) of the given `<tr>` inside the enclosing `<thead>/<tbody>/<tfoot>`. -- `tr.rowIndex` -- the number of the `<tr>` in the table as a whole (including all table rows). +- `tr.cells` -- samlingen af `<td>` og `<th>` celler indeni det givne `<tr>`. +- `tr.sectionRowIndex` -- positionen (indeks) af det givne `<tr>` indeni det omkringliggende `<thead>/<tbody>/<tfoot>`. +- `tr.rowIndex` -- nummeret på den givne `<tr>` i tabellen som helhed (inklusiv alle tabelrækker). **`<td>` and `<th>`:** -- `td.cellIndex` -- the number of the cell inside the enclosing `<tr>`. +- `td.cellIndex` -- nummeret på cellen indeni den omkransende `<tr>`. -An example of usage: +Et eksempel på brug: ```html run height=100 <table id="table"> <tr> - <td>one</td><td>two</td> + <td>en</td><td>to</td> </tr> <tr> - <td>three</td><td>four</td> + <td>tre</td><td>fire</td> </tr> </table> <script> - // get td with "two" (first row, second column) + // hent td med "to" (første række, anden kolonne) let td = table.*!*rows[0].cells[1]*/!*; - td.style.backgroundColor = "red"; // highlight it + td.style.backgroundColor = "red"; // fremhæv den </script> ``` -The specification: [tabular data](https://html.spec.whatwg.org/multipage/tables.html). +Specifikationen: [tabular data](https://html.spec.whatwg.org/multipage/tables.html). -There are also additional navigation properties for HTML forms. We'll look at them later when we start working with forms. +Der er også yderligere muligheder for at navigere formularer. Dem kigger vi på senere når vi skal til at arbejde med formularer. -## Summary +## Opsummering -Given a DOM node, we can go to its immediate neighbors using navigation properties. +Givet en hvilken som helst DOM-node, kan vi gå til dens umiddelbare naboer ved at bruge navigationsegenskaber. -There are two main sets of them: +Der er to hovedsæt af dem: -- For all nodes: `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`. -- For element nodes only: `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`. +- For alle noder: `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`. +- For elementnoder alene: `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`. -Some types of DOM elements, e.g. tables, provide additional properties and collections to access their content. +Nogle typer af DOM-elementer, f.eks. tabeller, leverer yderligere egenskaber og samlinger til at tilgå deres indhold. diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements.svg b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg index fd0b2826a..079ad6e5f 100644 --- a/2-ui/1-document/03-dom-navigation/dom-links-elements.svg +++ b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg @@ -1 +1,104 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="440" height="316" viewBox="0 0 440 316"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-links-elements.svg"><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M129 10h198v28H129z"/><text id="document.documentEle" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="142.6" y="29">document.documentElement </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="336.9" y="29"><HTML></tspan></text><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M163 78h117v28H163z"/><text id="document.body--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="174.2" y="95">document.body </tspan></text><text id="(if-inside-body)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="300.9" y="95">(if inside body)</tspan></text><path id="Line-5" stroke="#AF6E24" stroke-linecap="square" stroke-width="2" d="M14.5 115H427"/><text id="parentElement" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="175" y="172" fill="#AF6E24">parent</tspan> <tspan x="218.2" y="172" fill="#C06334">Element</tspan></text><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M180 213h80v28h-80z"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="204.192" y="232"><DIV></tspan></text><path id="Line-6" fill="#C06334" fill-rule="nonzero" d="M220.5 178.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674V208.5h-2v-23.933l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-7" fill="#C06334" fill-rule="nonzero" d="M415.369 218.388l.871.49 12 6.75 1.55.872-1.55.872-12 6.75-.871.49-.98-1.743.87-.49 8.673-4.879H266.5v-2h157.432l-8.672-4.878-.872-.49.98-1.744z"/><text id="nextElementSibling" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="276" y="212" fill="#AF6E24">next</tspan> <tspan x="304.8" y="212" fill="#C06334">Element</tspan> <tspan x="355.2" y="212" fill="#AF6E24">Sibling</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M23.631 218.388l.98 1.743-.87.49-8.674 4.879H169v2H15.067l8.673 4.878.872.49-.98 1.744-.872-.49-12-6.75-1.55-.872 1.55-.872 12-6.75.871-.49z"/><text id="previousElementSibli" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="6" y="212" fill="#AF6E24">previous</tspan> <tspan x="63.6" y="212" fill="#C06334">Element</tspan> <tspan x="114" y="212" fill="#AF6E24">Sibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M174.822 248.123l1.055 1.7-.85.527-48.476 30.089-6.917 4.292 9.941-.428 1-.043.085 1.998-.999.043-13.755.594-1.776.076.857-1.557 6.636-12.064.482-.876 1.752.964-.482.876-4.797 8.718 6.918-4.293 48.477-30.089.85-.527z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M269.214 248.115l.835.55 46.157 30.354 6.78 4.46-4.565-8.841-.459-.889 1.777-.918.459.889 6.317 12.233.816 1.58-1.774-.123-13.735-.954-.997-.07.138-1.995.998.07 9.926.689-6.78-4.46-46.156-30.354-.836-.55 1.099-1.671z"/><text id="children" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="188" y="273">children</tspan></text><text id="firstElementChild--" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="43" y="307" fill="#AF6E24">first</tspan> <tspan x="79" y="307" fill="#C06334">Element</tspan> <tspan x="129.4" y="307" fill="#AF6E24">Child </tspan></text><text id="lastElementChild" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="273" y="307" fill="#AF6E24">last</tspan> <tspan x="301.8" y="307" fill="#C06334">Element</tspan> <tspan x="352.2" y="307" fill="#AF6E24">Child</tspan></text><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M222.5 151.5v4h-2v-4h2zm0-6v4h-2v-4h2zm0-6v4h-2v-4h2zm-1-14.29l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001.435h-2l-.001-.432-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55zm1 8.29v4h-2v-4h2z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M221.5 44.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v21.528h-2V50.567l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/></g></g></svg> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 440 316"> + <!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) --> + <defs> + <style> + .st0, .st1 { + font-family: PTMono-Regular, 'PT Mono'; + } + + .st0, .st1, .st2 { + font-size: 12px; + } + + .st0, .st1, .st2, .st3 { + isolation: isolate; + } + + .st0, .st4 { + fill: #c06334; + } + + .st5 { + fill: #fbf2ec; + fill-rule: evenodd; + stroke: #dbaf88; + } + + .st5, .st6 { + stroke-width: 2px; + } + + .st1, .st2 { + fill: #af6e24; + } + + .st2 { + font-family: OpenSans, 'Open Sans'; + } + + .st6 { + fill: none; + stroke: #af6e24; + stroke-linecap: square; + } + </style> + </defs> + <g id="dom"> + <g id="dom-links-elements.svg"> + <path id="Rectangle-8" class="st5" d="M129,10h198v28h-198V10Z"/> + <g id="document.documentEle" class="st3"> + <text class="st1" transform="translate(142.6 29)"><tspan x="0" y="0">document.documentElement</tspan></text> + </g> + <g id="Type-something" class="st3"> + <text class="st1" transform="translate(336.9 29)"><tspan x="0" y="0"><HTML></tspan></text> + </g> + <path id="Rectangle-7" class="st5" d="M163,78h117v28h-117v-28Z"/> + <g id="document.body--" class="st3"> + <text class="st1" transform="translate(174.2 95)"><tspan x="0" y="0">document.body</tspan></text> + </g> + <g id="_x28_if-inside-body_x29_" class="st3"> + <text class="st1" transform="translate(300.9 95)"><tspan x="0" y="0">(hvis inde i body)</tspan></text> + </g> + <path id="Line-5" class="st6" d="M14.5,115h412.5"/> + <g id="parentElement" class="st3"> + <text class="st1" transform="translate(175 172)"><tspan x="0" y="0">parent</tspan></text> + <text class="st0" transform="translate(218.2 172)"><tspan x="0" y="0">Element</tspan></text> + </g> + <path id="Rectangle-6" class="st5" d="M180,213h80v28h-80v-28Z"/> + <g id="_x3C_DIV_x3E_" class="st3"> + <text class="st2" transform="translate(204.19 232)"><tspan x="0" y="0"><DIV></tspan></text> + </g> + <path id="Line-6" class="st4" d="M220.5,178.71l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.67v23.93h-2v-23.93l-4.88,8.67-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0Z"/> + <path id="Line-7" class="st4" d="M415.37,218.39l.87.49,12,6.75,1.55.87-1.55.87-12,6.75-.87.49-.98-1.74.87-.49,8.67-4.88h-157.43v-2h157.43l-8.67-4.88-.87-.49.98-1.74h0Z"/> + <g id="nextElementSibling" class="st3"> + <text class="st1" transform="translate(276 212)"><tspan x="0" y="0">next</tspan></text> + <text class="st0" transform="translate(304.8 212)"><tspan x="0" y="0">Element</tspan></text> + <text class="st1" transform="translate(355.2 212)"><tspan x="0" y="0">Sibling</tspan></text> + </g> + <path id="Line-8" class="st4" d="M23.63,218.39l.98,1.74-.87.49-8.67,4.88h153.93v2H15.07l8.67,4.88.87.49-.98,1.74-.87-.49-12-6.75-1.55-.87,1.55-.87,12-6.75.87-.49h0Z"/> + <g id="previousElementSibli" class="st3"> + <text class="st1" transform="translate(6 212)"><tspan x="0" y="0">previous</tspan></text> + <text class="st0" transform="translate(63.6 212)"><tspan x="0" y="0">Element</tspan></text> + <text class="st1" transform="translate(114 212)"><tspan x="0" y="0">Sibling</tspan></text> + </g> + <path id="Line" class="st4" d="M174.82,248.12l1.05,1.7-.85.53-48.48,30.09-6.92,4.29,9.94-.43,1-.04.09,2-1,.04-13.75.59-1.78.08.86-1.56,6.64-12.06.48-.88,1.75.96-.48.88-4.8,8.72,6.92-4.29,48.48-30.09.85-.53h0Z"/> + <path id="Line-3" class="st4" d="M269.21,248.12l.83.55,46.16,30.35,6.78,4.46-4.57-8.84-.46-.89,1.78-.92.46.89,6.32,12.23.82,1.58-1.77-.12-13.73-.95-1-.07.14-1.99,1,.07,9.93.69-6.78-4.46-46.16-30.35-.84-.55,1.1-1.67h0Z"/> + <g id="children" class="st3"> + <text class="st0" transform="translate(188 273)"><tspan x="0" y="0">children</tspan></text> + </g> + <g id="firstElementChild--" class="st3"> + <text class="st1" transform="translate(43 307)"><tspan x="0" y="0">first</tspan></text> + <text class="st0" transform="translate(79 307)"><tspan x="0" y="0">Element</tspan></text> + <text class="st1" transform="translate(129.4 307)"><tspan x="0" y="0">Child</tspan></text> + </g> + <g id="lastElementChild" class="st3"> + <text class="st1" transform="translate(273 307)"><tspan x="0" y="0">last</tspan></text> + <text class="st0" transform="translate(301.8 307)"><tspan x="0" y="0">Element</tspan></text> + <text class="st1" transform="translate(352.2 307)"><tspan x="0" y="0">Child</tspan></text> + </g> + <path id="Line-Copy-2" class="st4" d="M222.5,151.5v4h-2v-4h2ZM222.5,145.5v4h-2v-4h2ZM222.5,139.5v4h-2v-4h2ZM221.5,125.21l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.68v.43s-2,0-2,0v-.43s-4.88,8.67-4.88,8.67l-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0ZM222.5,133.5v4h-2v-4h2Z"/> + <path id="Line-2-Copy" class="st4" d="M221.5,44.71l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.67v21.53h-2v-21.53l-4.88,8.67-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0Z"/> + </g> + </g> +</svg> \ No newline at end of file diff --git a/2-ui/1-document/03-dom-navigation/dom-links.svg b/2-ui/1-document/03-dom-navigation/dom-links.svg index 6c34bca4a..739c7bc90 100644 --- a/2-ui/1-document/03-dom-navigation/dom-links.svg +++ b/2-ui/1-document/03-dom-navigation/dom-links.svg @@ -1 +1,97 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="420" height="388" viewBox="0 0 420 388"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-links.svg"><path id="Rectangle-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M150 20h117v28H150z"/><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M151 154h117v28H151z"/><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M117 87h198v28H117z"/><text id="document" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="179.7" y="38">document</tspan></text><text id="document.documentEle" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="129.6" y="105">document.documentElement </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="324.9" y="105"><HTML></tspan></text><text id="document.body--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="164.2" y="173">document.body </tspan></text><text id="(if-inside-body)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="283.9" y="173">(if inside body)</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M209.5 119.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v23.785h-2v-23.785l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M209.5 52.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v21.528h-2V58.567l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line" stroke="#AF6E24" stroke-linecap="square" stroke-width="2" d="M2.5 191H415"/><text id="parentNode" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="172" y="248">parentNode</tspan></text><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M169 289h80v28h-80z"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="192.192" y="308"><DIV></tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M208.5 254.21l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001 8.185v16.25h-2v-16.25l-.001-8.182-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M209.5 226.5v4h-2v-4h2zm0-6v4h-2v-4h2zm0-6v4h-2v-4h2zm-1-14.29l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001.435h-2l-.001-.432-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55zm1 8.29v4h-2v-4h2z"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M356.369 294.388l.871.49 12 6.75 1.55.872-1.55.872-12 6.75-.871.49-.98-1.743.87-.49 8.673-4.879H254.5v-2h110.432l-8.672-4.878-.872-.49.98-1.744z"/><text id="nextSibling" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="264" y="288">nextSibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M61.631 294.388l.98 1.743-.87.49-8.674 4.879H165.5v2H53.067l8.673 4.878.872.49-.98 1.744-.872-.49-12-6.75-1.55-.872 1.55-.872 12-6.75.871-.49z"/><text id="previousSibling" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="50" y="288">previousSibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M162.822 324.123l1.055 1.7-.85.527-48.476 30.089-6.917 4.292 9.941-.428 1-.043.085 1.998-.999.043-13.755.594-1.776.076.857-1.557 6.636-12.064.482-.876 1.752.964-.482.876-4.797 8.718 6.918-4.293 48.477-30.089.85-.527z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M257.214 324.115l.835.55 46.157 30.354 6.78 4.46-4.565-8.841-.459-.889 1.777-.918.459.889 6.317 12.233.816 1.58-1.774-.123-13.735-.954-.997-.07.138-1.995.998.07 9.926.689-6.78-4.46-46.156-30.354-.836-.55 1.099-1.671z"/><text id="childNodes" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="170" y="343">childNodes</tspan></text><text id="firstChild--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="64" y="379">firstChild </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="279" y="379">lastChild</tspan></text></g></g></svg> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 420 388"> + <!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) --> + <defs> + <style> + .st0 { + fill: #fbf2ec; + fill-rule: evenodd; + stroke: #dbaf88; + } + + .st0, .st1 { + stroke-width: 2px; + } + + .st2 { + font-family: PTMono-Regular, 'PT Mono'; + } + + .st2, .st3 { + fill: #af6e24; + font-size: 12px; + } + + .st2, .st3, .st4 { + isolation: isolate; + } + + .st3 { + font-family: OpenSans, 'Open Sans'; + } + + .st5 { + fill: #c06334; + } + + .st1 { + fill: none; + stroke: #af6e24; + stroke-linecap: square; + } + </style> + </defs> + <g id="dom"> + <g id="dom-links.svg"> + <path id="Rectangle-9" class="st0" d="M150,20h117v28h-117v-28Z"/> + <path id="Rectangle-7" class="st0" d="M151,154h117v28h-117v-28Z"/> + <path id="Rectangle-8" class="st0" d="M117,87h198v28H117v-28Z"/> + <g id="document" class="st4"> + <text class="st2" transform="translate(179.7 38)"><tspan x="0" y="0">document</tspan></text> + </g> + <g id="document.documentEle" class="st4"> + <text class="st2" transform="translate(129.6 105)"><tspan x="0" y="0">document.documentElement</tspan></text> + </g> + <g id="Type-something" class="st4"> + <text class="st2" transform="translate(324.9 105)"><tspan x="0" y="0"><HTML></tspan></text> + </g> + <g id="document.body--" class="st4"> + <text class="st2" transform="translate(164.2 173)"><tspan x="0" y="0">document.body</tspan></text> + </g> + <g id="_x28_if-inside-body_x29_" class="st4"> + <text class="st2" transform="translate(283.9 173)"><tspan x="0" y="0">(hvis inde i body)</tspan></text> + </g> + <path id="Line" class="st5" d="M209.5,119.71l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.67v23.79h-2v-23.79l-4.88,8.67-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0Z"/> + <path id="Line-2" class="st5" d="M209.5,52.71l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.67v21.53h-2v-21.53l-4.88,8.67-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0Z"/> + <path id="Line1" data-name="Line" class="st1" d="M2.5,191h412.5"/> + <g id="parentNode" class="st4"> + <text class="st2" transform="translate(172 248)"><tspan x="0" y="0">parentNode</tspan></text> + </g> + <path id="Rectangle-6" class="st0" d="M169,289h80v28h-80v-28Z"/> + <g id="_x3C_DIV_x3E_" class="st4"> + <text class="st3" transform="translate(192.19 308)"><tspan x="0" y="0"><DIV></tspan></text> + </g> + <path id="Line2" data-name="Line" class="st5" d="M208.5,254.21l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.68v8.18s0,16.25,0,16.25h-2v-24.43s-4.88,8.67-4.88,8.67l-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0Z"/> + <path id="Line-Copy" class="st5" d="M209.5,226.5v4h-2v-4h2ZM209.5,220.5v4h-2v-4h2ZM209.5,214.5v4h-2v-4h2ZM208.5,200.21l.87,1.55,6.75,12,.49.87-1.74.98-.49-.87-4.88-8.68v.43s-2,0-2,0v-.43s-4.88,8.67-4.88,8.67l-.49.87-1.74-.98.49-.87,6.75-12,.87-1.55h0ZM209.5,208.5v4h-2v-4h2Z"/> + <path id="Line3" data-name="Line" class="st5" d="M356.37,294.39l.87.49,12,6.75,1.55.87-1.55.87-12,6.75-.87.49-.98-1.74.87-.49,8.67-4.88h-110.43v-2h110.43l-8.67-4.88-.87-.49.98-1.74h0Z"/> + <g id="nextSibling" class="st4"> + <text class="st2" transform="translate(264 288)"><tspan x="0" y="0">nextSibling</tspan></text> + </g> + <path id="Line4" data-name="Line" class="st5" d="M61.63,294.39l.98,1.74-.87.49-8.67,4.88h112.43v2H53.07l8.67,4.88.87.49-.98,1.74-.87-.49-12-6.75-1.55-.87,1.55-.87,12-6.75.87-.49h0Z"/> + <g id="previousSibling" class="st4"> + <text class="st2" transform="translate(50 288)"><tspan x="0" y="0">previousSibling</tspan></text> + </g> + <path id="Line5" data-name="Line" class="st5" d="M162.82,324.12l1.05,1.7-.85.53-48.48,30.09-6.92,4.29,9.94-.43,1-.04.08,2-1,.04-13.75.59-1.78.08.86-1.56,6.64-12.06.48-.88,1.75.96-.48.88-4.8,8.72,6.92-4.29,48.48-30.09.85-.53h0Z"/> + <path id="Line-3" class="st5" d="M257.21,324.11l.83.55,46.16,30.35,6.78,4.46-4.57-8.84-.46-.89,1.78-.92.46.89,6.32,12.23.82,1.58-1.77-.12-13.73-.95-1-.07.14-1.99,1,.07,9.93.69-6.78-4.46-46.16-30.35-.84-.55,1.1-1.67Z"/> + <g id="childNodes" class="st4"> + <text class="st2" transform="translate(170 343)"><tspan x="0" y="0">childNodes</tspan></text> + </g> + <g id="firstChild--" class="st4"> + <text class="st2" transform="translate(64 379)"><tspan x="0" y="0">firstChild</tspan></text> + </g> + <g id="Type-something1" data-name="Type-something" class="st4"> + <text class="st2" transform="translate(279 379)"><tspan x="0" y="0">lastChild</tspan></text> + </g> + </g> + </g> +</svg> \ No newline at end of file From 00ebbf91034186f58d8fa47e7ae5cdea3499d9c5 Mon Sep 17 00:00:00 2001 From: ockley <vestergaard.karsten@gmail.com> Date: Sat, 2 May 2026 10:00:07 +0200 Subject: [PATCH 30/33] Oversat til dansk --- .../1-find-elements/solution.md | 24 +-- .../1-find-elements/table.html | 78 ++++---- .../1-find-elements/task.md | 21 +- .../04-searching-elements-dom/article.md | 189 +++++++++--------- 4 files changed, 160 insertions(+), 152 deletions(-) diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md index c73aecd99..564058950 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md @@ -1,35 +1,35 @@ -There are many ways to do it. +Der er mange måder at gøre det. -Here are some of them: +Her er et par af dem: ```js -// 1. The table with `id="age-table"`. +// 1. Tabellen med `id="age-table"`. let table = document.getElementById('age-table') -// 2. All label elements inside that table +// 2. Alle label-elementer inde i den tabel table.getElementsByTagName('label') // or document.querySelectorAll('#age-table label') -// 3. The first td in that table (with the word "Age") +// 3. Det første `td` i den tabel (med ordet "Alder") table.rows[0].cells[0] // or table.getElementsByTagName('td')[0] // or table.querySelector('td') -// 4. The form with the name "search" -// assuming there's only one element with name="search" in the document +// 4. Formularen med navnet "search" +// antager, at der kun er ét element med name="search" i dokumentet let form = document.getElementsByName('search')[0] -// or, form specifically +// Eller specifikt selve formularen document.querySelector('form[name="search"]') -// 5. The first input in that form. +// 5. Det første input i den form form.getElementsByTagName('input')[0] // or form.querySelector('input') -// 6. The last input in that form -let inputs = form.querySelectorAll('input') // find all inputs -inputs[inputs.length-1] // take the last one +// 6. Det sidste input i den form +let inputs = form.querySelectorAll('input') // find alle input +inputs[inputs.length-1] // tag den sidste ``` diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html b/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html index 5b92c34b9..af76d43ca 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html @@ -1,42 +1,48 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> - <form name="search"> - <label>Search the site: - <input type="text" name="search"> - </label> - <input type="submit" value="Search!"> - </form> + <body> + <form name="search"> + <label> + Søg på siden: + <input type="text" name="search" /> + </label> + <input type="submit" value="Search!" /> + </form> - <hr> + <hr /> - <form name="search-person"> - Search the visitors: - <table id="age-table"> - <tr> - <td>Age:</td> - <td id="age-list"> - <label> - <input type="radio" name="age" value="young">less than 18</label> - <label> - <input type="radio" name="age" value="mature">18-50</label> - <label> - <input type="radio" name="age" value="senior">more than 50</label> - </td> - </tr> + <form name="search-person"> + Søg efter besøgende: + <table id="age-table"> + <tr> + <td>Alder:</td> + <td id="age-list"> + <label> + <input type="radio" name="age" value="young" /> + under 18 + </label> + <label> + <input type="radio" name="age" value="mature" /> + 18-50 + </label> + <label> + <input type="radio" name="age" value="senior" /> + over 50 + </label> + </td> + </tr> - <tr> - <td>Additionally:</td> - <td> - <input type="text" name="info[0]"> - <input type="text" name="info[1]"> - <input type="text" name="info[2]"> - </td> - </tr> + <tr> + <td>Yderligere:</td> + <td> + <input type="text" name="info[0]" /> + <input type="text" name="info[1]" /> + <input type="text" name="info[2]" /> + </td> + </tr> + </table> - </table> - - <input type="submit" value="Search!"> - </form> -</body> + <input type="submit" value="Search!" /> + </form> + </body> </html> diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md index f0b54beac..82ebb912a 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md @@ -2,17 +2,18 @@ importance: 4 --- -# Search for elements +# Søg efter elementer -Here's the document with the table and form. +Her er dokumentet med tabellen og formularen. -How to find?... +Hvordan finder du ...? -1. The table with `id="age-table"`. -2. All `label` elements inside that table (there should be 3 of them). -3. The first `td` in that table (with the word "Age"). -4. The `form` with `name="search"`. -5. The first `input` in that form. -6. The last `input` in that form. +1. Tabellen med `id="age-table"`. +2. Alle `label` elementer inde i den tabel (der burde være 3 af dem). +3. Det første `td` i den tabel (med ordet "Alder"). +4. Den første `form` med `name="search"`. +5. Det første `input` i den form. +6. Det sidste `input` i den form. + +Åben siden [table.html](table.html) i et separat vindue og brug browserværktøjerne til det. -Open the page [table.html](table.html) in a separate window and make use of browser tools for that. diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index 405129694..b19ac3479 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -1,14 +1,14 @@ -# Searching: getElement*, querySelector* +# Søgning: getElement*, querySelector* -DOM navigation properties are great when elements are close to each other. What if they are not? How to get an arbitrary element of the page? +Egenskaberne til DOM navigation properties er gode når elementerne er tætte på hinanden. Hvad nu hvis de ikke er det? Hvordan får man adgang til et vilkårligt element på siden? -There are additional searching methods for that. +Der er heldigvis flere søgemetoder til rådighed. ## document.getElementById or just id -If an element has the `id` attribute, we can get the element using the method `document.getElementById(id)`, no matter where it is. +Hvis et element har `id` attributten, kan vi få adgang til elementet ved hjælp af metoden `document.getElementById(id)`, uanset hvor det er. -For instance: +For eksempel: ```html run <div id="elem"> @@ -16,17 +16,17 @@ For instance: </div> <script> - // get the element + // hent elementet *!* let elem = document.getElementById('elem'); */!* - // make its background red + // gør baggrunden rød elem.style.background = 'red'; </script> ``` -Also, there's a global variable named by `id` that references the element: +Der er også en global variabel kaldet `id`der refererer til elementet: ```html run <div id="*!*elem*/!*"> @@ -34,60 +34,60 @@ Also, there's a global variable named by `id` that references the element: </div> <script> - // elem is a reference to DOM-element with id="elem" + // elem er en reference til DOM-elementet med id="elem" elem.style.background = 'red'; - // id="elem-content" has a hyphen inside, so it can't be a variable name - // ...but we can access it using square brackets: window['elem-content'] + // id="elem-content" har en bindestreg i navnet, så det ikke kan være et variabelnavn + // ...men vi kan tilgå det ved hjælp af firkantede parenteser: window['elem-content'] </script> ``` -...That's unless we declare a JavaScript variable with the same name, then it takes precedence: +...Det sker med mindre at du deklarerer en JavaScript variabel med det samme navn - så vil den tage præcedens. ```html run untrusted height=0 <div id="elem"></div> <script> - let elem = 5; // now elem is 5, not a reference to <div id="elem"> + let elem = 5; // elem er nu 5, ikke en reference til <div id="elem"> alert(elem); // 5 </script> ``` -```warn header="Please don't use id-named global variables to access elements" -This behavior is described [in the specification](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), but it is supported mainly for compatibility. +```warn header="Du skal helst ikke bruge id-navngivede globale variable til at tilgå elememnter" +Denne adfærd er beskrevet [i specifikationen](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), men det er understøttet hovedsageligt af kompetibilitet. -The browser tries to help us by mixing namespaces of JS and DOM. That's fine for simple scripts, inlined into HTML, but generally isn't a good thing. There may be naming conflicts. Also, when one reads JS code and doesn't have HTML in view, it's not obvious where the variable comes from. +Browseren forsøger at hjælpe os ved at blande namespaces for JS og DOM. Det er i orden for simple scripts, indlejret i HTML, men generelt er det ikke en god idé. Der kan være navnekonflikter. Desuden, når man læser JS-kode og ikke har HTML i samme visning, er det ikke helt klart hvor variablen kommer fra. -Here in the tutorial we use `id` to directly reference an element for brevity, when it's obvious where the element comes from. +Her i tutorialen bruger vi `id` til at referere direkte til et element for korthed, når det er klart hvor elementet kommer fra. -In real life `document.getElementById` is the preferred method. +I virkeligheden er `document.getElementById` den foretrukne metode. ``` -```smart header="The `id` must be unique" -The `id` must be unique. There can be only one element in the document with the given `id`. +```smart header="Et `id` skal være unikt" +Et `id` skal være unikt. Der kan kun være ét element i dokumentet med det givne `id`. -If there are multiple elements with the same `id`, then the behavior of methods that use it is unpredictable, e.g. `document.getElementById` may return any of such elements at random. So please stick to the rule and keep `id` unique. +Hvis der er flere elementer med det samme `id`, så er opførslen for metoder, der bruger det, uforudsigelig, f.eks. `document.getElementById` kan returnere et af sådanne elementer tilfældigt. Så husk venligst reglen og hold `id` unikt. ``` -```warn header="Only `document.getElementById`, not `anyElem.getElementById`" -The method `getElementById` can be called only on `document` object. It looks for the given `id` in the whole document. +```warn header="Kun `document.getElementById`, ikke `anyElem.getElementById`" +Metoden `getElementById` kan kun kaldes på `document` objektet. Den leder efter det givne `id` i hele dokumentet. ``` ## querySelectorAll [#querySelectorAll] -By far, the most versatile method, `elem.querySelectorAll(css)` returns all elements inside `elem` matching the given CSS selector. +Den mest fleksible metode, `elem.querySelectorAll(css)` returnerer alle elementer inden for `elem` som matcher den givne CSS-selektor. -Here we look for all `<li>` elements that are last children: +Her leder vi efter alle `<li>` elementer som er sidste børn: ```html run <ul> - <li>The</li> + <li>Denne</li> <li>test</li> </ul> <ul> - <li>has</li> - <li>passed</li> + <li>er</li> + <li>fuldført</li> </ul> <script> *!* @@ -95,44 +95,44 @@ Here we look for all `<li>` elements that are last children: */!* for (let elem of elements) { - alert(elem.innerHTML); // "test", "passed" + alert(elem.innerHTML); // "test", "fuldført" } </script> ``` -This method is indeed powerful, because any CSS selector can be used. +Denne metode er ret kraftfuld, fordi du kan bruge en hvilken som helst CSS-selektor. -```smart header="Can use pseudo-classes as well" -Pseudo-classes in the CSS selector like `:hover` and `:active` are also supported. For instance, `document.querySelectorAll(':hover')` will return the collection with elements that the pointer is over now (in nesting order: from the outermost `<html>` to the most nested one). +```smart header="Du kan også bruge pseudo-classes" +Pseudo-classes i en CSS selector såsom `:hover` og `:active` understøttes også. For eksempel vil `document.querySelectorAll(':hover')` returnere en samling der indeholder elementer, som markøren er over (i indlejret orden: fra den yderste `<html>` til den mest indlejrede). ``` ## querySelector [#querySelector] -The call to `elem.querySelector(css)` returns the first element for the given CSS selector. +Kaldet til `elem.querySelector(css)` returnere det første element med den givne CSS-selektor. -In other words, the result is the same as `elem.querySelectorAll(css)[0]`, but the latter is looking for *all* elements and picking one, while `elem.querySelector` just looks for one. So it's faster and also shorter to write. +Med andre ord er resultatet det samme som `elem.querySelectorAll(css)[0]`, men det vil kigge efte *alle* elementer og give det første, mens `elem.querySelector` kun kigger efter det første og returnerer det. Så det er både hurtigere at afvikle og skrive. ## matches -Previous methods were searching the DOM. +De forrige metoder søger i DOM'en. -The [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) does not look for anything, it merely checks if `elem` matches the given CSS-selector. It returns `true` or `false`. +Metoden [elem.matches(css)](https://dom.spec.whatwg.org/#dom-element-matches) kigger ikke efter noget, det tjekker kun om `elem` matcher den givne CSS-selector. Det returnerer `true` eller `false`. -The method comes in handy when we are iterating over elements (like in an array or something) and trying to filter out those that interest us. +Metoden kommer i brug, når vi itererer over elementer (som i en liste eller noget lignende) og forsøger at filtrere de elementer, der interesserer os. -For instance: +For eksempel, hvis vi har en liste af elementer og ønsker at finde dem, der er links til zip-arkiver: ```html run <a href="http://example.com/file.zip">...</a> <a href="http://ya.ru">...</a> <script> - // can be any collection instead of document.body.children + // kan være en hvilken som helst samling i stedet for document.body.children for (let elem of document.body.children) { *!* if (elem.matches('a[href$="zip"]')) { */!* - alert("The archive reference: " + elem.href ); + alert("Referencen til arkivet: " + elem.href ); } } </script> @@ -140,21 +140,21 @@ For instance: ## closest -*Ancestors* of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top. +*Forfædre* til et element er: forældre, forældren af forældren, etc. De forfædre sammen danner kæden af forældre fra elementet til toppen. -The method `elem.closest(css)` looks for the nearest ancestor that matches the CSS-selector. The `elem` itself is also included in the search. +Metoden `elem.closest(css)` leder efter den nærmeste forfader, der matcher CSS-selektoren. `elem` selv er også inkluderet i søgningen. -In other words, the method `closest` goes up from the element and checks each of parents. If it matches the selector, then the search stops, and the ancestor is returned. +Med andre ord går metoden `closest` op fra elementet og tjekker hver enkelt forfader. Hvis den matcher selektoren, stopper søgningen, og forfaderen returneres. Hvis ingen forfader matcher, returneres `null`. -For instance: +For eksempel: ```html run <h1>Contents</h1> <div class="contents"> <ul class="book"> - <li class="chapter">Chapter 1</li> - <li class="chapter">Chapter 2</li> + <li class="chapter">Kapitel 1</li> + <li class="chapter">Kapitel 2</li> </ul> </div> @@ -164,44 +164,45 @@ For instance: alert(chapter.closest('.book')); // UL alert(chapter.closest('.contents')); // DIV - alert(chapter.closest('h1')); // null (because h1 is not an ancestor) + alert(chapter.closest('h1')); // null (fordi h1 er ikke en forælder) +is not an ancestor) </script> ``` ## getElementsBy* -There are also other methods to look for nodes by a tag, class, etc. +Der er også andre metoder til at finde elementer - via tag, class, etc. -Today, they are mostly history, as `querySelector` is more powerful and shorter to write. +De bliver sjældent brugt i dag, da `querySelector` er mere kraftfuld og kortere at skrive. -So here we cover them mainly for completeness, while you can still find them in the old scripts. +Så her dækker vi dem hovedsageligt for komplettheden, mens du stadig kan finde dem i gamle scripts. -- `elem.getElementsByTagName(tag)` looks for elements with the given tag and returns the collection of them. The `tag` parameter can also be a star `"*"` for "any tags". -- `elem.getElementsByClassName(className)` returns elements that have the given CSS class. -- `document.getElementsByName(name)` returns elements with the given `name` attribute, document-wide. Very rarely used. +- `elem.getElementsByTagName(tag)` kigger efter elementer med den givne tag og returnerer en samling med dem. Parameteren `tag` kan også være en stjerne `"*"` for "any tags". +- `elem.getElementsByClassName(className)` returnerer elementer, der har den givne CSS-class. +- `document.getElementsByName(name)` returnerer elementer med den givne `name`-attribut, Søger hele dokumentet og er meget sjældent brugt. -For instance: +For eksempel: ```js -// get all divs in the document +// hent alle div i dokumentet let divs = document.getElementsByTagName('div'); ``` -Let's find all `input` tags inside the table: +Lad os finde alle `input` tags inde i tabellen: ```html run height=50 <table id="table"> <tr> - <td>Your age:</td> + <td>Din alder:</td> <td> <label> - <input type="radio" name="age" value="young" checked> less than 18 + <input type="radio" name="age" value="young" checked> under 18 </label> <label> - <input type="radio" name="age" value="mature"> from 18 to 50 + <input type="radio" name="age" value="mature"> mellem 18 og 50 </label> <label> - <input type="radio" name="age" value="senior"> more than 60 + <input type="radio" name="age" value="senior"> over 60 </label> </td> </tr> @@ -218,56 +219,56 @@ Let's find all `input` tags inside the table: </script> ``` -```warn header="Don't forget the `\"s\"` letter!" -Novice developers sometimes forget the letter `"s"`. That is, they try to call `getElementByTagName` instead of <code>getElement<b>s</b>ByTagName</code>. +```warn header="Glem ikke bogstavet `\"s\"`!" +Udviklere kan nogle gange glemme bogstavet `"s"`. F. eks. når de forsøger at kalde `getElementByTagName` istedet for <code>getElement<b>s</b>ByTagName</code>. -The `"s"` letter is absent in `getElementById`, because it returns a single element. But `getElementsByTagName` returns a collection of elements, so there's `"s"` inside. +Når bogstavet `"s"` udelades i `getElementById` returnerer det et enkelt element. Men `getElementsByTagName` returnerer en samling af elementer - så det lille `"s"` i metoden er vigtig. ``` -````warn header="It returns a collection, not an element!" -Another widespread novice mistake is to write: +````warn header="Det returnerer en samling, ikke et element!" +En anden udbredt fejl er at skrive: ```js -// doesn't work +// virker ikke document.getElementsByTagName('input').value = 5; ``` -That won't work, because it takes a *collection* of inputs and assigns the value to it rather than to elements inside it. +Dette vil ikke virke fordi det tager en *samling* af input og tildeler en værdi til den i stedet for elementerne inde i samlingen. -We should either iterate over the collection or get an element by its index, and then assign, like this: +Vi skal enten iterere over samlingen eller pege på et specifikt element i samlingen og tildele værdien til det, sådan her: ```js -// should work (if there's an input) +// bør virke (hvis der er et input) document.getElementsByTagName('input')[0].value = 5; ``` ```` -Looking for `.article` elements: +Her kigges efter `.article` elementer: ```html run height=50 <form name="my-form"> - <div class="article">Article</div> - <div class="long article">Long article</div> + <div class="article">Artikel</div> + <div class="long article">Lang artikel</div> </form> <script> - // find by name attribute + // find via attributten name let form = document.getElementsByName('my-form')[0]; - // find by class inside the form + // find via en class inde i formen let articles = form.getElementsByClassName('article'); - alert(articles.length); // 2, found two elements with class "article" + alert(articles.length); // 2, fandt to elementer med class "article" </script> ``` -## Live collections +## Live samlinger -All methods `"getElementsBy*"` return a *live* collection. Such collections always reflect the current state of the document and "auto-update" when it changes. +Alle metoderne `"getElementsBy*"` returnerer en *live* samling. Sådanne samlinger reflekterer altid den nuværende tilstand af dokumentet og opdateres automatisk når det ændres. -In the example below, there are two scripts. +I eksemplet herunder er der to scripts. -1. The first one creates a reference to the collection of `<div>`. As of now, its length is `1`. -2. The second scripts runs after the browser meets one more `<div>`, so its length is `2`. +1. Det første opretter en reference til en samling af `<div>`. Som det er nu er dets længde `1`. +2. Det andet script kører efter at browseren har fundet et tilføjet `<div>`, så dets længde er `2`. ```html run <div>First div</div> @@ -286,9 +287,9 @@ In the example below, there are two scripts. </script> ``` -In contrast, `querySelectorAll` returns a *static* collection. It's like a fixed array of elements. +Som kontrast til dette så returnerer `querySelectorAll` en *statisk* samling. Her er det en fikseret liste af elementer. -If we use it instead, then both scripts output `1`: +Hvis vi brugte denne metode i stedet, så ville begge scripts outputte `1`: ```html run @@ -308,18 +309,18 @@ If we use it instead, then both scripts output `1`: </script> ``` -Now we can easily see the difference. The static collection did not increase after the appearance of a new `div` in the document. +Nu bør det være tydeligt at se forskellen. Den statiske samling øgede ikke sin længde efter det nye `div` blev tilføjet i dokumentet. -## Summary +## Opsummering -There are 6 main methods to search for nodes in DOM: +Der er seks hoved-metoder til at finde elementer i DOM'en: <table> <thead> <tr> -<td>Method</td> -<td>Searches by...</td> -<td>Can call on an element?</td> +<td>Metoder</td> +<td>Søger ved ...</td> +<td>Kan kaldes på et element?</td> <td>Live?</td> </tr> </thead> @@ -363,12 +364,12 @@ There are 6 main methods to search for nodes in DOM: </tbody> </table> -By far the most used are `querySelector` and `querySelectorAll`, but `getElement(s)By*` can be sporadically helpful or found in the old scripts. +Den klart mest fleksible metode er `querySelector` og `querySelectorAll`, men `getElement(s)By*` kan fra tid til anden være nyttige eller findes i gamle scripts. -Besides that: +Derudover finde der: -- There is `elem.matches(css)` to check if `elem` matches the given CSS selector. -- There is `elem.closest(css)` to look for the nearest ancestor that matches the given CSS-selector. The `elem` itself is also checked. +- metoden `elem.matches(css)` der tjekker om `elem` matcher det givne CSS-selector. +- metoden `elem.closest(css)` der kigger efter den nærmeste forælder, der matcher det givne CSS-selector. Selve `elem` er også med i søgningen. -And let's mention one more method here to check for the child-parent relationship, as it's sometimes useful: -- `elemA.contains(elemB)` returns true if `elemB` is inside `elemA` (a descendant of `elemA`) or when `elemA==elemB`. +Og lad os nævne en yderligere metode her til at tjekke for barn-forælder-forhold, da det kan være nyttigt: +- `elemA.contains(elemB)` returnerer true hvis `elemB` er inde i `elemA` (en efterkommer af `elemA`) eller når `elemA==elemB`. From 98880fd9bbffc65bf45e612bb9bd49bcc4456ce4 Mon Sep 17 00:00:00 2001 From: ockley <vestergaard.karsten@gmail.com> Date: Mon, 4 May 2026 09:53:02 +0200 Subject: [PATCH 31/33] Oversat til dansk --- .../2-lastchild-nodetype-inline/solution.md | 6 +- .../2-lastchild-nodetype-inline/task.md | 4 +- .../2-tree-info/solution.md | 10 +- .../2-tree-info/solution.view/index.html | 106 ++--- .../2-tree-info/source.view/index.html | 92 +++-- .../2-tree-info/task.md | 10 +- .../3-tag-in-comment/solution.md | 10 +- .../3-tag-in-comment/task.md | 6 +- .../4-where-document-in-hierarchy/solution.md | 22 +- .../4-where-document-in-hierarchy/task.md | 8 +- .../05-basic-dom-node-properties/article.md | 386 +++++++++--------- .../dom-class-hierarchy.svg | 123 +++++- 12 files changed, 456 insertions(+), 327 deletions(-) diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md index 52c34640a..219a0ca85 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md @@ -1,8 +1,8 @@ -There's a catch here. +Der er en lille hage er. -At the time of `<script>` execution the last DOM node is exactly `<script>`, because the browser did not process the rest of the page yet. +På det tidspunkt hvor `<script>` eksekveres er DOM noden præcis `<script>`, fordi browseren ikke har processeret resten af siden endnu. -So the result is `1` (element node). +Så resultatet er `1` (element node). ```html run height=60 <html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md index 0ed407cae..00b2c62c8 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# What's in the nodeType? +# Hvad er der i nodeType? -What does the script show? +Hvad viser scriptet nedenfor? ```html <html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md index 0088882c2..4f7a70e95 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md @@ -1,4 +1,4 @@ -Let's make a loop over `<li>`: +Lad os lave en løkke over `<li>`: ```js for (let li of document.querySelectorAll('li')) { @@ -6,16 +6,16 @@ for (let li of document.querySelectorAll('li')) { } ``` -In the loop we need to get the text inside every `li`. +I løkken skal vi hente teksten inde i hvert `li`. -We can read the text from the first child node of `li`, that is the text node: +Vi kan læse teksten fra det første barn af `li`, som er tekst node: ```js for (let li of document.querySelectorAll('li')) { let title = li.firstChild.data; - // title is the text in <li> before any other nodes + // title er teksten inde i <li> før andre noder } ``` -Then we can get the number of descendants as `li.getElementsByTagName('li').length`. +Derefter kan vi hente nummeret af efterkommere med `li.getElementsByTagName('li').length`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html index 5947ec097..9026f4819 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html @@ -1,57 +1,61 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> + <body> + <ul> + <li> + Dyr + <ul> + <li> + Pattedyr + <ul> + <li>Køer</li> + <li>Aber</li> + <li>Hunde</li> + <li>Tigre</li> + </ul> + </li> + <li> + Andet + <ul> + <li>Slanger</li> + <li>Fugle</li> + <li>Øgler</li> + </ul> + </li> + </ul> + </li> + <li> + Fisk + <ul> + <li> + Akvarium + <ul> + <li>Guppy</li> + <li>Skalare</li> + </ul> + </li> + <li> + Havet + <ul> + <li>Havørred</li> + </ul> + </li> + </ul> + </li> + </ul> - <ul> - <li>Animals - <ul> - <li>Mammals - <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> - </ul> - </li> - <li>Other - <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> - </ul> - </li> - </ul> - </li> - <li>Fishes - <ul> - <li>Aquarium - <ul> - <li>Guppy</li> - <li>Angelfish</li> - </ul> - </li> - <li>Sea - <ul> - <li>Sea trout</li> - </ul> - </li> - </ul> - </li> - </ul> + <script> + for (let li of document.querySelectorAll('li')) { + // hent titlen fra tekstnoden + let title = li.firstChild.data; - <script> - for (let li of document.querySelectorAll('li')) { - // get the title from the text node - let title = li.firstChild.data; + title = title.trim(); // fjern ekstra mellemrum rundt om teksten - title = title.trim(); // remove extra spaces from ends + // få antallet af efterkommere + let count = li.getElementsByTagName('li').length; - // get the descendants count - let count = li.getElementsByTagName('li').length; - - alert(title + ': ' + count); - } - </script> - -</body> + alert(title + ': ' + count); + } + </script> + </body> </html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html index fbfacaa88..26ea8a0dd 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html @@ -1,47 +1,51 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> + <body> + <ul> + <li> + Dyr + <ul> + <li> + Pattedyr + <ul> + <li>Køer</li> + <li>Aber</li> + <li>Hunde</li> + <li>Tigre</li> + </ul> + </li> + <li> + Andet + <ul> + <li>Slanger</li> + <li>Fugle</li> + <li>Øgler</li> + </ul> + </li> + </ul> + </li> + <li> + Fisk + <ul> + <li> + Akvarium + <ul> + <li>Guppy</li> + <li>Skalare</li> + </ul> + </li> + <li> + Havet + <ul> + <li>Havørred</li> + </ul> + </li> + </ul> + </li> + </ul> - <ul> - <li>Animals - <ul> - <li>Mammals - <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> - </ul> - </li> - <li>Other - <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> - </ul> - </li> - </ul> - </li> - <li>Fishes - <ul> - <li>Aquarium - <ul> - <li>Guppy</li> - <li>Angelfish</li> - </ul> - </li> - <li>Sea - <ul> - <li>Sea trout</li> - </ul> - </li> - </ul> - </li> - </ul> - - <script> - // ... your code... - </script> - -</body> + <script> + // ... din kode ... + </script> + </body> </html> diff --git a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md index f2d9edc67..cce6876e6 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Count descendants +# Tæl alle efterkommere -There's a tree structured as nested `ul/li`. +Der er en træstruktur af indlejrede `ul/li`. -Write the code that for each `<li>` shows: +Skriv den kode der for hvert `<li>` viser: -1. What's the text inside it (without the subtree) -2. The number of nested `<li>` -- all descendants, including the deeply nested ones. +1. Hvad er teksten inde i det (uden undertræet) +2. Antallet af indlejrede `<li>` -- alle efterkommere, inklusiv de dybt indlejrede. [demo src="solution"] diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md index 32900a789..9eb0b4c61 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md @@ -1,4 +1,4 @@ -The answer: **`BODY`**. +Svaret er: **`BODY`**. ```html run <script> @@ -10,8 +10,8 @@ The answer: **`BODY`**. </script> ``` -What's going on step by step: +Hvad sker der trin for trin: -1. The content of `<body>` is replaced with the comment. The comment is `<!--BODY-->`, because `body.tagName == "BODY"`. As we remember, `tagName` is always uppercase in HTML. -2. The comment is now the only child node, so we get it in `body.firstChild`. -3. The `data` property of the comment is its contents (inside `<!--...-->`): `"BODY"`. +1. Indholdet af `<body>` erstattes af en kommentar. Kommentaren er `<!--BODY-->`, fordi `body.tagName == "BODY"`. Som vi husker, er `tagName` altid versaler i HTML. +2. Kommentaren er nu det eneste barn, så vi får den med `body.firstChild`. +3. `data`-egenskaben af kommentaren er dens indhold (inde i `<!--...-->`): `"BODY"`. diff --git a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md index efe50b48f..1a0d9f540 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md @@ -2,9 +2,9 @@ importance: 3 --- -# Tag in comment +# Tag i kommentarer -What does this code show? +Hvad viser denne kode? ```html <script> @@ -12,6 +12,6 @@ What does this code show? body.innerHTML = "<!--" + body.tagName + "-->"; - alert( body.firstChild.data ); // what's here? + alert( body.firstChild.data ); // hvad er der her? </script> ``` diff --git a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md index cb9456717..1bc721ffd 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md +++ b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md @@ -1,33 +1,33 @@ -We can see which class it belongs by outputting it, like: +Vi kan se, hvilken kalsse den tilhører ved at udskrive den, f. eks. sådan her: ```js run alert(document); // [object HTMLDocument] ``` -Or: +eller: ```js run alert(document.constructor.name); // HTMLDocument ``` -So, `document` is an instance of `HTMLDocument` class. +Så, `document` er en udgave af klassen `HTMLDocument`. -What's its place in the hierarchy? +Hvad er dens position i hierarkiet? -Yeah, we could browse the specification, but it would be faster to figure out manually. +Ja, vi kunne selvfølgelig gennemgå specifikationen, men det er måske hurtigere at finde ud af det manuelt. -Let's traverse the prototype chain via `__proto__`. +Lad os gennemløbe prototypekæden via `__proto__`. -As we know, methods of a class are in the `prototype` of the constructor. For instance, `HTMLDocument.prototype` has methods for documents. +Vi ved følgende: Metoderne for en klasse findes i egenskaben `prototype` hos konstruktøren. For eksempel har `HTMLDocument.prototype` metoder for dokumenter. -Also, there's a reference to the constructor function inside the `prototype`: +Derudover er der en reference til `constructor` funktionen inde i `prototype`: ```js run alert(HTMLDocument.prototype.constructor === HTMLDocument); // true ``` -To get a name of the class as a string, we can use `constructor.name`. Let's do it for the whole `document` prototype chain, till class `Node`: +For at få et navn på klassen som en streng, kan vi bruge `constructor.name`. Lad os gøre det for hele `document` prototypekæden, indtil klassen `Node`: ```js run alert(HTMLDocument.prototype.constructor.name); // HTMLDocument @@ -35,6 +35,6 @@ alert(HTMLDocument.prototype.__proto__.constructor.name); // Document alert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node ``` -That's the hierarchy. +Det er hierarkiet. -We also could examine the object using `console.dir(document)` and see these names by opening `__proto__`. The console takes them from `constructor` internally. +Vi kunne også undersøge objektet ved hjælp af `console.dir(document)` og se disse navne ved at åbne `__proto__`. Konsollen henter dem internt fra `constructor` egenskaben. diff --git a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md index de266c6ae..016a13fcd 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md +++ b/2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md @@ -2,10 +2,10 @@ importance: 4 --- -# Where's the "document" in the hierarchy? +# Hvor er "document" i hierarkiet? -Which class does the `document` belong to? +Hvilken klasse hører `document` til? -What's its place in the DOM hierarchy? +Hvad er dens position i DOM-hierarkiet? -Does it inherit from `Node` or `Element`, or maybe `HTMLElement`? +Arver den fra `Node` eller `Element`, eller måske `HTMLElement`? diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 99dde5bcd..8424f287c 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -1,81 +1,81 @@ -# Node properties: type, tag and contents +# Node egenskaber: type, tag og contents -Let's get a more in-depth look at DOM nodes. +Lad os gå lidt mere i dybden med DOM-noder. -In this chapter we'll see more into what they are and learn their most used properties. +I dette kapitel vil vi se mere på, hvad de er, og lære deres mest brugte egenskaber. -## DOM node classes +## DOM node klasser -Different DOM nodes may have different properties. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy. +Forskellige DOM-noder kan have forskellige egenskaber. For eksempel har et element svarende til et `<a>` tag, link-relaterede egenskaber, og den svarende til et `<input>` tag har input-relaterede egenskaber osv. Tekst-noder er ikke de samme som element-noder. Men der er også fælles egenskaber og metoder mellem dem alle, fordi alle klasser af DOM-noder danner en enkelt hierarki. -Each DOM node belongs to the corresponding built-in class. +Hver DOM-node tilhører den tilsvarende indbyggede klasse. -The root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by [Node](https://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it. +Roden af hierarkiet er [EventTarget](https://dom.spec.whatwg.org/#eventtarget), som er nedarvet fra [Node](https://dom.spec.whatwg.org/#interface-node), og andre DOM-noder nedarver fra den. -Here's the picture, explanations to follow: +Her er først en oversigt over klasserne, og derefter vil vi se på dem i detaljer: ![](dom-class-hierarchy.svg) -The classes are: +Klasserne er: -- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root "abstract" class for everything. +- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- er roden. En "abstract" klasse for alt andet. - Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called "events", we'll study them later. + Objekter af denne klasse bliver aldrig oprettet. Den fungerer som en base så alle DOM-noder understøtter hændelser (såkaldte "events"), som vi vil studere senere. -- [Node](https://dom.spec.whatwg.org/#interface-node) -- is also an "abstract" class, serving as a base for DOM nodes. +- [Node](https://dom.spec.whatwg.org/#interface-node) -- er også en "abstract" klasse, der fungerer som en base for DOM-noder. - It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are other classes that inherit from it (and so inherit the `Node` functionality). + Den tilbyder den grundlæggende træ-funktionalitet: `parentNode`, `nextSibling`, `childNodes` og så videre (de er getters). Objekter af `Node`-klassen bliver aldrig oprettet. Men der er andre klasser, der nedarver fra den (og derigennem nedarver `Node`-funktionaliteten). -- [Document](https://dom.spec.whatwg.org/#interface-document), for historical reasons often inherited by `HTMLDocument` (though the latest spec doesn't dictate it) -- is a document as a whole. +- [Document](https://dom.spec.whatwg.org/#interface-document), af historiske grunde ofte nedarvet af `HTMLDocument` (selvom den nyeste specifikation ikke dikterer det) -- refererer til dokumentet som et hele. - The `document` global object belongs exactly to this class. It serves as an entry point to the DOM. + Det globale objekt `document` tilhører præcis denne klasse. Det fungerer som et indgangspunkt til DOM. -- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- an "abstract" class, inherited by: - - [Text](https://dom.spec.whatwg.org/#interface-text) -- the class corresponding to a text inside elements, e.g. `Hello` in `<p>Hello</p>`. - - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- the class for comments. They are not shown, but each comment becomes a member of DOM. +- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- en "abstract" klasse, nedarves af: + - [Text](https://dom.spec.whatwg.org/#interface-text) -- klassen der korresponderer med en tekst indeni elementer, f.eks. `Hej` i `<p>Hej</p>`. + - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- klassen for kommentarer. De vises ikke, men hver kommentar bliver et medlem af DOM. -- [Element](https://dom.spec.whatwg.org/#interface-element) -- is the base class for DOM elements. +- [Element](https://dom.spec.whatwg.org/#interface-element) -- er den grundlæggende klasse for DOM-elementer. - It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. + Den tilbyder element-niveau navigation som `nextElementSibling`, `children` og søgemetoder som `getElementsByTagName`, `querySelector`. - A browser supports not only HTML, but also XML and SVG. So the `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` (we don't need them here) and `HTMLElement`. + En browser understøtter ikke kun HTML. Den understøtter også ting som XML and SVG. Så `Element` klassen fungerer som base for mere specifikke klasser: `SVGElement`, `XMLElement` (vi bruger dem ikke i denne sammenhæng) og `HTMLElement`. -- Finally, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) is the basic class for all HTML elements. We'll work with it most of the time. +- Endelig er [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) den grundlæggende klasse for alle HTML-elementer. Vi vil arbejde med den det meste af tiden. - It is inherited by concrete HTML elements: - - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `<input>` elements, - - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `<body>` elements, - - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `<a>` elements, + Den bliver nedarvet af konkrete HTML-elementer, som har deres egne klasser, for eksempel: + - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- klassen for `<input>` elements, + - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- klassen for `<body>` elements, + - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- klassen for `<a>` elements, - ...and so on. -There are many other tags with their own classes that may have specific properties and methods, while some elements, such as `<span>`, `<section>`, `<article>` do not have any specific properties, so they are instances of `HTMLElement` class. +Der er mange andre tags med deres egne klasser, der kan have specifikke egenskaber og metoder, mens nogle elementer, såsom `<span>`, `<section>`, `<article>` ikke har nogen specifikke egenskaber, så de er instanser af `HTMLElement`-klassen. -So, the full set of properties and methods of a given node comes as the result of the chain of inheritance. +Således kommer det fulde sæt af egenskaber og metoder for en given node som resultatet af arvekæden. -For example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class. +For eksempel, lad os betragte DOM-objektet for et `<input>` element. Det tilhører [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) klassen. -It gets properties and methods as a superposition of (listed in inheritance order): +Den får sine egenskaber og metoder som en superposition af (opstillet i arveorden): -- `HTMLInputElement` -- this class provides input-specific properties, -- `HTMLElement` -- it provides common HTML element methods (and getters/setters), -- `Element` -- provides generic element methods, -- `Node` -- provides common DOM node properties, -- `EventTarget` -- gives the support for events (to be covered), -- ...and finally it inherits from `Object`, so "plain object" methods like `hasOwnProperty` are also available. +- `HTMLInputElement` -- denne klasse leverer input-specifikke egenskaber, +- `HTMLElement` -- den leverer fælles HTML-elementmetoder (og getters/setters), +- `Element` -- den leverer generiske elementmetoder, +- `Node` -- den leverer fælles DOM-nodeegenskaber, +- `EventTarget` -- den giver støtte for hændelser (til dækning), +- ...og endelig nedarver den fra `Object`, så "almene objektmetoder" som `hasOwnProperty` også er tilgængelige. -To see the DOM node class name, we can recall that an object usually has the `constructor` property. It references the class constructor, and `constructor.name` is its name: +For at se DOM-nodens klasse navn, kan vi huske på, at et objekt normalt har `constructor` egenskaben. Den refererer til klasse constructor, og `constructor.name` er dens navn. Så for `document.body` kan vi se: ```js run alert( document.body.constructor.name ); // HTMLBodyElement ``` -...Or we can just `toString` it: +... eller vi kan bare bruge `toString` på den: ```js run alert( document.body ); // [object HTMLBodyElement] ``` -We also can use `instanceof` to check the inheritance: +Vi kan også bruge `instanceof` for at tjekke nedarvning: ```js run alert( document.body instanceof HTMLBodyElement ); // true @@ -85,38 +85,38 @@ alert( document.body instanceof Node ); // true alert( document.body instanceof EventTarget ); // true ``` -As we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance. +Som vi kan se er DOM-noder regulære JavaScript-objekter. De bruger prototype-baserede klasser til arv. -That's also easy to see by outputting an element with `console.dir(elem)` in a browser. There in the console you can see `HTMLElement.prototype`, `Element.prototype` and so on. +Det kan også nemt vises ved at outputte et element med `console.dir(elem)` i en browser. Her kan du i konsollen se `HTMLElement.prototype`, `Element.prototype` og så videre. ```smart header="`console.dir(elem)` versus `console.log(elem)`" -Most browsers support two commands in their developer tools: `console.log` and `console.dir`. They output their arguments to the console. For JavaScript objects these commands usually do the same. +De fleste browsere understøtter to udviklerrværktøjer: `console.log` og `console.dir`. De outputter deres argumenter til konsollen. For JavaScript-objekter er disse kommandoer normalt ens. -But for DOM elements they are different: +Men for DOM-elementer er de forskellige: -- `console.log(elem)` shows the element DOM tree. -- `console.dir(elem)` shows the element as a DOM object, good to explore its properties. +- `console.log(elem)` viser elementets DOM-træ. +- `console.dir(elem)` viser elementet som et DOM-objekt, godt til at udforske dets egenskaber. -Try it on `document.body`. +Prøv det på `document.body`. ``` -````smart header="IDL in the spec" -In the specification, DOM classes aren't described by using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand. +````smart header="IDL i specifikationen" +I specifikationen beskrives DOM-klasser ikke ved hjælp af JavaScript, men ved hjælp af et specielt [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), som er nemmere at forstå. -In IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on. +I IDL er alle egenskaber foranstillet med deres typer. For eksempel, `DOMString`, `boolean` og så videre. -Here's an excerpt from it, with comments: +Her er et uddrag fra specifikationen med kommentarer, der forklarer IDL-syntaksen: ```js // Define HTMLInputElement *!* -// The colon ":" means that HTMLInputElement inherits from HTMLElement +// Kolon ":" betyder at HTMLInputElement nedarver fra HTMLElement */!* interface HTMLInputElement: HTMLElement { // here go properties and methods of <input> elements *!* - // "DOMString" means that the value of a property is a string + // "DOMString" betyder at værdien af en egenskab er en streng */!* attribute DOMString accept; attribute DOMString alt; @@ -124,12 +124,12 @@ interface HTMLInputElement: HTMLElement { attribute DOMString value; *!* - // boolean value property (true/false) + // boolesk værdi i egenskab (true/false) attribute boolean autofocus; */!* ... *!* - // now the method: "void" means that the method returns no value + // nNu til metoderne: "void" betyder at metoden ikke returnerer nogen værdi */!* void select(); ... @@ -137,60 +137,60 @@ interface HTMLInputElement: HTMLElement { ``` ```` -## The "nodeType" property +## Egenskaben "nodeType" -The `nodeType` property provides one more, "old-fashioned" way to get the "type" of a DOM node. +Egenskaben `nodeType` leverer en anden mere "gammeldags" måde at få datatypen af en DOM-node på. -It has a numeric value: -- `elem.nodeType == 1` for element nodes, -- `elem.nodeType == 3` for text nodes, -- `elem.nodeType == 9` for the document object, -- there are few other values in [the specification](https://dom.spec.whatwg.org/#node). +Den har en numerisk værdi: +- `elem.nodeType == 1` for elementnoder, +- `elem.nodeType == 3` for tekstnoder, +- `elem.nodeType == 9` for dokumentobjektet, +- der er et par andre værdier i [specifikationen](https://dom.spec.whatwg.org/#node). -For instance: +For eksempel: ```html run <body> <script> let elem = document.body; - // let's examine: what type of node is in elem? + // Lad os undersøge: hvilken datatype er noden elem? alert(elem.nodeType); // 1 => element - // and its first child is... + // og dens første barn er... alert(elem.firstChild.nodeType); // 3 => text - // for the document object, the type is 9 + // for selve dokumentet er typen 9 alert( document.nodeType ); // 9 </script> </body> ``` -In modern scripts, we can use `instanceof` and other class-based tests to see the node type, but sometimes `nodeType` may be simpler. We can only read `nodeType`, not change it. +I moderne scripts kan vi bruge `instanceof` og andre class-baserede tests til at se nodetype, men nogle gange kan `nodeType` være enklere. Vi kan kun læse `nodeType`, ikke ændre det. -## Tag: nodeName and tagName +## Tag: nodeName og tagName -Given a DOM node, we can read its tag name from `nodeName` or `tagName` properties: +Med en givet DOM-node kan vi læse dets tag-navn fra `nodeName` eller `tagName` egenskaber: -For instance: +For eksempel: ```js run alert( document.body.nodeName ); // BODY alert( document.body.tagName ); // BODY ``` -Is there any difference between `tagName` and `nodeName`? +Er der nogen forskel mellem `tagName` og `nodeName`? -Sure, the difference is reflected in their names, but is indeed a bit subtle. +Det er der, men forskellen er reflekteret i deres navne, og foskellen er subtil. -- The `tagName` property exists only for `Element` nodes. -- The `nodeName` is defined for any `Node`: - - for elements it means the same as `tagName`. - - for other node types (text, comment, etc.) it has a string with the node type. +- Egenskaben `tagName` eksisterer kun for `Element` noder. +- Egenskaben `nodeName` er defineret for alle noder via nedarvning fra `Node`: + - for elementer betyder det det samme som `tagName`. + - for andre typer af noder (text, comment, etc.) har det en streng med nodens type. -In other words, `tagName` is only supported by element nodes (as it originates from `Element` class), while `nodeName` can say something about other node types. +Med andre ord, `tagName` er kun understøttet af elementnoder (da det stammer fra `Element`-klassen), mens `nodeName` kan sige noget om andre nodetyper. -For instance, let's compare `tagName` and `nodeName` for the `document` and a comment node: +For eksempel, lad os sammenligne `tagName` og `nodeName` for `document` og en kommentarnode: ```html run @@ -198,220 +198,220 @@ For instance, let's compare `tagName` and `nodeName` for the `document` and a co <script> // for comment - alert( document.body.firstChild.tagName ); // undefined (not an element) + alert( document.body.firstChild.tagName ); // undefined (ikke et element) alert( document.body.firstChild.nodeName ); // #comment // for document - alert( document.tagName ); // undefined (not an element) + alert( document.tagName ); // undefined (ikke et element) alert( document.nodeName ); // #document </script> </body> ``` -If we only deal with elements, then we can use both `tagName` and `nodeName` - there's no difference. +Hvis vi kun arbejder med elementer, kan vi både bruge `tagName` og `nodeName` - der er ingen forskel. -```smart header="The tag name is always uppercase except in XML mode" -The browser has two modes of processing documents: HTML and XML. Usually the HTML-mode is used for webpages. XML-mode is enabled when the browser receives an XML-document with the header: `Content-Type: application/xml+xhtml`. +```smart header="Navnet på tag er altid i store bogstaver på nær i XML tilstand" +Browseren har to tilstande den kan processere dokumenter: HTML og XML. Normalt bruges HTML tilstanden for websider. XML tilstanden aktiveres når browseren modtager et XML dokument med headeren: `Content-Type: application/xml+xhtml`. -In HTML mode `tagName/nodeName` is always uppercased: it's `BODY` either for `<body>` or `<BoDy>`. +I HTML tilstand skrives `tagName/nodeName` altid med store bogstaver: det er `BODY` enten for `<body>` eller `<BoDy>`. -In XML mode the case is kept "as is". Nowadays XML mode is rarely used. +I XML tilstand bevares små bogstaver "som de er". I dag er XML tilstanden sjældent brugt. ``` -## innerHTML: the contents +## innerHTML: indholdet -The [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string. +Egenskaben [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) tillader at trække HTML ud af et andet element som en streng. -We can also modify it. So it's one of the most powerful ways to change the page. +Vi kan også ændre det, så det er en af de mest kraftige måder at ændre siden på. -The example shows the contents of `document.body` and then replaces it completely: +Eksemplet viser indholdet af `document.body` og erstatter det derefter helt: ```html run <body> - <p>A paragraph</p> - <div>A div</div> + <p>Et afsnit</p> + <div>En div</div> <script> - alert( document.body.innerHTML ); // read the current contents - document.body.innerHTML = 'The new BODY!'; // replace it + alert( document.body.innerHTML ); // slet det nuværende indhold + document.body.innerHTML = 'Den nye BODY!'; // erstat det </script> </body> ``` -We can try to insert invalid HTML, the browser will fix our errors: +Vi kan prøve at indsætte ugyldigt HTML, så vil browseren fikse vores fejl og indsætte det korrekt i DOM'en. For eksempel, hvis vi glemmer at lukke en tag, så vil browseren gøre det for os: ```html run <body> <script> - document.body.innerHTML = '<b>test'; // forgot to close the tag - alert( document.body.innerHTML ); // <b>test</b> (fixed) + document.body.innerHTML = '<b>test'; // glemt at lukke tag + alert( document.body.innerHTML ); // <b>test</b> (fikset) </script> </body> ``` -```smart header="Scripts don't execute" -If `innerHTML` inserts a `<script>` tag into the document -- it becomes a part of HTML, but doesn't execute. +```smart header="Scripts eksekveres ikke" +Hvis `innerHTML` indsætter et `<script>` tag i dokumentet bliver det en del af HTML, men eksekveres ikke. ``` -### Beware: "innerHTML+=" does a full overwrite +### Pas på: "innerHTML+=" overskriver fuldstændigt -We can append HTML to an element by using `elem.innerHTML+="more html"`. +Vi kan tilføje HTML til et element ved at bruge `elem.innerHTML+="mere html"`. -Like this: +Sådan her: ```js -chatDiv.innerHTML += "<div>Hello<img src='smile.gif'/> !</div>"; -chatDiv.innerHTML += "How goes?"; +chatDiv.innerHTML += "<div>Hej<img src='smile.gif'/> !</div>"; +chatDiv.innerHTML += "Hvordan går det?"; ``` -But we should be very careful about doing it, because what's going on is *not* an addition, but a full overwrite. +Men vi skal være meget forsigtige med at gøre dette. Det der foregår er nemlig ikke en tilføjelse, men en fuld overskrivning. -Technically, these two lines do the same: +Teknisk set er disse to linjer det samme: ```js elem.innerHTML += "..."; -// is a shorter way to write: +// er en kortere måde at skrive: *!* elem.innerHTML = elem.innerHTML + "..." */!* ``` -In other words, `innerHTML+=` does this: +Med andre ord gør `innerHTML+=` dette: -1. The old contents is removed. -2. The new `innerHTML` is written instead (a concatenation of the old and the new one). +1. Det gamle indhold er fjernet. +2. Det nye `innerHTML` er skrevet i stedet (en sammenkædning af det gamle og det nye). -**As the content is "zeroed-out" and rewritten from the scratch, all images and other resources will be reloaded**. +**Da indholdet er "nulstillet" og genoprettet fra bunden, vil alle billeder og andre ressourcer blive genindlæst**. -In the `chatDiv` example above the line `chatDiv.innerHTML+="How goes?"` re-creates the HTML content and reloads `smile.gif` (hope it's cached). If `chatDiv` has a lot of other text and images, then the reload becomes clearly visible. +I eksemplet med `chatDiv` ovenfor genskaber linjen `chatDiv.innerHTML+="Hvordan går det?"` indholdet og henter `smile.gif` igen (forhåbentlig er det cached). Hvis `chatDiv` har en del anden tekst og billeder bliver genindlæsningen meget tydelig. -There are other side-effects as well. For instance, if the existing text was selected with the mouse, then most browsers will remove the selection upon rewriting `innerHTML`. And if there was an `<input>` with a text entered by the visitor, then the text will be removed. And so on. +There are other side-effects as well. For instance, if the existing text was selected with the mouse, then most browsers will remove the selection upn rewriting `innerHTML`. And if there was an `<input>` with a text entered by the visitor, then the text will be removed. And so on. -Luckily, there are other ways to add HTML besides `innerHTML`, and we'll study them soon. +Heldigvis er der andre måder at tilføje HTML end `innerHTML`, som vi snart vil se nærmere på. -## outerHTML: full HTML of the element +## outerHTML: fuld HTML af et elementof the element -The `outerHTML` property contains the full HTML of the element. That's like `innerHTML` plus the element itself. +Egenskaben `outerHTML` indeholder elementets fulde HTML. Det er det samme som `innerHTML` men inklusiv elementet selv. -Here's an example: +Her er et eksempel: ```html run -<div id="elem">Hello <b>World</b></div> +<div id="elem">Hej <b>verden</b></div> <script> - alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div> + alert(elem.outerHTML); // <div id="elem">Hej <b>verden</b></div> </script> ``` -**Beware: unlike `innerHTML`, writing to `outerHTML` does not change the element. Instead, it replaces it in the DOM.** +**Pas på: Modsat `innerHTML`, ændrer `outerHTML` ikke elementet. I stedet erstatter det det i DOM'en.** -Yeah, sounds strange, and strange it is, that's why we make a separate note about it here. Take a look. +Ja, det lyder underligt, og det er det også. Derfor lige denne seperate note. Lad os se på det. -Consider the example: +Forestil dig dette eksempel: ```html run -<div>Hello, world!</div> +<div>Hej, verden!</div> <script> let div = document.querySelector('div'); *!* - // replace div.outerHTML with <p>...</p> + // erstat div.outerHTML med <p>...</p> */!* - div.outerHTML = '<p>A new element</p>'; // (*) + div.outerHTML = '<p>Et nyt element</p>'; // (*) *!* - // Wow! 'div' is still the same! + // Wow! 'div' er stadig det samme! */!* - alert(div.outerHTML); // <div>Hello, world!</div> (**) + alert(div.outerHTML); // <div>Hej, verden!</div> (**) </script> ``` -Looks really odd, right? +Det er underligt, ikke? -In the line `(*)` we replaced `div` with `<p>A new element</p>`. In the outer document (the DOM) we can see the new content instead of the `<div>`. But, as we can see in line `(**)`, the value of the old `div` variable hasn't changed! +I linjen med `(*)` erstattede vi `div` med `<p>Et nyt element</p>`. I det ydre dokument (DOM'en) kan vi se det nye indhold i stedet for den gamle `<div>`. Men, som vi kan se i linjen med `(**)` har værdien af den gamle `div` ikke ændret sig! +Tildelingen af `outerHTML` ændrer ikke selve DOM-elementet (det objekt, som variablen 'div' i dette tilfælde refererer til), men fjerner det fra DOM'en og indsætter det nye HTML i dets sted. -The `outerHTML` assignment does not modify the DOM element (the object referenced by, in this case, the variable 'div'), but removes it from the DOM and inserts the new HTML in its place. +Så, hvad der sker `div.outerHTML=...` er: +- `div` blev fjernet fra dokumentet. +- Et nyt stykke HTML `<p>Et nyt element</p>` blev indsat i dets sted. +- `div` har stadig den gamle værdi. Det nye HTML blev ikke gemt i nogen variabel. -So what happened in `div.outerHTML=...` is: -- `div` was removed from the document. -- Another piece of HTML `<p>A new element</p>` was inserted in its place. -- `div` still has its old value. The new HTML wasn't saved to any variable. +Det er så nemt at lave en fejl her: Ændr `div.outerHTML` og arbejd bagefter videre med `div` som om det indeholder det nye indhold. Men det gør det ikke. Det vil være korrekt for `innerHTML`, men ikke for `outerHTML`. -It's so easy to make an error here: modify `div.outerHTML` and then continue to work with `div` as if it had the new content in it. But it doesn't. Such thing is correct for `innerHTML`, but not for `outerHTML`. +Vi kan skrive til `elem.outerHTML`, men skal huske, at det ikke ændrer det element, vi skriver til ('elem'). Det indsætter det nye HTML i stedet. Vi kan få referencer til de nye elementer ved at forespørge på DOM'en. -We can write to `elem.outerHTML`, but should keep in mind that it doesn't change the element we're writing to ('elem'). It puts the new HTML in its place instead. We can get references to the new elements by querying the DOM. +## nodeValue/data: tekst noders indhold -## nodeValue/data: text node content +Egenskaben `innerHTML` er kun gyldig for element noder. -The `innerHTML` property is only valid for element nodes. +Andre node typer, såsom tekst noder, har deres modstykke: `nodeValue` og `data` egenskaber. Disse to er praktisk taget de næsten de samme, der er kun små specifikationsforskelle. Så vi vil bruge `data`, fordi det er kortere. -Other node types, such as text nodes, have their counterpart: `nodeValue` and `data` properties. These two are almost the same for practical use, there are only minor specification differences. So we'll use `data`, because it's shorter. - -An example of reading the content of a text node and a comment: +Her er et eksempel på læsning af indholdet fra en tekstnode og en kommentarnode: ```html run height="50" <body> - Hello - <!-- Comment --> + Hej + <!-- Kommentar --> <script> let text = document.body.firstChild; *!* - alert(text.data); // Hello + alert(text.data); // Hej */!* let comment = text.nextSibling; *!* - alert(comment.data); // Comment + alert(comment.data); // Kommentar */!* </script> </body> ``` -For text nodes we can imagine a reason to read or modify them, but why comments? +Vi kan forestille os grunde til at læse eller ændre dem, men hvorfor kommentarer? -Sometimes developers embed information or template instructions into HTML in them, like this: +Nogle gange indlejrer udviklere information eller template instruktioner til HTML brug i dem, i stil med: ```html <!-- if isAdmin --> - <div>Welcome, Admin!</div> + <div>Velkommen, administrator!</div> <!-- /if --> ``` -...Then JavaScript can read it from `data` property and process embedded instructions. +... så kan JavaScript læse det fra `data` egenskaben og behandle de indlejrede instruktioner. -## textContent: pure text +## textContent: ren tekst -The `textContent` provides access to the *text* inside the element: only text, minus all `<tags>`. +Egenskaben `textContent` leverer adgang til selve *teksten* inde i elementet: kun tekst - minus alle `<tags>`. -For instance: +For eksempel: ```html run <div id="news"> - <h1>Headline!</h1> - <p>Martians attack people!</p> + <h1>Overskrift!</h1> + <p>Marsboere angriber folk!</p> </div> <script> - // Headline! Martians attack people! + // Overskrift! + // Marsboere angriber folk! alert(news.textContent); </script> ``` -As we can see, only text is returned, as if all `<tags>` were cut out, but the text in them remained. +Som vi kan se returneres kun teksten. Det er som om alle `<tags>` var klippet ud, men teksten i dem har fået lov til at blive. -In practice, reading such text is rarely needed. +I praksis er læsning af teksten på denne måde ikke så tit brugt. -**Writing to `textContent` is much more useful, because it allows to write text the "safe way".** +**Skrivning til `textContent` er meget mere nyttig, fordi det tillader os at skrive tekst på den "sikre måde".** -Let's say we have an arbitrary string, for instance entered by a user, and want to show it. +Lad os sige, vi har en vilkårlig streng, for eksempel indtastet af en bruger, og vi vil vise den på siden. -- With `innerHTML` we'll have it inserted "as HTML", with all HTML tags. -- With `textContent` we'll have it inserted "as text", all symbols are treated literally. +- Med `innerHTML` bliver det sat ind "som HTML", med alle HTML tags. +- Med `textContent` bliver det sat ind "som tekst", og alle symboler behandles bogstaveligt. Compare the two: @@ -420,7 +420,7 @@ Compare the two: <div id="elem2"></div> <script> - let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>"); + let name = prompt("Hvad er dit navn?", "<b>Peter-Plys!</b>"); elem1.innerHTML = name; elem2.textContent = name; @@ -428,51 +428,51 @@ Compare the two: ``` 1. The first `<div>` gets the name "as HTML": all tags become tags, so we see the bold name. -2. The second `<div>` gets the name "as text", so we literally see `<b>Winnie-the-Pooh!</b>`. +2. The second `<div>` gets the name "as text", so we literally see `<b>Peter-Plys!</b>`. -In most cases, we expect the text from a user, and want to treat it as text. We don't want unexpected HTML in our site. An assignment to `textContent` does exactly that. +I de fleste tilfælde forventer vi tekst fra en bruger og vil behandle den som tekst. Vi vil ikke have uventet HTML ind på vores side. En tildeling via `textContent` sikrer præcis dette. -## The "hidden" property +## Den skjulte egenskab "hidden" -The "hidden" attribute and the DOM property specifies whether the element is visible or not. +Egenskaben `hidden` og den tilsvarende HTML-attribute specificerer om elementet er synligt eller ikke. -We can use it in HTML or assign it using JavaScript, like this: +Vi kan bruge den i HTML eller tildele den ved hjælp af JavaScript, sådan: ```html run height="80" -<div>Both divs below are hidden</div> +<div>Begge divs nedenfor er skjulte</div> -<div hidden>With the attribute "hidden"</div> +<div hidden>Med attributten "hidden"</div> -<div id="elem">JavaScript assigned the property "hidden"</div> +<div id="elem">JavaScript tildelte egenskaben "hidden"</div> <script> elem.hidden = true; </script> ``` -Technically, `hidden` works the same as `style="display:none"`. But it's shorter to write. +Teksnisk set virker `hidden` på samme måde som `style="display:none"`. men er kortere at skrive. -Here's a blinking element: +Her er et blinkende element: ```html run height=50 -<div id="elem">A blinking element</div> +<div id="elem">Et blinkende element</div> <script> setInterval(() => elem.hidden = !elem.hidden, 1000); </script> ``` -## More properties +## Flere egenskaber -DOM elements also have additional properties, in particular those that depend on the class: +DOM elementer har flere egenskaber. Særligt findes egenskaber der afhænger af klassen. For eksempel: -- `value` -- the value for `<input>`, `<select>` and `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...). -- `href` -- the "href" for `<a href="...">` (`HTMLAnchorElement`). -- `id` -- the value of "id" attribute, for all elements (`HTMLElement`). -- ...and much more... +- `value` -- værdien for tags som `<input>`, `<select>` og `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...). +- `href` -- hyperlink referencen "href" for `<a href="...">` (`HTMLAnchorElement`). +- `id` -- værdien af "id" attributten, for alle elementer (`HTMLElement`). +- ...og meget mere... -For instance: +For eksempel: ```html run height="80" <input type="text" id="elem" value="value"> @@ -484,39 +484,39 @@ For instance: </script> ``` -Most standard HTML attributes have the corresponding DOM property, and we can access it like that. +De fleste standard HTML attributter har en tilsvarende DOM egenskab og vi kan umiddelbart tilgå dem som sådan. -If we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, `HTMLInputElement` is documented at <https://html.spec.whatwg.org/#htmlinputelement>. +Hvis vi vil kende til hele listen af understøttede egenskaber kan vi finde dem i specifikationen. For eksempel er `HTMLInputElement` dokumenteret på <https://html.spec.whatwg.org/#htmlinputelement>. -Or if we'd like to get them fast or are interested in a concrete browser specification -- we can always output the element using `console.dir(elem)` and read the properties. Or explore "DOM properties" in the Elements tab of the browser developer tools. +Eller, hvis vi vil have dem hurtigt eller er interesseret i en bestemt browser specifikation -- kan vi altid outputte elementet ved hjælp af `console.dir(elem)` og læse egenskaberne. Eller udforske "DOM egenskaber" i Elements fanebladet i browserens udviklerværktøjer. -## Summary +## Opsummering -Each DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods come as the result of inheritance. +Hver DOM node tilhører en bestemt klasse. Klasserne udgør en hierarki. Den fulde sæt af egenskaber og metoder kommer som resultatet af nedarvning. -Main DOM node properties are: +Vigtige DOM node egenskaber er: `nodeType` -: We can use it to see if a node is a text or an element node. It has a numeric value: `1` for elements,`3` for text nodes, and a few others for other node types. Read-only. +: Vi kan bruge den til at se om en node er en tekst- eller elementnode. Den har en numerisk værdi: `1` for elementer,`3` for tekstnoder, og et par andre for andre nodetyper. Kun læsning. `nodeName/tagName` -: For elements, tag name (uppercased unless XML-mode). For non-element nodes `nodeName` describes what it is. Read-only. +: For elementer. Viser tag navn (skrevet med store bogstaver med mindre browseren er i XML-mode). For ikke-elementnoder `nodeName` beskriver hvad det er. Kun læsning. `innerHTML` -: The HTML content of the element. Can be modified. +: Indholdet af HTML for elementet. Kan ændres. `outerHTML` -: The full HTML of the element. A write operation into `elem.outerHTML` does not touch `elem` itself. Instead it gets replaced with the new HTML in the outer context. +: Det fulde HTML for elementet. Skrivning til `elem.outerHTML` berører ikke `elem` selv. I stedet bliver det erstattet med det nye HTML i den ydre kontekst. `nodeValue/data` -: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can be modified. +: Indholdet af en ikke-element node (tekst, kommentar). Disse to er næsten de samme, og vi bruger normalt `data`. Kan ændres. `textContent` -: The text inside the element: HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions. +: Den tekst, der er inde i elementet: HTML minus alle `<tags>`. Skrivning til `textContent` sætter teksten ind i elementet, med alle specielle tegn og tags behandlet præcis som tekst. Kan dermed sikkert indsætte tekst fra brugere og beskytte mod uønsket indsættelse af HTML. `hidden` -: When set to `true`, does the same as CSS `display:none`. +: Når sat til `true`, gør det det samme som CSS `display:none`. -DOM nodes also have other properties depending on their class. For instance, `<input>` elements (`HTMLInputElement`) support `value`, `type`, while `<a>` elements (`HTMLAnchorElement`) support `href` etc. Most standard HTML attributes have a corresponding DOM property. +DOM noder har også andre egenskaber afhængigt af deres klasse. For eksempel, `<input>` elementet (`HTMLInputElement`) understøtter `value`, `type`, mens `<a>` elementet (`HTMLAnchorElement`) understøtter `href` etc. De fleste standard HTML attributter har en tilsvarende DOM egenskab. -However, HTML attributes and DOM properties are not always the same, as we'll see in the next chapter. +Men, HTML attributter og DOM egenskaber er ikke altid de samme, som vi vil se i næste kapitel. diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg index ccd93d500..f060ff9dd 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg +++ b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg @@ -1 +1,122 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="552" height="403" viewBox="0 0 552 403"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-class-hierarchy.svg"><path id="Rectangle-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 6h118v28H181z"/><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 74h118v28H181z"/><text id="EventTarget" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="200.9" y="24">EventTarget</tspan></text><text id="Node" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="225.6" y="91">Node </tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M240.5 39.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 144h118v28H181z"/><text id="Element" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="214.8" y="161">Element </tspan></text><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M240.5 109.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 230h118v28H181z"/><text id="HTMLElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="200.4" y="247">HTMLElement </tspan></text><path id="Line-2-Copy-4" fill="#C06334" fill-rule="nonzero" d="M240.5 195.5l7 14h-6v17h-2v-17h-6l7-14zM72.5 158.5l7.273 13.86-5.999.117L74 183.98l.02 1-2 .04-.02-1-.226-11.503-5.998.118L72.5 158.5z"/><path id="Rectangle-8-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M171 300h138v28H171z"/><text id="HTMLBodyElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="186" y="317">HTMLBodyElement </tspan></text><path id="Line-2-Copy-6" fill="#C06334" fill-rule="nonzero" d="M240.5 265.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 300h138v28H1z"/><text id="HTMLInputElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="13.4" y="317">HTMLInputElement </tspan></text><path id="Line-2-Copy-7" fill="#C06334" fill-rule="nonzero" d="M159 261l-6.753 14.12-3.685-4.736-29.448 22.905-.79.614-1.227-1.578.79-.614 29.448-22.906-3.684-4.735L159 261z"/><path id="Rectangle-8-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M339 300h138v28H339z"/><text id="HTMLAnchorElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="348.3" y="317">HTMLAnchorElement </tspan></text><path id="Line-2-Copy-8" fill="#C06334" fill-rule="nonzero" d="M312 261l15.305 3.28-3.749 4.684 29.069 23.255.78.625-1.249 1.562-.78-.625-29.069-23.254-3.748 4.685L312 261z"/><path id="Rectangle-8-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M22 126h98v28H22z"/><text id="Document" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="42.2" y="144">Document </tspan></text><path id="Rectangle-8-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M22 192h98v28H22z"/><text id="HTMLDocument" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="27.8" y="210">HTMLDocument </tspan></text><path id="Line-2-Copy-2" fill="#C06334" fill-rule="nonzero" d="M168 90l-8.862 12.902-2.905-5.251-34.749 19.224-.875.484-.968-1.75.875-.484 34.749-19.224-2.904-5.25L168 90z"/><path id="Rectangle-8-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M369 126h118v28H369z"/><text id="CharacterData" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="381.2" y="144">CharacterData </tspan></text><path id="Line-2-Copy-3" fill="#C06334" fill-rule="nonzero" d="M314 91l15.648.379-2.813 5.299 36.634 19.439.883.468-.937 1.767-.884-.469-36.633-19.439-2.812 5.301L314 91zM458 159l13.93 7.138-4.836 3.552 9.712 13.218.592.806-1.612 1.184-.592-.806-9.712-13.218-4.834 3.553L458 159zM400 158l-1.376 15.592-5.11-3.146-8.662 14.078-.524.852-1.704-1.048.524-.852 8.663-14.078-5.11-3.143L400 158z"/><text id="Document-as-a-whole" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="6.6" y="235">Document as a whole</tspan></text><text id="<input-type="…">" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="13.4" y="341"><input type="…"></tspan></text><text id="<body>" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="220.4" y="341"><body></tspan></text><text id="<a-href="…">" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="365.8" y="341"><a href="…"></tspan></text><text id="<div>...</div>" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="192.6" y="188" fill="#AF6E24"><</tspan> <tspan x="199.8" y="188" fill="#C06334">div</tspan> <tspan x="221.4" y="188" fill="#AF6E24">></tspan> <tspan x="228.6" y="188" fill="#DBAF88">...</tspan> <tspan x="250.2" y="188" fill="#AF6E24"></</tspan> <tspan x="264.6" y="188" fill="#C06334">div</tspan> <tspan x="286.2" y="188" fill="#AF6E24">></tspan></text><path id="Rectangle-8-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M449 192h78v28h-78z"/><text id="Comment" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="463.8" y="210">Comment </tspan></text><text id="<!--comment-->" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="442.6" y="237"><!--comment--></tspan></text><path id="Rectangle-8-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M342 192h78v28h-78z"/><text id="Text" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="365.6" y="210">Text </tspan></text><text id=""Hello"" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="356.8" y="237">"Hello"</tspan></text></g></g></svg> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 552 403"> + <!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) --> + <defs> + <style> + .st0, .st1, .st2 { + font-family: PTMono-Regular, 'PT Mono'; + font-size: 12px; + } + + .st0, .st1, .st2, .st3 { + isolation: isolate; + } + + .st0, .st4 { + fill: #c06334; + } + + .st1 { + fill: #dbaf88; + } + + .st5 { + fill: #fbf2ec; + fill-rule: evenodd; + stroke: #dbaf88; + stroke-width: 2px; + } + + .st2 { + fill: #af6e24; + } + </style> + </defs> + <g id="dom"> + <g id="dom-class-hierarchy.svg"> + <path id="Rectangle-9" class="st5" d="M181,6h118v28h-118V6Z"/> + <path id="Rectangle-8" class="st5" d="M181,74h118v28h-118v-28Z"/> + <g id="EventTarget" class="st3"> + <text class="st2" transform="translate(200.9 24)"><tspan x="0" y="0">EventTarget</tspan></text> + </g> + <g id="Node" class="st3"> + <text class="st2" transform="translate(225.6 91)"><tspan x="0" y="0">Node</tspan></text> + </g> + <path id="Line-2" class="st4" d="M240.5,39.5l7,14h-6v17h-2v-17h-6l7-14Z"/> + <path id="Rectangle-8-Copy" class="st5" d="M181,144h118v28h-118v-28Z"/> + <g id="Element" class="st3"> + <text class="st2" transform="translate(214.8 161)"><tspan x="0" y="0">Element</tspan></text> + </g> + <path id="Line-2-Copy" class="st4" d="M240.5,109.5l7,14h-6v17h-2v-17h-6l7-14Z"/> + <path id="Rectangle-8-Copy-4" class="st5" d="M181,230h118v28h-118v-28Z"/> + <g id="HTMLElement" class="st3"> + <text class="st2" transform="translate(200.4 247)"><tspan x="0" y="0">HTMLElement</tspan></text> + </g> + <path id="Line-2-Copy-4" class="st4" d="M240.5,195.5l7,14h-6v17h-2v-17h-6l7-14ZM72.5,158.5l7.27,13.86-6,.12.23,11.5.02,1-2,.04-.02-1-.23-11.5-6,.12,6.72-14.13Z"/> + <path id="Rectangle-8-Copy-6" class="st5" d="M171,300h138v28h-138v-28Z"/> + <g id="HTMLBodyElement" class="st3"> + <text class="st2" transform="translate(186 317)"><tspan x="0" y="0">HTMLBodyElement</tspan></text> + </g> + <path id="Line-2-Copy-6" class="st4" d="M240.5,265.5l7,14h-6v17h-2v-17h-6l7-14Z"/> + <path id="Rectangle-8-Copy-7" class="st5" d="M1,300h138v28H1v-28Z"/> + <g id="HTMLInputElement" class="st3"> + <text class="st2" transform="translate(13.4 317)"><tspan x="0" y="0">HTMLInputElement</tspan></text> + </g> + <path id="Line-2-Copy-7" class="st4" d="M159,261l-6.75,14.12-3.68-4.74-29.45,22.9-.79.61-1.23-1.58.79-.61,29.45-22.91-3.68-4.73,15.35-3.07Z"/> + <path id="Rectangle-8-Copy-8" class="st5" d="M339,300h138v28h-138v-28Z"/> + <g id="HTMLAnchorElement" class="st3"> + <text class="st2" transform="translate(348.3 317)"><tspan x="0" y="0">HTMLAnchorElement</tspan></text> + </g> + <path id="Line-2-Copy-8" class="st4" d="M312,261l15.3,3.28-3.75,4.68,29.07,23.26.78.62-1.25,1.56-.78-.62-29.07-23.25-3.75,4.68-6.56-14.21Z"/> + <path id="Rectangle-8-Copy-2" class="st5" d="M22,126h98v28H22v-28Z"/> + <g id="Document" class="st3"> + <text class="st2" transform="translate(42.2 144)"><tspan x="0" y="0">Document</tspan></text> + </g> + <path id="Rectangle-8-Copy-21" data-name="Rectangle-8-Copy-2" class="st5" d="M22,192h98v28H22v-28Z"/> + <g id="HTMLDocument" class="st3"> + <text class="st2" transform="translate(27.8 210)"><tspan x="0" y="0">HTMLDocument</tspan></text> + </g> + <path id="Line-2-Copy-2" class="st4" d="M168,90l-8.86,12.9-2.9-5.25-34.75,19.22-.88.48-.97-1.75.88-.48,34.75-19.22-2.9-5.25,15.64-.65Z"/> + <path id="Rectangle-8-Copy-3" class="st5" d="M369,126h118v28h-118v-28Z"/> + <g id="CharacterData" class="st3"> + <text class="st2" transform="translate(381.2 144)"><tspan x="0" y="0">CharacterData</tspan></text> + </g> + <path id="Line-2-Copy-3" class="st4" d="M314,91l15.65.38-2.81,5.3,36.63,19.44.88.47-.94,1.77-.88-.47-36.63-19.44-2.81,5.3-9.09-12.74ZM458,159l13.93,7.14-4.84,3.55,9.71,13.22.59.81-1.61,1.18-.59-.81-9.71-13.22-4.83,3.55-2.65-15.43ZM400,158l-1.38,15.59-5.11-3.15-8.66,14.08-.52.85-1.7-1.05.52-.85,8.66-14.08-5.11-3.14,13.3-8.25Z"/> + <g id="Document-as-a-whole" class="st3"> + <text class="st0" transform="translate(6.6 235)"><tspan x="0" y="0">Dokumentet som helhed</tspan></text> + </g> + <g id="_x3C_input-type_x3D__x22__x2026__x22__x3E_" class="st3"> + <text class="st0" transform="translate(13.4 341)"><tspan x="0" y="0"><input type="…"></tspan></text> + </g> + <g id="_x3C_body_x3E_" class="st3"> + <text class="st0" transform="translate(220.4 341)"><tspan x="0" y="0"><body></tspan></text> + </g> + <g id="_x3C_a-href_x3D__x22__x2026__x22__x3E_" class="st3"> + <text class="st0" transform="translate(365.8 341)"><tspan x="0" y="0"><a href="…"></tspan></text> + </g> + <g id="_x3C_div_x3E_..._x3C__x2F_div_x3E_" class="st3"> + <text class="st2" transform="translate(192.6 188)"><tspan x="0" y="0"><</tspan></text> + <text class="st0" transform="translate(199.8 188)"><tspan x="0" y="0">div</tspan></text> + <text class="st2" transform="translate(221.4 188)"><tspan x="0" y="0">></tspan></text> + <text class="st1" transform="translate(228.6 188)"><tspan x="0" y="0">...</tspan></text> + <text class="st2" transform="translate(250.2 188)"><tspan x="0" y="0"></</tspan></text> + <text class="st0" transform="translate(264.6 188)"><tspan x="0" y="0">div</tspan></text> + <text class="st2" transform="translate(286.2 188)"><tspan x="0" y="0">></tspan></text> + </g> + <path id="Rectangle-8-Copy-31" data-name="Rectangle-8-Copy-3" class="st5" d="M449,192h78v28h-78v-28Z"/> + <g id="Comment" class="st3"> + <text class="st2" transform="translate(463.8 210)"><tspan x="0" y="0">Comment</tspan></text> + </g> + <g id="_x3C__x21_--comment--_x3E_" class="st3"> + <text class="st0" transform="translate(442.6 237)"><tspan x="0" y="0"><!--comment--></tspan></text> + </g> + <path id="Rectangle-8-Copy-32" data-name="Rectangle-8-Copy-3" class="st5" d="M342,192h78v28h-78v-28Z"/> + <g id="Text" class="st3"> + <text class="st2" transform="translate(365.6 210)"><tspan x="0" y="0">Text</tspan></text> + </g> + <g id="_x22_Hello_x22_" class="st3"> + <text class="st0" transform="translate(356.8 237)"><tspan x="0" y="0">"Hej"</tspan></text> + </g> + </g> + </g> +</svg> \ No newline at end of file From f7f540ea1c134b7ca309900a9a4b2e5a68d366dc Mon Sep 17 00:00:00 2001 From: ockley <vestergaard.karsten@gmail.com> Date: Mon, 4 May 2026 11:45:45 +0200 Subject: [PATCH 32/33] Oversat til dansk --- .../1-get-user-attribute/solution.md | 8 +- .../1-get-user-attribute/task.md | 8 +- .../2-yellow-links/solution.md | 20 +- .../2-yellow-links/solution.view/index.html | 2 +- .../2-yellow-links/source.view/index.html | 4 +- .../2-yellow-links/task.md | 16 +- .../article.md | 262 +++++++++--------- 7 files changed, 160 insertions(+), 160 deletions(-) diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md index 0507832f3..db03368ab 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md @@ -4,15 +4,15 @@ <html> <body> - <div data-widget-name="menu">Choose the genre</div> + <div data-widget-name="menu">Vælg en genre</div> <script> - // getting it + // hent elementet let elem = document.querySelector('[data-widget-name]'); - // reading the value + // læs værdien alert(elem.dataset.widgetName); - // or + // eller alert(elem.getAttribute('data-widget-name')); </script> </body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md index 4cdf231b0..c4ae970d9 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Get the attribute +# Hent attributten -Write the code to select the element with `data-widget-name` attribute from the document and to read its value. +Skriv en kode der vælger elementet med attributten `data-widget-name` fra dokumentet og læser dens værdi. ```html run <!DOCTYPE html> <html> <body> - <div data-widget-name="menu">Choose the genre</div> + <div data-widget-name="menu">Vælg en genre</div> <script> - /* your code */ + /* din kode */ </script> </body> </html> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md index 726be4c8f..e114d4854 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md @@ -1,9 +1,9 @@ -First, we need to find all external references. +Først skal vi finde alle eksterne referencer. -There are two ways. +Der er to måder. -The first is to find all links using `document.querySelectorAll('a')` and then filter out what we need: +Den første er at finde alle links ved hjælp af `document.querySelectorAll('a')` og derefter filtrere ud, hvad vi har brug for: ```js let links = document.querySelectorAll('a'); @@ -12,23 +12,23 @@ for (let link of links) { *!* let href = link.getAttribute('href'); */!* - if (!href) continue; // no attribute + if (!href) continue; // ingen attribut - if (!href.includes('://')) continue; // no protocol + if (!href.includes('://')) continue; // ingen protokol - if (href.startsWith('http://internal.com')) continue; // internal + if (href.startsWith('http://internal.com')) continue; // intern link.style.color = 'orange'; } ``` -Please note: we use `link.getAttribute('href')`. Not `link.href`, because we need the value from HTML. +Bemærk: vi bruger `link.getAttribute('href')`. ikke `link.href` fordi vi har brug for værdien fra HTML. -...Another, simpler way would be to add the checks to CSS selector: +... en anden, simplere måde vil være at tilføje vores tjek til CSS-vælgeren: ```js -// look for all links that have :// in href -// but href doesn't start with http://internal.com +// kig efter alle links der har :// i href +// men hvor href ikke starter med http://internal.com let selector = 'a[href*="://"]:not([href^="http://internal.com"])'; let links = document.querySelectorAll(selector); diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html index 4209a5f34..7fd2f7593 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html @@ -2,7 +2,7 @@ <html> <body> - <a name="list">The list:</a> + <a name="list">Listen:</a> <ul> <li><a href="http://google.com">http://google.com</a></li> <li><a href="/tutorial">/tutorial.html</a></li> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html index e12048323..80caebbb7 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html @@ -2,7 +2,7 @@ <html> <body> - <a name="list">The list:</a> + <a name="list">Listen:</a> <ul> <li><a href="http://google.com">http://google.com</a></li> <li><a href="/tutorial">/tutorial.html</a></li> @@ -13,7 +13,7 @@ </ul> <script> - // ...your code... + // ...din kode... </script> </body> diff --git a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md index b0a8ab7b1..0ff1551c8 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md @@ -2,15 +2,15 @@ importance: 3 --- -# Make external links orange +# Gør eksterne links orange -Make all external links orange by altering their `style` property. +Gør alle eksterne links orange ved at ændre deres `style`-egenskab. -A link is external if: -- Its `href` has `://` in it -- But doesn't start with `http://internal.com`. +Et link er eksternt hvis: +- Dets `href` har `://` i sig, og +- Det ikke starter med `http://internal.com`. -Example: +Eksempel: ```html run <a name="list">the list</a> @@ -24,12 +24,12 @@ Example: </ul> <script> - // setting style for a single link + // set style for et enkelt link let link = document.querySelector('a'); link.style.color = 'orange'; </script> ``` -The result should be: +Resultatet skal være som her: [iframe border=1 height=180 src="solution"] diff --git a/2-ui/1-document/06-dom-attributes-and-properties/article.md b/2-ui/1-document/06-dom-attributes-and-properties/article.md index b02f626dc..54520ecf4 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/article.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/article.md @@ -1,150 +1,150 @@ -# Attributes and properties +# Attributter og egenskaber -When the browser loads the page, it "reads" (another word: "parses") the HTML and generates DOM objects from it. For element nodes, most standard HTML attributes automatically become properties of DOM objects. +Når en browser henter en side, læser den (et andet ord: "parser") HTML'en og genererer DOM-objekter ud fra dem. For elementnoder bliver de fleste standard HTML-attributter automatisk til egenskaber for DOM-objekterne. -For instance, if the tag is `<body id="page">`, then the DOM object has `body.id="page"`. +For eksempel, hvis tagget er `<body id="page">`, så har DOM-objektet `body.id="page"`. -But the attribute-property mapping is not one-to-one! In this chapter we'll pay attention to separate these two notions, to see how to work with them, when they are the same, and when they are different. +Men forholdet mellem attributter og egenskaber er ikke en-til-en! I dette kapitel vil vi fokusere på at adskille disse to koncepter, for at se, hvordan man arbejder med dem, når de er de samme, og når de er forskellige. -## DOM properties +## DOM egenskaber -We've already seen built-in DOM properties. There are a lot. But technically no one limits us, and if there aren't enough, we can add our own. +Vi har allerede set det indbyggede DOM egenskaber - der er mange. Men teknisk set er der ingen begrænsninger for os. Hvis der ikke er nok, kan vi oprette vores egne. -DOM nodes are regular JavaScript objects. We can alter them. +DOM noder er regulære JavaScript objekter. Vi kan ændre dem. -For instance, let's create a new property in `document.body`: +For eksempel, lad os oprette en ny egenskab i `document.body`: ```js run document.body.myData = { - name: 'Caesar', - title: 'Imperator' + name: 'Cæsar', + title: 'Kejser' }; -alert(document.body.myData.title); // Imperator +alert(document.body.myData.title); // Kejser ``` -We can add a method as well: +Vi kan også tilføje en metode: ```js run document.body.sayTagName = function() { alert(this.tagName); }; -document.body.sayTagName(); // BODY (the value of "this" in the method is document.body) +document.body.sayTagName(); // BODY (værdien af "this" i metoden er document.body) ``` -We can also modify built-in prototypes like `Element.prototype` and add new methods to all elements: +Vi kan også modificere indbyggede prototyper som `Element.prototype` og tilføje nye metoder til alle elementer: ```js run Element.prototype.sayHi = function() { - alert(`Hello, I'm ${this.tagName}`); + alert(`Hej, Jeg er ${this.tagName}!`); }; -document.documentElement.sayHi(); // Hello, I'm HTML -document.body.sayHi(); // Hello, I'm BODY +document.documentElement.sayHi(); // Hej, Jeg er HTML! +document.body.sayHi(); // Hej, Jeg er BODY! ``` -So, DOM properties and methods behave just like those of regular JavaScript objects: +Så, DOM egenskaber og metoder er de samme som regulære JavaScript objekter. -- They can have any value. -- They are case-sensitive (write `elem.nodeType`, not `elem.NoDeTyPe`). +- De kan have hvilken som helst værdi. +- De er case-sensitive (på dansk versalfølsomme, men det er der ingen der siger) (skriv `elem.nodeType`, ikke `elem.NoDeTyPe`). -## HTML attributes +## HTML attributter -In HTML, tags may have attributes. When the browser parses the HTML to create DOM objects for tags, it recognizes *standard* attributes and creates DOM properties from them. +I HTML kan tags have attributter. Når browseren parser HTML'en for at oprette DOM-objekter for tags, genkender den *standard* attributter og opretter DOM-egenskaber ud fra dem. -So when an element has `id` or another *standard* attribute, the corresponding property gets created. But that doesn't happen if the attribute is non-standard. +Så når et element har `id` eller en anden *standard* attribut, oprettes den tilsvarende egenskab. Men det sker ikke, hvis attributten *ikke er standard*. -For instance: +For eksempel: ```html run -<body id="test" something="non-standard"> +<body id="test" something="ikke-standard"> <script> alert(document.body.id); // test *!* - // non-standard attribute does not yield a property + // ikke-standard attribut giver ikke en egenskab alert(document.body.something); // undefined */!* </script> </body> ``` -Please note that a standard attribute for one element can be unknown for another one. For instance, `"type"` is standard for `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), but not for `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Standard attributes are described in the specification for the corresponding element class. +Bemærk at en standard attribut for et element kan være ukendt for et andet element. For eksempel er `"type"` standard for `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), men ikke for `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Standard attributter er beskrevet i specifikationen for den tilsvarende elementklasse. -Here we can see it: +Det kan vi se her: ```html run <body id="body" type="..."> <input id="input" type="text"> <script> alert(input.type); // text *!* - alert(body.type); // undefined: DOM property not created, because it's non-standard + alert(body.type); // undefined: DOM egenskaben oprettes ikke, fordi den ikke er standard for elementet */!* </script> </body> ``` -So, if an attribute is non-standard, there won't be a DOM-property for it. Is there a way to access such attributes? +Så, hvis en attribut ikke er standard for elementet oprettes der ikke en DOM egenskab. Men, er der så en måde at tilgå sådanne attributter? -Sure. All attributes are accessible by using the following methods: +Det er der! Alle attributter er tilgængelige ved hjælp af følgende metoder: -- `elem.hasAttribute(name)` -- checks for existence. -- `elem.getAttribute(name)` -- gets the value. -- `elem.setAttribute(name, value)` -- sets the value. -- `elem.removeAttribute(name)` -- removes the attribute. +- `elem.hasAttribute(name)` -- tjekker for eksistens. +- `elem.getAttribute(name)` -- henter værdien. +- `elem.setAttribute(name, value)` -- sætter værdien. +- `elem.removeAttribute(name)` -- fjerner attributten. -These methods operate exactly with what's written in HTML. +Disse metoder arbejder præcis med det, der er skrevet i HTML. -Also one can read all attributes using `elem.attributes`: a collection of objects that belong to a built-in [Attr](https://dom.spec.whatwg.org/#attr) class, with `name` and `value` properties. +Man kan også læse alle attributter ved hjælp af `elem.attributes`: en samling af objekter, der tilhører en indbygget [Attr](https://dom.spec.whatwg.org/#attr) klasse, med `name` og `value` egenskaber. -Here's a demo of reading a non-standard property: +Her er en demonstration af læsning af en ikke-standard egenskab: ```html run -<body something="non-standard"> +<body something="ikke-standard"> <script> *!* - alert(document.body.getAttribute('something')); // non-standard + alert(document.body.getAttribute('something')); // ikke-standard */!* </script> </body> ``` -HTML attributes have the following features: +HTML attributter har følgende egenskaber: -- Their name is case-insensitive (`id` is same as `ID`). -- Their values are always strings. +- Deres navne er ikke case-sensitive (`id` er set samme som `ID`). +- Deres værdi er altid strenge. -Here's an extended demo of working with attributes: +Her er en udvidet demonstration af arbejde med attributter: ```html run <body> - <div id="elem" about="Elephant"></div> + <div id="elem" about="Elefant"></div> <script> - alert( elem.getAttribute('About') ); // (1) 'Elephant', reading + alert( elem.getAttribute('About') ); // (1) 'Elefant', reading elem.setAttribute('Test', 123); // (2), writing - alert( elem.outerHTML ); // (3), see if the attribute is in HTML (yes) + alert( elem.outerHTML ); // (3), se om attributten er i HTML (ja, det er den) - for (let attr of elem.attributes) { // (4) list all + for (let attr of elem.attributes) { // (4) oplist alle attributter alert( `${attr.name} = ${attr.value}` ); } </script> </body> ``` -Please note: +Bemærk: -1. `getAttribute('About')` -- the first letter is uppercase here, and in HTML it's all lowercase. But that doesn't matter: attribute names are case-insensitive. -2. We can assign anything to an attribute, but it becomes a string. So here we have `"123"` as the value. -3. All attributes including ones that we set are visible in `outerHTML`. -4. The `attributes` collection is iterable and has all the attributes of the element (standard and non-standard) as objects with `name` and `value` properties. +1. `getAttribute('About')` -- det første bogstav er stort, og i HTML er alle bogstaverne små. Men det er lige meget: attributnavne er ikke case-sensitive. +2. Vi kan tildele hvad som helst til en attribut, men det bliver til en streng. Så her har vi `"123"` som værdien. +3. Alle attributter, også dem vi selv sæltter, er synlige i `outerHTML`. +4. Samlingen `attributes` er itererbar og har alle attributter for elementet (standard og ikke-standard) som objekter med `name` og `value` egenskaber. -## Property-attribute synchronization +## Egenskab-attribut synkronisering -When a standard attribute changes, the corresponding property is auto-updated, and (with some exceptions) vice versa. +Når en standard attribut ændres, opdateres den tilsvarende egenskab automatisk, og (med nogle undtagelser) vice versa. -In the example below `id` is modified as an attribute, and we can see the property changed too. And then the same backwards: +I eksemplet herunder ændres `id` som attribut, og vi kan se at egenskaben også ændres. Og det samme sker den anden vej: ```html run <input> @@ -152,17 +152,17 @@ In the example below `id` is modified as an attribute, and we can see the proper <script> let input = document.querySelector('input'); - // attribute => property + // attribut => egenskab input.setAttribute('id', 'id'); - alert(input.id); // id (updated) + alert(input.id); // id (opdateret) - // property => attribute + // egenskab => attribut input.id = 'newId'; - alert(input.getAttribute('id')); // newId (updated) + alert(input.getAttribute('id')); // newId (opdateret) </script> ``` -But there are exclusions, for instance `input.value` synchronizes only from attribute -> property, but not back: +Men der er undtagelser, for eksempel synkroniserer `input.value` kun fra attribut til egenskab og ikke den anden vej rundt: ```html run <input> @@ -170,41 +170,41 @@ But there are exclusions, for instance `input.value` synchronizes only from attr <script> let input = document.querySelector('input'); - // attribute => property - input.setAttribute('value', 'text'); - alert(input.value); // text + // attribut => egenskab + input.setAttribute('value', 'tekst'); + alert(input.value); // tekst *!* - // NOT property => attribute + // IKKE egenskab => attribut input.value = 'newValue'; - alert(input.getAttribute('value')); // text (not updated!) + alert(input.getAttribute('value')); // tekst (ikke opdateret!) */!* </script> ``` -In the example above: -- Changing the attribute `value` updates the property. -- But the property change does not affect the attribute. +I eksemplet ovenfor: +- Ændring af attributten `value` opdaterer egenskaben. +- Men ændring af egenskaben påvirker ikke attributten. -That "feature" may actually come in handy, because the user actions may lead to `value` changes, and then after them, if we want to recover the "original" value from HTML, it's in the attribute. +Denne "feature" kan faktisk være nyttig, fordi brugerhandlinger kan føre til `value`-ændringer, og derefter, hvis vi vil gendanne den "originale" værdi fra HTML, er den i attributten. -## DOM properties are typed +## DOM egenskaber er en bestemt type -DOM properties are not always strings. For instance, the `input.checked` property (for checkboxes) is a boolean: +DOM egenskaber er ikke altid strenge. For eksempel er egenskaben `input.checked` (for afkrydsningsbokse) boolesk: ```html run <input id="input" type="checkbox" checked> checkbox <script> - alert(input.getAttribute('checked')); // the attribute value is: empty string - alert(input.checked); // the property value is: true + alert(input.getAttribute('checked')); // værdien af attributten er: tom streng + alert(input.checked); // værdien af egenskaben er: true </script> ``` -There are other examples. The `style` attribute is a string, but the `style` property is an object: +Der er andre eksempler. Attributten `style` er en streng, men egenskaben `style` er et objekt: ```html run -<div id="div" style="color:red;font-size:120%">Hello</div> +<div id="div" style="color:red;font-size:120%">Hej</div> <script> // string @@ -216,62 +216,62 @@ There are other examples. The `style` attribute is a string, but the `style` pro </script> ``` -Most properties are strings though. +De fleste egenskaber er dog strenge. -Quite rarely, even if a DOM property type is a string, it may differ from the attribute. For instance, the `href` DOM property is always a *full* URL, even if the attribute contains a relative URL or just a `#hash`. +I sjældne tilfælde kan de adskille sig fra attributten, selvom en DOM-egenskabens type er en streng. For eksempel er `href`-egenskaben altid en *fuld* URL, selv hvis attributten indeholder en relativ URL eller kun en `#hash`. -Here's an example: +Her er et eksempel: ```html height=30 run <a id="a" href="#hello">link</a> <script> - // attribute + // attribut alert(a.getAttribute('href')); // #hello - // property - alert(a.href ); // full URL in the form http://site.com/page#hello + // egenskab + alert(a.href ); // fuld URL i formaetet http://site.com/page#hello </script> ``` -If we need the value of `href` or any other attribute exactly as written in the HTML, we can use `getAttribute`. +Hvis vi har brug for værdien af `href` eller en anden attribut præcis som den er skrevet i HTML, kan vi bruge metoden `getAttribute`. -## Non-standard attributes, dataset +## Ikke-standard attributter, dataset -When writing HTML, we use a lot of standard attributes. But what about non-standard, custom ones? First, let's see whether they are useful or not? What for? +Når vi skriver HTML, bruger vi mange standard attributter. Men hvad med ikke-standard, brugerdefinerede attributter? Lad os først se på om de overhovedet er brugbare eller ej. Hvad kan de bruges til? -Sometimes non-standard attributes are used to pass custom data from HTML to JavaScript, or to "mark" HTML-elements for JavaScript. +Nogle gange bruges ikke-standard attributter til at sende brugerdefinerede data fra HTML til JavaScript, eller til at "markere" HTML-elementer for JavaScript. -Like this: +Sådan her: ```html run -<!-- mark the div to show "name" here --> +<!-- marker denne div fil at vise "name" her --> <div *!*show-info="name"*/!*></div> -<!-- and age here --> +<!-- og age her --> <div *!*show-info="age"*/!*></div> <script> - // the code finds an element with the mark and shows what's requested + // koden finder elementet med markeringen og viser det der efterspørges let user = { - name: "Pete", + name: "Valdemar", age: 25 }; for(let div of document.querySelectorAll('[show-info]')) { - // insert the corresponding info into the field + // idsætter den tilsvarende info i feltet der efterspørges let field = div.getAttribute('show-info'); - div.innerHTML = user[field]; // first Pete into "name", then 25 into "age" + div.innerHTML = user[field]; // først sættes Valdemar ind i "name", derefter 25 ind i "age" } </script> ``` -Also they can be used to style an element. +De kan også bruges til at style et element. -For instance, here for the order state the attribute `order-state` is used: +For eksempel opdateres en ordres status med attributten `order-state`: ```html run <style> - /* styles rely on the custom attribute "order-state" */ + /* styles der forlader sig på den brugerdefinerede attribut "order-state" */ .order[order-state="new"] { color: green; } @@ -286,47 +286,47 @@ For instance, here for the order state the attribute `order-state` is used: </style> <div class="order" order-state="new"> - A new order. + En ny ordre. </div> <div class="order" order-state="pending"> - A pending order. + En ventende ordre. </div> <div class="order" order-state="canceled"> - A canceled order. + En annulleret ordre. </div> ``` -Why would using an attribute be preferable to having classes like `.order-state-new`, `.order-state-pending`, `.order-state-canceled`? +HVorfor vil en attribut være at foretrække frem for en klasse i stil med `.order-state-new`, `.order-state-pending`, `.order-state-canceled`? -Because an attribute is more convenient to manage. The state can be changed as easy as: +Fordi en attribut er mere praktisk at håndtere. Tilstanden kan ændres lige så let som: ```js -// a bit simpler than removing old/adding a new class +// en smule mere simpelt end at slette og tilføje en ny klasse div.setAttribute('order-state', 'canceled'); ``` -But there may be a possible problem with custom attributes. What if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows, and more attributes appear to suit the needs of developers. There may be unexpected effects in such case. +Men, der kan være mulige problemer med brugerdefinerede attributter. Hvad hvis vi bruger ikke-standard attributter i vores projekt, men standarden senere introducere samme attribut? HTML sproget er levende, det gror og flere attributter vil komme for at tilfredsstille udvikleres behov. Det kan derfor føre til uventede resultater. -To avoid conflicts, there exist [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributes. +For at undgå den slags konflikter, findes [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributter. -**All attributes starting with "data-" are reserved for programmers' use. They are available in the `dataset` property.** +**Alle attributter der starter med "data-" er reserveret til brug for programører. De er tilgængelige i egenskaben `dataset`.** -For instance, if an `elem` has an attribute named `"data-about"`, it's available as `elem.dataset.about`. +For eksempel, hvis et `elem` har en attribut med navnet `"data-about"`, er den tilgængelig som `elem.dataset.about`. -Like this: +Sådan her: ```html run -<body data-about="Elephants"> +<body data-about="Elefanter"> <script> - alert(document.body.dataset.about); // Elephants + alert(document.body.dataset.about); // Elefanter </script> ``` -Multiword attributes like `data-order-state` become camel-cased: `dataset.orderState`. +Attributter med flere ord som `data-order-state` bliver til camel-cased: `dataset.orderState`. -Here's a rewritten "order state" example: +Her er et omskrevet eksempel af ordretilstanden, der bruger `data-*` attributter: ```html run <style> @@ -344,43 +344,43 @@ Here's a rewritten "order state" example: </style> <div id="order" class="order" data-order-state="new"> - A new order. + En ny ordre. </div> <script> - // read + // læs alert(order.dataset.orderState); // new - // modify + // modificer order.dataset.orderState = "pending"; // (*) </script> ``` -Using `data-*` attributes is a valid, safe way to pass custom data. +Brugen af `data-*` attributter er en valid og sikker måde at overføre brugerdefinerede data. -Please note that we can not only read, but also modify data-attributes. Then CSS updates the view accordingly: in the example above the last line `(*)` changes the color to blue. +Bemærk, at vi ikke kun kan læse data-attributter, vi kan også modificere dem. Derefter vil CSS opdatere skærmen: I eksemplet vil den sidste linje `(*)` ændre farven til blå. -## Summary +## Opsummering -- Attributes -- is what's written in HTML. -- Properties -- is what's in DOM objects. +- Attributter -- er det der skrives i HTML. +- Egenskaber -- er det der er i DOM-objekter. -A small comparison: +En lille sammenligning: | | Properties | Attributes | |------------|------------|------------| -|Type|Any value, standard properties have types described in the spec|A string| -|Name|Name is case-sensitive|Name is not case-sensitive| +|Type|Alle værdier, standard egenskaber har deres type beskrevet i specifikationen|En streng| +|Navn|case-sensitiv|ikke case-sensitiv| -Methods to work with attributes are: +Metoder til at arbejde med attributter er: -- `elem.hasAttribute(name)` -- to check for existence. -- `elem.getAttribute(name)` -- to get the value. -- `elem.setAttribute(name, value)` -- to set the value. -- `elem.removeAttribute(name)` -- to remove the attribute. -- `elem.attributes` is a collection of all attributes. +- `elem.hasAttribute(name)` -- for at tjekke om attributten eksisterer. +- `elem.getAttribute(name)` -- for at få værdien af en attribut. +- `elem.setAttribute(name, value)` -- for at sætte værdien af en attribut. +- `elem.removeAttribute(name)` -- for at fjerne et attribut. +- `elem.attributes` er en samling af attributter på et element. -For most situations using DOM properties is preferable. We should refer to attributes only when DOM properties do not suit us, when we need exactly attributes, for instance: +I de fleste situationer er det bedre at bruge DOM-egenskaber. Vi bør kun henvise til attributter, når DOM-egenskaber ikke passer med det vil vil opnå. Altså, situationer hvor vi har brug for det præcise indhold af en attribut, for eksempel: -- We need a non-standard attribute. But if it starts with `data-`, then we should use `dataset`. -- We want to read the value "as written" in HTML. The value of the DOM property may be different, for instance the `href` property is always a full URL, and we may want to get the "original" value. +- Vi har brug for en oprettet ikke-standard attribut. Men hvis den starter med `data-`, så bør vi bruge `dataset`. +- Vi vil læse værdien "som den er skrevet" i HTML. Værdien af DOM-egenskaben kan være anderledes, for eksempel `href`-egenskaben er altid en fuld URL, og vi kan ønske at få "original" værdi. From cc5c958e9177513e5fe6f29f6ed935cc714c82d9 Mon Sep 17 00:00:00 2001 From: ockley <vestergaard.karsten@gmail.com> Date: Mon, 4 May 2026 15:07:32 +0200 Subject: [PATCH 33/33] Oversat til dansk --- .../1-createtextnode-vs-innerhtml/solution.md | 8 +- .../1-createtextnode-vs-innerhtml/task.md | 4 +- .../10-clock-setinterval/solution.md | 22 +- .../solution.view/index.html | 107 +++--- .../source.view/index.html | 16 +- .../10-clock-setinterval/task.md | 6 +- .../11-append-to-list/solution.md | 4 +- .../11-append-to-list/task.md | 4 +- .../12-sort-table/solution.md | 14 +- .../12-sort-table/solution.view/index.html | 53 +-- .../12-sort-table/source.view/index.html | 52 +-- .../12-sort-table/task.md | 16 +- .../4-clear-elem/solution.md | 10 +- .../4-clear-elem/task.md | 12 +- .../5-why-aaa/solution.md | 10 +- .../07-modifying-document/5-why-aaa/task.md | 12 +- .../6-create-list/solution.md | 2 +- .../6-create-list/solution.view/index.html | 35 +- .../6-create-list/task.md | 17 +- .../build-tree-dom.view/index.html | 84 +++-- .../innerhtml.view/index.html | 72 ++-- .../7-create-object-tree/solution.md | 6 +- .../source.view/index.html | 38 +-- .../7-create-object-tree/task.md | 40 +-- .../8-tree-count/solution.md | 2 +- .../8-tree-count/solution.view/index.html | 106 +++--- .../8-tree-count/source.view/index.html | 92 ++--- .../8-tree-count/task.md | 8 +- .../9-calendar-table/solution.md | 14 +- .../9-calendar-table/solution.view/index.html | 119 ++++--- .../9-calendar-table/source.view/index.html | 68 ++-- .../9-calendar-table/task.md | 12 +- .../07-modifying-document/article.md | 321 +++++++++--------- .../before-prepend-append-after.svg | 108 +++++- 34 files changed, 807 insertions(+), 687 deletions(-) diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md index a38f01645..62fe57f74 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md @@ -1,15 +1,15 @@ -Answer: **1 and 3**. +Svar: **1 og 3**. -Both commands result in adding the `text` "as text" into the `elem`. +Begge kommandoer resulterer i at tilføje `text` "som tekst" til `elem`. -Here's an example: +Her er et eksempel: ```html run height=80 <div id="elem1"></div> <div id="elem2"></div> <div id="elem3"></div> <script> - let text = '<b>text</b>'; + let text = '<b>Tekst</b>'; elem1.append(document.createTextNode(text)); elem2.innerHTML = text; diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md index 40c75dff3..4441a8b98 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md @@ -4,9 +4,9 @@ importance: 5 # createTextNode vs innerHTML vs textContent -We have an empty DOM element `elem` and a string `text`. +Vi har et tomt DOM-element `elem` og en streng `text`. -Which of these 3 commands will do exactly the same? +Hvilken af disse 3 kommandoer vil gøre præcis det samme? 1. `elem.append(document.createTextNode(text))` 2. `elem.innerHTML = text` diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md index 1414e90c1..86def73ca 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md @@ -1,16 +1,16 @@ -First, let's make HTML/CSS. +Lad os først oprette HTML/CSS. -Each component of the time would look great in its own `<span>`: +Hvert komponent af tiden ville se rigtig godt ud i sin egen `<span>`: ```html <div id="clock"> - <span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span> + <span class="hour">tt</span>:<span class="min">mm</span>:<span class="sec">ss</span> </div> ``` -Also we'll need CSS to color them. +Vi har også brug for CSS til at farve dem. -The `update` function will refresh the clock, to be called by `setInterval` every second: +Funktionen `update` vil genopfriske uret, og vil blive kaldt af `setInterval` hver sekundd. Den skal hente det aktuelle tidspunkt og opdatere indholdet af de tre `<span>` elementer: ```js function update() { @@ -32,15 +32,15 @@ function update() { } ``` -In the line `(*)` we every time check the current date. The calls to `setInterval` are not reliable: they may happen with delays. +I linjen mærket `(*)` tjekker vi den aktuelle dato. Kaldet til `setInterval` er ikke troværdigt nok: de kan ske med forsinkelser. -The clock-managing functions: +Funktionen til at håndtere ur-funktioner: ```js let timerId; -function clockStart() { // run the clock - if (!timerId) { // only set a new interval if the clock is not running +function clockStart() { // kør klokken + if (!timerId) { // sæt kun et nyt interval hvis uret ikke er startet timerId = setInterval(update, 1000); } update(); // (*) @@ -52,6 +52,6 @@ function clockStop() { } ``` -Please note that the call to `update()` is not only scheduled in `clockStart()`, but immediately run in the line `(*)`. Otherwise the visitor would have to wait till the first execution of `setInterval`. And the clock would be empty till then. +Bemærk at kaldet til `update()` ikke kun er planlagt i `clockStart()`, men også kørt umiddelbart i linjen `(*)`. Ellers ville brugeren have ventet til den første udførelse af `setInterval`. Og uret ville være tomt indtil da. -Also it is important to set a new interval in `clockStart()` only when the clock is not running. Otherways clicking the start button several times would set multiple concurrent intervals. Even worse - we would only keep the `timerID` of the last interval, losing references to all others. Then we wouldn't be able to stop the clock ever again! Note that we need to clear the `timerID` when the clock is stopped in the line `(**)`, so that it can be started again by running `clockStart()`. +Det er også vigtigt kun at sætte et nyt interval i `clockStart()` når uret *ikke* kører. Hvis ikke, ville klik på start-knappen flere gange sætte flere samtidige intervaller. Vi ville samtidig kun beholde `timerID` for det sidste interval, og miste referencer til alle andre. På denne måde ville vi ikke kunne stoppe uret igen! Bemærk at vi skal rydde `timerID` når uret er stopped i linjen `(**)`, så det kan startes igen ved at køre `clockStart()`. diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html index 84ee26f19..492254af0 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html @@ -1,68 +1,69 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<head> - <style> - .hour { - color: red - } - - .min { - color: green - } - - .sec { - color: blue - } - </style> -</head> + <head> + <style> + .hour { + color: red; + } -<body> + .min { + color: green; + } - <div id="clock"> - <span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span> - </div> + .sec { + color: blue; + } + </style> + </head> - <script> - let timerId; + <body> + <div id="clock"> + <span class="hour">tt</span> + : + <span class="min">mm</span> + : + <span class="sec">ss</span> + </div> - function update() { - let clock = document.getElementById('clock'); - let date = new Date(); + <script> + let timerId; - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - clock.children[0].innerHTML = hours; + function update() { + let clock = document.getElementById('clock'); + let date = new Date(); - let minutes = date.getMinutes(); - if (minutes < 10) minutes = '0' + minutes; - clock.children[1].innerHTML = minutes; + let hours = date.getHours(); + if (hours < 10) hours = '0' + hours; + clock.children[0].innerHTML = hours; - let seconds = date.getSeconds(); - if (seconds < 10) seconds = '0' + seconds; - clock.children[2].innerHTML = seconds; - } + let minutes = date.getMinutes(); + if (minutes < 10) minutes = '0' + minutes; + clock.children[1].innerHTML = minutes; - function clockStart() { - // set a new interval only if the clock is stopped - // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again - if (!timerId) { - timerId = setInterval(update, 1000); + let seconds = date.getSeconds(); + if (seconds < 10) seconds = '0' + seconds; + clock.children[2].innerHTML = seconds; } - update(); // <-- start right now, don't wait 1 second till the first setInterval works - } - - function clockStop() { - clearInterval(timerId); - timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart() - } - </script> + function clockStart() { + // sæt kun et nyt interval hvis uret ikke er startet + // ellers ville vi overskrive timerID-referencen til det kørende interval og ikke kunne stoppe uret igen + if (!timerId) { + timerId = setInterval(update, 1000); + } + update(); // <-- start umiddelbart, vent ikke 1 sekund før det første setInterval virker + } - <!-- click on this button calls clockStart() --> - <input type="button" onclick="clockStart()" value="Start"> + function clockStop() { + clearInterval(timerId); + timerId = null; // <-- slet timerID for at indikere at uret er stoppet, så det er muligt at starte det igen i clockStart() + } + </script> - <!-- click on this button calls clockStop() --> - <input type="button" onclick="clockStop()" value="Stop"> + <!-- klik på denne knap kalder clockStart() --> + <input type="button" onclick="clockStart()" value="Start" /> -</body> + <!-- klik på denne knap kalder clockStop() --> + <input type="button" onclick="clockStop()" value="Stop" /> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html b/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html index ecf5df99a..34ce09e9c 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html @@ -1,12 +1,10 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> + <body> + <!-- klik på denne knap kalder clockStart() --> + <input type="button" onclick="clockStart()" value="Start" /> - <!-- click on this button calls clockStart() --> - <input type="button" onclick="clockStart()" value="Start"> - - <!-- click on this button calls clockStop() --> - <input type="button" onclick="clockStop()" value="Stop"> - -</body> + <!-- klik på denne knap kalder clockStop() --> + <input type="button" onclick="clockStop()" value="Stop" /> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md index a1b53e337..58d898f67 100644 --- a/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md +++ b/2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md @@ -2,10 +2,10 @@ importance: 4 --- -# Colored clock with setInterval +# Farvet ud med setInterval -Create a colored clock like here: +Opret et farvet ur som det her: [iframe src="solution" height=60] -Use HTML/CSS for the styling, JavaScript only updates time in elements. +Brug HTML/CSS til styling, JavaScript opdaterer kun tiden i elementerne. diff --git a/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md b/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md index 4e77fb5cb..d85e2b4d8 100644 --- a/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md +++ b/2-ui/1-document/07-modifying-document/11-append-to-list/solution.md @@ -1,7 +1,7 @@ -When we need to insert a piece of HTML somewhere, `insertAdjacentHTML` is the best fit. +Når vi skal indsætte HTML i en liste, er `insertAdjacentHTML` det bedste værktøj. -The solution: +Løsningen: ```js one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>'); diff --git a/2-ui/1-document/07-modifying-document/11-append-to-list/task.md b/2-ui/1-document/07-modifying-document/11-append-to-list/task.md index 543cd3e46..630594e32 100644 --- a/2-ui/1-document/07-modifying-document/11-append-to-list/task.md +++ b/2-ui/1-document/07-modifying-document/11-append-to-list/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# Insert the HTML in the list +# Indsæt HTML i listen -Write the code to insert `<li>2</li><li>3</li>` between two `<li>` here: +Skriv koden til at indsætte `<li>2</li><li>3</li>` mellem to `<li>` her: ```html <ul id="ul"> diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/solution.md b/2-ui/1-document/07-modifying-document/12-sort-table/solution.md index 49243e8e3..420ffa033 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/solution.md +++ b/2-ui/1-document/07-modifying-document/12-sort-table/solution.md @@ -1,4 +1,4 @@ -The solution is short, yet may look a bit tricky, so here I provide it with extensive comments: +Løsningen er kort, men kan se lidt tricky ud, så her leverer jeg den med omfattende kommentarer: ```js let sortedRows = Array.from(table.tBodies[0].rows) // 1 @@ -7,12 +7,12 @@ let sortedRows = Array.from(table.tBodies[0].rows) // 1 table.tBodies[0].append(...sortedRows); // (3) ``` -The step-by-step algorthm: +Algoritmen trin for trin er: -1. Get all `<tr>`, from `<tbody>`. -2. Then sort them comparing by the content of the first `<td>` (the name field). -3. Now insert nodes in the right order by `.append(...sortedRows)`. +1. Hent alle `<tr>`, fra `<tbody>`. +2. Sorter dem ved at sammenligne indholdet af det første `<td>` (navnefeltet). +3. Indsæt nu nodene i den rigtige rækkefølge ved hjælp af `.append(...sortedRows)`. -We don't have to remove row elements, just "re-insert", they leave the old place automatically. +Vi behøver ikke at fjerne rækkelementerne, bare "genindsætte" dem - de forlader den gamle plads automatisk. -P.S. In our case, there's an explicit `<tbody>` in the table, but even if HTML table doesn't have `<tbody>`, the DOM structure always has it. +P.S. I vores tilfælde er der en eksplicit `<tbody>` i tabellen, men selvom HTML-tabellen ikke har `<tbody>`, har DOM-strukturen altid en. diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html b/2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html index 40692031a..f447f0df1 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html @@ -1,30 +1,39 @@ -<!DOCTYPE html> +<!doctype html> <table id="table"> -<thead> - <tr> - <th>Name</th><th>Surname</th><th>Age</th> - </tr> -</thead> -<tbody> - <tr> - <td>John</td><td>Smith</td><td>10</td> - </tr> - <tr> - <td>Pete</td><td>Brown</td><td>15</td> - </tr> - <tr> - <td>Ann</td><td>Lee</td><td>5</td> - </tr> - <tr> - <td>...</td><td>...</td><td>...</td> - </tr> -</tbody> + <thead> + <tr> + <th>Navn</th> + <th>Efternavn</th> + <th>Alder</th> + </tr> + </thead> + <tbody> + <tr> + <td>Vibeke</td> + <td>Langholm</td> + <td>10</td> + </tr> + <tr> + <td>Felix</td> + <td>Nielsen</td> + <td>15</td> + </tr> + <tr> + <td>Carla</td> + <td>Bratskov</td> + <td>5</td> + </tr> + <tr> + <td>...</td> + <td>...</td> + <td>...</td> + </tr> + </tbody> </table> <script> - let sortedRows = Array.from(table.tBodies[0].rows) - .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML)); + let sortedRows = Array.from(table.tBodies[0].rows).sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML)); table.tBodies[0].append(...sortedRows); </script> diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html index 9071c88ee..2a454f6e0 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html @@ -1,27 +1,37 @@ -<!DOCTYPE html> +<!doctype html> <table id="table"> -<thead> - <tr> - <th>Name</th><th>Surname</th><th>Age</th> - </tr> -</thead> -<tbody> - <tr> - <td>John</td><td>Smith</td><td>10</td> - </tr> - <tr> - <td>Pete</td><td>Brown</td><td>15</td> - </tr> - <tr> - <td>Ann</td><td>Lee</td><td>5</td> - </tr> - <tr> - <td>...</td><td>...</td><td>...</td> - </tr> -</tbody> + <thead> + <tr> + <th>Navn</th> + <th>Efternavn</th> + <th>Alder</th> + </tr> + </thead> + <tbody> + <tr> + <td>Vibeke</td> + <td>Langholm</td> + <td>10</td> + </tr> + <tr> + <td>Felix</td> + <td>Nielsen</td> + <td>15</td> + </tr> + <tr> + <td>Carla</td> + <td>Bratskov</td> + <td>5</td> + </tr> + <tr> + <td>...</td> + <td>...</td> + <td>...</td> + </tr> + </tbody> </table> <script> - // ... your code ... + // ... din kode ... </script> diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/task.md b/2-ui/1-document/07-modifying-document/12-sort-table/task.md index 7cdba35bc..3e36c0f10 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/task.md +++ b/2-ui/1-document/07-modifying-document/12-sort-table/task.md @@ -2,26 +2,26 @@ importance: 5 --- -# Sort the table +# Sorter tabellen -There's a table: +Der er en tabel: ```html run <table> <thead> <tr> - <th>Name</th><th>Surname</th><th>Age</th> + <th>Navn</th><th>Efternavn</th><th>Alder</th> </tr> </thead> <tbody> <tr> - <td>John</td><td>Smith</td><td>10</td> + <td>Vibeke</td><td>Langholm</td><td>10</td> </tr> <tr> - <td>Pete</td><td>Brown</td><td>15</td> + <td>Felix</td><td>Nielsen</td><td>15</td> </tr> <tr> - <td>Ann</td><td>Lee</td><td>5</td> + <td>Carla</td><td>Bratskov</td><td>5</td> </tr> <tr> <td>...</td><td>...</td><td>...</td> @@ -30,6 +30,6 @@ There's a table: </table> ``` -There may be more rows in it. +Der kan være flere rækker i den. -Write the code to sort it by the `"name"` column. +Skriv koden til at sortere den efter `"Navn"`-kolonnen. diff --git a/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md b/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md index 62c3386d8..b4cdf645c 100644 --- a/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md +++ b/2-ui/1-document/07-modifying-document/4-clear-elem/solution.md @@ -1,5 +1,5 @@ -First, let's see how *not* to do it: +Lad os først se hvordan man *ikke* skal gøre det: ```js function clear(elem) { @@ -9,11 +9,11 @@ function clear(elem) { } ``` -That won't work, because the call to `remove()` shifts the collection `elem.childNodes`, so elements start from the index `0` every time. But `i` increases, and some elements will be skipped. +Dette vil ikke virke, fordi kaldet til `remove()` flytter samlingen `elem.childNodes`. Efter fjernelse af et element flyttes de resterende nedad. Men `i` øges alligevel, så nogle elementer vil blive sprunget over. -The `for..of` loop also does the same. +Løkken `for..of` gør det samme. -The right variant could be: +Den rigtige variant kunne være: ```js function clear(elem) { @@ -23,7 +23,7 @@ function clear(elem) { } ``` -And also there's a simpler way to do the same: +Der er også en enklere måde at gøre det samme på: ```js function clear(elem) { diff --git a/2-ui/1-document/07-modifying-document/4-clear-elem/task.md b/2-ui/1-document/07-modifying-document/4-clear-elem/task.md index 938d53470..e3a9337af 100644 --- a/2-ui/1-document/07-modifying-document/4-clear-elem/task.md +++ b/2-ui/1-document/07-modifying-document/4-clear-elem/task.md @@ -2,19 +2,19 @@ importance: 5 --- -# Clear the element +# Slet et element -Create a function `clear(elem)` that removes everything from the element. +Opret en funktion `clear(elem)` der fjerner alt indhold fra et element. ```html run height=60 <ol id="elem"> - <li>Hello</li> - <li>World</li> + <li>Hej</li> + <li>Verden</li> </ol> <script> - function clear(elem) { /* your code */ } + function clear(elem) { /* din kode */ } - clear(elem); // clears the list + clear(elem); // sletter listen </script> ``` diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index 3d1f6698f..bf1787c61 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -1,9 +1,9 @@ -The HTML in the task is incorrect. That's the reason of the odd thing. +Selve HTML-koden i opgaven er forkert. Det er årsagen til det mærkelige resultat. -The browser has to fix it automatically. But there may be no text inside the `<table>`: according to the spec only table-specific tags are allowed. So the browser shows `"aaa"` *before* the `<table>`. +Browseren må reparere det automatisk. Men der kan ikke være noget tekst inden i `<table>`: ifølge specifikationen er kun tabel-specifikke tags tilladt. Så browseren viser `"aaa"` *før* `<table>`. -Now it's obvious that when we remove the table, it remains. +Nu bliver det mere tydeligt hvorfor det bliver stående når tabellen fjernes. -The question can be easily answered by exploring the DOM using the browser tools. You'll see `"aaa"` before the `<table>`. +Spørgsmålet kan nemt besvares ved at udforske DOM'en ved hjælp af browserværktøjerne. Du vil se `"aaa"` før `<table>`. -The HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct. +HTML-standarden specificerer i detaljer, hvordan man behandler dårlig HTML, og sådan adfærd fra browseren anses for korrekt. diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md index 861f70503..4c05d124a 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/task.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/task.md @@ -2,13 +2,13 @@ importance: 1 --- -# Why does "aaa" remain? +# Hvorfor bliver "aaa" ved med at være synlig? -In the example below, the call `table.remove()` removes the table from the document. +I eksemplet nedenfor fjerner kaldet `table.remove()` tabellen fra dokumentet. -But if you run it, you can see that the text `"aaa"` is still visible. +Men hvis du kører det, kan du se at teksten `"aaa"` stadig er synlig. -Why does that happen? +Hvorfor sker det? ```html height=100 run <table id="table"> @@ -19,9 +19,9 @@ Why does that happen? </table> <script> - alert(table); // the table, as it should be + alert(table); // tabellen, som forventet table.remove(); - // why there's still "aaa" in the document? + // hvorfor er der stadig "aaa" i dokumentet? </script> ``` diff --git a/2-ui/1-document/07-modifying-document/6-create-list/solution.md b/2-ui/1-document/07-modifying-document/6-create-list/solution.md index 1669be18f..75e188404 100644 --- a/2-ui/1-document/07-modifying-document/6-create-list/solution.md +++ b/2-ui/1-document/07-modifying-document/6-create-list/solution.md @@ -1 +1 @@ -Please note the usage of `textContent` to assign the `<li>` content. +Bemærk brugen af `textContent` til at tildele `<li>`-indholdet. diff --git a/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html b/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html index 071645e8d..edede33c5 100755 --- a/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html @@ -1,24 +1,23 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> - <h1>Create a list</h1> + <body> + <h1>Opret en liste</h1> - <script> - let ul = document.createElement('ul'); - document.body.append(ul); + <script> + let ul = document.createElement('ul'); + document.body.append(ul); - while (true) { - let data = prompt("Enter the text for the list item", ""); + while (true) { + let data = prompt('Skriv tekst for elementet', ''); - if (!data) { - break; - } - - let li = document.createElement('li'); - li.textContent = data; - ul.append(li); - } - </script> + if (!data) { + break; + } -</body> + let li = document.createElement('li'); + li.textContent = data; + ul.append(li); + } + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/6-create-list/task.md b/2-ui/1-document/07-modifying-document/6-create-list/task.md index a57e7e2d9..7c7da8bd3 100644 --- a/2-ui/1-document/07-modifying-document/6-create-list/task.md +++ b/2-ui/1-document/07-modifying-document/6-create-list/task.md @@ -2,18 +2,17 @@ importance: 4 --- -# Create a list +# Opret en liste -Write an interface to create a list from user input. +Skriv et interface til at oprette en liste fra brugerinput. -For every list item: +For hvert listeelement: +1. Spørg brugeren om dens indhold ved hjælp af `prompt`. +2. Opret `<li>` med det og tilføj det til `<ul>`. +3. Fortsæt indtil brugeren annullerer input (ved at trykke på `key:Esc` eller via en tom indtastning). -1. Ask a user about its content using `prompt`. -2. Create the `<li>` with it and add it to `<ul>`. -3. Continue until the user cancels the input (by pressing `key:Esc` or via an empty entry). +Alle elementer skal oprettes dynamisk. -All elements should be created dynamically. - -If a user types HTML-tags, they should be treated like a text. +Hvis en bruger indtaster HTML-tags, skal de behandles som tekst. [demo src="solution"] diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html index 06d9c01b1..da60291ca 100755 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html @@ -1,57 +1,55 @@ -<!DOCTYPE html> +<!doctype html> <html> -<body> - - <div id="container"></div> - - <script> - let data = { - "Fish": { - "trout": {}, - "salmon": {} - }, + <body> + <div id="container"></div> + + <script> + let data = { + Fisk: { + ørred: {}, + laks: {}, + }, - "Tree": { - "Huge": { - "sequoia": {}, - "oak": {} + Træ: { + Store: { + mammuttræ: {}, + eg: {}, + }, + Blomstrende: { + æbletræ: {}, + magnolia: {}, + }, }, - "Flowering": { - "apple tree": {}, - "magnolia": {} - } + }; + + function createTree(container, obj) { + container.append(createTreeDom(obj)); } - }; - function createTree(container, obj) { - container.append(createTreeDom(obj)); - } + function createTreeDom(obj) { + // hvis der ikke er nogen børn, så returnerer kaldet undefined + // og <ul> vil ikke blive oprettet + if (!Object.keys(obj).length) return; - function createTreeDom(obj) { - // if there's no children, then the call returns undefined - // and the <ul> won't be created - if (!Object.keys(obj).length) return; + let ul = document.createElement('ul'); - let ul = document.createElement('ul'); + for (let key in obj) { + let li = document.createElement('li'); + li.innerHTML = key; - for (let key in obj) { - let li = document.createElement('li'); - li.innerHTML = key; + let childrenUl = createTreeDom(obj[key]); + if (childrenUl) { + li.append(childrenUl); + } - let childrenUl = createTreeDom(obj[key]); - if (childrenUl) { - li.append(childrenUl); + ul.append(li); } - ul.append(li); + return ul; } - return ul; - } - - let container = document.getElementById('container'); - createTree(container, data); - </script> - -</body> + let container = document.getElementById('container'); + createTree(container, data); + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html index 0f5f6b037..2d621aa44 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html @@ -1,45 +1,45 @@ -<!DOCTYPE html> +<!doctype html> <html> -<body> + <body> + <div id="container"></div> - <div id="container"></div> - - <script> - let data = { - "Fish": { - "trout": {}, - "salmon": {} - }, - - "Tree": { - "Huge": { - "sequoia": {}, - "oak": {} + <script> + let data = { + Fisk: { + ørred: {}, + laks: {}, }, - "Flowering": { - "apple tree": {}, - "magnolia": {} - } - } - }; - function createTree(container, obj) { - container.innerHTML = createTreeText(obj); - } + Træ: { + Store: { + mammuttræ: {}, + eg: {}, + }, + Blomstrende: { + æbletræ: {}, + magnolia: {}, + }, + }, + }; - function createTreeText(obj) { // standalone recursive function - let li = ''; - let ul; - for (let key in obj) { - li += '<li>' + key + createTreeText(obj[key]) + '</li>'; + function createTree(container, obj) { + container.innerHTML = createTreeText(obj); } - if (li) { - ul = '<ul>' + li + '</ul>' + + function createTreeText(obj) { + // selvstændig rekursiv funktion + let li = ''; + let ul; + for (let key in obj) { + li += '<li>' + key + createTreeText(obj[key]) + '</li>'; + } + if (li) { + ul = '<ul>' + li + '</ul>'; + } + return ul || ''; } - return ul || ''; - } - createTree(container, data); - </script> -</body> + createTree(container, data); + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md b/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md index d29636ee2..b7b9405e5 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md @@ -1,4 +1,4 @@ -The easiest way to walk the object is to use recursion. +Den nemmeste måde at gennemgå objektet er at bruge rekursion. -1. [The solution with innerHTML](sandbox:innerhtml). -2. [The solution with DOM](sandbox:build-tree-dom). +1. [Løsningen med innerHTML](sandbox:innerhtml). +2. [Løsningen med DOM](sandbox:build-tree-dom). diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html b/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html index 8586f6b24..a6f25247e 100755 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html @@ -9,26 +9,26 @@ <div id="tree"></div> - <!-- The result should be: + <!-- Resultatet bør se sådan ud: <div id="tree"> <ul> - <li>Fish + <li>Fisk <ul> - <li>trout</li> - <li>salmon</li> + <li>ørred</li> + <li>laks</li> </ul> </li> - <li>Trees + <li>Træer <ul> - <li>Huge + <li>Store <ul> - <li>sequoia</li> - <li>oak</li> + <li>mammuttræ</li> + <li>eg</li> </ul> </li> - <li>Flowering + <li>Blomstrende <ul> - <li>apple tree</li> + <li>æbletræ</li> <li>magnolia</li> </ul> </li> @@ -40,18 +40,18 @@ <script> let data = { - "Fish": { - "trout": {}, - "salmon": {} + "Fisk": { + "ørred": {}, + "laks": {} }, - "Tree": { - "Huge": { - "sequoia": {}, - "oak": {} + "Træ": { + "Store": { + "mammuttræ": {}, + "eg": {} }, - "Flowering": { - "apple tree": {}, + "Blomstrende": { + "æbletræ": {}, "magnolia": {} } } diff --git a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md index 5ec1a01bc..782ef92a8 100644 --- a/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md +++ b/2-ui/1-document/07-modifying-document/7-create-object-tree/task.md @@ -2,50 +2,50 @@ importance: 5 --- -# Create a tree from the object +# OPret et træ fra objektet -Write a function `createTree` that creates a nested `ul/li` list from the nested object. +Skriv en funktion `createTree`, som opretter en indlejret `ul/li`-liste fra det indlejrede objekt. -For instance: +For eksempel: ```js let data = { - "Fish": { - "trout": {}, - "salmon": {} + "Fisk": { + "ørred": {}, + "laks": {} }, - "Tree": { - "Huge": { - "sequoia": {}, - "oak": {} + "Træ": { + "Store": { + "Mammuttræ": {}, + "Eg": {} }, - "Flowering": { - "apple tree": {}, + "Blomstrende": { + "æbletræ": {}, "magnolia": {} } } }; ``` -The syntax: +Syntaksen for at kalde funktionen er: ```js let container = document.getElementById('container'); *!* -createTree(container, data); // creates the tree in the container +createTree(container, data); // opretter træet i container */!* ``` -The result (tree) should look like this: +Resultatet (træet) bør se sådan ud: [iframe border=1 src="build-tree-dom"] -Choose one of two ways of solving this task: +Vælg en af følgende to måder at løse denne opgave: -1. Create the HTML for the tree and then assign to `container.innerHTML`. -2. Create tree nodes and append with DOM methods. +1. Opret HTML'en for træet og tildel den til `container.innerHTML`. +2. Opret de enkelte træ-noder og tilføj dem med DOM-metoder. -Would be great if you could do both. +Det ville være dejligt, hvis du kunne gøre begge dele. -P.S. The tree should not have "extra" elements like empty `<ul></ul>` for the leaves. +P.S. Træet bør ikke have "ekstra" elementer som tomme `<ul></ul>` for bladene. diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/solution.md b/2-ui/1-document/07-modifying-document/8-tree-count/solution.md index 43b9a362c..d30fa0dca 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/solution.md +++ b/2-ui/1-document/07-modifying-document/8-tree-count/solution.md @@ -1 +1 @@ -To append text to each `<li>` we can alter the text node `data`. +For at tilføje tekst til hvert `<li>` kan vi ændre tekstnøglen `data`. diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html b/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html index ec44bfda1..e9890be8c 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html @@ -1,56 +1,60 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> + <body> + <ul> + <li> + Dyr + <ul> + <li> + Pattedyr + <ul> + <li>Køer</li> + <li>Aber</li> + <li>Hunde</li> + <li>Tigre</li> + </ul> + </li> + <li> + Andet + <ul> + <li>Slanger</li> + <li>Fugle</li> + <li>Øgler</li> + </ul> + </li> + </ul> + </li> + <li> + Fisk + <ul> + <li> + Akvarium + <ul> + <li>Guppy</li> + <li>Skalare</li> + </ul> + </li> + <li> + Havet + <ul> + <li>Havørred</li> + </ul> + </li> + </ul> + </li> + </ul> - <ul> - <li>Animals - <ul> - <li>Mammals - <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> - </ul> - </li> - <li>Other - <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> - </ul> - </li> - </ul> - </li> - <li>Fishes - <ul> - <li>Aquarium - <ul> - <li>Guppy</li> - <li>Angelfish</li> - </ul> - </li> - <li>Sea - <ul> - <li>Sea trout</li> - </ul> - </li> - </ul> - </li> - </ul> + <script> + let lis = document.getElementsByTagName('li'); - <script> - let lis = document.getElementsByTagName('li'); + for (let li of lis) { + // find antallet af alle <li> under denne <li> + let descendantsCount = li.getElementsByTagName('li').length; + if (!descendantsCount) continue; - for (let li of lis) { - // get the count of all <li> below this <li> - let descendantsCount = li.getElementsByTagName('li').length; - if (!descendantsCount) continue; - - // add directly to the text node (append to the text) - li.firstChild.data += ' [' + descendantsCount + ']'; - } - </script> - -</body> + // tilføj direkte til textnoden (tilføj til teksten i <li>) + li.firstChild.data += ' [' + descendantsCount + ']'; + } + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html b/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html index 542bd9376..26ea8a0dd 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html @@ -1,47 +1,51 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> -<body> + <body> + <ul> + <li> + Dyr + <ul> + <li> + Pattedyr + <ul> + <li>Køer</li> + <li>Aber</li> + <li>Hunde</li> + <li>Tigre</li> + </ul> + </li> + <li> + Andet + <ul> + <li>Slanger</li> + <li>Fugle</li> + <li>Øgler</li> + </ul> + </li> + </ul> + </li> + <li> + Fisk + <ul> + <li> + Akvarium + <ul> + <li>Guppy</li> + <li>Skalare</li> + </ul> + </li> + <li> + Havet + <ul> + <li>Havørred</li> + </ul> + </li> + </ul> + </li> + </ul> - <ul> - <li>Animals - <ul> - <li>Mammals - <ul> - <li>Cows</li> - <li>Donkeys</li> - <li>Dogs</li> - <li>Tigers</li> - </ul> - </li> - <li>Other - <ul> - <li>Snakes</li> - <li>Birds</li> - <li>Lizards</li> - </ul> - </li> - </ul> - </li> - <li>Fishes - <ul> - <li>Aquarium - <ul> - <li>Guppy</li> - <li>Angelfish</li> - </ul> - </li> - <li>Sea - <ul> - <li>Sea trout</li> - </ul> - </li> - </ul> - </li> - </ul> - - <script> - // ... your code ... - </script> - -</body> + <script> + // ... din kode ... + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/8-tree-count/task.md b/2-ui/1-document/07-modifying-document/8-tree-count/task.md index d6343bf3b..2cd3283e8 100644 --- a/2-ui/1-document/07-modifying-document/8-tree-count/task.md +++ b/2-ui/1-document/07-modifying-document/8-tree-count/task.md @@ -2,12 +2,12 @@ importance: 5 --- -# Show descendants in a tree +# Vis efterkommere i et træ -There's a tree organized as nested `ul/li`. +Der er et træ organiseret som indlejrede `ul/li`. -Write the code that adds to each `<li>` the number of its descendants. Skip leaves (nodes without children). +Skriv koden, der tilføjer til hvert `<li>` antallet af dets efterkommere. Spring blade over (noder uden børn). -The result: +Resultatet skal være sådan: [iframe border=1 src="solution"] diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md index de8be56e9..7b0cc9743 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.md @@ -1,9 +1,9 @@ -We'll create the table as a string: `"<table>...</table>"`, and then assign it to `innerHTML`. +Vi opretter tabellen som en streng: `"<table>...</table>"`, og tilknytter den til `innerHTML`. -The algorithm: +Algoritmen er som følger: -1. Create the table header with `<th>` and weekday names. -2. Create the date object `d = new Date(year, month-1)`. That's the first day of `month` (taking into account that months in JavaScript start from `0`, not `1`). -3. First few cells till the first day of the month `d.getDay()` may be empty. Let's fill them in with `<td></td>`. -4. Increase the day in `d`: `d.setDate(d.getDate()+1)`. If `d.getMonth()` is not yet the next month, then add the new cell `<td>` to the calendar. If that's a Sunday, then add a newline <code>"</tr><tr>"</code>. -5. If the month has finished, but the table row is not yet full, add empty `<td>` into it, to make it square. +1. Opret en tabelhoved med `<th>` og navne på ugedage. +2. Opret datoobjektet `d = new Date(year, month-1)`. Det er den første dag i `month` (med hensyn til at måneder i JavaScript starter fra `0`, ikke `1`). +3. De første få celler indtil den første dag i måneden `d.getDay()` kan være tomme. Lad os fylde dem med `<td></td>`. +4. Øg dagen i `d`: `d.setDate(d.getDate()+1)`. Hvis `d.getMonth()` ikke er den næste måned, så tilføj den nye celle `<td>` til kalenderen. Hvis det er en søndag, så tilføj en ny linje <code>"</tr><tr>"</code>. +5. Hvis måneden er slut, men tabelrækken ikke er fuld, tilføj tomme `<td>` til den, for at gøre den kvadratisk. diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html index 7e211abc6..60fcb6bdb 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html @@ -1,79 +1,76 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> + <head> + <style> + table { + border-collapse: collapse; + } -<head> - <style> - table { - border-collapse: collapse; - } - - td, - th { - border: 1px solid black; - padding: 3px; - text-align: center; - } - - th { - font-weight: bold; - background-color: #E6E6E6; - } - </style> -</head> - -<body> + td, + th { + border: 1px solid black; + padding: 3px; + text-align: center; + } + th { + font-weight: bold; + background-color: #e6e6e6; + } + </style> + </head> - <div id="calendar"></div> + <body> + <div id="calendar"></div> - <script> - function createCalendar(elem, year, month) { + <script> + function createCalendar(elem, year, month) { + let mon = month - 1; // måneder i JS er 0..11, ikke 1..12 + let d = new Date(year, mon); - let mon = month - 1; // months in JS are 0..11, not 1..12 - let d = new Date(year, mon); + let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>'; - let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>'; + // mellemrum for den første række + // fra mandag til den første dag i måneden + // * * * 1 2 3 4 + for (let i = 0; i < getDay(d); i++) { + table += '<td></td>'; + } - // spaces for the first row - // from Monday till the first day of the month - // * * * 1 2 3 4 - for (let i = 0; i < getDay(d); i++) { - table += '<td></td>'; - } + // <td> med faktiske datoer + while (d.getMonth() == mon) { + table += '<td>' + d.getDate() + '</td>'; - // <td> with actual dates - while (d.getMonth() == mon) { - table += '<td>' + d.getDate() + '</td>'; + if (getDay(d) % 7 == 6) { + // søndag, sidste dag i ugen - ny linje + table += '</tr><tr>'; + } - if (getDay(d) % 7 == 6) { // sunday, last day of week - newline - table += '</tr><tr>'; + d.setDate(d.getDate() + 1); } - d.setDate(d.getDate() + 1); - } - - // add spaces after last days of month for the last row - // 29 30 31 * * * * - if (getDay(d) != 0) { - for (let i = getDay(d); i < 7; i++) { - table += '<td></td>'; + // tilføj mellemrum efter sidste dage i måneden for den sidste række + // 29 30 31 * * * * + if (getDay(d) != 0) { + for (let i = getDay(d); i < 7; i++) { + table += '<td></td>'; + } } - } - - // close the table - table += '</tr></table>'; - elem.innerHTML = table; - } + // luk tabellen + table += '</tr></table>'; - function getDay(date) { // get day number from 0 (monday) to 6 (sunday) - let day = date.getDay(); - if (day == 0) day = 7; // make Sunday (0) the last day - return day - 1; - } + elem.innerHTML = table; + } - createCalendar(calendar, 2012, 9); - </script> + function getDay(date) { + // hent dagens nummer fra 0 (mandag) til 6 (søndag) + let day = date.getDay(); + if (day == 0) day = 7; // gør søndag (0) til den sidste dag + return day - 1; + } -</body> + createCalendar(calendar, 2012, 9); + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html b/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html index e1f4cd6bd..1e0f1bb8f 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html @@ -1,38 +1,34 @@ -<!DOCTYPE HTML> +<!DOCTYPE html> <html> - -<head> - <style> - table { - border-collapse: collapse; - } - - td, - th { - border: 1px solid black; - padding: 3px; - text-align: center; - } - - th { - font-weight: bold; - background-color: #E6E6E6; - } - </style> -</head> - -<body> - - - <div id="calendar"></div> - - <script> - function createCalendar(elem, year, month) { - // ...your code that generates the calndar in elem... - } - - createCalendar(calendar, 2012, 9); - </script> - -</body> + <head> + <style> + table { + border-collapse: collapse; + } + + td, + th { + border: 1px solid black; + padding: 3px; + text-align: center; + } + + th { + font-weight: bold; + background-color: #e6e6e6; + } + </style> + </head> + + <body> + <div id="calendar"></div> + + <script> + function createCalendar(elem, year, month) { + // ...din kode der genererer kalenderen i elem... + } + + createCalendar(calendar, 2012, 9); + </script> + </body> </html> diff --git a/2-ui/1-document/07-modifying-document/9-calendar-table/task.md b/2-ui/1-document/07-modifying-document/9-calendar-table/task.md index 37b1a60d2..319918010 100644 --- a/2-ui/1-document/07-modifying-document/9-calendar-table/task.md +++ b/2-ui/1-document/07-modifying-document/9-calendar-table/task.md @@ -2,16 +2,16 @@ importance: 4 --- -# Create a calendar +# Opret en kalender -Write a function `createCalendar(elem, year, month)`. +Skriv funktionen `createCalendar(elem, year, month)`. -The call should create a calendar for the given year/month and put it inside `elem`. +Kaldet skal oprette en kalender for det givne år/måned og putte den indeni `elem`. -The calendar should be a table, where a week is `<tr>`, and a day is `<td>`. The table top should be `<th>` with weekday names: the first day should be Monday, and so on till Sunday. +Kalenderen skal være en tabel, hvor en uge er `<tr>`, og en dag er `<td>`. Den øverste del af tabellen skal være `<th>` med navne på ugedage: den første dag skal være mandag, og så videre til søndag. -For instance, `createCalendar(cal, 2012, 9)` should generate in element `cal` the following calendar: +For eksempel, `createCalendar(cal, 2012, 9)` skal generere følgende kalender i element `cal`: [iframe height=210 src="solution"] -P.S. For this task it's enough to generate the calendar, should not yet be clickable. +P.S. Det er nok at generere kalenderen, den skal ikke være klikbar endnu. diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index 75ce1fbb0..31bb8e4b5 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -1,14 +1,14 @@ -# Modifying the document +# Modificering af dokumentet -DOM modification is the key to creating "live" pages. +DOM modificering er nøglen til at skabe "levende" sider. -Here we'll see how to create new elements "on the fly" and modify the existing page content. +I denne del vil vi se på hvordan vi opretter nye elementer "on the fly" og modificerer eksisterende indhold på siden. -## Example: show a message +## Eksempel: vis en besked -Let's demonstrate using an example. We'll add a message on the page that looks nicer than `alert`. +Lad os demonstrere ved hjælp af et eksempel hvor vi tilføjer en besked på siden, der ser lidt bedre ud end `alert`. -Here's how it will look: +Her er hvordan det vil se ud i HTML: ```html autorun height="80" <style> @@ -23,57 +23,57 @@ Here's how it will look: *!* <div class="alert"> - <strong>Hi there!</strong> You've read an important message. + <strong>Hej med dig!</strong> Du har læst en vigtig besked. </div> */!* ``` -That was the HTML example. Now let's create the same `div` with JavaScript (assuming that the styles are in the HTML/CSS already). +Dette var HTML eksemplet. Lad os nu oprette den samme `div` med JavaScript (vi antager at stilen alerede er defineret er i HTML/CSS). -## Creating an element +## Oprettelse af et element -To create DOM nodes, there are two methods: +Der findes to metoder til at oprette DOM-noder: `document.createElement(tag)` -: Creates a new *element node* with the given tag: +: Opretter en ny *elementnode* med det givne tag: ```js let div = document.createElement('div'); ``` `document.createTextNode(text)` -: Creates a new *text node* with the given text: +: Opretter en ny *textnode* med den givne tekst: ```js - let textNode = document.createTextNode('Here I am'); + let textNode = document.createTextNode('Her er jeg'); ``` -Most of the time we need to create element nodes, such as the `div` for the message. +I de fleste tilfælde er vi nødt til at oprette elementnoder, såsom `div` for beskeden. -### Creating the message +### Oprettelse af beskeden -Creating the message div takes 3 steps: +Oprettelse af beskeden gøres med 3 trin: ```js -// 1. Create <div> element +// 1. Opret <div> element let div = document.createElement('div'); -// 2. Set its class to "alert" +// 2. Sæt dens class til "alert" div.className = "alert"; -// 3. Fill it with the content -div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; +// 3. Fyld indholdet i +div.innerHTML = "<strong>Hej med dig!</strong> Du har læst en vigtig besked."; ``` -We've created the element. But as of now it's only in a variable named `div`, not in the page yet. So we can't see it. +Nu er elementet oprettet. Men ind til videre er det bare en variabel kaldet `div`. Den er ikke på siden endnu, så vi kan ikke se den. -## Insertion methods +## Metoder til indsættelse af elementer -To make the `div` show up, we need to insert it somewhere into `document`. For instance, into `<body>` element, referenced by `document.body`. +For at få vores `div` til at blive vist på siden skal den indsættes et sted i `document`. For eksempel, i `<body>` elementet, refereret til af `document.body`. -There's a special method `append` for that: `document.body.append(div)`. +Der er en speciel metode `append` til dette: `document.body.append(div)`. -Here's the full code: +Her er hele koden: ```html run height="80" <style> @@ -89,7 +89,7 @@ Here's the full code: <script> let div = document.createElement('div'); div.className = "alert"; - div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; + div.innerHTML = "<strong>Hej med dig!</strong> Du har læst en vigtig besked."; *!* document.body.append(div); @@ -97,21 +97,21 @@ Here's the full code: </script> ``` -Here we called `append` on `document.body`, but we can call `append` method on any other element, to put another element into it. For instance, we can append something to `<div>` by calling `div.append(anotherElement)`. +Her kalder vi `append` på `document.body`, men vi kan kalde `append` metoden på ethvert andet element, for at putte et andet element ind i det. For eksempel, vi kan tilføje noget til `<div>` ved at kalde `div.append(anotherElement)`. -Here are more insertion methods, they specify different places where to insert: +Her er en række andre metoder til indsættelse. De specificerer hvor elementet skal indsættes. -- `node.append(...nodes or strings)` -- append nodes or strings *at the end* of `node`, -- `node.prepend(...nodes or strings)` -- insert nodes or strings *at the beginning* of `node`, -- `node.before(...nodes or strings)` –- insert nodes or strings *before* `node`, -- `node.after(...nodes or strings)` –- insert nodes or strings *after* `node`, -- `node.replaceWith(...nodes or strings)` –- replaces `node` with the given nodes or strings. +- `node.append(...noder eller strenge)` -- tilføjer noder eller strenge *i slutningen* af `node`, +- `node.prepend(...noder eller strenge)` -- indsætter noder eller strenge *i begyndelsen* af `node`, +- `node.before(...noder eller strenge)` –- indsætter noder eller strenge *før* `node`, +- `node.after(...noder eller strenge)` –- indsætter noder eller strenge *efter* `node`, +- `node.replaceWith(...noder eller strenge)` –- erstatter `node` med de givne noder eller strenge. -Arguments of these methods are an arbitrary list of DOM nodes to insert, or text strings (that become text nodes automatically). +Argumenterne til disse metoder er en vilkårlig liste af DOM-noder der skal indsættes. Det kan også være tekststrenge (der automatisk bliver omdannet til tekstnoder). -Let's see them in action. +Lad os se dem i aktion. -Here's an example of using these methods to add items to a list and the text before/after it: +Her er et eksempel hvor metoderne bruges til at tilføje elementer før og efter de eksisterende i en liste: ```html autorun <ol id="ol"> @@ -121,24 +121,24 @@ Here's an example of using these methods to add items to a list and the text bef </ol> <script> - ol.before('before'); // insert string "before" before <ol> - ol.after('after'); // insert string "after" after <ol> + ol.before('before'); // indsæt strengen "before" før <ol> + ol.after('after'); // indsæt strengen "after" efter <ol> let liFirst = document.createElement('li'); liFirst.innerHTML = 'prepend'; - ol.prepend(liFirst); // insert liFirst at the beginning of <ol> + ol.prepend(liFirst); // indsæt elementet "liFirst" i begyndelsen af <ol> let liLast = document.createElement('li'); liLast.innerHTML = 'append'; - ol.append(liLast); // insert liLast at the end of <ol> + ol.append(liLast); // indsæt elementet "liLast" i slutningen af <ol> </script> ``` -Here's a visual picture of what the methods do: +Her er et overblik over hvad metoderne gør: ![](before-prepend-append-after.svg) -So the final list will be: +Så den endelige liste vil se således ud: ```html before @@ -152,82 +152,82 @@ before after ``` -As said, these methods can insert multiple nodes and text pieces in a single call. +Som sagt så kan disse metoder indsætte flere noder og strenge i ét kald. -For instance, here a string and an element are inserted: +Her indsættes for eksempel både en streng og et element: ```html run <div id="div"></div> <script> - div.before('<p>Hello</p>', document.createElement('hr')); + div.before('<p>Hej</p>', document.createElement('hr')); </script> ``` -Please note: the text is inserted "as text", not "as HTML", with proper escaping of characters such as `<`, `>`. +Bemærk: Teksten bliver indsat "som tekst" ikke "som HTML", med korrekt opstilling af specialtegn såsom `<`, `>`. -So the final HTML is: +Så den endelige HTML ser sådan ud: ```html run *!* -<p>Hello</p> +<p>Hej</p> */!* <hr> <div id="div"></div> ``` -In other words, strings are inserted in a safe way, like `elem.textContent` does it. +Med andre ord bliver strenge indsat på en "sikker måde", på samme måde som `elem.textContent` gør det. -So, these methods can only be used to insert DOM nodes or text pieces. +Så disse metoder kan kun bruges til at indsætte DOM-noder eller tekststykker. -But what if we'd like to insert an HTML string "as html", with all tags and stuff working, in the same manner as `elem.innerHTML` does it? +Men hvad hvis vi vil indsætte en HTML-streng "som HTML", med alle tags osv. fungerende, på samme måde som `elem.innerHTML` gør det? -## insertAdjacentHTML/Text/Element +## insertAdjacentHTML/-Text/-Element -For that we can use another, pretty versatile method: `elem.insertAdjacentHTML(where, html)`. +Til dette kan vi bruge en anden, ganske fleksibel metode: `elem.insertAdjacentHTML(where, html)`. -The first parameter is a code word, specifying where to insert relative to `elem`. Must be one of the following: +Den første parameter er et kodeord, der specificerer hvor der skal indsættes i forhold til `elem`. Det skal være en af følgende: -- `"beforebegin"` -- insert `html` immediately before `elem`, -- `"afterbegin"` -- insert `html` into `elem`, at the beginning, -- `"beforeend"` -- insert `html` into `elem`, at the end, -- `"afterend"` -- insert `html` immediately after `elem`. +- `"beforebegin"` -- indsæt `html` umiddelbart før `elem`, +- `"afterbegin"` -- indsæt `html` i `elem`, ved begyndelsen, +- `"beforeend"` -- indsæt `html` i `elem`, ved slutningen, +- `"afterend"` -- indsæt `html` umiddelbart efter `elem`. -The second parameter is an HTML string, that is inserted "as HTML". +Det andet parameter er den HTML-streng der skal indsættes "som HTML". -For instance: +For eksempel: ```html run <div id="div"></div> <script> - div.insertAdjacentHTML('beforebegin', '<p>Hello</p>'); - div.insertAdjacentHTML('afterend', '<p>Bye</p>'); + div.insertAdjacentHTML('beforebegin', '<p>Hej</p>'); + div.insertAdjacentHTML('afterend', '<p>Farvel</p>'); </script> ``` -...Would lead to: +... vil føre til: ```html run -<p>Hello</p> +<p>Hej</p> <div id="div"></div> -<p>Bye</p> +<p>Farvel</p> ``` -That's how we can append arbitrary HTML to the page. +Det er metoden til at tilføje vilkårlig HTML til siden. -Here's the picture of insertion variants: +Her er et billede af mulighederne for indsættelse: ![](insert-adjacent.svg) -We can easily notice similarities between this and the previous picture. The insertion points are actually the same, but this method inserts HTML. +Vi kan tydeligt se ligninger mellem denne og det forrige billede. Indstikningspunkterne er faktisk de samme, men denne metode indsætter HTML. -The method has two brothers: +Metoden har to søskende: -- `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` is inserted "as text" instead of HTML, -- `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element. +- `elem.insertAdjacentText(where, text)` -- samme syntaks, men med en streng af `text` indsat "som tekst" i stedet for HTML, +- `elem.insertAdjacentElement(where, elem)` -- samme syntaks, men indsætter et element. -They exist mainly to make the syntax "uniform". In practice, only `insertAdjacentHTML` is used most of the time. Because for elements and text, we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces. +De eksisterer hovedsageligt for at gøre syntaksen "uniform". I praksis bruges kun `insertAdjacentHTML` de fleste gange. Til elementer og tekst har vi metoderne `append/prepend/before/after` -- de er kortere at skrive og kan indsætte noder/textstykker. -So here's an alternative variant of showing a message: +Så her er en alternativ variant af at vise en besked: ```html run <style> @@ -242,16 +242,16 @@ So here's an alternative variant of showing a message: <script> document.body.insertAdjacentHTML("afterbegin", `<div class="alert"> - <strong>Hi there!</strong> You've read an important message. + <strong>Hej med dig!</strong> Du har læst en vigtig besked. </div>`); </script> ``` -## Node removal +## Fjernelse af noder -To remove a node, there's a method `node.remove()`. +Til at fjerne en node findes metoden `node.remove()`. -Let's make our message disappear after a second: +Lad os få vores besked til at forsvinde efter et sekund: ```html run untrusted <style> @@ -267,7 +267,7 @@ Let's make our message disappear after a second: <script> let div = document.createElement('div'); div.className = "alert"; - div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; + div.innerHTML = "<strong>Hej med dig!</strong> Du har læst en vigtig besked."; document.body.append(div); *!* @@ -276,32 +276,31 @@ Let's make our message disappear after a second: </script> ``` -Please note: if we want to *move* an element to another place -- there's no need to remove it from the old one. +Bemærk: Hvis vi vil *flytte* et element til en ny placering, behøver du ikke at flytte det fra den gamle placering. -**All insertion methods automatically remove the node from the old place.** +**Alle indsættelsesmetoder fjerner automatisk noden fra den gamle placering.** -For instance, let's swap elements: +Hvis vi for eksempel vil bytte plads på to elementer, så kan vi bare indsætte det første element efter det andet: ```html run height=50 -<div id="first">First</div> -<div id="second">Second</div> +<div id="first">Første</div> +<div id="second">Anden</div> <script> - // no need to call remove - second.after(first); // take #second and after it insert #first + // ingen behov for at kalde remove() + second.after(first); // tag #second og indsæt #first efter (after) det. </script> ``` -## Cloning nodes: cloneNode +## Kloning af noder: cloneNode -How to insert one more similar message? +Hvordan indsætter man en tilsvarende besked? -We could make a function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it (if needed). +Vi kunne lave en funktion og putte koden der. Men et bedre alternativ er at *klone* den eksisterende `div` og ændre teksten inde i den (hvis det er nødvendigt). -Sometimes when we have a big element, that may be faster and simpler. +Nogle gange når vi har et stort element, kan det være både hurtigere og enklere. +- Kaldet til `elem.cloneNode(true)` opretter en "dyb" klon af elementet -- med alle attributter og underelementer. Hvis vi kalder `elem.cloneNode(false)`, oprettes klonen uden underelementer. -- The call `elem.cloneNode(true)` creates a "deep" clone of the element -- with all attributes and subelements. If we call `elem.cloneNode(false)`, then the clone is made without child elements. - -An example of copying the message: +Et eksempel på kopiering af en besked: ```html run height="120" <style> @@ -315,26 +314,26 @@ An example of copying the message: </style> <div class="alert" id="div"> - <strong>Hi there!</strong> You've read an important message. + <strong>Hej med dig!</strong> Du har læst en vigtig besked. </div> <script> *!* - let div2 = div.cloneNode(true); // clone the message - div2.querySelector('strong').innerHTML = 'Bye there!'; // change the clone + let div2 = div.cloneNode(true); // Klon beskeden, inklusive dens indhold + div2.querySelector('strong').innerHTML = 'Farvel med dig!'; // ændr klonen - div.after(div2); // show the clone after the existing div + div.after(div2); // vis klonen efter den eksisterende div */!* </script> ``` ## DocumentFragment [#document-fragment] -`DocumentFragment` is a special DOM node that serves as a wrapper to pass around lists of nodes. +`DocumentFragment` er en speciel DOM-node, der fungerer som et wrapper-element til at flytte rundt med lister af noder. -We can append other nodes to it, but when we insert it somewhere, then its content is inserted instead. +Vi kan tilføje andre noder til den, men når vi indsætter den et sted, så indsættes dens indhold i stedet. -For example, `getListContent` below generates a fragment with `<li>` items, that are later inserted into `<ul>`: +For eksempel genererer `getListContent` nedenfor et fragment med `<li>` elementer, som senere indsættes i `<ul>`: ```html run <ul id="ul"></ul> @@ -358,7 +357,7 @@ ul.append(getListContent()); // (*) </script> ``` -Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends in", so the resulting structure will be: +Bemærk at vi i den sidste linje mærket med `(*)` tilføjer `DocumentFragment` med append(), men den "blandes ind" så resultatet af kodestrukturen bliver: ```html <ul> @@ -368,7 +367,7 @@ Please note, at the last line `(*)` we append `DocumentFragment`, but it "blends </ul> ``` -`DocumentFragment` is rarely used explicitly. Why append to a special kind of node, if we can return an array of nodes instead? Rewritten example: +`DocumentFragment` bliver sjældent brugt direkte. Hvorfor vil man bruge en særlig slags node, når vi bare kan returnere en liste af noder i stedet? Et omskrevet eksempel vil se således ud: ```html run <ul id="ul"></ul> @@ -387,27 +386,27 @@ function getListContent() { } *!* -ul.append(...getListContent()); // append + "..." operator = friends! +ul.append(...getListContent()); // append + "..." operator = venner! */!* </script> ``` -We mention `DocumentFragment` mainly because there are some concepts on top of it, like [template](info:template-element) element, that we'll cover much later. +Vi nævner primært `DocumentFragment` fordi der er nogle koncepter lagt hen over det, såsom [template](info:template-element) elementet og shadow DOM, som vi ser på meget senere. -## Old-school insert/remove methods +## Old-school insert/remove metoder [old] -There are also "old school" DOM manipulation methods, existing for historical reasons. +Der findes også gamle metoder for at indsætte og fjerne noder, som er der af historiske grunde. -These methods come from really ancient times. Nowadays, there's no reason to use them, as modern methods, such as `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, are more flexible. +Disse metoder kommer fra meget gamle tider. I dag er der ingen grund til at bruge dem, da moderne metoder, såsom `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, er mere fleksible. -The only reason we list these methods here is that you can find them in many old scripts: +Den eneste grund til at vi lister metoderne her er, fordi du kan finde dem i mange gamle scripts: `parentElem.appendChild(node)` -: Appends `node` as the last child of `parentElem`. +: Tilføjer `node` som det sidste barn af `parentElem`. - The following example adds a new `<li>` to the end of `<ol>`: + Det følgende eksempel tilføjer et nyt `<li>` til slutningen af `<ol>`: ```html run height=100 <ol id="list"> @@ -418,16 +417,16 @@ The only reason we list these methods here is that you can find them in many old <script> let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; + newLi.innerHTML = 'Hej, verden!'; list.appendChild(newLi); </script> ``` `parentElem.insertBefore(node, nextSibling)` -: Inserts `node` before `nextSibling` into `parentElem`. +: Indsætter `node` før `nextSibling` inde i `parentElem`. - The following code inserts a new list item before the second `<li>`: + Det følgende eksempel indsætter et nyt listeelement før det andet `<li>`: ```html run height=100 <ol id="list"> @@ -437,26 +436,26 @@ The only reason we list these methods here is that you can find them in many old </ol> <script> let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; + newLi.innerHTML = 'Hej, verden!'; *!* list.insertBefore(newLi, list.children[1]); */!* </script> ``` - To insert `newLi` as the first element, we can do it like this: + For at indsætte `newLi` som det første element, kan vi gøre sådan her: ```js list.insertBefore(newLi, list.firstChild); ``` `parentElem.replaceChild(node, oldChild)` -: Replaces `oldChild` with `node` among children of `parentElem`. +: Erstatter `oldChild` med `node` blandt børn af `parentElem`. `parentElem.removeChild(node)` -: Removes `node` from `parentElem` (assuming `node` is its child). +: Fjerner `node` fra `parentElem` (antaget at `node` er dets barn). - The following example removes first `<li>` from `<ol>`: + Det følgende eksempel fjerner første `<li>` fra `<ol>`: ```html run height=100 <ol id="list"> @@ -471,91 +470,91 @@ The only reason we list these methods here is that you can find them in many old </script> ``` -All these methods return the inserted/removed node. In other words, `parentElem.appendChild(node)` returns `node`. But usually the returned value is not used, we just run the method. +Alle disse metoder returnerer den indsatte/fjernede node. Med andre ord, `parentElem.appendChild(node)` returnerer `node`. Men ofte bruges det returnerede værdi ikke, vi køre bare metoden. -## A word about "document.write" +## Et ord om "document.write" -There's one more, very ancient method of adding something to a web-page: `document.write`. +Der er en yderligere (meget gammel) metode til at tilføje noget til en webside: `document.write`. -The syntax: +Syntaksen er: ```html run -<p>Somewhere in the page...</p> +<p>Et sted på siden ...</p> *!* <script> - document.write('<b>Hello from JS</b>'); + document.write('<b>Hej fra JS</b>'); </script> */!* <p>The end</p> ``` -The call to `document.write(html)` writes the `html` into page "right here and now". The `html` string can be dynamically generated, so it's kind of flexible. We can use JavaScript to create a full-fledged webpage and write it. +Kaldet til `document.write(html)` skriver den givne `html` ind på siden "lige nu og her". `html` kan genreres dynamisk, så den er fleksibel. Vi kan bruge JavaScript til at skabe en hel hjemmeside og skrive den ind. -The method comes from times when there was no DOM, no standards... Really old times. It still lives, because there are scripts using it. +Metoden kommer fra en tid, hvor der ikke var en DOM, ingen standarder ... virkelig gamle dage. Den lever stadig, fordi der kan være scripts, der bruger den. -In modern scripts we can rarely see it, because of the following important limitation: +I moderne scripts kan vi sjældent se den, på grund af følgende vigtige begrænsning: -**The call to `document.write` only works while the page is loading.** +**Kaldet til `document.write` virker kun, mens siden indlæses.** -If we call it afterwards, the existing document content is erased. +Hvis vi kalder det senere, bliver det eksisterende dokumentindhold slettet. -For instance: +For eksempel: ```html run -<p>After one second the contents of this page will be replaced...</p> +<p>Efter en sekund vil indholdet på denne side blive erstattet...</p> *!* <script> - // document.write after 1 second - // that's after the page loaded, so it erases the existing content - setTimeout(() => document.write('<b>...By this.</b>'), 1000); + // document.write efter 1 sekund + // efter siden er indlæst, så vil den slette det eksisterende indhold + setTimeout(() => document.write('<b>...Af denne.</b>'), 1000); </script> */!* ``` -So it's kind of unusable at "after loaded" stage, unlike other DOM methods we covered above. +Så den er ret ubrugelig "efter" siden er indlæst, i modsætning til andre DOM metoder vi har gennemgået ovenfor. -That's the downside. +Det er ulempen. -There's an upside also. Technically, when `document.write` is called while the browser is reading ("parsing") incoming HTML, and it writes something, the browser consumes it just as if it were initially there, in the HTML text. +Men, der er en fordel. Teksnisk set, når `document.write` kaldes med browseren læser ("parser") den indkommende HTML,m og den skriver noget, så konsumerer browseren det som om det hele tiden har stået der - i HTML teksten. -So it works blazingly fast, because there's *no DOM modification* involved. It writes directly into the page text, while the DOM is not yet built. +Så det går lynende stærkt fordi der *ikke modificeres nogen DOM*. Der skrives direkte til sidens tekst, inden DOM'en er blevet bygget. -So if we need to add a lot of text into HTML dynamically, and we're at page loading phase, and the speed matters, it may help. But in practice these requirements rarely come together. And usually we can see this method in scripts just because they are old. +Så hvis vi har brug for at tilføje en masse tekst til HTML dynamisk, OG vi er i sidens indlæsningsfase, OG hastigheden er vigtig, kan det hjælpe. Men i praksis kommer disse krav sjældent sammen. Ofte ser vi denne metode i scripts ... bare fordi de er gamle. -## Summary +## Opsummering -- Methods to create new nodes: - - `document.createElement(tag)` -- creates an element with the given tag, - - `document.createTextNode(value)` -- creates a text node (rarely used), - - `elem.cloneNode(deep)` -- clones the element, if `deep==true` then with all descendants. +- Metoder til at oprette nye noder: + - `document.createElement(tag)` -- opretter et element med det givne tag, + - `document.createTextNode(value)` -- opretter en tekstnode (sjældent brugt), + - `elem.cloneNode(deep)` -- kloner elementet, hvis `deep==true` så med alle efterkommere. -- Insertion and removal: - - `node.append(...nodes or strings)` -- insert into `node`, at the end, - - `node.prepend(...nodes or strings)` -- insert into `node`, at the beginning, - - `node.before(...nodes or strings)` –- insert right before `node`, - - `node.after(...nodes or strings)` –- insert right after `node`, - - `node.replaceWith(...nodes or strings)` –- replace `node`. - - `node.remove()` –- remove the `node`. +- Indsættelse og fjernelse af noder: + - `node.append(...noder eller strenge)` -- indsætter i `node`, ved enden, + - `node.prepend(...noder eller strenge)` -- indsætter i `node`, ved begyndelsen, + - `node.before(...noder eller strenge)` –- indsætter lige før `node`, + - `node.after(...noder eller strenge)` –- indsætter lige efter `node`, + - `node.replaceWith(...noder eller strenge)` –- erstatter `node`. + - `node.remove()` –- fjerner `node`. - Text strings are inserted "as text". + Tekststrenge indsættes "som tekst". -- There are also "old school" methods: +- Der er også "old school" metoder: - `parent.appendChild(node)` - `parent.insertBefore(node, nextSibling)` - `parent.removeChild(node)` - `parent.replaceChild(newElem, node)` - All these methods return `node`. + Alle disse metoder returnerer `node`. -- Given some HTML in `html`, `elem.insertAdjacentHTML(where, html)` inserts it depending on the value of `where`: - - `"beforebegin"` -- insert `html` right before `elem`, - - `"afterbegin"` -- insert `html` into `elem`, at the beginning, - - `"beforeend"` -- insert `html` into `elem`, at the end, - - `"afterend"` -- insert `html` right after `elem`. +- Givet nogle HTML i `html`, indsætter `elem.insertAdjacentHTML(where, html)` det afhængigt af værdien af `where`: + - `"beforebegin"` -- indsætter `html` lige før `elem`, + - `"afterbegin"` -- indsætter `html` inde i `elem`, i begyndelsen, + - `"beforeend"` -- indsætter `html` inde i `elem`, i slutningen, + - `"afterend"` -- indsætter `html` lige efter `elem`. - Also there are similar methods, `elem.insertAdjacentText` and `elem.insertAdjacentElement`, that insert text strings and elements, but they are rarely used. + Der er lignende metoder, `elem.insertAdjacentText` og `elem.insertAdjacentElement`, der indsætter strenge og elementer, men de bruges sjældent. -- To append HTML to the page before it has finished loading: +- For at tilføje HTML til siden før den er færdig med at indlæse: - `document.write(html)` - After the page is loaded such a call erases the document. Mostly seen in old scripts. + Efter siden er indlæst vil et sådant kald slette dokumentet. Mest set i gamle scripts. diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg index 0843713ce..51e442e0c 100644 --- a/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg +++ b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg @@ -1 +1,107 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="409" height="203" viewBox="0 0 409 203"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="before-prepend-append-after.svg"><g id="<ol>-<li>0</li>-<li>" fill-rule="nonzero" transform="translate(18.63 60.2)"><path id="<" fill="#7E7C7B" d="M0 5.53v-.406l6.594-4.018.532.868-5.558 3.29L7.14 8.498l-.532.84z"/><path id="ol" fill="#A7333A" d="M8.568 6.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162A4.963 4.963 0 018.568 6.3zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zM17.836 0h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197V.938h-1.232V0z"/><path id="><" fill="#7E7C7B" d="M25.732 9.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 25.53z"/><path id="li" fill="#A7333A" d="M26.236 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H34.44V22.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="0" fill="#181717" d="M50.54 24.9c0-.793.07-1.505.21-2.135.14-.63.35-1.162.63-1.596.28-.434.635-.765 1.064-.994.43-.229.938-.343 1.526-.343.625 0 1.155.112 1.589.336.434.224.786.55 1.057.98.27.43.467.959.588 1.589.121.63.182 1.351.182 2.163a9.88 9.88 0 01-.21 2.135c-.14.63-.35 1.162-.63 1.596-.28.434-.635.765-1.064.994-.43.229-.938.343-1.526.343-.616 0-1.141-.124-1.575-.371a2.956 2.956 0 01-1.064-1.043c-.275-.448-.474-.982-.595-1.603a10.713 10.713 0 01-.182-2.051zm5.698 0c0-.495-.028-.966-.084-1.414l-4.158 3.794c.159.532.397.957.714 1.274.317.317.733.476 1.246.476.821 0 1.407-.34 1.757-1.022.35-.681.525-1.717.525-3.108zm-4.536 0c0 .233.007.457.021.672.014.215.03.425.049.63l4.172-3.78c-.159-.504-.394-.905-.707-1.204-.313-.299-.735-.448-1.267-.448-.83 0-1.416.343-1.757 1.029-.34.686-.511 1.72-.511 3.101z"/><path id="</" fill="#7E7C7B" d="M58.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 25.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H84.84V22.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 45.53z"/><path id="li" fill="#A7333A" d="M26.236 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H34.44V42.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="1" fill="#181717" d="M51.548 48.792h2.198v-7.364l-2.24 1.568-.546-.798L54.04 40h.784v8.792h2.156V49.8h-5.432z"/><path id="</" fill="#7E7C7B" d="M58.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 45.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H84.84V42.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 65.53z"/><path id="li" fill="#A7333A" d="M26.236 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H34.44V62.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="2" fill="#181717" d="M56.77 62.478c0 .859-.406 1.815-1.218 2.87-.812 1.055-1.946 2.203-3.402 3.444h4.928V69.8h-6.216v-1.008c.177-.168.42-.387.728-.658.308-.27.64-.572.994-.903a29.01 29.01 0 001.085-1.071c.369-.383.702-.775 1.001-1.176.299-.401.541-.803.728-1.204a2.75 2.75 0 00.28-1.162c0-.56-.147-1.001-.441-1.323-.294-.322-.735-.483-1.323-.483-.504 0-.929.056-1.274.168a3.101 3.101 0 00-.938.49l-.476-.77c.42-.299.866-.518 1.337-.658s.987-.21 1.547-.21c.877 0 1.54.238 1.988.714.448.476.672 1.12.672 1.932z"/><path id="</" fill="#7E7C7B" d="M58.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 65.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H84.84V62.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="></" fill="#7E7C7B" d="M92.932 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM0 85.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L0 85.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="ol" fill="#A7333A" d="M16.968 86.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162 4.963 4.963 0 01-.203-1.442zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zm8.106-6.3h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V80z"/><path id=">" fill="#7E7C7B" d="M34.132 89.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 37 184.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/><path id="Line" d="M15 178v-17"/><path id="Line-Copy" d="M15 178l15-5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 53.5 171.5)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 225 96)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><path id="Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5l5-14"/><path id="Line-Copy-2" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5h14"/><text id="ol.after" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="193">ol.after</tspan></text><text id="ol.append" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="156">ol.append</tspan></text><text id="ol.prepend" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="66">ol.prepend</tspan></text><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 222.5 64.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/></g><text id="ol.before" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="111" y="26">ol.before</tspan></text><text id="(…nodes-or-strings)" fill="#643B0C" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="198" y="112">(…nodes or strings)</tspan></text></g></g></svg> \ No newline at end of file +<?xml version="1.0" encoding="UTF-8"?> +<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 409 203"> + <!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) --> + <defs> + <style> + .st0 { + fill: #7e7c7b; + } + + .st1, .st2, .st3 { + isolation: isolate; + } + + .st4 { + fill: none; + stroke: #c06334; + stroke-linecap: square; + stroke-width: 3px; + } + + .st5 { + fill: #181717; + } + + .st2 { + fill: #c06334; + } + + .st2, .st3 { + font-family: PTMono-Regular, 'PT Mono'; + font-size: 14px; + } + + .st3 { + fill: #643b0c; + } + + .st6 { + fill: #a7333a; + } + </style> + </defs> + <g id="dom"> + <g id="before-prepend-append-after.svg"> + <g id="_x3C_ol_x3E_-_x3C_li_x3E_0_x3C__x2F_li_x3E_-_x3C_li_x3E_"> + <path id="_x3C_" class="st0" d="M18.63,65.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81Z"/> + <path id="ol" class="st6" d="M27.2,66.5c0-1.13.29-2.02.88-2.68.59-.66,1.43-.99,2.52-.99.59,0,1.09.1,1.52.29s.78.45,1.06.78c.28.33.49.71.62,1.16s.2.93.2,1.44c0,.56-.07,1.07-.22,1.52-.15.45-.37.84-.66,1.16-.29.32-.65.56-1.07.73-.46.18-.95.27-1.45.26-.58,0-1.08-.1-1.51-.29-.41-.18-.77-.44-1.06-.78-.29-.34-.5-.74-.62-1.16-.14-.47-.21-.95-.2-1.44ZM28.36,66.5c0,.33.04.65.12.98.08.33.21.62.39.88.18.26.41.47.69.63s.63.24,1.04.24c.75,0,1.31-.23,1.69-.69s.57-1.14.57-2.04c0-.34-.04-.66-.12-.99-.08-.31-.21-.61-.39-.88-.18-.26-.42-.48-.7-.63-.28-.16-.63-.24-1.04-.24-.75,0-1.31.23-1.68.69s-.56,1.14-.56,2.04ZM36.47,60.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23v-.94Z"/> + <path id="_x3E__x3C_" class="st0" d="M44.36,69.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81ZM35.43,85.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81Z"/> + <path id="li" class="st6" d="M44.87,80.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14.24-.09.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM53.07,90v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM55.13,81.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E_" class="st0" d="M61.16,89.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81Z"/> + <path id="_x30_" class="st5" d="M69.17,85.1c0-.79.07-1.5.21-2.14.14-.63.35-1.16.63-1.6.28-.43.63-.76,1.06-.99.43-.23.94-.34,1.53-.34.62,0,1.15.11,1.59.34.43.22.79.55,1.06.98.27.43.47.96.59,1.59.12.63.18,1.35.18,2.16,0,.72-.06,1.43-.21,2.14-.14.63-.35,1.16-.63,1.6-.28.43-.63.76-1.06.99-.43.23-.94.34-1.53.34s-1.14-.12-1.58-.37c-.44-.25-.8-.61-1.06-1.04-.28-.45-.47-.98-.6-1.6-.13-.68-.19-1.36-.18-2.05ZM74.87,85.1c0-.5-.03-.97-.08-1.41l-4.16,3.79c.16.53.4.96.71,1.27s.73.48,1.25.48c.82,0,1.41-.34,1.76-1.02.35-.68.53-1.72.53-3.11ZM70.33,85.1c0,.23,0,.46.02.67s.03.42.05.63l4.17-3.78c-.16-.5-.39-.91-.71-1.2s-.74-.45-1.27-.45c-.83,0-1.42.34-1.76,1.03-.34.69-.51,1.72-.51,3.1Z"/> + <path id="_x3C__x2F_" class="st0" d="M77.43,85.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81ZM91.51,80.03l.88.39-5.11,11.54-.88-.39,5.11-11.54Z"/> + <path id="li1" data-name="li" class="st6" d="M95.27,80.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14.24-.09.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM103.47,90v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM105.53,81.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E__x3C_1" data-name="_x3E__x3C_" class="st0" d="M111.56,89.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81ZM35.43,105.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81Z"/> + <path id="li2" data-name="li" class="st6" d="M44.87,100.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM53.07,110v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM55.13,101.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E_1" data-name="_x3E_" class="st0" d="M61.16,109.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81Z"/> + <path id="_x31_" class="st5" d="M70.18,108.99h2.2v-7.36l-2.24,1.57-.55-.8,3.08-2.2h.78v8.79h2.16v1.01h-5.43v-1.01Z"/> + <path id="_x3C__x2F_1" data-name="_x3C__x2F_" class="st0" d="M77.43,105.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81ZM91.51,100.03l.88.39-5.11,11.54-.88-.39,5.11-11.54Z"/> + <path id="li3" data-name="li" class="st6" d="M95.27,100.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM103.47,110v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM105.53,101.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E__x3C_2" data-name="_x3E__x3C_" class="st0" d="M111.56,109.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41s-6.61,3.81-6.61,3.81ZM35.43,125.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81Z"/> + <path id="li4" data-name="li" class="st6" d="M44.87,120.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM53.07,130v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM55.13,121.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E_2" data-name="_x3E_" class="st0" d="M61.16,129.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81Z"/> + <path id="_x32_" class="st5" d="M75.4,122.68c0,.86-.41,1.81-1.22,2.87s-1.95,2.2-3.4,3.44h4.93v1.01h-6.22v-1.01c.18-.17.42-.39.73-.66.31-.27.64-.57.99-.9.37-.35.73-.7,1.08-1.07.37-.38.7-.78,1-1.18s.54-.8.73-1.2c.18-.36.27-.76.28-1.16,0-.56-.15-1-.44-1.32-.29-.32-.74-.48-1.32-.48-.5,0-.93.06-1.27.17-.34.11-.66.27-.94.49l-.48-.77c.42-.3.87-.52,1.34-.66s.99-.21,1.55-.21c.88,0,1.54.24,1.99.71.45.48.67,1.12.67,1.93Z"/> + <path id="_x3C__x2F_2" data-name="_x3C__x2F_" class="st0" d="M77.43,125.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81ZM91.51,120.03l.88.39-5.11,11.54-.88-.39s5.11-11.54,5.11-11.54Z"/> + <path id="li5" data-name="li" class="st6" d="M95.27,120.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94ZM103.47,130v-.94h2.44v-5.12h-2.44v-.94h3.56v6.06h2.38v.94s-5.94,0-5.94,0ZM105.53,121.01c0-.25.08-.47.25-.65.16-.18.4-.28.64-.27.27,0,.5.09.68.27.18.17.28.41.27.65,0,.24-.1.46-.27.62-.18.17-.43.26-.68.25-.24,0-.47-.08-.64-.25-.17-.16-.26-.38-.25-.62Z"/> + <path id="_x3E__x3C__x2F_" class="st0" d="M111.56,129.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81ZM18.63,145.73v-.41l6.59-4.02.53.87-5.56,3.29,5.57,3.23-.53.84-6.61-3.81ZM32.71,140.03l.88.39-5.11,11.54-.88-.39,5.11-11.54Z"/> + <path id="ol1" data-name="ol" class="st6" d="M35.6,146.5c0-1.13.29-2.02.88-2.68s1.43-.99,2.52-.99c.59,0,1.09.1,1.52.29s.78.45,1.06.78c.28.33.49.71.62,1.16.14.45.2.93.2,1.44,0,.56-.08,1.07-.22,1.52-.15.45-.37.84-.66,1.15-.29.32-.65.56-1.07.74-.46.18-.95.27-1.45.26-.58,0-1.08-.1-1.51-.29-.41-.18-.77-.44-1.06-.78-.29-.34-.5-.74-.62-1.16-.14-.47-.21-.95-.2-1.44ZM36.76,146.5c0,.33.04.65.12.98.08.33.21.62.39.88.18.26.41.47.69.63.28.16.63.24,1.04.24.75,0,1.31-.23,1.69-.69s.57-1.14.57-2.04c0-.34-.04-.67-.12-.99-.08-.31-.21-.61-.39-.88-.18-.26-.42-.48-.7-.63-.28-.16-.63-.24-1.04-.24-.75,0-1.31.23-1.68.69-.37.46-.56,1.14-.56,2.04ZM44.87,140.2h2.32v7.45c0,.57.1.97.29,1.2.19.23.47.35.85.35.26,0,.51-.05.75-.14s.5-.25.79-.48l.5.77c-.15.13-.31.25-.49.35-.34.2-.72.34-1.11.42-.48.09-.98.07-1.44-.08-.26-.08-.49-.23-.68-.43-.2-.22-.34-.49-.41-.78-.09-.32-.14-.72-.14-1.2v-6.51h-1.23s0-.94,0-.94Z"/> + <path id="_x3E_3" data-name="_x3E_" class="st0" d="M52.76,149.54l-.53-.84,5.57-3.23-5.56-3.29.53-.87,6.59,4.02v.41l-6.61,3.81Z"/> + </g> + <g id="Line-Copy-5-_x2B_-Line-Copy-4-_x2B_-Line-Copy-3"> + <path id="Line-Copy-5" class="st4" d="M220.88,148.03s4.12,30.36-80.62,30.36-94.72-15.18-94.72-15.18"/> + <path id="Line" class="st4" d="M43.5,162.5h17"/> + <path id="Line-Copy" class="st4" d="M43.5,162.5l5,15"/> + </g> + <g id="Line-Copy-5-_x2B_-Line-Copy-4-_x2B_-Line-Copy-3-Copy-Copy"> + <path id="Line-Copy-51" data-name="Line-Copy-5" class="st4" d="M224.26,118.26s-7.59,21.2-93.12,21.2H63.9"/> + <path id="Line1" data-name="Line" class="st4" d="M62.34,139.41l12.16-6.91"/> + <path id="Line-Copy1" data-name="Line-Copy" class="st4" d="M62.51,139.48l11.99,6.02"/> + </g> + <g id="Line-Copy-5-_x2B_-Line-Copy-4-_x2B_-Line-Copy-3-Copy-Copy-Copy"> + <path id="Line-Copy-52" data-name="Line-Copy-5" class="st4" d="M224.26,95.74s-7.59-21.2-93.12-21.2H63.9"/> + <path id="Line2" data-name="Line" class="st4" d="M62.34,74.59l12.16,6.91"/> + <path id="Line-Copy2" data-name="Line-Copy" class="st4" d="M62.51,74.52l11.99-6.02"/> + </g> + <path id="Line-Copy-3" class="st4" d="M45.5,49.5l5-14"/> + <path id="Line-Copy-2" class="st4" d="M45.5,49.5h14"/> + <g id="ol.after" class="st1"> + <text class="st2" transform="translate(113 193)"><tspan x="0" y="0">ol.after</tspan></text> + </g> + <g id="ol.append" class="st1"> + <text class="st2" transform="translate(113 156)"><tspan x="0" y="0">ol.append</tspan></text> + </g> + <g id="ol.prepend" class="st1"> + <text class="st2" transform="translate(113 66)"><tspan x="0" y="0">ol.prepend</tspan></text> + </g> + <g id="Line-Copy-5-_x2B_-Line-Copy-4-_x2B_-Line-Copy-3-Copy"> + <path id="Line-Copy-53" data-name="Line-Copy-5" class="st4" d="M221.88,63.97s4.12-30.36-80.62-30.36-94.72,15.18-94.72,15.18"/> + </g> + <g id="ol.before" class="st1"> + <text class="st2" transform="translate(111 26)"><tspan x="0" y="0">ol.before</tspan></text> + </g> + <g id="_x28__x2026_nodes-or-strings_x29_" class="st1"> + <text class="st3" transform="translate(198 112)"><tspan x="0" y="0">(…noder eller strenge)</tspan></text> + </g> + </g> + </g> +</svg> \ No newline at end of file