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
7
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 . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
57 58 61 63 64 65 67 69 70
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 . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
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
. . . . . . .
. . . . . . .
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() . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
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 . . . . . . . . KravspesiďŹ kasjon . 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 . . . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
9
. . . . .
158 161 163 166 169
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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
170 171 171 172 173 175 175 177 177 179 181 183 186
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 . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
188 188 189 190 191 191 192 193 193 194 195 196 200 205
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
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
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 . . . . . . . KravspesiďŹ kasjon 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 . . . . . . . KravspesiďŹ kasjon 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.
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 while-løkke 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: 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 for-løkke, kan vi skrive: 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
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.
pssst 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)
119
7.4 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å. 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.
pssst 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
// Skriver ut en avs lu tn in gs beskjed if ( spillerPoeng === VINNERSUM ) { infoEl . innerHTML = " Gratulerer ! Du vant ! " ; } else { infoEl . innerHTML = " Auda ! Du ble sl å tt av en maskin ... " ; }
14 15 16 17 18 19 20
}
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.
188 Objekter
Kapittel 12
Objekter
Etter dette kapitlet skal du kunne • lage og bruke objekter • legge til og endre objekters egenskaper • lage objekter med metoder • samle og sortere objekter i arrayer pssst Du finner videoer og andre ressurser på nettstedet på Lokus.
I forrige kapittel så vi på hvordan vi kan samle informasjon i arrayer. En array fungerer veldig bra hvis vi har mange verdier av ett slag, for eksempel tall eller navn. Men hva gjør vi hvis vi vil lagre flere ting samtidig? La oss si at vi ønsker å lage en oversikt over personer, med fornavn og fødselsår. Vi kan kanskje lagre verdiene i en todimensjonal array, men det blir ganske avansert for et nokså enkelt problem. Alternativt kan vi lage to arrayer, der hver person har en fast indeks. Det er heller ingen god løsning, bare tenk på hva som skjer om en av verdiene i den ene arrayen blir fjernet; da vil ikke lenger fornavn og fødselsår for den samme personen ha lik indeks. Løsningen i situasjoner som denne er å bruke objekter. Et objekt minner om en array, men i stedet for å bruke indekser til å finne de ulike elementene, bruker vi tekster (nøkler).
12.1 Objekter Vi bruker ofte objekter når vi skal jobbe med noe som kan beskrives som et objekt i det virkelige liv. I dette kapitlet skal vi se på et objekt som representerer et land, og objekter som representerer personer. Alle objekter har et sett med egenskaper, som fungerer som variabler. Det vil si at et land-objekt for eksempel kan ha en hovedstad, og et person-objekt kan ha et navn. Vi har også sett at arrayer har egenskaper (for eksempel length). Det er fordi arrayer egentlig er objekter, men de er såpass spesialiserte at vi likevel skiller mellom de to.
Objekter
189
I tillegg kan objekter ha metoder. En metode er som nevnt en funksjon som hører til et objekt. Vi skal for eksempel se på metoden fulltNavn(), som lar oss finne en persons fulle navn fra personens fornavn og etternavn.
Lage et objekt Et objekt lages nesten som en array, men vi bruker sløyfeparenteser ({ og }) i stedet for hakeparenteser ([ og ]), og vi angir nøkler for hver av egenskapene i objektet: var land = { navn: "Norge", hovedstad: "Oslo", myntenhet: "NOK" };
Her bruker vi nøkkelordet var, og vi gir objektet navnet «land». Mellom sløyfeparentesene angir vi objektets egenskaper. Alle egenskapene har et navn (en nøkkel) og en verdi. De minner om variabler, men med en annen skrivemåte. Slike par av nøkler og verdier kalles gjerne «key-value pairs». Vi kan bruke nøklene til å finne verdiene. Egenskapsnavnene kan angis i gåseøyne, men det er ikke nødvendig, så vi har valgt å ikke gjøre det i denne boka. Legg merke til at egenskapene og verdiene skilles av kolon, ikke likhetstegn, og at egenskapene skilles med komma, ikke semikolon. Hvis du får feilmelding når du skal lage objekter, skyldes det ofte at du har skrevet likhetstegn i stedet for kolon, eller semikolon i stedet for komma. Skrivemåten ovenfor kan bli uoversiktlig om vi har mange egenskaper; vi vil derfor stort sett skrive objekter slik: var land = { navn: "Norge", hovedstad: "Oslo", myntenhet: "NOK" };
Da blir det mye lettere å se hvilke egenskaper objektet har. I dette objektet har vi bare med egenskaper med datatypen string, men vi kan også bruke datatypene number, boolean og array. Vi kan til og med ha andre objekter som egenskaper om vi ønsker det. Hvis vi vil ha med en array, gjøres det slik: var land = { navn: "Norge", hovedstad: "Oslo", myntenhet: "NOK", storsteByer: ["Oslo", "Bergen", "Stavanger/Sandnes"] };
190 Objekter
Bruke et objekt Vi har allerede sett mange eksempler på hvordan vi bruker objekter. Vi har brukt egenskapen length, som hører til array-objektet, og vi har brukt metoden random(), som hører til Math-objektet. Felles for bruken av disse er at vi skriver et punktum mellom objektets navn og egenskapen/metoden vi ønsker å bruke. Dette kalles dot-notasjon, fordi punktum kalles «dot» på engelsk. Vi kan også bruke hakeparenteser ([ og ]) til å hente ut egenskaper, på samme måte som vi gjør for arrayer. Dot-notasjonen er lettere å skrive og lese, men hakeparentesene er også nyttige, spesielt når vi skal se på hvordan vi kan finne alle verdiene i et objekt (senere i kapitlet). Nå kan vi finne egenskapene i objektet land som vi laget tidligere: console.log(land.navn); // Skriver ut: "Norge" console.log(land["navn"]); // Skriver ut: "Norge"
Her bruker den første utskriften dot-notasjon, mens den andre bruker hakeparenteser. De skriver begge ut egenskapen navn. På samme måte kan vi se på egenskapen storsteByer, som er en array: console.log(land.storsteByer); // Skriver ut: ["Oslo", "Bergen", "Stavanger/Sandnes"] console.log(land.storsteByer[1]); // Skriver ut: "Bergen" console.log(land["storsteByer"][2]); // Skriver ut: "Stavanger/Sandnes"
Legg merke til at arrayen brukes på samme måte som vi så i forrige kapittel, vi må bare skrive land. først for å hente den ut av objektet. Hvis vi ønsker å se alt innholdet i et objekt, kan vi skrive ut hele objektet også: console.log(land); // Skriver ut: // Object {navn: "Norge", hovedstad: "Oslo", myntenhet: "NOK", storsteByer: Array[3]}
Oppgaver 12.1 Lag et objekt som du kaller meg. Legg til egenskapene navn, fodselsaar, favorittfilm og favorittall. Gi egenskapene verdier som passer for deg. Legg også til arrayen favorittmat, der du legger inn minst tre ting du liker å spise. 12.2 Skriv alle egenskapene, inkludert all favorittmaten, til konsollen. Bruk begge framgangsmåtene som er nevnt ovenfor.
Objekter
191
Gå gjennom alle egenskapene i et objekt Det finnes en løkke som lar oss gå gjennom alle egenskapene i et objekt, omtrent som vi går gjennom alle egenskapene i en array. Den heter «for...in» og minner litt om en for-løkke: for (var egenskap in land) { console.log(egenskap); }
Her lages det en midlertidig variabel med navn «egenskap» for hver egenskap. Denne løkken vil bare skrive ut egenskapenes navn til konsollen ("navn", "hovedstad", "myntenhet" og "storsteByer"). Vi ønsker oss vanligvis verdiene til egenskapene, og for å få til det kan vi skrive: for (var egenskap in land) { console.log(land[egenskap]); }
Denne løkken skriver ut alle verdiene i objektet. Legg merke til at vi bruker hakeparenteser ([ og ]) i stedet for dot-notasjon. Det skyldes at egenskapsnavnene tolkes som tekster (datatypen string), noe som gjør at dot-notasjonen ikke fungerer.
Oppgaver 12.3 Skriv alle egenskapene i objektet du laget i oppgave 12.1, til konsollen. Bruk begge framgangsmåtene beskrevet ovenfor.
Redigere innholdet i et objekt Vi kan jobbe med egenskaper i objekter på samme måte som vi jobber med verdier i variabler. La oss si at vi har følgende objekt med informasjon om en person: var person = { navn: "Ola", adresse: "Storgata 12" };
Hvis Ola flytter, må vi endre adressen hans. Det kan vi gjøre ved å bruke dot-notasjon eller hakeparenteser: person.adresse = "Kirkeveien 72"; person["adresse"] = "Kirkeveien 72";
192 Objekter
Disse to linjene gjør akkurat det samme, de setter verdien i egenskapen adresse til "Kirkeveien 72". Hvis vi ønsker å legge til en egenskap, kan vi gjøre det slik: person.favorittfarge = "rosa";
Her bruker vi dot-notasjon, men vi kan også legge til egenskaper med hakeparenteser. Hvis vi nå skriver console.log(person), vil vi få denne utskriften: Object {navn: "Ola", adresse: "Kirkeveien 72", favorittfarge: "rosa"}
Objektet har altså fått en ny egenskap, nemlig favorittfarge. Hvis vi ønsker å fjerne en egenskap, kan vi bruke nøkkelordet delete: delete person.favorittfarge;
Vi kan altså endre, legge til og fjerne egenskaper etter at vi har laget et objekt.
Oppgaver 12.4 Utvid objektet du laget i oppgave 12.1, med egenskapen favorittfarge. Du kan selv velge om egenskapen skal ha én verdi, eller om den skal være en array med flere favorittfarger. 12.5 Skriv ut de nye verdiene du har lagt til i konsollen.
12.2
Metoder
Foreløpig har vi sett på objekter som har egenskaper, men et objekt kan også ha funksjoner. En funksjon i et objekt kalles en metode. Vi kan utvide «person»-objektet med en metode som lar oss finne personens fulle navn: var person = { fornavn: "Ola", etternavn: "Hansen", fulltNavn: function() { return this.fornavn + " " + this.etternavn; } }
Metoden lages på samme måte som en egenskap i objektet, med et navn etterfulgt av et kolon. Deretter skriver vi funksjonen på samme måte som vi har gjort tidligere. Hvis vi nå skriver console.log(person.fulltNavn());, får vi utskriften "Ola Hansen" i konsollen.
Objekter
193
Legg merke til at vi bruker ordet «this» i funksjonskoden. Dette ordet henviser til objektet vi er i, som her er objektet person. Det vil si at ordet «this» blir erstattet av ordet «person» når funksjonen kjøres. Senere i boka skal vi se på hvordan vi kan lage generelle maler for objekter, slik at for eksempel den samme «person-malen» kan gi opphav til mange person-objekter. Da er det greit å referere til objektene med et generelt ord som «this», i stedet for å lage en ny metode hver gang vi registrerer en ny person.
Oppgaver 12.6 Kopier metoden fulltNavn() og legg den til i objektet du laget i oppgave 12.1. 12.7 Legg til metoden hvaSkalJegHaTilMiddag() i objektet du laget i oppgave 12.1. Metoden skal velge en tilfeldig verdi fra arrayen favorittmat og skrive den ut i konsollen.
12.3
Objekter i arrayer
Vi har sett at arrayer fungerer veldig bra når vi ønsker å samle mange ting med like egenskaper. Det inkluderer også objekter. Hvis vi har mange «person»objekter, som alle representerer ulike personer, er det veldig praktisk å samle dem i en array. Vi skal nå se på noen eksempler der vi plasserer objekter i arrayer.
Lage en array med objekter La oss si at vi har tre objekter: var person1 = { navn: "Ola", fodselsaar: 2014 }; var person2 = { navn: "Iben", fodselsaar: 2010 }; var person3 = { navn: "Martin", fodselsaar: 2004 };
Vi kan sette inn disse tre objektene i en array på samme måte som vi setter andre variabler i en array: var personer = [person1, person2, person3];
Nå kan vi finne de tre personene ved å skrive personer[0], personer[1] og personer[2]. Alternativt kan vi lage arrayen og objektene samtidig, da slipper vi å gi hvert av objektene et eget navn:
194 Objekter
var personer = [ { navn: "Ola", fodselsaar: 2014 }, { navn: "Iben", fodselsaar: 2010 }, { navn: "Martin", fodselsaar: 2004 } ];
Her starter og avslutter vi arrayen på egne linjer. Det gjør koden mer oversiktlig. Inni arrayen kan vi enten skrive objektene på hver sin linje, eller vi kan dele opp objektene i egenskaper som vi har gjort her. Husk at nettleseren ikke bryr seg om ekstra mellomrom og linjeskift; det er opp til oss å skrive koden på en måte som gjør den lett å lese. For å få tilgang til objektene bruker vi samme framgangsmåte som vi er vant til å bruke med arrayer: console.log(personer[0]); // Skriver ut: Object {navn: "Ola", fodselsaar: 2014} console.log(personer[1].navn); // Skriver ut: "Iben"
Hente alle objekter fra en array Når vi har objekter i en array som i forrige avsnitt, kan vi finne dem ved å bruke indeksene deres. Vi kan derfor bruke en for-løkke til å gå gjennom alle objektene i en array. Hvis vi vil skrive de tre personene i arrayen personer til konsollen, kan vi gjøre det slik: for (var i = 0; i < personer.length; i++) { console.log(personer[i].navn + " ble født i " + personer[i].fodselsaar + ". "); }
Denne for-løkken skriver ut: Ola ble født i 2014. Iben ble født i 2010. Martin ble født i 2004.
Objekter
195
Oppgaver 12.8 Lag arrayen bilmerker. Arrayen skal bestå av objekter som inneholder informasjon om bilmerker. Hvert objekt skal ha egenskapene forkortelse, bilmerke og land. Registrer minst fem bilmerker i arrayen din. 12.9 Skriv ut informasjonen om alle bilmerkene i arrayen bilmerker til konsollen.
Sortere objekter i en array I kapittel 11 så vi på hvordan vi kan sortere verdiene i en array. Nå skal vi se på hvordan vi kan sortere følgende array etter personenes etternavn: var personer = [ { fornavn: "Lisa", etternavn: "Simpson" }, { fornavn: "Peter", etternavn: "Griffin" }, { fornavn: "Eric", etternavn: "Cartman" } ];
Vi kan bruke metoden sort() for å sortere tekster, men her er det ikke snakk om bare én tekst. For å sortere tall måtte vi legge til en egen sammenligningsfunksjon. Vi må gjøre det samme for å sortere objekter etter spesifikke egenskaper. Vi brukte denne funksjonen til å sortere tall: function sammenlignTall(a, b) { return a - b; }
Vi kan skrive om denne funksjonen slik at den blir litt lettere å overføre til det vi ønsker å gjøre nå: function sammenlignTall(a, b) { if (a > b) { return 1; } else if (a < b) { return -1; } else { return 0; } }
Her kan du se at funksjonen returnerer et positivt tall hvis a er større enn b. Det tolkes av nettleseren som at a skal plasseres etter b. Funksjonen returnerer et negativt tall hvis a er mindre enn b. Det tolkes av nettleseren som at a skal plasseres foran b. Og hvis a verken er større enn, eller mindre enn, b, må de to
196 Objekter
være like store, og nettleseren trenger ikke flytte på noen av dem. Vi kan skrive om denne funksjonen slik at den lar oss sortere etter personenes etternavn: function sammenlignEtternavn(a, b) { if (a.etternavn > b.etternavn) { return 1; } else if (a.etternavn < b.etternavn) { return -1; } else { return 0; } }
Hvis vi nå skriver personer.sort(sammenlignEtternavn);, vil objektene bli sortert i arrayen etter etternavn.
Oppgaver 12.10 Lag en sammenligningsfunksjon som lar deg sortere arrayen bilmerker fra oppgave 12.8. Arrayen skal sorteres etter bilmerkenes navn (ikke forkortelse).
Lage en tabell fra en array med objekter I forrige avsnitt skrev vi ut informasjonen om personene våre i konsollen. Nå skal vi se på hvordan vi kan lage en HTML-tabell som viser fram den samme informasjonen. Vi kan gjøre dette ved å bruke egenskapen innerHTML, men det går også an å lage HTML-elementer slik vi så på i kapittel 7. I dette eksemplet skal vi bruke en blanding av de to metodene for å lage tabellen du kan se i figur 12.1.
Figur 12.1: Den ferdige tabellen.
Objekter
197
Vi starter med et enkelt HTML-dokument som inneholder følgende <div>element: <div class="innpakning"></div>
Og denne CSS-koden: body { background-color: #e0e0e0; font-family: "Century Gothic", Arial, sans-serif; font-size: 14px; color: #323232; } .innpakning { width: 50%; margin: auto; padding: 5px; background-color: #fefefe; }
Vi har også lagt til CSS-koden for tabeller fra kapittel 6, slik at den ferdige tabellen får et fint utseende. Det første vi gjør, er å hente inn innpaknings-elementet der vi skal plassere tabellen vår. Vi lager også en array med objekter som utgjør informasjonen som skal vises i tabellen: // Henter inn et div-element der vi vil plassere tabellen var innpakningEl = document.querySelector(".innpakning"); // Lager en array med informasjon om personene våre var personer = [ { navn: "Ola", fodselsaar: 2014 }, { navn: "Iben", fodselsaar: 2010 }, { navn: "Martin", fodselsaar: 2004 } ];
Det neste vi gjør, er å lage et <table>-element som vi skal fylle med innhold. Vi må også lage et <tbody>-element. Vi har ikke sett på det elementet tidligere, og vi trenger ikke bruke det når vi lager vanlige HTML-tabeller. Nettleseren legger automatisk til et slikt element rundt alt innholdet i tabellen, men når vi bruker metoden vi skal se på nå, vil nettleseren legge til et nytt <tbody>-element for hver rad, og det vil vi unngå, derfor lager vi et selv:
198 Objekter
// Lager et table-element var tabellEl = document.createElement("table"); // Lager et tbody-element var tbodyEl = document.createElement("tbody");
Nå kan vi begynne å lage innhold som vi vil fylle <tbody>-elementet med. Vi starter med en overskriftsrad der vi angir overskrifter som passer til dataene i objektene våre. Vi kan også bruke egenskapsnavnene direkte, men ofte ønsker vi mer kompliserte overskriftsnavn enn de vi har gitt til egenskapene. For å lage en overskriftsrad lager vi først en variabel som vi fyller med overskriftene, deretter legger vi den til i <tbody>-elementet. Hvis vi legger til overskriftsraden direkte i <tbody>-elementet, vil nettleseren prøve å avslutte <tr>-elementene før vi ønsker å avslutte dem. // Lager en overskriftsrad var overskrifter = "<tr>"; overskrifter
+= "<th> Navn </th>";
overskrifter
+= "<th> Fødselsår </th>";
overskrifter
+= "</tr>";
Når overskriftsraden er ferdig, legger vi den til i <tbody>-elementet: // Legg til overskriftsraden i tabellen tbodyEl.innerHTML += overskrifter;
For å legge til resten av radene bruker vi samme framgangsmåte som for overskriftsraden. Vi gjentar det for hvert av objektene i arrayen: // Bruker en løkke til å legge til en rad for hver av personene for (var i = 0; i < personer.length; i++) { var rad
= "<tr>";
rad
+= "<td>" + personer[i].navn + "</td>";
rad
+= "<td>" + personer[i].fodselsaar + "</td>";
rad
+= "</tr>";
// Legger til raden i tabellen tbodyEl.innerHTML += rad; }
Nå som vi har plassert alle radene i <tbody>-elementet, kan vi legge det til i <table>-elementet. Deretter legger vi til <table>-elementet i innpakningselementet: // Legger til tbody i tabellelementet tabellEl.appendChild(tbodyEl);
Objekter
199
// Legger til tabellen i innpakningselementet innpakningEl.appendChild(tabellEl);
Hvis du lurer på hvordan DOM-treet ser ut etter at vi har lagt til nye elementer, kan du åpne konsollen som vanlig, og velge «Elements» fra menyen. Da kan du se alle HTML-elementene som er i DOM-treet. I figur 12.2 kan du se en skjermdump av deler av DOM-treet vi nå har laget.
Figur 12.2: Skjermdump av DOM-treet.
Oppgaver 12.11 Utfordring: Lag funksjonen lagTabell(overskrifter, innhold) som tar to argumenter. Det første argumentet, overskrifter, skal være en array som inneholder tabellens overskrifter. Det andre argumentet, innhold, skal være en array med objekter som utgjør tabellens innhold. 12.12 Prøv ut funksjonen på arrayen bilmerker som du laget i oppgave 12.8. 12.13 Lag en array med objekter som ikke inneholder like mange egenskaper. Hvordan fungerer denne arrayen med funksjonen du nå har laget? Du kan eventuelt legge inn en egen feilmelding i funksjonen du skrev i forrige oppgave. For å sjekke antall egenskaper i et objekt kan du bruke metoden Object.keys(objekt). Du vil få en array med objektets egenskaper som du kan finne lengden til. For å skrive ut feilmeldingen kan du bruke console.error(feilmelding). Den fungerer som console.log(), men den gir et feilmeldingssymbol i konsollen.
200 Objekter
12.4
Lage en quiz fra objekter
Vi skal nå se på hvordan vi kan lage en enkel quiz ved å bruke objekter i en array. Quizen viser alle spørsmålene på den samme siden, og en knapp lar brukeren sende inn svarene sine. Vi skal også legge til rette for at ett spørsmål skal kunne ha flere riktige svar. I figur 12.3 på side 204 kan du se en skjermdump av den ferdige quizen. Det første vi gjør, er å lage HTML-dokumentet som du kan se i kode 12.1. Her har vi hentet inn stilarket vi laget i kapittel 10, i tillegg til at vi har satt av plass til mer CSS-kode og JavaScript-kode. Innholdet vårt består bare av et <div>-element som skal fungere som innpakning, en overskrift og et <p>-element der resultatet skal skrives ut. 1 2 3 4 5 6 7 8 9 10 11
< ! doctype html > < html > < head > < title > Quiz < / title > < meta charset= " UTF-8 " > < li nk rel= " stylesheet " href= " brukerinput . css " > < s tyle > /* Her skal vi skrive CSS-koden */ < / s tyle > < / head > < b ody >
12 13
< div class= " innpakning " >
14 15 16
< h1 > Quiz < / h1 > < p class= " resultat " > < / p >
17 18
< / div >
19 20 21 22
< s cript > /* Her skal vi skrive JavaScript-koden */ < / s cript >
23 24 25
< / b ody > < / html >
Kode 12.1: Utgangspunktet for quizen vår.
Med HTML-koden på plass kan vi legge til litt CSS. Vi legger til vårt faste utseende for <body>-elementet og midtstiller innpakningen. I tillegg øker vi skriftstørrelsen i <p>-elementet som skal vise resultatet. Her bruker vi verdien 3.5ex, som betyr at en «x» her skal bli 3,5 ganger så stor som en vanlig «x». Resten av teksten blir forstørret tilsvarende. CSS-koden kan du se i kode 12.2 på neste side.
Objekter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
201
body { background-color: # e0e0e0; font-family: " Century Gothic " , Arial , sans-serif; font-size: 14 px; color: #323232; } . innpakning { width: 50%; margin: auto; padding: 5 px; background-color: # fefefe; border: 3 px solid black; } . resultat { margin: 20 px 5 px; padding: 20 px; font-size: 3.5 ex; }
Kode 12.2: CSS-koden for quizen vår.
Nå kan vi begynne med JavaScript-koden vår. Vi starter med å hente inn de to HTML-elementene som skal brukes i JavaScript-koden, se kode 12.3. Deretter lager vi en array som inneholder alle spørsmålene i quizen. Hvert spørsmål består av selve spørsmålet, i form av en tekst, en array med alternativer, og en like lang array som indikerer om hvert av alternativene er riktig eller galt. Det vil si at hvis det første alternativet er et riktig alternativ, skal også den første verdien i «fasiten» være "riktig". Det finnes mange måter å lage en quiz på, men denne måten gjør det enkelt å ha flere riktige svaralternativer. 1 2
// Henter inn et < div > -element hvor vi vil plassere quizen var innpakningEl = document . querySelector ( " . innpakning " ) ;
3 4 5
// Henter inn et < p > -element der vi vil skrive ut poengsummen var resultatEl = document . querySelector ( " . resultat " ) ;
6 7 8 9 10 11 12 13 14 15 16 17 18
// Lager en array med sp ø rsm å lsobjekter var quiz = [ { sporsmaal: " Hva heter Norges hovedstad ? " , alternativer: [ " Oslo " , " Stockholm " , " K ø benhavn " ] , fasit: [ " riktig " , " galt " , " galt " ]} , { sporsmaal: " Hvilke byer ligger i Europa ? " , alternativer: [ " Oslo " , " New York " , " London " ] , fasit: [ " riktig " , " galt " , " riktig " ]} , { sporsmaal: " Hvilken innsj ø er Norges st ø rste ? " , alternativer: [ " R ø ssvatnet " , " Mj ø sa " , " Femunden " ] , fasit: [ " galt " , " riktig " , " galt " ]} ];
Kode 12.3: Første del av JavaScript-koden for quizen.
202 Objekter
Nå som vi har laget spørsmålene, kan vi skrive dem ut på nettsiden. Dette er vist i kode 12.4 nedenfor. Vi starter med en løkke som går gjennom alle spørsmålene. For hvert spørsmål oppretter vi et <p>-element. Inni dette <p>-elementet skriver vi ut spørsmålsteksten som en <h3>-overskrift. Deretter lager vi én avkrysningsboks («checkbox») for hvert alternativ. Legg merke til at avkrysningsboksene får verdien sin fra fasiten, mens de får teksten fra alternativer-arrayen skrevet ved siden av. Hver avkrysningsboks blir lagt til nederst i <p>-elementet til spørsmålet før innerHTML og operatoren += brukes til å legge til alternativ-teksten etter avkrysningsboksen. Helt til slutt legger vi til hele <p>-elementet, med spørsmål, avkrysningsbokser og alternativer, nederst i innpaknings-elementet. 1 2
// Skriver sp ø rsm å lene til nettsiden for ( var i = 0; i < quiz . length; i ++) {
3
// Lager et < p > -element til hvert sp ø rsm å l var sporsmaalEl = document . createElement ( " p " ) ;
4 5 6
// Skriver ut sp ø rsm å let sporsmaalEl . innerHTML + = " < h3 > " + quiz [ i ]. sporsmaal + " < / h3 > " ;
7 8 9
// Lager elementer for hvert av alternativene for ( var j = 0; j < quiz [ i ]. alternativer . length; j ++) { var nyCheckbox = document . createElement ( " input " ) ; // Angir typen checkbox nyCheckbox . type = " checkbox " ; // Setter boksens value til fasitsvaret nyCheckbox . value = quiz [ i ]. fasit [ j ]; // Legger til boksen sporsmaalEl . appendChild ( nyCheckbox ) ; // Legger til svaralternativ sporsmaalEl . innerHTML + = quiz [ i ]. alternativer [ j ] + " < b r > " ; }
10 11 12 13 14 15 16 17 18 19 20 21 22
// Legger til < p > -elementet i innpakningen innpakningEl . appendChild ( sporsmaalEl ) ;
23 24 25
}
Kode 12.4: Andre del av JavaScript-koden for quizen.
Nå er alt det visuelle på plass. Det eneste vi mangler, er beregningen som skal skje når noen trykker på knappen. Vi starter med å opprette en knapp som vi legger til nederst i innpakningen, som vist i kode 12.5 på neste side. Vi legger til en click-hendelse på knappen, som fører til at funksjonen finnPoeng() blir kjørt når knappen trykkes. I funksjonen finnPoeng() bruker vi metoden querySelectorAll() til å hente alle avkrysningsboksene. Deretter sjekker vi, for hver av avkrysningsboksene, om de er haket av. Hvis de er haket av, sjekker vi om avkrysningsboksens value stemmer med spørsmålets fasit. Hvis alternativet er riktig, gir vi ett poeng til
Objekter
203
spilleren (antallPoeng++). Hvis alternativet er feil, tar vi ett poeng fra spilleren (antallPoeng--). Helt til slutt skriver vi poengsummen til «resultat-elementet» vårt.
1 2 3 4 5
// Legger til en knapp til slutt var knapp = document . createElement ( " button " ) ; knapp . innerHTML = " Sjekk svar " ; knapp . addEventListener ( " click " , finnPoeng ) ; innpakningEl . appendChild ( knapp ) ;
6 7 8
function finnPoeng () { var alleCheckboxEl = document . querySelectorAll ( " input [ type= ' checkbox '] " ) ;
9
var antallPoeng = 0;
10 11
for ( var i = 0; i < alleCheckboxEl . length; i ++) { // Hvis en checkbox er haket av if ( alleCheckboxEl [ i ]. checked ) { // Unders ø k om alternativet er riktig if ( alleCheckboxEl [ i ]. value === " riktig " ) { // Hvis riktig , gi ett poeng antallPoeng ++; } else { // Hvis feil , trekk fra ett poeng antallPoeng--; } } }
12 13 14 15 16 17 18 19 20 21 22 23 24 25
resultatEl . innerHTML = " Du fikk " + antallPoeng + " poeng ! " ;
26 27
}
Kode 12.5: Tredje del av JavaScript-koden for quizen.
Det er et alternativ å lytte etter hendelser på alle checkbokser, for så å oppdatere poengsummen umiddelbart, men da kan ikke brukeren ombestemme seg. Den framgangsmåten er derfor ikke særlig brukervennlig i denne appen.
Oppgaver 12.14 Lag din egen quiz med dine egne spørsmål. Quizen skal inneholde minst 6 spørsmål. Prøv deg fram med ulike antall alternativer og ulike antall riktige.
204 Objekter
Figur 12.3: Den ferdige quizen.
Objekter
205
Sammendrag Vi bruker nøkkelordet var og sløyfeparenteser for å lage et objekt. Et objekt har egenskaper som består av navn og verdier. Egenskapene og verdiene skilles med kolon (ikke likhetstegn som i variabler), og egenskapene skilles med komma (ikke semikolon): var mittObjekt = { egenskap1: verdi, egenskap2: verdi, egenskap3: verdi };
Verdien til en egenskap kan være som i en vanlig variabel, men den kan også være en array, et annet objekt eller en funksjon. En funksjon som hører til et objekt, kalles en metode. For å få tilgang til egenskapene i et objekt bruker vi dot-notasjon: mittObjekt.egenskap1, eller hakeparenteser: mittObjekt["egenskap1"]. Vi kan legge til nye egenskaper til eksisterende objekter: mittObjekt.egenskap4 = verdi;
Objekter kan samles i arrayer. Da trenger ikke objektene ha et eget navn: var objektArray = [ { egenskap1: verdi, egenskap2: verdi }, { egenskap1: verdi, egenskap2: verdi }, { egenskap1: verdi, egenskap2: verdi } ];
Vi kan sortere objekter i arrayer etter ønskede egenskaper. Da må vi lage en egen sammenligningsfunksjon som sammenligner objektene.
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
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.
pssst 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 a n s i tion-property: width; t r a n s i tion-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 a n s i tion-property: all; t r a n s i tion-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 i m a t ion-direction: alternate; } . boks { background-color: red; a n i m a t i o n - p l a y - s t a t e : 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 m a t i o n - p l a y - s t a t e : 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 an imasjonshendelser 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
// Hvis hendelsen animationend inntreffer if ( e . type === " animationend " ) { // Sett i gang animasjonen til sirkelelementet sirkelEl . style . animationPlayState = " running " ; }
58 59 60 61 62 63
}
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.