Lab 4: JavaScript and DOM

ENSE 374 - Software Engineering Management - Laboratory

University of Regina - Engineering and Applied Science - Software Systems Engineering

Lab Instructor: Adam Tilson


This lab introduces JavaScript (JS) which is a programming language which runs in the Web Browser. In addition to being able to perform typical programming language tasks, like data storage and computation, it can additionally interact with the HTML and CSS on a web page through the Document Object Model (DOM), allowing real time interactions with the page performed on the client side. We’ll look at a small subset of the capabilities of JavaScript in this lab.

Computer running Windows, MacOS or Linux, with an Intel or AMD-based processor (x86 or x86-64) with administrator privileges.

  • A modern web browser, with a strong preference for Firefox or Chrome
  • A text editor, preferably VS Code

What is JavaScript

  • A language designed to give your browsers autonomy so they do not need to communicate with the server for trivial operations, like addition
  • Today a standard (ECMAscript) which browsers need to implement
  • Interpreted, not compiled

Hello JavaScript World

  • Open the Dev console (F12)
    alert("Hello World");
    console.log("Hello World");
    

Where to put JavaScript code?

While JavaScript can run from many places, just like CSS, external is preferable to use an external script, so that it is easy to identify the location of the code and modify it appropriately.

Inline (in a tag):

<a onclick="alert('hello');" href="#">Pop Up</a>

Internal:

<script type="text/javascript">
    alert("hello")
</script>

External:

...
<body>
...
<script src="index.js" charset="utf-8"></script>
</body>
...

Always link the external page as the last element in the <body> tag, this is to ensure necessary HTML elements are loaded before the script executes, in case we need to find HTML elements in the DOM

Learning and Mastering Javascript

White Space

  • C-style, typically optional, but recommended for readability
  • Style Guide

Comments

// line style
/* block comments */

Strive for self-documenting code through good variable and method names and logical control structures. Wrong, obsolete and obvious comments are worse than none!

Creating / Assigning Variables

Block scoped variable:

let daysThisMonth = 30;

Once constants are set they cannot be changed:

const cmPerInch = 2.54;

Globally scoped variables should be avoided where possible:

var phrase = "Pretend I don't exist";

Type is inferred from the literal, and it can change later on

  • If you want more rigorous typing, TypeScript is a JavaScript extension which imposes additional rules on the language during development, but compiles back to vanilla JavaScript.

Variable Names

  • letter followed by letters, digits, underscores.
    • no spaces (as in all programming languages!)
  • no reserved words allowed!
  • no dashes, as they represent minus (which will clash with CSS naming conventions in some contexts)
  • camelCase is preferred

Primitives

  • numbers, strings, booleans
  • null - intentional absence of a value, assigned by you.
  • undefined - unintentional absence of a value. Returned by JS if you forgot to assign something

Numbers

All numbers are 64-bit floats (double).

  • 1e2 means 100
  • -37
  • NaN means Not a Number, e.g. a 0/0 error.
  • Infinity exists if the magnitude of a number is huge (>310 0’s).
    • Also returned by div/0 errors.
    • Signed.
  • -0 is legal.
  • There are methods for numbers, e.g. Math.floor(number)
  • Math object defines common methods, e.g. Math.pow(...), Math.abs(...), Math.floor(...), Math.random(...)
  • +, -, +=, -=, ++, -- all work as expected

Number type built in functions:

number.toExponential()
number.toFixed(totalDigits)
number.toPrecision(decimalPoints)
number.toString(base)

Strings

  • Single ' or double " quotes
    • Double is preferred most of the time
  • Zero or more characters
  • \ used as an escape character: \\, \', \", \n, and others
  • No char type, only length one strings
  • length property.
    >> "word".length
    4
    
  • Strings are immutable - once made they can’t be changed directly
  • + for concatenation
  • Strings have methods, e.g. toUpperCase()
>> "word".toUpperCase()
"WORD"
>> "WORD".toLowerCase()
"word"
  • If your strings needs to include one type of quotes as a character in the string, you can use the other to delimit the string, e.g. "He said 'hi' " or 'They said "see ya later!" '
    • This can be more convenient than escaping
  • Positions are indexed, like in arrays (but are read only!)

Template Literals

  • strings which allow inline insertion of variables or other code
  • strings declared surrounded by the backtick character: e.g. :`
    `I counted ${ 3 + 5 } apples` 
    
    • Any JS in the ${ }`’s is evaluated.
    • If you want the ‘$’ in the string, do ‘$$’.

