The browser receives an HTML file, reads it top to bottom and builds a tree of objects in memory from it. Every tag becomes a node, and every node knows its parent and its children.
That tree has a root, and JavaScript holds it in one variable available everywhere in the page: document.
Definition
document is the object the browser provides to stand for the page loaded in the tab. It is the single entry point used to read the content, change it, add elements or listen to what the visitor does.
document.title = "Cart (2)";
console.log(document.querySelector("h1").textContent);
console.log(document.body.children.length);It is not a library to install: the object exists as soon as a script runs inside a page. It exists nowhere else though, neither under Node.js nor inside a Web Worker.
The entry points of the tree
| Property | What it points at |
|---|---|
document.documentElement | The <html> tag, root of the document |
document.head | The <head> tag and its metadata |
document.body | The <body> tag, so every visible piece of content |
document.title | The tab title, readable and writable alike |
document.activeElement | The element holding focus at this very moment |
Finding and creating
Any work on a page starts by locating an element, or by building one.
- querySelector() hands back the first element matching a CSS selector, getElementById() searches by identifier.
querySelectorAllhands back every matching element, in a list frozen at the moment of the call.- createElement() builds a detached element, which still has to be inserted into the tree before anyone can see it.
const row = document.createElement("li");
row.textContent = "Mechanical keyboard";
document.querySelector("#cart").append(row);Waiting for the HTML to be ready
A script placed in the <head> runs before the body of the page exists: every search then hands back null, without the slightest error message. There are two answers to that.
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#cart").classList.add("ready");
});
console.log(document.readyState); // "loading", "interactive" or "complete"The simplest one stays the defer attribute on the script tag, which pushes execution back until the HTML has been fully parsed. The DOMContentLoaded event is for the cases where the location of the script is out of your hands.
Frequently asked questions
How does it differ from window?
window stands for the whole tab: its address, its history, its size, its timers. document only stands for the HTML page displayed inside it. The former contains the latter, reachable through window.document, and one tab can load several documents in a row without ever changing window.
Why do my searches hand back null?
In the vast majority of cases, the script runs before the element being searched for has been parsed. Add defer to the script tag. If the problem survives that, read the selector again: a forgotten class and a typo both produce exactly the same null.
Is document.write still usable?
It still works, but nothing justifies it any more. Called after the page has loaded, it wipes the entire existing document before writing. Browsers even neutralize it inside scripts inserted asynchronously. Go through createElement or through innerHTML instead.