Informasjonsteknologi 2

Page 1



BOKMÃ…L


Informasjonsteknologi 2 følger læreplanen Informasjonsteknologi 2 (2006). © H. Aschehoug & Co. (W. Nygaard) 2017 1. utgave / 1. opplag 2017 Det må ikke kopieres fra denne boka i strid med åndsverkloven eller i strid med avtaler om kopiering inngått med Kopinor, interesseorgan for rettighetshavere til åndsverk. Kopiering i strid med lov eller avtale kan føre til erstatningsansvar og inndragning, og kan straffes med bøter eller fengsel. Redaktør: Dag-Erik Møller Grafisk formgiving og tekniske illustrasjoner: Lars Nersveen Omslagsillustrasjon: Lars Fiske Illustrasjoner: Lars Fiske Omslagsdesign: Mona Markeng Bilderedaktør: Nina Hovda Johannesen Grunnskrift: Latin Modern Roman 11 Papir: 100 g Arctic matt 1,0 Trykk: 07 PrintMedia AS Innbinding: Bokbinderiet Johnsen AS, Skien ISBN 978-82-03-40083-4 www.aschehoug.no


Forord Læreboka Læreboka går gjennom grunnleggende HTML5, CSS3 og JavaScript. Dette er verktøy som lar deg kode interaktive web-applikasjoner med tekst, bilde, lyd, video og animasjoner. Boka inneholder teori, eksempler og varierte oppgaver. Hvert kapittel avsluttes med et sammendrag. Du finner også fire prosjektkapitler, som fungerer som repetisjon av bokas teoristoff. Alt lærestoffet kan gjennomgås med gratis programvare.

Elevnettstedet Læreverket har et gratis elevnettsted, på lokus.no/direkte/IT2. Her finner du bl.a. undervisningsvideoer, løsninger til oppgaver i læreboka, kodeeksempler, filer til nedlasting, ekstra oppgaver og nyttige lenker. Du finner også videoene på YouTube-kanalen til læreverket.

Lærernettstedet På lærernettstedet finner du bl.a. kapittelprøver, prosjekter, eksamensoppgaver, eksamensløsninger, årsplan og en digital utgave av læreboka. Lærernettstedet finner du på Lokus.no.

Tilbakemeldinger Vi setter pris på alle tilbakemeldinger om læreverket, både læreboka og nettstedene. Ønsker du å gi kommentarer, kan du bruke adressen IT2@aschehoug.no.

Takk Jeg vil nok en gang takke mine gode konsulenter: Inger Elisabeth Løchen Grenborgen, Helge Grenborgen, Andreas Nakkerud, Johan Hake og Roger Antonsen, og min glimrende redaktør Dag-Erik Møller for gode forslag og innspill. En stor takk rettes også til elevene mine ved Blindern videregående skole for ekstra «konsulentarbeid» og stor tålmodighet med halvferdig lærebok. Til slutt vil jeg takke den fine familien min, som på ulike måter har bidratt til at jeg har kunnet skrive denne boka. Heia Karianne, Ola og lillebror! Hilsen Lars Nersveen


4

Innledning

Programvare som brukes i boka Nettleser Alle skjermbilder i denne boka er fra Google Chrome. Det vil si at alle eksempler i boka støttes av Google Chrome. Hvis en annen nettleser blir brukt, kan det hende at noen av kodeeksemplene vil gi et noe annerledes resultat. Det gjelder for de nyeste elementene i HTML (HTML5) og CSS (CSS3).

Teksteditor For å skrive JavaScript-, HTML- og CSS-kode trenger du en teksteditor (et tekstredigeringsverktøy). Til dette kan programmet Notisblokk (PC) eller TextEdit (Mac) benyttes, men det er hensiktsmessig å bruke et program med kodemarkering – slik at korrekt skrevet kode blir fargelagt. For kodemarkering kan for eksempel programmet Atom eller Notepad++ benyttes.

Bildebehandling, lydredigering og videoredigering Kapitlene om bildebehandling (kapittel 5) og lyd- og videoredigering (kapittel 13) er skrevet uavhengig av programvare. Det vil si at ulike programmer kan brukes. Adobe Photoshop og GIMP er gode bildebehandlingsprogrammer. Sistnevnte er gratis. Til lydredigering anbefaler vi Audacity. På nettstedet på Lokus finner du forslag til videoredigeringsprogrammer.

Til eleven I denne boka er det mange kodeeksempler. For best mulig læringsutbytte bør du prøve ut alle kodene. Boka inneholder også fire prosjektkapitler. De lar deg lage fullstendige spill og programmer samtidig som du får repetert fagstoff fra tidligere i boka. De er skrevet slik at du kan skrive din egen kode mens du leser deg gjennom dem.


For at du skal lære noe av dem, er det viktig at du prøver ut kodene underveis, gjerne med dine egne tilpasninger. Du finner undervisningsvideoer og andre nyttige ressurser på elevnettstedet. Direktelenke: lokus.no/direkte/IT2

Til læreren På lærernettstedet på Lokus finner du kapittelprøver og andre nyttige ressurser. For å gi et fullstendig tilbud har vi valgt å gjennomgå enkelte temaer grundigere enn hva læreplanen krever. Vi har for eksempel valgt å gå gjennom animasjon med både CSS og JavaScript. Elevene kan ha glede av å se begge variantene, men det går fint an å velge én av dem. I årsplanen på lærernettstedet har vi markert hvilke kapitler dette gjelder.


6 INNHOLD

Innhold

1 HTML, CSS og JavaScript – en introduksjon 1.1 HTML . . . . . . . . . . . . . . . . . . . . . . . Plassering av filer . . . . . . . . . . . . . 1.2 CSS . . . . . . . . . . . . . . . . . . . . . . . . 1.3 JavaScript . . . . . . . . . . . . . . . . . . . . . JavaScript-konsollen . . . . . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

12 13 15 15 18 20 23

2 Variabler, datatyper og operatorer 2.1 Variabler . . . . . . . . . . . . . . . . . . . . 2.2 Datatyper . . . . . . . . . . . . . . . . . . . . Datatypen Number . . . . . . . . . . . Datatypen String . . . . . . . . . . . . Datatypen boolean . . . . . . . . . . . 2.3 Operatorer . . . . . . . . . . . . . . . . . . . Aritmetiske operatorer (matematikk) . Tilordningsoperatorer . . . . . . . . . Stringoperatorer . . . . . . . . . . . . typeof-operatoren . . . . . . . . . . . . Gjøre om mellom string og number . . Sammendrag . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

24 25 29 29 29 30 31 31 34 34 36 36 38

3 Kontrollstrukturer (valg og løkker) 3.1 Valg . . . . . . . . . . . . . . . . . . Sammenligningsoperatorer . . if-setninger . . . . . . . . . . Logiske operatorer . . . . . . Tilfeldige tall (Math.random) 3.2 Løkker . . . . . . . . . . . . . . . . . while-løkker . . . . . . . . . . for-løkker . . . . . . . . . . . Lete gjennom en tekst . . . . Sammendrag . . . . . . . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

40 40 42 44 46 48 49 50 52 54 56

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .


INNHOLD

4 Funksjoner 4.1 Enkle funksjoner . . . . . . . . . . . . . . . 4.2 Gi informasjon til funksjoner (argumenter) . 4.3 Få verdier fra en funksjon (returverdier) . . Avslutte funksjoner med return . . . En terningfunksjon . . . . . . . . . . 4.4 Variabler og rekkevidde (scope) . . . . . . . 4.5 Kommentering av kode . . . . . . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . 5 Bilder 5.1 Opprette et dokument . . . . 5.2 Bildestørrelse . . . . . . . . . 5.3 Forminske og forstørre bilder 5.4 Beskjære bilder . . . . . . . . 5.5 Sette sammen bilder . . . . . 5.6 Valg av filformat . . . . . . . Sammendrag . . . . . . . . . . . .

. . . . . . .

. . . . . . .

6 Mer om HTML og CSS 6.1 Hvordan bruker vi CSS? . . . . . 6.2 Viktige CSS-egenskaper . . . . . 6.3 Tekst og bakgrunnsfarge . . . . . 6.4 Overskrifter . . . . . . . . . . . . Overskrifter (HTML) . . Overskrifter (CSS) . . . . 6.5 Lister . . . . . . . . . . . . . . . Lister (HTML) . . . . . . Lister (CSS) . . . . . . . 6.6 Lenker . . . . . . . . . . . . . . . Lenker (HTML) . . . . . Lenker (CSS) . . . . . . . 6.7 Tabeller . . . . . . . . . . . . . . Tabeller (HTML) . . . . . Tabeller (CSS) . . . . . . 6.8 Bilder . . . . . . . . . . . . . . . Bakgrunnsbilder med CSS 6.9 Menyer . . . . . . . . . . . . . . Loddrett meny . . . . . . Vannrett meny . . . . . . 6.10 Avanserte CSS-selektorer . . . . . Velge elementer . . . . . . Klasser og id-er . . . . . . hover . . . . . . . . . . . nth-child() . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

7

. . . . . . . .

57 58 61 63 64 65 67 69 70

. . . . . . .

71 71 72 73 74 74 75 77

. . . . . . . . . . . . . . . . . . . . . . . . .

78 79 80 83 85 85 86 87 87 88 90 90 91 93 93 94 95 97 98 98 99 101 101 101 103 103


8 INNHOLD

6.11 CSS-farger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 Sammendrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 7 Document Object Model (DOM) 7.1 Objekter i HTML-dokumentet . . . . . . . . . . . 7.2 Hente HTML-elementer med JavaScript . . . . . . 7.3 Redigere HTML med JavaScript . . . . . . . . . . Endre innhold i HTML-elementer . . . . . . Legge til HTML-elementer med attributter Redigere attributter . . . . . . . . . . . . . Redigere CSS-egenskaper direkte . . . . . . 7.4 Lage HTML-elementer med JavaScript . . . . . . . Lage elementene . . . . . . . . . . . . . . . Legge til innhold . . . . . . . . . . . . . . . Legge til elementer i DOM-treet . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

109 109 112 113 113 115 116 118 119 119 119 120 122

8 Hendelser 8.1 Lyttere (event listeners) og hendelser addEventListener . . . . . . . removeEventListener . . . . . 8.2 Hendelsesobjektet . . . . . . . . . . . 8.3 Andre hendelsestyper . . . . . . . . . Musebevegelser . . . . . . . . Tastaturhendelser . . . . . . 8.4 Hendelsesfunksjoner og argumenter . Sammendrag . . . . . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

123 123 125 126 128 131 132 133 135 136

. . . . . . . .

137 137 138 139 139 141 141 143 145

. . . . . .

151 151 152 154 155 156 157

9 Prosjekt: stein, saks, papir 9.1 Planlegging . . . . . . . . Kravspesifikasjon . Skisse (wireframe) Pseudokode . . . . 9.2 Koding . . . . . . . . . . . HTML . . . . . . . CSS . . . . . . . . JavaScript . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . . . . .

. . . . . . . .

10 Brukerinput 10.1 Brukerinput med HTML . . . . . . . . . . . Tekst og tall . . . . . . . . . . . . . Nedtrekksmenyer . . . . . . . . . . . Avkrysningsbokser og radioknapper Knapper . . . . . . . . . . . . . . . . 10.2 CSS for brukerinput . . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . .

. . . . . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .

. . . . . . . .

. . . . . .


INNHOLD

Aritmetiske operatorer (matematikk) . Knapper . . . . . . . . . . . . . . . . . 10.3 Brukerinput og JavaScript . . . . . . . . . . . Hendelser for brukerinput . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

11 Arrayer 11.1 Arrayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lage arrayer . . . . . . . . . . . . . . . . . . . . . . Redigere arrayer . . . . . . . . . . . . . . . . . . . . Finne og fjerne enkeltverdier . . . . . . . . . . . . . 11.2 Løpe gjennom arrayer med løkker . . . . . . . . . . . . . . . Enkle arrayer . . . . . . . . . . . . . . . . . . . . . . Arrayer i funksjoner . . . . . . . . . . . . . . . . . . querySelectorAll() . . . . . . . . . . . . . . . . . . . Hente nye HTML-elementer med querySelectorAll() Todimensjonale arrayer . . . . . . . . . . . . . . . . 11.3 Sortere arrayer . . . . . . . . . . . . . . . . . . . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Objekter 12.1 Objekter . . . . . . . . . . . . . . . . . . . . . . Lage et objekt . . . . . . . . . . . . . . Bruke et objekt . . . . . . . . . . . . . . Gå gjennom alle egenskapene i et objekt Redigere innholdet i et objekt . . . . . . 12.2 Metoder . . . . . . . . . . . . . . . . . . . . . . 12.3 Objekter i arrayer . . . . . . . . . . . . . . . . Lage en array med objekter . . . . . . . Hente alle objekter fra en array . . . . . Sortere objekter i en array . . . . . . . . Lage en tabell fra en array med objekter 12.4 Lage en quiz fra objekter . . . . . . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . . . . . 13 Lyd og video 13.1 Lyd (HTML) . . . . . . . . . . 13.2 Lydredigering . . . . . . . . . . Klippe lydfiler . . . . . Sette sammen lydfiler . Lydformater . . . . . . 13.3 Video (HTML) . . . . . . . . . 13.4 Videoredigering . . . . . . . . . Klippe videofiler . . . . Sette sammen videofiler

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

. . . . .

. . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . .

9

. . . . .

158 161 163 166 169

. . . . . . . . . . . .

170 171 171 172 173 175 175 177 177 179 181 183 186

. . . . . . . . . . . . .

188 188 189 190 191 191 192 193 193 194 195 196 200 205

. . . . . . . . .

206 206 207 207 208 208 209 209 210 210


10 INNHOLD

Videoformater 13.5 Styre lyd og video med 13.6 Forgrunnsvindu . . . . Sammendrag . . . . . . . .

. . . . . . . JavaScript . . . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

210 211 213 216

14 Diagrammer og tegninger med HTML5 14.1 canvas-elementet . . . . . . . . . . . . . . . 14.2 Tegne på canvas-elementet . . . . . . . . . . Rektangler . . . . . . . . . . . . . . Mange rektangler og Math.random() Linjer og mangekanter . . . . . . . . Sirkler . . . . . . . . . . . . . . . . . 14.3 Skrive tekst på canvas-elementet . . . . . . 14.4 Søylediagrammer med canvas-elementet . . Sammendrag . . . . . . . . . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

217 217 218 219 222 225 228 230 232 237

15 Prosjekt: hangman 15.1 Planlegging . . . . . . . . . . . . . Kravspesifikasjon . . . . . . Skisse (wireframe) . . . . . Pseudokode . . . . . . . . . 15.2 Koding . . . . . . . . . . . . . . . . HTML . . . . . . . . . . . . CSS . . . . . . . . . . . . . JavaScript Del 1: tegninger JavaScript Del 2: program .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

238 238 238 239 240 241 241 242 242 245

16 Posisjonering med CSS 16.1 Posisjonering . . . . . . . . . . . . . . . . CSS-egenskapen position . . . . . Et element som følger musepekeren Styre et element med piltastene . . 16.2 Flexible Box Layout (flexbox) . . . . . . . En vannrett flexbox-meny . . . . . Flexbox i flexbox . . . . . . . . . . Sammendrag . . . . . . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

251 251 251 254 256 259 261 262 268

17 Overganger og animasjoner med CSS 17.1 Overgang eller animasjon? . . . . . . . . 17.2 Overganger (transitions) . . . . . . . . . Timing-funksjoner . . . . . . . . Styre overganger med JavaScript Overgangshendelser . . . . . . . 17.3 Animasjoner . . . . . . . . . . . . . . . . Animasjonsegenskaper . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

269 269 270 273 273 276 277 279

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . .


INNHOLD

11

Styre animasjoner med JavaScript . . . . . . . . . . . . 280 Animasjonshendelser . . . . . . . . . . . . . . . . . . . . 283 Sammendrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 18 Transformasjoner med CSS 286 18.1 Transformasjoner . . . . . . . . . . . . . . . . . . . . . . . . . . 286 18.2 Animasjon med transformasjoner . . . . . . . . . . . . . . . . . 289 Sammendrag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 19 Prosjekt: bildekarusell 19.1 Planlegging . . . . . . . Kravspesifikasjon Skisse . . . . . . Pseudokode . . . 19.2 Koding . . . . . . . . . . HTML . . . . . . CSS . . . . . . . JavaScript . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

294 294 295 295 295 297 297 297 298

20 Klasser og objekter 20.1 Objektorientert programmering 20.2 Klasser . . . . . . . . . . . . . . 20.3 Arv . . . . . . . . . . . . . . . Sammendrag . . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

302 302 303 305 306

21 Animasjoner med JavaScript 21.1 requestAnimationFrame() . . 21.2 Enkle kollisjoner . . . . . . . 21.3 Kollisjoner mellom objekter . 21.4 cancelAnimationFrame() . . . 21.5 Et enkelt spill med kollisjoner Sammendrag . . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

307 307 309 313 314 315 321

22 Prosjekt: pong 22.1 Planlegging . . . . . . . Kravspesifikasjon Pseudokode . . . UML-diagram . . 22.2 Koding . . . . . . . . . . HTML og CSS . JavaScript . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

322 322 323 323 324 325 325 325

. . . . . . . .

. . . . . . .

. . . . . . . .

. . . . . . .

. . . . . . . .

. . . . . . .


12 HTML, CSS og JavaScript – en introduksjon

Kapittel 1

HTML, CSS og JavaScript – en introduksjon

Etter dette kapitlet skal du kunne • forklare hovedforskjellene på HTML, CSS og JavaScript • lage et enkelt HTML-dokument • forklare hvor CSS- og JavaScript-kode skal plasseres i et HTMLdokument pssst Husk nettstedet på Lokus.

I denne boka bruker vi tre ulike språk for å lage web-apper: HTML, CSS og JavaScript. En app (applikasjon) er et dataprogram. Ofte brukes ordet for å beskrive «lettere» programmer som kalkulatorer og enkle spill. De tre språkene har ulike skriveregler (syntakser), og ulike bruksområder. Vi bruker HTML for å bestemme hva en nettside skal inneholde, vi bruker CSS for å bestemme utseendet til nettsiden, og vi bruker JavaScript for å bestemme hva som skal skje på nettsiden (interaksjon). Felles for de tre språkene er at de utføres hos klienten. En klient er en datamaskin som er koblet til Internett, for eksempel din bærbare PC eller smarttelefon. Det vil si at koden tolkes av nettleseren du bruker, og ikke av en tjener som du er koblet til på Internett. En tjener (eng. server) er en datamaskin vi henter informasjon fra på Internett. Vi kan bruke mange ulike nettlesere, og det hender at de tolker kode ulikt. Det gjelder spesielt for de nyeste CSS-egenskapene. Hvilken nettleser som brukes, har derfor betydning for resultatet av koden. I denne boka brukes nettleseren Google Chrome i alle eksempler. For å lage nettsider må du ha et program der du kan skrive kode. I utgangspunktet er HTML, CSS og JavaScript bare tekst. Derfor er det fullt mulig å bruke noe så enkelt som Notisblokk-programmet, men det finnes programmer som hjelper deg litt, spesielt ved å fargelegge forskjellige typer kode. Det gjør det mye lettere å finne feil. Se innledningen for å finne ut hvilke programmer du kan bruke.


HTML, CSS og JavaScript – en introduksjon

13

Vi skal nå se på et enkelt eksempel med HTML, CSS og JavaScript. Hensikten med eksemplet er å vise noen av egenskapene til de tre språkene. Alt som vises her, vil bli grundigere forklart i senere kapitler.

1.1

HTML

HTML («HyperText Markup Language») er et markeringsspråk som forteller en nettleser hva slags struktur og innhold en nettside skal ha. Vi kaller det et markeringsspråk fordi vi bruker det til å markere innhold; vi kan for eksempel markere et ord som skal vises som en overskrift. Det kan sammenlignes litt med markeringstusjene vi bruker på papir. I denne boka vil hoveddokumentet vårt alltid være et HTML-dokument (med filendelsen .html). I dette dokumentet plasserer vi alt innhold vi ønsker på siden vår (overskrifter, tekst, tabeller, bilder osv.). Vi kan også plassere CSS- og JavaScript-kode i HTML-dokumentene, for å endre utseendet på innholdet med CSS, og legge til interaktivitet med JavaScript. HTML er bygd opp av elementer som skrives ved hjelp av tagger. En tagg er omsluttet av tegnene < og >. Et element består alltid av en start-tagg, og som oftest også en slutt-tagg. En slutt-tagg starter med en skråstrek (</taggnavn>). Tagger kan også ha ulike egenskaper (kalt attributter). Vi legger til attributter i start-taggene ved å skrive egenskap="verdi". I koden nedenfor kan du se et eksempel på et enkelt HTML-dokument. Nedenfor koden kan du lese en forklaring av innholdet. 1 2 3 4 5 6 7 8 9 10 11 12

< ! doctype html > < html > < head > < title > Hei ! < / title > < meta charset= " UTF-8 " > < / head > < b ody > < h1 > Hei < / h1 > < p > Hallo , verden ! < / p > < p id= " avsnitt " > Velkommen til min f ø rste nettside . < / p > < / b ody > < / html >

Kode 1.1: Et enkelt HTML-dokument.

Vi starter med å skrive <!doctype html> for å fortelle nettleseren at dette er et HTML-dokument. Alt som står mellom <html> og </html>, utgjør HTMLdokumentet vårt. Mellom <head> og </head> angir vi informasjon om dokumentet vårt. Her har vi for eksempel angitt at vi bruker tegnsettet UTF-8 med attributtet charset i <meta>-taggen. Et tegnsett bestemmer hvordan tegn skal vises; og for å få riktig visning av æ, ø og å bruker vi UTF-8. Vi må også velge UTF-8 når vi lagrer en fil, slik at filen benytter samme tegnsett.


14 HTML, CSS og JavaScript – en introduksjon

Mellom <title> og </title> gir vi dokumentet vårt en tittel (det er denne som er synlig helt øverst i nettleseren og som gir navn til bokmerker). Mellom <body> og </body> plasserer vi alt det vi ønsker skal være synlig i HTML-dokumentet vårt. Teksten mellom taggene <h1> og </h1> gir oss en overskrift (eng. header), og teksten mellom taggene <p> og </p> utgjør et avsnitt (eng. paragraph). I det nederste <p>-elementet har vi også lagt til attributtet id="avsnitt". Ved å gjøre det kan vi «finne» elementet ved hjelp av JavaScript, slik at vi kan endre det senere. Vi kan også bruke id-attributtet for å endre et elements utseende med CSS. I kapittel 6 kan du lære mer om HTML. HTML5 I denne boka vil forkortelsene HTML og HTML5 brukes om hverandre. HTML5 er den nyeste versjonen av HTML og inneholder flere nye elementer, som blant annet gjør det lettere å vise video og spille av lyd. Forkortelsen HTML5 dukker derfor ofte opp på nettsider og i bøker for å understreke at det er den nyeste versjonen som brukes. Det er også noen som feilaktig bruker HTML5 som en samlebetegnelse på språkene HTML, CSS og JavaScript.

HTML5-logoen er hentet fra World Wide Web Consortium (W3C).

Oppgaver 1.1 Hva bruker vi HTML til? 1.2 Hva er et HTML-element? 1.3 Hva er et attributt? 1.4 Forklar innholdet i kode 1.1. 1.5 Skriv inn kode 1.1 i en teksteditor, og lagre filen som index.html. Åpne den i en nettleser og se hvordan den ser ut. 1.6 Gjør noen endringer i teksten i filen, åpne den i en nettleser og se hvordan den ser ut.


HTML, CSS og JavaScript – en introduksjon

15

Plassering av filer En app vil ofte bestå av flere filer, og vi kommer til å lage mange apper. Det er derfor viktig å organisere filene på en måte som gjør det lett å finne fram til riktig fil. Når vi starter på en ny app, bør vi opprette en mappe som skal inneholde alle filene vi skal bruke. Inni denne mappen er det lurt å lage egne mapper der vi kan ha CSS-dokumenter og mediefiler (bilder, lyd og video). I større prosjekter er det også lurt å ha en mappe med JavaScript-dokumenter. HTML-dokumenter plasserer vi i hovedmappen. Hvis du følger en fast filstruktur, som den som vises i figur 1.1, vil du unngå mye trøbbel. Vi glemmer raskere enn vi kanskje tror; med en fast filstruktur blir det lettere å finne fram.

prosjekt

index.html

CSS

media

JS

Figur 1.1: Vi kan spare mye tid med en god filstruktur.

1.2

CSS

CSS («Cascading Style Sheets») er et stilspråk som vi bruker for å gjøre om utseendet på det innholdet vi lager med HTML. Hensikten med språket er å skille innhold fra utseende, slik at vi kan jobbe med innhold og utseende hver for seg. En stor fordel med det er at vi kan legge til samme CSS-kode i mange HTML-dokumenter. En annen stor fordel med CSS er at vi kan slippe å tenke på utseende når vi lager innhold, og at vi kan slippe å tenke på innhold når vi designer. Dette gjør det enklere for designere og innholdsprodusenter å jobbe sammen. Vi kan bruke CSS på tre ulike måter:


16 HTML, CSS og JavaScript – en introduksjon

1. I et eget CSS-dokument. Det er lurt når samme CSS-kode skal brukes i flere HTML-dokumenter. Vi henter inn CSS-dokumenter ved å skrive <link rel="stylesheet" href="filnavn.css"> i <head>-elementet (mellom <head> og </head>). Vi kan inkludere flere CSS-dokumenter i samme HTML-dokument. Et slikt eksternt CSS-dokument kalles et stilark. I kapittel 10 skal vi se hvordan vi kan lage et stilark som gir fint utseende til tekstfelter og knapper, et stilark vi kan legge til hver gang vi har tekstfelter og knapper på en side. 2. I et <style>-element i <head>-elementet i et HTML-dokument. Vi bruker denne varianten hvis appen vår bare består av ett HTML-dokument, noe som ofte vil være tilfellet. Vi kan fremdeles legge til CSS-dokumenter med metoden nevnt ovenfor. 3. I attributtet style i HTML-elementer. Det anbefales ikke, fordi vi da blander inn utseende med innhold. I koden nedenfor kan du se et eksempel der <style>-elementet brukes (variant 2 i listen ovenfor). Denne framgangsmåten kan vi bruke når vi bare vil endre utseende for ett HTML-dokument. Nedenfor koden kan du lese en forklaring av innholdet. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

< ! doctype html > < html > < head > < title > Hei ! < / title > < meta charset= " UTF-8 " > < s tyle > p { border: 3 px solid red; /* heltrukket r ø d kantlinje */ } < / s tyle > < / head > < b ody > < h1 > Hei < / h1 > < p > Hallo , verden ! < / p > < p id= " avsnitt " > Velkommen til min f ø rste nettside . < / p > < / b ody > < / html >

Kode 1.2: HTML-dokumentet fra kode 1.1, med litt CSS lagt til.

Vi angir først hva vi ønsker å endre ved hjelp av en selektor (eng. selector). Her er det <p>-elementene som endres (med selektoren p). Vi kan bruke CSS til å gjøre om utseendet på nesten alle HTML-elementer. Ved å skrive #avsnitt i stedet for p vil bare elementet med id="avsnitt" bli påvirket. I tillegg finnes det mer avanserte selektorer, som for eksempel lar oss bestemme utseendet for et element når vi fører musepekeren over det.


HTML, CSS og JavaScript – en introduksjon

17

Deretter angir vi en liste med egenskaper pakket inn i sløyfeparenteser ({ og }, også kjent som krøllparenteser). For hver av egenskapene finnes det mange mulige verdier. Her bruker vi egenskapen border med verdien 3px solid red. Denne verdien gir en rød (red), heltrukket (solid) kantlinje med tykkelse 3 piksler/bildepunkter (3px). En kombinasjon av en egenskap og en verdi kalles en deklarasjon (eng. declaration). Vi avslutter hver deklarasjon med et semikolon. I koden på forrige side kan du også se en kommentar (/* heltrukket rød kantlinje */). Kommentarer lar oss skrive forklaringer på koden vår. Dette er nyttig både for vår egen del, og for andre som skal lese koden. Vi kan lage HTML-kommentarer også (med <!-- og -->), men de brukes ikke like ofte, fordi HTML-kode er enklere og mer selvforklarende enn CSS- og JavaScript-kode. I figur 1.2 kan du se resultatet av koden i en nettleser. I kapittel 6 kan du lære mer om CSS.

Figur 1.2: Resultatet av kode 1.2.

CSS3 CSS ble utviklet i 1996, blant annet av nordmannen Håkon Wium Lie. To år senere ble en oppdatert versjon lansert (CSS2). Etter CSS2 ble innholdet delt opp i moduler (som for eksempel farge og selektorer). Disse modulene har blitt lansert fortløpende, hvor noen er en oppgradering av innholdet i CSS2, mens noen er helt nye. Det vil si at CSS3 ikke er en tydelig definert spesifikasjon, men en samling av moduler, hvor noen forekommer i versjon 3, mens andre forekommer i versjon 1. De aller nyeste CSS-modulene er ikke støttet i alle nettlesere. Hvis vi lurer på om en CSS-egenskap fungerer i nettleseren vi bruker, kan vi sjekke det på nettsiden caniuse.com.


18 HTML, CSS og JavaScript – en introduksjon

Oppgaver 1.7 Hva bruker vi CSS til? 1.8 Legg til CSS-koden i kode 1.2 i den filen du laget tidligere i kapitlet (index.html). 1.9 Gjør om verdien for border til 5px dashed blue, åpne filen i en nettleser og se hvordan den ser ut. 1.10 Hva er en selektor? Hva tror du vi skriver for å gjøre om utseendet på en overskrift (<h1>)? 1.11 Gjør om fargen på overskriften i kode 1.2 med CSS-koden color: green;. 1.12 Hva er en kommentar?

1.3

JavaScript

Med HTML og CSS kan vi bare lage det vi kaller statiske nettsider. En statisk nettside er en nettside som ikke kan forandre seg når den først er laget. Med JavaScript kan vi gjøre nettsidene interaktive. Vi kan for eksempel la noe skje når vi trykker på en knapp. Vi legger til JavaScript-kode i et HTMLdokument med <script>-elementet. Dette elementet kan plasseres nederst i <head>-elementet eller nederst i <body>-elementet i HTML-dokumentet. I denne boka plasserer vi alltid JavaScript-kode nederst i <body>-elementet (som i eksemplet på neste side). I eksempler på nettet kan du noen ganger se typeattributtet bli brukt (<script type="text/javascript">). Det er ikke lenger nødvendig fordi nettleseren nå antar at du bruker JavaScript i <script>-elementet. Vi kan også bruke eksterne JavaScript-dokumenter. Det er lurt hvis vi har skrevet kode som vi vil bruke i flere dokumenter. Et eksternt JavaScript-dokument har filendelsen .js. Vi henter inn eksterne JavaScript-dokumenter ved å skrive <script src="filnavn.js"></script> på de samme stedene som nevnt ovenfor. JavaScript-kode kan også skrives i HTML-elementer ved hjelp av attributter som onclick. Det anbefales ikke, av samme grunn som at det ikke anbefales å skrive CSS-kode i HTML-elementer. Vi bør skille innhold (HTML), utseende (CSS) og oppførsel/hendelser (JavaScript) så godt det lar seg gjøre. I koden nedenfor kan du se et enkelt eksempel på bruk av JavaScript. Denne koden bruker funksjonen prompt() for å «spørre» brukeren hva han eller hun heter. Vi lagrer brukerens svar i variabelen navn og bruker funksjonen alert() til å lage en skreddersydd hilsen til brukeren. Funksjonen prompt() behandler alt som skrives inn som tekst. Hvis du i stedet vil motta tall, må du bruke funksjonen Number(): var tall = Number(prompt("Skriv inn et tall"));


HTML, CSS og JavaScript – en introduksjon

19

