GAMR1520: Markup languages and scripting

GAMR1520

Markup languages and scripting

Introduction to JavaScript

Dr Graeme Stuart


HyperText

Hypertext documents are non-linear. The links are part of the content.

Many digital hypertext systems existed in the 80’s, but none were very successful.

hypertext
Hypertext documents include embedded links to other hypertext documents

“‘Hypertext’ is a recent coinage. ‘Hyper-‘ is used in the mathematical sense of extension and generality (as in ‘hyperspace,’ ‘hypercube’) rather than the medical sense of ‘excessive’ (‘hyperactivity’). There is no implication about size— a hypertext could contain only 500 words or so. ‘Hyper-‘ refers to structure and not size.” — Ted Nelson, “Brief Words on the Hypertext”, 23 January 1967


HyperText Markup Language

In 1989, Tim Berners-Lee created the hypertext system known as the world wide web on top of the well-established internet. The core technologies included HTML (Hypertext Markup Language) and HTTP (HyperText Transfer Protocol).

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>My first HTML document</title>
    </head>
    <body>
        <h1>This is a document!</h1>
        <p>It's nice.</p>
    </body>
</html>

The web is essentially a publishing platform. Users create HTML documents and share them via an HTTP server. HTTP clients can access HTML documents from remote HTTP servers.

HTTP is an open protocol and proved popular within CERN, one of the largest networks on the internet, for sharing work with colleagues both inside and outside of CERN.


The basic elements

The first ever website was pretty basic.

<p>
    <a href="http://info.cern.ch/hypertext/WWW/TheProject.html">
        The first ever website
    </a>
    was pretty basic.
</p>

This is a level 1 heading (<h1>)

This is a level 2 heading (<h2>)

This is a level 3 heading (<h3>)

This is a level 4 heading (<h4>)

This is a paragraph (<p>)


The first(-ish) browser

The first recognisable web browser, Nexus, was created by Tim Berners-Lee in 1990.

Originally named WorldWideWeb, the name was changed to avoid confusion.

The Nexus web browser
The Nexus browser - "An exercise in global information availability"

The browser provided both authoring and editing functionality. See a demo here.


In the beginning… the browser wars

In the early years of the web a “Cambrian explosion” of browsers proliferated.

In 1994, the Mosaic browser was released and was an instant success.

The Mosaic browser
The Mosaic browser

Eventually Mozaic was renamed to Netscape Navigator - codename: Mozilla.


Images

The <img> element was introduced in HTML2.0 in 1995.

<img src="img/hypertext.svg" alt="hypertext">

hypertext


Javascript origins

Mocha / LiveScript was invented by Brendan Eich in 1995 for the Netscape 2 browser.

A young Brendan Brendan more recently

It was renamed to Javascript to sound a bit like Java, as a marketing ploy.

“Java is to JavaScript as car is to carpet” - Chris Heilmann


The first browser war - divergence

Computing giant Microsoft creates Internet Explorer and makes it available for free in 1995. Netscape Navigator struggled to remain viable (only free for home and educational use).

The first browser war
The first browser war and the beginning of the second

Many versions of each browser were released including new proprietary (i.e. non-standard) features.

Internet Explorer shipped with a reverse-engineered equivalent JScript in 1996. JScript and Javascript were subtly incompatible, obviously. Causing many developer headaches.


The second browser war - convergence

The Mozilla foundation created the open-source phoenix browser which became firebird and then firefox. Instead of touting proprietary extensions, browsers were marketed based on standards compliance.

The first browser war
Firefox is open-sourced, Google release Chrome and mobile browsers are on the rise

Google Chrome includes faster JavaScript and an open-source version Chromium. Internet Explorer is now obsolete and the current Microsoft Edge browser is now based on the Chromium code.

The Mozilla Developer Network (MDN) is your best resource on web technologies - including JavaScript


Standardisation - ECMAScript

Netscape submitted JavaScript to ECMA International in November 1996 and the first ECMAScript specification was released in June 1997. A huge update came in ECMAScript 2015 (ES6) which made my book obsolete on the day it was published.

ECMAScript June 1997
ECMAScript 2 June 1998
ECMAScript 3 Dec 1999

