Det er snart skolestart for studenter, og med det kommer en ny eksamen-sesong!
Det kan være vanskelig å holde styr på nøyaktig hvor god tid man har igjen til å forberede seg, og noen av oss kan nok trenge en påminnelse. Hva med å lage en i JavaScript?
Inspirasjonen til denne bloggposten fikk jeg fra en nyhetsartikkel som telte ned dager, timer, minuttter og sekunder til barer fikk lov til å åpne etter COVID-19 lockdown-en.
Du kan se en live demo av det vi skal lage her.
Eksempelet teller ned til nyttårsaften, og kildekoden finner du selvsagt på Github.
Merk deg at koden er bitte litt forandret slik at den automatisk setter riktig år, ellers er den identisk med eksempelet under.
Opprett prosjektet
Lag en mappe for prosjektet ditt, og opprett tre filer. Jeg gjør det via terminalen, men du kan selvsagt gjøre det akkurat slik du vil.
mkdir countdown-timer
cd countdown-timer
touch index.html style.css script.js
HTML og CSS
HTML-en og CSS-en til dette prosjektet er rimelig enkel. Hovedidéen rundt HTML-en er å lage en innholdsboks for hele nedtellingen vår. Hver del av nedtellingsmekanismen, og merkelappen som hører til, vil bli holdt sammen i en div, slik at det blir enkelt å holde dem adskilt.
Vi putter selve nedtellingstallet i hver sin div også, og merkelappen i en span, slik at vi enkelt kan få tak i dem med både JavaScript og CSS.
Innenfor body-taggen i index.html-dokumentet mitt har jeg nå dette:
<div class="countdown">
<div class="box">
<div class="time" id="days">0</div>
<span class="label">days</span>
</div>
<div class="box">
<div class="time" id="hours">00</div>
<span class="label">hours</span>
</div>
<div class="box">
<div class="time" id="minutes">00</div>
<span class="label">minutes</span>
</div>
<div class="box">
<div class="time" id="seconds">00</div>
<span class="label">seconds</span>
</div>
</div>
<script src="script.js"></script>
Legg merke til at vi også inkluderer et foreløpig tomt script mot slutten av dokumentet. Det skal vi bruke til å forandre tallene inne i div-ene våre som har klassen "time".
Slik ser nettsiden ut foreløpig:
Det ser ikke spesielt lekkert ut, så det må vi fikse.
Først lenker vi til stilarket vårt med en link-tag innenfor head-taggen i index.html-dokumentet vårt.
Jeg har også valgt å importere den populære fonten Roboto fra Google Fonts. Dette er hva jeg har i head-taggen i HTML-filen min nå:
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
<title>Simple countdown</title>
Som du sikkert ser er det også to meta-tagger, som editoren min VSCode har plassert der. Den først sier i fra til nettleseren at vi bruker tekstenkodingen UTF-8, og den andre passer på at siden skalerer riktig på mobil.
Slik ser den komplette HTML-fila ut:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
<title>Simple countdown</title>
</head>
<body>
<div class="countdown">
<div class="box">
<div class="time" id="days"></div>
<span class="label">days</span>
</div>
<div class="box">
<div class="time" id="hours"></div>
<span class="label">hours</span>
</div>
<div class="box">
<div class="time" id="minutes"></div>
<span class="label">minutes</span>
</div>
<div class="box">
<div class="time" id="seconds"></div>
<span class="label">seconds</span>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Vi hopper inn i style.css og gir siden en mørk bakgrunn og gjør teksten hvit. Vi vil at nedtellingen skal være sentrert på siden, både vertikalt og horisontalt. For å oppnå dette må man gi body-taggen en høyde, gjøre den om til flexbox, og bruke CSS-attributtene justify-content og align-items.
Vi setter også margin til 0, slik at vi ikke får scrolling i nettleseren.
Vi bruke den universale selektoren * og legger til "box-sizing: border-box;" for å sikre at all border og padding er inkludert når nettlesere kalkulerer høyde og bredde på elementer.
.countdown {
width: 500px;
display: flex;
justify-content: space-between;
}
Vil vi ha alle tellerne - dager, timer, minutter, sekunder - på rad horisontalt, med jevnt mellomrom. Min foretrukne måte å fikse det på er med flexbox.
Til sist legger vi på noen regler slik at boksene ikke krymper når nummerene går fra to til ett siffer, sentrerer teksten og setter en fontstørrelse.
Slik ser den komplette style.css-fila ut:
.box {
text-align: center;
min-width: 100px;
}
.time {
font-size: 4rem;
font-weight: 500;
}
.label {
font-size: 1.2rem;
font-weight: 300;
}
Og slik ser sida vår ut nå:
På tide å få den til å faktisk telle.
JavaScript
Det første vi skal gjøre i script.js er å opprette variablene for hver nedteller.
const days = document.getElementById("days");
const hours = document.getElementById("hours");
const minutes = document.getElementById("minutes");
const seconds = document.getElementById("seconds");
For å være sikker på at jeg ikke har gjort enkle skrivefeil, liker jeg å logge alle mine nylig opprettede variabler til konsollen.
console.log(days);
console.log(hours);
console.log(minutes);
console.log(seconds);
Hvis du åpner konsollen din ser du nok noe som ligner på dette:
Litt avhengig av hvilken nettleser du bruker kan det nok se litt forskjellig ut. Så lenge det ikke sier null noe sted, er du trygg.
Når du har bekreftet at du har opprettet alle variablene du trenger, kan du fjerne konsoll-loggingen.
Slik er planen vår videre:
- Definere en funksjon som tar i mot et Date-objekt og returnerer dager, timer, minutter og sekunder igjen til datoen i objektet.
- Definere en funksjon som tar i mot et Date-objekt, bruker den forrige funksjonen til å få verdiene vi trenger til tellerne våre, og oppdaterer dem med .innerText. Slik at de viser riktige verdier.
- Kalle funksjonen vår hvert sekund for å skape en nedtelling.
For å fullføre første del av planen, kommer vi til å bruke Date.now()-metoden. Denne gir oss antall millisekunder siden 1. januar, 1970. Du kan skaffe den samme verdien for et gitt date-object minDato ved å kalle minDato.getTime().
Starten på funksjonen vår blir altså:
const getTimeDifference = (targetDate) => {
const diff = targetDate.getTime() - Date.now();
};
Variabelen diff holder antall millisekunder mellom nå og når targetDate-variabelen inntreffer. Merk deg at hvis targetDate er satt til en dato i fortiden vil denne variabelen få et negativt tall. I dette tilfelle vil vil at nedtelleren vår bare skal vise 0.
Det kan vi implementere med en if-setning:
const getTimeDifference = (targetDate) => {
let diff = targetDate.getTime() - Date.now();
if (diff < 0) {
return {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
};
}
};
Vi returnerer et objekt for å gjøre det enklere å håndtere verdiene når vi kaller funksjonen.
Deretter bruker vi litt matte for å konvertere millisekunder til noe litt mer lesbart. Vi bruker Math.floor() og modulo-operatoren for å oppnå dette.
Metoden Math.floor() tar i mot et tall som argument og returnerer den største integeren som er mindre enn eller lik dette tallet. I praksis betyr det at den fjerner desimaler fra tall.
Math.floor(2.3); // 2
Math.floor(199.9999999); // 199
Math.floor(4); // 4
Modulo-operatoren % returnerer resten etter en heltalls-divisjon. Si at du deler 5 på 2. Da får du 2 som svar og 1 i rest. 5 % 2 er altså lik 1. Et par ekstra eksempler:
11 % 4; // 3
4 % 11; // 4
6 % 3; // 0
For å få korrekt antall gjenstående dager deler vi antall millisekunder på antall millisekunder i en dag, og runder ned til nærmeste integer.
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
Vi kan selvsagt gjøre lignende operasjoner både for timer, minutter og sekunder. Men, dette vil inkludere antall gjenstående dager også. Si at vi teller ned til et tidspunkt som er 1 dag og 1 time unna. Da vil kalkuleringen over si at det er 1 dag og 25 timer igjen. Dette kan vi fikse med modulo-operatoren, slik at vi får gjenstående tid etter vi har fjernet dager, timer og minutter først:
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor(diff / (1000 * 60 * 60)) % 24;
const minutes = Math.floor(diff / (1000 * 60)) % 60;
const seconds = Math.floor(diff / 1000) % 60;
Nå gjenstår det bare å returnere verdiene, og så er funksjonen vår klar.
const getTimeDifference = (targetDate) => {
let diff = targetDate.getTime() - Date.now();
if (diff < 0) {
return {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
};
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor(diff / (1000 * 60 * 60)) % 24;
const minutes = Math.floor(diff / (1000 * 60)) % 60;
const seconds = Math.floor(diff / 1000) % 60;
return { days, hours, minutes, seconds };
};
La oss lage funksjonen som skal oppdatere tellerne på sida vår. Dette er ganske rett frem. Vi skaffer de riktige tidene med getTimeDifference()-funksjonen vår, og oppdaterer den indre teksten i HTML-elementene våre med .innerText.
const updateTime = (targetDate) => {
const { days, hours, minutes, seconds } = getTimeDifference(targetDate);
daysElement.innerText = days;
hoursElement.innerText = hours;
minutesElement.innerText = minutes;
secondsElement.innerText = seconds;
};
På den andre linjen legger du kanskje merke til at vi bruker object-destrukturering. En fin syntaks som er tilgjengelig i JavaScript for å opprette flere variabler samtidig fra et objekt.
Nå er vi straks klar til å teste funksjonene våre. Vi definerer en dato som er et år fra nå, og kaller telleroppdaterings-funksjonen vår. Legg til disse linjene på slutten av scriptet ditt:
const targetDate = new Date("2021-05-06 16:30:00");
updateTime(targetDate);
Nå bør du se dette:
Det virker! Men jeg hadde foretrukket at alle ensifrede tall for timer, minutter og sekunder hadde startet med en 0. Det kan vi gjøre med å kombinere JavaScript sin .toString() og .padStart(). Den siste er en prototype-metode på strenger i JavaScript, derfor må vi konvertere tallene våre til strenger først.
Slik gjør du det:
const updateTime = (targetDate) => {
const { days, hours, minutes, seconds } = getTimeDifference(targetDate);
daysElement.innerText = days;
hoursElement.innerText = hours.toString().padStart(2, "0");
minutesElement.innerText = minutes.toString().padStart(2, "0");
secondsElement.innerText = seconds.toString().padStart(2, "0");
};
Det første argumentet du sender til .padStart() er minimumslengden på en streng. Det andre argumentet er tegnet som skal brukes til å forlenge strengen. Nå ser nedtelleren vår slik ut:
Til slutt trenger vi å oppdatere telleren hvert sekund. Heldigvis er det en innebygget metode i JavaScript som heter setInterval().
Rediger script.js, slik at den slutter med disse tre linjene:
const targetDate = new Date("2021-05-06 16:30:00");
updateTime(targetDate);
setInterval(updateTime, 1000, targetDate);
I setInterval() er første argumentet funksjonen som skal utføres hvert intervall, og det andre argumentet mellomrommet i tid mellom hvert intervall. Argumenter etter dette vil bli sendt som argumenter til funksjonen, derfor sender vi targetDate som det tredje argumentet.
Slik ser script.js ut nå:
const daysElement = document.getElementById("days");
const hoursElement = document.getElementById("hours");
const minutesElement = document.getElementById("minutes");
const secondsElement = document.getElementById("seconds");
const getTimeDifference = (targetDate) => {
let diff = targetDate.getTime() - Date.now();
if (diff < 0) {
return {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
};
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor(diff / (1000 * 60 * 60)) % 24;
const minutes = Math.floor(diff / (1000 * 60)) % 60;
const seconds = Math.floor(diff / 1000) % 60;
return { days, hours, minutes, seconds };
};
const updateTime = (targetDate) => {
const { days, hours, minutes, seconds } = getTimeDifference(targetDate);
daysElement.innerText = days;
hoursElement.innerText = hours.toString().padStart(2, "0");
minutesElement.innerText = minutes.toString().padStart(2, "0");
secondsElement.innerText = seconds.toString().padStart(2, "0");
};
const targetDate = new Date("2021-05-06 16:30:00");
updateTime(targetDate);
setInterval(updateTime, 1000, targetDate);
Merk at vi kaller både updateTime() før og inne i setInterval(). Det har å gjøre med at setInterval venter et intervall før den setter i gang.
Hvis du lagrer script-filen din og åpner index.html i en nettleser bør du nå se tida tikke nedover. Slik så det ut da jeg lagde denne artikkelen, og satt den til eksamensdatoen min tidligere i år. 😲
.