I de neste kapitlene vil du lære mer om JavaScript, og du kan da bruke disse enkle verktøyene til å lage kodene dine mer interaktive. Legg merke til at vi i koden på neste side har laget kommentarer på to ulike måter. Varianten med to skråstreker (//) bruker vi for å lage kommentarer som passer på én linje. Kommentarer i JavaScript som går over flere linjer, lages som i CSS (med /* og */). 1 2 3 4 5 6 7

< ! doctype html > < html > < head > < title > Hei ! < / title > < meta charset= " UTF-8 " > < / head > < b ody >

8 9 10 11 12

< s cript > /* Lager en tekstboks som sp ø r brukeren hva han eller hun heter . Vi lagrer navnet i variabelen navn */ var navn = prompt ( " Hei , hva heter du ? " ) ;

13 14 15

// Lager en beskjedboks som skriver en hilsen til brukeren alert ( " Hei p å deg " + navn + " ! " ) ;

16 17

< / s cript >

18 19 20

< / b ody > < / html >

Kode 1.3: Et enkelt eksempel på bruk av JavaScript.


20 HTML, CSS og JavaScript – en introduksjon

JavaScript-konsollen Hvis vi skriver kode som inneholder feil i HTML eller CSS, vil vi raskt se at noe er galt i nettleservinduet. I JavaScript, derimot, vil vi ofte ha kode som utføres i «kulissene», mens det eneste som vises i nettleseren, er det endelige resultatet av koden. For å identifisere feil i JavaScript-kode er det derfor stor hjelp i å se hva som skjer underveis. Til det kan vi bruke et av JavaScripts innebygde objekter, Console (konsollen). I koden nedenfor kan du se hvordan vi kan bruke metoden console.log til å skrive ut det som skjer underveis.

1 2 3 4

< s cript > /* Lager en tekstboks som sp ø r brukeren hva han eller hun heter . Vi lagrer navnet i variabelen navn */ var navn = prompt ( " Hei , hva heter du ? " ) ;

5 6 7

// Skriver verdien i variabelen navn til konsollen console . log ( navn ) ;

8 9 10

// Lager en beskjedboks som skriver en hilsen til brukeren alert ( " Hei p å deg " + navn + " ! " ) ;

11 12

< / s cript >

Kode 1.4: JavaScript-kode som viser bruken av console.log. Her vises bare <script>elementet for å spare plass; resten av koden skal være som i kode 1.3.

For å se det som skrives ut med console.log, må vi åpne nettleserens konsoll. I tabell 1.1 kan du se hvordan du finner konsollen i de ulike nettleserne. Resultatet av koden ovenfor, med konsollen åpnet i Google Chrome, kan du se i figur 1.3. Du kan også se filnavnet til filen som skriver til konsollen, i tillegg til linjenummeret (i filen). Eventuelle feilmeldinger vil også vises her; derfor bør vi alltid ha dette verktøyet åpent når vi jobber med JavaScript.

Figur 1.3: Konsollen slik den vises i Google Chrome (her vises utskriften fra kode 1.4).


HTML, CSS og JavaScript – en introduksjon

Google Chrome

Snarvei PC

Snarvei Mac

Ctrl+Shift+J / F12

Cmd+Opt+J

Safari (Mac)

21

Cmd+Opt+C

Internet Explorer

F12

Mozilla Firefox

Ctrl+Shift+K

Cmd+Opt+K

Opera

Ctrl+Shift+I

Cmd+Opt+I

Tabell 1.1: Hvordan finne konsollen i ulike nettlesere1 .

JavaScript er ikke Java JavaScript forveksles av mange med programmeringsspråket Java. Disse to språkene har svært lite til felles, men da JavaScript skulle lanseres i 1995, var Java et meget populært språk. Netscape, som sto bak den første versjonen av JavaScript, ville benytte seg av populariteten til Java, og kalte sitt nye språk for JavaScript (det skulle egentlig hete LiveScript). JavaScript ble senere standardisert under navnet ECMAscript. I dag er JavaScript, sammen med flere andre språk, basert på denne standarden. JavaScript er altså en variant av språket ECMAscript.

Oppgaver 1.13 Hva bruker vi JavaScript til? 1.14 Kopier JavaScript-koden i kode 1.3 i en ny fil, og se hvordan den fungerer. 1.15 Gjør om teksten/spørsmålet i prompt()-funksjonen og se hvordan resultatet påvirkes. 1.16 Legg også til JavaScript-koden i kode 1.4, og finn utskriften i konsollen i nettleseren du bruker.

1

I Safari på Mac må du aktivere «Vis Utvikle-menyen i menylinjen» under avanserte innstillinger.


22 HTML, CSS og JavaScript – en introduksjon

Innrykk i kode I dette kapitlet har vi sett eksempler på HTML-, CSS- og JavaScriptkode. I all kode bruker vi innrykk og linjeskift, slik at koden skal bli lett å lese. Et innrykk lages vanligvis ved hjelp av to eller fire mellomrom, eller tabulatortasten (TAB). I denne boka bruker vi to mellomrom for å spare plass. Målet med innrykk og linjeskift er å se hvilken kode som hører til hva. I HTML-kode rykker vi inn elementer som er inni andre elementer. Det eneste unntaket er <style>- og <script>-elementet. Disse to elementene vil inneholde annen kode enn resten av dokumentet, og blir lette å lese også uten innrykk. I CSS- og JavaScript-kode rykker vi inn kode som er skrevet mellom sløyfeparenteser ({ og }). Nedenfor kan du se samme kode som i kode 1.1, men uten riktig bruk av innrykk og linjeskift. Nettleseren overser ekstra mellomrom og linjeskift; resultatet blir det samme, men koden blir mye vanskeligere å lese. < ! doctype html > < html > < head > < title > Hei ! < / title > < meta charset= " UTF-8 " > < / head > < b ody > < h1 > Hei < / h1 > < p > Hallo , verden ! < / p > < p id= " avsnitt " > Velkommen til min f ø rste nettside . < / p > < / b ody > < / html >

Kode 1.5: HTML-kode uten god bruk av innrykk og linjeskift.


HTML, CSS og JavaScript – en introduksjon

23

Sammendrag Vi bruker språkene HTML, CSS og JavaScript for å lage interaktive nettsider (web-apper). HTML bestemmer nettsidens innhold. CSS bestemmer nettsidens utseende. JavaScript styrer nettsidens hendelser. HTML består av elementer, som igjen består av tagger (som kan ha flere attributter). HTML-dokumenter har filendelsen .html. CSS består av en selektor, og ett eller flere par av egenskaper og verdier. Vi kan plassere CSS-kode i HTML-dokumenter (i et <style>-element), eller i eksterne stilark (med filendelsen .css). JavaScript lar oss gjøre nettsider interaktive ved å manipulere HTMLog CSS-kode. Vi kan plassere JavaScript-kode i HTML-dokumenter (i et <script>-element), eller i eksterne JavaScript-dokumenter (med filendelsen .js). Når vi jobber med JavaScript, bør vi alltid åpne nettleserens konsoll. Vi kan skrive kommentarer i koden vår. En kommentar lar oss dokumentere hva som skjer i koden. Det er nyttig både for oss selv, og for andre som skal lese koden. Vi lager kommentarer i HTML med <!-- og -->, i CSS med /* og */ og i JavaScript med // eller /* og */. En god filstruktur (hvordan vi lagrer filene våre) kan spare oss for mye ekstraarbeid.


24 Variabler, datatyper og operatorer

Kapittel 2

Variabler, datatyper og operatorer

Etter dette kapitlet skal du kunne • forklare hva variabler er, og hvordan vi lager dem • kjenne til regler for navngiving av variabler • forklare hva en datatype er • forklare hva vi bruker datatypene number, string og boolean til • manipulere tekster (strings) med metodene til String-objektet • forklare hva en operator er • bruke aritmetiske operatorer, tilordningsoperatorer, konkateneringsoperatoren og typeof-operatoren • gjøre om mellom datatypene number og string pssst Du finner videoer og andre ressurser på nettstedet på Lokus.

Når vi lager programmer, er det viktig å holde orden på verdier underveis. Vi kan se på et enkelt eksempel for å illustrere dette behovet. I figur 2.1 kan du se en kalkulator-app som lar brukeren skrive inn en temperatur i grader fahrenheit, for så å regne ut tilsvarende temperatur i grader celsius. Formelen for å finne temperaturen i grader celsius når vi kjenner temperaturen i grader fahrenheit, ser slik ut: C = (F −32)· 59 . C står for temperaturen i grader celsius, og F står for temperaturen i grader fahrenheit. Kalkulator-appen må altså kjenne temperaturen i grader fahrenheit for å regne ut temperaturen i grader celsius. Når vi skriver inn en temperatur i grader fahrenheit i kalkulatoren, blir verdien lagret i datamaskinens minne, slik at programmet kan bruke denne verdien i beregningen. For å finne igjen en verdi bruker vi et navn som peker på verdiens posisjon i minnet. Dette navnet kalles en variabel. Deler av JavaScript-koden som brukes i kalkulatoren, kan derfor se slik ut (selve utregningen forklares senere i kapitlet):


Variabler, datatyper og operatorer

25

// Lager en variabel for å huske hva brukeren skriver inn var temperatur_fahrenheit; /* Kode som mottar verdien brukeren skriver inn */ // Lager en variabel for å huske temperaturen i grader celsius var temperatur_celsius = (temperatur_fahrenheit - 32) * (5/9); /* Kode som skriver ut celsius-verdien på nettsiden */

Figur 2.1: En enkel kalkulator som gjør om fra grader fahrenheit til grader celsius. Du kan prøve ut denne kalkulatoren på elevnettstedet.

2.1

Variabler

En variabel er altså et navn som peker på en plass i datamaskinens minne. Det vil si at variabelen inneholder posisjonen til verdien, og ikke selve verdien. Likevel sier vi ofte at en variabel har en verdi, og det er en helt grei praktisk tolkning som vi også vil bruke i denne boka. Som navnet antyder, kan en variabel variere. Det vil si at verdien til en variabel kan forandre seg i løpet av et program. Tenk på antall mål en spiller har scoret i FIFA, eller antall mynter Mario har samlet. Disse tallene varierer i løpet av et spill, men vi trenger ikke å lage nye variabler når tallene endrer seg. Fahrenheit-verdiene i eksemplet ovenfor vil også kunne variere. Vi bruker også variabler når vi ønsker å bruke den samme verdien mange ganger i løpet av et program. I spill kan vi for eksempel ofte angi en vanskelighetsgrad. Alle nivåene i spillet vil variere med denne vanskelighetsgraden. Da er det nyttig å lagre vanskelighetsgraden i én variabel, som vi bruker gjennom hele spillet. For å endre spillets vanskelighetsgrad er det da nok å endre én variabel.


26 Variabler, datatyper og operatorer

I JavaScript lager1 vi en variabel ved å bruke nøkkelordet var etterfulgt av et navn vi bestemmer selv, og et semikolon: var antall;

Deretter kan vi bruke tilordningsoperatoren (=) for å gi variabelen en verdi: antall = 7;

Hvis vi ønsker at variabelen skal ha en verdi med én gang, kan vi tilordne en verdi idet vi lager en variabel: var antall = 7;

Når vi først har laget en variabel, kan vi endre verdien slik: antall = 42;

Vi bruker altså nøkkelordet var bare når vi lager nye variabler. Verdien til en variabel vil til enhver tid være den siste verdien vi har gitt den. En variabel kan endre sin verdi mange ganger i løpet av kjøringen av en app. For å undersøke hvilken verdi en variabel har, må vi enten skrive ut verdien på en nettside eller i konsollen. I koden nedenfor vises et fullstendig HTMLdokument der en variabel opprettes og verdien til variabelen skrives ut i konsollen. I resten av dette kapitlet viser vi bare koden som skal skrives mellom taggene <script> og </script>. Du bør derfor kopiere koden nedenfor for å prøve ut det vi skal gjennomgå i kapitlet. 1 2 3 4 5 6

< ! doctype html > < html > < head > < title > Variabler og konsollen < / title > < / head > < b ody >

7 8 9 10

< s cript > // Lager variabelen antall og tilordner verdien 7 var antall = 7;

11 12 13 14

// Skriver variabelens verdi til konsollen console . log ( antall ) ; < / s cript >

15 16 17

< / b ody > < / html >

Kode 2.1: Et enkelt HTML-dokument som bruker konsollen. 1

Formelt kalles det å deklarere en variabel.


Variabler, datatyper og operatorer

27

For at koden vår skal bli så lett å lese som mulig, er det viktig at vi velger gode variabelnavn. Korte navn gjør koden lett å skrive, men det kan være lurt å velge lengre navn hvis det gjør koden lettere å forstå. For eksempel er det mye lettere å lese en kode som bruker variabelnavnene antallRiktige og antallMulige, enn en som bruker navnene tall1 og tall2. Derfor er det viktigere å fokusere på god leselighet enn på korte navn. Det finnes noen begrensninger for hva vi kan kalle variablene våre. I figur 2.2 kan du se en oversikt over regler for variabelnavn.

Navnet kan ikke starte med et tall. Feil: var 2omgang; Riktig: var omgang2;

Navnet kan ikke være det samme som et nøkkelord (ord som brukes av JavaScript). Feil: var var; Riktig: var min_var;

Variabelnavn skiller mellom store og små bokstaver. Det vil si at navn og NAVN regnes som ulike variabelnavn.

Navnet kan ikke inneholde mellomrom, bindestreker eller punktum. Feil: var min-variabel.3; Riktig: var min_variabel3;

Tegnene æ, ø og å kan brukes i variabelnavn, men det kan være lurt å venne seg til å ikke bruke dem. I stedet for æ, ø og å kan ae, o og a brukes. Du bør for eksempel ikke bruke æ, ø eller å i filnavn.

For å bedre leseligheten av variabelnavn kan «camel case» brukes. Da bruker vi stor forbokstav i alle ord etter variabelens første ord. Alternativt kan understrek (_) brukes til å skille ord. JavaScript bruker camel case, det samme gjør vi i denne boka. Vanskelig å lese: var sumpoeng; Bedre: var sumPoeng;

Figur 2.2: En oversikt over lovlige variabelnavn i JavaScript.

Camel case og store bokstaver i JavaScript Camel case har fått sitt navn fordi de store forbokstavene kan minne om kamelenes pukler. Vi bruker ikke stor forbokstav i det første ordet fordi vanlige variabelnavn bør skrives med små bokstaver. Vi bruker altså camel case for å vise hvor et nytt ord dukker opp i et variabelnavn, for eksempel slik: var antallRiktige;. Du kan også møte på variabelnavn med bare store bokstaver. Denne skrivemåten brukes på variabler som ikke skal forandres i løpet av kjøringen av en app. Et eksempel på en konstant er π, som vi finner i matematikkobjektet (Math.PI).


28 Variabler, datatyper og operatorer

Oppgaver 2.1 Forklar om følgende variabelnavn er lovlige i JavaScript: vaRiaBEl, spiller.2, 3.plass, antall_deltagere, antallDeltagere, antall-deltagere og årstall. 2.2 Foreslå bedre alternativer for variabelnavnene i forrige oppgave. Forklar hvorfor du mener dine forslag er bedre. 2.3 Lag en variabel som har ditt favorittall som verdi. Skriv ut verdien til variabelen i konsollen. 2.4 Gjør om verdien til variabelen du laget i forrige oppgave. Hva blir variabelens verdi i konsollen nå? 2.5 Lag variabelen var tall = 142; og skriv console.log(tal);. Legg merke til feilmeldingen du får.

Semikolon i JavaScript I kode 2.1 på side 26 består JavaScript-koden av fire linjer: to kommentarer og to kodelinjer. Begge kodelinjene avsluttes med et semikolon (;). Vi bruker semikolon i JavaScript omtrent som vi bruker punktum i vanlig norsk språk. Et semikolon avslutter en setning (eng. statement), slik at JavaScript vet når en setning slutter. Vi kunne derfor ha skrevet kode 2.1 slik: var antall = 7; console.log(antall);

Men hvis vi dropper det første semikolonet, slik: var antall = 7 console.log(antall);

vil JavaScript tro at variabelen antall skal få verdien som ikke gir mening. Vi vil da få feilmeldingen Uncaught SyntaxError: Unexpected identifier i konsollen.

7 console.log(antall),

Ofte skyldes en feilmelding at vi har glemt et tegn (for eksempel et semikolon) i koden vår, eller at vi har brukt feil tegn.


Variabler, datatyper og operatorer

2.2

29

Datatyper

JavaScript har tre primære datatyper: number, string og boolean. Number er for tall, string er for tekst, og boolean er for enten sann eller usann (true eller false). Når vi skriver på et ark, er det lett for oss å vite hva som er et tall, og hva som er tekst. Det er ikke like lett for datamaskinen, som lagrer alt som 0er og 1-ere. Vi må derfor hjelpe til litt, slik at datamaskinen kan utføre de instruksjonene vi ønsker at den skal utføre. Datatyper lar oss gjøre det. I tillegg har JavaScript de to spesielle datatypene null og undefined. En variabel uten verdi (for eksempel var alder;) får datatypen undefined. Datatypen null brukes av noen programmerere for å angi at noe mangler innhold.

Datatypen number Tall i JavaScript er av datatypen number. Et tall kan være et heltall (42), et negativt tall (-12) eller et desimaltall (3.14). I mange programmeringsspråk skilles det mellom heltall og desimaltall, men i JavaScript er alle tall av datatypen number. En tallvariabel kan se slik ut (legg merke til at punktum brukes som desimalskilletegn, ikke komma): var andel = 0.72;

Datatypen string Tekst i JavaScript er av datatypen string. En tekst kjennetegnes ved at den er omsluttet av anførselstegn (") eller apostrofer ('). Det er ingen praktisk forskjell mellom de to; det er altså en smakssak om du ønsker å bruke anførselstegn eller apostrofer. En tekstvariabel kan se slik ut: var sitat = "May the force be with you";

En tekst i JavaScript er egentlig et String-objekt. Objekter i JavaScript har ofte egne verktøy, kalt metoder. I figur 2.3 kan du se eksempler på noen av String-objektets metoder. Det første du må legge merke til, er at alle tegn i en tekst har sitt eget nummer (en index), som starter på 0. Denne nummereringen gjør det lettere å manipulere tekst. Ved å bruke hakeparenteser kan vi hente ut enkelttegn (for eksempel sitat[17]), og vi kan bruke metoden indexOf for å lete etter et tegn i en tekst. Hvis tegnet finnes, får vi indexen til den første forekomsten av bokstaven. Hvis tegnet ikke finnes, får vi -1. Vi kan derfor bruke indexOf for å finne ut om et tegn finnes i en tekst. Vi får for eksempel bruk for String-objektets metoder når vi skal ha tekstfelter der brukere kan skrive inn informasjon. Hvis vi vil sjekke om en bruker har


30 Variabler, datatyper og operatorer

skrevet ordet «hei», er metoden toLowerCase verdifull. Det er nemlig slik at ingen av ordene «HeI», «heI», «hEI», «Hei», «hEi» eller «HEI» har samme verdi som «hei». Det vil si at JavaScript tolker ordene som forskjellige. Grunnen til det er at JavaScript skiller mellom store og små bokstaver (JavaScript er skiftskillende, eng. case sensitive). Hvis vi bruker metoden toLowerCase på teksten brukeren har skrevet inn, vil alle variantene av «hei» ovenfor, bli gjort om til «hei».

Figur 2.3: Oversikt over string-indekser og String-metoder.

Datatypen boolean En app består av en serie med instruksjoner. Noen av instruksjonene skjer hver gang programmet kjøres, mens andre bare vil skje i spesielle tilfeller. For å avgjøre om noe skal skje eller ikke, gjennomføres en test. For eksempel kan et program si «God morgen!» hvis tidspunktet er før kl. 09, og «God kveld!» hvis tidspunktet er etter kl. 18 på kvelden. I et slikt program vil koden inneholde et «spørsmål», for eksempel: «Er tidspunktet før kl. 09?». JavaScript vil da gi verdien true eller false, avhengig av hva tidspunktet er. Denne verdien kaller vi en boolean. Datatypen boolean har altså bare to mulige verdier: true (sann) eller false (usann). Vi bruker denne datatypen hver gang vi ønsker at et program skal foreta en avgjørelse.


Variabler, datatyper og operatorer

31

Oppgaver 2.6 Prøv å lage variabelen var tall = 2,71; (med komma, ikke punktum). Legg merke til feilmeldingen du får i konsollen. 2.7 Ta utgangspunkt i tekstvariabelen i figur 2.3. Hva blir resultatet av kodene nedenfor? a sitat[12]; b sitat.indexOf("e"); c sitat.substring(14,21); 2.8 Lag en ny tekstvariabel og bruk length for å finne tekstens lengde.

2.3

Operatorer

Et program vil inneholde mange forskjellige instruksjoner av ulik type. Noen ganger vil vi bruke matematikk, andre ganger vil vi undersøke om en variabel er av datatypen number eller string. En operator lar oss lage én verdi basert på flere verdier (for eksempel én sum fra fire tall). En setning som gjør om flere verdier til én verdi, kalles et uttrykk (eng. expression).

Aritmetiske operatorer (matematikk) De fire regneartene som vi kjenner fra matematikken, kalles aritmetiske operatorer. Operatorene som tilsvarer de fire regneartene, er + (pluss), - (minus), * (gange) og / (dele). var sum = 4 + 8; console.log(sum); // skriver ut 12 var differanse = 9 - 6; console.log(differanse); // skriver ut 3 var produkt = 8 * 7; console.log(produkt); // skriver ut 56 var kvotient = 15 / 3; console.log(kvotient); // skriver ut 5

Det er god praksis å bruke mellomrom på hver side av en operator, det gjør koden lettere å lese.


32 Variabler, datatyper og operatorer

Når vi skal utføre beregninger, er det viktig å ta hensyn til regnerekkefølge. Vi kan illustrere med et eksempel, der vi vil regne ut hvor mange flasker brus vi må kjøpe inn til et bursdagsselskap: var antallBarn = 8; var antallVoksne = 8; var brusPerGjest = 2; var antallBrus = antallBarn + antallVoksne * brusPerGjest; console.log(antallBrus); // skriver ut 24

Her prøver vi å legge sammen antall barn og antall voksne, for så å gange svaret med 2 (antall flasker brus per gjest). Vi forventer derfor at variabelen antallBrus får verdien 32, men hvis du prøver denne koden, vil du se at verdien blir 24. Grunnen til det er at gange (multiplikasjon) gjennomføres før pluss (addisjon). JavaScript følger regnerekkefølgen gange, dele, pluss og minus. For å påvirke regnerekkefølgen må vi bruke parenteser. I koden ovenfor må vi altså legge til parenteser i den nest nederste linjen, slik at antallBarn først legges til antallVoksne, før vi ganger med brusPerGjest: var antallBrus = (antallBarn + antallVoksne) * brusPerGjest; console.log(antallBrus); // skriver ut 32

I tillegg til de fire regneartene har vi de to operatorene inkrement (++) og dekrement (--). Dette er to nyttige operatorer som lar oss øke (inkrement) eller redusere (dekrement) en verdi med én. Vi kan for eksempel telle hvor mange ganger en knapp har blitt trykket: var antallTrykk = 0; // Når noen trykker på knappen antallTrykk++; // Øker variabelen antallTrykk med 1 console.log(antallTrykk); // skriver ut 1

Vi kommer til å bruke inkrement-operatoren i mange sammenhenger i denne boka.


Variabler, datatyper og operatorer

33

Vi har også den litt underlige modulusoperatoren (%), som gir oss resten ved deling: var rest1 = 9 % 3; // rest1 får verdien 0, fordi 9 delt på 3 er 3, med rest 0 var rest2 = 8 % 3; // rest2 får verdien 2, fordi 8 delt på 3 er 2, med rest 2

Vi bruker for eksempel modulus når vi ønsker å sjekke om et tall er delelig med et annet. Hvis vi får rest 0 når vi bruker modulus, vet vi at det første tallet er delelig med det andre. I tabell 2.1 vises en oversikt over aritmetiske operatorer (der var a = 7; og var b = 3;).

Operator

Beskrivelse

Eksempel

+

Addisjon

var c = a + b; // c = 10

-

Subtraksjon

var c = a - b; // c = 4

*

Multiplikasjon

var c = a * b; // c = 21

/

Divisjon

var c = b / a; // c = 0.428.....

%

Modulus (rest)

var c = a % b; // c = 1

++

Inkrement (øke med 1)

var c = a++;

// c = 8

--

Dekrement (minke med 1)

var c = a--;

// c = 6

Tabell 2.1: Aritmetiske operatorer (her er var a = 7; og var b = 3;).

Oppgaver 2.9 Undersøk (og forklar) hva som blir resultatet av kodene nedenfor (bruk konsollen): a 14 + 3 * 12 / 3

d (14 + 3) * (12 / 3)

b 42 - 9 / 3 * 4

e 14 % 3

c (14 + 3) * 12 / 3

f 20 % 4


34 Variabler, datatyper og operatorer

Tilordningsoperatorer Tidligere i kapitlet så vi på tilordningsoperatoren (=). Denne operatoren tilordner en verdi til en variabel. Det vil si at vi setter en variabels verdi lik noe, for eksempel var alder = 16;. Denne operatoren må ikke forveksles med likhetstegnet i matematikk. Vi har egne operatorer for å sammenligne verdier; dem går vi gjennom i neste kapittel. Det finnes andre tilordningsoperatorer som lar oss endre verdier. Du kan se en oversikt over disse i tabell 2.2. Som du kan se, er disse bare forenklede skrivemåter av de aritmetiske operatorene. Vi bruker disse som alternativer til inkrementoperatoren (++) og dekrementoperatoren (--) når vi ønsker å endre en variabel med noe annet enn én. Hvis vi for eksempel lager et basketballspill, og noen scorer en 3-poenger, kan vi løse det slik: var poeng = 0; poeng += 3; // øker poeng med 3 (poeng = poeng + 3) console.log(poeng); // skriver ut 3

Her kan vi lese += 3 som «legg til 3 til variabelen poeng». Verdien til variabelen poeng øker altså med 3. Tilsvarende betyr -= 4 at vi skal trekke fra fire. Vi kan også kombinere gange, dele og modulus med tilordningsoperatoren (*=, /= og %=).

Operator

Eksempel

Tilsvarer

=

a = 4

a = 4

+=

a += 3

a = a + 3

-=

a -= 5

a = a - 5

*=

a *= 2

a = a * 2

/=

a /= 7

a = a / 7

%=

a %= 3

a = a % 3

Tabell 2.2: Tilordningsoperatorer.

String-operatorer Hvis vi vil legge sammen to tall, kan vi bruke addisjonsoperatoren (+). Men hva skjer om vi legger sammen to tekster (strings)? var toTekster = "Hei" + " på deg!"; console.log(toTekster); // skriver ut "Hei på deg!"


Variabler, datatyper og operatorer

35

Resultatet blir én tekst: «Hei på deg!» Pluss-symbolet har altså flere anvendelser, som avhenger av datatypene vi bruker. Hvis datatypen er string, blir tekstene «limt» sammen til en lengre tekst. Vi sier at vi konkatenerer tekstene. Pluss-symbolet kalles derfor konkateneringsoperatoren når det brukes på tekst. Vi kan også bruke tilordningsoperatoren += på tekst: var tekst = "Hei"; console.log(tekst); // skriver ut "Hei" tekst += " på"; console.log(tekst); // skriver ut "Hei på" tekst += " deg!"; console.log(tekst); // skriver ut "Hei på deg!"

Denne operatoren konkatenerer («limer») ny tekst på eksisterende tekst. Resultatet av denne koden blir derfor «Hei på deg!». Legg merke til at vi må legge til mellomrom. Hvis vi ikke gjør det, får vi «Heipådeg!» i stedet. En vanlig feil skyldes at vi legger sammen det vi tror er to tall, mens det egentlig er et tall og en tekst: var tall = 17; var tekst = "42"; console.log(tall + tekst); // gir "1742" (en string)

Denne feilen oppstår vanligvis når vi har et tekstfelt der en bruker kan skrive inn informasjon. I slike tilfeller må vi være litt ekstra på vakt. På neste side presenteres typeof-operatoren, som lar oss sjekke en variabels datatype. Den kan vi bruke hvis vi er i tvil om en variabel inneholder en tekstverdi eller en tallverdi.

Oppgaver 2.10 Lag variabelen var poeng = 50;. Undersøk (og forklar) hva som skjer med variabelen hvis du skriver: a poeng += 10;

e poeng %= 7;

b poeng -= 10;

f poeng %= 5;

c poeng *= 2;

g poeng++;

d poeng /= 2;

h poeng--;

2.11 Lag variabelene var tekst1 = "Java"; og var tekst2 = "Script";, og bruk konkateneringsoperatoren (+) for å sette dem sammen til "JavaScript".


36 Variabler, datatyper og operatorer

typeof-operatoren En applikasjon kan være fri for feilmeldinger i konsollen, men likevel ikke fungere som vi ønsker. I slike tilfeller må vi bruke metoden console.log() underveis i koden vår for å finne ut hvor ting går galt. En vanlig feil er at vi prøver å gjøre beregninger på en variabel som ikke er av datatypen number. Det skjer for eksempel når en bruker har skrevet noe i et tekstfelt. For å avdekke en slik feil, kan vi bruke typeof-operatoren. Denne operatoren lar oss undersøke hvilken datatype en variabel har. Nedenfor vises noen eksempler: var tekst = "litt tekst"; console.log(typeof(tekst)); // gir "string" var tall = 17; console.log(typeof(tall)); // gir "number" var tall = "17"; console.log(typeof(tall)); // gir "string"

Datatypene object og array I tillegg til datatypene som er nevnt i dette kapitlet, har vi de to sammensatte datatypene array og object. De lar oss gruppere og organisere data. Hvis vi bruker typeof på arrayer og objecter, vil vi i begge tilfeller få Det skyldes at arrayer er objekter.

"object".

Vi kommer tilbake til disse senere i boka.

Gjøre om mellom string og number Noen ganger vil typeof fortelle oss at en variabel har en annen datatype enn det vi ønsker. Da må vi gjøre om datatypen. Vi kan bruke funksjonen String(), for å gjøre om til datatypen string: var tall = 42; console.log(typeof(tall)); // gir "number" var tekstFraTall = String(tall); console.log(tekstFraTall); // gir "42" console.log(typeof(tekstFraTall)); // gir "string"

Tilsvarende kan vi bruke funksjonen Number() for å gjøre om til datatypen number, men det vil bare fungere hvis det er et tall å hente: var tekst = "42"; console.log(typeof(tekst)); // gir "string"


Variabler, datatyper og operatorer

37

var tallFraTekst = Number(tekst); console.log(tallFraTekst); // gir 42 console.log(typeof(tallFraTekst)); // gir "number" var tekst2 = "1 414"; // mellomrom gir feil var tekst3 = "1,72"; // husk at punktum er desimaltegnet, komma gir feil console.log(Number(tekst2)); // gir NaN (Not a Number) console.log(Number(tekst3)); // gir NaN

For at funksjonen Number() skal fungere, må teksten ha en spesiell form som JavaScript kjenner igjen som tall. Oppgaver 2.12 Lag følgende variabler og bruk typeof for å finne variablenes datatyper: a var navn; b var alder = 2; c var alder = 2.718; d var navn = "Ola"; e var aarstall = "1984" 2.13 Lag variablene var tekst1 = "17";, var tekst1 = "20 17"; og var tekst1 = "4,7";. a Hvilken datatype har disse variablene? b Prøv å gjøre om variablenes datatype til tall (number). Hvorfor går det ikke med alle?


38 Variabler, datatyper og operatorer

Sammendrag En variabel er et navn som peker på en verdi i datamaskinens minne. Verdien til en variabel kan variere. Vi lager (deklarerer) en variabel med nøkkelordet var. Det finnes regler for navngiving av variabler. Navnet kan ikke starte med et tall, inneholde bindestreker eller punktum, eller være det samme som et JavaScript-nøkkelord. I tillegg bør vanlige variabelnavn skrives med små bokstaver. Variabelnavn som består av flere ord, bør deles opp ved å bruke stor forbokstav (camel case), for eksempel antallMulige i stedet for antallmulige. Vi bruker ikke stor forbokstav på det første ordet. Vi bruker tilordningsoperatoren (=) for å gi en variabel en verdi. Vi bruker semikolon (;) for å avslutte en setning i JavaScript. JavaScript bruker datatypene number (tall), string (tekst) og boolean (true/false) for å skille mellom ulike typer data. Datatypen string (tekst) har egne metoder som lar oss manipulere tekster: – indexOf() lar oss lete etter tegn i tekster – substring() lar oss dele opp en tekst – toUpperCase() gjør om alle bokstaver i en tekst til store bokstaver – toLowerCase() gjør om alle bokstaver i en tekst til små bokstaver En operator lar oss lage én verdi basert på flere verdier. Vi har aritmetiske operatorer (matematikk, se tabell 2.1), tilordningsoperatorer (se tabell 2.2), konkateneringsoperatoren for tekst (strings) og typeof-operatoren som lar oss finne datatypen til en verdi. Vi kan gjøre om fra tekst (string) til tall (number) med funksjonen Number(), og fra tall (number) til tekst (string) med funksjonen String().



40 Kontrollstrukturer (valg og løkker)

Kapittel 3

Kontrollstrukturer (valg og løkker)

Etter dette kapitlet skal du kunne • sammenligne verdier med sammenligningsoperatorer • gjennomføre valg med if-setninger • utvide if-setninger med else if og else • sammenligne flere variabler ved hjelp av logiske operatorer • lage tilfeldige tall med Math.random() • bruke while-løkker og for-løkker for å gjenta kode • finne antall forekomster av en bokstav i en tekst pssst Du finner videoer og andre ressurser på nettstedet på Lokus.

I forrige kapittel så vi på datatypen boolean, som enten er sann (true) eller usann (false). I dette kapitlet skal vi se på hvordan vi kan bruke denne datatypen til å styre hvilke deler av en kode som blir gjennomført. I figur 3.1 på neste side kan du se et enkelt gjettespill der brukeren skal gjette på et tall som gutten tenker på. Denne appen har ulike utfall, avhengig av hva brukeren skriver inn. Vi ønsker at det skal skrives ut korte beskjeder som angir om tallet brukeren gjetter, er for høyt, for lavt eller riktig.

3.1

Valg

For å kunne skrive ut «For høyt», «For lavt» eller «Riktig!» i gjettespillet, må vi altså foreta et valg. Vi må velge hva som skal skrives ut. I figur 3.2 kan du se et flytdiagram som illustrerer koden vi må skrive. En hendelse fyres av når knappen trykkes, deretter må vi lagre tallet som brukeren gjetter, i en variabel (som i fahrenheit-eksemplet i forrige kapittel). Når vi har brukerens tall, kan vi sammenligne det med det riktige tallet (tallet gutten tenker på).


Kontrollstrukturer (valg og løkker)

41

Figur 3.1: Hvilket tall tenker gutten på? Du kan prøve denne enkle appen på elevnettstedet.

Hvis tallene er like, skriver vi «Riktig!». Hvis de ikke er like, må vi sjekke om tallet er større enn det riktige tallet. Hvis det er større, skriver vi «For høyt». Hvis det ikke er større, skriver vi «For lavt». Et flytdiagram er et godt verktøy som kan brukes til å planlegge apper. Det blir lettere å skrive koden når vi vet hva vi ønsker at koden skal gjøre, og i hvilken rekkefølge koden skal utføres.

når knappen trykkes

motta tallet brukeren har skrevet

JA er riktig tall gjettet?

Skriv “Riktig!”

NEI

JA er tallet høyere enn riktig tall?

Skriv “For høyt”

NEI

Skriv “For lavt”

Figur 3.2: Flytdiagram som viser hvordan valg behandles i gjettespillet.

Vi skal snart se på hvordan vi foretar valg, men først må vi se på hvordan vi kan sammenligne verdier.


42 Kontrollstrukturer (valg og løkker)

Sammenligningsoperatorer I eksemplet på forrige side må vi ha en variabel som inneholder det «riktige» tallet: var riktigTall = 42;

Vi må også ha en variabel som inneholder tallet brukeren gjetter: var gjettet;

Vi kjenner ikke innholdet i variabelen gjettet og må derfor skrive kode som tar hensyn til det. La oss først anta at brukeren gjetter 17: gjettet = 17;

Nå kan vi sjekke om brukeren har gjettet riktig: console.log(gjettet == riktigTall); // skriver ut false

Legg merke til at vi her bruker to likhetstegn (==) for å sjekke om de to verdiene er like. Ikke glem at ett likhetstegn (=) tilordner verdier (setter venstresiden lik høyresiden). De to likhetstegnene er et eksempel på en sammenligningsoperator. I tabell 3.1 kan du se en oversikt over sammenligningsoperatorene vi finner i JavaScript.

Operator

Beskrivelse

Eksempel

==

Lik verdi

4 == "4"

// true

===

Lik verdi og lik datatype

4 === "4" // false

!=

Ikke lik verdi

7 != 5 // true

!==

Ikke lik verdi eller datatype

4 !== "4" // true

>

Større enn

5 > 3 // true

<

Mindre enn

3 < 3 // false

<=

Mindre enn eller lik

3 <= 3 // true

>=

Større enn eller lik

4 >= 5 // false

Tabell 3.1: Sammenligningsoperatorer.

De to likhetstegnene (==) sammenligner bare verdiene. Det vil si at de ikke sammenligner datatyper. For å sammenligne verdi og datatype må vi bruke tre


Kontrollstrukturer (valg og løkker)

43

likhetstegn (===). Det er derfor lurt å bruke tre likhetstegn ved sammenligning av variabler, fordi vi ofte ønsker at de også skal ha samme datatype. console.log(5 == "5"); // skriver ut true console.log(5 === "5"); // skriver ut false

Hvis vi vil undersøke om noe ikke er likt, kan vi bruke operatoren !=. Den sammenligner to verdier og gir true hvis de ikke er like. For å sammenligne både verdi og datatype kan vi bruke operatoren !==. Da får vi true hvis verdiene eller datatypene ikke er like: console.log(5 != 4); // skriver ut true console.log(5 != 5); // skriver ut false console.log(5 != "5"); // skriver ut false console.log(5 !== 4); // skriver ut true console.log(5 !== "5"); // skriver ut true

For å sjekke om tallet til brukeren i gjetteapplikasjonen er større enn eller mindre enn det riktige tallet, kan vi bruke > (større enn) og < (mindre enn): console.log(gjettet > riktigTall); // skriver ut false console.log(gjettet < riktigTall); // skriver ut true

Oppgaver 3.1 Lag først variablene var a = 3;, var b = 7;, var c = "7";. Hvilket utfall (true eller false) forventer du fra kodene nedenfor? Prøv kodene i konsollen etter at du har foreslått resultatet (du kan skrive kodene nedenfor rett inn i console.log();). a b == c

e a > b

b b === c

f a != b

c a < b

g b != c

d a <= b

h b !== c

3.2 Gjør om verdien til én av variablene i hver av deloppgavene ovenfor, slik at resultatet blir det omvendte av hva det er nå (altså at de deloppgavene som gir false, skal gi true, og omvendt).


44 Kontrollstrukturer (valg og løkker)

if-setninger Nå som vi har verktøyene vi trenger for å sammenligne verdier, kan vi skrive koden som foretar valget vårt. Til det bruker vi en if-setning (en «hvis»setning). Den fungerer for eksempel slik: Hvis du har gjort som du skal, får du en sjokolade. Men hvis du ikke har gjort som du skal, vil ingenting skje. Vi kan bruke en if-setning for å se om brukeren har gjettet riktig: if (gjettet === riktigTall) { console.log("Riktig!"); }

Vi skriver altså if etterfulgt av betingelsen vår i parenteser (gjettet === riktigTall). Hvis betingelsen er sann (true), vil det som står i sløyfeparentesene ({ og }) skje. Hvis betingelsen er usann (false), vil ingenting skje. Det vil si at maskinen hopper over koden i sløyfeparentesene og fortsetter med å utføre koden etter dem. Alternativt kan vi sjekke om gjettet tall ikke er lik det riktige tallet: if (gjettet !== riktigTall) { console.log("Feil!"); }

Vi kan bygge ut en if-setning med en else-bit (en «ellers»-bit), slik at noe også kan skje hvis betingelsen er usann (false): if (gjettet === riktigTall) { console.log("Riktig!"); } else { console.log("Feil!"); }

Her vil det som står i den første sløyfeparentesen skje hvis betingelsen (gjettet === riktigTall) er sann (true). Mens det som står i sløyfeparentesen etter else, vil skje hvis betingelsen er usann (false). For å fullføre eksemplet vårt må vi også undersøke om det gjettede tallet er mindre enn eller større enn det riktige tallet. Vi kan slå sammen flere ifsetninger ved å skrive else if for å få til det: if (gjettet === riktigTall) { console.log("Riktig!"); } else if (gjettet < riktigTall) { console.log("For lavt"); } else if (gjettet > riktigTall) { console.log("For høyt"); }


Kontrollstrukturer (valg og løkker)

45

Hvis den første betingelsen (gjettet === riktigTall) er sann, vil "Riktig!" bli skrevet ut. Hvis den første betingelsen er usann, og den andre betingelsen (gjettet < riktigTall) er sann, vil "For lavt" bli skrevet ut. Og hvis de to første betingelsene er usanne, og den tredje betingelsen (gjettet > riktigTall) er sann, vil "For høyt" bli skrevet ut. I dette eksemplet er egentlig siste del overflødig. Hvis tallet ikke er riktig eller hvis tallet ikke er for lavt, må tallet være for høyt. Vi kan derfor forenkle den siste delen av koden: if (gjettet === riktigTall) { console.log("Riktig!"); } else if (gjettet < riktigTall) { console.log("For lavt"); } else { console.log("For høyt"); }

Legg merke til at vi bruker else i stedet for else if nederst i denne koden. Vi trenger ikke å teste om tallet er høyere enn det riktige tallet fordi tallet må være høyere når det ikke er likt eller lavere.

Oppgaver 3.3 Du kan bare kjøre i en berg-og-dal-bane i en fornøyelsespark om du er over 100 cm høy. Skriv en if-setning som tester om en person er høy nok. 3.4 I en annen berg-og-dal-bane kan du bare kjøre om høyden din er over 100 cm og under 180 cm. Skriv en if-setning som tester om en person kan kjøre med denne berg-og-dal-banen.

Kodeblokker I kapittel 2 så vi på setninger (eng. statements) i JavaScript. En setning er en kodebit som avsluttes med et semikolon. I dette kapitlet bruker vi også sløyfeparenteser ({ og }) i koden vår. Kode som omsluttes av sløyfeparenteser, kalles en kodeblokk. En kodeblokk inneholder oftest én eller flere setninger kode, men de kan også være tomme. Sløyfeparentesene lar oss avgrense kode, slik at vi for eksempel kan bruke if-setninger for å angi hvilke kodeblokker som skal gjennomføres.


46 Kontrollstrukturer (valg og løkker)

Logiske operatorer Foreløpig har vi sett på hvordan vi kan sammenligne to variabler, men hva om vi ønsker å sammenligne flere samtidig? La oss si at vi har en nettside der en bruker skal skrive inn alderen sin. Da vil vi få variabelen var alder;. På nettsiden vil vi skrive ut en tekst hvis brukeren er en tenåring. Det kan vi gjøre med if-setninger slik: if (alder >= 13) { if (alder <= 19) { console.log("Du er en tenåring"); } }

Her har vi brukt to if-setninger inni hverandre, der vi først sjekker om brukeren er 13 år eller eldre (med operatoren større eller lik). Hvis brukeren er 13 år eller eldre, sjekker vi om hun er 19 år eller yngre (med operatoren mindre eller lik). Hvis hun også er 19 år eller yngre skriver vi ut meldingen "Du er en tenåring". Hvis vi i stedet bruker en logisk operator, kan vi få til det samme på en enklere måte. Logiske operatorer lar oss sjekke flere betingelser samtidig. I dette tilfellet kan vi bruke operatoren && (og) for å sjekke om alderen er 13 år eller høyere, og om alderen samtidig er 19 år eller lavere: if (alder >= 13 && alder <= 19) { console.log("Du er en tenåring"); }

I kapittel 2 så vi at en operator gir oss én verdi basert på flere verdier (for eksempel 2 + 3 = 5). En logisk operator gjør det samme, men med datatypen boolean. Vi gjør om flere boolean-verdier til én. I eksemplet ovenfor blir betingelsen alder >= 13 && alder <= 19 true så lenge både alder >= 13 og alder <= 19 gir true. Vi har også operatoren || (eller), som lar oss undersøke om minst én betingelse er sann. Vi kan for eksempel skrive ut en beskjed til alle som ikke er tenåringer: if (alder < 13 || alder > 19) { console.log("Du er ikke en tenåring"); }

Her blir teksten "Du er ikke en tenåring" skrevet ut hvis alderen er mindre enn 13 eller større enn 19. Altså hvis brukeren ikke er en tenåring. Det vil si at kodebiten alder < 13 || alder > 19 blir true hvis enten alder < 13 gir true, eller hvis alder > 19 gir true.


Kontrollstrukturer (valg og løkker)

-10

0

10

47

20

x > 3 && x < 13

-10

0

10

20

x < 2 || x > 11

Figur 3.3: Illustrasjon av de logiske operatorene && (og) og || (eller).

I figur 3.3 kan du se en illustrasjon av de to logiske operatorene && og ||. Det grønne området viser hvilke verdier for x som gjør at betingelsen blir sann. Det finnes også en tredje logisk operator, ! (ikke), som gir oss det omvendte av et resultat. Det vil si at true gjøres om til false, og omvendt: console.log(!true); // skriver ut false console.log(!false); // skriver ut true

Vi kommer ikke til å bruke denne operatoren så mye fordi vi ofte kan oppnå det vi ønsker, med sammenligningsoperatorene. Men den er veldig nyttig hvis vi har en verdi med datatypen boolean: var myndig = true;

Da kan vi bruke ! (ikke) for å skrive ut en beskjed til alle som ikke er myndige: if (!myndig) { console.log("Du er ikke myndig."); }

Operator

Beskrivelse

Eksempel

&&

Og (begge er sanne)

(a < 10 && b > 2) // true

||

Eller (én eller begge er sanne)

(a > 8 || b > 5) // false

!

Ikke

!(a > b) // false

Tabell 3.2: Logiske operatorer. Her er var a = 7; og var b = 3;.


48 Kontrollstrukturer (valg og løkker)

Oppgaver 3.5 Lag først variablene var a = 3; og var b = 7;. Hvilket utfall (true eller false) forventer du fra kodene nedenfor? Prøv kodene i konsollen etter at du har foreslått resultatet. a (a != 2) && (b < 10)

d (a === 2) || (b > 8)

b (a > 3) && (b === 7)

e !(b === 10)

c (a > 2) || (b === 8)

f !(b > a)

3.6 Bruk logiske operatorer for å løse oppgave 3.4.

Tilfeldige tall (Math.random) Vi har tidligere sett på de innebygde objektene Console og String. Et annet objekt vi har stor glede av, er matematikk-objektet Math. For å lage uforutsigbare situasjoner i spill og andre apper, bruker vi metoden Math.random(), som gir oss et tilfeldig tall som er større enn eller lik 0 og mindre enn 1. Vi får altså et nytt tilfeldig tall hver gang vi skriver Math.random(): console.log(Math.random()); // 0.013074025867976236 console.log(Math.random()); // 0.9319828796502934 console.log(Math.random()); // 0.8053305842128071

Tallene vi får, går fra og med 0, og opp til (men ikke med) 1. Hvis vi ønsker et av de hele tallene 0, 1 eller 2, kan vi gange det tilfeldige tallet med 3 og runde ned til nærmeste hele tall. Ganger vi et tall mellom 0 og 1 med 3, får vi et tall mellom 0 og 3. Siden tallet vi startet med, måtte være mindre enn 1, så ender vi med et tall som er mindre enn 3. Dersom vi runder ned, altså fjerner alle sifre etter komma, sitter vi igjen med 0, 1 eller 2. For å runde ned må vi bruke metoden Math.floor(): var tilfeldig = Math.random() * 3; // 1.06965824116858 console.log(Math.floor(tilfeldig)); // 1 tilfeldig = Math.random() * 3; // 2.568852403415863 console.log(Math.floor(tilfeldig)); // 2 tilfeldig = Math.random() * 3; // 0.08042433925622894 console.log(Math.floor(tilfeldig)); // 0

I figur 3.4 kan du se hvordan vi gjør om tallet vi får fra Math.random(), til andre tilfeldige tall.


Kontrollstrukturer (valg og løkker)

Math.random()

49

(større enn eller lik 0 og mindre enn 1)

Math.random() * 5

(større enn eller lik 0 og mindre enn 5)

(Math.random() * 5) + 5

(større enn eller lik 5 og mindre enn 10)

0

1

2

3

4

5

6

7

8

9 10 11 12 13 14

Figur 3.4: Vi kan bruke Math.random() til å lage tilfeldige tall.

Vi kan bruke tilfeldige tall når vi ønsker at noe tilfeldig skal skje. Hvis vi kombinerer et tilfeldig tall med en if-setning, kan vi for eksempel lage en «kompliment-generator»: var tilfeldig = Math.floor(Math.random * 3); if (tilfeldig === 0) { console.log("Du er den beste koderen i landet!"); } else if (tilfeldig === 1) { console.log("Steve Jobs kan ikke måle seg med deg!"); } else if (tilfeldig === 2) { console.log("Du er den neste Mark Zuckerberg!"); }

Oppgaver 3.7 Utfordring: En «Magic 8-ball» er en kule som svarer på typiske ja/nei-spørsmål, med svar som «Ja, garantert», «Ikke regn med det» og «Spør senere». Det vil si at vi for eksempel kan stille spørsmålet: «Vinner jeg i lotto til helgen?» og få svaret «Tviler på det». Lag din egen tekstbaserte «Magic 8-ball» som gir et tilfeldig svar.

SPØR SENERE

3.2

Løkker

Vi havner ofte i situasjoner der vi vil gjøre den samme operasjonen mange ganger. En løkke lar oss gjenta en kodeblokk et bestemt antall ganger, eller til en betingelse ikke lenger er sann (true). Det er blant annet løkker som gjør


50 Kontrollstrukturer (valg og løkker)

datamaskiner til så effektive regnemaskiner. Hvis vi for eksempel skal «telle» fra 1 til 100 uten en løkke, må vi skrive: console.log(1); console.log(2); console.log(3); // osv. osv. osv. console.log(99); console.log(100);

I stedet for å skrive 100 linjer med kode, kan vi gjøre dette raskt og enkelt ved å bruke en løkke. I denne boka er det i hovedsak to løkkevarianter vi bruker: while-løkker og for-løkker.

while-løkker En while-løkke lar oss gjenta noe så lenge en gitt betingelse er sann (true): while (betingelse er sann) { kode }

Vi lager en while-løkke med kodeordet while etterfulgt av en betingelse i parentes. Så lenge betingelsen er sann, blir koden mellom sløyfeparentesene ({ og }) gjentatt. Vi kan derfor «telle» til 100 med ganske få kodelinjer: var i = 1; while (i <= 100) { // Så lenge i er mindre enn eller lik 100 console.log(i); // Skriver ut verdien til i i++; // Øker verdien til i med 1 }

I koden ovenfor lager vi først variabelen i med verdien 1. Det er vanlig å bruke bokstavene i, j og k som variabelnavn for slike tellere i løkker. Deretter lager vi en while-løkke der koden gjentas så lenge verdien til i er mindre enn eller lik 100. I selve kodeblokken (i sløyfeparentesen) skrives i til konsollen før verdien til i økes med én (i++). Dette blir da gjentatt for verdiene 1, 2, 3, ..., 98, 99, 100, før løkken slutter (fordi i ikke lenger er mindre enn eller lik 100). I figur 3.5 vises deler av utskriften vi får fra denne koden. I eksemplet ovenfor vet vi hvor mange ganger løkken skal gjentas (100 ganger). En while-løkke brukes vanligvis når vi ikke vet hvor mange ganger noe skal gjentas. I figur 3.6 vises en illustrasjon av dette. Flytdiagrammet beskriver en robot som skal kjøre rett fram så lenge den ikke møter noen hindringer. Vi kan ikke vite hvor langt roboten må kjøre før den møter en hindring, så vi


Kontrollstrukturer (valg og løkker)

51

Figur 3.5: Her vises deler av resultatet av koden som teller til 100. Teksten til høyre i figuren viser filnavn og linjenummer.

vet ikke hvor mange ganger løkken skal gjentas, men vi vet når løkken skal stoppes (når roboten er mindre enn 10 cm fra en hindring). Derfor egner en while-løkke seg godt til denne oppgaven.

start

NEI Over 10 cm til hindring?

JA

slutt

Kjør videre

Figur 3.6: Flytdiagram for en robot som skal kjøre til den møter en hindring.

Vi kan bruke Math.random() for å se på en slik situasjon der vi ikke vet når en skal avsluttes. Husk at Math.random() gir et tall som er større enn eller lik 0 og mindre enn 1. Vi kan for eksempel «trekke» tilfeldige tall, helt til vi har et som er større enn eller lik 0,9:

while-løkke

var tall = 0; while (tall < 0.9) { // Så lenge tallet er mindre enn 0.9 console.log(tall); // Skriv ut tallet tall = Math.random(); // Trekk et nytt tall }


52 Kontrollstrukturer (valg og løkker)

for-løkker En for-løkke er ikke veldig ulik en while-løkke. Hvis vi vil telle til 100 med en kan vi skrive:

for-løkke,

for (var i = 1; i <= 100; i++) { console.log(i); }

Denne løkken starter med å lage variabelen i og gi den verdien 1. Deretter angir vi at løkken skal gjentas så lenge i er mindre enn eller lik 100 (i <= 100). Til slutt angir vi at verdien til i skal økes med én (i++) etter hver gjentagelse av løkken. Vi bruker semikolon for å skille mellom de tre delene. Vi angir altså en start-verdi, en stopp-betingelse og en endring: for (start; betingelse; endring) { kode }

I figur 3.7 vises en illustrasjon av hvordan denne løkken fungerer. Legg merke til at vi her gjør det samme som i while-løkken vi brukte tidligere i kapitlet. Forskjellen er at vi samler den delen av koden som har med gjentagelsen å gjøre, på ett sted. Dette gjør koden lettere å lese, og reduserer faren for feil. Hva tror du skjer hvis vi glemmer å øke verdien til i? Som nevnt tidligere bruker vi while-løkker når vi ikke vet hvor mange ganger noe skal gjentas. Hvis vi vet, eller lett kan finne, antall gjentagelser, bruker vi en for-løkke. I neste avsnitt skal vi se et eksempel på det.

start

NEI er i <= 100?

JA

slutt

i++

console.log(i);

Figur 3.7: Flytdiagram som viser gangen i en for-løkke.


Kontrollstrukturer (valg og løkker)

53

Oppgaver 3.8 Lag en while-løkke som skriver ut tallene fra 0 til 50. 3.9 Lag en for-løkke som skriver ut tallene fra 0 til 50. Sammenlign denne løkken med løkken du skrev i forrige oppgave. 3.10 Lag en løkke som skriver ut alle tallene i 3-ganger’n opp til 999 (0, 3, 6, 9, 12, 15 osv.). 3.11 Lag en løkke som skriver ut alle partallene mellom 1 og 100. 3.12 Lag en løkke som summerer alle tallene fra 1 til 100, men som bare skriver ut summen i konsollen. 3.13 Lag en løkke som skriver ut følgende til konsollen: # ## ### ####

(Hint: Lag en tom tekstvariabel før løkken, og husk at operatoren += også kan brukes på tekst.) 3.14 Gjør om på koden du skrev i forrige oppgave slik at den siste linjen inneholder 50 emneknagger (#). 3.15 Lag en løkke som skriver ut alle tallene fra −20 til 20 bortsett fra −13 og 13. 3.16 Lag en løkke som skriver ut alle versene i sangen «99 flasker med brus»: 99 flasker med brus på hylla, 99 flasker med brus. Ta en ned og send den rundt, 98 flasker med brus på hylla. 98 flasker med brus på hylla, 98 flasker med brus. Ta en ned og send den rundt, 97 flasker med brus på hylla. 97 flasker med brus på hylla, 97 flasker med brus. Ta en ned og send den rundt, 96 flasker med brus på hylla. ... Ingen flere flasker på hylla, ingen flasker igjen. Gå i butikken og kjøp noen flere, 99 flasker med brus på hylla. (Hint: Se på versene og se hva som endrer seg, og hva som er det samme.) 3.17 Utfordring: Lag en løkke som skriver ut tallene fra 1 til 100. Hvis et tall er delelig på 3, skal du i stedet for tallet skrive "Fizz", og hvis et tall er delelig på 5, skal du i stedet for tallet skrive "Buzz". Hvis et tall er delelig på både 3 og 5, skal du skrive "FizzBuzz". Husk at modulusoperatoren (%) lar oss undersøke om et tall er delelig på et annet.


54 Kontrollstrukturer (valg og løkker)

Lete gjennom en tekst I kapittel 2 så vi på datatypen string og metodene vi kan bruke på variabler med denne datatypen. Der så vi at vi kan finne lengden til en tekst ved å bruke egenskapen length: var sitat = "You shall not pass!"; console.log(sitat.length); // skriver ut 19

Vi kan også hente ut enkelttegn fra en tekst (husk at det første tegnet har index 0): var sitat = "You shall not pass!"; console.log(sitat[0]); // skriver ut "Y" console.log(sitat[2]); // skriver ut "u" console.log(sitat[9]); // skriver ut " " (et mellomrom)

Vi kan kombinere disse egenskapene med en for-løkke og skrive ut én og én bokstav: for (var i = 0; i < sitat.length; i++) { console.log(sitat[i]); }

Legg merke til at vi her starter å telle fra 0 (var i = 0), og at løkken skal holde på så lenge verdien til i er mindre enn sitatets lengde (i < sitat.length). Sitatets lengde er 19, men fordi tegnenes index starter på 0, har det siste tegnet index 18. Derfor gjentas løkken så lenge verdien til i er mindre enn sitatets lengde, og ikke mindre enn eller lik sitatets lengde. En naturlig anvendelse av for-løkker på tekster er å telle antall forekomster av en bokstav. String-objektet har metoden indexOf(), som lar oss finne ut om en tekst inneholder et tegn, men den metoden gir oss bare den første forekomsten av tegnet. For å finne antall forekomster kan vi bruke en for-løkke slik: var sitat = "You shall not pass!"; var antall = 0; for (var i = 0; i < sitat.length; i++) { if (sitat[i] === "s") { antall++; } } console.log(antall); // skriver ut 3


Kontrollstrukturer (valg og løkker)

55

Her lager vi variabelen antall utenfor løkken. Den skal vi bruke til å telle antall forekomster av bokstaven «s» i sitatet. I for-løkken bruker vi en if-setning for å undersøke om gjeldende bokstav er en «s» (if (sitat[i] === "s")). Hvis gjeldende bokstav er en s, øker vi antallet med én (antall++). Etter løkken skriver vi ut verdien til variabelen antall, som er 3, fordi bokstaven «s» har tre forekomster i sitatet. Oppgaver 3.18 Hvorfor bruker vi i < sitat.length (mindre enn) og ikke i <= sitat.length (mindre enn eller lik) i koden på forrige side? 3.19 Utfordring: Lag variabelen alfabet = "abcdefghijklmnopqrstuvwxyzæøå";. Du skal deretter lage en app som lager tilfeldige ord. Bruk Math.random() for å hente ut tilfeldige bokstaver fra teksten i variabelen alfabet. Lag noen tilfeldige ord med ulik lengde. 3.20 Utfordring: Du skal lage en krypteringsapp som gjør om en tekst ved å flytte alle bokstaver ett hakk til høyre i alfabetet. For eksempel vil teksten «hei» bli til «ifj». Her bør du lage en tom variabel som skal inneholde det nye ordet, og en løkke som går gjennom ordet som skal krypteres. For å få til dette kan du bruke metoden indexOf slik: var index = alfabet.indexOf("h"); var nyindex = index + 1; nyttord += alfabet[nyindex];

Hvis du vil ha med mellomrom og tegn i originalteksten, må du legge dem til i alfabetvariabelen. Du bør også legge til et ekstra tegn på slutten, slik at bokstaven «å» også kan krypteres, eller du kan behandle bokstaven «å» på en annen måte, for eksempel gjøre den om til en «a». Lag også en tilsvarende kode som finner det riktige ordet fra det krypterte ordet.


56 Kontrollstrukturer (valg og løkker)

Sammendrag Vi kan sammenligne verdier med sammenligningsoperatorer (se tabell 3.1 på side 42). En if-setning lar oss foreta valg: if (betingelse) { kode }

Vi kan utvide if-setninger med else if og else: if (betingelse 1) { kode } else if (betingelse 2) { kode } else { kode }

Med logiske operatorer (&&, || og !) kan vi gjøre flere tester samtidig, og vi kan gjøre om et testresultat til det motsatte av hva det var. Metoden Math.random() gir oss et tilfeldig tall som er større enn eller lik 0 og mindre enn 1. Metoden Math.floor() runder et tall ned til nærmeste heltall. En while-løkke lar oss gjenta kode så lenge en betingelse er sann: while (betingelse) { kode }

En for-løkke lar oss gjenta kode et bestemt antall ganger: for (start; betingelse; endring) { kode }


Document Object Model (DOM)

109

Kapittel 7

Document Object Model (DOM)

Etter dette kapitlet skal du kunne • forklare hvordan «Document Object Model» fungerer • hente HTML-elementer med JavaScript • redigere innhold i HTML-elementer med JavaScript • redigere attributtene til HTML-elementer med JavaScript • legge til nye HTML-elementer med JavaScript pssst

Foreløpig har vi stort sett jobbet med JavaScript og HTML/CSS hver for seg, men det er først når vi kombinerer disse tre språkene at vi kan lage fine og interaktive applikasjoner. I dette kapitlet skal vi se på hvordan vi kan hente ut og endre HTML-innhold med JavaScript.

7.1

Objekter i HTML-dokumentet

Når en nettleser skal vise fram en nettside, lager den en modell av nettsidens innhold. Denne modellen kalles Document Object Model (DOM). Den har fått dette navnet fordi modellen består av objekter som hører til HTMLdokumentet. Objektene er blant annet HTML-elementer. Det er denne modellen som lager grunnlaget for hva nettleseren viser fram, og det er innholdet i denne modellen vi kan redigere ved å bruke JavaScript. Det er altså Document Object Model vi redigerer med JavaScript, ikke selve HTML-koden. La oss se på et eksempel før vi går inn i detaljene. I kode 7.1 på neste side kan du se et enkelt eksempel på et HTML-dokument. Resultatet av koden kan du se i figur 7.1.

Du finner videoer og andre ressurser på nettstedet på Lokus.


110 Document Object Model (DOM)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

< ! doctype html > < html > < head > < title > Hjemmesiden min < / title > < meta charset= " UTF-8 " > < s tyle > . gul { background-color: yellow; } < / s tyle > < / head > < b ody > < h1 > Velkommen til siden min < / h1 > < p > P å denne siden kan du l æ re mer om meg . < / p > < p class= " gul " > Min favorittfarge er gul . < / p > < / b ody > < / html >

Kode 7.1: HTML-koden vi skal jobbe med i dette kapitlet.

Figur 7.1: Resultatet av kode 7.1 i en nettleser.

HTML-elementene i denne koden kan vi illustrere som i figur 7.2 på neste side. Vi har <html>-elementet, som inneholder alle de andre elementene, og vi har <head>-elementet, som inneholder <title> og <style>, og <body>-elementet, som inneholder en overskrift (<h1>) og to avsnitt (<p>). Disse elementene er alle eksempler på objektene som til sammen utgjør innholdet i «Document Object Model» til denne nettsiden. I tillegg til HTML-elementer, finner vi også HTML-attributter og tekstinnhold i «Document Object Model». I eksemplet ovenfor er altså class="gul" et eksempel på en attributtegenskap, mens teksten Velkommen til siden min er et eksempel på en tekstegenskap. Til sammen utgjør HTML-elementer, HTMLattributter og tekstinnhold et DOM-tre. DOM-treet for vårt eksempel kan du se i figur 7.3 på side 111. I «starten» av treet finner vi også «documentobjektet», som representerer hele dokumentet vårt. DOM-treet viser alt innholdet på nettsiden, og vi kan gå inn og redigere alle elementene i treet ved å bruke JavaScript. Vi kan også legge til og fjerne


Document Object Model (DOM)

111

html head title Hjemmesiden min style .gul { background-color: yellow; } meta charset="UTF-8" body h1 Velkommen til siden min p På denne siden kan du lære mer om meg. p class="gul" Min favorittfarge er gul.

Figur 7.2: Oversikt over DOM-objekter i kode 7.1.

elementer, slik at vi i teorien kan bygge opp fullstendige nettsider utelukkende ved hjelp av JavaScript. I resten av dette kapitlet skal vi se på hvordan vi kan opprette kontakt mellom JavaScript og DOM-treet, og hvordan vi kan bruke JavaScript til å gjøre endringer på det.

document

html

head

title meta

body document-objektet (hele siden)

Hjemmesiden min attributt (charset)

style

.gul { backgrou...

h1

Velkommen til siden min

p

På denne siden kan ...

p

attributt (class)

HTML-element attributt i HTML-element

Min favorittfarge er ...

Tekstinnhold i HTML-element

Figur 7.3: DOM-treet til kode 7.1.


112 Document Object Model (DOM)

Oppgaver 7.1 Hva er «Document Object Model» (DOM)? 7.2 Ta utgangspunkt i kode 7.1 og figur 7.3 og gi eksempler på hva slags innhold vi finner i «Document Object Model».

7.2

Hente HTML-elementer med JavaScript

For å redigere HTML med JavaScript må vi opprette en kontakt mellom JavaScript og HTML-koden. Vi må kunne angi hva som skal endres. For å gjøre det bruker vi metoden document.querySelector(). Vi skriver document først fordi «document-objektet» er objektet som representerer nettsiden din. Alle andre objekter hører til dette. Ordet «query» kan oversettes til «spørring», et ord som er kjent for dem som har lest Informasjonsteknologi 1. I metoden querySelector() «spør» vi etter HTML-elementer ved hjelp av CSS-selektorer. Vi kan for eksempel hente det første <p>-elementet som har CSS-klassen .gul, slik: document.querySelector(".gul");

Legg merke til at argumentet er en string (tekst), og at vi her tar med punktumet for å angi at det er snakk om en CSS-klasse. For å kunne gjøre noe med dette elementet er det lurt å lagre det i en variabel. Vi får ofte flere elementer å forholde oss til i JavaScript-koden, i tillegg til alle andre variabler, derfor er det lurt å skrive El (for element) i slutten av alle variabelnavn som inneholder elementer. Vi kommer til å bruke El konsekvent i denne boka. var avsnittEl = document.querySelector(".gul");

Vi kan nå se på innholdet i denne variabelen ved å skrive den til konsollen: console.log(avsnittEl); // Skriver ut: <p class="gul">Min favorittfarge er gul.</p>

Variabelen inneholder altså hele <p>-elementet. Vi kan bruke alle mulige CSS-selektorer i querySelector(). Det vil si at vi også kan skrive document.querySelector("p");. Da bruker vi selektoren for <p>elementer. Men hva skjer i dette tilfellet, når vi har flere <p>-elementer i samme DOM-tre? Jo, querySelector() velger da det første <p>-elementet i DOM-treet. For å hente alle <p>-elementene kan vi bruke metoden querySelectorAll(), men den venter vi med til kapitlet om arrayer (kapittel 11). Vi trenger nemlig noen ekstra verktøy for å behandle flere elementer samtidig.


Document Object Model (DOM)

113

For å skille ut ett av flere like elementer kan vi gi elementet en id (for eksempel <p id="mittAvsnitt">Tekst</p>). For å hente elementer basert på id-en deres bruker vi samme framgangsmåte som vi bruker i CSS-kode. Hvis vi har et element med id="mittAvsnitt", skriver vi document.querySelector("#mittAvsnitt"); (med emneknagg). getElementById, getElementsByClassName og getElementsByTagName I tillegg til querySelector() vil du også kunne se at noen bruker getElementById() (for å velge elementer med en gitt id), getElementsByClassName() (for å hente elementer med samme klassenavn), og getElementsByTagName() (for å hente elementer av samme type). Vi har valgt å bruke querySelector() og querySelectorAll() i denne boka, fordi de har en enklere skrivemåte, og fordi det bare blir én metodevariant å forholde seg til.

Oppgaver 7.3 Lag et HTML-dokument med samme innhold som i kode 7.1 på side 110. Legg også til en uordnet liste med tre listepunkter. 7.4 Bruk querySelector() og lag variabler som inneholder: a <h1>-elementet b <style>-elementet c <body>-elementet d <title>-elementet 7.5 Skriv ut alle variablene du laget i forrige oppgave, til konsollen. Sammenlign det som skrives ut med figur 7.3 på side 111. Forklar sammenhengen. 7.6 Lag variabelen var listepunktEl = document.querySelector("li"); og skriv den til konsollen. Forklar hvorfor variabelen har den verdien den har.

7.3

Redigere HTML med JavaScript

Nå som vi vet hvordan vi kan hente HTML-elementer, kan vi se på hvordan vi kan endre eksisterende elementer med JavaScript.

Endre innhold i HTML-elementer La oss si at vi ønsker å endre overskriften i kode 7.1 på side 110. Da må vi først hente elementet:


114 Document Object Model (DOM)

var overskriftEl = document.querySelector("h1");

Nå som vi har elementet i en variabel, kan vi bruke egenskapen innerHTML for å få tilgang til innholdet i <h1>-elementet: console.log(overskriftEl.innerHTML); // Skriver ut: Velkommen til siden min

Legg merke til at innerHTML er en egenskap som kan gis en verdi, og som da fungerer som en variabel, og ikke en metode/funksjon. Vi skriver derfor ikke parenteser etter innerHTML når vi bruker den. Vi kan bruke innerHTML til å gjøre om innholdet i et element: overskriftEl.innerHTML = "Hei på deg!";

Hvis vi nå skriver console.log(overskriftEl);, vil vi få <h1>Hei pådeg!</h1> skrevet ut i konsollen. I tillegg vil sidens overskrift umiddelbart forandres (prøv det selv!). Her har vi bare brukt egenskapen innerHTML til å endre teksten i overskriften, men vi kan også bruke innerHTML til å legge til nye HTML-elementer. Vi kan derfor gjøre om hele innholdet på siden vår om vi ønsker det: bodyEl = document.querySelector("body"); bodyEl.innerHTML = "<p> Dette avsnittet erstatter alt tidligere innhold </p>";

Med denne koden overskriver vi alt innholdet i <body>-elementet. Hvis vi vil legge til mye innhold, kan vi skrive det på en ryddig måte ved å bruke operatoren +=. Denne operatoren legger til mer innhold i stedet for å overskrive det: bodyEl = document.querySelector("body"); var tabell = ""; tabell += "<table>"; tabell += "<tr>"; tabell += "<th>Overskrift 1</th>"; tabell += "<th>Overskrift 2</th>"; tabell += "</tr>"; tabell += "<tr>"; tabell += "<td>Innhold 1</td>"; tabell += "<td>Innhold 2</td>"; tabell += "</tr>"; tabell += "</table>"; bodyEl.innerHTML += tabell;


Document Object Model (DOM)

115

Her lager vi en tabell ved å legge den til nederst i <body>-elementet. Legg merke til at vi først lager variabelen tabell, som vi fyller med tabellkoden vår. Hvis vi skriver denne koden direkte til <body>-elementet, vil nettleseren forsøke å avslutte tabellen så snart den møter koden <table>. Det fører til at resten av tabellkoden havner utenfor <table>-elementet. Vi unngår det problemet ved å først lagre koden i en variabel, for så å legge den til i <body>-elementet. Denne framgangsmåten vil du få bruk for i mange sammenhenger.

Legge til HTML-elementer med attributter I eksemplet ovenfor la vi til nye HTML-elementer (<table>, <tr>, <th> og <td>) med innerHTML. Vi vil ofte at nye elementer også skal ha attributter, både for å kunne hente dem med document.querySelector(), og fordi mange HTMLelementer må ha attributter for å fungere som de skal. Når vi bruker innerHTML for å legge til attributter, vil vi få en utfordring. Hva tror du vil skje om vi prøver oss på følgende kode? (Prøv det selv.) bodyEl = document.querySelector("body"); bodyEl.innerHTML += "<p id="mittAvsnitt">Et nytt avsnitt</p>";

Her får vi en feilmelding fordi nettleseren tror at teksten bare består av "<p id=". For å unngå denne feilen kan vi bruke apostrofer (') i stedet for anførselstegn ("): bodyEl = document.querySelector("body"); bodyEl.innerHTML += "<p id='mittAvsnitt'>Et gult avsnitt</p>";

Nå får vi et nytt avsnitt med attributtet id="mittAvsnitt". Dette elementet kan vi nå jobbe videre med ved å bruke document.querySelector("#mittAvsnitt");.

Oppgaver 7.7 Lag et HTML-dokument med en overskrift og to avsnitt. 7.8 Bruk JavaScript til å gjøre om teksten i overskriften og begge avsnittene. Her må du finne en løsning som lar deg hente ut to like HTML-elementer (de to <p>-elementene). 7.9 Legg til en ordnet liste nederst i <body>-elementet ved å bare bruke JavaScript. 7.10 Legg til en CSS-klasse i dokumentet som gir en rød kantlinje med tykkelse 3 piksler. 7.11 Legg til et nytt listepunkt nederst i listen ved å bare bruke JavaScript. Listepunktet skal bruke CSS-klassen du laget i forrige oppgave.


116 Document Object Model (DOM)

innerText I tillegg til egenskapen innerHTML kan du også møte på innerText når du ser på eksempler på Internett. Forskjellen på de to metodene er at innerText bare lar deg legge til tekst i et element, mens innerHTML lar deg legge til tekst og HTML. Det er derfor greit å bruke innerHTML uansett hva vi ønsker å legge til, for å spare oss for unødvendig forvirring.

Redigere attributter I tillegg til å redigere innholdet i HTML vil det være behov for å endre og legge til attributter i HTML-elementer. Husk at et HTML-attributt lar oss legge til ekstra informasjon i HTML-elementer, for eksempel bildefilen i et <img>element (med src-attributtet), eller hvor en lenke peker (med href-attributtet). I kode 7.2 har vi utvidet eksemplet fra kode 7.1 på side 110 med en ekstra CSS-klasse. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

< ! doctype html > < html > < head > < title > Hjemmesiden min < / title > < meta charset= " UTF-8 " > < s tyle > . gul { background-color: yellow; } . rosa { background-color: pink; } < / s tyle > < / head > < b ody > < h1 > Velkommen til siden min < / h1 > < p > P å denne siden kan du l æ re mer om meg . < / p > < p class= " gul " > Min favorittfarge er gul . < / p > < / b ody > < / html >

Kode 7.2: HTML-kode med CSS-klasser.

Nå kan vi hente ut <p>-elementet med klassen .gul, og bytte ut klassen med klassen .rosa: avsnittEl = document.querySelector(".gul"); avsnittEl.setAttribute("class", "rosa");

Her bruker vi metoden setAttribute for å angi verdien til et attributt. Vi skriver da både attributtets navn og attributtets nye verdi. NB! Legg merke til at vi ikke bruker punktum foran klassenavnet her. Det er fordi vi endrer HTMLattributtet, og der angis klassenavnene uten punktum. Vi kan også bruke setAttribute til å legge til attributter der vi ikke har angitt noen fra før:


Document Object Model (DOM)

117

overskriftEl = document.querySelector("h1"); overskriftEl.setAttribute("class", "rosa");

Her får overskriften (<h1>-elementet) også rosa bakgrunnsfarge. Hvis vi ønsker å ta bort et attributt, kan vi bruke metoden removeAttribute(): overskriftEl.removeAttribute("class");

Her angir vi bare attributtets navn fordi det ikke har noe å si hvilken verdi attributtet har, når vi bare skal fjerne det. Endre attributter med egenskaper Som et alternativ til setAttribute() i eksemplet ovenfor kan vi bruke className. De fleste attributtene vi omtaler i denne boka, har sine egne egenskaper som gjør det lettere å endre dem: avsnittEl = document.querySelector(".gul"); avsnittEl.className = "rosa";

Her bruker vi altså egenskapen className, og fordi det er en egenskap og ikke en metode, bruker vi likhetstegn for å endre den i stedet for parenteser. Det finnes også tilsvarende egenskaper for andre attributter, for eksempel href, id og src. I kode 7.3 på side 120 brukes både className og href. Det vil si at vi lett kan endre hvor en lenke peker, et elements id eller bildefilen som vises i et bilde. I neste kapittel skal vi se på hvordan vi kan bruke JavaScript til å styre hvilket bilde som vises.


118 Document Object Model (DOM)

Oppgaver Her skal du fortsette med HTML-dokumentet du jobbet med i oppgave 7.7 til 7.11. 7.12 Lag en ny CSS-klasse som gir en blå kantlinje med tykkelse 3 piksler. 7.13 Gi overskriften i dokumentet ditt den nye CSS-klassen. Bruk JavaScript og egenskapen className for å oppnå dette. 7.14 Bruk JavaScript for å ta bort CSS-klassen på listepunktet som fikk rød kantlinje i oppgave 7.10. 7.15 Legg til kodene <a id="lenkeNRK"></a> og <a id="lenkeTV2"></a> i dokumentet ditt. Legg til teksten NRK i det første <a>-elementet og TV2 i det andre <a>-elementet ved å bruke JavaScript. 7.16 Bruk metoden setAttribute() og legg til attributter slik at NRK-lenken fører til http://www.nrk.no og åpnes i et nytt vindu. 7.17 Bruk JavaScript og egenskapene href og target og legg til attributter slik at TV2-lenken fører til http://www.tv2.no og åpnes i et nytt vindu.

Redigere CSS-egenskaper direkte I kapittel 1 så vi at vi kan bruke CSS på tre ulike måter. Én av måtene involverte å skrive CSS i style-attributtet direkte i HTML-tagger: <p style="background-color: red;"> Et avsnitt </p>

Denne framgangsmåten anbefales ikke, fordi vi bør skille innhold (HTML) og utseende (CSS), men vi kan likevel bruke dette attributtet via JavaScript. Vi har allerede sett hvordan vi kan få tilgang til HTML-attributter med JavaScript: var avsnittEl = document.querySelector("p"); console.log(avsnittEl.style);

Den siste linjen ovenfor skriver ut alle CSS-egenskapene til avsnittet i konsollen. Nå som vi har style-attributtet, kan vi endre CSS-egenskaper: avsnittEl.style.backgroundColor = "blue";

Denne koden endrer egenskapen backgroundColor som vi finner i style-attributtet. Legg merke til at vi skriver backgroundColor og ikke background-color. Alle CSSegenskaper som bruker bindestrek, blir slått sammen på denne måten. Med denne framgangsmåten kan vi gå inn og redigere enkeltegenskaper i tillegg til å skifte ut CSS-klasser (som vi allerede har sett på).


Document Object Model (DOM)

7.4

119

Lage HTML-elementer med JavaScript

Vi har allerede sett at vi kan legge til HTML-elementer ved å bruke egenskapen innerHTML. Det finnes også en annen framgangsmåte vi kan bruke for å legge til nye elementer. I figur 7.4 vises gangen i denne varianten. Vi må først lage et nytt element, før vi kan gi elementet attributter og innhold, og legge det til i DOM-treet (og på nettsiden).

Lag et nytt element med createElement() Det nye elementet kan lagres i en variabel for senere bruk. Det blir ikke synlig på nettsiden før vi legger det til i DOM-treet.

Legg til attributter og innhold i det nye elementet.

Legg til elementet i DOM-treet med appendChild() Elementet kan legges til nederst i et annet HTMLelement i DOM-treet.

Figur 7.4: Prosessen som lar oss legge til nye HTML-elementer.

Lage elementene For å lage et nytt HTML-element bruker vi metoden document.createElement();: var nyttAvsnittEl = document.createElement("p");

Legg merke til at vi skriver "p" og ikke "<p>". Det nye elementet blir lagret i en variabel slik at vi kan jobbe videre med det. Vi kan også bruke denne variabelen til å gjøre endringer på elementet på et senere tidspunkt, slik som vi har sett på tidligere i dette kapitlet. Vi kan lage alle mulige HTML-elementer på denne måten, men hvis vi for eksempel ønsker å lage et <li>-element, er det viktig at det finnes et <ul>- eller <ol>-element som vi kan plassere det i.

Legge til innhold Når vi har laget et nytt element, kan vi redigere attributter og innhold, som vi har sett på tidligere. Vi kan for eksempel legge til litt tekst og CSS-klassen .gul: nyttAvsnittEl.innerHTML = "Dette avsnittet er splitter nytt!"; nyttAvsnittEl.className = "gul";


120 Document Object Model (DOM)

Her bruker vi egenskapen className, men metoden setAttribute() kan også brukes. Nå har vi et ferdig <p>-element med innhold og ett attributt. Det eneste som mangler, er å legge det til i DOM-treet, slik at det blir synlig på nettsiden.

Legge til elementer i DOM-treet For å legge til vårt nye <p>-element må vi ha et sted der det er mulig å legge det til. Det vil si at vi må finne et element som vi kan plassere det nye <p>elementet i. For å legge til et nytt element bruker vi metoden appendChild(), denne metoden legger til elementet nederst i elementet vi velger. Vi kan for eksempel legge det nye elementet nederst i <body>-elementet: var bodyEl = document.querySelector("body"); bodyEl.appendChild(nyttAvsnittEl);

I koden nedenfor bygger vi opp alt HTML-innhold med JavaScript. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

< ! doctype html > < html > < head > < title > Hjemmesiden min < / title > < meta charset= " UTF-8 " > < s tyle > . rosa { background-color: pink; } < / s tyle > < / head > < b ody > < / b ody > < s cript > // Henter body-elementet var bodyEl = document . querySelector ( " body " ) ;

15 16 17

// Lager et avsnitt var avsnittEl = document . createElement ( " p " ) ;

18 19 20

// Legger til tekst i avsnittet avsnittEl . innerHTML = " Les noen nyheter " ;

21 22 23

// Gir avsnittet CSS-klassen rosa avsnittEl . className = " rosa " ;

24 25 26

// Legger til avsnittet nederst i body-elementet bodyEl . appendChild ( avsnittEl ) ;

27 28 29

// Lager en lenke var lenkeEl = document . createElement ( " a " ) ;

30 31 32 33

// Legger til tekst i lenken lenkeEl . innerHTML = " her " ;


Document Object Model (DOM)

34 35

121

// Gir lenken en adresse lenkeEl . href = " http: // www . aftenposten . no " ;

36 37 38

// Bruker target-attributtet for å å pne lenken i nytt vindu lenkeEl . target = " _blank " ;

39 40 41

// Legger til lenken nederst i avsnittet avsnittEl . appendChild ( lenkeEl ) ;

42 43 44

// Legger til et punktum i slutten av avsnittet avsnittEl . innerHTML + = " . " ;

45 46 47

< / s cript > < / html >

Kode 7.3: Kode som bruker JavaScript til å lage HTML-innhold.

I koden ovenfor kan du se en fullstendig kode som utvider en tom HTML-side med en overskrift og et avsnitt som inneholder en lenke. Legg merke til at <body>-elementet i utgangspunktet er tomt. I figur 7.5 kan du se resultatet av denne koden i en nettleser.

Figur 7.5: Resultatet av kode 7.3 i en nettleser.

Oppgaver 7.18 Lag et tomt HTML-dokument med to CSS-klasser som gir ulike bakgrunnsfarger. 7.19 Bruk metodene createElement() og appendChild() for å lage en liste med fire listepunkter. Listepunktene skal bruke de to CSS-klassene du laget i forrige oppgave, slik at oddetallspunktene har den ene fargen, mens partallspunktene har den andre. 7.20 Gjenta det du gjorde i forrige oppgave, men bruk i stedet en løkke som lager listepunktene. Listen skal ha 12 listepunkter. Teksten kan være den samme i alle listepunktene. 7.21 Lag en funksjon som lager en liste som i forrige oppgave, men antallet listepunkter skal angis som et argument til funksjonen. 7.22 Utfordring: Lag en tabell med 3 kolonner og 4 rader med metodene createElement() og appendChild().


122 Document Object Model (DOM)

Sammendrag Alle HTML-elementer på en nettside, med innhold og attributter, utgjør nettsidens «Document Object Model» (DOM). Nettlesere representerer nettsider med et DOM-tre. Et DOM-tre har «grener» bestående av HTML-elementer, HTML-attributter og tekstinnhold. Vi kan velge elementer i DOM-treet med querySelector() ved å bruke CSS-selektorer (for eksempel elementnavn, klassenavn og id-navn). Metoden document.querySelector() lar oss hente HTML-elementer slik at vi kan redigere dem med JavaScript: bodyEl = document.querySelector("body");

Metoden document.querySelectorAll() lar oss hente alle HTML-elementer med lik CSS-selektor (for eksempel alle <p>-elementer). Vi kan bruke id-attributtet til å skille like HTML-elementer fra hverandre. Egenskapen innerHTML lar oss redigere innholdet i HTML-elementer: avsnittEl.innerHTML = "Ny tekst i avsnittet";

Vi kan bruke egenskapen innerHTML til å lage nye HTML-elementer: bodyEl.innerHTML = "<p>Et nytt avsnitt</p>";

Ved å bruke operatoren += kan vi legge til innhold i slutten av et element: bodyEl.innerHTML += "<p>Enda et nytt avsnitt</p>";

Egenskapene setAttribute(); og removeAttribute(); lar oss legge til og fjerne attributter fra HTML-elementer. De fleste attributter kan også endres med egenskaper (for eksempel avsnittEl.className = "gul"). Metoden createElement() lar oss lage nye HTML-elementer som senere kan legges til i DOM-treet: var nyttAvsnittEl = document.createElement("p");

Metoden addChild() lar oss legge til et HTML-element nederst i et annet HTML-element: bodyEl.addChild(nyttAvsnittEl);


Prosjekt: stein, saks, papir

137

Kapittel 9

Prosjekt: stein, saks, papir

Hva er et prosjekt? I denne boka har vi lagt inn fire prosjekter. De fungerer som repetisjon av teoristoffet som har blitt gjennomgått i forkant. Hensikten med prosjektene er å vise hvordan det vi har lært, kan brukes i praksis, og hvordan vi kan planlegge applikasjoner og dokumentere koden vår med god kommentering. Prosjektene har ingen oppgaver underveis; hensikten er at du skal gjenskape det som lages i kapitlet. Bakerst i hvert av prosjektkapitlene vil du også finne forslag til hvordan du kan utvide prosjektene. På elevnettstedet finner du koder og eventuelle filer vi bruker. P.S. Ikke la deg skremme av mengden kode. Koden kan deles opp i mange biter, som hver for seg er lette å forstå. pssst

Det første prosjektet vi skal se på, er spillet «stein, saks, papir». Vi skal lage en enkel versjon der brukeren spiller mot datamaskinen, men du kan selv utvide appen slik at du kan spille mot venner i stedet. Bildene som brukes i appen, finner du på elevnettstedet.

9.1

Planlegging

Det aller første vi må gjøre når vi skal lage en app, er å bestemme oss for hva vi ønsker at den skal gjøre. I dette kapitlet skal vi lage en «stein, saks, papir»-app, og da må vi bestemme oss for hvordan den skal fungere. Hvordan skal spilleren velge mellom stein, saks og papir? Skal vi ha to spillere, eller skal vi spille mot «datamaskinen»? Skal vi spille én gang, eller «best av fem»? Nedenfor skal vi gå gjennom tre steg i planleggingen av denne appen. Ved å planlegge en app i forkant, blir det mye lettere å lage den. Det er også mye lettere å legge inn alt vi ønsker, om vi vet hva det er på forhånd. Det kan være komplisert å gjøre om hovedfunksjonene i en app etter at den er laget.

Husk nettstedet på Lokus.


138 Prosjekt: stein, saks, papir

Kravspesifikasjon Det første vi bør ha på plass når vi skal lage en app, er en kravspesifikasjon. En kravspesifikasjon kan beskrives som en oversikt (spesifikasjon) over hva vi vil at en app skal kunne gjøre (krav). En enkel kravspesifikasjon for «stein, saks, papir»-appen kan se slik ut: • Spillet skal bestå av én spiller (brukeren) med «datamaskinen» som motspiller. • Spilleren velger stein, saks eller papir ved å klikke på ett av tre bilder. • Maskinen velger tilfeldig stein, saks eller papir. Maskinens valg skal vises fram i et fjerde bilde. • Spillet skal avsluttes når én av spillerne har fått fem poeng. Det skal være mulig å gjøre endringer på appen i framtiden, slik at spilleren selv kan velge vinnersummen. • Appen skal hele tiden vise hvor mange poeng spiller og maskin har. • Appen skal ha en vinnersum, altså antall runder som må vinnes for å vinne et spill. • Når spiller eller maskin har nådd vinnersummen, skal spillet avsluttes. Appen skal skrive ut en melding om hvem som vant. • Spillet skal oppleves som ferdig når spillet er slutt. Det vil si at spilleren ikke skal kunne klikke på noe lenger.


Prosjekt: stein, saks, papir

139

Skisse (wireframe) Nå som vi vet litt mer om spillet vi skal lage, kan vi lage en skisse av hvordan vi vil at det skal se ut. En slik skisse kalles ofte en wireframe. Nedenfor kan du se skissen vi kan bruke når vi skal lage «stein, saks, papir».

midtstilt innpakning

Spiller: poengsum Maskin: poengsum

Bilde av stein

Bilde av saks

Bilde av papir

Bilde av maskinens valg

Spillforklaring og beskjeder til spilleren

Det er mye lettere å skrive HTML-kode og CSS-kode når vi vet hva vi ønsker å lage. En skisse trenger ikke å være spesielt lekker; det går fint an å tegne en rask skisse på papir.

Pseudokode Det neste vi ønsker å gjøre, er å planlegge appens hendelsesforløp. Vi har tidligere sett på flytdiagrammer, og vi kan bruke et flytdiagram til å planlegge denne appen, men de blir fort veldig store, og da kan det være greit å bruke et alternativ. Her skal vi se et eksempel på pseudokode. Pseudokode er en slags «skisse» av koden vi ønsker å skrive. Det er som en forenklet kode, der vi ikke bryr oss om detaljene i skrivemåte, som parenteser og semikolon, men om hva som skal skje, og når det skal skje. Vi kan se på et lite eksempel først. I JavaScript skriver vi en if-setning slik:


140 Prosjekt: stein, saks, papir

if (alder >= 18) { console.log("Personen er myndig"); }

Den samme koden kan gjengis i pseudokode slik: IF alder >= 18 skriv beskjed: personen er myndig

Det finnes ingen faste regler for hvordan pseudokode skrives. Poenget er at man bruker et mer muntlig språk for å beskrive hva som skal skje i koden. Vi har valgt å bruke store bokstaver på reserverte ord, slik at pseudokoden blir lett å lese. Vi benytter også anledningen til å bestemme variabelnavn i pseudokoden. Du kan selv finne din egen «pseudokode-stil». Et forslag til pseudokode for «stein, saks, papir» kan se slik ut: Variabler: VINNERSUM (antall poeng som trengs for å vinne), spillerPoeng (spillers poengsum), maskinPoeng (maskinens poengsum), spillerValg (spillerens valg, velges ny hver runde), maskinValg (maskinens valg, velges ny hver runde) HENDELSE: spiller klikker på et bilde spillerValg = bildets id maskinValg = tilfeldig valg av stein, saks eller papir IF spillerValg === maskinValg Ingen får poeng (uavgjort) ELSE IF spillerValg === stein IF maskinValg === papir maskinPoeng++ ELSE spillerPoeng++ ELSE IF spillerValg === saks IF maskinValg === papir spillerPoeng++ ELSE maskinPoeng++ ELSE IF spillerValg === papir IF maskinValg === stein spillerPoeng++ ELSE maskinPoeng++ IF spillerPoeng === VINNERSUM || maskinPoeng === VINNERSUM Avslutt spillet

Det viktigste med denne pseudokoden er hvordan vi håndterer sammenligningen av spillervalget og maskinvalget. Når vi har klart for oss hvordan vi ønsker


Prosjekt: stein, saks, papir

141

å sammenligne de to, blir den faktiske koden mye lettere å skrive. Du kan se at vi bare sjekker ett alternativ for maskinen for hvert av spillerens valg. Det er fordi vi sjekker alle like valg helt i starten. Vi sjekker altså først om spilleren og maskinen har valgt det samme. Hvis de ikke har valgt det samme, gjenstår det bare to maskin-alternativer for hvert av spillerens alternativer. Det ene gir seier til maskinen, og det andre gir seier til spilleren. Konstanter Når vi lager en app, har vi som oftest noen variabler som skal endre verdi underveis i appen, og noen variabler som skal ha den samme verdien gjennom hele appen. Den siste varianten kaller vi for en konstant. I pseudokoden på forrige side har vi skrevet variabelen VINNERSUM med store bokstaver. Det er vanlig praksis å skrive konstantnavn på den måten. Da vet vi at vi har å gjøre med en variabel som vi ikke skal forandre underveis i koden. Et eksempel på en innebygd JavaScript-konstant er PI som vi finner i Mathobjektet (Math.PI).

9.2

Koding

Vi ønsker alltid å skille HTML, CSS og JavaScript når vi skriver kode. Ved å gjøre det kan vi fokusere på én ting av gangen. I dette prosjektet vil vi for eksempel først bestemme hvilket innhold appen skal ha, med HTML. Deretter vil vi gi appen et fint utseende med CSS. Til slutt vil vi gjøre appen interaktiv ved å legge til JavaScript.

HTML HTML-koden vi skal bruke i dette prosjektet, kan du se i kode 9.1 nedenfor. 1 2 3 4 5 6 7 8 9 10

< ! doctype html > < html > < head > < title > Stein , saks , papir < / title > < meta charset= " UTF-8 " > < s tyle > /* Her skal vi skrive CSS-koden */ < / s tyle > < / head > < b ody >

11 12

< div class= " innpakning " >

13 14 15 16

< p id= " spillerPoeng " > < b > Spiller: < / b > 0 < / p > < p id= " maskinPoeng " > < b > Maskin: < / b > 0 < / p >


142 Prosjekt: stein, saks, papir

17

18

19

20

< i mg src= " bilder / spiller_stein . png " width= " 200 " height= " 200 " alt= " spiller stein " id= " stein " > < i mg src= " bilder / spiller_saks . png " width= " 200 " height= " 200 " alt= " spiller saks " id= " saks " > < i mg src= " bilder / spiller_papir . png " width= " 200 " height= " 200 " alt= " spiller papir " id= " papir " > < i mg src= " bilder / maskin_ukjent . png " width= " 200 " height= " 200 " alt= " maskinens valg " id= " maskin " >

21 22

< p id= " info " > < / p >

23 24

< / div >

25 26 27 28 29 30

< s cript > /* Her skal vi skrive JavaScript-koden */ < / s cript > < / b ody > < / html >

Kode 9.1: HTML-koden vi skal bruke i dette prosjektet.

Her har vi tatt med det som alltid skal med i et HTML-dokument. Vi har også satt av plass til å skrive CSS (i <style>-elementet) og JavaScript (i <script>elementet). Innholdet på siden vår består av et <div>-element som skal fungere som «innpakning» for resten av innholdet. Inni innpakningen har vi først to <p>-elementer som skal vise hvor mange poeng spilleren og maskinen har. Nedenfor disse har vi fire <img>-elementer. Alle bildene er plassert i en mappe med navnet bilder. Det er tre spillerbilder: ett for stein, ett for saks og ett for papir, og ett maskinbilde. Spillerbildene lar spilleren velge mellom stein, saks og papir, mens maskinbildet skal vise maskinens valg. Nedenfor bildene har vi et <p>-element som skal gi informasjon til spilleren. Det foreløpige resultatet av appen vår ser slik ut:


Prosjekt: stein, saks, papir

143

CSS Nå som vi har innholdet på plass, kan vi gradvis utvikle appens design med CSS. Det første vi ønsker å gjøre, er å velge en skrifttype og den samme bakgrunnsfargen som bildene har: body { font-family: "Century Gothic", arial, sans-serif; background-color: #ecf0f1; }

Før vi går løs på innpakningen, skal vi se litt på bildenes CSS-egenskaper. De har nemlig betydning for hvor bred vi skal lage innpakningen. Alle bildene har bredde 200 piksler. I tillegg vil vi bruke margin: 10px; for å legge til luft rundt dem. Det fører til at bildene i praksis tar opp 220 piksler i bredden (10 piksler ekstra på venstre og høyre side). For å skille ut maskinens bilde vil vi legge til 100 piksler med ekstra luft på venstre side, til sammen 110 piksler. Summen av bildenes bredder og «luft» blir derfor 220 piksler ganger fire, pluss 100 ekstra piksler mellom spillerbildene og maskinbildet. Totalt 980 piksler. img { margin: 10px; } #maskin { margin-left: 110px; }

Legg merke til at vi skriver #maskin for å gi utseende til bildet som skal vise maskinens valg. Emneknaggen lar oss angi CSS for elementer som har fått en id. Selv om vi har angitt CSS for #maskin-elementet, får maskinens bilde fremdeles egenskapene vi har angitt for img. Det er bare margin-left som endres. Nå som vi kjenner innholdets bredde, kan vi gå løs på innpakningen. Vi har brukt en klasse for å angi CSS for innpakningselementet, og må derfor bruke punktum i CSS-koden vår. Bredden skal være 980px, i tillegg til at vi ønsker å midtstille innpakningen. Det gjør vi ved å bruke koden margin: 120px auto;. Her angir vi at vi ønsker 120 piksler med luft ovenfor og nedenfor elementet, og at vi ønsker «automatisk» luft (auto) på venstre og høyre side. Ved å bruke auto får vi like mye luft på venstre og høyre side – innpakningen blir derfor midtstilt. .innpakning { width: 980px; margin: 120px auto; }


144 Prosjekt: stein, saks, papir

Hvis du prøver denne koden, vil du se at vi ikke er helt i mål. Bildene ser ut til å ta mer plass enn vi har gitt dem, og de fordeler seg på to rader i stedet for én:

Det skyldes at vi har mellomrom i koden vår mellom bildeelementene. Disse mellomrommene tolkes som mellomrom i tekst, og vi får derfor ikke plass til bildene på én rad. Vi kan løse dette ved å øke innpakningens bredde, men vi kan også bruke et lite triks, nemlig å sette tekststørrelsen i innpakningen til 0: font-size: 0;

Problemet vårt er løst, men nå har all teksten forsvunnet. Det løser vi ved å angi tekststørrelse for <p>-elementene: p { font-size: 21px; }

Da har vi en layout som fungerer! Til slutt kan vi legge til en liten detalj som gjør appen mer intuitiv for brukeren. Ved å bruke cursor: pointer; kan vi gjøre om utseendet på musepekeren fra en pil til en hånd. Vi legger til denne egenskapen på bildene vi kan klikke på, slik at man får inntrykket av at de kan klikkes på: #stein, #saks, #papir { cursor: pointer; }

Appens ferdige layout, før vi skal begynne å skrive JavaScript-kode, ser nå slik ut:


Prosjekt: stein, saks, papir

145

JavaScript Nå har vi laget innholdet vårt med HTML, og vi har justert designet med CSS. Det eneste som mangler nå, er JavaScript-koden. Vi starter med å lage tre variabler: én for hvor mange poeng som trengs for å vinne spillet, én for poengsummen til spilleren, og én for poengsummen til maskinen. Vi må også hente ut elementene vi ønsker å jobbe med: de tre <p>-elementene og de fire <img>-elementene. Når vi har disse elementene, kan vi skrive ut en «bruksanvisning» i det nederste <p>-elementet, og vi kan knytte clickhendelser til de tre bildene spilleren kan klikke på. Når click-hendelsene fyres av, skal funksjonen sjekkResultat() utføres. Summen av alt dette gir oss kode 9.2 nedenfor. 1 2

// Antall poeng for å vinne var VINNERSUM = 5;

3 4 5

// Spillerens poengsum var spillerPoeng = 0;

6 7 8

// Maskinens poengsum var maskinPoeng = 0;

9 10 11 12 13

// Lager variabler for de tre p-elementene var spillerPoengEl = document . querySelector ( " # spillerPoeng " ) ; var maskinPoengEl = document . querySelector ( " # maskinPoeng " ) ; var infoEl = document . querySelector ( " # info " ) ;

14 15 16 17 18 19

// Lager variabler for de fire img-elementene var steinEl = document . querySelector ( " # stein " ) ; var saksEl = document . querySelector ( " # saks " ) ; var papirEl = document . querySelector ( " # papir " ) ; var maskinEl = document . querySelector ( " # maskin " ) ;

20 21 22 23 24

// Legger til click-hendelser p å de tre spillerbildene steinEl . addEventListener ( " click " , sjekkResultat ) ; saksEl . addEventListener ( " click " , sjekkResultat ) ; papirEl . addEventListener ( " click " , sjekkResultat ) ;

25 26

// Skriver ut " bruksanvisningen " i info-elementet


146 Prosjekt: stein, saks, papir

27

infoEl . innerHTML = " Velg stein , saks eller papir . F ø rstemann til " + VINNERSUM + " poeng vinner spillet . " ;

28 29 30 31 32

// Funksjonen som skal h å ndtere klikkene v å re function sjekkResultat ( e ) { console . log ( " Du klikket p å et bilde . " ) ; }

Kode 9.2: Utgangspunktet for JavaScript-koden vår.

Foreløpig skrives det bare en kort beskjed til konsollen når vi klikker på bildene. Vi må derfor legge til mer kode i sjekkResultat()-funksjonen for at appen skal fungere. Vi starter med å registrere hva spilleren velger. I forrige kapittel så vi på hendelsesobjektet der vi kan finne nyttig informasjon om en hendelse. Vi kan finne ut hvilket bilde brukeren klikket på, ved å bruke dette objektet. I funksjonen sjekkResultat(e) sender vi med hendelsesobjektet (e). I objektets targetegenskap finner vi elementet vi klikket på, og i elementets id-egenskap kan vi finne id-en til elementet vi klikket på. Vi kan derfor finne id-en til bildet vi klikket på, med koden e.target.id. I kode 9.3 nedenfor starter vi med å gjøre det. Vi skriver også ut verdien vi får til konsollen, for å være sikker på at koden fungerer. Det er alltid lurt å skrive ut slik informasjon til konsollen underveis. Da blir det mye lettere å oppdage feil. 1 2

// Finner f ø rst bildet som ble klikket p å var spillerValg = e . target . id;

3 4 5

// Skriver valgt bilde til konsollen console . log ( " Spiller valgte: " + spillerValg ) ;

Kode 9.3: Første utvidelse av sjekkResultat(e)-funksjonen.

Vi vet nå hva spilleren har valgt, men maskinen må også gjøre et «valg». Det gjør vi ved å bruke Math.random(), som vi har sett på tidligere i boka. Vi velger tilfeldig fra tallene 1, 2 og 3, hvor vi lar 1 representere stein, 2 representere saks og 3 representere papir. Senere vil vi løse dette ved å bruke noe som kalles en «array», men fordi vi ikke har gått gjennom dem enda, løser vi det heller på denne måten. Vi lagrer maskinens valg i variabelen maskinValg, og skriver det ut til konsollen for å se at vi har gjort alt riktig. Til slutt setter vi sammen maskinvalget med teksten "bilder/maskin_" og ".png" for å angi en filbane til et bilde som stemmer overens med maskinens valg. Dette bildet setter vi inn i stedet for spørsmålstegnet som foreløpig har representert maskinens valg. Funksjonen sjekkResultat(e) skal nå inneholde koden fra kode 9.3 ovenfor og kode 9.4 på neste side.


Prosjekt: stein, saks, papir

1 2

147

// Velger tilfeldig for maskinen var tilfeldig = Math . floor ( Math . random () * 3) + 1;

3 4 5

// Lagrer maskinens " valg " i en variabel var maskinValg;

6 7 8 9 10 11 12 13 14

// Gj ø r om maskinens valg til riktig bildefil if ( tilfeldig === 1) { maskinValg = " stein " ; } else if ( tilfeldig === 2) { maskinValg = " saks " ; } else if ( tilfeldig === 3) { maskinValg = " papir " ; }

15 16 17

// Skriver maskinens valg til konsollen console . log ( " Maskinen valgte: " + maskinValg ) ;

18 19 20

// Endrer maskinens bilde maskinEl . src = " bilder / maskin_ " + maskinValg + " . png " ;

Kode 9.4: Andre utvidelse av sjekkResultat(e)-funksjonen.

Vi valgte id-ene «stein», «saks» og «papir» for spillerbildene, og nå har vi også verdien «stein», «saks» eller «papir» i variabelen maskinValg. Det betyr at vi kan sammenligne spillervalget og maskinvalget for å finne ut hvem som vinner. For å sammenligne valgene bruker vi if-setninger. Vi sjekker først om spiller og maskin har valgt det samme. Hvis det er tilfellet, blir det uavgjort, og ingen skal få poeng. Deretter sjekker vi om spiller har valgt «stein», «saks» eller «papir», og for hver av de tre sjekker vi hva maskinen har valgt. Vi trenger ikke å sjekke om maskinen har valgt det samme, fordi vi gjorde det i begynnelsen. Hvis spilleren vinner, øker vi hennes poengsum med én (spillerPoeng++). Hvis maskinen vinner, øker vi maskinens poengsum med én (maskinPoeng++). Til slutt skriver vi ut poengsummene på nettsiden. Funksjonen sjekkResultat() skal nå også utvides med kode 9.5 på neste side.


148 Prosjekt: stein, saks, papir

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

// Sammenligner for å avgj ø re vinner if ( spillerValg === maskinValg ) { // Ingen vant , gj ø r ingenting } else if ( spillerValg === " stein " ) { if ( maskinValg === " saks " ) { // Spiller vant spillerPoeng ++; } else if ( maskinValg === " papir " ) { // Maskin vant maskinPoeng ++; } } else if ( spillerValg === " saks " ) { if ( maskinValg === " papir " ) { // Spiller vant spillerPoeng ++; } else if ( maskinValg === " stein " ) { // Maskin vant maskinPoeng ++; } } else if ( spillerValg === " papir " ) { if ( maskinValg === " stein " ) { // Spiller vant spillerPoeng ++; } else if ( maskinValg === " saks " ) { // Maskin vant maskinPoeng ++; } }

29 30 31 32

// Endrer poengsummene i skjermbildet spillerPoengEl . innerHTML = " < b > Spiller: < / b > " + spillerPoeng; maskinPoengEl . innerHTML = " < b > Maskin: < / b > " + maskinPoeng;

Kode 9.5: Tredje utvidelse av sjekkResultat(e)-funksjonen.

Nå er vi nesten i mål med spillet vårt. Det eneste som mangler, er at spillet tar slutt når spilleren eller maskinen har nådd poengsummen som gir seier. På slutten av funksjonen sjekkResultat() må vi derfor legge inn en test der vi undersøker om noen har vunnet. Hvis spillerens eller maskinens poengsum er lik vinnersummen, skal spillet avsluttes. Vi avslutter spillet ved å bruke removeEventListener() slik at vi ikke kan klikke på bildene lenger. Vi gjør også om musepekerens utseende, slik at det ikke lenger ser ut som om vi kan klikke på bildene. Til slutt skriver vi ut en tekst som angir hvem som vant. Denne siste koden finner du i kode 9.6 på neste side. Legg til den nederst i funksjonen sjekkResultat(), og appen er ferdig!


Prosjekt: stein, saks, papir

1 2 3 4 5 6 7

149

// Sjekker om noen har vunnet if ( spillerPoeng === VINNERSUM || maskinPoeng === VINNERSUM ) { // Noen har vunnet // Fjerner hendelser , slik at man ikke kan klikke p å bildene steinEl . removeEventListener ( " click " , sjekkResultat ) ; saksEl . removeEventListener ( " click " , sjekkResultat ) ; papirEl . removeEventListener ( " click " , sjekkResultat ) ;

8

// Endrer musepekeren , slik at bildene virker " deaktiverte " steinEl . style . cursor = " auto " ; saksEl . style . cursor = " auto " ; papirEl . style . cursor = " auto " ;

9 10 11 12 13 14 15 16 17 18 19 20

}

// Skriver ut en avslutningsbeskjed if ( spillerPoeng === VINNERSUM ) { infoEl . innerHTML = " Gratulerer ! Du vant ! " ; } else { infoEl . innerHTML = " Auda ! Du ble sl å tt av en maskin ... " ; }

Kode 9.6: Fjerde utvidelse av sjekkResultat(e)-funksjonen.

Feilsøking I dette kapitlet har du sett at vi bruker console.log() for å undersøke hva som faktisk skjer underveis i koden. Det er alltid lurt å gjøre dette når vi skriver kode. Når alt fungerer som det skal, kan du fjerne console.log()kodene igjen, om du ønsker det. Det er ikke alltid så lett å se hvilke feil vi har gjort, men ved å bruke console.log(), kan vi med én gang se om det vi tror skal skje, faktisk skjer.


150 Prosjekt: stein, saks, papir

Forslag til utvidelser I dette kapitlet har vi laget en app som lar oss spille «stein, saks, papir» mot maskinen vår. Som ekstra utfordringer kan du prøve å forbedre appen din med de to forslagene nedenfor. En mer elegant løsning

Appen vi har laget i dette kapitlet, har én svakhet i brukergrensesnittet. Det er ikke alltid like lett å se at maskinen har gjort et valg, spesielt hvis den velger det samme to ganger på rad. Vi kan løse det ved å vise bildet med spørsmålstegnet mellom hver runde. Det finnes en funksjon som egner seg godt til dette, nemlig setTimeout(). Denne funksjonen lar oss utføre noe etter en viss tid. Vi kan for eksempel skrive en ny funksjon som endrer maskinens bilde tilbake til spørsmålstegnet: function tilbakestillMaskin(){ // Endrer maskinens bilde tilbake til spørsmålstegnet maskinEl.src = "bilder/maskin_ukjent.png"; }

Denne funksjonen kan vi utføre for eksempel 1,5 sekunder etter at maskinen har gjort sitt valg. Funksjonen setTimeout() bruker to argumenter: funksjonen som skal utføres, og hvor lenge den skal vente før funksjonen skal utføres (i millisekunder): setTimeout(tilbakestillMaskin, 1500);

Problemet er løst, men det dukker opp et nytt problem: spilleren må vite at han skal vente i 1,5 sekunder. Kan du legge til dette i koden din, i tillegg til å hindre spilleren i å gjøre noe i 1,5 sekunder? To spillere

Lag en versjon av «stein, saks, papir» for to spillere. Det vil si at to brukere kan spille mot hverandre, i stedet for at én bruker spiller mot maskinen.


Brukerinput

151

Kapittel 10

Brukerinput

Etter dette kapitlet skal du kunne • lage tekstfelter, tallfelter, tekstbokser, nedtrekksmenyer, radioknapper, avkrysningsbokser og knapper med HTML • gi utseende til tekstfelter, tallfelter, tekstbokser, nedtrekksmenyer, radioknapper, avkrysningsbokser og knapper med CSS • registrere brukerinput med JavaScript pssst

I forrige kapittel lot vi brukeren velge mellom stein, saks og papir ved å klikke på bilder. I dette kapitlet skal vi se på hvordan vi kan la brukeren angi informasjon i form av blant annet tekst og tall. Vi skal ta utgangspunkt i et eksempel der vi kan registrere informasjon om filmer vi har sett. I figur 10.1 på neste side kan du se sluttproduktet som vi skal lage. For å holde bokas tekst så oversiktlig som mulig, har vi valgt å gå gjennom HTML, CSS og JavaScript for brukerinput hver for seg. Det betyr at du kan ha glede av å hoppe litt fram og tilbake i kapitlet hvis du for eksempel ønsker å bruke JavaScript med en gang.

10.1

Brukerinput med HTML

Det finnes mange ulike felter vi kan bruke for registrering av informasjon, og med HTML5 kom det enda flere. I denne boka kommer vi til å holde oss til de feltene du kan se i figur 10.1. De er oppsummert på side 170 sammen med et utvalg andre felter som ikke blir gjennomgått noe videre i denne boka.

Du finner videoer og andre ressurser på nettstedet på Lokus.


152 Brukerinput

Figur 10.1: Et skjema som lar oss registrere informasjon om filmer vi har sett.

Tekst og tall Det vanligste alternativet for å la en bruker skrive inn tekst, er et enkelt tekstfelt. Vi lager et tekstfelt med <input>-elementet og attributtet type="text": Tittel <input type="text">

Her har vi også skrevet «Tittel» foran tekstfeltet, slik at brukeren kan vite hva som skal skrives i feltet. Uten noe CSS ser tekstfeltet slik ut:

Her kan du se at det står «Filmens tittel» i lys tekst inni tekstfeltet. Det er en «placeholder»-tekst, som kan brukes til å gi mer informasjon til brukeren. «Placeholder»-teksten forsvinner når vi klikker i feltet. For å angi en «placeholder»-tekst bruker vi attributtet placeholder: Tittel <input type="text" placeholder="Filmens tittel">


Brukerinput

153

Legg merke til at <input>-elementet består av bare én tagg (ingen avslutningstagg). Et slikt tekstfelt egner seg best til korte tekster. Hvis vi ønsker å la brukeren skrive inn mye tekst, kan vi i stedet bruke <textarea>-elementet: Beskrivelse <textarea placeholder="Skriv en kort beskrivelse av filmen"></textarea>

I motsetning til <input>-elementet bruker dette elementet en avslutningstagg. En tekstboks ser slik ut:

For å skrive inn tallverdier kan vi bruke et <input>-element med attributtet type="number": Årstall <input type="number">

Dette feltet ser ut som et tekstfelt, men nettleseren vil forvente en tallverdi. På smarttelefoner skiftes ofte tastaturet ut med tall, slik at det blir lettere for brukeren. Her kan vi ikke bruke «placeholder»-attributtet. Det er uansett ikke stor hjelp i en «placeholder» her, fordi den må i så fall være et tall. Et tallfelt ser slik ut:

Skjemaer og validering Hvis du har lest boka Informasjonsteknologi 1, husker du kanskje at vi brukte <form>-elementet når vi skulle ha brukerinput. Da var hensikten med skjemaet at vi skulle gå til en ny side når vi klikket på knappen. Når vi bruker JavaScript, ønsker vi ikke å gå til en ny side. For å bruke et <form>-element må vi derfor hindre nettleseren i å gå til en ny side når vi klikker på knappen. Det er mulig, men det gjør koden mer komplisert enn nødvendig. En fordel med å bruke et <form>-element er at nettleseren hjelper oss med validering. Vi vil for eksempel få en feilmelding hvis vi lar et nødvendig felt stå tomt. Alternativet er å lage egne feilmeldinger. Du kan finne en oppskrift på elevnettstedet på hvordan vi kan bruke HTML5 og <form>elementet til å validere brukerinput.


154 Brukerinput

Nedtrekksmenyer Hvis vi ønsker å la en bruker velge ett alternativ fra flere forhåndsbestemte alternativer, kan vi bruke en nedtrekksmeny. En nedtrekksmeny lages med et <select>-element, og alternativene i menyen lages med <option>-elementer: Aldersgrense <select name="aldersgrense"> <option value="alle">Tillatt for alle</option> <option value="syv">7 år</option> <option value="elleve">11 år</option> <option value="femten">15 år</option> <option value="atten">18 år</option> </select>

Her lar vi brukeren velge en aldersgrense for filmen som skal registreres. Vi kan bare velge ett av alternativene. I nettleseren ser nedtrekksmenyen slik ut:

Datalist Med HTML5 kom det et alternativ til nedtrekksmenyer, nemlig datalister. En dataliste er en kombinasjon av et tekstfelt og en nedtrekksmeny. Det vil si at vi kan angi forslag til hva brukeren kan skrive, men at det fortsatt er mulig å skrive noe annet. Datalisten vil autofullføre teksten hvis brukeren skriver et ord som finnes i listen. Vi bruker en dataliste slik: <input list="drikke"> <datalist id="drikke"> <option value="Kaffe"> <option value="Te"> <option value="Vann"> <option value="Brus"> </datalist>

Det er id-attributtet i <datalist>-elementet som kobler listen til <input>elementet.


Brukerinput

155

Avkrysningsbokser og radioknapper Vi kan bruke avkrysningsbokser når vi vil at brukeren skal kunne velge flere alternativer fra en liste med forhåndsbestemte alternativer. Det vil si at vi kan bestemme noen alternativer, og så kan brukeren velge ingen eller flere av disse alternativene. Vi lager avkrysningsbokser med <input>-elementet og attributtene name, value og type="checkbox": Format <br> <input type="checkbox" name="format" value="kino"> Kino <br> <input type="checkbox" name="format" value="dvd"> DVD <br> <input type="checkbox" name="format" value="strommetjeneste"> Strømmetjeneste <br> <input type="checkbox" name="format" value="tv"> TV <br> <br>

Vi knytter avkrysningsboksene sammen ved å gi dem det samme navnet (name), men hver av boksene får sin unike verdi (value). I tillegg har vi lagt til <br>tagger for å gjøre visningen mer ryddig. En <br>-tagg legger til et linjeskift. I nettleseren ser avkrysningsboksene slik ut:

Radioknapper har samme funksjon som nedtrekksmenyer, det vil si at brukeren bare kan velge ett alternativ fra flere forhåndsbestemte alternativer. Forskjellen er at alternativene blir synligere når vi bruker radioknapper. Hvis det er mange alternativer, blir det likevel ryddigere å bruke en nedtrekksmeny. Vi lager radioknapper med <input>-elementet og attributtene type="radio", name og value: Fått Oscar?<br> <input type="radio" name="oscar"> Ja <br> <input type="radio" name="oscar" checked> Nei <br> <br>

Her er det viktig at radioknappene får samme navn. Det er det som gjør at vi bare kan velge én av knappene. Vi har også lagt til checked for den ene knappen.


156 Brukerinput

Vi bør alltid velge én av radioknappene som avkrysset på forhånd. Da kan ikke brukeren la være å velge et alternativ. I nettleseren ser radioknappene slik ut:

Knapper Det eneste vi mangler nå for å lage et skjema som i figur 10.1 på side 152, er en knapp. En knapp vil ikke alltid være nødvendig, fordi vi kan bruke JavaScript-hendelser til å registrere når brukeren skriver noe i et felt. Med en slik hendelse kan vi oppdatere siden uten at brukeren klikker på knappen. Det vil likevel ofte være viktig med en knapp, for eksempel hvis brukeren må ta en avgjørelse som får konsekvenser når knappen klikkes. Vi skal senere lage et hangman-spill, og da er det viktig at brukeren selv kan velge når han eller hun skal klikke på knappen. For å lage en knapp bruker vi elementet <button>. Knappens tekst plasserer vi inni elementet: <button>Registrer</button>

Legg merke til at <button>-elementet også har en avslutnings-tagg. I nettleseren ser denne knappen slik ut:

Du kan også finne knapper laget med <input>-elementet. Det er ingen stor forskjell mellom de to variantene, men <button>-elementet gir oss muligheten til å legge til innhold (for eksempel bilder).


Brukerinput

157

Oppgaver I oppgavene i dette kapitlet skal du lage et bestillingsskjema for Bø turisthotell. 10.1 Lag en nettside med et skjema med følgende input-muligheter: • Et tekstfelt for registrering av navn • Et tallfelt for registrering av telefonnummer • En nedtrekksmeny for valg av antall gjester (det skal være mulig å velge opp til seks gjester) • Radioknapper for valg av frokost / ikke frokost (frokost koster 200 kr) • Avkrysningsbokser for valg av ekstraaktiviteter; følgende aktiviteter kan velges: – – – –

Dagspass til Bø Sommarland (300 kr) Dagspass til Høyt og Lavt klatrepark (250 kr) Omvisning på fruktgård (200 kr) Dagstur på Telemarkskanalen (800 kr)

• En knapp for registrering av bestillingen

10.2

CSS for brukerinput

Nå har vi sett på HTML-elementene vi kan bruke for å ta inn brukerinput. Resultatet blir ikke spesielt pent, så nå skal vi se på hvordan vi kan forbedre utseendet med CSS. Som nevnt tidligere i boka er det lurt å lage CSSdokumenter (stilark) som kan gjenbrukes. Det samme gjelder her. Hver gang du skal ha brukerinput på en side, bør du ha et stilark som du kan bruke.


158 Brukerinput

I eksemplene som følger, kommer vi til å plassere alt inni et <div>-element. Da kan vi justere bredden til skjemaet ved å justere bredden til <div>-elementet. Hvis <div>-elementet har klassen .innpakning, kan CSS-koden se slik ut: .innpakning { width: 50%; padding: 5px; margin: auto; border: 3px solid black; }

Her midtstiller vi <div>-elementet med margin: auto;, vi gir det også en kantlinje og litt padding for å få litt luft mellom skjemaet og kantlinjen. Bredden kan tilpasses etter behov.

Tekst, tall og nedtrekksmenyer Du har kanskje allerede lagt merke til at et skjema består av flere <input>elementer. Det betyr at vi ikke kan bruke input som CSS-selektor, fordi vi ønsker at forskjellige <input>-elementer skal ha ulikt utseende. Vi må innføre en ny selektorvariant for å få tak i de ulike elementene. Vi kan nemlig spesifisere både hva slags element vi vil velge, og en av attributtene til elementet. Vi kan derfor bruke selektoren input[type="text"] for å gi CSS til <input>-elementer med attributtet type="text". Tekstfelter, tekstbokser, tallfelter og nedtrekksmenyer kan alle få det samme generelle utseendet, derfor kan vi starte med CSS-koden nedenfor: input[type="text"], input[type="number"], textarea, select { /* CSS-koden kommer her */ }

Her velger vi <input type="text">, <input type="number">, <textarea> og <select>. La oss først legge til CSS-kode som rydder opp i elementenes plassering: display: block; /* elementene havner på egne rader */ margin-top: 2px; /* litt mellomrom til elementet ovenfor */ margin-bottom: 10px; /* litt mellomrom til elementet nedenfor */


Brukerinput

159

Nå havner beskrivelsene våre ovenfor tekstfeltene, og det blir litt luft mellom elementene. Utseendet er allerede en del bedre:

Nå kan vi pynte litt på elementenes utseende: font-family: inherit; /* arver nettsidens skrifttype */ color: #666666;; /* mørk grå tekstfarge */ border: 1px solid rgba(0,0,0,0.1); /* lys grå kantlinje */ border-radius: 4px; /* avrundet kantlinje */ padding: 5px; /* luft inni tekstboksene */ box-shadow: 0 1px 5px rgba(0,0,0,0.7); /* skygge på tekstboksene */ box-sizing: border-box; /* teller med kantlinjen i bredden */

Her lar vi tekstboksene bruke samme skrifttype som resten av nettsiden, med en mørk grå tekstfarge. Vi legger også til en lys avrundet kantlinje og litt skygge. Den siste linjen (box-sizing: border-box;) lar oss medregne kantlinjen i boksenes bredde. Denne er nødvendig for å få til det neste vi skal gjøre. Hvis vi ikke har med denne, vil tekstbokser med bredde 100 % bli bredere enn <div>-elementet vi har plassert dem i. Nå ser skjemaet vårt slik ut (neste side):


160 Brukerinput

Tekstboksene har blitt fine, men de er litt smale. For at skjemaet skal være lett å fylle ut, bør spesielt tekstfeltene bli litt bredere. Vi kan løse det med denne koden: input[type="text"] { min-width: 130px; /* aldri smalere enn 130 px */ width: 100%; /* fyller ut hele innpakningen */ } input[type="number"], select { min-width: 130px; /* aldri smalere enn 130 px */ width: 30%; /* fyller ut 30 % av innpakningen */ } textarea { min-width: 130px; /* aldri smalere enn 130 px */ width: 100%; /* fyller ut hele innpakningen */ height: 60px; /* høyde 60 px */ }

Her gir vi først tekstfeltet bredden 100 % og en minimumsbredde som gjør at elementet aldri blir mindre enn 130 piksler bredt. Vi gjør tilsvarende for tallfeltet og nedtrekksmenyen, men der setter vi bredden til 30 % i stedet for 100 %. Tekstboksen får samme størrelse som tekstfeltet, og en høyde på 60 piksler i tillegg. Det er ikke så mye CSS kan gjøre med avkrysningsbokser og radioknapper, så vi lar dem være som de er. Resultat vårt så langt ser nå slik ut:


Brukerinput

161

Det eneste som mangler nå, er å pynte litt på knappen.

Knapper Koden nedenfor er et forslag til et utseende for knapper. Denne koden gir knappen du kan se i figur 10.1 på side 152. Du kan selv velge hvor mye du ønsker å ta med av denne koden. Mange av egenskapene er selvforklarende, men vi har innført en litt spesiell bakgrunnsfarge her. Den avanserte koden linear-gradient(rgba(255,255,255,0.2), transparent) lager en rett (lineær) gradient fra en nesten gjennomsiktig hvitfarge (rgba(255, 255, 255, 0.2)) til fullstendig gjennomsiktig (transparent). Gradienten kommer i tillegg til den blå bakgrunnsfargen. Resultatet er at knappen blir litt lysere på toppen. button { padding: 10px; /* luft inni knappen */ color: white; /* hvit tekstfarge */ text-shadow: 0 -1px 1px #335166; /* skygge på teksten */ border: 1px solid rgba(0, 0, 0, 0.1); /* lys grå kantlinje */ border-radius: 4px; /* avrunder kantene */ background: linear-gradient(rgba(255, 255, 255, 0.2), transparent); background-color: #5588AA; /* bakgrunnsfarge */ box-shadow: 0 1px 5px rgba(0, 0, 0, 0.7); /* skygge bak knappen */ outline: none; /* fjerne ekstra kantlinje */ cursor: pointer; /* gjør om musepekeren til en hånd */ }


162 Brukerinput

Vi kan også legge til en effekt når knappen blir trykket på. Det gjør vi med CSS-selektoren button:active. Her har vi lagt til en transform, som flytter knappen litt mot høyre og litt ned. Det gir inntrykk av at knappen blir trykket ned. Vi skal se nærmere på transforms senere i boka. button:active { transform: translate(1px,1px); /* 1px til høyre og 1px ned */ }

Nå kan vi bruke CSS-koden vi skrev for den blå knappen, til å lage knapper med flere farger. Det er vanlig å bruke grønn knapp for å godkjenne noe og rød knapp for å avbryte. Vi kan derfor lage to CSS-klasser som er utvidelser av CSS-koden vi allerede har skrevet. Klassene heter ok og avbryt, og for å bruke dem skriver vi: <button class="ok">OK</button> <button class="avbryt">Avbryt</button>

CSS-koden for de to knappene kan du se på neste side. Det er bare bakgrunnsfargen som er endret. button.ok { background-color: #66BB00; /* grønn */ } button.avbryt { background-color: #CC0000; /* rød */ }

Her er det egentlig nok å bare skrive .ok og .avbryt, men det kan være greit å spesifisere at disse klassene hører til knapper, slik som vi har gjort her. Utseendet på de tre knappene blir slik:


Brukerinput

163

Oppgaver 10.2 Plasser koden du skrev i oppgave 10.1, i et midtstilt <div>-element. 10.3 Lag et eget stilark for skjemaer som du kaller brukerinput.css. Legg til stilarket i nettsiden du lager for Bø turisthotell.

Gjenbruk av stilark I kapittel 6 nevnte vi verdien av å lage egne stilark for ulike HTMLelementer. I dette kapitlet møter vi et godt eksempel på en slik situasjon. Hvis du lager et gjennomtenkt stilark som gir utseende til ulike brukerinputfelter og knapper, vil du spare mye tid i framtiden når du skal lage apper med slike elementer.

10.3

Brukerinput og JavaScript

Nå har vi et skjema med en knapp, så nå kan vi legge til JavaScript for å finne ut hva brukeren har skrevet i skjemaet. Først må vi hente inn elementene med querySelector(), som vi har sett på tidligere. La oss si at vi har følgende tekstfelt: <input type="text" id="filmtittel">

Da kan vi hente inn elementet med JavaScript slik: var filmtittelEl = document.querySelector("#filmtittel");

For å se hva brukeren har skrevet, kan vi bruke egenskapen value, som inneholder verdien i tekstfeltet: console.log(filmtittelEl.value);

Vi kan bruke denne framgangsmåten for alle elementene vi har sett på (tekstfelter, tallfelter, tekstbokser, nedtrekksmenyer, avkrysningsbokser og radioknapper). Når det gjelder avkrysningsbokser og radioknapper, er det ikke så mye hjelp i å vite verdien, fordi vi ikke vet hvilken av boksene/knappene som er haket av. Vi må i stedet finne ut hvilke alternativer som er haket av. La oss si at vi har disse radioknappene: <input type="radio" name="oscar" value="ja" id="oscarJa"> Ja <br> <input type="radio" name="oscar" value="nei" id="oscarNei" checked> Nei <br>


164 Brukerinput

Da kan vi hente inn elementene med JavaScript som vi gjorde med tekstfeltet: var oscarJaEl = document.querySelector("#oscarJa"); var oscarNeiEl = document.querySelector("#oscarNei");

Nå som vi har tilgang til de to radioknappene i JavaScript, kan vi sjekke hvilken som er haket av. Det gjør vi ved å undersøke egenskapen checked. Hvis en knapp er haket av, vil verdien i checked være true. Hvis en knapp ikke er haket av, vil verdien være false: if (oscarJaEl.checked){ console.log("Filmen har vunnet Oscar."); } else if (oscarNeiEl.checked){ console.log("Filmen har ikke vunnet Oscar."); }

Du lurer kanskje på hvorfor vi har brukt value-attributtet i avkrysningsbokser og radioknapper, når vi kan klare oss fint uten. Senere lærer vi å bruke querySelectorAll(), og da kan vi hente inn alle avkrysningsboksene i et dokument. Vi kan da sjekke hvilke som er haket av som ovenfor; og hvis de er haket av, kan vi bruke value-attributtet til å finne ut hvilken boks som er haket av. Da trenger vi ikke ha med id-attributtet. Hvis vi kobler en click-hendelse til en knapp i et skjema, kan vi skrive en funksjon som behandler brukerinputen. La oss ta et eksempel med en enkel kalkulator som legger sammen to tall. Først trenger vi to tallfelt, en knapp og et <p>-element der vi kan skrive resultatet: Tall 1 <input type="number" id="tall1"> Tall 2 <input type="number" id="tall2"> <button id="knapp">Finn summen</button> <p id="resultat"></p>

Nå kan vi hente inn de fire elementene med JavaScript, og koble en clickhendelse til knappen: var tall1El = document.querySelector("#tall1"); var tall2El = document.querySelector("#tall2"); var knappEl = document.querySelector("#knapp"); var resultatEl = document.querySelector("#resultat");


Brukerinput

165

knappEl.addEventListener("click", beregn);

Funksjonen beregn() må finne verdiene i de to tallfeltene, og summere dem: function beregn() { var tall1 = tall1El.value; var tall2 = tall2El.value; var sum = tall1 + tall2; resultatEl.innerHTML = "Summen av " + tall1 + " og " + tall2 + " er " + sum; }

Hvis du prøver ut denne koden, vil du se at tallene blir behandlet som tekst. Det vil for eksempel si at summen av 2 og 4 blir 24, og summen av 7 og 3 blir 73. Det kan virke rart, fordi vi brukte type="number" i HTML-koden. Men dette attributtet påvirker bare hvordan nettleseren skal vise fram feltet, ikke hvordan JavaScript tolker verdiene som skrives i det. Vi må derfor gjøre om verdiene til datatypen number for at koden skal fungere: function beregn() { var tall1 = Number(tall1El.value); var tall2 = Number(tall2El.value); var sum = tall1 + tall2; resultatEl.innerHTML = "Summen av " + tall1 + " og " + tall2 + " er " + sum; }

Nå brukes de riktige datatypene, og «kalkulatoren» vår fungerer som den skal.

Oppgaver 10.4 Lag din egen versjon av «kalkulatoren» som ble beskrevet i teksten på forrige side. 10.5 Legg til radioknapper i «kalkulatoren» som lar brukeren velge mellom å finne summen, differansen eller gjennomsnittet av tallene. Utvid funksjonen slik at riktig resultat blir skrevet ut.


166 Brukerinput

Hendelser for brukerinput Det er vanlig å bruke knapper i skjemaer, men det finnes også en annen løsning: Vi kan bruke hendelser til å registrere alt som skrives inn i skjemaet. Vi kan se på et eksempel der vi bruker den samme «kalkulatoren» som vi laget i forrige avsnitt. Først kan vi ta bort knappen fra HTML-koden. Da sitter vi igjen med denne koden: Tall 1 <input type="number" id="tall1"> Tall 2 <input type="number" id="tall2"> <p id="resultat"></p>

Vi må hente inn elementene som tidligere; i tillegg skal vi legge en inputhendelse på de to tallfeltene. Denne hendelsen fyres av hver gang vi skriver noe i feltene: var tall1El = document.querySelector("#tall1"); var tall2El = document.querySelector("#tall2"); var resultatEl = document.querySelector("#resultat"); tall1.addEventListener("input", beregn); tall2.addEventListener("input", beregn);

Her har vi koblet den samme funksjonen til begge tallfeltene. Når vi skriver noe i ett av dem, vil hendelsen fyres av og funksjonen beregn() blir utført. Det er spesielt tre hendelser som er nyttige i forbindelse med brukerinput, nemlig input, change og click. Se tabell 10.1 på neste side for beskrivelser av disse hendelsene.

Hendelse

Fyres av ...

input

etter endring i <input> eller <textarea>

change

ved endring i <input>, <select> eller <textarea>

click

når et element klikkes på med musepekeren (checkbox/radio)

Tabell 10.1: Hendelser for brukerinput.


Brukerinput

167

Oppgaver I disse oppgavene skal du fortsette med bestillingsskjemaet til Bø turisthotell (fra oppgave 10.1 og 10.3). 10.6 Legg til et <p>-element nederst i koden din. 10.7 Når brukeren trykker på knappen for å registrere en bestilling, skal totalprisen vises i <p>-elementet du nettopp laget. Prisen skal vise hva det vil koste å overnatte per natt i tillegg til prisen for ekstraaktivitetene. Du kan anta at alle blir med på frokost og eventuelle aktiviteter. 10.8 Gjør om koden din slik at prisen oppdateres hver gang brukeren gjør en endring. Da kan du også ta bort knappen.


<input type="text"> Tekstfelt for korte tekster <input type="number"> Tallfelt <textarea></textarea> Tekstfelt for lange tekster <select> <option>Alternativ 1</option> ... </select> Nedtrekksmeny <input type="radio"> Radioknapp (bare ĂŠn kan velges) <input type="checkbox"> Avkrysningsknapp (flere kan velges) <button>Trykk pĂĽ meg</button> Knapp <input type="date"> Datovelger

<input type="color"> Fargevelger (lar en bruker velge farger) <input type="password"> Tekstfelt som skjuler hva som skrives inn <input type="range"> Glider (eng. slider)


Brukerinput

169

Sammendrag Vi bruker <input>-elementet til å lage tekstfelter (type="text"), tallfelter (type="number"), avkrysningsbokser (type="checkbox") og radioknapper (type="radio"). Hvis vi vil at brukeren skal skrive lengre tekster i tekstfeltene, bruker vi <textarea>-elementet. For å lage nedtrekksmenyer bruker vi <select>-elementet med ett <option>element for hvert alternativ. Vi lager knapper med <button>-elementet. Knappens innhold plasseres inni elementet. Ved å lage et felles CSS-dokument for alle felter i et skjema, kan vi spare mye tid. Vi kan inkludere CSS-dokumentet hver gang vi har med brukerinput på en nettside. Med egenskapen value kan vi finne verdien til et felt, for eksempel fornavnEl.value. Vi kan finne ut om en avkrysningsboks eller radioknapp er haket av med egenskapen checked, for eksempel kvinneEl.checked. For å reagere når en bruker endrer en verdi i et skjema, kan vi bruke hendelsen input for tekstfelter, tallfelter og tekstbokser. Tilsvarende kan vi bruke egenskapen change for nedtrekksmenyer og click for avkrysningsbokser, radioknapper og vanlige knapper.


170 Arrayer

Kapittel 11

Arrayer

Etter dette kapitlet skal du kunne • lage arrayer • endre, legge til og fjerne verdier i arrayer • løpe gjennom arrayer med løkker • sortere arrayer pssst Du finner videoer og andre ressurser på nettstedet på Lokus.

Se for deg at du skal lage en app som finner gjennomsnittet av tjue tall. Hvis vi skal bruke verktøyene vi har gått gjennom så langt, må vi lage tjue variabler: var tall01 = 7; var tall02 = 12; var tall03 = 4; /* osv. osv. */ var tall20 = 21;

Det er både tidkrevende og upraktisk å gjøre det på denne måten. Heldigvis finnes det et bedre verktøy for slike oppgaver, nemlig arrayen. I en array kan vi lagre tallene ovenfor slik: var tall = [7, 12, 4, /* osv. osv. */, 21];

En array er en samling av verdier. En slags liste. Ordet «array» er engelsk og kan oversettes til «samling» på norsk. En array får et navn, som en variabel, men forskjellen er at den kan inneholde flere verdier, og hver verdi får sin egen posisjon. I dette kapitlet skal vi se på hvilke muligheter arrayer gir oss.


Arrayer

11.1

171

Arrayer

Lage arrayer Vi lager en array omtrent som vi lager en variabel. Vi bruker nøkkelordet var, hakeparenteser ([ og ]) og komma for å skille verdiene fra hverandre: var tall = [4, 6, 14, 75, 3, 17, 42]; var byer = ["Bergen", "Bodø", "Oslo", "Kristiansand", "Trondheim"];

Alle verdiene får sitt unike indeks-nummer som starter på 0 og teller oppover, akkurat som vi så med datatypen string i kapittel 2. Vi bruker hakeparenteser for å finne de ulike verdiene i en array: console.log(tall[0]); // Skriver ut: 4 console.log(tall[3]); // Skriver ut: 75 console.log(byer[2]); // Skriver ut: "Oslo" console.log(byer[4]); // Skriver ut: "Trondheim"

I JavaScript er en array egentlig et objekt. Arrayen har egenskaper og metoder akkurat som andre objekter. En viktig array-egenskap er length, som gir oss antall verdier i en array. Vi kan bruke length til å finne antall verdier i de to arrayene våre: console.log(tall.length); // Skriver ut: 7 console.log(byer.length); // Skriver ut: 5

En array kan inneholde tall eller tekst, som ovenfor, men den kan også inneholde arrayer og objekter. En array som inneholder arrayer, kan se slik ut: var matOgDrikke = [ ["pizza", "pølse", "hamburger"], ["kaffe", "vann", "te"] ];

For å få tilgang til verdiene i de «indre» arrayene må vi først finne den arrayen vi er ute etter i hovedarrayen: console.log(matOgDrikke[0]); // Skriver ut: ["pizza", "pølse", "hamburger"] console.log(matOgDrikke[1]); // Skriver ut: ["kaffe", "vann", "te"]

Vi ser altså at «maten» ligger i matOgDrikke[0], mens «drikken» ligger i matOgDrikke[1]. Vi kan videre hente ut verdiene i de to arrayene slik: console.log(matOgDrikke[0][1]); // Skriver ut: "pølse" console.log(matOgDrikke[1][0]); // Skriver ut: "kaffe"


172 Arrayer

Hvis hver av verdiene i «hovedarrayen» er en array, kaller vi arrayen vår en todimensjonal array. Vi skal se nærmere på dem senere i kapitlet. Du kan se en oversikt over nummereringen av verdier i arrayer i figur 11.1 nedenfor.

diverse[2][0] diverse[2][1] var tall = 17; var diverse = [12, "hei", ["kaffe", "te"], 1042, tall]; diverse[0]

diverse[2] diverse[1]

diverse[4] diverse[3]

Figur 11.1: Nummerering av array-verdier.

Redigere arrayer Det er i hovedsak tre forskjellige endringer vi ønsker å gjøre med én array: 1. redigere verdier som allerede er i en array 2. legge til nye verdier i en array 3. fjerne verdier fra en array Noen av metodene vi skal se på her, er illustrert i figur 11.2 på neste side. For å redigere verdier som allerede er i en array, kan vi bruke samme framgangsmåte som for en variabel: var tall = [4, 2, 5, 8, 12]; console.log(tall[2]); // Skriver ut: 5 tall[2] = 42; console.log(tall); // Skriver ut: [4, 2, 42, 8, 12]

For å legge til nye verdier finnes det to metoder vi kan bruke: push(), som legger til en verdi bakerst i en array, og unshift(), som legger til en verdi i starten av en array. Vi sender verdien vi vil legge til, som et argument: var tall = [2, 3, 4]; tall.push(5); console.log(tall); // Skriver ut: [2, 3, 4, 5]


Arrayer

173

tall.unshift(1); console.log(tall); // Skriver ut: [1, 2, 3, 4, 5]

For å fjerne verdier finnes det to tilsvarende metoder: pop(), som fjerner den siste verdien i en array, og shift(), som fjerner den første verdien i en array. Disse metodene krever ikke et argument: var tall = [1, 2, 3, 4]; tall.pop(); console.log(tall); // Skriver ut: [1, 2, 3] tall.shift(); console.log(tall); // Skriver ut: [2, 3]

drikke.push("vann") var drikke = ["kaffe", "te", "brus",

var drikke = ["kaffe", "te", "brus",

"va

nn" ];

"vann" ];

drikke.pop() drikke.unshift("vann")

n" var drikke = [ "van , "kaffe", "te", "brus"];

var drikke = [

"van n"

, "kaffe", "te", "brus"];

drikke.shift()

Figur 11.2: Metoder som lar oss legge til og fjerne verdier i arrayer.

Finne og fjerne enkeltverdier Vi skal senere se hvordan vi kan bruke løkker til å gå gjennom arrayer. Vi kan blant annet bruke løkker til å lete etter verdier i en array. Det finnes også en innebygd metode, indexOf(), som lar oss lete etter verdier: var tall = [2, 4, 7, 14, 7, 42]; console.log(indexOf(7)); // Skriver ut: 2

Metoden indexOf() gir oss den første forekomsten av verdien i arrayen. Selv om det er to forekomster av tallet 7 i arrayen ovenfor, får vi bare posisjonen


174 Arrayer

til den første av dem. Vi må derfor bruke en løkke (se senere i kapitlet) for å finne alle forekomstene. Det finnes også en lignende metode, lastIndexOf(), som gir oss den siste forekomsten av en verdi. Vi har sett at det er greit å fjerne første og siste verdi fra en array, men vi kan også få bruk for å fjerne en verdi som er i midten av en array. Til det bruker vi metoden splice(). Denne metoden tar to argumenter, det første angir indeksen vi ønsker å starte på, og det andre angir hvor mange verdier vi ønsker å ta bort. Hvis vi for eksempel ønsker å ta bort verdien med indeks 2, kan vi skrive splice(2, 1): var tall = [1, 2, 3, 4, 5]; tall.splice(2, 1); console.log(tall); // Skriver ut: [1, 2, 4, 5]

Vi kan kombinere indexOf() og splice() for å fjerne den første forekomsten av en gitt verdi: var tall = [2, 4, 7, 14, 7, 42]; tall.splice(tall.indexOf(7), 1); console.log(tall); // Skriver ut: [2, 4, 14, 7, 42]

Her bruker vi indexOf() til å finne den første forekomsten av tallet 7, og så bruker vi splice() for å fjerne tallet fra arrayen. Hvis vi gjentar prosessen, vil også det andre 7-tallet forsvinne fra arrayen. Metoden splice() kan også brukes til å legge til elementer inni en array. Vi bruker fremdeles de samme to argumentene, men vi kan legge til flere argumenter. Ekstra argumenter blir lagt til i arrayen på posisjonen vi angir i det første argumentet. Fordi vi her ønsker å legge til verdier, er det lurt å sette det andre argumentet til 0, altså at null verdier skal fjernes. Nye verdier blir satt inn før indeksen vi angir i det første argumentet: var tall = [1, 2, 3, 6]; tall.splice(3, 0, 4, 5); console.log(tall); // Skriver ut: [1, 2, 3, 4, 5, 6]


Arrayer

175

Oppgaver 11.1 Lag arrayen var tall = [2, 4, 6, 8];. 11.2 Bruk metodene vi har sett på, og gjør om arrayen tall til [4, 6]. 11.3 Legg til tallet 5 mellom tallene 4 og 6, slik at arrayen nå inneholder [4, 5, 6]. 11.4 Gjør om arrayen slik at den inneholder [3, 4, 5, 6, 7]. 11.5 Gjør om arrayen slik at den inneholder "tre", 4, "fem", 6, "syv".

11.2

Løpe gjennom arrayer med løkker

Vi har nå sett at alle verdiene i en array har sin egen posisjon, med et eget indeks-nummer. Fordi verdiene kan finnes med deres unike nummer, kan vi bruke løkker til å gå gjennom alle verdiene i en array. Husk at en for-løkke kan gå gjennom en serie med hele tall som vi bestemmer. Hvis en array inneholder 10 verdier, så vil vi at løkken skal gå gjennom tallene 0, 1, 2, 3, 4, 5, 6, 7, 8 og 9. Vi starter på 0 fordi den første verdien alltid har indeks-nummer 0. En passende løkke kan derfor se slik ut: for (var i = 0; i < 10; i++) { console.log(i); }

Som nevnt ovenfor: Legg merke til at vi starter på 0 og holder på så lenge i < 10. Altså så lenge i er mindre enn arrayens lengde. Det skyldes at indeksnumrene starter på 0, slik at den siste verdien har indeks-nummer én mindre enn arrayens lengde. Nå skal vi se på hvordan vi kan bruke en løkke som denne på arrayer.

Enkle arrayer La oss si at vi har følgende array: var tall = [1, 2, 3, 4, 6, 8, 12];

For å gå gjennom alle verdiene i denne arrayen må vi først kjenne arrayens lengde. Tidligere i kapitlet nevnte vi egenskapen length. Vi kan bruke den til å finne antall verdier i arrayen tall: console.log(tall.length); // Skriver ut: 7

Nå vet vi nok til å lage en for-løkke. Vi skal starte på 0, øke med én for hver runde, og holde på så lenge tallet er mindre enn arrayens lengde (her 7):


176 Arrayer

for (var i = 0; i < tall.length; i++) { console.log(tall[i]); }

Med denne løkken blir alle verdiene i arrayen tall skrevet til konsollen. Det er ikke så veldig spennende; så la oss heller gjøre noe nyttig med disse tallene. Vi kan for eksempel finne summen av dem. Vi møter da en veldig vanlig situasjon der vi må lage en egen variabel for å holde orden på summen. Det er viktig at vi lager den variabelen utenfor løkken, slik at den ikke blir laget på nytt hele tiden. Vi gir også variabelen verdien 0 for å forsikre oss om at den får riktig datatype. Summen av tallene finner vi derfor slik: var sum = 0; for (var i = 0; i < tall.length; i++) { sum += tall[i]; } console.log(sum); // Skriver ut: 36

Dette konseptet er veldig viktig, så vi tar et eksempel til før vi går videre. Hvordan kan vi finne det største tallet i arrayen tall? Tenk litt på det selv før du fortsetter å lese. Vi kan tenke på samme måte som med sum-eksemplet ovenfor. Vi lager en variabel som vi sammenligner med hvert av tallene i arrayen. Hvis et tall er større enn variabelen, endrer vi variabelens verdi: // Lagrer det første tallet i arrayen i en variabel var storst = tall[0]; for (var i = 0; i < tall.length; i++) { if (tall[i] > storst) { storst = tall[i]; } } console.log(storst); // Skriver ut: 12

Det finnes også andre måter å gjøre dette på. Vi kan for eksempel sortere tallene i arrayen (se senere i kapitlet) og velge den siste verdien, eller vi kan bruke innebygde funksjoner. Det er likevel mye å lære ved å gjøre slike ting «manuelt» når man skal lære å programmere.


Arrayer

177

Arrayer i funksjoner Vi har tidligere sett på funksjoner og argumentene de kan motta. En array kan brukes som et argument på samme måte som andre variabler. Vi kan for eksempel lage en funksjon som summerer tallene i en array: function summer(tallArray) { var sum = 0; for (var i = 0; i < tallArray.length; i++) { sum += tallArray[i]; } return sum; }

Denne funksjonen bruker vi slik: var mineTall = [2, 4, 6, 8]; console.log(summer(mineTall));

Her blir summen, som i dette tilfellet er 20, skrevet til konsollen.

Oppgaver 11.6 Lag en funksjon som finner det minste tallet i en array. Test funksjonen med forskjellige arrayer. 11.7 Lag en funksjon som finner gjennomsnittet av tallene i en array. Test funksjonen med forskjellige arrayer.

querySelectorAll() Vi har nevnt metoden querySelectorAll() flere ganger i denne boka, og nå kan vi endelig bruke den! Den brukes på akkurat samme måte som querySelector(), men i stedet for å returnere det første elementet som stemmer med den angitte selektoren, så gir den oss alle elementene, i form av en array. I koden på neste side henter vi inn alle <p>-elementene med querySelectorAll(), og legger til en click-lytter på hver av dem. Legg merke til at vi kan bruke en løkke for å legge den samme lytteren på alle elementene. Det sparer oss for mye koding! Funksjonen bytter CSS-klassen til elementene slik at bakgrunnsfargen byttes ut hver gang vi klikker på et <p>-element. Resultatet av koden kan du se i figur 11.3.


178 Arrayer

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

< ! doctype html > < html > < head > < title > Bytt farge < / title > < meta charset= " UTF-8 " > < s tyle > body { font-family: " Century Gothic " , Arial , sans-serif; font-size: 14 px; } p { padding: 20 px; cursor: pointer; } . gul { background-color: yellow; } . rod { background-color: red; } < / s tyle > < / head > < b ody > < p class= " gul " > Trykk p å meg ! < / p > < p class= " gul " > Trykk p å meg ! < / p > < p class= " gul " > Trykk p å meg ! < / p > < p class= " gul " > Trykk p å meg ! < / p > < p class= " gul " > Trykk p å meg ! < / p > < p class= " gul " > Trykk p å meg ! < / p >

30 31 32 33

< s cript > // Lager en array med alle p-elementene var avsnittEl = document . querySelectorAll ( " p " ) ;

34 35 36 37 38

// Legger til en lytter p å hvert av p-elementene for ( var i = 0; i < avsnittEl . length; i ++) { avsnittEl [ i ]. addEventListener ( " click " , byttFarge ) ; }

39 40 41 42 43 44 45 46 47 48 49 50

// Hvis klassen er gul , bytt til r ød , hvis ikke bytt til gul function byttFarge ( e ) { if ( e . target . className === " gul " ) { e . target . className = " rod " ; } else { e . target . className = " gul " ; } } < / s cript > < / b ody > < / html >

Kode 11.1: Kode som bruker querySelectorAll() til å finne alle <p>-elementer

Når vi klikker på et av <p>-elementene, vil bakgrunnsfargen endres. Det vil si at et gult <p>-element vil få rød bakgrunnsfarge, og et rødt <p>-element vil få


Arrayer

179

gul bakgrunnsfarge. Resultatet av denne koden, etter noen klikk, kan du se i figur 11.3.

Figur 11.3: Resultatet av kode 11.1.

Hente nye HTML-elementer med querySelectorAll() I forrige kapittel laget vi en enkel «kalkulator» som fant summen av to tall. Med querySelectorAll() kan vi lett utvide denne til å inneholde mange tall. Vi kan også generere tallfeltene automatisk, nå som vi ikke trenger å hente ut hver og en av dem med en unik id. La oss først se på hvordan vi kan lage mange tallfelter ved å legge dem inn i DOM-treet med JavaScript: // Henter inn et div-element der vi vil plassere tallfeltene var innpakningEl = document.querySelector(".innpakning"); // Henter inn et p-element der vi kan skrive ut summen var resultatEl = document.querySelector("#resultat"); // Angir hvor mange tallfelter vi ønsker var antallFelter = 6; for (var i = 0; i < antallFelter; i++){ var nyttTallFelt = document.createElement("input"); nyttTallFelt.type = "number"; nyttTallFelt.addEventListener("input", beregn); innpakningEl.appendChild(nyttTallFelt); }

For at denne koden skal fungere, må vi ha et <div>-element med klassen .innpakning, og et <p>-element med id-en resultat. I løkken lager vi like mange <input>-elementer som vi angir i variabelen antallFelter. Vi legger også til en input-hendelse på hver av feltene før vi legger dem til nederst i <div>-elementet.


180 Arrayer

Funksjonen beregn(), som kjøres hver gang det skrives inn et nytt tall, ser slik ut: function beregn() { var tallFeltEl = document.querySelectorAll("input[type='number']"); var sum = 0; for (var i = 0; i < tallFeltEl.length; i++) { sum += Number(tallFeltEl[i].value); } resultatEl.innerHTML = "Summen av tallene er " + sum; }

Vi bruker selektoren input[type='number'] her fordi vi bare ønsker å hente med type="number". I dette eksemplet er ikke det egentlig nødvendig fordi alle <input>-elementene våre er like. Det er likevel greit å gjøre det slik fordi vi vanligvis har flere ulike <input>-elementer i det samme dokumentet. Et alternativ til denne selektoren er å gi alle elementene en klasse, for så å bruke klassenavnet i querySelectorAll(). <input>-elementene

Videre finner vi summen av verdiene i tallfeltene med samme framgangsmåte som vi så på tidligere i dette kapitlet. Legg merke til at vi bruker Number() for å forsikre oss om at tallene blir håndtert som tall og ikke tekst. Til slutt endrer vi teksten i <p>-elementet. Resultat av koden kan du se i figur 11.4.

Figur 11.4: «Kalkulatoren» som genererer tallfelter automatisk og finner dem med querySelectorAll().


Arrayer

181

Oppgaver 11.8 Lag et HTML-dokument der en bruker kan velge opp til 10 favorittfrukter ved å bruke avkrysningsbokser. 11.9 Bruk querySelectorAll() til å hente alle avkrysningsboksene og skrive ut en liste over brukerens favorittfrukter. Du kan selv velge om brukeren skal trykke på en knapp, eller om du bruker en hendelse som automatisk oppdaterer listen.

Todimensjonale arrayer Som nevnt i starten av dette kapitlet kan vi også ha arrayer inni arrayer. Hvis vi har to arrayer inni hverandre, kaller vi det en todimensjonal array. Vi kan for eksempel lage én hovedarray med tre verdier: forretter, hovedretter og desserter. Disse tre verdiene kan også være arrayer, slik at vi har én array med forretter, én med hovedretter og én med desserter. Arrayen vår kan for eksempel se slik ut: var retter = [ ["laksetartar", "ertesuppe"], ["biff med rødvinssaus", "lammeskank"], ["jordbæris", "eplekake"] ];

Her har vi skrevet arrayen over fem linjer. Det er ikke nødvendig, men det gjør koden mye lettere å lese. Her er det for eksempel lett å se hva som er forretter, hva som er hovedretter, og hva som er desserter. Nå som vi har arrayen, må vi finne ut hvordan vi kan gå gjennom alle verdiene med en løkke. Prøv gjerne selv før du leser videre. Vi vet allerede hvordan vi kan lage en løkke for å gå gjennom de tre verdiene i hovedarrayen: for (var i = 0; i < retter.length; i++) { console.log(retter[i]); }

Denne koden skriver ut de tre «indre» arrayene til konsollen, men hva om vi vil ha alle rettene skrevet ut? Vi vet at forrettene ligger i retter[0], hovedretter i retter[1] og desserter i retter[2]. Vi vet også at den første desserten ligger i retter[2][0], og at den andre forretten ligger i retter[0][1]. Vi må derfor gå gjennom både den første indeksen, for å finne forrett, hovedrett og dessert, og den andre indeksen for å finne enkeltrettene. Vi kan lage en ny løkke inni den første, slik at vi får gått gjennom hver av de indre arrayene også:


182 Arrayer

for (var i = 0; i < retter.length; i++) { for (var j = 0; j < retter[i].length; j++) { console.log(retter[i][j]); } }

Her får vi tilgang til alle enkeltverdiene i de «indre» arrayene. Denne framgangsmåten gjør også at de «indre» arrayene kan ha ulik lengde, fordi vi sjekker lengden til hver av dem. Legg merke til at vi bruker ulike tellere (i og j). Vi kan ikke bruke i i begge løkkene fordi det vil endre variabelen og påvirke den ytre løkken. Det er naturlig å bruke todimensjonale arrayer hvis vi skal representere et rutenett. Da kan vi la den første arrayen representere rader, og de indre arrayene kan representere kolonner. Da kan vi for eksempel lage en array som kan brukes til å lage et tre-på-rad-spill: var trePaaRad = [ [_,_,_], [_,_,_], [_,_,_] ];

Her brukes _ for å angi at feltene er tomme. Vi kan bruke liten «x» og liten «o» for å representere de to spillerne.

Oppgaver 11.10 Lag en todimensjonal array som inneholder en gangetabell. Du skal kunne angi faktorer som indekser og få ut produktet av dem som verdi. Det vil for eksempel bety at gangetabell[3][7] skal inneholde verdien 21. 11.11 Utfordring: Gå sammen med en venn og lag deres eget tre-på-rad-spill. Lag en array som dere oppdaterer og skriver ut hver gang en spiller plasserer en brikke. Arrayen kan skrives direkte til konsollen, eller til en nettside i form av en HTML-tabell. 11.12 Utfordring: Hvis dere har god tid, kan dere også lage et slagskip-spill. Da må hver spiller lage et rutenett, for eksempel 10 ganger 10 ruter, og plassere ut båter i rutenettet. Bruk båter med lengdene 2, 3, 4 og 5 felter, de kan ikke plasseres på skrå. Deretter kan dere prøve å gjette hvor motstanderen har plassert sine båter, ved å lage et nytt rutenett der dere lager en «x» for hvert forsøk på å treffe en båt. Vinneren er den som har «senket» alle båtene til motstanderen først.


Arrayer

11.3

183

Sortere arrayer

Arrayobjektet har en innebygd metode som lar oss sortere verdiene i en array. Den bruker det vi kaller leksikografisk sortering, som vil si at verdiene blir sortert alfabetisk. Dette fungerer fint for tekster, men vi får problemer med tall. La oss se på et eksempel: var drikke = ["te", "vann", "kaffe", "appelsinsaft"]; drikke.sort(); console.log(drikke); // Skriver ut: ["appelsinsaft", "kaffe", "te", "vann"]

Her blir ordene sortert som vi ønsker, men hva skjer med tall? var tall = [1, 4, 41, 82, 101, 14]; tall.sort(); console.log(tall); // Skriver ut: [1, 101, 14, 4, 41, 82]

Her blir tallene sortert alfabetisk (leksikografisk), men det er ikke slik vi ønsker å sortere dem. For å kontrollere hvordan metoden sort() skal sortere verdiene, kan vi lage en sammenligningsfunksjon som vi lar sort() bruke. Sammenligningsfunksjonen skal sammenligne to tall av gangen, la oss kalle dem a og b. Vi lar funksjonen returnere a − b, altså differansen mellom de to tallene. Hvis funksjonen returnerer et negativt tall, skal a plasseres foran b, fordi a er mindre enn b. Hvis funksjonen returnerer tallet null, skal tallene stå som de er, fordi de er like, og hvis funksjonen returnerer et positivt tall, skal b plasseres foran a, fordi b er større enn a. Funksjonen kan se slik ut: function sammenlignTall(a, b) { return a - b; }

Ulike nettlesere bruker ulike sorteringsteknikker, men de tolker resultatet av sammenligningsfunksjonen på samme måte. Et eksempel på en slik sorteringsteknikk, som kalles «bubble sort», illustreres i figur 11.5 på neste side. For å bruke sammenligningsfunksjonen må vi gi den som et argument til sort()metoden. Vi gjør altså nesten det samme som ovenfor, men vi må sørge for at vår sammenligningsfunksjon brukes: var tall = [1, 4, 41, 82, 101, 14]; tall.sort(sammenlignTall); console.log(tall); // Skriver ut: [1, 4, 14, 41, 82, 101]


184 Arrayer

Nå fikk vi sortert tallene som vi ønsker. Husk at vi bare bruker denne funksjonen hvis vi vil sortere tall. Vi kan bruke sort() uten noe argument om det er tekster vi vil sortere. Hvis du ønsker å sortere verdiene i omvendt rekkefølge, kan du gjøre slik som vi har gjort nå, for så å bruke metoden reverse(), som også hører til array-objektet: var tall = [1, 4, 41, 82, 101, 14]; tall.sort(sammenlignTall); tall.reverse(); console.log(tall); // Skriver ut: [101, 82, 41, 14, 4, 1]

Det kan anbefales å lage en egen JavaScript-fil, for eksempel mineFunksjoner.js, der du plasserer viktige funksjoner som sammenligningsfunksjonen vi har skrevet nå. Da kan du inkludere den i kodene dine, slik at du alltid har tilgang til funksjonene du selv har skrevet. Du kan legge inn ekstra JavaScript-filer slik: <script src="mineFunksjoner.js"></script> <script> /* Resten av JavaScript-koden din */ </script>

Her bruker vi to <script>-elementer. Det ene for å hente inn et eksternt JavaScript-dokument, og det andre for å skrive resten av JavaScript-koden som vanlig. a[0] a[1] a[2] a[3]

3

1

4

2

3-1 = 2 (3 skal stå etter 1)

1

3

4

2

3-4 = -1 (3 skal stå foran 4)

1

3

4

2

4-2 = 2 (4 skal stå etter 2)

1

3

2

4

1-3 = -2 (1 skal stå foran 3)

1

3

2

4

3-2 = 1 (3 skal stå etter 2)

1

2

3

4

Alt gjentas til ingen bytter er nødvendig

Figur 11.5: Illustrasjon av sorteringsmetoden «bubble sort».


Arrayer

185

Oppgaver 11.13 Lag sorterte versjoner av følgende arrayer: a var array1 = [2, 1, 7, 5]; b var array2 = ["melon", "eple", "appelsin", "ananas", "pære"]; c var array3 = [2, 10, 104, 17, 82, 109]; 11.14 Legg til linjen console.log(a + " - " + b + " = " + (a - b)); i sammenligningsfunksjonen din. Prøv å sortere en usortert array som består av tall, og se hva som kommer i konsollen. Kan du se hvordan nettleseren din sorterer tallene? 11.15 Utfordring: Sorter personene i følgende array etter alder: var personer = [["Hans", 12], ["Nils", 3], ["Sofie", 5]]; . I denne oppgaven må du lage en ny sammenligningsfunksjon.


186 Arrayer

Sammendrag Vi bruker nøkkelordet var og hakeparenteser for å lage en array: var minArray = [];

En array kan være tom, eller den kan inneholde verdier: var mineTall = [2, 4, 5];

Alle verdier i en array har sitt unike indeksnummer. Den første verdien har indeks 0, den andre verdien har indeks 1, den tredje verdien har indeks 2, osv. Verdiene kan være samme type verdier som vi kan ha i vanlige variabler, men de kan også være andre arrayer: var mineTall = [2, 4, 5, ["en", "to", "tre"], 14];

Metoden push() lar oss legge til en verdi bakerst i en array. Metoden lar oss legge til en verdi i starten av en array.

unshift()

Metoden pop() lar oss fjerne den bakerste verdien i en array. Metoden shift() lar oss fjerne den første verdien i en array. Metoden splice() lar oss fjerne elementer inni en array. Den lar oss også legge til nye verdier inni en array. Vi kan lete etter verdier i en array med metodene indexOf(), som finner første forekomst av en verdi, og lastIndexOf(), som finner siste forekomst av en verdi. Vi kan løpe gjennom alle verdiene i en array med en for-løkke: for (var i = 0; i < mineTall.length; i++) { console.log(mineTall[i]); }

Metoden querySelectorAll() returnerer en array med alle HTML-elementene i et DOM-tre med CSS-selektoren vi angir. For å sortere arrayer med tekstverdier kan vi bruke metoden sort(). Hvis vi vil sortere tall, må vi lage en egen funksjon for sammenligning av verdier.




Posisjonering med CSS

251

Kapittel 16

Posisjonering med CSS

Etter dette kapitlet skal du kunne • plassere et element hvor som helst på en nettside • lage layouter med flexbox pssst

I dette kapitlet skal vi se på hvordan vi kan bruke posisjonering til å plassere elementer hvor som helst på en nettside, og hvordan vi kan bruke flexbox («Flexible Box Layout») til å lage avanserte layouter. Vi bruker posisjonering når vi ønsker å plassere et element et spesifikt sted, mens vi bruker flexbox til å lage layouter. Du kan også ha hørt om CSS-egenskapen float, som ble brukt mye til å lage layouter tidligere, men som egentlig skal brukes til å la tekst flyte rundt et bilde. Flexbox er betydelig lettere å bruke enn float, og gjør det derfor enklere å lage fine layouter. Når vi skal jobbe med plassering av elementer, er det nyttig å huske at alle elementer på en nettside oppfører seg som rektangler. Derfor er det lurt å tenke på hvor vi ønsker «rektanglene» våre, og hvordan vi skal plassere dem, før vi begynner å skrive kode.

16.1

Posisjonering

CSS-egenskapen position For å posisjonere et element med CSS bruker vi egenskapen position. Denne egenskapen kan ha fire ulike verdier: 1. static (elementene tegnes opp på nettsiden i samme rekkefølge som de har i koden) 2. absolute (elementet tegnes opp på en absolutt posisjon i forhold til et posisjonert element det er inni) 3. relative (elementet tegnes opp relativt til sin normale posisjon)

Du finner videoer og andre ressurser på nettstedet på Lokus.


252 Posisjonering med CSS

4. fixed (elementet tegnes opp relativt til nettleservinduet) Den første verdien er den du allerede er vant med. Alle elementer har nemlig position: static; som standardverdi. De andre verdiene, derimot, er ikke spesielt intuitive, så vi må se på noen eksempler for å forstå dem. Anta at vi har følgende HTML-kode, som består av et <div>-element som inneholder tre andre <div>-elementer: <div class="innpakning"> <div class="absolutt"> absolute </div> <div class="relativ"> relative </div> <div class="fiksert"> fixed </div> </div>

Vi legger også til følgende CSS-kode: .innpakning { width: 800px; height: 200px; background-color: tomato; margin: 50px auto; } .innpakning div { /* Alle div-elementer inni .innpakning */ width: 100px; height: 100px; background-color: LightBlue; } .absolutt { position: absolute; /* i forhold til elementet det er i */ left: 50px; top: 5px; } .relativ { position: relative; /* i forhold til sin opprinnelige posisjon */ top: -20px; } .fiksert { position: fixed; /* i forhold til nettleservinduet */ left: 0; top: 200px; }


Posisjonering med CSS

253

Her bruker vi egenskapene top og left til å angi elementenes posisjoner. Resultatet av disse kodene kan du se i figur 16.1. Elementet med fiksert posisjon havner inntil nettleserens venstre kant, og vil bli der selv om vi beveger oss nedover nettsiden. Elementet med relativ posisjon vil alltid ligge 20 piksler høyere enn sin naturlige posisjon, som nå er inni innpakningen. Legg merke til at elementet havner til venstre i innpakningen. Det skyldes at det absoluttposisjonerte elementet ikke tar opp noen plass, det påvirker ingen andre elementer på siden. Det absoluttposisjonerte elementet blir posisjonert i forhold til <html>-elementet fordi det ikke er inni et annet posisjonert element. Det vil si at hvis vi gir innpaknings-elementet (.innpakning) en posisjon (her egner relative seg best), vil det absoluttposisjonerte elementet havne inni innpakningen i stedet.

Figur 16.1: Illustrasjon av verdiene absolute, relative og fixed.

Oppgaver 16.1 Bruk kodene beskrevet i avsnittet ovenfor og gjenskap resultatet som er vist i figur 16.1. 16.2 Gi innpakningen høyden 2000 piksler. Hva skjer med elementene når du beveger deg nedover nettsiden (scroller nedover)? 16.3 Gi innpakningen position: relative;. Hvordan påvirkes de andre elementene av denne koden? 16.4 Lag et nytt <div>-element inni elementet med klassen .fiksert. Gi det høyde 100 piksler, bredde 100 piksler, position: absolute;, left: 10px;, top: 10px; og en rød bakgrunnsfarge. Hvordan blir dette elementet plassert?


254 Posisjonering med CSS

Et element som følger musepekeren Nå skal vi bruke absoluttposisjonering sammen med en hendelse for å lage en sirkel som følger musepekeren rundt i nettleservinduet. Vi skal også la sirkelens farge bestemmes av posisjonene i nettleservinduet. Til venstre i figur 16.2 på side 257 kan du se et skjermbilde av sirkelen vår. Vi må altså lage en sirkel, og bruke en hendelse som fyres av når vi flytter musepekeren rundt, for å flytte på sirkelen. Vi kan bruke CSS-egenskapen border-radius for å lage en sirkel med ren HTML. Vi har tidligere sett på hvordan vi kan lage en sirkel på <canvas>-elementet, og vi skal i kapittel 21 se på hvordan vi kan flytte en slik sirkel. For å lage en sirkel ved hjelp av border-radius må vi sette verdien til halvparten av elementets bredde. I vårt tilfelle vil vi lage en sirkel med en diameter på 50 piksler. Da må elementet ha bredden 50 piksler, høyden 50 piksler og border-radius må være enten 25px eller 50%. For å flytte på sirkelen bruker vi hendelsen «mousemove» på <body>-elementet, som fyres av hver gang vi flytter musepekeren innenfor <body>-elementets utstrekning. Vi har angitt bredde og høyde lik 100 % for både <html>- og <body>elementet for å forsikre oss om at elementene fyller hele nettleseren. Du kan se hele koden i kode 16.1 nedenfor. Når «mousemove»-hendelsen fyres av, bruker vi hendelsesobjektet, e, som blir sendt som argument til hendelsesfunksjonen flyttSirkel() for å finne musepekerens koordinater. Vi finner x-koordinaten i egenskapen e.clientX og ykoordinaten i egenskapen e.clientY. Disse egenskapene inneholder tall, så vi må gjøre dem om til tekster med "px" på slutten når vi skal bruke dem som CSS-verdier. Vi trekker også fra 25 piksler fra koordinatene slik at sirkelens sentrum, og ikke hjørnet øverst til venstre, tegnes opp der musepekeren er. Helt til slutt har vi lagt til en liten detalj som lar sirkelens farge bestemmes av musepekerens koordinater. Fargetonen bestemmes av x-koordinaten og fargens metning bestemmes av y-koordinaten. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

< html > < head > < title > Sirkel som f ø lger musepekeren < / title > < meta charset= " UTF-8 " > < s tyle > html , body { width: 100%; height: 100%; } body { background-color: #323232; cursor: none; /* skjuler musepekeren */ overflow: hidden; /* hindrer scrollbar */ }


Posisjonering med CSS

15 16 17 18 19 20 21 22 23 24 25

255

div { width: 50 px; height: 50 px; border-radius: 50%; position: absolute; left: 0; top: 0; } < / s tyle > < / head > < b ody >

26 27

< div > < / div >

28 29 30 31 32

< s cript > // Henter body-elementet og div-elementet var bodyEl = document . querySelector ( " body " ) ; var sirkelEl = document . querySelector ( " div " ) ;

33 34 35

// Lytter etter musebevegelser p å body-elementet bodyEl . addEventListener ( " mousemove " , flyttSirkel ) ;

36 37 38 39 40

// Funksjonen flyttSirkel () flytter sirkelen etter musepekeren function flyttSirkel ( e ) { // Setter sirkelens left-egenskap lik musepekerens x-koordinat sirkelEl . style . left = ( e . clientX - 25) + " px " ;

41

// Setter sirkelens top-egenskap lik musepekerens y-koordinat sirkelEl . style . top = ( e . clientY - 25) + " px " ;

42 43 44 45 46

47

}

// Lar musepekerens posisjon styre sirkelens farge sirkelEl . style . backgroundColor = " hsl ( " + e . clientX + " ," + e . clientY + " % , 50%) " ;

48 49 50 51

< / s cript > < / b ody > < / html >

Kode 16.1: En kode som lager en sirkel som følger etter musepekeren.

Oppgaver 16.5 Gjør om koden ovenfor slik at sirkelens størrelse også bestemmes av posisjonen i nettleservinduet. Som en ekstra utfordring kan du prøve å gjøre sirkelen stor i midten av vinduet, og mindre ut mot kantene.


256 Posisjonering med CSS

Styre et element med piltastene På samme måte som vi fikk en sirkel til å følge musepekeren, kan vi få en firkant til å bevege seg når vi trykker på piltastene. Forskjellene er at vi bruker hendelsen «keydown» i stedet for «mousemove», og at vi må vi flytte på boksen selv i stedet for å la den være samme sted som musepekeren. I kode 16.2 på neste side finner du koden som gjør alt dette, og i figur 16.2 på side 257 kan du se et skjermbilde av firkanten. For å flytte på firkanten har vi først laget to variabler, topp og venstre, som skal inneholde posisjonen til firkanten vår. Vi har også laget variabelen fart, som bestemmer hvor mange piksler firkanten skal flyttes når vi trykker på en av piltastene. Når en av piltastene trykkes, endrer vi posisjonen i retningen vi ønsker. Til slutt oppdaterer vi firkantens CSS-kode med riktige verdier for left og top. Vi må kjenne piltastenes «keyCode» for å vite hvilken av piltastene vi trykker på. Hvis du ikke kjenner en «keyCode», kan du lage en kode med en «keydown»hendelse som skriver e.keyCode til konsollen. Da kan du finne alle tastenes «keyCode» ved å trykke på dem. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

< html > < head > < title > Styre en firkant < / title > < meta charset= " UTF-8 " > < s tyle > body { background-color: #323232; cursor: none; /* skjuler musepekeren */ overflow: hidden; /* hindrer scrollbar */ } div { width: 50 px; height: 50 px; position: absolute; left: 0; top: 0; background-color: LightBlue; } < / s tyle > < / head > < b ody >

22 23

< div > < / div >

24 25 26 27 28

< s cript > // Henter body-elementet og div-elementet var bodyEl = document . querySelector ( " body " ) ; var boksEl = document . querySelector ( " div " ) ;

29 30 31

// Variabler for left- og top-egenskapene til boksen var venstre = 0;


Posisjonering med CSS

32

257

var topp = 0;

33 34 35

// Setter boksens fart ( flyttelengde ) var fart = 10;

36 37 38

// Lytter etter tastaturtrykk p å body-elementet bodyEl . addEventListener ( " keydown " , flyttBoks ) ;

39 40 41 42 43 44 45 46 47 48 49 50 51

// Funksjonen flyttBoks () flytter boksen function flyttBoks ( e ) { // Finner ut hvilken tast som ble trykket if ( e . keyCode === 37) { // Venstre tast venstre -= fart; } else if ( e . keyCode === 39) { // H ø yre tast venstre + = fart; } else if ( e . keyCode === 38) { // Oppovertast topp -= fart; } else if ( e . keyCode === 40) { // Nedovertast topp + = fart; }

52 53 54 55 56

}

// Setter boksens top- og left-egenskaper boksEl . style . top = topp + " px " ; boksEl . style . left = venstre + " px " ;

57 58 59 60

< / s cript > < / b ody > < / html >

Kode 16.2: En kode som lager en firkant som styres av piltastene.

Figur 16.2: To eksempler på hendelser som fører til at elementer flytter på seg. Til venstre en sirkel som følger musepekeren, og til høyre en firkant som styres av piltastene.


258 Posisjonering med CSS

Oppgaver 16.6 Kopier kode 16.2. Utvid koden slik at firkanten ikke kan forlate nettleservinduet. Hint: window.innerWidth og window.innerHeight gir deg nettleservinduets størrelse. 16.7 Utvid koden slik at firkantens farge bestemmes av firkantens retning (én farge for oppover, én for nedover, én for venstre og én for høyre). 16.8 Utvid koden slik at nettsiden (<body>) har én bakgrunnsfarge når firkanten er i venstre halvdel av vinduet, og en annen bakgrunnsfarge når firkanten er i høyre halvdel av vinduet. 16.9 Klarer du å utvide koden slik at det blir mulig å bevege boksen på skrå? Se infoboksen på neste side for hint. 16.10 Skriv en kode som lar brukeren klikke i nettleservinduet. Når brukeren klikker, skal det dukke opp 10 <div>-elementer med ulike farger i nærheten av punktet der brukeren klikket. Du velger selv om elementene skal være firkantede eller runde. Det skal gå an å klikke flere ganger.


Posisjonering med CSS

259

Bevege seg på skrå? I kode 16.2 på side 256 klarer vi bare å styre boksen rett opp/ned eller rett mot venstre/høyre. Men hva gjør vi om vi ønsker å gå på skrå i stedet? Én måte å løse det på er å lage en array, for eksempel var knapper = [];. Den må vi lage utenfor hendelsesfunksjonen. Denne arrayen skal til enhver tid inneholde alle knapper som trykkes ned. Vi kan for eksempel gjøre det slik at knapper[37] har verdien true hvis venstre piltast trykkes ned. Da kan vi endre funksjonen flyttBoks() i kode 16.2 slik: if (knapper[37]) { venstre -= fart; } if (knapper[39]) { venstre += fart; } if (knapper[38]) { topp -= fart; } if (knapper[40]) { topp += fart; }

Men hvordan får vi satt knapper[37] til true? Vi kan lytte etter hendelsene «keydown» og «keyup» samtidig; da kan vi bruke hendelsesobjektet slik: if (e.type === "keydown") { knapper[e.keyCode] = true; } else { knapper[e.keyCode] = false; }

Og da bør det gå an å gå på skrå! (På elevnettstedet kan du finne en utvidet kode som gjør dette.)

16.2

Flexible Box Layout (flexbox)

Vi har nå sett på hvordan vi kan plassere enkeltelementer på en nettside akkurat der vi ønsker. Vi kan bruke den samme framgangsmåten til å lage layout, men det er en lite fleksibel løsning. Vi bør derfor unngå posisjonering av elementer når vi skal lage mer avanserte layouter. Til det anbefaler vi «Flexible Box Layout», også kjent som flexbox. Flexbox, som navnet antyder, lar oss lage fleksible bokser. Det vil si at vi kan lage elementer som ikke bestemmes av faste størrelser, men som tilpasser seg plassen som er tilgjengelig i forhold til nettleseren. Vår jobb blir å styre denne fleksibiliteten slik at vi får den layouten vi ønsker oss. Flexbox kan deles opp i to komponenter: (i) innpakningselementet (eng. parent element), som er elementet vi kaller flexbox, og (ii) «barna» / elementene inni innpakningen (eng. children/items). Vi angir ulike CSS-egenskaper for de to komponentene. Innpakningselementet styrer hvordan flexboxen skal oppføre


260 Posisjonering med CSS

seg, mens «barna» har egenskaper som påvirker deres oppførsel inni innpakningen.

Parent For å lage en flexbox (et «parent»-element) bruker vi egenskapen display med verdien flex. Alle elementer som plasseres i «parent»-elementet, blir automatisk «barn» av det elementet. Det gjelder alle elementer, ikke bare <div>elementer.

Figur 16.3: Til venstre er egenskapene flex-direction: row; og flex-wrap: wrap; illustrert. Til høyre brukes flex-direction: column;.

Videre må vi velge flexboxens hovedakse, altså om flexboxen skal inneholde rader eller kolonner. Ved å angi flex-direction: column; vil elementene plasseres i en kolonne. Hvis det er <div>-elementer vi ønsker å plassere, vil jo de uansett plasseres i en kolonne, men med flexbox kan vi gi dem fleksibel høyde slik at de fyller opp all ledig plass i sitt «parent»-element. Det er ikke like lett å gjøre uten en flexbox. Ved å velge flex-direction: row; vil elementene bli plassert i en rad. I begge tilfeller, altså både for row og column, vil elementene plasseres i én kolonne eller én rad. Det vil si at elementene blir klemt sammen slik at det blir plass til dem. For å unngå det kan vi bruke flex-wrap: wrap;, som lar «barna» fordeles over flere kolonner/rader dersom det blir nødvendig. I figur 16.3 ovenfor kan du se flex-direction og flex-wrap illustrert, og i tabell 16.1 på neste side kan du se en oversikt over egenskapene vi kan bruke på «parent»-elementet. Noen av disse egenskapene er illustrert på side 268.

Barn For «barna» finnes det flere egenskaper vi kan bruke, men vi skal se på to av dem. Egenskapen flex-grow lar oss bestemme om «barna» skal fylle ut tilgjenglig plass, og hvordan de skal gjøre det. Ved å sette flex-grow: 0; vil


Posisjonering med CSS

Egenskap

261

Beskrivelse

display

Verdien flex gir oss en flexbox

flex-direction

row eller column

flex-wrap

wrap lar oss bryte rader/kolonner

justify-content

Styrer fordelingen av «barna» langs hovedaksen

align-items

Styrer fordelingen av «barna» langs andreaksen

Tabell 16.1: En oversikt over egenskaper som kan brukes på «parent»-elementer. Hovedaksen er vannrett hvis flex-direction er satt til row, og loddrett hvis flex-direction er satt til column.

elementet alltid ha sin faste størrelse. Ved å angi en heltallverdi (for eksempel 1 eller 2), vil elementet vokse tilsvarende verdien vi har angitt. Har alle elementer verdien 1, vil alle vokse like mye, mens et element med verdien 2, vil ta dobbelt så stor plass som et element med verdien 1. Denne egenskapen er illustrert på side 269. Den andre egenskapen vi vil angi for barna, er flex-basis. Her angir vi elementenes basisbredde. Den bredden vil elementene uansett ha, men vi kan altså la dem bli større ved å bruke flex-grow. Disse to egenskapene, i tillegg til noen egenskaper vi ikke ser nærmere på i denne boka, er oppsummert i tabell 16.2 nedenfor.

Egenskap

Beskrivelse

flex-basis

Elementets basisbredde

flex-grow

Lar elementene vokse og fylle ledig plass

flex-shrink

Spesifiserer hvordan elementene skal krympe

order

Bestemmer elementenes rekkefølge

align-self

Overstyrer verdien i align-items (angitt for «parent»-elementet)

Tabell 16.2: En oversikt over egenskaper som kan brukes på «barna» til en flexbox.

En vannrett flexbox-meny La oss se på et enkelt eksempel der vi lager en vannrett meny med flexbox. Vi ønsker oss en midtstilt nettside med en overskrift over en vannrett meny, som igjen ligger over et innholdselement. Resultatet kan du se i figur 16.4. I koden


262 Posisjonering med CSS

nedenfor har vi laget en innpakning som inneholder tre <div>-elementer: ett for overskriften, ett for menyen og ett for innholdet. <div class="innpakning"> <div class="topptekst"> <h1> Overskrift </h1> </div> <div class="meny"> <a href="#"> Lenke 1 </a> <a href="#"> Lenke 2 </a> <a href="#"> Lenke 3 </a> <a href="#"> Lenke 4 </a> </div> <div class="innhold"> <p> Lorem ipsum dolor ... </p> </div> </div>

Fordi <div>-elementer er «block»-elementer, havner de naturlig nedenfor hverandre. Vi trenger derfor ikke å bruke flexbox for å lage denne layouten. Det vi skal bruke flexbox til, er å lage en meny som fordeler lenkene sine jevnt utover innpakningens bredde. Det gjør vi ved å gjøre .meny-elementet til en flexbox med display: flex;. Videre angir vi at <a>-elementene skal ha flex-grow: 1;. Det fører til at <a>-elementene fyller ut tilgjengelig plass, og siden alle lenkene får verdien 1 for denne egenskapen, vil de bli like store. Det er altså veldig lite som skal til for å lage en fin meny med flexbox. Resten av CSS-koden som er brukt for å lage nettsiden i figur 16.4, er kjent fra før av.

Figur 16.4: En vannrett meny laget med flexbox. Her er det bare <div>-elementet som innholder menyen, som er en flexbox.

Flexbox i flexbox For å lage avanserte layouter kan vi få bruk for å plassere en flexbox inni en annen flexbox. Dette er illustrert i figur 16.5 på neste side. I layouten til venstre er det bare én flexbox med tre «barn». I layouten i midten har vi en


Posisjonering med CSS

263

flexbox ytterst, med to «barn» i en kolonne. Det nederste av de to «barna» er selv en flexbox, med to «barn» i en rad. I layouten til høyre har vi også en flexbox ytterst, med to «barn» i en rad. Det andre «barnet» er også en flexbox, med to «barn» i en kolonne. flexbox (parent)

flexbox (parent) og child samtidig

Figur 16.5: En flexbox kan plasseres inni en annen flexbox. Her er de blå boksene «parent»-elementer, de lyserøde boksene er både «barn» og «parent» samtidig, mens de grå boksene er bare «barn».

Vi skal se på hvordan vi kan lage layouten til høyre i figur 16.5, ved å lage resultatet du kan se i figur 16.6 på side 265. De andre layoutene i figur 16.5 bør være greie å lage når du har sett dette eksemplet. I kode 16.3 på neste side kan du se koden som gir oss resultatet i figur 16.6. I HTML-koden har vi et <div>-element (.innpakning), som inneholder to andre <div>-elementer (.venstre og .hoyre). Det venstre <div>-elementet har en grå bakgrunnsfarge, mens det høyre <div>-elementet har en oransje bakgrunnsfarge. Innpakningen er en flexbox, det venstre <div>-elementet har flex-grow: 1; og det høyre <div>-elementet har flex-grow: 2;. Det fører til at det høyre elementet blir dobbelt så bredt som det venstre. Vi har også lagt til litt margin, slik at boksene blir lettere å skille fra hverandre. Vi har videre gjort det høyre <div>-elementet til en flexbox, med flex-direction: slik at «barna» blir plassert i en kolonne. Inni dette <div>-elementet har vi to <div>-elementer (.innhold), som begge har flex-grow: 1;, slik at de blir like høye og fyller ut boksen de er inni. column;,


264 Posisjonering med CSS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

< ! doctype html > < html > < head > < title > flexbox i flexbox < / title > < s tyle > body { background-color: # e0e0e0; font-family: " Century Gothic " , sans-serif; font-size: 14 px; color: #323232; } . innpakning { width: 700 px; min-height: 300 px; margin: 50 px auto; background-color: #323232; display: flex; } . venstre , . hoyre { margin: 5 px; } . venstre { background-color: # c3c3c3; flex-grow: 1; } . hoyre { flex-grow: 2; display: flex; flex-direction: column; background-color: # FF6A36; } . innhold { flex-grow: 1; background-color: # c3c3c3; margin: 5 px; }

37 38 39

< / s tyle > < / head >

40 41

< b ody >

42 43 44 45 46 47 48 49

< div class= " innpakning " > < div class= " venstre " > < / div > < div class= " hoyre " > < div class= " innhold " > < / div > < div class= " innhold " > < / div > < / div > < / div >

50 51 52

< / b ody > < / html >

Kode 16.3: Kode som gjenskaper layouten til høyre i figur 16.5.


Posisjonering med CSS

265

Oppgaver 16.11 Klarer du å midtstille et element perfekt ved å bruke flexbox? Elementet skal altså være midstilt både vannrett og loddrett. (Det var ikke like lett før flexbox dukket opp.) 16.12 Gjenskap de tre layoutene i figur 16.5 på side 263. 16.13 Gjenskap layoutene nederst på side 269.

Figur 16.6: Resultatet av kode 16.3.

CSS Grid Layout Vi har i dette kapitlet sett på flexbox, som er en relativt ny modul i CSS. Du har kanskje lagt merke til at flexbox har sin styrke i å definere layouter i én dimensjon. Vi har for eksempel brukt to flexboxer om vi ønsker en kolonne med bokser inni et element som er i en rad med bokser (to dimensjoner med bokser). CSS Grid Layout lar oss derimot spesifisere en layout i to dimensjoner. Grid har foreløpig dårlig støtte i de ulike nettleserne. Det er også mer krevende å bruke grid enn flexbox, men om du er interessert i layout, bør du absolutt undersøke hvordan grid fungerer. Vi vil lage vår egen guide på elevnettstedet når grid støttes i flere nettlesere.


flex-items / flex-children (alle elementer inni innpakningen)

parent (innpakning) med display: flex;

justify-content: flex-start;

justify-content: space-between;

justify-content: center;

justify-content: space-around;

justify-content: flex-end;

align-items: flex-start;

align-items: center;

align-items: flex-end;

align-items: stretch;

align-items: baseline; tekst

tekst

tekst


flex-grow: 0; på alle children / items (de røde boksene)

flex-grow: 1; på alle children / items (de røde boksene)

flex-grow: 1; på de to røde boksene, og flex-grow: 2; på den blå boksen

Inspirasjon Alle layoutene nedenfor lar seg lage med flexbox. Se om du klarer å kopiere dem.


268 Posisjonering med CSS

Sammendrag Vi kan styre et elements plassering på en nettside med CSS-egenskapen position. I utgangspunktet har alle elementer position: static;. Ved å bruke position: absolute; kan vi plassere et element i forhold til elementet det er inni, så lenge det elementet også er posisjonert (for eksempel med position: relative;). Ved å bruke position: fixed; kan vi plassere et element akkurat der vi vil. Det vil aldri flytte på seg, og det vil følge vinduet hvis vi beveger oss nedover eller oppover på en nettside. Egenskapene left, top, right og bottom lar oss angi hvor langt et element skal plasseres fra kantene til elementet det er plassert i forhold til. Vi kan bruke absoluttposisjonering og hendelser til å la HTML-elementer følge musepekeren eller bevege seg når vi trykker på piltastene. Vi lager en flexbox med display: flex;. En flexbox er en innpakning / et «parent»-element, mens elementene inni blir «barn» av flexboxen. Vi angir hvordan elementene skal fordele seg inni en «parent»-flexbox med flex-direction (row eller column). Vi kan midtstille elementer i en flexbox med egenskapene justify-content (vannrett) og align-items (loddrett). Vi kan sette en minimumsbredde for et flexbox-«barn» med flex-basis. Vi kan bestemme hvor mye flexbox-«barn» kan vokse i forhold til hverandre med egenskapen flex-grow.


Overganger og animasjoner med CSS

269

Kapittel 17

Overganger og animasjoner med CSS

Etter dette kapitlet skal du kunne • bruke overganger (eng. transitions) og animasjoner (eng. animations) i CSS • forklare når vi bruker overganger, og når vi bruker animasjoner pssst

Vi har allerede sett at CSS gir oss mange muligheter. I dette kapitlet skal vi se at vi kan bruke CSS til å lage fine overganger og avanserte animasjoner. CSS skiller mellom overganger (eng. transitions) og animasjoner (eng. animations), så la oss starte med å finne ut hva som er forskjellen mellom de to.

17.1

Overgang eller animasjon?

Før vi begynner, kan det være greit å presisere forskjellen mellom en overgang (eng. transition) og en animasjon (eng. animation). Felles for de to er at de lar oss gå gradvis fra én CSS-verdi til en annen, det vil si at vi for eksempel kan få en boks til å gli over skjermen i stedet for at den spretter fra ett punkt til et annet. Vi kan også lage en overgang fra fargen hvit til fargen svart; da vil vi få forskjellige gråtoner underveis i stedet for at fargen umiddelbart skifter fra hvit til grå. Overganger og animasjoner har også flere felles CSS-egenskaper, for eksempel hvor lang tid en overgang skal ta. Vi bruker en overgang når vi ønsker å gå fra én tilstand til en annen uten noen steg underveis og uten repetisjoner. Det vil si at et naturlig bruksområde for overganger er når vi fører musepekeren over et element (CSS-selektoren :hover). Animasjoner bruker vi hvis vi ønsker å gjenta en overgang, eller om vi ønsker flere steg underveis. Det vil si at vi bruker en animasjon hvis vi ønsker å lage en sirkel som beveger seg i en firkantet bane på en nettside. Da trenger vi flere steg i animasjonen vår, og vi får også muligheten til å gjenta animasjonen, slik at sirkelen beveger seg uten stopp.

Du finner videoer og andre ressurser på nettstedet på Lokus.


270 Overganger og animasjoner med CSS

17.2

Overganger (transitions)

En overgang kan utføres når vi endrer en CSS-egenskap for et HTML-element. Derfor må vi innføre en endring i en CSS-egenskap for at en overgang skal gi mening. Det kan vi for eksempel gjøre ved å bruke selektoren :hover, eller ved å lage en knapp og bruke JavaScript til å endre CSS-koden til et element. For at endringen skal skje gradvis, må vi angi noen egenskaper for elementet som skal forandres. La oss se på et enkelt eksempel først, der vi endrer størrelsen på et <div>element når vi fører musepekeren over det. I kode 17.1 på neste side har vi laget et enkelt <div>-element med bredde og høyde 150 piksler og en bakgrunnsfarge, i tillegg til informasjon om hva vi ønsker å endre med en overgang, og hvor lang tid endringen skal ta. Vi har også lagt til selektoren div:hover, der vi angir at elementet skal bli bredere når vi fører musepekeren over det. I figur 17.1a nedenfor kan du se et skjermbilde som er tatt underveis i overgangen.

a Overgang med endring av width

b Overgang med endring av border-radius

Figur 17.1: Skjermbilder tatt underveis i overgangen vi laget i kode 17.1 til venstre (a), og kode 17.2 til høyre (b).


Overganger og animasjoner med CSS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

271

< ! doctype html > < html > < head > < title > Overgang - bredde < / title > < s tyle > div { width: 150 px; height: 150 px; background-color: #359689; t r ansition-property: width; t r ansition-duration: 2 s; } div:hover { width: 400 px; } < / s tyle > < / head >

18 19

< b ody >

20 21

< div > < / div >

22 23 24

< / b ody > < / html >

Kode 17.1: En kode som gir en gradvis breddeøkning for et <div>-element.

Legg merke til at vi har angitt transition-property: width; for å angi at det er elementets bredde vi ønsker å endre gradvis. Vi har også angitt overgangens varighet med transition-duration: 2s;. Det finnes to andre egenskaper vi kan bruke: transition-timing-function, som vi skal se nærmere på senere i dette kapitlet, og transition-delay, som lar oss utsette en overgang. I tabell 17.1 nedenfor kan du se en oversikt over egenskapene vi kan bruke. I stedet for å angi egenskapene hver for seg, kan vi bruke en samlebetegnelse. Vi kan for eksempel skrive transition: width 2s; i kode 17.1 ovenfor.

Egenskap

Beskrivelse

transition-delay

Utsettelse av overgangen (i sekunder)

transition-duration

Overgangens varighet (i sekunder)

transition-property

Egenskapen(e) som skal endres

transition-timing-function

Overgangens fartskurve

transition

Samler flere egenskaper

Tabell 17.1: En oversikt over egenskaper som kan brukes for å lage overganger.


272 Overganger og animasjoner med CSS

Vi kan angi overganger for flere egenskaper samtidig. I kode 17.2 nedenfor har vi utvidet kode 17.1 med en endring i border-radius. Når vi skal angi overganger for flere egenskaper samtidig, kan vi enten angi transition-property: all;, eller vi kan spesifisere egenskapene vi ønsker: transition-property: width border-radius;. Du kan se et skjermbilde fra denne overgangen i figur 17.1b på side 270. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

< ! doctype html > < html > < head > < title > Overgang - bredde og kantlinje < / title > < s tyle > div { width: 150 px; height: 150 px; background-color: #359689; t r ansition-property: all; t r ansition-duration: 2 s; } div:hover { width: 400 px; border-radius: 50%; } < / s tyle > < / head >

19 20

< b ody >

21 22

< div > < / div >

23 24 25

< / b ody > < / html >

Kode 17.2: Her er kode 17.1 utvidet med en endring i border-radius

Oppgaver 17.1 Kopier kode 17.2 og legg til en endring i bakgrunnsfarge i overgangen, slik at figuren også skifter farge. 17.2 Prøv deg fram med ulike verdier for transition-duration, og se hvordan overgangen påvirkes. 17.3 Legg til transition-delay: 2s;. Hva gjør denne koden? 17.4 I kapittel 10 laget du et stilark med CSS-kode for <button>-elementet. Prøv deg litt fram med overganger ved å endre ulike egenskaper når musepekeren føres over knappen.


Overganger og animasjoner med CSS

273

Timing-funksjoner Når vi lager overganger og animasjoner, kan vi angi en timing-funksjon som bestemmer hvordan endringene skal fordele seg over tid. Vi kan for eksempel lage en overgang med en lineær timing-funksjon; da vil endringens fart være konstant. Alternativt kan vi bruke standardverdien, ease, som gjør at endringen først går sakte, så ganske raskt, og så sakte igjen. Det finnes mange ulike timing-funksjoner, og det går også an å lage sine egne. I figur 17.2 nedenfor kan du se et skjermbilde av nettsiden easings.net, der vi kan finne illustrerte varianter av mange timing-funksjoner. Hvilken timing-funksjon vi velger, har mye å si for hvordan en overgang eller animasjon oppleves. Vi skal se på en animasjon senere i kapitlet der vi må endre timing-funksjonen for å oppnå den effekten vi ønsker.

Figur 17.2: Nettsiden easings.net har fine illustrerte eksempler av timing-funksjoner. X-aksen viser hvor langt animasjonen har gått, og y-aksen viser tid.

Oppgaver 17.5 Lag en enkel overgang som flytter et element fra ett sted til et annet. Det kan være lurt å flytte elementet over en lang avstand. 17.6 Prøv ut timing-funksjoner fra easings.net på overgangen din. Hvis det er vanskelig å se forskjellene, hjelper det å gi overgangen lengre varighet.

Styre overganger med JavaScript Så langt har vi sett på overganger som utføres når vi fører musepekeren over et element. Men det kan også være nyttig å sette i gang en overgang med JavaScript. For å få til det skal vi bruke en egenskap som heter classList. Et HTML-element kan ha mer enn én CSS-klasse knyttet til seg, og ved å bruke classList kan vi styre hvilke klasser et element skal ha. Det vil si at vi kan legge til og ta bort klasser, slik at elementets CSS-egenskaper endrer seg, og en


274 Overganger og animasjoner med CSS

overgang kan skje. Vi kan også legge til enkeltegenskaper, men om vi ønsker å reversere overgangen, som vi skal gjøre i kode 17.3 nedenfor, er det nødvendig å legge til eller fjerne CSS-klasser. I kode 17.3 nedenfor har vi laget et standard-utseende for et <div>-element, i tillegg til ekstra klasser. Klassen .flyttHoyre forskyver <div>-elementets posisjon mot midten av vinduet, mens klassen .flyttVenstre tar <div>-elementet tilbake til venstre kant av vinduet. Når knappens tekst (value) har verdien «start», blir klassen .flyttHoyre lagt til, og knappens tekst blir endret. Vi må også fjerne klassen .flyttVenstre fordi på et senere tidspunkt vil den klassen også bli lagt til. Hvis knappens tekst ikke er «start», gjør vi det omvendte, og flytter boksen tilbake mot venstre. I figur 17.3 på side 275 kan du se et skjermbilde som er tatt underveis i overgangen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

< html > < head > < title > Styre transition med knapp < / title > < meta charset= " UTF-8 " > < s tyle > . boks { margin: 30 px; margin-left: 0; height: 50 px; width: 50 px; background-color: blue; transition: margin-left 3 s; } . flyttHoyre { margin-left: 50%; } . flyttVenstre { margin-left: 0; } < / s tyle > < / head > < b ody >

23 24

< div class= " boks " > < / div >

25 26

< b utton id= " knapp " > start < / b utton >

27 28 29 30 31

< s cript > // Henter div-elementet og knappen var boksEl = document . querySelector ( " . boks " ) ; var knappEl = document . querySelector ( " # knapp " ) ;

32 33 34

// Lytter etter click-hendelser p å knappen knappEl . addEventListener ( " click " , knappFunksjon ) ;

35 36 37 38

// N å r knappen trykkes function knappFunksjon () { if ( knappEl . innerHTML === " start " ) {


Overganger og animasjoner med CSS

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

275

// Endrer knappens tekst knappEl . innerHTML = " tilbake " ; // Bruker classList for å ta bort klassen flyttVenstre boksEl . classList . remove ( " flyttVenstre " ) ; // Bruker classList for å legge til klassen flyttHoyre boksEl . classList . add ( " flyttHoyre " ) ; } else { // Endrer knappens tekst knappEl . innerHTML = " start " ; // Bruker classList for å ta bort klassen flyttHoyre boksEl . classList . remove ( " flyttHoyre " ) ; // Bruker classList for å legge til klassen flyttVenstre boksEl . classList . add ( " flyttVenstre " ) ; }

} < / s cript >

55 56 57

< / b ody > < / html >

Kode 17.3: En kode som bruker JavaScript og en knapp til å styre overganger.

I koden ovenfor bruker vi classList.add() og classList.remove() for å legge til og ta bort klassene. Alternativt kan vi bruke metoden classList.toggle() som skrur på eller av klassen vi angir. Det betyr at vi i stedet kan skrive boksEl.classList.toggle("flyttHoyre") og boksEl.classList.toggle("flyttVenstre") for begge tilfeller (høyre og venstre). For at det skal fungere, må vi gi <div>elementet klassen .flyttVenstre fra start slik at den skrus av når .flyttHoyre skrus på (i stedet for å skrus på).

Figur 17.3: Skjermbilde tatt underveis i overgangen i kode 17.3.


276 Overganger og animasjoner med CSS

Oppgaver 17.7 Lag en forenklet versjon av kode 17.3 der knappen flytter elementet til høyre, men ikke tilbake igjen. 17.8 Utvid koden slik at overgangen kan gå begge veier. 17.9 Gjør om overgangen i koden din slik at noe annet skjer (for eksempel en endring av elementets størrelse eller farge). 17.10 Lag et HTML-dokument med et absolutt-posisjonert <div>-element på midten av siden. Legg til hendelser slik at venstre piltast gjør boksen smalere, høyre piltast gjør den bredere, piltasten oppover gjør den høyere, og piltasten nedover gjør den lavere. Alle endringene skal skje i form av overganger.

Overgangshendelser Overganger og animasjoner har sine egne hendelser. For overganger har vi hendelsen «transitionend», som inntreffer når en overgang er ferdig. Vi må lytte på elementet som gjennomgår en overgang, for å plukke opp denne hendelsen. For å illustrere hvordan denne hendelsen kan brukes, skal vi utvide kode 17.1 på side 271. Vi ønsker å skrive teksten «FERDIG» inni <div>-elementet når overgangen er ferdig. For å få til det kan vi legge til følgende JavaScript-kode: var divEl = document.querySelector("div"); divEl.addEventListener("transitionend", ferdig); function ferdig() { divEl.innerHTML = "FERDIG"; }

Her henter vi først <div>-elementet, før vi legger til en lytter etter hendelsen «transitionend». Hvis denne hendelsen inntreffer, skal funksjonen ferdig() kjøres. I den funksjonen legger vi til teksten «FERDIG» inni <div>-elementet. Hvis du ønsker et mer elegant resultat, med fullstendig midtstilt tekst, kan du legge til følgende CSS-kode på <div>-elementet: display: flex; align-items: center; justify-content: center; font-size: 24px;

Her endrer vi <div>-elementet til en flexbox, og sier at «barna» (items) skal plasseres på midten i både vannrett og loddrett retning.


Overganger og animasjoner med CSS

277

Oppgaver 17.11 I koden beskrevet på forrige side inntreffer hendelsen «transitionend» to ganger. Kan du finne ut hvor den inntreffer den andre gangen? Hint: Det kan være stor hjelp i console.log() når du leter etter hendelser. 17.12 Kan du utvide koden beskrevet på forrige side slik at teksten «FERDIG» forsvinner når elementet er tilbake i sin opprinnelige størrelse?

17.3

Animasjoner

En animasjon er litt mer komplisert å lage enn en overgang, men den gir oss muligheten til å gjenta overganger, i tillegg til at vi kan ha flere steg underveis. For å lage en animasjon må vi først definere alle stegene vi ønsker at animasjonen skal gjennomgå. Det gjør vi med CSS-koden @keyframes. En «keyframe» er en spesifisert tilstand underveis i en animasjon. Alle punkter mellom våre «keyframes» vil bli beregnet akkurat som mellom to punkter i en overgang (transition). La oss se på et enkelt eksempel (denne koden skrives der vi skriver CSS-kode): @keyframes forstorr { from { width: 50px; to

}

{ width: 100px; }

}

Her har vi definert hvordan animasjonen med navnet «forstorr» skal oppføre seg. Vi angir at den skal gå fra (from) bredden 50 piksler til (to) bredden 100 piksler. Vi kan også angi stadiene med prosenter: @keyframes forstorr { 0%

{ width: 50px;

100%

{ width: 100px; }

}

}

For å bruke animasjonen må vi skrive animation-name: forstorr; for elementet vi ønsker å animere. Hvis vi også legger til animation-duration: 2s;, får vi det samme som om vi lager en overgang (transition) som forstørrer et element over 2 sekunder. La oss heller lage noe som vi ikke kan lage med overganger. Nettsiden kahoot.it har en bakgrunn som skifter farge med jevne mellomrom (se skjermbilde av en av fargene i figur 17.4 på neste side). For å få til noe lignende kan vi bruke koden på neste side. Her bruker vi også prosentverdier for å angi flere stadier i animasjonen:


278 Overganger og animasjoner med CSS

@keyframes fargespekter { 0%

{ background-color: #38c0d3; }

25%

{ background-color: #64bf3e; }

50%

{ background-color: #fea602; }

75%

{ background-color: #ff3454; }

100%

{ background-color: #38c0d3; }

}

Legg merke til at første og siste farge er like. Det er fordi vi ønsker å skape et inntrykk av at fargene går i en «sirkel», slik at vi ikke hopper rett fra siste farge til en ny farge (0% kommer rett etter 100% hvis vi gjentar animasjonen). For å anvende denne animasjonen på <body>-elementet kan vi skrive: body { animation-name: fargespekter; animation-duration: 10s; animation-iteration-count: infinite; }

Eller med forenklet skrivemåte: body { animation: fargespekter 10s infinite; }

Denne siden vil da gradvis bevege seg gjennom fire farger i løpet av 10 sekunder. Igjen og igjen (infinite).

Figur 17.4: Skjermbilde av nettsiden kahoot.it.


Overganger og animasjoner med CSS

279

Oppgaver 17.13 Bruk kodene beskrevet på forrige side og lag din egen «Kahoot»-bakgrunn. 17.14 Lag en animasjon som flytter et element i en firkantet bane rundt på skjermen. 17.15 Utvid animasjonen din slik at elementet også forandrer seg mens det beveger seg rundt. Du kan for eksempel la elementet være stort i to av hjørnene, og lite i to av dem, og du kan la elementet skifte farge i hvert hjørne.

Animasjonsegenskaper Animasjoner har de samme egenskapene som overganger, i tillegg til en del andre. For å skape litt oversikt går vi gjennom de mest avanserte av dem her. Egenskapene animation-name, animation-duration, animation-delay og animation -timing-function fungerer som overgangs-egenskapene med lignende navn. Egenskapen animation-iteration-count lar oss angi hvor mange ganger en animasjon skal gjennomføres. Standardverdien er 1. Her kan vi angi et heltall, eller vi kan angi infinite, som fører til at animasjonen gjentas i det uendelige (som i fargespekter-eksemplet i forrige avsnitt). Egenskapen animation-direction lar oss bestemme animasjonens «retning». Standardverdien, normal, lar animasjonen gå fra 0% til 100%. Alternativt kan vi angi reverse, som gjør at animasjonen går baklengs, eller alternate, som gjør at animasjonen skifter «retning» annenhver gang (Først fra 0% til 100% og så fra 100% til 0%, osv.). I tillegg finnes verdien alternate-reverse, som gjør det omvendte av alternate. Egenskapen animation-fill-mode er viktig å ha kjennskap til for animasjoner som har en slutt. Altså animasjoner som ikke gjentas uendelig mange ganger. Det er nemlig slik at et element vil få samme utseende som før en animasjon hvis vi ikke gjør noe. Ved å sette animation-fill-mode til forwards, vil elementet beholde utseendet det hadde i slutten av animasjonen. Den siste egenskapen, animation-play-state, lar oss kontrollere animasjoner med JavaScript. Vi kan angi verdien paused hvis vi ønsker å sette animasjonen på pause. Hvis vi ønsker å starte animasjonen igjen, kan vi sette verdien til running.


280 Overganger og animasjoner med CSS

Egenskap

Beskrivelse

@keyframes animasjonsnavn

For å angi keyframes

animation-name

Animasjonens navn

animation-duration

Animasjonens varighet

animation-timing-function

Animasjonens fartskurve

animation-delay

Utsettelse av animasjonen (i sekunder)

animation-iteration-count

Antall repetisjoner (ev. infinite)

animation-direction

Animasjonens retning

animation-fill-mode

Elementets utseende etter animasjonen

animation-play-state

running hvis spiller, paused hvis pause

animation

Samler flere egenskaper

Tabell 17.2: Oversikt over animasjonsegenskaper. Det kan være lurt å bruke enkeltegenskaper i stedet for samlebetegnelsen fordi det er så mange egenskaper.

Oppgaver 17.16 Fortsett med koden du laget i oppgave 17.15. Prøv ulike animasjonshastigheter og timing-funksjoner (som vist i figur 17.2 på side 273). 17.17 Prøv egenskapen animation-direction med verdiene reverse, alternate og alternate-reverse. Du må se på flere runder av animasjonen for å se effekten av disse.

Styre animasjoner med JavaScript Nå som vi kjenner til animasjonsegenskapene og hvordan vi lager enkle animasjoner, er det på tide å lage en stilig animasjon der vi bruker JavaScript. Vi skal lage en kode som genererer bokser (firkantede <div>-elementer) som plasseres etter hverandre på en linje. Deretter skal hver av boksene animeres slik at de beveger seg ned og opp. Vi ønsker å legge inn forsinkelser, slik at animasjonen sprer seg mot høyre som en bølge. Du kan se et skjermbilde av resultatet i figur 17.5 på side 282, og den fullstendige koden i kode 17.4 på neste side. Det første vi gjør, er å lage en innpakning der vi skal plassere boksene som skal animeres. Innpakningen får position: relative;, slik at vi kan absoluttposisjonere boksene inni innpakningen. Videre definerer vi boksene med bredde 20 piksler, høyde 5 piksler og en bakgrunnsfarge. Alle boksene skal plasseres inntil toppen av innpakningen (top: 0px;). Vi kommer tilbake til hvorfor de ikke har


Overganger og animasjoner med CSS

281

noen left-verdi. Alle boksene får også animasjonen «sprett» knyttet til seg. Den skal vare i 2 sekunder, gjentas i det uendelige og bruke timing-funksjonen «ease-in-out». I JavaScript-koden lager vi <div>-elementer med createElement(). De nye elementene får alle CSS-klassen .animer. De plasseres etter hverandre mot høyre, ved at vi hele tiden øker left-verdien deres, og alle får en større forsinkelse (animationDelay) fordi den også bestemmes av løkkens teller (i). Det fører til at boksene ikke begynner å bevege seg samtidig. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

< html > < head > < title > Bokser som danser < / title > < meta charset= " UTF-8 " > < s tyle > body { background-color: #323232; } . innpakning { width: 600 px; height: 300 px; margin: 50 px auto; background-color: #1 a4741; position: relative; } . animer { position: absolute; top: 0 px; width: 20 px; height: 5 px; background-color: #54 ffe8; animation: sprett 2 s infinite ease-in-out; } @keyframes sprett { 0% { top: 0 px; } 50% { top: 295 px; } 100% { top: 0 px; } } < / s tyle > < / head > < b ody >

32 33 34

< div class= " innpakning " > < / div >

35 36 37 38 39 40

< s cript > // Henter innpakningen der vi skal plassere bokser var innpakningEl = document . querySelector ( " . innpakning " ) ; // Bestemmer hvor mange bokser vi vil lage ( og animere ) var antallBokser = 30;

41 42 43

// L ø kke som lager og animerer alle boksene for ( var i = 0; i < antallBokser; i ++) {


282 Overganger og animasjoner med CSS

44 45 46 47 48 49 50 51 52 53 54 55 56

// Lager en ny boks ( et nytt div-element ) var nyBoks = document . createElement ( " div " ) ; // Legger til klassen . animer til den nye boksen nyBoks . className = " animer " ; // evt . nyBoks . classList . add (" animer ") ; // Plasserer boksene vannrett etter hverandre nyBoks . style . left = i * 20 + " px " ; // Utsetter hver animasjon , slik at de ikke starter samtidig nyBoks . style . animationDelay = i * 0.2 + " s " ; // Legger boksen til i innpakningen innpakningEl . appendChild ( nyBoks ) ;

} < / s cript >

57 58 59

< / b ody > < / html >

Kode 17.4: En kode som lager <div>-elementer og styrer elementenes animasjoner med JavaScript.

Figur 17.5: Skjermbilde av startfasen av kode 17.4. Firkantene danner en bølge.

Oppgaver 17.18 Kopier kode 17.4 ovenfor. Fjern timing-funksjonen ease-in-out fra koden. Observer hvor viktig timing-funksjonen er for opplevelsen av animasjonen. Prøv deg også fram med andre timing-funksjoner. 17.19 Bruk variabelen antallBokser til å øke antallet firkanter, og la antallet bestemme innpakningens bredde (slik at få firkanter gir en smal innpakning, og mange firkanter gir en bred innpakning). 17.20 Hva tror du skjer om du gir elementene en tilfeldig verdi for animationDelay? Prøv det! 17.21 Gjør om kode 17.4 slik at boksene får ulik fart (ulik animation-duration).


Overganger og animasjoner med CSS

283

Animasjonshendelser Animasjoner har som nevnt tilhørende hendelser, blant annet hendelsen «animationend», som tilsvarer «transitionend», som vi så på tidligere i kapitlet. I tillegg har vi hendelsen «animationiteration», som fyrer av hver gang en animasjon gjentas, men ikke etter siste repetisjon, da fyres bare en «animationend»hendelse av. Vi har også «animationstart», som fyrer av når en animasjon starter. I tillegg har hendelsesobjektene til alle disse hendelsene egenskapen elapsedTime, som forteller oss hvor lang tid som har gått siden animasjonen startet (i sekunder). I kode 17.5 nedenfor undersøker vi disse tre hendelsene. Vi har to elementer som skal gjennom den samme animasjonen: en animasjon som lar elementene gå fra usynlig til synlig. Det første elementet (.boks) går gjennom animasjonen fem ganger i vekslende retning. Det vil si at elementet først blir synlig, deretter usynlig, så synlig, osv. Når den animasjonen er ferdig, starter det andre elementet, som skal gjenta den samme animasjonen tre ganger. For begge elementene skriver vi hele tiden de tre hendelsene og elapsedTime til konsollen. Du kan se resultatet i konsollen i figur 17.6 på side 284. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

< ! doctype html > < html > < head > < title > Animasjonshendelser < / title > < s tyle > . boks , . sirkel { width: 100 px; height: 100 px; margin: 10 px; animation-name: vis; a nimation-duration: 2 s; a n i m a t i o n - t i m i n g - f u n c t i o n : linear; a n imation-direction: alternate; } . boks { background-color: red; a n i mati on-p lay-s tate : running; a n i m a t i o n - i t e r a t i o n - c o u n t : 5; } . sirkel { background-color: blue; border-radius: 50%; a n i mati on-p lay-s tate : paused; a n i m a t i o n - i t e r a t i o n - c o u n t : 3; } @keyframes vis { 0% { opacity: 0; } 100% { opacity: 1; } } < / s tyle > < / head >


284 Overganger og animasjoner med CSS

33

< b ody >

34 35

< div class= " boks " > < / div >

36 37

< div class= " sirkel " > < / div >

38 39 40 41 42

< s cript > // Henter de to elementene var boksEl = document . querySelector ( " . boks " ) ; var sirkelEl = document . querySelector ( " . sirkel " ) ;

43 44 45 46 47 48 49 50

// Legger til alle animasjonshendelser p å begge elementene boksEl . addEventListener ( " animationstart " , lytteFunksjon ) ; boksEl . addEventListener ( " animationiteration " , lytteFunksjon ) ; boksEl . addEventListener ( " animationend " , lytteFunksjon ) ; sirkelEl . addEventListener ( " animationstart " , lytteFunksjon ) ; sirkelEl . addEventListener ( " animationiteration " , lytteFunksjon ) ; sirkelEl . addEventListener ( " animationend " , lytteFunksjon ) ;

51 52 53 54 55 56

// Funksjon som kj ø rer hver gang en hendelse fyres av function lytteFunksjon ( e ) { // Skriver hvilken hendelse som inntraff , og tidspunktet // til konsollen console . log ( e . type + " tid: " + e . elapsedTime ) ;

57 58 59 60 61 62 63

}

// Hvis hendelsen animationend inntreffer if ( e . type === " animationend " ) { // Sett i gang animasjonen til sirkelelementet sirkelEl . style . animationPlayState = " running " ; }

64 65 66 67

< / s cript > < / b ody > < / html >

Kode 17.5: Animasjonen «vis» genererer hendelser som vi kan undersøke i konsollen.

Figur 17.6: Skjermbilde av konsollen under kjøring av kode 17.5.


Overganger og animasjoner med CSS

285

Oppgaver 17.22 Lag to animasjoner i det samme dokumentet. Den ene skal være en boks som går fram og tilbake mellom høyre og venstre side av nettleseren. Den andre animasjonen skal være en boks som går opp og ned mellom toppen og bunnen av nettleseren. Den vannrette animasjonen skal gjentas uendelig, mens den loddrette animasjonen skal kjøres for hver oddetallrepetisjon av den vannrette animasjonen. Det vil si at animasjon nummer to (den loddrette) skal gjentas når den første animasjonen (den vannrette) har gått 1, 3, 5, 7, 9, osv. runder. 17.23 Utfordring: Gjør om koden i forrige oppgave slik at animasjon nummer to bare gjentas når den første animasjonen har gått et primtall antall runder (1, 3, 5, 7, 11, osv.). Du kan selv velge hvor mange primtall du ønsker å bruke.

Sammendrag Vi bruker overganger (eng. transitions) når vi ønsker å gå fra én tilstand til en annen én gang. Vi bruker animasjoner (eng. animations) når vi ønsker å gå fra én tilstand til en annen over flere steg, flere ganger. Vi lager en overgang ved å angi en endring i et elements CSS-egenskaper. Vi kan for eksempel gjøre det ved å bruke selektoren :hover eller ved å legge til ekstra klasser med JavaScript. En timing-funksjon bestemmer hvordan hastighet skal fordeles i en overgang. De samme timing-funksjonene kan brukes på både overganger og animasjoner. Vi kan styre overganger med JavaScript ved å legge til og fjerne klasser i en classList. Hendelsen «transitionend» fyres av når en overgang er ferdig. Vi styrer tidsforløpet til en animasjon ved å skrive @keyframes i CSS-koden vår. Vi kan bestemme stegene i animasjonen ved å angi prosenter. Vi bruker animasjonen ved å angi animation-name, i tillegg til andre egenskaper, for CSS-elementet som skal animeres. Vi kan styre animasjoner med JavaScript ved å redigere CSS-egenskaper direkte. Animasjoner har hendelsene «animationstart», «animationend» og «animationiteration». De ulike hendelsene har alle egenskapen elapsedTime.


286 Transformasjoner med CSS

Kapittel 18

Transformasjoner med CSS

Etter dette kapitlet skal du kunne • transformere utseendet til HTML-elementer med CSS • lage animasjoner og overganger med transformasjoner pssst Du finner videoer og andre ressurser på nettstedet på Lokus.

En transformasjon (eng. transformation) lar oss gjøre om et elements utseende ved å for eksempel forstørre eller flytte på det uten at elementene rundt blir påvirket. Ved å kombinere ulike transformasjoner kan vi blant annet lage fine animasjoner. De kan også gjøre det lettere å lage ulike layouter og former med HTML.

18.1

Transformasjoner

Alle transformasjoner er samlet i én CSS-egenskap: transform. Det betyr at ulike transformasjoner lages med ulike verdier for denne egenskapen. Et utvalg av verdiene vi kan bruke, er oppsummert i tabell 18.1 på side 288. Hvis vi for eksempel ønsker å flytte et element, kan vi bruke verdien translate: div { transform: translate(200px, 300px); }

Her flytter vi alle <div>-elementer 200 piksler mot høyre og 300 piksler nedover (husk at origo er øverst til venstre i nettleservinduet). Fordelen med å bruke translate i stedet for margin eller absoluttposisjonering er at vi ikke trenger å tenke på resten av innholdet på siden vår. Elementet blir flyttet, men det påvirker bare elementer inni seg når vi bruker translate. La oss se på et eksempel. I kode 18.1 på neste side har vi laget en flexbox med tre bokser inni. Resultatet kan du se i figur 18.1 på side 289. Alle boksene har flex-grow: 1; og vil i utgangspunktet fordele seg med like bredder i innpakningen (den lyse firkanten i bakgrunnen). I tillegg har vi anvendt ulike


Transformasjoner med CSS

287

transformasjoner på de tre boksene. Legg merke til at boksene har to CSSklasser: én for de felles egenskapene, og én for boksens unike egenskaper. Den første boksen (rød) har blitt flyttet litt ned mot venstre med translate. Den andre boksen (blå) har blitt rotert 20 grader med klokka. Den tredje boksen (grønn) har fått en forminsket bredde og en økt høyde med scale, i tillegg til at hele boksen har blitt forskjøvet 40 grader mot venstre med skew. Figur 18.2 på side 289 viser hvordan skew fungerer. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

< html > < head > < title > Transformasjoner < / title > < meta charset= " UTF-8 " > < s tyle > body { background-color: #323232; } . innpakning { background-color: # fff3b1; width: 600 px; height: 200 px; margin: 100 px auto; display: flex; } . boks { flex-grow: 1; margin: 5 px; } . boks1 { background-color: # ed1e24; transform: translate ( -20px , 50 px ) ; } . boks2 { background-color: #371 eed; transform: rotate (20 deg ) ; } . boks3 { background-color: #1 eed37; transform: scale (0.8 , 1.6) skew (40 deg ) ; } < / s tyle > < / head > < b ody >

35 36 37 38 39 40

< div class= " innpakning " > < div class= " boks boks1 " > < / div > < div class= " boks boks2 " > < / div > < div class= " boks boks3 " > < / div > < / div >

41 42 43

< / b ody > < / html >

Kode 18.1: Tre bokser i en flexbox som blir transformert på ulike måter.


288 Transformasjoner med CSS

rotateX(x) el. rotateY(y)

rotate(nturn)

rotate(vinkel)

scaleX(x) el. scaleY(y)

scale(x, y)

translateX(x) / translateY(y)

translate(x, y)

Kombinerer skewX() og skewY (se nedenfor)

Roterer et element rundt én av aksene (finnes også for Z)

Samme som over, men vi kan angi 1turn for en hel runde, eller 0.5turn for en halv

Roterer et element med klokka. Husk å skrive «deg» etter antall grader, for eksempel: rotate(30deg)

Skalerer et element langs én av aksene

Skalerer et element i x- og y-retning (angi én verdi for å skalere like mye i begge retninger)

Flytter et element langs én av aksene

Flytter et element i x- og y-retning (i to dimensjoner)

Beskrivelse

skew(x-vinkel, y-vinkel)

Skyver punktene i et element i positiv eller negativ retning i forhold til valgt akse

Verdi

skewX(x) el. skewY(y)

Tabell 18.1: Noen verdier som kan brukes for å lage transformasjoner.


Transformasjoner med CSS

289

Figur 18.1: Resultatet av kode 18.1.

30°

30°

Figur 18.2: Illustrasjon av hvordan skewX(30deg) fungerer. Vinkelen 30 grader er mellom x-aksen og en linje som står vinkelrett på x-aksen. Det lyseblå området blir «lagt til» og det røde området «fjernes».

Oppgaver 18.1 Kopier kode 18.1 og se hvordan den ser ut uten transformasjonene. 18.2 Prøv deg fram med transformasjonene i tabell 18.1 på side 288 og se hvilke andre ting du kan gjøre med boksene i kode 18.1. Hva skjer for eksempel om du roterer selve innpakningen? 18.3 Legg til CSS-koden overflow: hidden; på innpakningen. Hva gjør denne koden?

18.2

Animasjon med transformasjoner

Nå som vi lett kan flytte på, skalere og rotere elementer, åpner det seg mange morsomme animasjonsmuligheter. Vi skal se på to eksempler på hvordan vi kan kombinere transformasjoner og animasjoner. Den første animasjonen skalerer sirkler slik at de vokser utover skjermen. Du kan se et skjermbilde av animasjonen i figur 18.3 på side 291. For å få til det har vi absoluttposisjonert tre <div>-elementer, og gitt dem border-radius: 50%; for å få runde kanter. Vi har også laget en animasjon som skalerer sirklene fra 0 ganger størrelsen (usynlige) til 6 ganger størrelsen. Alle sirklene bruker altså denne animasjonen, men de har fått ulik varighet og ulike fartskurver, slik at de oppfører seg ulikt. Fullstendig kode kan du se på neste side (kode 18.2).


290 Transformasjoner med CSS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

< html > < head > < title > Sirkler < / title > < meta charset= " UTF-8 " > < s tyle > body { background-color: #323232; overflow: hidden; } div { position: absolute; left: 30%; top: 20%; width: 200 px; height: 200 px; border-radius: 50%; border: 7 px solid tomato; } . sirkel1 { animation: skaler 2 s infinite linear; } . sirkel2 { animation: skaler 3.5 s infinite ease-in-out; } . sirkel3 { animation: skaler 4.2 s infinite; } @keyframes skaler { 0% { transform: scale (0) ; } 100% { transform: scale (6) ; } } < / s tyle > < / head > < b ody >

35 36 37 38

< div class= " sirkel1 " > < / div > < div class= " sirkel2 " > < / div > < div class= " sirkel3 " > < / div >

39 40 41

< / b ody > < / html >

Kode 18.2: En kode som skalerer tre sirkler med ulik hastighet og fartskurve.


Transformasjoner med CSS

291

Figur 18.3: Skjermbilde tatt underveis i kjøringen av kode 18.2.

Oppgaver 18.4 Ta utgangspunkt i kode 18.2 og gjør endringer slik at sirklene blir laget og plassert med JavaScript. 18.5 Utvid koden slik at det plasseres mange sirkler på vilkårlige posisjoner, spredd over hele skjermen. 18.6 Finn et passende lydklipp på Internett (husk opphavsrett) som du spiller av i bakgrunnen av animasjonen. Brukeren skal ha anledning til å skru av lyden.

Den andre animasjonen vi skal se på, fyller opp en innpakning med <div>elementer som roterer med ulike hastigheter. De skifter også farge underveis i animasjonen. Du kan se et skjermbilde av resultatet i figur 18.4 på side 293. Vi bruker altså JavaScript til å generere mange <div>-elementer inni en innpakning, og til å gi dem en tilfeldig varighet (animation-duration) med Math.random(). Alle boksene har ellers den samme animasjonen. I selve animasjonen utnytter vi at vi kan angi flere stadier underveis. Vi sier at boksene skal rotere til 45 grader i løpet av halve tiden, og videre til 180 grader på resten av tiden. Det fører til at de først roterer sakte, og så roterer fort. Du kan se hele koden på neste side (kode 18.3).


292 Transformasjoner med CSS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

< html > < head > < title > Snurrende firkanter < / title > < meta charset= " UTF-8 " > < s tyle > body { background-color: #323232; } . innpakning { width: 600 px; height: 200 px; margin: 100 px auto; display: flex; flex-wrap: wrap; } . boks { width: 40 px; height: 40 px; border-radius: 10 px; margin: 5 px; animation: roter linear infinite; } @keyframes roter { 0% { transform: rotate (0 deg ) ; background-color: red; } 50% { transform: rotate (45 deg ) ; background-color: yellow; } 100% { transform: rotate (180 deg ) ; background-color: red; } } < / s tyle > < / head > < b ody >

31 32

< div class= " innpakning " > < / div >

33 34 35 36

< s cript > // Henter inn i nnpak ning s-el ement et var innpakningEl = document . querySelector ( " . innpakning " ) ;

37 38 39 40 41

// Genererer 48 div-elementer inni innpakningen for ( var i = 0; i < 48; i ++) { // Lager et nytt div-element var div = document . createElement ( " div " ) ;

42 43 44

// Gir elementet klassen . boks div . className = " boks " ;

45 46 47

// Setter elementets animasjonsfart til en tilfeldig fart div . style . animationDuration = ( Math . random () * 5 + 1) + " s " ;

48 49 50 51 52 53 54

// Legger til elementet i innpakningselementet innpakningEl . appendChild ( div ) ;

} < / s cript > < / b ody > < / html >

Kode 18.3: Kode som genererer mange roterende firkanter.


Transformasjoner med CSS

293

Figur 18.4: Skjermbilde tatt underveis i kjøringen av kode 18.3.

Oppgaver 18.7 Ta utgangspunkt i kode 18.3 og få firkantene til å fylle hele skjermen. Hint: Egenskapene window.innerWidth og window.innerHeight gir deg nettleserens indre dimensjoner. 18.8 Utvid koden slik at hver av boksene også får en vilkårlig animasjon. Foreløpig har vi bare rotasjon, men legg også til skalering som et alternativ.

Sammendrag CSS-egenskapen transform lar oss endre utseende på elementer for eksempel ved å rotere, flytte eller skalere dem. Når vi transformerer et element, vil det ikke påvirke andre elementer i nærheten. Det vil si at elementet fremdeles tar opp den plassen elementet tok før transformasjonen. Elementer inni det transformerte elementet vil følge den samme transformasjonen. De fleste av verdiene finnes i flere versjoner: én versjon som påvirker hele elementet, én versjon som bare påvirker elementet i x-retning (for eksempel scaleX), og én versjon som bare påvirker elementet i y-retning (for eksempel scaleY). Transformasjoner gir oss mange muligheter til å lage stilige overganger og animasjoner, for eksempel sirkler som «vokser», og firkanter som «snurrer».


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.