The Document Object Model

Use JavaScript to change a web page while it is open. Learn how the browser turns HTML into a tree of objects, select the elements you need, and change their text, attributes, classes and styles. Create, move and remove elements, and turn arrays of data into lists and tables that stay in step with the data, all without reloading the page.

BeginnerLesson 5 of 9 in JavaScript Programming Course

What you will learn

  1. Explain what the Document Object Model is, how the browser builds it from HTML, and how its nodes relate to each other so you can find your way around any page.
    • Describe the DOM as a tree of nodes
      Explain what the DOM is, how the browser builds it from HTML, which kinds of nodes it holds and how it differs from the HTML source.
    • Move from one element to its parent, children and siblings
      Walk the tree from any element to its relatives, choose between the properties that include text nodes and those that return elements only, and find the nearest matching ancestor.
  2. Find exactly the elements a script needs with CSS selectors, handle elements that are missing, and work with collections of matching elements.
    • Select a single element
      Find one element with querySelector or getElementById, search inside a part of the page, and handle the case where nothing matches.
    • Select many elements and work with the results
      Select every matching element, loop over the results, convert them to an array when needed, and know when a collection updates by itself.
  3. Update what elements say and the attributes and properties that control them, choose the safe method when content comes from users or data, and keep extra data on elements.
    • Change the text and HTML inside an element
      Read and replace an element's content with textContent, innerText and innerHTML, and keep pages safe from injected code.
    • Read and set attributes and properties
      Work with attributes through getAttribute and setAttribute and with the matching element properties, and handle boolean attributes and differences between the two.
    • Store data on elements and read form fields
      Keep extra information on elements with data attributes, and read and set the current value of form fields.
  4. Switch CSS classes, set inline styles when a value is calculated, read the styles the browser applies, and show or hide parts of a page.
    • Add, remove and toggle CSS classes
      Change an element's appearance by switching classes with classList, and keep the visual rules in the stylesheet.
    • Set inline styles and read computed styles
      Set inline styles through the style property when a value is calculated at run time, remove them again, and read the styles the browser actually applies.
    • Show and hide parts of a page
      Hide and reveal elements with the hidden attribute or a class, choose between the ways CSS can make something invisible, and keep the state clear for screen-reader users.
  5. Build new elements in JavaScript, put them exactly where they belong, and remove, replace or copy elements so the page changes without reloading.
    • Create new elements and insert them
      Create elements with createElement, fill them with text and attributes, and insert them at the end, the start or next to another element.
    • Remove, replace and copy elements
      Take elements out of the page, empty a container, replace one element with another, and make copies with cloneNode.
  6. Turn arrays of data into lists and tables on the page, build many items efficiently, and keep the page in step with the data as it changes.
    • Build list items and table rows from an array
      Turn an array of objects into list items or table rows with textContent, show sensible output for empty lists and missing values, and link each element to its data.
    • Build many items efficiently with fragments and templates
      Insert many new elements in one step with a DocumentFragment or a single append, and reuse markup written in an HTML template element.
    • Update the page when the data changes
      Keep the data as the single source of truth, re-render from it after every change, and show filtered, sorted and summary views without losing the original data.

Document Object Model study guide

6 chapters

1

Read the page as a tree

The browser turns HTML into objects that JavaScript can read and change. Those objects form a tree, so finding a page element also means understanding where it sits among other nodes. Start by connecting familiar tags to the objects in the browser. Then follow parent, child, and sibling relationships, paying attention to the text nodes made from spaces and line breaks. DevTools lets you see this tree while your code changes it.

1.1 HTML becomes a tree of objects

The Document Object Model, or DOM, is the browser's live tree of page objects. JavaScript reads and changes these objects to update the page without reloading. The browser supplies the DOM API; JavaScript is the language used to call it.

Start at the document

document is the entry point. document.documentElement refers to <html>, document.head to <head>, and document.body to <body>. The browser tab is represented by window, whose document property refers to this document.

Every item in the tree is a node. Elements represent tags; text nodes hold characters; comment nodes hold HTML comments; the document node sits above the page's elements. Their nodeType values are 1, 3, 8, and 9 respectively. Every element is a node, but text and comments are not elements.

Read the family relationships
<ul id="menu"><li>Home</li><li>About</li></ul>

The list is the parent of two sibling item elements. Each item is its child. The first item has a text-node child holding "Home". The list is an ancestor of that text node, and the text is a descendant of the list. These relationships describe nesting, not positions on the screen.

Spaces and line breaks between tags usually become whitespace text nodes. An indented list can therefore have two child elements but more than two child nodes. children and firstElementChild return elements only; childNodes and firstChild include text and comments.

Compare the tree with the source

Parsing turns markup into the tree and can complete or repair it. Rows written directly inside a <table> acquire a <tbody> parent. Missing <html>, <head>, or <body> elements can also be supplied. Select from the actual tree, including these parser-added elements.

Script changes affect the browser's current DOM, not the HTML file on the server. "View page source" shows the original HTML, while DevTools Elements shows the current tree. Reloading builds a fresh DOM; an edit alone does not save its underlying data.

Inspect an object

Right-click an element and choose "Inspect". Enter $0 in the Console to refer to that selected element. console.log($0) presents it as markup; console.dir($0) exposes its properties. Expand an indented list to see the whitespace nodes alongside its item elements.

Key Takeaways

  • The DOM is a browser-provided tree that JavaScript can read and change.
  • Elements, text, comments, and the document are different node kinds.
  • Whitespace and parser-added elements can make the DOM differ from the written HTML.
  • Inspect the current tree in DevTools; a DOM edit does not rewrite the server's HTML.

1.2 Follow parents, children, and siblings

Tree properties let a script reach nearby content from an element it already holds. Choose whether that path should include text and comments or only elements.

Choose nodes or elements

Node properties include parentNode, childNodes, firstChild, lastChild, previousSibling, and nextSibling. Their element counterparts are parentElement, children, firstElementChild, lastElementChild, previousElementSibling, and nextElementSibling. children is an HTMLCollection; childNodes is a NodeList.

<ul id="tasks">
  <li>Buy milk</li>
  <li>Call Sam</li>
</ul>

Here tasks.childNodes.length is 5: two elements and three runs of whitespace. tasks.children.length is 2. tasks.firstChild is whitespace text, while tasks.firstElementChild.textContent is "Buy milk". Indexes start at 0, so tasks.children[1] is the second item.

Follow a short path
const tasks = document.querySelector("#tasks");
const first = tasks.firstElementChild;
const second = first.nextElementSibling;
console.log(second.textContent);
console.log(second.parentElement === tasks);

The output is "Call Sam", then true. tasks.lastElementChild reaches the same item directly, as does tasks.children[tasks.children.length - 1].

Handle a missing relative

The last item's nextElementSibling is null. An element with no element children has firstElementChild === null and an empty children collection. The root HTML element's parentNode is the document, but its parentElement is null.

Reading a property from null throws a TypeError. Check first or use second.nextElementSibling?.textContent, which gives undefined when no next element exists.

Find an enclosing item

closest(selector) checks the current element, then its ancestors, returning the nearest match or null. A button inside <li class="task" data-id="7"> can use button.closest(".task"); the matched item's dataset.id is "7". matches(selector) instead tests only the current element and returns a boolean.

Long paths such as el.parentElement.parentElement.nextElementSibling depend on exact nesting. An added wrapper changes their meaning. Use selectors for named regions, short navigation for stable relationships, and closest() for an enclosing item.

Key Takeaways

  • Use element-only navigation when whitespace and comments should be skipped.
  • Collections start at index 0; their length is a count, not the last index.
  • A missing relative is null; check before reading from it.
  • closest() checks the element itself and then its ancestors.
  • matches() tests the current element without moving through the tree.
2

Find page elements

A DOM change begins with a reference to the right element. CSS selectors let you search the whole page or just one container, while collection methods let you work with many results. The return value matters: one search may give an element or null, another always gives a collection. You will also need to know when the script runs and whether a saved collection changes as the page changes, because both affect what your code finds.

2.1 Find one element and check the result

A selector turns a description of an element into an object reference. Start with the smallest search that identifies the element you need, then check that the element was found before changing it.

Search with a CSS selector or an id

document.querySelector(selector) returns the first matching element in document order. It accepts familiar CSS selectors: "h1", ".price", "#cart", "nav a", or 'input[type="email"]'. If nothing matches, it returns null.

document.getElementById("cart") searches for an id directly. Its argument is the bare id. getElementById("#cart") looks for an id that actually includes the hash character, while querySelector("cart") looks for a <cart> tag. Use querySelector("#cart") for the id selector.

Ids must be unique. If copied markup gives two elements the same id, these searches return the first in document order. Fix the duplicate markup instead of expecting an id search to reach the second copy.

Search within a container

Suppose several cards have an element with class name. Select the profile card first, then search its descendants:

const card = document.querySelector("#profile");
if (card) {
  const name = card.querySelector(".name");
  if (name) name.textContent = "Maya";
}

The inner search is limited to the profile card, so an earlier .name elsewhere does not interfere. The container itself is not a candidate in its own querySelector() search. Ordinary elements support this method, but getElementById() belongs to document.

Distinguish no match from an invalid selector

A valid search with no match gives null. Trying to set null.textContent throws a TypeError, so guard optional content:

const banner = document.querySelector(".banner");
if (banner) banner.textContent = "Sale ends Friday";

An invalid CSS selector throws a SyntaxError before there can be a result. For <p id="1st">, querySelector("#1st") is invalid because the digit needs escaping in that CSS id selector. getElementById("1st") uses a literal id and works directly.

Run after the needed markup exists

A classic external script in <head> runs before the browser has parsed the body. A search for a later paragraph therefore returns null even when the HTML contains it. Use <script src="app.js" defer></script> to run after parsing, use a module script, or place the script just before </body>. Module scripts are deferred by default. The defer attribute has no effect on an inline classic script.

For an HTML page starting with <!DOCTYPE html>, id and class selectors are case-sensitive: .Price misses class="price". HTML tag selectors are not case-sensitive, so "LI" and "li" both match list items. When a search fails, check spelling, selector syntax, the search container, and script timing.

