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’ 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 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.
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">
Javascript origins
Mocha / LiveScript was invented by Brendan Eich in 1995 for the Netscape 2 browser.
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).
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.
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 was always an unwanted trade name that sounds like a skin disease.” - Brendan Eich
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.
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 whichvar
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.
- Luke Skywalker
- C-3PO
- R2-D2
- Darth Vader
- Leia Organa
- Owen Lars
- Beru Whitesun lars
- R5-D4
- Biggs Darklighter
- 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.