“ECMAScript was always an unwanted trade name that sounds like a skin disease.” - Brendan Eich

ECMAScript 2015 June 2015
ECMAScript 2016 June 2016
ECMAScript 2018 June 2018
ECMAScript 2022 June 2022
ECMAScript 2023 June 2023
ECMAScript 2024 June 2024?

Linking a script into an HTML document

Javascript code can be added directly into an HTML document.

Simply insert a <script> element anywhere in the document <head> or <body>.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My first HTML document</title>
</head>
<body>
    <h1>This is a document!</h1>
    <p>It's nice.</p>
    <script src="scripts.js"></script>
</body>
</html>

Be careful, the script will be executed inline, so any elements following the script will not be in the DOM yet. This is why it is common to insert scripts at the end of the <body> element.


The Document Object Model

The browser requests documents and related content from the web via HTTP, parses the HTML and populates the Document Object Model (DOM).

<html lang="en">
    <head>
    <title>my webpage</title>
</head>
<body>
    <h1>Welcome</h1>
    <p>Hello world</p>
    <img src="hw.png" alt="world">
</body>
</html>

Javascript can be used to manipulate the nodes of the DOM.

Diagram of the Document Object Model
Diagram of the Document Object Model (DOM)

adapted from MDN


DOM Manipulation

The DOM, or Document Object Model is a data structure representing the entire document.

Within the context of a web page, the most common thing to do is known as DOM manipulation. This is done via the global document object which presents many methods for inspecting and manipulating the DOM.

e.g. We can grab an element and change its content. Here we get all the <div> elements and change the content of the first one.

const divs = document.getElementsByTagName('div');
divs[0].textContent = "I have replaced the original content with my own.";

We can also create new elements and insert them into the DOM.

const newDiv = document.createElement('div');
newDiv.textContent = "this is a completely new element.";
document.body.append(newDiv);

Most of what you can imagine can be done using the built-in document API.


Syntax comparison with python

Code blocks in python were identified by indentation. In JavaScript we wrap code blocks in curly braces. The simple conditional if statement shows the basic pattern.

In python the compound statement expects a code block, identified by indentation.

if a > 10:
    print(a)

In Javascript we have a similar code block, wrapped in curly braces.

if(a > 10) {
    console.log(a);
}

In JavaScript, the if statement also needs parentheses. Good JavaScript has semi-colons at the end of every line. Though the interpreter does not insist.

If you find mistakes in my code, please let me know.


Declaring variables - scope

Before ECMAScript 2015, there was only one way to declare a variable and all variables were globally-scoped.

var a = 1;  // Globally-scoped and reassignable  - DON'T DO THIS

Never use the old var, replace it if you find it in code on the web. let is always a good replacement as it will see subtle errors which var may miss.

ECMAScript 2015 introduced a better, block-scoped variable as well as a more restricted constant variable.

const b = 1 // Block-scoped and not reassignable - USE THIS BY DEFAULT 
let c = 1;  // Block-scoped and reassignable     - THIS IS FINE WHEN NEEDED

Using const or let to declare variables is highly recommended. Use const when you will never reassign the variable. Use let only if you need to reassign to a new value.

The reason to use const is that we want to get an error if we accidentally reassign.


Strict mode vs sloppy mode

In sloppy mode (the default) this mistake would not be picked up.

// declare a variable
let myVariable;

// later in your code, you make a typo
myVarible = 17;

Activating strict mode ensures this line throws a ReferenceError

"use strict";
let myVariable;

myVarible = 17;
Uncaught ReferenceError: myVarible is not defined

Using strict mode is good. Strict mode will raise more errors and enforce better habits. For example, errors will be raised if a variable is not declared.


Data types, literals and operations

Data types in Javascript are pretty simple. The basic data types are Boolean, Number and String. There is also a Bigint data type for when values greater than 253 are reasonably expected.

const a = true;     // Boolean
const b = 2.6;      // Number
const c = 'hello';  // String
const d = 1n;       // Bigint

Basic maths operations are pretty intuitive. However, be careful because the rules for type coercion might be unexpected e.g. the + operator is used for both addition and string concatenation. If either operand is a string, it coerces the other to a string.

