- Learn
- Computer Science & IT
- Programming
- JavaScript Programming
- Document Object Model
The Document Object Model
BeginnerLesson 5 of 9 in JavaScript Programming Course
What you will learn
- 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 nodesExplain 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 siblingsWalk 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.
- Describe the DOM as a tree of nodes
- 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 elementFind 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 resultsSelect every matching element, loop over the results, convert them to an array when needed, and know when a collection updates by itself.
- Select a single element
- 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 elementRead and replace an element's content with textContent, innerText and innerHTML, and keep pages safe from injected code.
- Read and set attributes and propertiesWork 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 fieldsKeep extra information on elements with data attributes, and read and set the current value of form fields.
- Change the text and HTML inside an element
- 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 classesChange an element's appearance by switching classes with classList, and keep the visual rules in the stylesheet.
- Set inline styles and read computed stylesSet 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 pageHide 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.
- Add, remove and toggle CSS classes
- 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 themCreate 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 elementsTake elements out of the page, empty a container, replace one element with another, and make copies with cloneNode.
- Create new elements and insert them
- 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 arrayTurn 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 templatesInsert 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 changesKeep 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.
- Build list items and table rows from an array
Document Object Model study guide
1Read 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.
2Find 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.
3Update 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, ornullif 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.
4Control 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.
5Build 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.
6Turn 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
1Find your way through a page tree
11 steps
- 1IntroductionStart 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.
- 2ExplanationEnter through document
The DOM is the browser's live page model.
documentprovides access;document.bodyrefers 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.
- 3ExampleSee 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
nodeTypeto 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. - 4ExampleCount 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.lengthis therefore 2, whilechildNodes.lengthis 5. The extra nodes come from the characters between tags. - 5ChallengePredict the first node
In the indented list from the previous slide, what kind of node is
tasks.firstChild, and why is it different fromtasks.firstElementChild?---reveal---
firstChildis the whitespace text node before the first item.firstElementChildskips that node and returns the first<li>. Indentation affects the node sequence even though it does not create another visible task. - 6TipInspect the current tree
Choose "Inspect" on the first list item. The Elements panel shows the current DOM. With the item selected,
$0in the Console refers to it, andconsole.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. - 7ExampleMove to the next element
Save the list as
tasks, then follow one element relationship:const first = tasks.firstElementChild; const second = first.nextElementSibling;second.textContentis "Call Sam". Readingfirst.nextSiblinginstead 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. - 8ExplanationStop safely at the last item
The last item's
nextElementSiblingisnull. Reading a property directly from that missing reference throws a TypeError.second.nextElementSibling?.textContentstops there and givesundefined, without adding a node or supplying an empty string. - 9ConnectionFind 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. - 10ChallengeNavigate 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. - 11RecapTry 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.
2Select and update the intended elements
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionChoose a search before changing a page
- 2ExplanationUse the id syntax for the method
- 3ExampleKeep a repeated class inside its card
- 4TipDistinguish a missing node from bad syntax
- 5ChallengeExplain an early null result
- 6ExampleLoop through the returned elements
- 7ExplanationConvert when you need array methods
- 8ExampleCompare two saved collections
- 9Key InsightWatch a shrinking live collection
- 10ChallengeChoose the right combined selector
- 11ChallengeCombine scope with safe iteration
- 12RecapDiagnose your next failed search
3Update a profile and read its form values
15 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionSeparate the information an element holds
- 2ExampleRead text without its tags
- 3ExplanationTreat outside values as characters
- 4ChallengePreserve an existing input
- 5ConnectionChoose a rendered-text read when needed
- 6ExampleRead a written URL or a resolved URL
- 7ExplanationSet a boolean through presence
- 8ChallengeRepair a disabled control
- 9ExampleTurn a data name into a property name
- 10ExplanationRead current field content
- 11ChallengeCalculate with a field value
- 12ExampleFind an object using a displayed id
- 13TipGive an icon control a readable name
- 14ChallengeConnect data lookup to safe display
- 15RecapInspect a field before and after editing
4Make appearance follow a clear state
13 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionLet data choose the visible state
- 2ExampleKeep a base class while adding a state
- 3ExplanationUse a condition for repeated updates
- 4ChallengePredict two repeated updates
- 5ExampleCalculate a CSS percentage
- 6ExplanationRead the result of the cascade
- 7ChallengeRestore the stylesheet color
- 8ExplanationChoose what a closed panel removes
- 9ExampleMake a panel and its control agree
- 10TipCheck a style that defeats hidden
- 11ChallengeDiagnose an invisible but active control
- 12ChallengeCombine classes with a calculated value
- 13RecapCheck state and appearance separately
5Build items and follow their object references
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionBuild a task item in separate stages
- 2ExampleCreate a detached item
- 3ExampleInsert the finished item
- 4ExplanationChoose children or siblings
- 5ExampleMove a node that already exists
- 6ChallengeExplain a one-item loop result
- 7ExplanationDetach without destroying a reference
- 8ExampleReplace one item or a whole display
- 9TipCopy descendants and check ids
- 10ChallengePredict a shallow copy
- 11ChallengeMove the original and insert a copy
- 12RecapTrace identities alongside markup
6Render a list from data and update it reliably
17 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionMake the array drive the display
- 2ExampleWrite a builder for one book
- 3ExampleExpand the array into node arguments
- 4TipKeep missing data readable
- 5ConnectionApply the pattern to table rows
- 6ChallengeFind why the list shows object text
- 7ExampleCollect nodes away from the page
- 8ExplanationKeep repeated structure in HTML
- 9ExampleFill before transferring the clone
- 10ChallengePreserve the reusable template
- 11ExplanationReplace the display from current data
- 12ExampleUpdate an object and its summary
- 13ExplanationKeep a sorted view separate
- 14ChallengeExplain an item that returns after deletion
- 15Key InsightPreserve local input state
- 16ChallengeCombine a lookup with a targeted update
- 17RecapRun a three-state rendering check
Document Object Model practice questions
Set 1: Read and change a page10 Practice Questions
Question 1 of 10
Show answerHide answer
Question 2 of 10
<ul><li>A</li> <li>B</li></ul>, a variable first holds the first item. What does first.nextSibling.nodeType return?Show answerHide answer
nextElementSibling. Node navigation includes text between tags; element navigation skips it.Question 3 of 10
<section id="report"> with getElementById()?Show answerHide answer
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
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?Show answerHide answer
Question 5 of 10
label.textContent = "<em>Saved</em>"?Show answerHide answer
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
button.setAttribute("disabled", "false"). What state does button.disabled report?Show answerHide answer
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
input.value + 2 produce?Show answerHide answer
+ 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
class="card compact". How can code add "selected" while preserving both existing classes?Show answerHide answer
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
list.append(list.firstElementChild)?Show answerHide answer
Question 10 of 10
makeItem() returns a new <li>. What does list.append(records.map(makeItem)) insert when records has two entries?Show answerHide answer
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
<table><tr><td>8</td></tr></table>. What direct child element does the parser normally place inside the table?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
<head> selects a footer written later in <body> and gets null. The selector is correct. What script attribute makes it run after HTML parsing?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
.note elements. One loses its note class, and a new note element is appended. What is the saved NodeList's length?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
data-record-id="38". How can code compare that id with the number 38 using strict equality?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
container.replaceChildren(), sets a.textContent = "C", then runs container.append(a) and container.append(a.cloneNode(true)). What does the container hold?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
score: 0. What does record.score ?? "Pending" display when assigned to textContent?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
[{n: 14}, {n: 3}, {n: 9}]. A view must show ascending numeric order while preserving the source order. What expression builds that view?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
entry, inside a section with that same class. What does button.closest(".entry") return?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
.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?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
https://example.com/catalog/, a link is written as <a href="/guide">Guide</a>. What does link.getAttribute("href") return?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
class="row done". Code runs row.classList.toggle("done", false) twice. What classes remain?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
insertAdjacentHTML() insert a trusted note as the section's first child?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
template.content directly to a list. Why does a later template.content.querySelector(".title") return null?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
[{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?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
Hello, a nested <strong>team</strong>, and an HTML comment. Is the strong element's text a sibling of the comment?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
.status. A variable main holds the main section. What search finds its first status descendant?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
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?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
value="2". After the user changes it to 7, a script reads Number(input.getAttribute("value")) + 1. What number does it obtain?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
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?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
a, runs list.lastElementChild.before(a), then list.prepend(list.lastElementChild). What is the final order?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
author property. What assignment displays "Unknown author" without parsing the value as HTML?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
clone.querySelector(".name").textContent = record.name. Why does it fail?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
<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?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
[{"id":24,"done":false}]. After JSON.parse(), how do the id and done types compare with an element's data-id="24"?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
<li id="copy-me"><span>Map</span></li> is copied with cloneNode(false). What does the detached clone contain?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
<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?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
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?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
<script> tags first. What update keeps the preview element and inserts the comment as literal text without HTML parsing?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
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?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
const open = panel.hidden, sets panel.hidden = !open, and writes String(open) to aria-expanded. What are the final states?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
list.replaceChildren(fragment). Which objects remain populated?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
{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?Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
Ancestor
append()
item.append(title, button) assembles two child elements inside an item before page insertion.ARIA attribute
aria-label to "Close dialog" supplies an accessible name for an icon-only button.aria-expanded
aria-expanded="true".Array.from()
Array.from(list.children) lets a removal loop visit every original child without index shifts.Attribute
getAttribute() and setAttribute().href attribute can hold "/help" while its property reports a resolved full URL.B
Boolean attribute
setAttribute("disabled", "false") because the attribute exists.C
camelCase
backgroundColor.background-color is written as style.backgroundColor in JavaScript.checked
checked property becomes false.Child
Class token
classList.add() so neither token contains a space.classList
classList keeps the existing "card" class and its base styling.className
className removes its former "card" class unless included again.closest()
closest(".product") to find its enclosing card.Comparator
(a, b) => a - b orders numeric values from smallest to largest.Computed style
getComputedStyle(title).color can report red even when title.style.color is empty.createElement()
document.createElement("li") makes a list item but does not yet add it to the visible list.Cross-site scripting
textContent for a submitted name prevents its characters from becoming injected markup.CSS custom property
var() function."--accent" through style.setProperty() to change rules that reference the accent value.CSS selector
"#profile .name" finds name-class descendants of the profile element.CSS unit
D
Data attribute
data- that stores information for scripts without displaying it as text.data-id="26" can be matched to the object that supplied its displayed fields.dataset
row.dataset.recordId = "26" creates or changes the attribute data-record-id.Deep clone
defer
Derived view
tasks.filter(task => !task.done) without discarding completed ones.Detached node
DevTools
$0 in the Console.display: none
Document Object Model
DocumentFragment
E
Element
<ul> element can hold two <li> element children as well as whitespace text nodes.Empty state
F
Forced class toggle
row.classList.toggle("done", task.done) keeps the row consistent across repeated renders.G
getElementById()
document.getElementById("result") searches for the id "result", without a leading hash.Grouped insertion
replaceChildren(...items).H
hidden
panel.hidden = false to reveal a panel that was hidden without rebuilding its contents.HTMLCollection
list.children.I
In-place sorting
[...items] before calling sort() when the source order must remain unchanged.Inline style
bar.style.width = "60%" stores a calculated progress width directly on the bar.innerHTML
"<strong>Ready</strong>" creates a new strong element inside the container.innerText
innerText omits a nested note styled with display: none.insertAdjacentHTML()
insertBefore()
list.insertBefore(newItem, list.firstElementChild) puts the item before the first child element.L
Live collection
N
Node
NodeList
querySelectorAll() contains matching elements.Array.from() before using the array method map().null
Nullish coalescing
?? 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
outerHTML
P
Parent
<ul><li>Read</li></ul>, the list is the immediate parent of the item element.Parsing
<tbody> element.prepend()
Property
disabled property returns a boolean, while an attribute read returns a string or null.Q
querySelector()
null if none matches..name class elsewhere from becoming the result.R
Render function
Rendering
replaceChildren()
replaceWith()
S
Sibling
Source of truth
Spread syntax
list.append(...items) inserts each built node instead of the array's string representation.Static collection
querySelectorAll() result omits a fourth item appended after the original search.T
Table body
<tbody> section grouping a table's data rows.<tr> to the selected body and fills its fields in <td> cells.Targeted update
Template content
template.content, holding the template's undisplayed child structure.Template element
Text node
textContent
toFixed()
toFixed(1) before insertion into a cell.Type conversion
Number(row.dataset.id) lets strict equality compare the stored digits with a numeric object id.V
value
visibility: hidden
About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.