# Ustvarjanje igre z uporabo dogodkov
## Predhodni kviz
[Predhodni kviz](https://ff-quizzes.netlify.app/web/quiz/21)
## Programiranje, ki temelji na dogodkih
Ko ustvarjamo aplikacijo za brskalnik, uporabniku omogočimo grafični uporabniški vmesnik (GUI), ki ga uporablja za interakcijo z našim izdelkom. Najpogostejši način interakcije z brskalnikom je s klikom in vnosom besedila v različne elemente. Izziv, s katerim se soočamo kot razvijalci, je, da ne vemo, kdaj bo uporabnik izvedel te operacije!
[Programiranje, ki temelji na dogodkih](https://en.wikipedia.org/wiki/Event-driven_programming) je ime za vrsto programiranja, ki ga potrebujemo za ustvarjanje našega GUI-ja. Če nekoliko razčlenimo ta izraz, vidimo, da je ključna beseda tukaj **dogodek**. [Dogodek](https://www.merriam-webster.com/dictionary/event), po definiciji Merriam-Webster, pomeni "nekaj, kar se zgodi". To popolnoma opisuje našo situacijo. Vemo, da se bo nekaj zgodilo, za kar želimo izvesti kodo kot odziv, vendar ne vemo, kdaj se bo to zgodilo.
Način, kako označimo del kode, ki ga želimo izvesti, je z ustvarjanjem funkcije. Ko razmišljamo o [proceduralnem programiranju](https://en.wikipedia.org/wiki/Procedural_programming), se funkcije kličejo v določenem vrstnem redu. Enako velja za programiranje, ki temelji na dogodkih. Razlika je v tem, **kako** se funkcije kličejo.
Za obravnavo dogodkov (klik na gumb, vnos besedila itd.) registriramo **poslušalce dogodkov**. Poslušalec dogodkov je funkcija, ki posluša, da se zgodi dogodek, in se nato izvede kot odziv. Poslušalci dogodkov lahko posodobijo uporabniški vmesnik, opravijo klice na strežnik ali karkoli drugega, kar je potrebno kot odziv na dejanje uporabnika. Poslušalca dogodkov dodamo z uporabo [addEventListener](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener) in podamo funkcijo za izvedbo.
> **NOTE:** Pomembno je poudariti, da obstaja veliko načinov za ustvarjanje poslušalcev dogodkov. Uporabite lahko anonimne funkcije ali ustvarite poimenovane. Uporabite lahko različne bližnjice, kot je nastavitev lastnosti `click` ali uporaba `addEventListener`. V naši vaji se bomo osredotočili na `addEventListener` in anonimne funkcije, saj je to verjetno najpogostejša tehnika, ki jo uporabljajo spletni razvijalci. Prav tako je najbolj prilagodljiva, saj `addEventListener` deluje za vse dogodke, ime dogodka pa lahko podamo kot parameter.
### Pogosti dogodki
Na voljo je [na desetine dogodkov](https://developer.mozilla.org/docs/Web/Events), ki jih lahko poslušate pri ustvarjanju aplikacije. V bistvu vse, kar uporabnik naredi na strani, sproži dogodek, kar vam daje veliko moč, da zagotovite želeno izkušnjo. Na srečo boste običajno potrebovali le nekaj dogodkov. Tukaj je nekaj pogostih (vključno z dvema, ki ju bomo uporabili pri ustvarjanju naše igre):
- [click](https://developer.mozilla.org/docs/Web/API/Element/click_event): Uporabnik je kliknil nekaj, običajno gumb ali hiperpovezavo
- [contextmenu](https://developer.mozilla.org/docs/Web/API/Element/contextmenu_event): Uporabnik je kliknil z desnim gumbom miške
- [select](https://developer.mozilla.org/docs/Web/API/Element/select_event): Uporabnik je označil besedilo
- [input](https://developer.mozilla.org/docs/Web/API/Element/input_event): Uporabnik je vnesel besedilo
## Ustvarjanje igre
Ustvarili bomo igro, da raziščemo, kako dogodki delujejo v JavaScriptu. Naša igra bo preizkusila tipkarske spretnosti igralca, kar je ena najbolj podcenjenih spretnosti, ki bi jo moral imeti vsak razvijalec. Vsi bi morali vaditi tipkanje! Splošen potek igre bo videti takole:
- Igralec klikne gumb za začetek in dobi citat za tipkanje
- Igralec čim hitreje vtipka citat v besedilno polje
- Ko dokonča vsako besedo, se naslednja beseda označi
- Če igralec naredi tipkarsko napako, se besedilno polje obarva rdeče
- Ko igralec dokonča citat, se prikaže sporočilo o uspehu z izmerjenim časom
Zgradimo igro in se naučimo o dogodkih!
### Struktura datotek
Potrebovali bomo tri datoteke: **index.html**, **script.js** in **style.css**. Začnimo z nastavitvijo teh datotek, da si olajšamo delo.
- Ustvarite novo mapo za svoje delo tako, da odprete konzolo ali terminal in izvedete naslednji ukaz:
```bash
# Linux or macOS
mkdir typing-game && cd typing-game
# Windows
md typing-game && cd typing-game
```
- Odprite Visual Studio Code
```bash
code .
```
- Dodajte tri datoteke v mapo v Visual Studio Code z naslednjimi imeni:
- index.html
- script.js
- style.css
## Ustvarite uporabniški vmesnik
Če preučimo zahteve, vemo, da bomo na naši HTML strani potrebovali nekaj elementov. To je podobno receptu, kjer potrebujemo nekaj sestavin:
- Prostor za prikaz citata, ki ga mora uporabnik vtipkati
- Prostor za prikaz sporočil, kot je sporočilo o uspehu
- Besedilno polje za tipkanje
- Gumb za začetek
Vsak od teh elementov bo potreboval ID, da bomo lahko z njimi delali v našem JavaScriptu. Dodali bomo tudi reference na datoteke CSS in JavaScript, ki jih bomo ustvarili.
Ustvarite novo datoteko z imenom **index.html**. Dodajte naslednji HTML:
```html
Typing game
Typing game!
Practice your typing skills with a quote from Sherlock Holmes. Click **start** to begin!
```
### Zagon aplikacije
Vedno je najbolje razvijati iterativno, da vidimo, kako stvari izgledajo. Zaženimo našo aplikacijo. Obstaja čudovita razširitev za Visual Studio Code, imenovana [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon), ki bo gostila vašo aplikacijo lokalno in osvežila brskalnik vsakič, ko shranite.
- Namestite [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer&WT.mc_id=academic-77807-sagibbon) tako, da sledite povezavi in kliknete **Install**
- Brskalnik vas bo pozval, da odprete Visual Studio Code, nato pa vas bo Visual Studio Code pozval, da izvedete namestitev
- Po potrebi znova zaženite Visual Studio Code
- Ko je nameščen, v Visual Studio Code pritisnite Ctrl-Shift-P (ali Cmd-Shift-P), da odprete paleto ukazov
- Vnesite **Live Server: Open with Live Server**
- Live Server bo začel gostiti vašo aplikacijo
- Odprite brskalnik in pojdite na **https://localhost:5500**
- Zdaj bi morali videti stran, ki ste jo ustvarili!
Dodajmo nekaj funkcionalnosti.
## Dodajte CSS
Ko smo ustvarili HTML, dodajmo CSS za osnovno oblikovanje. Moramo označiti besedo, ki jo mora igralec vtipkati, in obarvati besedilno polje, če je vneseno besedilo napačno. To bomo storili z dvema razredoma.
Ustvarite novo datoteko z imenom **style.css** in dodajte naslednjo sintakso.
```css
/* inside style.css */
.highlight {
background-color: yellow;
}
.error {
background-color: lightcoral;
border: red;
}
```
✅ Ko gre za CSS, lahko svojo stran oblikujete, kakor želite. Vzemite si nekaj časa in naredite stran bolj privlačno:
- Izberite drugačno pisavo
- Obarvajte naslove
- Spremenite velikost elementov
## JavaScript
Ko smo ustvarili uporabniški vmesnik, se osredotočimo na JavaScript, ki bo zagotovil logiko. Razdelili bomo to na nekaj korakov:
- [Ustvarite konstante](../../../../4-typing-game/typing-game)
- [Poslušalec dogodkov za začetek igre](../../../../4-typing-game/typing-game)
- [Poslušalec dogodkov za tipkanje](../../../../4-typing-game/typing-game)
Najprej pa ustvarite novo datoteko z imenom **script.js**.
### Dodajte konstante
Potrebovali bomo nekaj elementov, da si olajšamo programiranje. Spet, podobno kot recept, tukaj je, kaj bomo potrebovali:
- Tabelo z vsemi citati
- Prazno tabelo za shranjevanje vseh besed trenutnega citata
- Prostor za shranjevanje indeksa besede, ki jo igralec trenutno tipka
- Čas, ko je igralec kliknil začetek
Prav tako bomo želeli reference na elemente uporabniškega vmesnika:
- Besedilno polje (**typed-value**)
- Prikaz citata (**quote**)
- Sporočilo (**message**)
```javascript
// inside script.js
// all of our quotes
const quotes = [
'When you have eliminated the impossible, whatever remains, however improbable, must be the truth.',
'There is nothing more deceptive than an obvious fact.',
'I ought to know by this time that when a fact appears to be opposed to a long train of deductions it invariably proves to be capable of bearing some other interpretation.',
'I never make exceptions. An exception disproves the rule.',
'What one man can invent another can discover.',
'Nothing clears up a case so much as stating it to another person.',
'Education never ends, Watson. It is a series of lessons, with the greatest for the last.',
];
// store the list of words and the index of the word the player is currently typing
let words = [];
let wordIndex = 0;
// the starting time
let startTime = Date.now();
// page elements
const quoteElement = document.getElementById('quote');
const messageElement = document.getElementById('message');
const typedValueElement = document.getElementById('typed-value');
```
✅ Dodajte več citatov v svojo igro
> **NOTE:** Elemente lahko kadar koli pridobimo v kodi z uporabo `document.getElementById`. Ker bomo te elemente redno uporabljali, se bomo izognili tipkarskim napakam z uporabo konstant. Okviri, kot sta [Vue.js](https://vuejs.org/) ali [React](https://reactjs.org/), vam lahko pomagajo bolje upravljati centralizacijo kode.
Vzemite si minuto in si oglejte video o uporabi `const`, `let` in `var`.
[](https://youtube.com/watch?v=JNIXfGiDWM8 "Vrste spremenljivk")
> 🎥 Kliknite zgornjo sliko za video o spremenljivkah.
### Dodajte logiko za začetek
Za začetek igre bo igralec kliknil na gumb za začetek. Seveda ne vemo, kdaj bo kliknil na začetek. Tukaj pride v poštev [poslušalec dogodkov](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener). Poslušalec dogodkov nam omogoča poslušanje, da se nekaj zgodi (dogodek), in izvedbo kode kot odziv. V našem primeru želimo izvesti kodo, ko uporabnik klikne na začetek.
Ko uporabnik klikne **start**, moramo izbrati citat, nastaviti uporabniški vmesnik in nastaviti sledenje trenutni besedi ter čas. Spodaj je JavaScript, ki ga morate dodati; razložimo ga takoj po bloku kode.
```javascript
// at the end of script.js
document.getElementById('start').addEventListener('click', () => {
// get a quote
const quoteIndex = Math.floor(Math.random() * quotes.length);
const quote = quotes[quoteIndex];
// Put the quote into an array of words
words = quote.split(' ');
// reset the word index for tracking
wordIndex = 0;
// UI updates
// Create an array of span elements so we can set a class
const spanWords = words.map(function(word) { return `${word} `});
// Convert into string and set as innerHTML on quote display
quoteElement.innerHTML = spanWords.join('');
// Highlight the first word
quoteElement.childNodes[0].className = 'highlight';
// Clear any prior messages
messageElement.innerText = '';
// Setup the textbox
// Clear the textbox
typedValueElement.value = '';
// set focus
typedValueElement.focus();
// set the event handler
// Start the timer
startTime = new Date().getTime();
});
```
Razčlenimo kodo!
- Nastavitev sledenja besedam
- Z uporabo [Math.floor](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) in [Math.random](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random) naključno izberemo citat iz tabele `quotes`
- `quote` pretvorimo v tabelo `words`, da lahko sledimo besedi, ki jo igralec trenutno tipka
- `wordIndex` nastavimo na 0, saj bo igralec začel z prvo besedo
- Nastavitev uporabniškega vmesnika
- Ustvarimo tabelo `spanWords`, ki vsebuje vsako besedo znotraj elementa `span`
- To nam omogoča označevanje besede na prikazu
- `join` tabelo, da ustvarimo niz, ki ga lahko uporabimo za posodobitev `innerHTML` na `quoteElement`
- To bo prikazalo citat igralcu
- Nastavimo `className` prvega elementa `span` na `highlight`, da ga označimo kot rumenega
- Počistimo `messageElement` tako, da nastavimo `innerText` na `''`
- Nastavitev besedilnega polja
- Počistimo trenutno `value` na `typedValueElement`
- Nastavimo `focus` na `typedValueElement`
- Začnemo časovnik z uporabo `getTime`
### Dodajte logiko za tipkanje
Ko igralec tipka, se sproži dogodek `input`. Ta poslušalec dogodkov bo preveril, ali igralec pravilno tipka besedo, in obravnaval trenutni status igre. Vrnite se v **script.js** in na konec dodajte naslednjo kodo. Razčlenili jo bomo takoj zatem.
```javascript
// at the end of script.js
typedValueElement.addEventListener('input', () => {
// Get the current word
const currentWord = words[wordIndex];
// get the current value
const typedValue = typedValueElement.value;
if (typedValue === currentWord && wordIndex === words.length - 1) {
// end of sentence
// Display success
const elapsedTime = new Date().getTime() - startTime;
const message = `CONGRATULATIONS! You finished in ${elapsedTime / 1000} seconds.`;
messageElement.innerText = message;
} else if (typedValue.endsWith(' ') && typedValue.trim() === currentWord) {
// end of word
// clear the typedValueElement for the new word
typedValueElement.value = '';
// move to the next word
wordIndex++;
// reset the class name for all elements in quote
for (const wordElement of quoteElement.childNodes) {
wordElement.className = '';
}
// highlight the new word
quoteElement.childNodes[wordIndex].className = 'highlight';
} else if (currentWord.startsWith(typedValue)) {
// currently correct
// highlight the next word
typedValueElement.className = '';
} else {
// error state
typedValueElement.className = 'error';
}
});
```
Razčlenimo kodo! Začnemo z zajemom trenutne besede in vrednosti, ki jo je igralec doslej vnesel. Nato imamo logiko, kjer preverimo, ali je citat dokončan, beseda dokončana, beseda pravilna ali (nazadnje), ali je prišlo do napake.
- Citat je dokončan, kar je označeno z `typedValue`, ki je enak `currentWord`, in `wordIndex`, ki je enak enemu manj kot `length` tabele `words`
- Izračunamo `elapsedTime` tako, da od trenutnega časa odštejemo `startTime`
- `elapsedTime` delimo z 1.000, da pretvorimo iz milisekund v sekunde
- Prikažemo sporočilo o uspehu
- Beseda je dokončana, kar je označeno z `typedValue`, ki se konča s presledkom (konec besede), in `typedValue`, ki je enak `currentWord`
- Nastavimo `value` na `typedElement` na `''`, da omogočimo vnos naslednje besede
- Povečamo `wordIndex`, da preidemo na naslednjo besedo
- Prehodimo vse `childNodes` elementa `quoteElement`, da nastavimo `className` na `''`, da se vrnemo na privzeti prikaz
- Nastavimo `className` trenutne besede na `highlight`, da jo označimo kot naslednjo besedo za tipkanje
- Beseda je trenutno pravilno vtipkana (a ni dokončana), kar je označeno z `currentWord`, ki se začne z `typedValue`
- Poskrbimo, da je `typedValueElement` prikazan kot privzet, tako da počistimo `className`
- Če smo prišli do sem, imamo napako
- Nastavimo `className` na `typedValueElement` na `error`
## Preizkusite svojo aplikacijo
Prišli ste do konca! Zadnji korak je, da zagotovite, da vaša aplikacija deluje. Preizkusite jo! Ne skrbite, če so napake; **vsi razvijalci** imajo napake. Preučite sporočila in odpravljajte težave po potrebi.
Kliknite na **start** in začnite tipkati! Videti bi moralo približno tako kot animacija, ki smo jo videli prej.

---
## 🚀 Izziv
Dodajte več funkcionalnosti
- Onemogočite poslušalca dogodkov `input` ob zaključku in ga znova omogočite, ko je gumb kliknjen
- Onemogočite besedilno polje, ko igralec dokonča citat
- Prikažite modalno okno s sporočilom o uspehu
- Shranite najboljše rezultate z uporabo [localStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage)
## Kviz po predavanju
[Kviz po predavanju](https://ff-quizzes.netlify.app/web/quiz/22)
## Pregled in samostojno učenje
Preberite si [vse dogodke, ki so na voljo](https://developer.mozilla.org/docs/Web/Events) razvijalcem prek spletnega brskalnika, in razmislite o scenarijih, v katerih bi uporabili posameznega.
## Naloga
[Ustvarite novo igro s tipkovnico](assignment.md)
---
**Omejitev odgovornosti**:
Ta dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.