Key Takeaways

  • querySelector() returns the first match or null.
  • getElementById() takes a bare id; querySelector() takes CSS selector syntax.
  • Search a container's descendants when a class appears in several page regions.
  • A missing match returns null; invalid selector syntax throws.
  • Run DOM code after its markup has been parsed.
  • Keep ids unique and preserve case in id and class selectors.

2.2 Work with collections of matching elements

Use querySelectorAll() when the same operation belongs on several elements. Its result is a collection, so you must decide how to loop over it and whether you need a snapshot or a live view.

Read and loop over a NodeList

document.querySelectorAll(".note") returns a NodeList of matching elements in document order. No matches gives an empty NodeList with length 0. The collection itself is still truthy; if (notes) does not tell you whether it contains any elements. Check notes.length.

A NodeList supports indexes, length, forEach(), and for...of, but it is not an array and has no map(), filter(), or reduce(). Convert it when you need those methods:

const weights = Array.from(document.querySelectorAll(".weight"))
  .map(el => Number(el.textContent));
const total = weights.reduce((sum, weight) => sum + weight, 0);

For elements containing "4.5", "12", and "3.25", the array is [4.5, 12, 3.25] and the total is 19.75 kg. Conversion to numbers matters because the text values are strings.

A collection is not a combined element. Setting notes.textContent changes no note, and notes.classList.add("saved") throws because the NodeList has no classList. Apply the change to each element:

const notes = document.querySelectorAll(".note");
notes.forEach(note => note.classList.add("saved"));
Compare static and live results

The NodeList from querySelectorAll() is static: its membership is fixed when the search runs. The elements it refers to are still the actual objects, so their text or classes can change.

getElementsByClassName("note") and getElementsByTagName("li") return a live HTMLCollection. children is live too. These collections update their membership when the DOM changes. They support indexes and for...of, but no forEach().

In a list with three .item elements, save both list.getElementsByClassName("item") and list.querySelectorAll(".item"). Append a fourth matching element. The live collection now has length 4; the snapshot still has length 3.

Avoid a shrinking live-loop trap

Suppose four elements belong to a live collection named live:

for (let i = 0; i < live.length; i++) {
  live[i].classList.remove("item");
}

Removing the first class removes that element from the collection. The next element shifts to index 0, but the loop advances to index 1 and skips it. Only two of the four lose the class. Loop over Array.from(live) or a querySelectorAll() snapshot when changing membership.

Check spaces in selectors

.task.done means one element with both classes. .task .done means a .done descendant inside a .task. For <li class="task done">, count completed items with document.querySelectorAll(".task.done").length. A comma combines selectors: "h2, h3" returns matches in document order, not all headings of one type followed by the other.

Key Takeaways

  • Loop over collection members to change their content or classes.
  • querySelectorAll() returns a static NodeList; an empty result has length 0.
  • Convert collections with Array.from() or spread before using array-only methods.
  • Live collections change membership as matching elements change.
  • Take a snapshot before a loop that removes live collection members.
  • A space in a selector changes the relationship being searched.
3

Update content and element data

An element holds several kinds of information: visible text, HTML structure, attributes, and JavaScript properties. Choosing the right one lets you update a label, change a link, read a field, or connect a displayed item to its data. The same text can be treated as ordinary characters or parsed as HTML, with very different results. Follow these differences closely so that updates show the intended content and outside text stays safe.

3.1 Choose text or HTML for content updates

Text and HTML are different inputs. Plain text should appear as characters; HTML describes elements the browser must build. Choosing between them affects both the page structure and whether outside text can run unwanted code.

Read and replace text

For <p id="msg">Total: <strong>3</strong> items</p>, msg.textContent is "Total: 3 items". It joins text from the element's descendants without including their tags, and it includes text hidden by CSS.

Setting textContent replaces all children with a text node. After msg.textContent = "Cart is empty", the <strong> child no longer exists inside the paragraph. Setting msg.textContent = "<b>Hi</b>" displays the literal characters "Hi"; it creates no bold element.

Text values are strings. If a counter contains "4", count.textContent + 1 produces "41". Convert before adding:

count.textContent = Number(count.textContent) + 1;

The addition gives the number 5, and assignment displays its text form, "5".

Read text that follows the layout

On a rendered element, innerText follows the displayed text and layout. It leaves out descendants hidden with display: none and includes line breaks that follow the layout. Reading it can require a layout calculation. Use textContent for ordinary text work; choose innerText when the rendered text is what you need. If the element itself is detached or not rendered, innerText falls back to its text content.

Parse HTML deliberately

innerHTML reads the markup inside an element. Setting it parses the string and replaces the children:

msg.innerHTML = "Total: <strong>5</strong> items";

This creates a new <strong> element. It suits markup you wrote and control. For text from a user or outside data, use textContent and build structure with createElement().

Inserting untrusted HTML can allow cross-site scripting (XSS): attacker-controlled code runs in the visitor's browser. An injected image can carry an onerror attribute that executes code when the image fails to load. Script tags inserted by innerHTML do not execute, but that does not make other injected HTML safe. Blocking only script tags leaves this attack path open.

Preserve existing children when adding content

list.innerHTML += "<li>New</li>" reads the existing markup, joins on a string, then parses and replaces the entire set of children. It does not preserve their object identity. A variable holding an old child still holds that detached object, and typed values in replaced input elements can be lost. Use append() with new nodes, or insertAdjacentHTML("beforeend", markup) for trusted markup, to add without recreating existing children.

outerHTML includes the element's own tag as well as its contents. Setting it replaces the element itself. A saved reference still points to the old, detached element after that replacement; it does not automatically switch to the new one.

Key Takeaways

  • textContent handles plain text and replaces the element's children when assigned.
  • Convert text to a number before arithmetic.
  • innerText follows rendered layout; innerHTML parses markup.
  • Keep untrusted text out of HTML-parsing assignments.
  • innerHTML += recreates existing children; append() preserves them.
  • outerHTML assignment replaces the element, leaving old references detached.

3.2 Distinguish attributes from object properties

An attribute is a name and value in HTML. A property belongs to the JavaScript object that represents an element. Many standard attributes have matching properties, but those properties can process values or report a different kind of state.

Work with an attribute by name

For <img src="logo.png" alt="Company logo">, the browser initializes properties such as img.src and img.alt. You can also use methods that take the attribute name as a string:

  • getAttribute(name) returns a string, or null if the attribute is absent.
  • setAttribute(name, value) adds or changes an attribute and stores its value as text.
  • hasAttribute(name) reports whether the attribute is present.
  • removeAttribute(name) deletes it.

For example, link.setAttribute("href", "https://example.com/help") changes a link's target. If there is no target attribute, link.getAttribute("target") returns null, not an empty string.

Read the value you actually need

On a page at https://example.com/shop/, the link <a href="/about"> has an attribute value of "/about". Its href property is the resolved URL, "https://example.com/about". Read the attribute when you need the value exactly as written, and the property when you need the resolved URL.

Properties can have types other than string. button.disabled is a boolean; input.maxLength is a number. Attribute methods still return strings or null. Property names are also case-sensitive JavaScript names: el.id works, while el.ID is a different property and is normally undefined.

Most names are familiar, but there are exceptions. The HTML class attribute is exposed through className and classList. A label's for attribute is its htmlFor property:

label.htmlFor = "email";

This associates the label with the element whose id is "email".

Treat boolean attributes by presence

Boolean attributes include disabled, checked, required, and readonly. Presence makes the attribute true, even if its text is "false". This code disables a button:

button.setAttribute("disabled", "false");

To enable it, set button.disabled = false or call button.removeAttribute("disabled"). button.toggleAttribute("disabled", isBusy) adds the attribute when isBusy is true and removes it when false.

Describe controls accessibly

ARIA attributes supply information to assistive technology. Attribute methods are useful for setting them, such as button.setAttribute("aria-label", "Close dialog") to name an icon-only button. Their values are strings; for example, an expanded-state value is "true" or "false".

Use properties for common operations such as img.src = "photo.jpg" or input.disabled = true. Use attribute methods when the name is held in a variable, the exact written value matters, or the attribute has no matching property you are using.

Trace a property change back to HTML
const field = document.createElement("input");
field.disabled = true;
console.log(field.hasAttribute("disabled"));
console.log(typeof field.disabled);
console.log(typeof field.getAttribute("disabled"));
field.removeAttribute("disabled");
console.log(field.disabled);

The outputs are true, "boolean", "string", and false. Setting the property adds the boolean attribute. Reading that attribute still returns text, even though the property is boolean. Removing it switches the property back to false. The method and property expose the same presence through different value types.

Key Takeaways

  • Attributes store HTML values; properties can expose typed or processed values.
  • getAttribute() returns a string or null when absent.
  • Boolean attributes are true by presence, including disabled="false".
  • Use property names such as className and htmlFor where HTML names differ.
  • ARIA values are strings that describe the element to assistive technology.

3.3 Connect elements to data and read fields

An element can carry an id for the data it displays without showing that id on screen. Form controls also carry values, but their current content is read through properties. Both kinds of value often need conversion before comparison or arithmetic.

Store custom data with data attributes

HTML names starting with data- store information for scripts:

<li data-task-id="17" data-priority="high">Prepare slides</li>

The dataset object exposes these attributes. Remove the data- prefix and turn each dash followed by a lowercase letter into that uppercase letter: data-task-id becomes dataset.taskId. data-priority becomes dataset.priority.

li.dataset.dueDate = "2026-11-05";
delete li.dataset.priority;

The assignment creates data-due-date="2026-11-05". The delete removes data-priority. Data attributes can also be selected with CSS attribute selectors, such as '[data-priority="high"]'.

A made-up attribute such as taskid="17" is invalid HTML and creates no li.taskid property. getAttribute("taskid") can still read it, but data-task-id is the proper custom-data form.

Convert string values deliberately

