JavaScript - Data & the DOM
SAP Implementation Consulting & ERP - Startupistan Germany · Block I · study notes for revision.
Up to now my variables held one thing at a time - a name, a price, a yes/no. This chapter is about holding many things and structured things: lists (arrays), described things (objects), and blueprints for making objects (classes). Then it puts them on a real web page through the DOM. The through-line of the whole week: content as data + one repeatable rule beats hand-copying anything.
Arrays - an ordered list in one variable
Section titled “Arrays - an ordered list in one variable”An array is an ordered list of values stored under one name. I write it with square brackets (an array literal) and read items back by their index - a position number that starts at 0, not 1.
'use strict';
const flavors = ["rye", "spelt", "wheat"];
console.log(flavors[0]); // rye - first itemconsole.log(flavors[2]); // wheat - last of threeconsole.log(flavors.length); // 3 - how many itemsflavors[1] = "sourdough"; // overwrite the second itemryewheat3Array methods - mutating vs non-mutating
Section titled “Array methods - mutating vs non-mutating”The single most important distinction: some methods change the array in place (mutate it), others leave it alone and return a new value. Getting these mixed up is where surprises come from.
| Method | Does what | Mutates? |
|---|---|---|
push() / pop() | Add / remove at the end | Yes |
unshift() / shift() | Add / remove at the front | Yes |
includes() / indexOf() | Is it present? / at which index? | No |
slice(start, end) | Copy a piece into a new array (end excluded) | No |
concat() / join(sep) | Join arrays / glue items into one string | No |
const flavors = ["rye", "spelt", "wheat"];
flavors.push("olive"); // now 4 items, changed in placeconsole.log(flavors.pop()); // olive - removed & returnedconsole.log(flavors.includes("rye")); // trueconsole.log(flavors.indexOf("wheat")); // 2console.log(flavors.slice(0, 2)); // [ 'rye', 'spelt' ] - a NEW arrayconsole.log(flavors.join(" and ")); // rye and spelt and wheatLoops - run the same block many times
Section titled “Loops - run the same block many times”A loop runs a block of code repeatedly. This is how I honour DRY (“Don’t Repeat Yourself”) over data instead of writing near-identical lines by hand.
Visits every item directly - no index bookkeeping. Read it as “for each flavor of the flavors”.
const flavors = ["rye", "spelt", "wheat"];
for (const flavor of flavors) { console.log(flavor);}// rye// spelt// wheatPacks three parts into one header: start, condition (checked before each pass), afterthought (runs after each pass). Reach for it when I need the index i itself.
const flavors = ["rye", "spelt", "wheat"];
for (let i = 0; i < flavors.length; i++) { console.log(`${i}: ${flavors[i]}`);}// 0: rye// 1: spelt// 2: wheatRepeats while its condition stays true. Something inside must move the condition toward false, or it loops forever.
let countdown = 3;
while (countdown > 0) { console.log(countdown); countdown--; // moves toward stopping}// 3// 2// 1Building output from data with one loop
Section titled “Building output from data with one loop”Here is the payoff - the same rule applied to every item, no copy-paste:
const artists = ["Pinkfong", "Asake", "Johnny Cash"];
for (const artist of artists) { console.log(`=== ${artist} ===`); console.log(`Listen to ${artist} now on every platform`);}=== Pinkfong ===Listen to Pinkfong now on every platform=== Asake ===Listen to Asake now on every platform=== Johnny Cash ===Listen to Johnny Cash now on every platformAdd a fourth artist to the array and a fourth card appears for free. Fix the sentence once and every card is fixed. That is DRY in action.
Callback array methods
Section titled “Callback array methods”These methods take a callback - a function I hand over, which the method runs once per item. The arrow form ((x) => ...) keeps each one to a readable one-liner. Pin this table above the desk:
| Method | Returns | Use it to… |
|---|---|---|
forEach | undefined | do something with each item (side effect, e.g. print) |
map | a new array, same length | transform each item into something new |
filter | a new array, usually shorter | keep only the items that pass a test |
reduce | one single value | boil the whole array down to one result |
find | one item (or undefined) | get the first item that passes a test |
some / every | a boolean | does any / do all items pass a test? |
const prices = [4.5, 12, 3.2, 8];
prices.forEach((p) => console.log(p)); // prints each; returns nothingconst withTax = prices.map((p) => (p * 1.07).toFixed(2)); // ['4.82','12.84','3.42','8.56']const cheap = prices.filter((p) => p < 5); // [ 4.5, 3.2 ]const total = prices.reduce((sum, p) => sum + p, 0); // 27.7 (0 = starting value)const firstBig = prices.find((p) => p > 10); // 12const anyBig = prices.some((p) => p > 10); // trueconst allBig = prices.every((p) => p > 10); // falseCopies vs shared references
Section titled “Copies vs shared references”Arrays and objects are reference types. A variable doesn’t hold the list itself - it holds an arrow (a reference) pointing at the one list in memory. So plain assignment copies the arrow, not the list:
const original = ["rye", "spelt"];const copy = original; // same arrow → same list!
copy.push("wheat");console.log(original); // [ 'rye', 'spelt', 'wheat' ] ← the "original" changed tooTwo names, one shared list - write through either and both see it. The deliberate way to make a genuinely new array is spread syntax - three dots inside a new literal:
const original = ["rye", "spelt"];const realCopy = [...original]; // brand-new array with the same items
realCopy.push("olive");console.log(realCopy.length); // 3console.log(original.length); // 2 ← untouchedObjects - a described thing
Section titled “Objects - a described thing”An array answers “which items, in what order?”. An object answers “what do I know about this one thing?”. It’s a collection of named values written with braces (an object literal). Each pair is a property: the key on the left, the value on the right (any type - string, number, boolean, even another array or object).
const croissant = { name: "Butter Croissant", price: 2.8, vegetarian: true,};
console.log(croissant.name); // Butter Croissant - dot notationconst field = "price";console.log(croissant[field]); // 2.8 - bracket notation, key from a variablecroissant.price = 3.0; // change a propertyWorking rule: dots when I can, brackets when the key lives in a variable (or contains characters a plain name can’t). Reading a key that doesn’t exist quietly returns undefined - a misspelled key is the usual culprit.
Methods and this
Section titled “Methods and this”A method is just a property whose value is a function. Inside a method, the keyword this refers to the object the method was called on - that’s how a method reads its own sibling properties.
const croissant = { name: "Butter Croissant", price: 2.8, describe: function () { return `${this.name} costs ${this.price} euros`; },};
console.log(croissant.describe()); // Butter Croissant costs 2.8 eurosArrays of objects
Section titled “Arrays of objects”Put the two together and you get the shape of most real data: a list of things, each with named details. Every array tool works unchanged; inside the loop each item answers to dot notation.
const artists = [ { name: "Pinkfong", genre: "Children's music", total: "11:31" }, { name: "Adriano Celentano", genre: "Italian pop", total: "20:52" }, { name: "Asake", genre: "Afrobeats", total: "14:08" },];
for (const artist of artists) { console.log(`${artist.name}, ${artist.genre}, ${artist.total} of music`);}
const popActs = artists.filter((a) => a.genre.includes("pop"));console.log(popActs.length); // 1Iterating an object’s properties
Section titled “Iterating an object’s properties”An object has no items to loop over - it has properties. Three built-ins turn its contents into arrays, and then every array tool applies:
| Helper | Returns |
|---|---|
Object.keys(obj) | array of the keys (strings) |
Object.values(obj) | array of the values |
Object.entries(obj) | array of [key, value] pairs |
const hours = { saturday: "7-14", sunday: "8-12", monday: "closed" };
for (const [day, time] of Object.entries(hours)) { console.log(`${day}: ${time}`);}// saturday: 7-14// sunday: 8-12// monday: closedThere’s also for...in, which visits an object’s keys directly (for (const day in hours)). It works and shows up in other people’s code, but I prefer the Object.keys / entries route so I stay in array-land with one mental model.
Destructuring
Section titled “Destructuring”Destructuring pulls properties (or array items) into their own variables in one line. Objects match by key name; arrays match by position.
const croissant = { name: "Butter Croissant", price: 2.8, vegetarian: true };const { name, price } = croissant; // by key nameconsole.log(`${name}, ${price} euros`); // Butter Croissant, 2.8 euros
const flavors = ["rye", "spelt", "wheat"];const [first, second] = flavors; // by positionconsole.log(first, second); // rye speltThat [day, time] in the entries loop above was array destructuring on each pair. Destructuring is everywhere in modern frameworks - worth reading fluently now. (Watch the case: const { Name } finds nothing and hands back undefined.)
OOP - classes as blueprints
Section titled “OOP - classes as blueprints”When every artist object carries the same keys and the same describe method, copying that structure by hand gets silly. Object-oriented programming (OOP) solves it: describe the shape once as a class (the blueprint), then manufacture as many instances (concrete objects) as the data needs.
The compact example shows the whole toolkit - class, constructor, new, a method, plus extends and super for inheritance:
'use strict';
class Artist { constructor(name, genre, total) { // runs once per new instance this.name = name; // install data onto THIS instance this.genre = genre; this.total = total; } describe() { // class method - shorthand, no `function` return `${this.name}, ${this.genre}, ${this.total} of music`; }}
class FeaturedArtist extends Artist { // subclass inherits everything from Artist constructor(name, genre, total, blurb) { super(name, genre, total); // run the parent constructor FIRST this.blurb = blurb; // then add my own property } describe() { // override - build on the parent version return `${super.describe()}. Featured: ${this.blurb}`; }}
const cash = new Artist("Johnny Cash", "Country", "15:40");const asake = new FeaturedArtist("Asake", "Afrobeats", "14:08", "Loudest new voice out of Lagos");
console.log(cash.describe());console.log(asake.describe());Johnny Cash, Country, 15:40 of musicAsake, Afrobeats, 14:08 of music. Featured: Loudest new voice out of LagosThe DOM - putting data on the page
Section titled “The DOM - putting data on the page”When the browser reads an HTML file it doesn’t keep the text - it builds the DOM (Document Object Model): a live tree of node objects in memory, one per element, nested exactly as the HTML nests. The rendered page is drawn from this tree, so the one sentence everything rests on: change the tree, and the page changes on screen, instantly.
First, connect the script to the page. This line goes in the <head>; defer tells the browser to run the file only after the whole document is parsed, so my code doesn’t reach for elements that don’t exist yet:
<script src="app.js" defer></script>Selecting and changing elements
Section titled “Selecting and changing elements”document is the tree’s entry point. querySelector(cssSelector) returns the first element matching a CSS selector (same selectors as CSS - .class, #id, tag); querySelectorAll returns all matches as a list I can loop.
| Task | How |
|---|---|
| Select first match | document.querySelector(".tagline") |
| Select all matches | document.querySelectorAll(".card") |
| Read / change text | el.textContent = "New text" |
| Add / remove a class | el.classList.add("highlight") / .remove("highlight") |
| Change inline style | el.style.color = "crimson" |
'use strict';
const heading = document.querySelector("h1");heading.textContent = "Startupistan Records"; // tree changes → screen changesheading.classList.add("highlight"); // CSS defines the look; JS decides whenThe division of labour: CSS owns appearance, JavaScript decides when an element wears a class. A selector that matches nothing returns null quietly - remember the dot for classes and the hash for ids.
Creating elements from data
Section titled “Creating elements from data”Reading the page is half the power; the other half is manufacturing content that was never in the HTML. Two tools: createElement(tag) builds a new detached element (it exists only in memory), and append() mounts it inside a parent.
const artists = [ { name: "Pinkfong", genre: "Children's music", total: "11:31" }, { name: "Asake", genre: "Afrobeats", total: "14:08" },];
const cardArea = document.querySelector(".cards");
for (const artist of artists) { const card = document.createElement("article"); // build the card const title = document.createElement("h3"); title.textContent = artist.name; // fill from the data const line = document.createElement("p"); line.textContent = `${artist.genre}, ${artist.total} of music`; card.append(title, line); // assemble the pieces cardArea.append(card); // mount into the tree}Read the loop body as an assembly line: build → fill from data → assemble → mount. Add a third artist to the array, reload, and a third card appears. This data-to-elements pattern is the beating heart of every framework I’ll meet later.
Events and forms - a worked example
Section titled “Events and forms - a worked example”Everything so far runs once at load. addEventListener(eventName, callback) registers a function to run every time a named event happens. Hand the callback over unrun (no parentheses) - writing () runs it once at load and never again.
For forms, two more pieces: an input’s value property holds whatever the visitor typed, and the submit event fires when they send the form. A submitting form navigates away by default, so I call event.preventDefault() on the event object to stop that and handle it in place.
-
Grab the elements once, up top.
-
Listen for
submitand stop the default navigation withpreventDefault(). -
Read
input.value, guard against empty input (an empty string is falsy), then act on it.
'use strict';
const form = document.querySelector(".signup");const nameInput = document.querySelector("#artist-name");const list = document.querySelector(".cards");
form.addEventListener("submit", (event) => { // callback handed over UNRUN event.preventDefault(); // keep the page put const name = nameInput.value.trim(); // read what was typed if (name) { // reject an empty submission const card = document.createElement("article"); card.textContent = name; list.append(card); // add the new card live nameInput.value = ""; // clear the field for the next one }});Revision summary
Section titled “Revision summary”| Must-know | One-line recall |
|---|---|
| Array | Ordered list in one variable; index starts at 0, last is length - 1 |
| Mutating vs not | push/pop/shift/unshift change in place; slice/map/filter/spread return new |
| Loops | for...of = every item (default); for = need the index; while = condition-driven |
| DRY | Content as data + one loop replaces every hand-copied line |
| forEach vs map | forEach returns undefined (acts); map returns a new array (transforms) |
| filter / find / reduce | keep many / get first match / boil down to one value |
| Reference types | Assigning an array/object copies the arrow, not the thing - use spread [...x] / {...x} to copy |
| Shallow copy | Spread copies the top level only; nested references stay shared (structuredClone for deep) |
| Object | Named values (key: value); . when key is literal, [var] when key is in a variable |
| Method & this | Method = property holding a function; this = the object before the dot (not an arrow) |
| Keys/values/entries | Turn an object into arrays; loop entries with a [key, value] destructure |
| Destructuring | { name } = obj by key name; [a, b] = arr by position |
| Class / new | Blueprint + new builds an instance; constructor installs data via this |
| extends / super | extends inherits; super(...) calls parent constructor; super.m() calls parent method |
| DOM | Live tree of node objects; change the tree → page updates instantly |
| defer | Runs the script after the document is parsed, so selections aren’t null |
| Select / change | querySelector (first) / querySelectorAll (all); textContent, classList, style |
| Create / append | createElement builds detached; append mounts it - forget append = silent nothing |
| Events | addEventListener("click", cb) runs cb each time; hand the callback over unrun |
| Forms | Read input.value; event.preventDefault() stops the submit navigating away |