"23.5" * 10 // 235
23.5 * "10" // 235
"1.2" + 8.8 // "1.28.8"
1.2 + "8.8" // "1.28.8"
"1.2" - 8.8 // -7.6000000000000005
1.2 - "8.8" // -7.6000000000000005

A common error - cannot read property of undefined

The undefined data type is a lot like None in python. You will often come across undefined if you try to access properties that don’t exist.

First, we create an object a and access a property that doesn’t exist. Our variable b becomes undefined. This is probably where the error in our code is. But it doesn’t raise a peep.

const a = 'hello';
const b = a.this_will_be_undefined;

Later, this line causes an error, because we can’t read properties of undefined.

const c = b.anything;
Uncaught TypeError: Cannot read properties of undefined (reading 'anything')

This is a very common error and you should recognise it when it arises and look back to work out why you have an undefined variable. Usually it’s because your data was not the type you thought it was.


Functions

Functions can help to avoid repetition and make code more readable and more maintainable.

function createElementWithContent(tagName, textContent) {
    const element = document.createElement(tagName);
    element.textContent = textContent;
    return element;
}

A newer syntax (ECMAScript 2015) for more compact functions using arrow (=>) notation.

const createElementWithContent = (tagName, textContent) => {
    const element = document.createElement(tagName);
    element.textContent = textContent;
    return element;
}

One-liners with single arguments can be very compact.

const doubleIt = value => value * 2;

Arrays

Arrays are list-like objects with built-in methods for traversal and mutation operations. They are indexed with integers (beginning with zero) and can contain anything.

const technologies = ["HTML", "CSS"];

technologies.length;        // 2
technologies[0];            // "HTML"

technologies.push('JS');    // This is not reassignment
technologies.length;        // 3

Functions such as map and forEach take callbacks.

technologies.map(tech => tech.toLowerCase())  // ["html", "css", "js"]

Looping over iterables

It’s fine to use the forEach method on arrays and array-like objects.

const technologies = ["HTML", "CSS", "JS"];

technologies.forEach((item, index) => {                     // "1: learn html"
  console.log(`${index + 1}: learn ${item.toLowerCase()}`); // "2: learn css"
});                                                         // "3: learn js"

Template literals use backticks and anything inside ${} brackets is interpolated.

But we can also iterate over a variety of iterable objects using for...of statements.

const paragraphs = document.getElementsByTagName('p');

for (const element of paragraphs) {
    element.textContent = "I AM A PARAGRAPH.";
}

Objects

JavaScript is a prototype-based language. Objects are more complicated than they first seem. However, we will not be covering the gory details in this module. We can start by thinking of objects as very similar to the python dict type.

let article = {
  title: "My article title",
  imageUrl: "images/article1.png",
  text: "Lorem ipsum dolor sit amet, incididunt ut labore et dolore magna aliqua."
}
console.log(article["text"]);

Notice the keys are not strings here.

Dot notation makes things easier, but imposes some restrictions.

console.log(article.title); // "My article title"
article.newKey = 12;
article['awkward key'] = "allowed, but awkward";

Prototype what?

Objects inherit functionality from their prototype. A prototype is just an object like anything else.

Setting and getting the prototype of an object can be done via the private __proto__ property.

const obj1 = {p1: "Hello world!", p2: 1000};
const obj2 = {p2: "Overridden value", p3: true, __proto__: obj1};
const obj3 = {p3: false, p4: "New property", __proto__: obj2};

for (const obj of [obj1, obj2, obj3]) {
    console.log(`p1: ${obj.p1}, p2: ${obj.p2}, p3: ${obj.p3}, p4: ${obj.p4}`);
}
p1: Hello world!, p2: 1000, p3: undefined, p4: undefined
p1: Hello world!, p2: Overridden value, p3: true, p4: undefined
p1: Hello world!, p2: Overridden value, p3: false, p4: New property

No classes are involved here.


It’s a bit like classes

Object properties can be functions.

const coordinate = {
  x: 0,
  y: 0,
  inverse() {
    // return an object with this as a prototype and inverted coordinates
    return {__proto__: this, x: this.y, y: this.x};
  }
};