li.dataset.taskId is "17", a string. li.dataset.taskId === 17 is false because the types differ. When ids in the data are numbers, use Number(li.dataset.taskId) === 17. An absent data attribute gives undefined. Assigning li.dataset.count = 3 stores the text "3".

This makes a useful bridge from a displayed item back to its object:

const id = Number(li.dataset.taskId);
const task = tasks.find(task => task.id === id);

The comparison now uses two numbers. If no object has that id, find() returns undefined; check before reading its properties.

Read a field's current value

An <input> does not hold the typed value as text content. Read input.value, which is a string, or assign it to change the field. input.value = "" clears it.

For <input id="qty" value="1">, after the user types 3, qty.value is "3" but qty.getAttribute("value") is still "1". The attribute supplies the starting value; the property supplies the current one. A checkbox works similarly: its checked property reports its current boolean state, while the attribute describes the initial checked state.

Use the right value for each control

Even <input type="number"> has a string value. If the quantity is "3" and each item weighs 0.25 kg, Number(qty.value) * 0.25 gives 0.75 kg. But qty.value + 1 gives "31" because + joins a string and a number. A <select> returns the selected option's value through select.value. For a checkbox, read checkbox.checked, not checkbox.value, when you need to know whether it is selected.

Key Takeaways

  • dataset maps data-task-id to taskId and stores string values.
  • Use delete on a dataset property to remove its data attribute.
  • Convert data ids before strict comparison with numeric ids.
  • Read current input and select content through value.
  • Read a checkbox's current selection through checked.
  • Initial value and checked attributes can differ from current control properties.
4

Control appearance and visibility

JavaScript can change a page's appearance by switching classes, setting a calculated inline value, or hiding a section. These choices work with the CSS already on the page. Learn which state belongs in a class, which values need units, and how to read the style the browser applies. Showing and hiding also affects layout and assistive technology, so keep visible panels and their accessibility attributes in step.

4.1 Switch classes without losing base styling

Keep fixed visual rules in CSS, then let JavaScript change the classes that select those rules. A class can express state such as "done", "active", or "error" without repeating the corresponding colors and decorations in code.

Add and remove class tokens

Suppose the stylesheet contains:

