- What is HTML?
- What is CSS?
- What is JavaScript?
All projects begin with creating single file! Create application entry point, which will be HTML document file
named index.html
- Base HTML document
- Create empty file
index.html - Define document base structure
- define
!doctypetag with attributehtmlon the first line - define
htmltag withheadandbodytags. Set lang attribute toen - inside
headtag- add
metatag with attributecharsetwith valueutf-8to set page encoding - add
titletag with text childrenPlay Hangman!
- add
- define
- Inside
bodyadddivtag with text childrenTest! - Open file in a browser to see if it works. You should see text from
divelement and title of window in Tab should be as You defined intitletag
We need to separate code into files, according to its purpose. Connect style sheet and script file to your HTML document.
- loading stylesheets
- loading scripts
- printing to browser console
- Create empty file called
styles.css- Add styling for
body. You can create style rule with template below:[tagName or selector]: { [cssProperty]: value; } - Set rule
family-fontwith valuesans-seriffor tagbody
- Add styling for
- Link style sheet to HTML document. In
headtag, inside document, add new taglinkwith 3 attributesrelattribute set linking relation, set value tostylesheettypeattribute with valuetext/csshrefattribute with file name to link with valuestyles.css- Open
index.htmlin browser and inspectdivelement to check if stylesheet is loaded.
- Create empty file
game.js- inside add following line:
console.log('game script loaded');
- inside add following line:
- Link script to HTML document. Add
scripttag with 2 attributes as last child ofbodytag:typewith valuetext/javascriptso browser can parse text as javascriptsrcwith valuegame.js- Check browser console to see if message is printed
To make dynamic pages we need to manipulate DOM. We can edit, add or delete HTML elements.
- finding DOM element with
idattribute - appending node to DOM
documentbuilt-in functions- storing references using
const
- Inside
index.htmladdidattribute with valuegameContenttodivinsidebodyto be able to find this element easily in javascript code - Inside
game.js:- Using built-in function
document.getElementByIdfinddivwith idgameContentdocument.getElementByIdfunction takes one parameter - element id- store returned value using
constfor later usage.
- Clear element content by changing element attribute
textConentto empty string''- To access element attribute use dot operator
gameContent.textContentand attribute name. - To change attribute value, just assign new value directly as so:
element.attribute = newValue;
- To access element attribute use dot operator
- Check browser to see that text initially loaded with html file is cleared
- Create header element with text
Welcome to Hangman!- To create element use
document.createElement- Pass one argument to function with tagName with value
h1 - Set
textContentattribute with valueWelcome to Hangman! - store returned value using
constwith nameviewTitle
- Pass one argument to function with tagName with value
- Append created header element to previously stored found element
- To append children to element use method
appendChildelement.appendChild(childElement);
- To append children to element use method
- To create element use
- Check browser to see changes. Investigate Element tab in devTools to see html structure
- Using built-in function
Every web application is based on user events. Handle blur event in input element, to see what character user
is typing.
- listening for elements events
- inline functions
- Create
divelement with textEnter your name:- store reference using
constwith namenameInputLabel
- store reference using
- Create
inputelement- store reference using
constwith namenameInput
- store reference using
- Add event listener to
inputelement- to add event listener to element use element method
addEventListener- first argument of this method is event name, listen for an element blur event (fired when element loses focus) which value is
blur - second argument is function which will be called every times element fires proper event
- inside event listener function add call to
console.logand print value of event target- event listener function first argument is event object which describes an event that took place
- example:
nameInput.addEventListener('input', event => { console.log(event.target.value); });
- inside event listener function add call to
- first argument of this method is event name, listen for an element blur event (fired when element loses focus) which value is
- to add event listener to element use element method
- Append new elements to
divwith game content - Try entering some text and lose focus by a click outside input to see if console is printing text entered in input
Some values change over time. Use mutable let to store some mutable data.
- mutable reference with
let
- Create variable for storing value from input with name
nameat file beginning- to store mutable value use
let - while defining, assign empty string:
'' letvalue can be reassigned whileconstnot
- to store mutable value use
- Inside input event listener assign value from an event to created variable
- After assigning print value of created variable, instead directly reading from event to check if value was stored correctly.
While writing code, it's getting longer and more complicated. Use function to group and encapsulate related logic.
- defining own functions with
functionkeyword
- Create function called
welcomeView, that takes no arguments- To create function use key word
functionfollowed by function name and then round parenthesis - example:
function nameIt() { // instructions }
- To create function use key word
- Move code to function (everything besides
letfor storing name,constfor storing reference to div with idgameContentand clearing its content)- creating header, label and input
- event listening
- appending to DOM
- Call created function
- check browser, there should be no visible changes
Drawing to screen is repeatable process. We need to abstract rendering logic to function.
- conditions
ifstatement
- Create mutable variable for storing active view name, with name
activeView- initialize variable with value
welcome
- initialize variable with value
- Create function called
renderwith no arguments - Move line with setting
gameContent.textContentto empty string torenderfunction - Inside
rendercheck which view render- use
ifstatement- example
ifstatement:
if (condition1) { // do something }
- example
- if
activeViewis equal towelcomecallwelcomeView
- use
- Check browser if there is still no changes in application visually
Every modern application is capable of changing whole view in instant. Create abstraction for changing view.
- contacting strings
else if
- Create function
playView- inside create
h1element with text equal to'Hi, ' + name
- inside create
- Inside
welcomeViewfunction- Create
buttonelement with text equal toPlay game! - Add event listener for event
click- on every click change value of
activeViewtoplay - call
renderfunction
- on every click change value of
- append button to game content
- Create
- Add additional condition statement inside
renderusingelse if, check ifactiveViewis equalplay- call
playViewfunction
- call
- Check browser, test if clicking in the button with text
Play game!changes view
Every game needs to be endless in terms of state. After ending game, we want to start over.
elsestatement
- Create function
endGameViewwith no arguments- create element
h1with textGame finished! - create
buttonelement with textPlay again- add event listener for
clickevent- change active view to welcome view
- call
render
- add event listener for
- append elements to game content
- create element
- Inside
playViewcreate button with textGive up- listen for
clickevent- change
activeViewvalue toendGame - call
renderfunction
- change
- append element to game content
- listen for
- Append the button to game content
- Inside
renderfunction addelsestatement afterelse ifstatement with call toendGameViewfunction
As game grows in logic, it stores more data, keeping it spread out all over code base is disaster. Define all game state as single object.
- objects
- Create an object named
gameState- example:
const objectName = { key1: 'value', key2: 123, }
- Add field
namewith initial value'' - Add field
activeViewwith initial valuewelcome - Change all usage of
nameandactiveViewvariables to usegameStateobject - Delete unused variables
- Check browser to see if everything works
Create more elastic way of rendering view. Instead of modifying game content, view should return new content to be shown.
- Updating objects
- Create function
gameStateUpdatewith body below:function gameStateUpdate(newGameState) { Object.assign(gameState, newGameState); render(); }
- Add 3 arguments to all view functions
- first argument named
contnt - second argument named
state - third argument named
stateUpdate
- first argument named
- Pass
gameCotent,gameStateandstateUpdateto every view call - Change lines modifying
gameStatedirectly to calls withstateUpdatewith proper partial state updated- example:
stateUpdate({ activeView: 'play' });
- remove call to
render- it's called by state update function now
- Change
gameStateusage inside views tostate - Change
gameContentusage inside views tocontent - Set value for input while rendering welcome view with stored name
- access current input value with
valuefield of element and assign new value from store
- access current input value with
- Inside render function
- add new mutable variable
viewContentwithout initializer - inside
ifstatement assign returned value toviewContent - after
ifstatement appendviewContentto game content
- add new mutable variable
- Check browser to see that input won't change value - everything else should work as previously
Add some real game logic. Draw all alphabet letters in play view, store clicked letters and disable one that were already clicked.
- Arrays
forloop
- At the beginning of file create variable to store all alphabet letters using array
- example array syntax
const array = ['element1', 'element2'];
- use english alphabet
- Add to
gameStateobject new fieldselectedLetters, and initialize it with empty array - Inside
playViewcreatedivelement for storing letters buttons - Iterate over all letters and create
buttonelement- to iterate overall letters use
forloop
for (let i = 0; i < 10; i++) { // iteration for 10 times }
- set button text to equal iterated letter
- set
disabledattribute by checking instate.selectedLettersincludes iterated letter- use array method
includeswhich takes one argument - element in an array you are looking for
- use array method
- add event listener for click event for every letter button
- on a click, update state so
state.selectedLettersincludes clicked letter- use array method
concat
- use array method
- on a click, update state so
- append the button to buttons container
- to iterate overall letters use
- Append buttons container to view content
We need so phrase to guess. Create some phrases and make some randomness logic while getting new phrase to guess.
- array function iterating
- logic operators
- randomness
- At the beginning of file create array for holding phrases that user will be guessing
- Create function for getting random phrase
- Inside create variable for holding random phrases array index
- Use
Math.floorandMath.randomand phrases array length - return phrase at found index
- Use
- Inside create variable for holding random phrases array index
- Add a new field to game state called
secretPhrasewith initial value empty string - Inside
welcomeViewwhen user click button- update state so
secretPhraseinside game state will be random phrase- use previously created function to get random phrase
- reset
selectedLettersto empty array
- update state so
- Inside
playView- Create a container for phrase letters (
div) - Split
state.secretPhraseinto array holding single letter- use string method
string.split(seperator) - split by empty string
''
- use string method
- Iterate over every letter
- use array function to iterate:
[1,2,3].forEach(number => { // iterates 3 times, every time argument `number` holding another element from iterated array })
- create span element
- check if letter is visible
- it is visible if letter is space character
- OR it is visible if letter is included in
state.selectedLetters - use OR operator
||
- Set element text using previously calculated value (is letter visible) with ternary expression
- example ternary expression:
const result = condition ? ifConditionTrue : ifConditionFalse- use asterisk character
*for hidden letters
- Append a span to letters container
- Append letters container to view before letters buttons container
- Create a container for phrase letters (
We need to add some logic for game to end. Every time user sees all letters in a phrase - call it end game.
- string template
everymethod on array
-
Inside play view add mutable variable for storing count of visible letters
- initialize it with number
0
- initialize it with number
-
Add to game state new field
mistakesand initialize it with number0 -
Inside letter button listener
- check if clicked letter was mistake
- use string method
includes- example
'abc'.includes('a'); // -> true
- use string method
- store check result in variable
- store selected letters to const (
const selectedLetters = state.selectedLetters.concat(letter);)- inside
stateUpdateuse object short notation{ selectedLetters: selectedLetters}is the same as{ selectedLetters }
- inside
- check if all letters are visible after selecting letter
- split
state.secretPhrasewith empty string to iterate over all letters usingeverymethod- array method
everyreturn true, only if all iterations on array returns true - while iterating check if
selectedLettersincludes iterated letter- if iterated letter is space, return true
- store iteration result
- array method
- split
- update active view to
endif all letters are visible or toplayif not
- check if clicked letter was mistake
-
Increase mistakes count if letter button was mistake
- use ternary expression
- for a good click don't change value
-
In end view create
h3element- use string template to interpolate a message for user, containing information about mistakes count
- example string template
const foo = 123; const text = `count: ${foo}`;
-
Clear
mistakesandselectedLettersfor every time user clickPlay game!button
Every time we refresh our page, progress is lost. Persist game state using browser local storage.
- localStorage
- JSON api
- Create variable for persisted game state read from local storage
- initialize it with loaded item from local storage
- example
const storedValue = localStorage.getItem('key');
- While initializing game state variable check if loaded game state from local storage is not empty and use this value
to initialize game state
- use ternary expression while assigning value
- parse value using JSON api
- use
parsemethod from api to parse string into object
const parsedObject = JSON.parse('{ "a": true }'); // -> { a: true }
- use
- Update
gameStateUpdatefunction, so it updates local storage on every state update- use
setItemmethod from local storage api - parse game state object to string using JSON api
- use
stringifymethod
const stringifiedObject = JSON.stringify({ a: true }); // -> '{ "a": true }'
- use
- use
Let draw Hangman! Every time, user make another mistake, draw another hangman part.
- template tag
- Add to
index.htmlcode below, afterdivwith idgameContent<template id="hangman"> <div class="hangmanContainer"> <div class="hangmanRow"> <div></div> <div class="hangmanHead"></div> <div></div> </div> <div class="hangmanRow"> <div class="hangmanLeftHand"></div> <div class="hangmanBody"></div> <div class="hangmanRightHand"></div> </div> <div class="hangmanRow"> <div class="hangmanLeftLeg"></div> <div></div> <div class="hangmanRightLeg"</div> </div> </div> </template>
- Add styles below to
styles.css.hangmanContainer { width: 200px; display: flex; flex-direction: column; } .hangmanRow { display: flex; justify-content: center; } .hangmanHead { border: solid 5px; width: 25px; height: 25px; border-radius: 50%; opacity: 0; } .hangmanBody { border: solid 3px; height: 20px; opacity: 0; } .hangmanLeftHand { border: solid; transform: rotate(45deg); margin-right: 5px; opacity: 0; } .hangmanRightHand { border: solid; transform: rotate(-45deg); margin-left: 5px; opacity: 0; } .hangmanLeftLeg { border: solid; height: 30px; transform: rotate(25deg); margin-right: 5px; opacity: 0; } .hangmanRightLeg { border: solid; height: 30px; transform: rotate(-25deg); margin-left: 5px; opacity: 0; }
- Inside play view using
document.querySelectorfindtemplateelement with idhangman- store to variable
- Clone template using template element method
content.cloneNode- pass
trueas argument
- pass
- Find element with class
hangmanHeadinside cloned element usingquerySelectormethod- pass proper css selector as argument
- store it as variable
- Repeat process for
hangmanBody,hangmanLeftHand,hangmanRightHand,hangmanLeftLeg,hangmanRightLeg - Set opacity style of every Hangman part
- change element style by changing
styleproperty- change
opacitystyle property - check mistakes count and assign proper opacity value for every part
- if mistakes count is greater than
0set head opacity to'1' - for every mistake show one part more
- if mistakes count is greater than
- change
- change element style by changing
Keeping all code in one file is not a good idea. Extract view functions to separate files.
- global scope
- closures
- Create separate files for views functions
- Add scripts in
index.html- add before
game.js
- add before
- Move code to files
After creating base project, You can try adding more functionalities by Your own, without steps with explanation.
- Game timer. Add a timer which will count down from fixed value 1a. Add way to set up time on first view
- Keyboard support. Try adding keyboard key pressed handling to update app state
- Score board. Add score board. It can be displayed on the last view, after game is over. Use player time, mistakes and phrase characters count to sort results.
- Load phreses from file using
fetchapi.
When your work is done, it is time to share it with world! Publish your project using GitHub pages
- Go push changes to github
- Go to project settings
- Scroll down to
GitHub Pagessection - Choose
masterbranch as source - Done! Go to
username.gitbub.io/project-name
- Tag/TagName - html element name, for example
div - Element/node - using proper tags You can create elements for example
<div>hello</div> - Attribute or element attribute - element can have additional attributes, for example:
charsetis attribute ofmetatag -<meta charset="utf-8"> - Children of element/node are descent of this element, for example:
<div><p>hello</p></div>, element with tagpis children of element with tagdiv