parseInt and parseFloat extracts the leading number from a string until it something non numeric.

slice lets you extract substrings

>> let name = "Robot"
>> name.slice(1,4)
`obo`

Some more useful built-in functions…

string.charAt(pos)
string.charCodeAt(pos)
string.concat(string...)
string.indexOf(searchString, pos)
string.lastIndxOf(searchString, pos)
string.match(regexp)
string.replace(searchValue, replaceValue)
string.search(regexp)
string.slice(start,end)   - like substr
string.split(separator, limit)
string.substring(start, end)
string.toLowerCase()
string.toUpperCase()
string.trim()
String.fromCharCode(char...)

Can cascade methods, e.g. string.trim().toUpperCase()

Booleans

  • Values that represent False: false, null, undefined, "", 0, NaN
    • Everything else represents true
  • Testing equality: always use === (also tests type)
  • ! toggles truthiness

For an explanation on why to always use === over ==, see:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

Infix Operators

By precedence:

  • *, /, % (modulo), ** (power).
  • +, -
  • >=, <=, >, <
  • ===, !==
  • ||, &&
  • override precedence with ( )’s

*Technically == exists in JavaScript, you should pretend it doesn’t.

Typeof

  • A prefix operator, not a function:
    typeof x
    
  • returns the type of a variable:
    • number
    • string
    • boolean
    • undefined
    • function
    • object
  • arrays and null types map to objects

Statements

Types:

  • expression
  • disruptive (break, return, throw)
  • condition (if, try, switch)
  • loop (for, do, while)

Mostly work as expected in c++, exceptions:

  • e.g. switch “cases” do not need to be const’s.

If you like syntax diagrams, JavaScript the Good Parts will show you what you need.

e.g., here’s one on whitespace

Source: JavaScript The Good Parts. Douglas Crockford. 2008.

Conditions

In JavaScript, if...else if...else and switch-case exist:

Example from W3Schools JS If Else ElseIf

if ( time < 10 ) {
  greeting = "Good morning";
} else if ( time < 20 ) {
  greeting = "Good day";
} else {
  greeting = "Good evening";
}

Example from W3Schools JS Switch

switch (new Date().getDay()) {
  case 1:
    day = "Monday";
    break;
  case 2:
     day = "Tuesday";
    break;
  case 3:
    day = "Wednesday";
    break;
  case 4:
    day = "Thursday";
    break;
  case 5:
    day = "Friday";
    break;
  default:
    day = "Weekend";
}
  • Cases can be any type
  • Fall-through will occur until a break is encountered

Loops

There are several types of for loops:

Examples from W3Schools JS Loop For

for (let i = 0; i < 5; i++) {
  text += "The number is " + i + "<br>";
}

for...in loops through properties of an object:

Examples from W3Schools JS Loop For In

const person = {fname:"John", lname:"Doe", age:25};

let text = "";
for (let x in person) {
  text += person[x];
} 

for...of loops through iterables data structures:

Example from W3Schools JS Loop For Of

const cars = ["BMW", "Volvo", "Mini"];

let text = "";
for (let x of cars) {
  text += x;
}

There are also while loops:

Example from W3Schools Loop While

while (i < 10) {
  text += "The number is " + i;
  i++;
}

And do...while loops:

Example from W3Schools Loop While

do {
  text += "The number is " + i;
  i++;
}
while (i < 10); 

Try Throw Catches

An additional control structure for running risky code and catching errors is the try-throw-catch

Example from W3School JS Errors

function myFunction() {
  const message = document.getElementById("p01");
  message.innerHTML = "";
  let x = document.getElementById("demo").value;
  try {
    // block for risky code to try
    if(x == "") throw "is empty";
    if(isNaN(x)) throw "is not a number";
    x = Number(x);
    if(x > 10) throw "is too high";
    if(x < 5) throw "is too low";
  }
  catch(err) {
    // runs if an error occurs
    message.innerHTML = "Error: " + err + ".";
  }
  finally {
    // always runs regardless of try/catch result
    document.getElementById("demo").value = "";
  }
} 

Objects

  • Similar to C++ classes
  • Collections of key-value pairs.
  • Can be nested
  • Ad-hoc or prototyped
  • No guarantee they will have all of the expected properties - test first!
  • First we’ll look at ad-hoc objects - these are both declared and instantiated on the fly

Creating Objects

// string style keys
var personString = {
    "first-name" : "Adam",
    "last-name" : "Tilson"
}