const c = {x: 3, y: 10, __proto__: coordinate};
console.log(c);
console.log(c.inverse());
console.log(c.inverse().inverse());
{x: 3, y: 10}
{x: 10, y: 3}
{x: 3, y: 10}

Wait a minute… what’s this?


What’s this?

All functions have access to something called this. The value of this will depend how the function is called.

A loose function is called from the global context which is the window object in a browser. The window object contains properties such as document and defines methods such as alert etc.

function f() {
    console.log(this);
}
f();
Window

Placing that function into an object will make this refer to the object itself.

const o = {f: f}
o.f();
{f: f}

Constructor functions

Constructors are functions called with the new keyword.

function Coordinate(x, y) {
    this.x = x;
    this.y = y;
}
c = new Coordinate(5, 10);
Coordinate {x: 5, y: 10}

Constructors will copy a property called prototype over to each new object created with new. Adding methods to the prototype can add methods to each instance.

Coordinate.prototype.invert = function() {
    return new Coordinate(this.y, this.x);
}
c.invert();
Coordinate {x: 10, y: 5}

Classes

The class syntax introduced in ECMAScript 2015 makes it all much easier. Though its still pretty much the same underneath.

class Coordinate {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    invert(x, y) {
        return new Coordinate(this.y, this.x);
    }
}
const c = new Coordinate(100, 2);
console.log(c);
console.log(c.invert());
Coordinate {x: 100, y: 2}
Coordinate {x: 2, y: 100}

This is what you should use.


Asynchronous code

Accessing data from remote servers is also common. Though it requires asynchronous code which can be a little confusing. This code inserts a <ul> element containing one <li> per person.

async function getJSON(url) {
    const response = await fetch(url);
    return await response.json();
}

getJSON('https://swapi.py4e.com/api/people').then(people => {
    const ul = document.createElement('ul');
    document.body.append(ul);
    for (const person of people.results) {
        const li = document.createElement('li');
        li.textContent = person.name;
        ul.append(li);
    }
});

The result

A <ul> is an unordered list. It can contain list items (<li> elements).

<ul>
    <li>Luke Skywalker</li>
    <li>C-3PO</li>
    <li>R2-D2</li>
    <li>Darth Vader</li>
    <li>Leia Organa</li>
    <li>Owen Lars</li>
    <li>Beru Whitesun lars</li>
    <li>R5-D4</li>
    <li>Biggs Darklighter</li>
    <li>Obi-Wan Kenobi</li>
</ul>

Indentation added for clarity.


Lists

The two main kinds of lists are unordered (<ul>) and ordered (<ol>).

The unordered list is displayed as bullets.

  • Luke Skywalker
  • C-3PO
  • R2-D2
  • Darth Vader
  • Leia Organa
  • Owen Lars
  • Beru Whitesun lars
  • R5-D4
  • Biggs Darklighter
  • Obi-Wan Kenobi

An ordered list is a numbered list.

  1. Luke Skywalker
  2. C-3PO
  3. R2-D2
  4. Darth Vader
  5. Leia Organa
  6. Owen Lars
  7. Beru Whitesun lars
  8. R5-D4
  9. Biggs Darklighter
  10. Obi-Wan Kenobi

The HTML canvas

A <canvas> is an element like an image. Here we create a 300px by 100px image which is given a red background via style rules.

<canvas width="300" height="100" id="myCanvas" style="background: red;"></canvas>

JavaScript can be used to draw on the canvas. We use a 2d canvas context object (assigned here to the ctx variable) to access a simple drawing API.

const ctx = myCanvas.getContext('2d');
ctx.fillRect(100, 0, 100, 100);
ctx.fillRect(30, 30, 40, 40);
ctx.fillRect(230, 30, 40, 40);
ctx.fillStyle = "red";
ctx.fillRect(130, 30, 40, 40);

This is the resulting canvas. We drew three black (black is the default fill colour) rectangles. Then we changed the fill colour to red and drew another rectangle.

Thanks for listening

Any questions?

We should have plenty of time for questions and answers.

Just ask, you are probably not the only one who wants to know.

Dr Graeme Stuart