Week 9 — Document Object Model (DOM)

This week covers HTML, CSS, the DOM API, events and callback functions.

HTML

  • HTML ⟶ a markup language. A structure for web pages (originates from XML).
  • Tags are denoted by angle brackets.
  • Almost every web page has a head and a body.
  • Head ⟶ where CSS and javascript files are loaded. Loaded before the rest of the page.
<html>
    <head>
        <script type="text/javascript" src="sketch.js"></script>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
    </body>
</html>
  • If a javascript file or a library fails to load, the head is the first place to look.

CSS vs Javascript

  • CSS (Cascading Style Sheets) ⟶ for styling elements.
  • Javascript ⟶ for interaction and logic. eg., mouse movement, clicking and navigating a page.
div.awesome {
    background-color: red;
}

HTML Elements (Tags)

  • div ⟶ most common element. A block element, other elements can be nested inside it.
  • img ⟶ image
  • a ⟶ anchor, a hyperlink to another page
  • p ⟶ paragraph, a place to write text
  • canvas ⟶ what p5js draws on

Attributes

  • id ⟶ identifies a specific element. Unique, no two elements can have the same id.
  • src ⟶ source, links to images
  • href ⟶ hyperlink reference, where the anchor goes on click
  • class ⟶ like id, but can be applied to multiple elements. Useful for CSS styling.
<div id="container" class="awesome">
    <img src="cat.jpg">
    <a href="https://p5js.org">p5js</a>
    <p class="awesome">Some text</p>
</div>

p5js app anatomy (index.html)

<head>
    <script src="../../libraries/p5.min.js"></script>
    <script src="../../libraries/p5.sound.min.js"></script>
</head>
<body>
    <script src="sketch.js"></script>
</body>

Note:

  • HTML loads elements one at a time (synchronously).
  • Always load libraries (dependencies) before sketch.js. Otherwise ⟶ error: p5 is not defined.

What is the DOM?

  • DOM ⟶ Document Object Model.
  • A way to tap into HTML using javascript ⟶ manipulate HTML elements and handle interaction with them.
  • p5js sits on top of the native javascript DOM API and makes it friendlier to use.
  • In the course, this needed the p5.dom library. It is now part of p5js itself, so no extra script tag is needed.

HTML Elements

Creating elements

  • createCanvas() ⟶ creates the canvas DOM element. This was a DOM element all along.
createDiv("text or html");
createP("paragraph");
createButton("click me");
createImg("path/to/image.jpg", "alt text");
createSlider(min, max, value);

// generic ⟶ (tag name, content)
createElement("h1", "Hi, I'm a header1");
  • HTML can be written inline inside these functions.
createDiv("Hi there, I'm inside the DOM. <a href='https://p5js.org'>p5js</a>");

Code in folder /9.4-CreatingElements

A page built only from p5 DOM functions: a div with a link, a paragraph, a button, an image, a slider and headers h1 to h6.
no canvas, just DOM elements

Positioning elements

var myDiv = createDiv("Hello, I'm a div");
myDiv.position(x, y);
  • translate() works only inside the canvas. DOM elements need position().
  • position is relative to the browser window, not the canvas ⟶ use windowWidth, windowHeight.
  • Create an element only once (in setup) and keep a reference in a variable. Don’t create it in draw.

Example (code in folder /9.4-PositioningElements)

var header;

function setup() {
    noCanvas();
    header = createElement("h1", 'Hi, I\'m an H1 tag');
    frameRate(1);
}

function draw() {
    header.position(random(windowWidth), random(windowHeight));
}
  • \ ⟶ escape character. To use a quotation mark inside a string with the same quotation marks.

Injecting HTML

  • html() ⟶ sets the content inside an element.
  • DOM elements can be stored in arrays like any other object.
  • There is no rotate() for DOM elements ⟶ use sin and cos to place them in a circle.

Example (code in folder /9.4-InjectingHTML)

var elements = [];

function setup() {
    noCanvas();
    for (var i = 0; i < 10; i++) {
        var elm = createElement("h1", "");
        var angle = map(i, 0, 10, 0, TWO_PI);
        elm.position(cos(angle) * 300 + windowWidth / 2, sin(angle) * 300 + windowHeight / 2);
        elements.push(elm);
    }
}

function draw() {
    for (var i = 0; i < elements.length; i++) {
        elements[i].html(frameCount);
    }
}

Synchronous vs Asynchronous Code

  • Synchronous ⟶ executes line by line. Moves to the next line when the current one is done. eg., draw() runs through its lines once per frame.
  • Asynchronous ⟶ runs only when an event happens. eg., mouseClicked() fires only when the mouse is clicked. It doesn’t check every frame.