// name style keys
var personName = {
    firstName : "Adam",
    lastName : "Tilson"
}

Reading Objects

//string style lookup
personString["first-name"];
//name style lookup
personName.firstName;
  • If a value doesn’t exist, it returns undefined
  • Can use || to fill in a default, eg. personName.firstName || "Adam"
  • If you need to retrieve from a nested obj, make sure it exists first:
    • adam.schedule && adam.schedule.morning
  • if you want to see all of the contents of an object in the console use: console.dir(myObject)

Updating Objects

  • Use the assignment operator,
personString["first-name"] = "A";
personName.firstName = "A";

Objects are passed by reference, not copied

Deleting Objects

delete personString["first-name"];

Prototyping

You can link an object to a prototype to inherit properties. This is like in C++ where you must first define a class before you can create an instance of it. This allows you to use a constructor:

function Person(first, last, age, eyeColor) {
    this.firstName = first;
    this.lastName = last;
    this.age = age;
    this.eyeColor = eyeColor;
}

var myFather = new Person("John", "Doe", 50, "blue");
var myMother = new Person("Sally", "Rally", 48, "green");

We can modify the prototype later by adding a new prototype with a default value:

Person.prototype.nationality = "English";

Add a function later

Person.prototype.name = function() {
    return this.firstName + " " + this.lastName;
};

Rule: you can modify your own prototypes, but probably shouldn’t modify others’!

Arrays

Arrays are simply objects that allow using ints for the names, i.e. the index.

  • They also have some shortcut operators and built in methods to make them operate like arrays do in other languages.
let x = [1, 2, 3];
let x = []
let x = ["one", "two", "three"] - associative arrays
  • length property
>> x.length;
3

Two ways to append a value to the end of the list:

x[x.length] = 6   
x.push(6)
  • Deleting values leaves holes in the array
    • Rather than deleting, you can splice instead e.g. start at 2 element, delete 1, and splice the two sub arrays together:
x.splice(2,1)
  • No 2d support out of the box, but you can make an array of arrays.

Array class methods:

array.concat (item, ...)
array.join (separator) - make a string from an array
array.pop()
array.push(item, ...)
array.reverse()
array.shift() - like pop but for the first element
array.slice(start, end) - copy a portion of an array
array.sort(compareFunction)
array.splice(start, count)
array.unshift(item, ...) - push front

A simple addition function:

function add (x, y) {
    return x + y;
}

Note: function keyword, and no explicitly defined argument or return types

Variable Passing

Primitive types are passed by Value. This means modifying the variable inside the function will not change it outside the function.

Objects are passed by Reference - this means that modifying an object inside the function will modify the original.

Anonymous Functions

Functions can be created anonymously as expressions.

In this case we can assign these to variables, however, we’ll see later how we can also declare these functions directly from their parameter list of other functions as callbacks.

let add = function (x, y) {
    return x + y;
}

Or using the popular arrow syntax:

let add = (x, y) => {
    return x + y;
}

or even as a one-liner, but this is a bit difficult…

let add = (x, y) => x + y;

Arrow functions have a few syntax options

In JavaScript functions are objects, so they can be stored in variables or passed as parameters!

Calling Functions

add (3, 4)

The implicit arguments array contains all of the arguments, even unnamed ones. e.g.:

var sum = function () {
    var i, sum = 0;
    for ( i = 0; i < arguments.length; i += 1 ) {
        sum += arguments[i];
    }
    return sum;
};

Scope

When we use the var keyword for variables, we have function scope, not block scope. If a function does know what a variable is, it will do a search up through blocks to try to find it. You will likely need to read through this a bunch of times!

var outer = function () {
    var a = 3, b = 5;
    console.log ("(1) At this point, a is 3, b is 5, and c is not defined")
    console.log(`a = ${a}`);
    console.log(`b = ${b}`);
    console.log(`c is not defined`);
    var inner = function () {
        var b = 7, c = 11;

        console.log ("(2) At this point, a is 3, b is 7, and c is 11")
        console.log(`a = ${a}`);
        console.log(`b = ${b}`);
        console.log(`c = ${c}`);

        a += b + c;

        console.log ("(3) At this point, a is 21, b is 7, and c is 11")
        console.log(`a = ${a}`);
        console.log(`b = ${b}`);
        console.log(`c = ${c}`);
    };
    inner ();

    console.log ("(4) At this point, a is 21, b is 5, and c is not defined")
    console.log(`a = ${a}`);
    console.log(`b = ${b}`);
    console.log(`c is not defined`);

};

outer();