.done { text-decoration: line-through; color: #888; }

item.classList.add("done") applies the completed appearance. item.classList.remove("done") removes that state while leaving other classes intact. Both methods accept several names; adding an existing class or removing an absent one is harmless.

contains("done") returns a boolean. replace("wide", "narrow") swaps a present class for another. toggle("active") adds a missing class or removes a present one, returning whether the class exists afterward.

Trace a class change

Start with <div class="card">:

card.classList.add("active", "wide");
const active = card.classList.toggle("active");
const replaced = card.classList.replace("wide", "narrow");

After the first line the classes are "card active wide". Toggling removes "active", so active is false and the classes become "card wide". Replacing "wide" leaves "card narrow" and returns true. A later card.classList.contains("active") is false.

Set a class from a condition

When data gives the desired state, pass a second argument:

row.classList.toggle("overdue", daysLeft < 0);

A negative daysLeft ensures presence; otherwise the class is absent. Repeating the call preserves that state. Plain toggle("overdue") would reverse it on every call.

Preserve the rest of the class attribute

className is the full class string. card.className = "active" replaces all existing classes, including "card". Use this assignment when replacing the entire set is intended; use classList for changes to individual states.

Pass bare class names, without CSS selector punctuation. classList.add(".active") adds a token containing a literal dot; it is not the class matched by .active. A single argument cannot contain spaces: classList.add("big red") throws an InvalidCharacterError. Write classList.add("big", "red") for two classes.

Check the CSS when the look does not change

If a class has no visible effect, inspect its token and rule in DevTools. Check for a missing or misspelled rule and overridden declarations crossed out in the Styles pane.

Key Takeaways

  • classList changes individual class tokens while preserving unrelated classes.
  • toggle(name) returns whether the class is present afterward.
  • toggle(name, condition) sets a class from data without reversing it on every run.
  • className assignment replaces the whole class string.
  • Pass separate bare names, without selector dots or spaces inside a token.

4.2 Set calculated styles and read applied styles

Use classes for fixed designs and inline styles for calculated values. The style object reads and writes inline declarations, not all the rules that affect an element.

Write CSS values through style

Dashed CSS names become camelCase JavaScript properties. background-color becomes backgroundColor, font-size becomes fontSize, and z-index becomes zIndex:

box.style.backgroundColor = "gold";
box.style.width = "250px";

Style values are strings. Include a unit when the property requires one. In a standards-mode page, assigning box.style.width = 250 is ignored because a nonzero width needs a CSS unit. The string "0" is valid for a zero width.

style.setProperty() takes the CSS spelling instead:

box.style.setProperty("background-color", "gold");
document.documentElement.style.setProperty("--accent", "#0a7d5a");

The root custom property supplies rules that reference var(--accent).

Turn a calculation into a CSS value

A progress bar shows 3 finished tasks out of 8:

const percent = (3 / 8) * 100;
bar.style.width = `${percent}%`;

The ratio 0.375 becomes 37.5 percent. The template literal supplies "37.5%", including its required unit.

Remove a temporary override

Set box.style.backgroundColor = "" or call box.style.removeProperty("background-color") to remove that inline declaration. The browser then applies the remaining stylesheet rules. Use the dashed CSS name with removeProperty().

Be careful with style.cssText. Assigning box.style.cssText = "color: red" replaces the entire inline style, so an existing inline width is lost. Changing one style property leaves the other inline declarations intact.

Read what the browser applies

If a stylesheet makes a paragraph red but it has no inline color, p.style.color is an empty string. Use getComputedStyle(p).color for the applied value, commonly "rgb(255, 0, 0)". Computed values are read-only; change the class or inline declaration to alter the appearance.

Inline declarations override ordinary stylesheet rules, but applicable !important declarations can win. If a class change seems ineffective, check for a leftover inline override.

Key Takeaways

  • style reads and writes inline declarations; camelCase maps to dashed CSS names.
  • Supply units in CSS value strings when required.
  • setProperty() and removeProperty() take dashed CSS names.
  • Remove an inline declaration to let stylesheet rules apply again.
  • getComputedStyle() reports applied values, including styles from CSS rules.

4.3 Hide content and keep its state clear

Hiding a panel leaves it in the DOM, ready to return with its existing content and field values. Removing it takes it out of the tree. Choose hiding for a panel that will return and removal for an item whose data no longer exists.

Use the hidden property

panel.hidden = true adds ordinary hidden state. The browser's default styling hides the panel with no layout space. panel.hidden = false removes that state and reveals it again.

A stylesheet can override this default. For example, .panel { display: flex; } can make a hidden panel visible. Where hidden state should always win, include:

[hidden] { display: none !important; }

Alternatively, control visibility with a CSS class. Given .is-hidden { display: none; }, menu.classList.toggle("is-hidden", !isOpen) adds the hiding class when the menu is closed. Check the stylesheet if the class is present but another display declaration wins.

Compare three ways to hide

display: none removes the element's layout space and hides it from screen readers. visibility: hidden makes it invisible while keeping its space, and also hides it from screen readers. opacity: 0 makes it transparent but keeps its space; it can still be clicked and remains available to screen readers. Transparency alone is therefore a poor way to close a panel that should no longer be available.

These effects concern visibility and layout, not deletion. A hidden input can still retain the value typed into it because the same input object remains in the tree.

Keep the control's description in step

A button associated with a collapsible panel should expose whether it is expanded. Set aria-expanded to the string "true" when open and "false" when closed. Keep that value and the panel's visibility based on the same state.

For an answer panel starting as <div id="answer-1" hidden>, with references named answer and button, run:

const willOpen = answer.hidden;
answer.hidden = !willOpen;
button.setAttribute("aria-expanded", String(willOpen));

Initially answer.hidden is true, so willOpen is true. The assignment sets hidden to false and shows the panel. The button's attribute becomes "true". Running the same statements again starts from hidden false, hides the panel, and writes "false" to the attribute.

Diagnose a mismatch

If the button announces "expanded" while the panel is hidden, the two values were not updated together. If hidden is true but the panel is still visible, inspect its computed display and the CSS rule responsible. The first problem is inconsistent application state; the second is a style overriding hidden state. Reading both the DOM properties and the applied CSS lets you distinguish them.

Key Takeaways

  • hidden keeps content in the DOM while hiding it with the default styling.
  • An explicit display rule can override hidden; inspect or enforce the intended rule.
  • display: none removes space; visibility: hidden preserves space.
  • opacity: 0 preserves interaction and screen-reader exposure.
  • Update aria-expanded from the same open state as the panel.
5

Build and change page structure

Page structure can change while the page is open. Build an element in memory, fill it, and insert it at the right point in the tree. The same insertion methods can move an existing node, so adding a reference twice does not create two copies. Removing, replacing, and cloning make different changes to the tree and to the references your script holds. Tracing those references helps explain why an old variable sometimes points to detached content.

5.1 Create a node, fill it, then insert it

A new element does not appear just because you created it. First make the object, then give it content and attributes, and finally insert it into the document. Before insertion, you can assemble a whole nested structure in memory.

Build one complete item
const li = document.createElement("li");
li.textContent = "Water the plants";
li.className = "task";
li.dataset.id = "4";
document.querySelector("#tasks").append(li);

createElement("li") makes an empty element. The next lines fill it; the last inserts it as the final child of the existing task list. It produces <li class="task" data-id="4">Water the plants</li>. The call needs a tag name, not markup: createElement("<li>") throws an InvalidCharacterError. Set classes and attributes after creation.

For nested content, create a title span and a button separately, set their text, and call li.append(title, button). Then insert the assembled item into the list. document.createTextNode("…") makes text directly; passing a string to append() also inserts a text node.

Choose an insertion position
  • parent.append(...) adds after the existing children.
  • parent.prepend(...) adds before the existing children.
  • element.before(...) adds previous siblings.
  • element.after(...) adds next siblings.

Each accepts multiple nodes and strings. Strings are treated as text, so list.append("<li>New</li>") does not build an item. The angle brackets appear as characters inside the list.

Older methods still appear in code. appendChild(node) accepts one node and returns it; it does not accept a string. append() returns undefined. parent.insertBefore(newNode, referenceNode) inserts a node before an existing child; using null as the reference adds at the end.

Understand moving versus copying

A node can occupy only one place in the tree. Inserting an existing node moves it. For a list ordered a, b, c, list.prepend(list.lastElementChild) moves c to the start, producing c, a, b. It does not leave a second c at the end.

This also explains a common loop bug: creating one <li> outside a loop and appending it repeatedly leaves one item. Each pass changes and moves that same object. Create a fresh element inside each pass when the page needs several items, or make separate clones.

Insert trusted markup beside existing content

insertAdjacentHTML(position, markup) parses HTML without replacing existing children. Its four position names are "beforebegin" and "afterend" for siblings outside the element, and "afterbegin" and "beforeend" for children inside it. list.insertAdjacentHTML("beforeend", "<li>New</li>") appends a parsed item. Use this only for trusted markup; outside text belongs in textContent. insertAdjacentElement() and insertAdjacentText() use the same position names for an element or plain text.

Key Takeaways

  • Create, fill, and insert are separate steps.
  • append/prepend add children; before/after add siblings.
  • Passing a string to append() inserts text without parsing HTML.
  • Inserting an existing node moves it instead of copying it.
  • Create a fresh item inside each loop pass when you need several objects.
  • appendChild() accepts one node and returns it; append() accepts several inputs and returns undefined.
  • insertAdjacentHTML() preserves existing children but still parses its input as HTML.

5.2 Remove, replace, and clone the right objects

Removing an element changes the tree, but it does not erase every reference to that object. Replacing it creates a similar distinction: the new object occupies its place while saved variables can still refer to the old one.

Remove one element or several matches

element.remove() detaches that element. parent.removeChild(child) is the older parent-based form and returns the removed node. If a variable still holds the node, the script can read it or insert it again later.

To remove finished tasks, select a static collection and loop:

document.querySelectorAll(".task.done")
  .forEach(task => task.remove());

With five tasks, two marked done, this leaves three. Removing an element does not change which references the static NodeList holds, so the loop visits both selected tasks.

Do not count forward through a shrinking live list

list.children is live. With six children, a forward loop that removes items[i] removes only three. After the first removal, the second element shifts into index 0, but i advances to 1. The next original child is skipped, and the same shift repeats.

Take a snapshot before removing all current children:

Array.from(list.children).forEach(item => item.remove());

For simply emptying a container, list.replaceChildren() is clearer. Setting list.textContent = "" or list.innerHTML = "" also empties it. An empty string parses no outside content.

Replace a single node or the whole child set

old.replaceWith(newElement) puts a new node where the old one stood. For <p id="status">Loading…</p>, document.querySelector("#status").replaceWith(list) replaces the message with the list. parent.replaceChildren(a, b, c) replaces all children of the parent in one operation while keeping the parent itself.

This difference matters for saved references. A variable holding the parent survives replaceChildren(), but references to the removed children now point to detached objects. Replacing a container with replaceWith() detaches that container too.

Copy the element with or without descendants

element.cloneNode(true) makes a deep copy, including descendants and attributes. cloneNode() or cloneNode(false) copies the element and its attributes without its children. A cloned list item with no descendants has no text from the original item.

A clone begins detached and must be inserted. It also inherits the original's id. Give it a new id or remove that attribute before placing it on the same page, so id-based selection remains reliable.

Handle optional elements

If a search can miss, do not immediately call a method on its result. document.querySelector("#promo").remove() throws when no promotion exists. document.querySelector("#promo")?.remove() safely does nothing in that case. For a required container, a missing result is a useful signal to check the selector and script timing before attempting a structural update.

Key Takeaways

  • remove() detaches a node; a saved reference can still use or reinsert it.
  • Use a static list when removing several selected elements.
  • replaceChildren() keeps the container and replaces or clears its children.
  • replaceWith() replaces the selected node itself.
  • cloneNode(true) copies descendants; a shallow clone omits them.
  • Cloning copies ids, so change or remove a copied id before insertion.
6

Turn data into a page

Arrays describe what a page should show; rendering turns those descriptions into elements. Start with one item, extend the pattern to lists and tables, and then organize repeated markup with fragments or templates. A render function makes later updates predictable when the data changes. Keep the original array, the selected view, and the displayed elements distinct so filtering, sorting, empty results, and summaries all stay consistent.

6.1 Render array items as list entries and table rows

Rendering means turning data into a visible DOM structure. Keep the array as the source and build an element for each object. Put data values into textContent so that they remain text even if they contain angle brackets.

Build one element for each object

Use these book objects:

const books = [
  { id: 1, title: "Dune", pages: 412 },
  { id: 2, title: "Emma", pages: 474 },
  { id: 3, title: "Beloved", pages: 324 }
];
function createBookItem(book) {
  const li = document.createElement("li");
  li.dataset.id = String(book.id);
  li.textContent = `${book.title} (${book.pages} pages)`;
  return li;
}
const list = document.querySelector("#books");
list.append(...books.map(createBookItem));

map() calls the builder once per book, giving three separate elements. Spread passes those elements as separate arguments to append(). The display reads "Dune (412 pages)", "Emma (474 pages)", and "Beloved (324 pages)". The page counts add to 1,210.

Without spread, list.append(books.map(createBookItem)) receives one array. It turns that array into text containing strings such as "[object HTMLLIElement]" instead of inserting the item elements.

The stored id connects the displayed element to the data. Read Number(li.dataset.id) before comparing it with numeric book ids, then use books.find(book => book.id === id).

Handle missing and empty data

If an optional author field is absent, direct interpolation displays "undefined". Use book.author ?? "Unknown author" for a fallback when the field is null or undefined. Nullish coalescing leaves other values, including 0 and an empty string, unchanged.

If the array is empty, display a message such as "No books yet". An empty result should tell the user that there are currently no items. When rendering again, replace the old display so that an earlier message or earlier items do not remain alongside the new result.

Know what an HTML-string approach does

A trusted array can be mapped to HTML strings and joined with join("") before assignment to innerHTML. Joining removes the commas that array-to-string conversion would otherwise insert. This approach parses every interpolated value as HTML. Use the element-building approach when values can come from outside sources.

Build a valid table structure

For table data, build one <tr> per object and one <td> for each displayed field. Append the rows to the table's <tbody>. For readings from Oslo at 4.25 °C and Cairo at 28.5 °C, the temperature cell can use reading.temp.toFixed(1): it displays "4.3" and "28.5". toFixed() returns a string with the requested decimal places. toLocaleString() instead formats for the user's locale, which can use a decimal comma. Keep formatting at the display step so numeric data stays available for calculations.

Build both cells before inserting the row

For an existing empty table body named tbody, this renders the readings:

const readings = [{ city: "Oslo", temp: 4.25 }, { city: "Cairo", temp: 28.5 }];
const rows = readings.map(reading => {
  const row = document.createElement("tr");
  const city = document.createElement("td");
  const temperature = document.createElement("td");
  city.textContent = reading.city;
  temperature.textContent = reading.temp.toFixed(1) + " °C";
  row.append(city, temperature);
  return row;
});
tbody.replaceChildren(...rows);

Each pass creates a new row and two new cells. Filling both cells before appending the row keeps field order explicit. Spreading rows supplies the two row nodes separately; the final replacement also clears any earlier rows.

Key Takeaways

  • Build a separate element for each data object and fill values with textContent.
  • Spread an array of nodes when passing them to append().
  • Use data attributes to connect displayed elements to objects.
  • Provide empty-state messages and nullish fallbacks.
  • Put table rows in tbody and fields in cells.
  • toFixed() returns display text; keep numeric data for calculations.

6.2 Assemble repeated content before insertion

When a list contains many items, build the new content off the page and insert it together. A DocumentFragment groups nodes without adding a wrapper element. A template lets you write a repeated structure in HTML and fill a fresh copy for each object.

Collect nodes in a fragment
const fragment = document.createDocumentFragment();
for (const book of books) {
  fragment.append(createBookItem(book));
}
list.append(fragment);

The fragment starts outside the document. Each new item becomes its child. Appending the fragment moves its children into the list in their existing order; it does not insert the fragment itself as a wrapper. After 100 items are transferred into an empty list, the list has 100 children and the fragment has 0.

This pattern groups the insertion and keeps the construction code organized. It does not promise a particular speed gain. An array of built nodes works too: list.append(...items) adds them, while list.replaceChildren(...items) replaces the current contents with them.

Define repeated markup in a template
<template id="book-row">
  <li class="book"><span class="title"></span>
    (<span class="pages"></span> pages)</li>
</template>

The browser parses this markup without displaying it. The contents live in a separate DocumentFragment at template.content. A document-level search does not search into that fragment. Select the template, clone its content, then search inside the clone:

const template = document.querySelector("#book-row");
const clone = template.content.cloneNode(true);
clone.querySelector(".title").textContent = book.title;
clone.querySelector(".pages").textContent = book.pages;
const item = clone.firstElementChild;
list.append(clone);

The title and page count are filled before insertion. item saves a reference to the new <li>. After appending, clone is empty because its children have moved, but item still refers to the inserted element.

Preserve the reusable source

Append a copy each time. Calling list.append(template.content) moves the source nodes out of the template. A later search inside template.content then returns null, and the next attempt to fill the missing title throws. The source template should remain available for later books.

For many copies, fill each cloned fragment, append those copies to a collecting fragment, then insert the collection. A fragment can receive another fragment's children just as a list can.

Choose a readable construction method

createElement() suits a small structure or one that varies substantially by item. A template suits larger repeated markup that is easier to edit as HTML. In both cases, insert outside values as text. An HTML string suits trusted markup, but interpolation into it still invokes HTML parsing. Select the construction method based on the structure you need to maintain, while keeping the same create-or-clone, fill, and insert order.

Collect filled copies for one replacement

Using the book template above and an existing books array:

const group = document.createDocumentFragment();
for (const book of books) {
  const copy = template.content.cloneNode(true);
  copy.querySelector(".title").textContent = book.title;
  copy.querySelector(".pages").textContent = book.pages;
  group.append(copy);
}
list.replaceChildren(group);

Each filled copy transfers its item into group. The last line replaces the list with those items and empties group. The original template is untouched throughout, so the function can repeat the construction for a later array.

Key Takeaways

  • Appending a fragment moves its children and leaves the fragment empty.
  • A fragment groups nodes without creating a wrapper in the page.
  • Template contents are parsed but live outside the displayed document tree.
  • Clone template.content for each item, then fill before insertion.
  • Save the item reference before its containing fragment is emptied.

6.3 Keep rendered views in step with data

Use data as the single source of truth. A render function derives a display from the current array. Call it after changing the array or an object in it.

Replace the display on each render

Suppose tasks holds four objects, exactly one done, and task 2 exists and is unfinished. A render function can build fresh items and a summary:

function render() {
  const items = tasks.map(task => {
    const li = document.createElement("li");
    li.dataset.id = String(task.id);
    li.textContent = task.text;
    li.classList.toggle("done", task.done);
    return li;
  });
  list.replaceChildren(...items);
  const doneCount = tasks.filter(task => task.done).length;
  summary.textContent = `${doneCount} of ${tasks.length} done`;
}

After tasks.push({ id: 5, text: "Book dentist", done: false }); render();, the list has five items and the summary says "1 of 5 done". Setting tasks.find(task => task.id === 2).done = true; render(); changes it to "2 of 5 done".

Replacing children is essential here. If the function only appended all items, calling it twice would duplicate the display. Removing only a DOM item is also temporary: if its object stays in the array, rendering brings it back. With tasks declared using let, delete from the data with tasks = tasks.filter(task => task.id !== id) and render again.

Derive a view without losing the source

For a renderer that accepts a view array, pass tasks.filter(task => !task.done) to display only unfinished tasks. Keep the full array for later views and whole-list counts. Give summaries clear meanings: a count of visible tasks is different from a count of all tasks.

sort() changes the array it is called on. Sort a copy for display with [...tasks].sort(...), or use tasks.toSorted(...) for a sorted copy. Supply a comparator: numeric values need (a, b) => a - b, and titles can use (a, b) => a.title.localeCompare(b.title). Default sorting compares string forms, so [100, 25, 3].sort() stays in that order; numeric sorting gives [3, 25, 100].

Match ids and parse data correctly

A displayed dataset.id is a string. Convert it before strict comparison with numeric ids, or find() can return undefined even when the intended object exists. JSON data is different: JSON.parse() preserves JSON numbers and booleans as those types. Parsed objects can go through the same rendering function as objects written directly in code.

Preserve local state when needed

A full rebuild creates new elements. It can lose focus and text typed into inputs inside the rebuilt area. For a small state change that must preserve those objects, update the data and then update the existing item, such as li.classList.toggle("done", task.done). The data must still record the change so that a later full render agrees with the current display.

Give the view and the summary separate inputs

A filtered view can still show a summary of all tasks:

function renderOpenTasks() {
  const visible = tasks.filter(task => !task.done);
  const nodes = visible.map(task => {
    const li = document.createElement("li");
    li.textContent = task.text;
    return li;
  });
  if (visible.length === 0) {
    const empty = document.createElement("li");
    empty.textContent = "No open tasks";
    nodes.push(empty);
  }
  list.replaceChildren(...nodes);
  const done = tasks.filter(task => task.done).length;
  summary.textContent = `${done} of ${tasks.length} done`;
}

An all-completed array displays the empty message for this view but retains every object for the total. Counting from visible would answer a different question and could report no completed tasks simply because this view excludes them.

Key Takeaways

  • Update the data, then derive the page from that data.
  • Replace old children when rebuilding a list to avoid duplicates.
  • Filter and sort a derived view while preserving the original array when needed.
  • Give numeric sorting a comparator and convert dataset ids before numeric comparison.
  • JSON.parse() preserves numeric and boolean data types.
  • Use a focused element update when rebuilding would lose needed input state or focus.

Document Object Model examples

6 Walkthroughs

1

Find your way through a page tree

11 steps
Inspect a small list, distinguish elements from text, and follow reliable paths to nearby content.
  1. 1
    IntroductionStart with a page you can inspect

    The browser builds objects from HTML. JavaScript works with this DOM tree.

    Follow a two-item list from markup to nodes, then use family relationships to reach a nearby item. Keep DevTools open to compare the visible page with its tree.

  2. 2
    ExplanationEnter through document

    The DOM is the browser's live page model. document provides access; document.body refers to the body element.

    The browser supplies this API. JavaScript calls it to change the current page without reloading or rewriting the server's HTML file.

  3. 3
    ExampleSee the element and its text separately

    Look at <li>Buy milk</li>. The <li> becomes an element node, and "Buy milk" becomes a text-node child inside it.

    Read nodeType to tell them apart: the element is type 1 and its text child is type 3. Comments have type 8, and the document itself has type 9. "Node" names the broader group; an element is one kind of node.

  4. 4
    ExampleCount what indentation adds

    Use this exact layout:

    <ul id="tasks">
      <li>Buy milk</li>
      <li>Call Sam</li>
    </ul>
    

    The list has two element children. It also has three whitespace text nodes: before, between, and after those items. Its children.length is therefore 2, while childNodes.length is 5. The extra nodes come from the characters between tags.

  5. 5
    ChallengePredict the first node

    In the indented list from the previous slide, what kind of node is tasks.firstChild, and why is it different from tasks.firstElementChild?

    ---reveal---

    firstChild is the whitespace text node before the first item. firstElementChild skips that node and returns the first <li>. Indentation affects the node sequence even though it does not create another visible task.

  6. 6
    TipInspect the current tree

    Choose "Inspect" on the first list item. The Elements panel shows the current DOM. With the item selected, $0 in the Console refers to it, and console.dir($0) exposes its properties.

    The tree can include parser-added elements, such as a <tbody> around table rows. Use this panel to check actual nesting instead of assuming it matches every tag boundary in the source.

  7. 7
    ExampleMove to the next element

    Save the list as tasks, then follow one element relationship:

    const first = tasks.firstElementChild;
    const second = first.nextElementSibling;
    

    second.textContent is "Call Sam". Reading first.nextSibling instead reaches the whitespace between the items. Choose the element form when the task is to move among page elements and text nodes should be skipped.

  8. 8
    ExplanationStop safely at the last item

    The last item's nextElementSibling is null. Reading a property directly from that missing reference throws a TypeError.

    second.nextElementSibling?.textContent stops there and gives undefined, without adding a node or supplying an empty string.

  9. 9
    ConnectionFind the enclosing item by meaning

    A nested span can find its enclosing task with span.closest("li"). The search begins at the span itself, then checks successive ancestors until a match is found.

    span.matches("li") only tests the span, returning false in this case. An ancestor search is useful when an extra wrapper could make a fixed number of parent hops lead to the wrong place.

  10. 10
    ChallengeNavigate through an added wrapper

    Each list item now wraps its text in a span. Starting from the first span, how can you read the next item's text without depending on whitespace nodes or the number of wrappers inside an item?

    ---reveal---

    Find the enclosing item, then its next element sibling: span.closest("li").nextElementSibling?.textContent. The enclosing item is found by its tag, and element-only sibling navigation skips the whitespace between items.

  11. 11
    RecapTry a table next

    Inspect a table whose source puts a row directly inside <table>. Find the row's actual parent.

    Compare its child-node and element-child counts, using the Elements panel to explain parser-added structure and whitespace.

2

Select and update the intended elements

12 steps
Choose search scope, check missing results, and avoid live-collection loop surprises.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose a search before changing a page
  2. 2
    ExplanationUse the id syntax for the method
  3. 3
    ExampleKeep a repeated class inside its card
  4. 4
    TipDistinguish a missing node from bad syntax
  5. 5
    ChallengeExplain an early null result
  6. 6
    ExampleLoop through the returned elements
  7. 7
    ExplanationConvert when you need array methods
  8. 8
    ExampleCompare two saved collections
  9. 9
    Key InsightWatch a shrinking live collection
  10. 10
    ChallengeChoose the right combined selector
  11. 11
    ChallengeCombine scope with safe iteration
  12. 12
    RecapDiagnose your next failed search
3

Update a profile and read its form values

15 steps
Choose plain text, element properties, attributes, and dataset entries for different kinds of information.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSeparate the information an element holds
  2. 2
    ExampleRead text without its tags
  3. 3
    ExplanationTreat outside values as characters
  4. 4
    ChallengePreserve an existing input
  5. 5
    ConnectionChoose a rendered-text read when needed
  6. 6
    ExampleRead a written URL or a resolved URL
  7. 7
    ExplanationSet a boolean through presence
  8. 8
    ChallengeRepair a disabled control
  9. 9
    ExampleTurn a data name into a property name
  10. 10
    ExplanationRead current field content
  11. 11
    ChallengeCalculate with a field value
  12. 12
    ExampleFind an object using a displayed id
  13. 13
    TipGive an icon control a readable name
  14. 14
    ChallengeConnect data lookup to safe display
  15. 15
    RecapInspect a field before and after editing
4

Make appearance follow a clear state

13 steps
Switch classes, calculate progress styles, and synchronize a panel with its accessible expanded state.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionLet data choose the visible state
  2. 2
    ExampleKeep a base class while adding a state
  3. 3
    ExplanationUse a condition for repeated updates
  4. 4
    ChallengePredict two repeated updates
  5. 5
    ExampleCalculate a CSS percentage
  6. 6
    ExplanationRead the result of the cascade
  7. 7
    ChallengeRestore the stylesheet color
  8. 8
    ExplanationChoose what a closed panel removes
  9. 9
    ExampleMake a panel and its control agree
  10. 10
    TipCheck a style that defeats hidden
  11. 11
    ChallengeDiagnose an invisible but active control
  12. 12
    ChallengeCombine classes with a calculated value
  13. 13
    RecapCheck state and appearance separately
5

Build items and follow their object references

12 steps
Create and insert new content, move an existing item, then remove and clone without losing track of identity.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionBuild a task item in separate stages
  2. 2
    ExampleCreate a detached item
  3. 3
    ExampleInsert the finished item
  4. 4
    ExplanationChoose children or siblings
  5. 5
    ExampleMove a node that already exists
  6. 6
    ChallengeExplain a one-item loop result
  7. 7
    ExplanationDetach without destroying a reference
  8. 8
    ExampleReplace one item or a whole display
  9. 9
    TipCopy descendants and check ids
  10. 10
    ChallengePredict a shallow copy
  11. 11
    ChallengeMove the original and insert a copy
  12. 12
    RecapTrace identities alongside markup
6

Render a list from data and update it reliably

17 steps
Build repeated items, use a template safely, and keep filtered views and summaries consistent with the data.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionMake the array drive the display
  2. 2
    ExampleWrite a builder for one book
  3. 3
    ExampleExpand the array into node arguments
  4. 4
    TipKeep missing data readable
  5. 5
    ConnectionApply the pattern to table rows
  6. 6
    ChallengeFind why the list shows object text
  7. 7
    ExampleCollect nodes away from the page
  8. 8
    ExplanationKeep repeated structure in HTML
  9. 9
    ExampleFill before transferring the clone
  10. 10
    ChallengePreserve the reusable template
  11. 11
    ExplanationReplace the display from current data
  12. 12
    ExampleUpdate an object and its summary
  13. 13
    ExplanationKeep a sorted view separate
  14. 14
    ChallengeExplain an item that returns after deletion
  15. 15
    Key InsightPreserve local input state
  16. 16
    ChallengeCombine a lookup with a targeted update
  17. 17
    RecapRun a three-state rendering check

Document Object Model practice questions

50 Practice Questions in 5 sets

Set 1: Read and change a page10 Practice Questions

Question 1 of 10

A script changes a paragraph from "Draft" to "Ready". What does "View page source" show for that paragraph after the change?
  1. The updated DOM text "Ready".
  2. The original HTML text "Draft".
  3. Both texts as adjacent text nodes.
  4. An empty paragraph after the edit.
Show answerHide answer
Answer: B
"View page source" still shows "Draft" because it reads the original HTML. The Elements panel shows the changed DOM text "Ready"; a script edit does not rewrite the HTML file on the server.

Question 2 of 10

For <ul><li>A</li> <li>B</li></ul>, a variable first holds the first item. What does first.nextSibling.nodeType return?
  1. 1
  2. 8
  3. 9
  4. 3
Show answerHide answer
Answer: D
The literal space between the two list items becomes a text node, so the next sibling has nodeType 3. The second item is an element with nodeType 1, but it is reached by nextElementSibling. Node navigation includes text between tags; element navigation skips it.

Question 3 of 10

How can a script select the element <section id="report"> with getElementById()?
  1. document.getElementById("report")
  2. document.getElementById("#report")
  3. document.getElementById("section#report")
  4. document.getElementById("[id=report]")
Show answerHide answer
Answer: A
Pass the bare id "report" to getElementById(). The hash belongs to CSS selector syntax; passing "#report" here searches for an id containing that character and misses this section.

Question 4 of 10

Five elements, in order A–E, all have class flag. Code saves const live = box.getElementsByClassName("flag"), then runs for (let i = 0; i < live.length; i++) { live[i].classList.remove("flag"); }. Which elements retain the class?
  1. C and E.
  2. B, C, and D.
  3. B and D.
  4. A, C, and E.
Show answerHide answer
Answer: C
Removing A shifts B into index 0, but the loop advances to index 1 and removes C. It then removes E at index 2 before the shortened length stops the loop, leaving B and D. Treating the collection as a fixed snapshot would incorrectly predict that every element loses its class.

Question 5 of 10

What appears after label.textContent = "<em>Saved</em>"?
  1. The word "Saved" in an em element.
  2. An em element with no text inside.
  3. The literal text "Saved".
  4. The literal text "Saved" without tags.
Show answerHide answer
Answer: C
textContent inserts the literal characters "Saved". It does not parse those tags into an emphasis element; use element creation or trusted HTML parsing when structure is intended.

Question 6 of 10

A button is disabled. Code runs button.setAttribute("disabled", "false"). What state does button.disabled report?
  1. false
  2. true
  3. "false"
  4. null
Show answerHide answer
Answer: B
The disabled attribute remains present, so button.disabled is true. Its text "false" cannot switch a boolean attribute off; remove it or set the boolean property to false to enable the button.

Question 7 of 10

An input currently contains "6". What does input.value + 2 produce?
  1. "62"
  2. 8
  3. "8"
  4. 62
Show answerHide answer
Answer: A
The field value is the string "6", so + 2 joins it into "62". Convert before adding with Number(input.value) + 2 to get the number 8, including when the field is a number-type input.

Question 8 of 10

A card starts with class="card compact". How can code add "selected" while preserving both existing classes?
  1. card.className = "selected"
  2. card.classList.add(".selected")
  3. card.setAttribute("class", "selected")
  4. card.classList.add("selected")
Show answerHide answer
Answer: D
classList.add("selected") preserves "card" and "compact". Assigning the whole class string replaces them, while a leading dot would become part of the added token instead of selector syntax.

Question 9 of 10

A list contains elements labeled P, Q, R. What is their order after list.append(list.firstElementChild)?
  1. P, Q, R, P.
  2. Q, R, P.
  3. R, P, Q.
  4. P, R, Q.
Show answerHide answer
Answer: B
Appending the existing first child moves P to the end, leaving Q, R, P. The same node cannot occupy both the old and new position, so the list still has three elements. A fourth P would require a separate node, for example a clone, rather than another insertion of the same object.

Question 10 of 10

makeItem() returns a new <li>. What does list.append(records.map(makeItem)) insert when records has two entries?
  1. Two li elements with their built content.
  2. One li containing both built elements.
  3. Text made from the two element objects.
  4. Two copies of the records as JSON text.
Show answerHide answer
Answer: C
map() returns an array, and append() receives that array as one argument. It converts the array to text containing the elements' string representations instead of inserting the nodes. Use list.append(...records.map(makeItem)) to pass each element as a separate argument.
Set 2: Choose the right DOM operation10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

HTML contains <table><tr><td>8</td></tr></table>. What direct child element does the parser normally place inside the table?
  1. The tr exactly as written.
  2. The td lifted out of the row.
  3. A tbody containing the row.
  4. A div wrapping the table cells.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A classic external script in <head> selects a footer written later in <body> and gets null. The selector is correct. What script attribute makes it run after HTML parsing?
  1. defer
  2. async
  3. disabled
  4. hidden
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A static NodeList contains two .note elements. One loses its note class, and a new note element is appended. What is the saved NodeList's length?
  1. 1
  2. 2
  3. 3
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

An element has data-record-id="38". How can code compare that id with the number 38 using strict equality?
  1. row.dataset.recordId === 38
  2. Number(row.getAttribute("record-id")) === 38
  3. row.dataset["record-id"] === 38
  4. Number(row.dataset.recordId) === 38
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A stylesheet sets a heading's color, and the heading has no inline color. How can code read the applied color?
  1. heading.style.color
  2. getComputedStyle(heading).color
  3. heading.getAttribute("color")
  4. heading.dataset.color
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A closed panel must keep its layout space and be hidden from screen readers. What CSS declaration fits?
  1. visibility: hidden
  2. display: none
  3. opacity: 0
  4. visibility: visible
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A container has items A and B. Code saves its first item as a, calls container.replaceChildren(), sets a.textContent = "C", then runs container.append(a) and container.append(a.cloneNode(true)). What does the container hold?
  1. One item showing "C".
  2. Two items showing "A" and "C".
  3. Two items both showing "C".
  4. Three items showing "A", "B", and "C".
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A record has score: 0. What does record.score ?? "Pending" display when assigned to textContent?
  1. "Pending"
  2. "undefined"
  3. An empty string.
  4. "0"
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

An empty list receives a fragment containing three items. Code then changes the fragment's textContent to "Reset" and appends the same fragment again. What is in the list?
  1. The three original items, then text "Reset".
  2. Only text "Reset" replacing the three items.
  3. Six items, followed by the text "Reset".
  4. The three items with every label set to "Reset".
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

The data is [{n: 14}, {n: 3}, {n: 9}]. A view must show ascending numeric order while preserving the source order. What expression builds that view?
  1. data.sort((a, b) => a.n - b.n)
  2. [...data].sort((a, b) => a.n - b.n)
  3. [...data].sort((a, b) => b.n - a.n)
  4. [...data].sort((a, b) => String(a.n).localeCompare(String(b.n)))
Show answerHide answer

Save this lesson to see this answer.

Set 3: Trace nodes, state, and collections10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A button itself has class entry, inside a section with that same class. What does button.closest(".entry") return?
  1. The outer section.
  2. The section's parent.
  3. Null; both match.
  4. The button itself.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A box starts with four .mark elements. Code saves a live class collection and a static matching NodeList. It removes the first live member, then removes the class from the new first live member. What are the final live and static lengths?
  1. Live 2, static 3.
  2. Live 3, static 4.
  3. Live 2, static 4.
  4. Live 3, static 3.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A counter displays "12". What assignment makes it display "13"?
  1. counter.textContent = Number(counter.textContent) + 1
  2. counter.textContent = counter.textContent + 1
  3. counter.textContent = counter.textContent + "1"
  4. counter.textContent = Number(counter.textContent + 1)
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

On https://example.com/catalog/, a link is written as <a href="/guide">Guide</a>. What does link.getAttribute("href") return?
  1. "https://example.com/guide"
  2. "/guide"
  3. "https://example.com/catalog/guide"
  4. "guide"
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A row begins with class="row done". Code runs row.classList.toggle("done", false) twice. What classes remain?
  1. Only "row".
  2. Both "row" and "done".
  3. Only "done".
  4. No classes remain.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A box has inline width and color. How can code remove only the inline color so the stylesheet decides it?
  1. box.style.cssText = ""
  2. box.removeAttribute("style")
  3. box.style.removeProperty("color")
  4. box.style.color = "inherit"
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A section already contains two paragraphs. At what position should insertAdjacentHTML() insert a trusted note as the section's first child?
  1. "beforebegin"
  2. "afterbegin"
  3. "beforeend"
  4. "afterend"
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

An item with id "entry" and a nested span is deeply cloned. The clone's id is changed to "entry-copy", then it is appended and the original is removed. What does a saved reference to the original span point to?
  1. The corresponding span inside the appended clone.
  2. The appended item's new parent container.
  3. Null because its original item was removed.
  4. The span inside the detached original item.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Code appends template.content directly to a list. Why does a later template.content.querySelector(".title") return null?
  1. The template became an HTML string.
  2. The class was removed during insertion.
  3. The source content moved into the list.
  4. Template content rejects class selectors.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

Tasks are [{id:31, done:true}, {id:32, done:false}, {id:33, done:false}]. Code removes only row 31 from the DOM, marks task 32 done in the array, then fully rebuilds all rows and the summary from that array. What is displayed?
  1. Three rows; "2 of 3 done".
  2. Two rows; "1 of 2 done".
  3. Three rows; "1 of 3 done".
  4. Two rows; "2 of 2 done".
Show answerHide answer

Save this lesson to see this answer.

Set 4: Build reliable data-driven displays10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A paragraph contains Hello, a nested <strong>team</strong>, and an HTML comment. Is the strong element's text a sibling of the comment?
  1. Yes; the paragraph is its parent.
  2. No; its text is a child of strong.
  3. Yes; comments are siblings of all text.
  4. No; that text is an attribute.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A page has a sidebar and a main section, each with .status. A variable main holds the main section. What search finds its first status descendant?
  1. document.querySelector(".status")
  2. main.getElementById("status")
  3. main.querySelector(".status")
  4. document.getElementById(".status")
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

Code saves const field = box.querySelector("input"). A user changes that input from "A" to "B". The script runs box.innerHTML += "<span>Done</span>" and then field.value = "C". What happens to the visible input?
  1. It is a replacement input; the last assignment changes only the detached original.
  2. It is the original input and now displays the final value "C".
  3. It is the replacement input and the saved reference redirects to it.
  4. It is removed permanently because innerHTML cannot reconstruct inputs.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

An input starts with value="2". After the user changes it to 7, a script reads Number(input.getAttribute("value")) + 1. What number does it obtain?
  1. 8
  2. 21
  3. 71
  4. 3
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A panel is hidden with panel.hidden = true, but CSS contains .panel { display: grid; } and the panel remains visible. Its control also still reports aria-expanded="true". What fixes both states for a closed panel?
  1. Keep the display rule and set aria-expanded to "false" without changing CSS.
  2. Set opacity to zero and leave aria-expanded at its current value.
  3. Remove hidden and set aria-expanded to "true" to agree with the current layout.
  4. Enforce [hidden] { display: none !important; } and set aria-expanded to "false".
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A list starts A, B, C. Code saves its first element as a, runs list.lastElementChild.before(a), then list.prepend(list.lastElementChild). What is the final order?
  1. B, A, C.
  2. C, B, A.
  3. C, A, B.
  4. A, C, B.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A record has no author property. What assignment displays "Unknown author" without parsing the value as HTML?
  1. cell.textContent = record.author ?? "Unknown author"
  2. cell.textContent = String(record.author)
  3. cell.innerHTML = record.author ?? "Unknown author"
  4. cell.textContent = record.author === null ? "Unknown author" : record.author
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A renderer clones a one-row template, appends the clone to a fragment, and only then tries clone.querySelector(".name").textContent = record.name. Why does it fail?
  1. A fragment cannot contain another fragment's children.
  2. Deep cloning removes classes from the cloned fields.
  3. Appending emptied the clone before its field was filled.
  4. A template field cannot accept textContent before display.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

An array has ids 11, 12, and 13. Code removes only id 12's <li>, then renders a filtered view from the unchanged array. The filter keeps all three objects. What must change so id 12 stays deleted after any later full render?
  1. Remove id 12 from the newly rendered DOM a second time.
  2. Remove id 12 from the source array before rendering.
  3. Keep a static NodeList that excludes the deleted element.
  4. Sort the source array before constructing the next view.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A JSON string is [{"id":24,"done":false}]. After JSON.parse(), how do the id and done types compare with an element's data-id="24"?
  1. Parsed id is numeric; done is boolean; dataset id is text.
  2. Parsed id is text; done is text; dataset id is numeric.
  3. Parsed id is numeric; done is text; dataset id is numeric.
  4. Parsed id is text; done is boolean; dataset id is text.
Show answerHide answer

Save this lesson to see this answer.

Set 5: Debug updates across the DOM10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

An item <li id="copy-me"><span>Map</span></li> is copied with cloneNode(false). What does the detached clone contain?
  1. The li and span, but without the id.
  2. The li and its id, with no span or text.
  3. The li, id, span, and the text "Map".
  4. Only a text node containing "Map".
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

An article contains <div class="card"><div class="body"><span class="label">A</span></div></div>. From the label, code uses parentElement.parentElement to find the card. A new wrapper is inserted between body and label. What replacement remains reliable?
  1. label.closest(".card")
  2. label.parentElement.parentElement
  3. label.querySelector(".card")
  4. label.nextElementSibling?.parentElement
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

An empty list receives a snapshot snap = list.querySelectorAll(".new"). Two new-class elements are appended, then a loop over snap removes class new. A fresh class search is saved as a live collection. What are snap.length and the live length?
  1. Snapshot 0, live 0.
  2. Snapshot 2, live 0.
  3. Snapshot 2, live 2.
  4. Snapshot 0, live 2.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A preview displays a comment supplied by a user. A developer filters out <script> tags first. What update keeps the preview element and inserts the comment as literal text without HTML parsing?
  1. preview.innerHTML = comment
  2. preview.textContent = comment
  3. preview.insertAdjacentHTML("beforeend", comment)
  4. preview.outerHTML = comment
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

How can code associate a label with the field whose id is "contact" using the label's DOM property?
  1. label.for = "contact"
  2. label.id = "contact"
  3. label.htmlFor = "contact"
  4. label.className = "contact"
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A row starts with class="row selected". The data says selected is false. Code runs row.classList.toggle("selected"), then later runs row.classList.toggle("selected", false). What are the two return values and the final classes?
  1. false, false; only row remains.
  2. false, true; row and selected remain.
  3. true, false; only row remains.
  4. true, true; row and selected remain.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A progress bar represents 7 completed records out of 20. What string sets its percentage width correctly?
  1. "0.35%"
  2. "35px"
  3. "35%"
  4. "7%"
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A panel starts hidden and its button reports aria-expanded "false". Twice, code saves const open = panel.hidden, sets panel.hidden = !open, and writes String(open) to aria-expanded. What are the final states?
  1. Hidden false; aria-expanded "true".
  2. Hidden true; aria-expanded "true".
  3. Hidden false; aria-expanded "false".
  4. Hidden true; aria-expanded "false".
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

For each of three records, code deeply clones a one-item template, fills its title, and appends the clone to a collecting fragment. It then calls list.replaceChildren(fragment). Which objects remain populated?
  1. The list and collecting fragment; the source template is empty.
  2. Only the source template; the list receives empty fragments.
  3. The list and source template; the collecting fragment is empty.
  4. All three; each keeps copies of the same item objects.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

Data contains {id: 5, done: false}. Its existing row has data-id="5" and a focused input. Code must mark the record done, reflect that class, and preserve the input object. What sequence fits?
  1. Convert the dataset id, update the matching object, then rebuild every row from the data.
  2. Compare the dataset id directly with numeric ids, then update the found object and row.
  3. Force the existing row's done class, leave the object unchanged, then rebuild all rows later.
  4. Convert the dataset id, update the matching object, then force the existing row's done class.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

74 Key Terms, each with its definition

A

Ancestor
An ancestor is a parent or more distant enclosing node on the path toward the document.
A list is an ancestor of the text inside a span nested within one of its list items.
append()
The append() method inserts nodes or plain text after a container's existing children, accepting several inputs in one call.
item.append(title, button) assembles two child elements inside an item before page insertion.
ARIA attribute
An ARIA attribute supplies information about an element's role, state, or name to assistive technology such as screen readers.
Setting aria-label to "Close dialog" supplies an accessible name for an icon-only button.
aria-expanded
The aria-expanded attribute describes a control's open or closed state to assistive technology using the strings "true" or "false".
When an answer panel opens, its controlling button should also report aria-expanded="true".
Array.from()
The Array.from() method creates an array from a collection, allowing array methods and a fixed membership snapshot.
Array.from(list.children) lets a removal loop visit every original child without index shifts.
Attribute
An attribute is a named HTML value on an element that can be read or changed by methods such as getAttribute() and setAttribute().
A link's href attribute can hold "/help" while its property reports a resolved full URL.

B

Boolean attribute
A boolean attribute represents true by being present and false by being absent, regardless of the text written as its value.
A button remains disabled after setAttribute("disabled", "false") because the attribute exists.

C

camelCase
camelCase joins words with later word starts capitalized, as in backgroundColor.
The CSS property background-color is written as style.backgroundColor in JavaScript.
checked
The checked property reports a checkbox's current selected state as a boolean, independently of its initial checked attribute.
After the user clears an initially selected checkbox, its checked property becomes false.
Child
A child is a node directly inside another node, without an intervening parent.
The text inside a nested span is a descendant of the list item but is the span's direct child.
Class token
A class token is one class name, separated from other names in the class attribute by whitespace.
Pass "large" and "red" separately to classList.add() so neither token contains a space.
classList
The classList property exposes an element's class tokens and methods to add, remove, test, toggle, or replace them individually.
Adding "selected" through classList keeps the existing "card" class and its base styling.
className
The className property is the whole class string; assigning it replaces every class.
Assigning "active" to a card's className removes its former "card" class unless included again.
closest()
The closest() method checks an element and its ancestors for the nearest selector match.
A span inside a product card can use closest(".product") to find its enclosing card.
Comparator
A comparator tells a sort how two values should be ordered through its return value.
The comparator (a, b) => a - b orders numeric values from smallest to largest.
Computed style
A computed style is a resolved, read-only style value exposed by the browser after applicable CSS declarations are considered.
getComputedStyle(title).color can report red even when title.style.color is empty.
createElement()
The createElement() method makes a detached element from a tag name, ready to fill and insert into the document later.
document.createElement("li") makes a list item but does not yet add it to the visible list.
Cross-site scripting
Cross-site scripting is an attack in which untrusted content causes attacker-controlled code to execute in a visitor's browser.
Using textContent for a submitted name prevents its characters from becoming injected markup.
CSS custom property
A CSS custom property is a named CSS value beginning with two dashes that rules can reference through the var() function.
Set "--accent" through style.setProperty() to change rules that reference the accent value.
CSS selector
A CSS selector is a pattern matching elements by features such as tag, class, id, or nesting.
The selector "#profile .name" finds name-class descendants of the profile element.
CSS unit
A CSS unit gives a length or other style value its meaning, such as pixels for an absolute width or percent for a relative width.
A nonzero width needs text such as "180px"; a bare number is ignored in standards mode.

D

Data attribute
A data attribute is a custom HTML attribute starting with data- that stores information for scripts without displaying it as text.
A row with data-id="26" can be matched to the object that supplied its displayed fields.
dataset
The dataset property exposes an element's data attributes as string-valued entries with names converted to camelCase.
Writing row.dataset.recordId = "26" creates or changes the attribute data-record-id.
Deep clone
A deep clone copies a node, its attributes, and its complete descendant subtree.
Cloning a populated card deeply copies its text and nested spans, as well as its original id.
defer
The defer attribute delays an external classic script until HTML parsing finishes.
A deferred script in the head can find a footer element written near the end of the body.
Derived view
A derived view is a filtered or reordered array used for display while the original data remains available for other views.
Show unfinished tasks from tasks.filter(task => !task.done) without discarding completed ones.
Detached node
A detached node is a node outside the document tree that can still exist in memory while a script holds a reference to it.
A removed item can be appended again through the variable that still refers to its object.
DevTools
DevTools is the browser's set of inspection tools for examining the current DOM, CSS rules, console output, and element properties.
Selecting a paragraph in the Elements panel makes it available as $0 in the Console.
display: none
The display: none declaration hides an element, removes its layout space, and excludes that hidden content from screen readers.
A closed answer panel using this declaration leaves no empty block in the page's layout.
Document Object Model
The Document Object Model is the browser's live tree of page objects, exposed through an API that JavaScript can read and change.
A script updates a heading through the DOM while the page remains open in the browser.
DocumentFragment
A DocumentFragment is an off-page container whose children move into the target when it is inserted, leaving the fragment empty.
A fragment collects several new rows before their children are transferred to the table body.

E

Element
An element is a DOM node representing an HTML tag, its attributes, and any child nodes.
A <ul> element can hold two <li> element children as well as whitespace text nodes.
Empty state
An empty state tells users that there are no data items to display.
A reading list with no books can show "No books yet" in place of its previous items.

F

Forced class toggle
A forced class toggle sets a class's presence from a second boolean argument, keeping the class aligned with a known state.
row.classList.toggle("done", task.done) keeps the row consistent across repeated renders.

G

getElementById()
The getElementById() method finds an element by its literal id, without CSS punctuation.
document.getElementById("result") searches for the id "result", without a leading hash.
Grouped insertion
Grouped insertion places several prepared nodes into a container through one operation, using a fragment or expanded array of nodes.
Build the item nodes first, then pass them together to replaceChildren(...items).

H

hidden
The hidden property controls ordinary hidden state, keeping the element in the DOM while default styling removes its layout space.
Set panel.hidden = false to reveal a panel that was hidden without rebuilding its contents.
HTMLCollection
An HTMLCollection is a live indexed collection of elements that updates as its defining element relationships change.
Removing a child changes the length and indexes of the collection returned by list.children.

I

In-place sorting
In-place sorting reorders the original array, so its existing references see the new order.
Copy with [...items] before calling sort() when the source order must remain unchanged.
Inline style
An inline style is a CSS declaration on an element's own style attribute.
Assigning bar.style.width = "60%" stores a calculated progress width directly on the bar.
innerHTML
The innerHTML property reads inner markup and parses assigned markup into new child nodes, replacing the existing children.
Assigning trusted "<strong>Ready</strong>" creates a new strong element inside the container.
innerText
The innerText property reads text according to rendered layout, skipping hidden descendants of a displayed element.
A visible label's innerText omits a nested note styled with display: none.
insertAdjacentHTML()
The insertAdjacentHTML() method parses trusted markup at a named position beside or inside an element, preserving existing children.
The position "beforeend" adds markup inside a container after its current last child.
insertBefore()
The insertBefore() method inserts one node before a specified child of a parent, or at the end when that reference is null.
list.insertBefore(newItem, list.firstElementChild) puts the item before the first child element.

L

Live collection
A live collection updates its membership to reflect the current DOM, so changes can alter its length and indexes during a loop.
Removing a matching class while counting forward can make a loop skip the next live member.

N

Node
A node is one object in the DOM tree, such as an element, text, comment, or the document.
The paragraph element and the text it contains occupy separate nodes in the page's tree.
NodeList
A NodeList is an indexed DOM collection with a length; the one returned by querySelectorAll() contains matching elements.
Convert the selected paragraphs with Array.from() before using the array method map().
null
null is the explicit missing-result value returned by single-element searches and tree properties when no matching object exists.
Check a missing banner reference before setting its text, because reading from null throws.
Nullish coalescing
Nullish coalescing selects a fallback with ?? only when the left-hand value is null or undefined, leaving other values unchanged.
book.author ?? "Unknown author" supplies readable text when the optional author is missing.

O

opacity: 0
The opacity: 0 declaration makes an element transparent while preserving its layout space, interaction, and screen-reader exposure.
An invisible button can still respond to clicks when only its opacity has been changed.
outerHTML
The outerHTML property includes an element's own markup; assigning it replaces the element itself with newly parsed nodes.
After replacement, a variable holding the original paragraph still refers to the detached object.

P

Parent
A parent is the node immediately above another node in the tree, containing it as a child.
In <ul><li>Read</li></ul>, the list is the immediate parent of the item element.
Parsing
Parsing turns the HTML markup read by the browser into a structured document tree.
While parsing rows directly inside a table, the browser can insert a <tbody> element.
prepend()
The prepend() method inserts nodes or plain text before a container's existing children, moving nodes that already exist elsewhere.
Prepending a list's last element moves that same object to the first position without copying it.
Property
A property is a named value or method on an element's JavaScript object, sometimes exposing processed or typed attribute data.
The disabled property returns a boolean, while an attribute read returns a string or null.

Q

querySelector()
The querySelector() method returns the first element matching a CSS selector within its search area, or null if none matches.
Searching within one card keeps a repeated .name class elsewhere from becoming the result.

R

Render function
A render function builds or updates the visible DOM from current data and can be called again after that data changes.
Calling the function after marking a task done updates both the task's class and a displayed count of completed tasks.
Rendering
Rendering is the process of building or updating DOM content from data so the visible page represents the current values.
A book array becomes a list by building one item with a title and page count per object.
replaceChildren()
The replaceChildren() method replaces a container's children with supplied nodes or text, or clears them when no inputs are supplied.
A full render uses this method to remove old items and install the newly built display.
replaceWith()
The replaceWith() method replaces the selected node itself with supplied nodes or text in its current position.
A loading paragraph can be replaced by a completed list while the surrounding page stays in place.

S

Sibling
A sibling is a node sharing the same immediate parent as another node in the DOM tree.
Two adjacent list items are element siblings even if a whitespace text node lies between them.
Source of truth
A source of truth is the data kept as the authoritative state from which the page's displayed items and summaries are derived.
Deleting an object from an array of tasks that a list is rendered from prevents the next render from restoring its list item.
Spread syntax
Spread syntax expands an iterable into separate values, allowing an array of nodes to be passed as individual method arguments.
list.append(...items) inserts each built node instead of the array's string representation.
Static collection
A static collection keeps the membership it had when created, even if matching elements are later added or removed from the page.
A saved querySelectorAll() result omits a fourth item appended after the original search.

T

Table body
A table body is the <tbody> section grouping a table's data rows.
A renderer appends each new <tr> to the selected body and fills its fields in <td> cells.
Targeted update
A targeted update changes an existing element to match changed data while retaining the element object and its local state.
Updating one row's done class can preserve focus that would be lost if all rows were rebuilt.
Template content
Template content is the separate DocumentFragment at template.content, holding the template's undisplayed child structure.
A document search misses a title span inside the template; search within its content or a clone.
Template element
A template element holds parsed HTML that is not displayed, providing a reusable source for copies of a repeated structure.
A card template can supply the same title and detail spans for each object in a data array.
Text node
A text node holds DOM characters, including visible words or whitespace between tags.
An indented list can start with a text node containing a line break before its first item.
textContent
The textContent property reads descendant text and, when assigned, replaces child nodes with plain text without parsing HTML.
Assigning "Ready" displays those characters and does not create an emphasis element.
toFixed()
The toFixed() method returns a number as a string with a specified number of decimal places, suitable for formatted display.
A temperature of 28.5 becomes the string "28.5" with toFixed(1) before insertion into a cell.
Type conversion
Type conversion changes a value's representation, such as turning numeric text from a field or data attribute into a number.
Number(row.dataset.id) lets strict equality compare the stored digits with a numeric object id.

V

value
The value property holds a form control's current value as a string, which can differ from its starting HTML attribute.
Reading a quantity field after editing returns the current typed text, not its original value.
visibility: hidden
The visibility: hidden declaration hides an element while preserving its layout space and hiding it from screen readers.
An invisible status box can reserve its usual space so the surrounding content does not move.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started