// synchronous
function draw() {
    background(220);
    ellipse(50, 50, 20);
}

// asynchronous
function mouseClicked() {
    drawCircle();
}

Events

Some built-in events in p5js

  • mousePressed()
  • mouseOver()
  • drop()
  • dragOver()

More in the p5js reference

  • Mouse events used so far (mouseX, mousePressed) are events attached to the canvas element.
  • Events can be attached to other elements too ⟶ buttons, inputs, divs etc.

Example: Button (code in folder /9.6-ButtonEvent) ⟶ changes background color on click.

var button;
var bg;

function setup() {
    createCanvas(windowWidth, windowHeight);
    bg = color(0);
    button = createElement("button", "click me");
    button.position(20, 20);
    button.mousePressed(function () {
        bg = color(random(255), random(255), random(255));
    });
}

function draw() {
    background(bg);
}
click me ⟶ random background

Example: Text Input (code in folder /9.6-TextInput) ⟶ writes the typed text at a random position on enter.

var inputTextElement;
var inputText = "";

function setup() {
    createCanvas(windowWidth, windowHeight);
    inputTextElement = createInput();
    inputTextElement.position(20, 20);
    inputTextElement.input(handleInput);
}

function handleInput() {
    // this ⟶ refers to the input element (like this in a class)
    inputText = this.value();
}

function keyPressed() {
    if (keyCode === ENTER) {
        text(inputText, random(width), random(height));
    }
}
type, then enter
  • No draw() needed. Everything happens on events.
  • In the course, enter key is checked using key == “\r” ⟶ carriage return (from typewriters). keyCode === ENTER is the same thing.

Callback Functions

  • A function passed to another function, called later when the event happens.
  • No round brackets when passing the callback. Brackets would call it right away.
function whoop() {
    print("whoop whoop");
}

// named callback
button.mouseClicked(whoop);

// anonymous callback ⟶ function without a name
button.mouseClicked(function () {
    print("whoop whoop");
});
  • Prefer named functions for readability. Anonymous functions are fine for simple things.

Example sketches

The p5 sketches for this week, from the course repo.

9.4 — Creating Elementsrepo ↗
// No createCanvas() here. createCanvas() is also just creating a DOM element.
function setup() {
  createDiv("Hi there, I'm inside the DOM. <a href='https://p5js.org'>p5js</a>");
  createP("I'm a paragraph");
  createButton("click me");
  createImg("https://picsum.photos/200", "random image");
  createSlider(0, 255, 100);
  createElement("h1", "Hi, I'm a header1");
  createElement("h2", "Hi, I'm a header2");
  createElement("h3", "Hi, I'm a header3");
  createElement("h4", "Hi, I'm a header4");
  createElement("h5", "Hi, I'm a header5");
  createElement("h6", "Hi, I'm a header6");
}
9.4 — Injecting HTMLrepo ↗
var elements = [];
var radius = 300;

function setup() {
  noCanvas();
  for (var i = 0; i < 10; i++) {
    var elm = createElement("h1", "");
    var angle = map(i, 0, 10, 0, TWO_PI);
    elm.position(
      cos(angle) * radius + windowWidth / 2,
      sin(angle) * radius + windowHeight / 2
    );
    elements.push(elm);
  }
}

function draw() {
  for (var i = 0; i < elements.length; i++) {
    elements[i].html(frameCount);
  }
}
9.4 — Positioning Elementsrepo ↗
var header;

function setup() {
  noCanvas();
  header = createElement("h1", "Hi, I'm an H1 tag");
  frameRate(1);
}

function draw() {
  // translate() only works inside the canvas, so position() is used for DOM elements
  header.position(random(windowWidth), random(windowHeight));
}
9.6 — Button Eventrepo ↗
var button;
var bg;

function setup() {
  createCanvas(windowWidth, windowHeight);
  bg = color(0);
  button = createElement("button", "click me");
  button.position(20, 20);
  // anonymous callback function, runs only when the button is pressed
  button.mousePressed(function () {
    bg = color(random(255), random(255), random(255));
  });
}

function draw() {
  background(bg);
}
9.6 — Text Inputrepo ↗
var inputTextElement;
var inputText = "";

function setup() {
  createCanvas(windowWidth, windowHeight);
  background(220);
  inputTextElement = createInput();
  inputTextElement.position(20, 20);
  // named callback function
  inputTextElement.input(handleInput);
}

function handleInput() {
  // this ⟶ the input element
  inputText = this.value();
}

function keyPressed() {
  if (keyCode === ENTER) {
    text(inputText, random(width), random(height));
  }
}