A scope search happens where JS looks for the variable in local scope first, if not it will fall back to parent block scope, e.g. in nested functions, then grandparent, etc.

This is very difficult to trace, and rarely matches the programmer’s intention. Instead we should use let!

High order functions and callbacks

A high order function is a function which takes a function as an argument, or returns a function:

Or a function expression:

var hello = function () {
    console.log("Hello");
}

A simple example: call twice:

function callTwice(func) {
    func()
    func()
}

When we pass a function in here

  • don’t include the ()’s,
  • that is, we want to pass a reference to function
  • we don’t want to run the function and pass in its return value

We can also have a function which returns a function. This is like a function factory:

function getAFunction () {
    return function () {
        console.log("Hello")
    }
}

Here’s how we can use that factory:

function makeBetweenFunc (x, y) {
    return function(num) {
        return num >= x && num <= y;
    }
}
isItNiceOutside = makeBetweenFunc(20, 30);

We sometimes need to invoke anonymous functions:

The following JavaScript built in function calls a function passed as an argument after 5000 ms:

setTimeout (func, 5000)

We can also define the callback function directly in the argument list:

setTimeout (function() {
    alert("Welcome")
}, 5000)

Callbacks are extremely useful, and we’ll see them a lot when working with both UI operations and Node.js in a few weeks.

DOM Overview

The document object model (DOM) catalogs the elements of a webpage into JavaScript objects which you can manipulate, namely HTML and CSS.

  • We can also listen to user interactions (Events) and respond to them.
  • All of the HTML Attributes exist in the JS object, not only the ones we manually define through HTML Code.
    • You can read it with dir

Single Responsibility

We will learn how to change individual CSS values in JS.

  • Should we? No!
  • Recall, all of our styles should be defined in CSS style sheets, e.g. using classes
  • Instead, you could attach or detach classes in JS, which will update the styles

What you need to know for this lab?

You don’t need to memorize all of the syntax (We’ll use JQuery next week which simplifies the syntax)

Instead, spend more time learning what you can do, more than specifically how you do it.

Waiting for user interactions

We can attach event listeners, which wait for a user to do an action, and then call a function when a user does that action

btnObj.addEventListener("mouseover", function () {
    alert("hi");
});

The Root of the DOM: The Document object

You can learn more about the document object using the:

console.dir(document)

You can also do this with individual elements.

Selecting and element from the DOM

Some commands for selecting elements from the DOM are:

let element = document.getElementById("hero-image") //(returns an HTMLElement)
let elements = document.getElementsByTagName("h1") //(returns HTMLCollection, like an Array)
let elements = document.getElementsByClassName("header") //(Also returns HTMLCollection)
  • The returned elements is a JavaScript object representing a chunk of HTML.
  • If you store it in a variable, e.g. const img.
    • Then you will have an object you can use. Using the CRUD principles we discussed, you can manipulate it!
  • HTML objects have prototypes, eg. HTMLImageElement

  • If you call any of these getElementBy…() functions on an HTML element, the returned objects will be from the HTML element’s children, rather than the documents children.

HTMLCollections

You can use array access [] and .length, that’s likely all you’ll need.

let inputs = document.getElementsByTagName("input")
for (let input of inputs) { 
    console.log(input.value);
};

querySelector

  • The newer, best selector by far
  • Pass in a CSS-style selector, using the same syntax as in CSS
  • querySelector returns 1, querySelectorAll returns all.
const img = document.querySelector("#my-image");
  • can even use the advanced CSS Selection rules like ul li.second for the li with class second that is a child of a ul

querySelectorAll

returns a nodelist.

  • Like an HTML collection, but slightly different.
  • has a .length, array access, [], and forEach.

Exercise: See if you can figure out how many of the smaller titles are on this page using javascript

Most important DOM properties and methods:

element.classList
element.getAttribute()
element.setAttribute()
element.appendChild()
element.append()
element.prepend()
element.removeChild()
element.remove()
element.createElement
element.innerText
element.textContent
element.innerHTML
element.value
element.parentElement
element.children
element.nextElementSibling
element.previousElementSibling
element.style

innerText and textContent

  • innerText is the text that’s in the tags. <p>This is the The inner Text</p>.
  • If there are multiple things inside with text, get all the text.
  • You can even call it on the body
  • If you change it, it will drop all children, and overwrite it with just text

  • textContent includes internal spacing, stuff that isn’t rendered on the page but is part of the document.
    • Less likely to use this one.

