Structure: done. Styling: done. The Night's Watch form is solid and has a handsome dark look. One problem: fill it out and press the button, and nothing interesting happens. The page looks good, but stands still.
Time for the third pillar: JavaScript, the programming language the browser executes. If HTML is the skeleton and CSS the clothing, JavaScript is the muscles and reflexes: reacting to users, making decisions, changing the page live.
π Bringing JavaScript into the page
Like CSS, code must be connected to HTML. The right approach is a separate file loaded with a <script> tag inside <head>:
<script src="guardiani.js" defer></script>defer matters: it tells the browser "download the file, but execute it only after building the DOM". Remember the tree from the browser chapter? If our code looked for a button before the browser created it, it would find nothing. defer keeps the order right.
π¦ Variables: naming things
A variable is a labeled box: put a value inside and retrieve it by name. Modern JavaScript has two ways to declare one:
1const nomeGuardiano = 'Jon Snow'; // will not change
2let reclute = 3; // can change over time
3
4reclute = reclute + 1; // ok: there are now 4 recruitsA rule that will save a thousand bugs: use const by default, switching to let only when a value really needs to change. Fewer mutable things mean fewer things that can break.
Variables can hold text (strings), numbers, true/false values (booleans), lists (arrays) and structures containing properties (objects):
1const motto = 'Night gathers'; // string
2const annoFondazione = -8000; // number (approximately)
3const accettaReclute = true; // boolean
4const castelli = ['Castle Black', 'Eastwatch']; // array
5const guardiano = { nome: 'Jon', ruolo: 'Lord Commander' }; // objectOlder tutorials also use
var, the old declaration style with quirks addressed byletandconst. Recognize it when you see it, but do not use it in new code.
βοΈ Functions: named actions
If a variable is a box, a function is a recipe: a named block of instructions ready to execute when needed, even a thousand times.
1function saluta(nome) {
2 return `Welcome to the Wall, ${nome}!`;
3}
4
5saluta('Jon'); // "Welcome to the Wall, Jon!"
6saluta('Sam'); // "Welcome to the Wall, Sam!"Notice two things. First, nome is a parameter, a placeholder receiving the value supplied at the call. Second, text between backticks (`) is a template literal, letting you insert variables through ${...}, much more conveniently than joining strings with +.
There is a more compact syntax, the arrow function, found everywhere in modern code:
const saluta = (nome) => `Welcome to the Wall, ${nome}!`;Same substance, less ceremony.
π Events: responding to users
Here comes the turning point. The browser creates an event whenever something happens (a click, a keypress, a form submission), and JavaScript listens through addEventListener:
1const bottone = document.querySelector('.btn-invio');
2
3bottone.addEventListener('click', () => {
4 console.log('Someone wants to enlist!');
5});Read the first line carefully, because it is half the job: document.querySelector('.btn-invio') finds the first DOM element matching that CSS selector. Yes, the selectors from last chapter, reused exactly. The next part says: "when a click happens on this element, run this function".
And console.log? It writes to DevTools' Console tab. Open it (F12), click the button and watch your messages appear: every developer's first investigative tool.
π³ Manipulating the DOM
Listening is not enough: we also want to change the page. The browser's DOM is no static drawing: JavaScript can read and modify it live:
1const titolo = document.querySelector('#titolo-pagina');
2
3titolo.textContent = 'The Watch is watching you'; // changes the text
4titolo.classList.add('evidenziato'); // adds a CSS classThe tools are few and quick to learn:
document.querySelector(...): find an element using a CSS selector..textContent: read or change an element's text..classList.add / .remove / .toggle: manage CSS classes.document.createElement(...): create a brand-new element..value: read what users entered in a field.
π‘οΈ The form comes alive
Combine variables, functions, events and DOM to complete the Night's Watch trilogy. Create guardiani.js in the project folder (we already connected the <script> with defer):
1// find the form on the page
2const form = document.querySelector('.form-dark');
3
4form.addEventListener('submit', (event) => {
5 // prevent default submission: we handle it ourselves
6 event.preventDefault();
7
8 // read the name entered by the user
9 const nome = document.querySelector('#name').value;
10
11 // create the welcome message
12 const messaggio = document.createElement('p');
13 messaggio.classList.add('benvenuto');
14 messaggio.textContent = `Welcome to the Watch, ${nome}. Your watch begins.`;
15
16 // replace the form with the message
17 form.replaceWith(messaggio);
18});Add a class to guardiani.css to style the message:
1.benvenuto {
2 max-width: 500px;
3 margin: 40px auto;
4 padding: 20px;
5 background-color: #2e2e2e;
6 border-radius: 8px;
7 color: #f2f2f2;
8 text-align: center;
9}Reload, fill out the form and press "Join the Watch": the form disappears, replaced by a welcome containing your name. Notice event.preventDefault(): submitting normally reloads the page; we block that and decide what happens. This is the same principle behind chapter 2's Single Page Applications, except there a framework does it; here you did.
π ES6+: modern JavaScript
One last detail, almost a revelation. Today's JavaScript (const, let, arrow functions, template literals) did not always exist: it arrived in 2015 with ES6 (ECMAScript 2015), the update that transformed the language. A new version has appeared yearly since, hence "ES6+".
Congratulations: you used ES6 throughout this chapter without knowing it. Two more gems you will encounter soon:
1// destructuring: extract properties in one go
2const guardiano = { nome: 'Jon', casata: 'Stark' };
3const { nome, casata } = guardiano;
4
5// spread: copy and expand arrays and objects
6const veterani = ['Jeor', 'Aemon'];
7const guardia = [...veterani, 'Jon']; // ['Jeor', 'Aemon', 'Jon']Then there are modules (import/export, splitting code across files) and Promises with async/await, handling operations taking time, such as chapter 2's HTTP requests. We will give them the attention they deserve later.
β Conclusion
Look at what you have: an HTML-structured page, dressed with CSS and alive through JavaScript. All three frontend pillars working in one project. As promised, none succeeds alone.
One thing remains before closing this part: our form needs fifteen JavaScript lines, but real apps have hundreds of thousands. How do we stay sane? In the next chapter, frameworks and libraries (Angular, React, Vue) show how they tame complexity.