π¨ JavaScript Β· Topic 16 of 20
DOM Manipulation β JavaScript Questions with Answers
30 practice questions on DOM Manipulation, each with the correct answer and a clear explanation of why it is right. Read through them as revision notes, or take this topic as an interactive quiz to test yourself.
2 easy Β· 21 medium Β· 7 hard Β· Part of JavaScript: The language of the web - basics to advanced, 20 topics.
Medium questions (21)
Q3. medium
What does querySelector return when nothing matches?
const el = document.querySelector(".missing");
if (el) el.textContent = "hi";
el?.classList.add("on");
- A null
- B undefined
- C An empty array
- D An error
Answer: A β null
It returns null, so accessing a property on it throws 'Cannot read properties of null' - the most common DOM error. Check first or use optional chaining.
nullError handling
Q4. medium
What is the difference between getElementById and querySelector?
- A getElementById takes a bare id (no #) and is slightly faster; querySelector takes any CSS selector
- B They are identical
- C querySelector only works on classes
- D getElementById returns an array
Answer: A β getElementById takes a bare id (no #) and is slightly faster; querySelector takes any CSS selector
getElementById("title") vs querySelector("#title"). The speed difference is negligible in practice; querySelector's flexibility usually wins.
getElementByIdquerySelector
Q6. medium
How do you change the visible TEXT of an element safely?
- A el.textContent = "Hello"
- B el.value = "Hello"
- C el.text = "Hello"
- D el.write("Hello")
Answer: A β el.textContent = "Hello"
textContent sets plain text and escapes any HTML - safe against injection. innerText is similar but slower (it respects CSS visibility).
textContentinnerText
Q8. medium
How do you read what a user typed into an <input>?
const n = Number(document.querySelector("#age").value);
- A input.value
- B input.textContent
- C input.innerHTML
- D input.text
Answer: A β input.value
Form controls store their current data in .value (always a STRING - convert with Number() for maths). textContent would be empty for inputs.
Formsvalue
Q9. medium
How do you add a CSS class to an element?
el.classList.add("active");
el.classList.remove("hidden");
el.classList.toggle("open");
el.classList.contains("active"); // true/false
- A el.classList.add("active")
- B el.class += "active"
- C el.addClass("active")
- D el.style.class = "active"
Answer: A β el.classList.add("active")
classList provides add, remove, toggle and contains - far safer than overwriting className, which would wipe existing classes.
classListCSS
Q10. medium
What does classList.toggle("open") do?
- A Adds the class if absent, removes it if present - and returns true/false
- B Always adds it
- C Always removes it
- D Renames the class
Answer: A β Adds the class if absent, removes it if present - and returns true/false
Perfect for menus, modals and dark mode. An optional second argument forces the state: toggle("open", isOpen).
classListtoggle
Q11. medium
How do you change an inline style?
el.style.backgroundColor = "red";
el.style.fontSize = "20px";
- A el.style.backgroundColor = "red" (camelCase property names)
- B el.style.background-color = "red"
- C el.css("background-color", "red")
- D el.setStyle("background", "red")
Answer: A β el.style.backgroundColor = "red" (camelCase property names)
CSS hyphenated names become camelCase in JavaScript because background-color would be parsed as subtraction. Prefer toggling classes over many inline styles.
styleCSS
Q12. medium
How do you create a new element and add it to the page?
const li = document.createElement("li");
li.textContent = "New item";
document.querySelector("ul").appendChild(li);
- A const li = document.createElement("li"); parent.appendChild(li);
- B document.newElement("li")
- C parent.add("<li>")
- D document.write("<li>")
Answer: A β const li = document.createElement("li"); parent.appendChild(li);
createElement builds a detached element; it appears only once inserted with appendChild, append, prepend or insertBefore.
createElementappendChild
Q13. medium
What is the difference between append() and appendChild()?
- A append() accepts multiple nodes AND plain strings; appendChild() takes exactly one node and returns it
- B They are identical
- C appendChild is newer
- D append only works on text
Answer: A β append() accepts multiple nodes AND plain strings; appendChild() takes exactly one node and returns it
append is the modern, more flexible method: parent.append(el, "text", el2). appendChild remains widely used and returns the appended node.
appendappendChild
Q14. medium
How do you REMOVE an element from the page?
- A el.remove()
- B el.delete()
- C document.remove(el)
- D el.destroy()
Answer: A β el.remove()
The modern one-liner. The older equivalent was el.parentNode.removeChild(el).
removeDOM manipulation
Q15. medium
How do you read a data attribute?
<div data-user-id="42">
const id = Number(el.dataset.userId); // 42
- A el.dataset.userId
- B el.data("user-id")
- C el.userId
- D el.attributes.userId
Answer: A β el.dataset.userId
dataset exposes all data-* attributes, converting kebab-case to camelCase. Values are always strings.
datasetAttributes
Q16. medium
What is the difference between setAttribute and setting a property?
- A setAttribute changes the HTML attribute; properties reflect the CURRENT state (they can diverge, e.g. input.value)
- B They are identical
- C setAttribute is deprecated
- D Properties do not exist
Answer: A β setAttribute changes the HTML attribute; properties reflect the CURRENT state (they can diverge, e.g. input.value)
After a user types, input.value shows the typed text while getAttribute("value") still shows the original HTML default. Use properties for live state.
Attributes vs properties
Q18. medium
What does el.closest(".card") do?
btn.closest(".card").remove();
- A Walks UP the tree from el and returns the nearest ancestor (or el itself) matching the selector
- B Finds the nearest child
- C Returns the sibling
- D Measures distance in pixels
Answer: A β Walks UP the tree from el and returns the nearest ancestor (or el itself) matching the selector
closest is invaluable in event delegation: from a clicked button you can find the containing card or row.
closestTraversal
Q19. medium
Which property gives an element's PARENT?
- A el.parentElement
- B el.parent
- C el.up
- D el.container
Answer: A β el.parentElement
Traversal properties: parentElement, children, firstElementChild, nextElementSibling. (The older node-based versions like childNodes also include text nodes.)
TraversalparentElement
Q21. medium
What does the DOMContentLoaded event mean?
- A The HTML is parsed and the DOM is ready - images may still be loading
- B Everything including images has loaded
- C The page is closing
- D CSS finished
Answer: A β The HTML is parsed and the DOM is ready - images may still be loading
DOMContentLoaded fires early (ideal for setup); the window 'load' event waits for all images and stylesheets, so it fires much later.
DOMContentLoadedload
Q22. medium
How do you loop over the result of querySelectorAll?
document.querySelectorAll(".item").forEach(el => el.classList.add("on"));
const texts = [...document.querySelectorAll("li")].map(li => li.textContent);
- A forEach or for...of - NodeLists support both
- B Only a classic for loop
- C map() directly
- D You cannot loop it
Answer: A β forEach or for...of - NodeLists support both
NodeList has forEach and is iterable. It does NOT have map/filter - convert with Array.from(list) or [...list] first.
NodeListArray.from
Q23. medium
What does insertAdjacentHTML("beforeend", html) do?
- A Inserts HTML at a precise position without destroying existing children/listeners
- B Replaces all content
- C Deletes the element
- D Only works with text
Answer: A β Inserts HTML at a precise position without destroying existing children/listeners
Unlike innerHTML += (which re-parses everything and kills existing event listeners), this inserts efficiently. Positions: beforebegin, afterbegin, beforeend, afterend.
insertAdjacentHTMLPerformance
Q25. medium
How do you check whether an element has a class?
- A el.classList.contains("active")
- B el.hasClass("active")
- C el.class === "active"
- D el.classList === "active"
Answer: A β el.classList.contains("active")
contains returns a boolean. Comparing className directly fails whenever an element has multiple classes.
classList
Q27. medium
How do you hide an element with JavaScript?
- A Toggle a CSS class, or set el.style.display = "none"
- B el.hide()
- C el.visible = false
- D delete el
Answer: A β Toggle a CSS class, or set el.style.display = "none"
Adding a class like .hidden { display: none } keeps styling in CSS where it belongs and is easy to animate or override.
styleclassList
Q29. medium
Which is generally better: building HTML strings or creating elements?
- A Strings are concise for static markup; createElement is safer with dynamic user data
- B Strings are always better
- C createElement is always better
- D They are identical
Answer: A β Strings are concise for static markup; createElement is safer with dynamic user data
Template literals are readable for trusted content, but inserting user data via innerHTML risks XSS. createElement + textContent escapes automatically.
SecurityBest practices
Q30. medium
How do you clear all children of an element?
- A el.replaceChildren() (or el.innerHTML = "")
- B el.clear()
- C el.empty()
- D delete el.children
Answer: A β el.replaceChildren() (or el.innerHTML = "")
replaceChildren() with no arguments removes everything cleanly and is the modern approach; innerHTML = "" also works and is widely used.
replaceChildreninnerHTML
Hard questions (7)
Q5. hard
What is the difference between an HTMLCollection (getElementsByClassName) and a NodeList (querySelectorAll)?
- A HTMLCollection is LIVE (updates automatically); querySelectorAll's NodeList is a static snapshot
- B They are the same
- C NodeList is live
- D HTMLCollection is an array
Answer: A β HTMLCollection is LIVE (updates automatically); querySelectorAll's NodeList is a static snapshot
A live collection reflects later DOM changes, which can cause surprising loop behaviour. Static NodeLists are predictable and support forEach.
NodeListLive collections
Q7. hard
Why is innerHTML risky with user-supplied data?
el.textContent = userInput; // safe
el.innerHTML = userInput; // dangerous!
- A It parses the string as HTML, allowing script/event injection (XSS)
- B It is slower only
- C It does not work in modern browsers
- D It is not risky
Answer: A β It parses the string as HTML, allowing script/event injection (XSS)
Inserting untrusted input via innerHTML lets attackers run code in your users' browsers. Use textContent for text; sanitise if HTML is genuinely required.
XSSSecurity
Q17. hard
Why should you avoid updating the DOM inside a long loop?
const frag = document.createDocumentFragment();
items.forEach(t => { const li = document.createElement("li"); li.textContent = t; frag.append(li); });
list.append(frag); // one DOM update
- A Each change can force reflow/repaint - build the content first, then insert once
- B Loops cannot touch the DOM
- C It causes syntax errors
- D There is no problem
Answer: A β Each change can force reflow/repaint - build the content first, then insert once
A thousand appendChild calls means a thousand layout recalculations. Build an HTML string or a DocumentFragment and insert it in one operation.
PerformanceDocumentFragment
Q20. hard
Why does this return null?
<script src="app.js"></script> is in <head>, and app.js runs document.querySelector("#btn")
<script src="app.js" defer></script>
// or
document.addEventListener("DOMContentLoaded", init);
- A The script runs BEFORE the body elements are parsed, so #btn does not exist yet
- B querySelector is broken
- C The id is wrong
- D Scripts cannot use querySelector
Answer: A β The script runs BEFORE the body elements are parsed, so #btn does not exist yet
Fix by adding defer to the script tag, moving it before </body>, or wrapping the code in a DOMContentLoaded listener.
deferDOMContentLoaded
Q24. hard
What is wrong with el.innerHTML += "<li>x</li>" in a loop?
- A It re-parses ALL the HTML each time and destroys existing elements' event listeners
- B It is a syntax error
- C It only adds text
- D Nothing is wrong
Answer: A β It re-parses ALL the HTML each time and destroys existing elements' event listeners
The += reads, concatenates and re-writes the entire subtree, which is slow and replaces existing nodes with new ones (losing listeners and input state).
innerHTMLPerformance
Q26. hard
What does document.querySelector("#list").children give you?
- A An HTMLCollection of ELEMENT children only (ignoring text/whitespace nodes)
- B All descendants
- C Text nodes too
- D An array
Answer: A β An HTMLCollection of ELEMENT children only (ignoring text/whitespace nodes)
children skips text nodes, unlike childNodes which includes the whitespace between tags - a classic source of unexpected counts.
childrenchildNodes
Q28. hard
What is the difference between offsetWidth and getBoundingClientRect().width?
- A offsetWidth is a rounded integer; getBoundingClientRect gives precise fractional values including transforms
- B They are identical
- C offsetWidth includes margins
- D getBoundingClientRect only works on images
Answer: A β offsetWidth is a rounded integer; getBoundingClientRect gives precise fractional values including transforms
Use getBoundingClientRect for precise measurement and viewport position (it also returns top/left/right/bottom).
MeasurementsLayout