innerHTML

  • similar to innerText, but returns all of the contained content as HTML as a string.
    • You can read and modify it as needed.
    • However, you need to add things as string
      • Generally DOM manipulation is preferred
  • innerHTML parses tags, vs. innerText would not parse them as tags.

Exercise: See if you can change the following title to Changing Attributes

Element Attributes such as value, src and href

We can access attributes as properties of the object e.g. .value, .checked, .placeholder Many of these can be easily accessed. However, not all!

Getters and Setters for less accessible attributes

getAttribute, setAttribute

e.g. getAttribute('max') e.g. setAttribute('max', 1000)

you can even change type attribute, e.g. for form elements

document.querySelector("a").attributes;
document.querySelector("a").getAttribute("href");
document.querySelector("a").setAttribute("href", "https://www.w3schools.com");

Traversing the DOM

If you’ve selected an element but want the next one, or child inside, what do you do? parentElement property gets the parent (Only one) children - returns an HTMLCollection element. (Many) nextElementSibling and previousElementSibling get the next or previous sibling, respectively.

Creating Elements

let h1 = document.createElement('h1')
  • From here you can add whatever you want, classes, attributes, etc.
h1.innerText = "A new heading!"
  • Then you just need to add it to the DOM somewhere
  • separately find the parent you would like to attach it to…
let parent = document.findElementById("my-div");
  • parent.appendChild(h1), appends it as the last child of the element
  • parent.prependChild(h1), prepends it as the first child of the element
  • insertBerfore(newNode, existingNode) allows insertion at a specific point

precise positioning with insertAdjacentElement

let div = document.createElement("div");
let span = document.createElement("span");
div.appendChild(span)
let newSpan = document.createElement("span");
div.insertBefore(newSpan, span);

Removing

remove, removeChild

self.remove () or parent.removeChild(element)

Advanced: Creating elements from templates with template literals

Sometimes you need to create more advanced objects which have a greater amount of elements. As an example, here is a Bootstrap Accordion of 2 items:

let accordText1 = "hello";
let accordText2 = "goodbye";
let insertionPoint = document.querySelector("#insertion-point");
const template = document.createElement('template');
template.innerHTML = `
<div class="accordion" id="accordionExample">
<div class="accordion-item">
    <h2 class="accordion-header" id="headingOne">
    <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
        Accordion Item #1
    </button>
    </h2>
    <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
    <div class="accordion-body">${accordText1}</div>
    </div>
</div>
<div class="accordion-item">
    <h2 class="accordion-header" id="headingTwo">
    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
        Accordion Item #2
    </button>
    </h2>
    <div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
    <div class="accordion-body">${accordText2}</div>
    </div>
</div>
</div>`;
const accordion = template.content.firstElementChild;
insertionPoint.append(accordion);

We can even search inside our element and further modify things:

accordion.querySelectorAll(".accordion-body")[0].innerHTML = "Updating first accordion live!"

Changing styles

Changing styles from the DOM is legal, but weird and generally no preferred

  • use the style property
  • conceptually “write-only” because it only reads inline styles
    • this is bad practice.
  • Because “-“ is not allowed in JavaScript identifiers, we switch from css-style kebab-case to JavaScript style camelCase.

e.g. font-family becomes fontFamily

All of the properties must be assigned as strings, eg. “30px”, “30%”, “3rem”

Finding the Computed Style

getComputedStyle(element)
  • returns an object-like structure with key-value pairs for all the CSS properties.
  • Many value will be default.

Changing class with classList

A better way to change styles, particularly if there are many changes is to just define different classes in your external CSS which are not linked to any objects, and then use JavaScript to attach those classes.

classList returns a DOMTokenList object in which you can add elements easy enough

  • functions:
classList.remove('classname')
classList.add ('classname')
classList.toggle('classname')
  • With toggle, if the class is not there, add it. If it is there, remove it.

Events

A small set of events we can manage:

  • clicks
  • drags
  • drops
  • hovers
  • scrolls
  • form fill
  • form submit
  • focus or blur
  • key presses
  • double click
  • screen resize
  • audio
  • animation

Check out the MDN event reference!

Events link an element, a user interaction, and a response (function)

Adding Events

Bad ways to register that you will likely see in examples, including mine:

  • in the html tag
<a href="#" onclick="alert('hi')">Click Me</a>
  • also not recommended, in JavaScript, simply as this way is older
let button = document.getElementById("my-button");
button.onclick = function () { alert("hi") }

The best way to register:

let button = document.getElementById("my-button");
button.addEventListener("click", function () { alert("hi") } );

The last method is universal, consistent between different types of events, and also uses callback syntax for declaring the function.

Event objects

Invoking an event sometimes creates an object which has more info about that event, eg. your mouse location, screen location, modifiers, etc. This is passed as the first arg of the callback, typically given the variable “e”

keydown, keyup, keypress

keydown and keyup fire on rising or falling edges for any key on the keyboard.

  • keypress only fires once per rising edge and falling edge
  • Both keydown and keypress are subject to keyboard repeat
  • If you want to process an enter in a textbox, do a keypress event, look inside the event e to see if enter was pressed, and if so, then execute code
  • If you want to only register one key press, avoiding keyboard repetition, set a flag variable on the keydown even, clear it on the keyup event, and use the flag variable to update your code.
  • Example from MDN Keypress Event
const log = document.getElementById('log');

document.addEventListener('keypress', function (e) {
    log.textContent += ` ${e.code}`;
});

One useful property of e is target, which corresponds with the DOM object that was interacted with.

Bubbling

When a click input is captured, it registers on the targeted element, then bubbles up through all the parent elements.

Example from JavaScript.info Bubbling and Capturing

<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

If you need to, you can use event.stopPropagation() to halt the bubbling process prematurely. Just be careful this doesn’t break other expected functionality - in other words, bubbling in JavaScript / DOM is an intended feature, not a bug, so you should have a good reason for disabling it!

The bind function and the this keyword:

Navigating bubbling and events can be a hassle, particularly if you are using to try to find an object, e.g. the one that was clicked on. An alternative approach is to pass a reference to a found object using the bind function, and then access that object in the function using the this keyword. Be warned, this syntax is a bit weird!

For example, given the following:

<p id="par">Click the button</p>
<button id="but">Click me!</button>

We can access elements as follows:

par = document.getElementById("par")
but = document.getElementById("but")
but.addEventListener("click", function () { this.innerHTML = "Button clicked" }.bind(par) );

This may be particularly useful when dealing with components and objects created with loops!

Exercise:

Create a page with a title and some text. Have a button that swaps between a dark theme and a light theme by attaching or removing pre-created CSS classes.

We’re going to have two parts for today’s lab. The first part takes a break from our main project for now - we’ll get back to that in the next lab. This exercise is purely to understand Front End JavaScript and the DOM.

Part 1: Burn after Reading

For the first part of this lab, we’re going to implement a simple application where users can add sticky notes to a page, and delete them once they are done.

The purpose is to get you used to finding and modifying DOM elements with JavaScript.

In this application, you will create a simple form for adding sticky notes, using a bootstrap card component:

When you press post, this will add a new sticky note to the app:

Pressing the “burn” button on each note will cause the note to be removed from the application:

Extra: Make the notes rotate between different colors. Use the following colors:

  • #ff7eb9
  • #ff65a3
  • #7afcff
  • #feff9c
  • #fff740

Thanks to color-hex

When you delete notes, make sure the the colors of the notes do not change. Position can change.

Hint: There are two approaches you could use:

  • An ad-hoc method, where you use the post form to add elements directly to the DOM using JavaScript, and then remove them in a similar fashion with the Burn Button.

  • An MVC approach where you store all of the data in a separate list of objects (model).
    • When you post an item, add it to the model.
      • Then, on the view side
        • clear any elements that exist.
        • loop through the model,
        • and add each to the view separately.
    • When you delete an object, remove it from the model,
      • then as before, on the view side:
        • clear the view,
        • loop through the model,
        • and add each element back to the view.
  • This approach may seem more difficult and time consuming, but will actually make your life much easier!

For this lab, I am not going to force the MVC approach, but I will in the next lab!

Part 2: Note-Vote Model as JavaScript Objects

Recall in an MVC, the purpose is to separate the data for our application (model) from the presentation (view).

In the second part of the lab, you are to create a JavaScript Object to represent the model for the first prototype version of our Note-Vote app. This type of syntax is called JavaScript Object Notation (JSON), and when saved in a separate file, is a popular way to store database-like data.

This object should include:

  • A unique identifier for the note, (numerical)
  • The note creator, (text)
  • The note’s text, (text)
  • A list of usernames who upvoted the note, (list of text)
  • A list of usernames who downvoted the note, (list of text)
  • Anything else you think would be needed

Create 3 sample notes according to this model. Note that we will NOT display this in the view in this lab, it will only appear in Lab 5.

Submission

Your progress will be checked by a demo next week! Happy scripting.