completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Bindings: how DOM objects exist in JavaScript

Learn how Web IDL turns browser objects into JavaScript wrappers with conversions, prototypes, live collections, DOMException, and legacy quirks.

By the end, you can
  • 01
    Read a bindingExplain how Web IDL lets your JavaScript use a browser feature, and how to read a short Web IDL line.
  • 02
    Predict conversionsSay what a DOMString, a USVString, and a sequence input do to the value you pass in.
  • 03
    Debug browser objectsSpot the same-object rule, prototypes, live and static lists, DOMException, and document.all.

A browser API is a binding

Your code can call document.createElement, read document.body, and build a URL. These are not part of the JavaScript language itself. They come from the browser.

The browser is a big program. Most of it is written in other languages, such as C++ or Rust. Its own objects, like the page and its elements, live inside that program. We call them platform objects.

So how does your JavaScript reach them? Through a binding. A binding is the set of rules that makes a browser feature show up in JavaScript. It decides:

  • which objects, methods, and properties your code can see,
  • how the values you pass in are converted,
  • which object you get back when you read something twice,
  • which error is thrown when something goes wrong,
  • a few odd old rules kept so old websites still work.
The main idea

Web IDL is a small language that web standards use to write these rules down. It lists what a browser feature offers and how each part looks in JavaScript. Behind those rules, the browser can be written in C++, Rust, or any other language.

This lesson follows How input reaches your event handlers. Your event handlers are JavaScript functions. But the event objects, the targets, and the document methods they use all reach you through bindings. After this lesson, Realms, globals & WindowProxy explains why each page and each iframe gets its own set of these objects.

Web IDL is the printed manual

Think of a TV remote. You press a button, and the electronics inside the TV do the work. The printed manual says what each button accepts and what it does.

Real-life analogyThe TV remote

You can change the channel without ever seeing the circuits. You only need the buttons. The manual tells you what each button does, so you know what will happen before you press it.

In real life: Pressing a remote button
In JavaScript: Calling a method in JavaScript
In real life: The electronics inside the TV
In JavaScript: The browser's own code and objects
In real life: The printed manual
In JavaScript: The Web IDL description of a feature
In real life: Each button takes one kind of input
In JavaScript: The binding converts and checks what you pass in

A Web IDL description is called an interface. It lists two kinds of parts:

  • attributes, which become properties you can read, like document.body,
  • operations, which become methods you can call, like setAttribute.

Web IDL also says how each value you pass in is converted before the method runs. If the conversion fails, the method does not run at all. This explains many browser surprises.

Read a small Web IDL descriptionJavaScript
// Simplified Web IDL. The real DOM standard has much more.
interface Element {
  undefined setAttribute(DOMString name, DOMString value);
};

const box = document.createElement("div");
box.setAttribute("data-count", 5);
console.log(box.getAttribute("data-count"));

Lines 2 to 4 are a short, simplified piece of Web IDL. They are not copied from the real DOM standard. Read them like this:

  1. Line 2, interface Element, names the kind of object: an element on the page.
  2. Line 3 describes the setAttribute method. The first word, undefined, is what it gives back: nothing useful. Inside the brackets are its two inputs, a name and a value.
  3. Both inputs have the type DOMString. That means "turn this into a JavaScript string first".

Lines 6 to 8 are normal JavaScript. Line 7 passes the number 5 as the value. The binding turns it into the string "5" before the browser stores it. Line 8 reads the attribute back and prints 5, which is now text.

box.setAttribute with the number 5. The number 5 goes through the binding, which follows the DOMString rule, and comes out as the text 5.box.setAttribute("data-count", 5)5numberbindingDOMString rule"5"text
Your code passes the number 5. The binding follows the DOMString rule and turns it into the text "5" before the browser stores it.

To read any Web IDL line, go in this order: the kind of object, the method name, what it gives back, then the type of each input. Web IDL tells you what your JavaScript will see. It does not say how the browser is built inside.

How values are converted: DOMString, USVString, sequences

A browser method does not take just any value as it is. Each input has a Web IDL type, and the binding converts your value to that type first. This section covers three common types.

First, a word about how JavaScript stores text. A string is a row of small pieces called code units. Most characters use one piece. Some characters, like many emoji, need a pair of two pieces. One half of a pair left on its own is called a lone surrogate. It is not a complete character.

  • A DOMString is a normal JavaScript string. It can hold anything a string can, even a lone surrogate.
  • A USVString is stricter. Every character must be complete, so each lone surrogate is swapped for the replacement character, U+FFFD (it looks like �).
A number becomes a DOMStringPop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");box.setAttribute("data-count", 5);console.log(typeof box.getAttribute("data-count"));

Line 1 creates a div element. It is not on the page yet. Line 2 gives setAttribute the number 5. The binding turns it into the string "5". So line 3 prints string, not number.

Step through: the number 5 becomes a string
Step 0 of 4Ready
Your turn: follow the blue line

A small model of what setAttribute does with the number 5: each line is one step of the conversion.

Running in
  1. script
Next: line 1
const text = String(value);const attribute = text;console.log(typeof attribute);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
A URL uses USVString textPop out in the code editor (opens in a new tab)JavaScript
const url = new URL("https://shop.example/?q=\uD800");console.log(url.search);

Line 1 builds a URL. The text ends with \uD800, which is a lone surrogate. The URL input is a USVString, so the binding swaps the lone surrogate for U+FFFD. Line 2 prints ?q=%EF%BF%BD. That is how a URL writes U+FFFD: as three bytes, each written as % and two letters or digits.

Why the difference? A DOMString keeps your string exactly as it is. A USVString makes a DOMString first, then swaps each lone surrogate for U+FFFD. URLs need complete characters, so they use the stricter type.

This does not mean you should clean every string in your app. It is a rule for one input of one method. When you are unsure, ask: what Web IDL type does this input have? The answer tells you how your value will be converted.

The third type is a sequence. A sequence is a list. The binding builds it from any iterable: a value you can loop over with for...of, such as an Array or a Set.

A sequence accepts an iterablePop out in the code editor (opens in a new tab)JavaScript
const snacks = new Set(["tea", "cake"]);const blob = new Blob(snacks);console.log(blob.size);

Line 1 makes a Set with two strings. A Set is iterable. Line 2 passes it to Blob, which holds a chunk of data. Its input is a sequence, so the binding loops over the Set and builds a list of parts. Line 3 prints 7, the size in bytes: tea is 3 bytes and cake is 4.

An iterable becomes a copied listPop out in the code editor (opens in a new tab)JavaScript
const letters = new Set(["a", "b"]);
const copied = [...letters];
console.log(copied.join(","));

This example shows the same idea in plain JavaScript. Line 1 makes a Set that gives "a", then "b", when you loop over it. Line 2 uses ... (spread) to copy those values into a new Array. Line 3 prints a,b. A sequence works the same way: it makes a new list from the values. It does not keep the Set itself.

The table below compares the three types. Read each row from left to right: the type, what it means, and one example from this lesson.

Three ways a value can be converted
TypeWhat it meansExample from this lesson
DOMStringA normal JavaScript string. It can hold anything a string can, even a lone surrogate.setAttribute turns the number 5 into the string "5".
USVStringA stricter string. Every character must be complete, so each lone surrogate becomes U+FFFD.new URL swaps a lone surrogate for U+FFFD.
sequence<T>A new list, copied from any value you can loop over with for...of. T is the type of each item.new Blob accepts a Set of parts.

One DOM node, one JavaScript object

Inside the browser, each part of the page, like the body, is a browser object. Your code never touches that object directly. Instead, it gets a JavaScript object that stands for it. People often call this JavaScript object a wrapper.

Reading body twice gives the same objectPop out in the code editor (opens in a new tab)JavaScript
const firstBody = document.body;const secondBody = document.body;console.log(firstBody === secondBody);

Line 1 reads document.body into firstBody. Line 2 reads it again into secondBody. Line 3 prints true. Both reads give the very same JavaScript object. That is why comparing elements with === and adding event listeners work the way you expect.

Two variables, firstBody and secondBody, both point to one cart called body. firstBody === secondBody is true.firstBody === secondBodytruefirstBodysecondBodybodyone object
Both variables point to the same body object, so firstBody === secondBody is true. The browser does not make a new object each time you read document.body.

You don't need to know what language the browser is written in. Web IDL lets each browser build things its own way. What it does promise is what your JavaScript sees: the same node always gives the same object, with the same methods.

So keep this simple picture in mind: your code holds a JavaScript object, and the browser does the real work behind it. That is also why you can't turn a DOM node into text, send it somewhere else, and get the same node back.

Web IDL also has a marker called [SameObject]. Don't mix it up with the rule above. It is a promise about one property: reading that property twice on the same object gives the same result. It is not written on every DOM property, and it does not make different nodes share one object.

A property you add stays on the objectPop out in the code editor (opens in a new tab)JavaScript
const body = document.body;
body.lessonNote = "tea";

console.log(document.body.lessonNote);
console.log(body === document.body);

delete body.lessonNote;

Line 1 reads the body once into body. Line 2 adds a normal JavaScript property, lessonNote, to that object. Line 4 reads document.body again and prints tea, so the new read sees the property. Line 5 prints true: both names still point to the same object.

Line 7 deletes the property again, so the example leaves the page as it found it. The example shows two separate facts:

  • lessonNote is a normal JavaScript property that lives on the wrapper.
  • The wrapper itself always stands for the same browser object.

The browser's inner object is not a second object you can reach from JavaScript. You only ever work with the wrapper.

Step through: two reads give the same body object
Step 0 of 5Ready
Your turn: follow the blue line

Add a note to the body, read the body again, and see that both reads give the same object. This replay shows what your JavaScript sees, not how the browser is built inside.

Running in
  1. script
Next: line 1
body.lessonNote = "tea";const again = document.body;console.log(again.lessonNote);console.log(body === again);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Interface objects and prototypes

Each Web IDL interface gets a global name in JavaScript, such as HTMLElement. The value behind that name is called the interface object. It is a function, so typeof HTMLElement gives "function". (typeof tells you what kind of value something is.)

A function you can't always call with newPop out in the code editor (opens in a new tab)JavaScript
console.log(typeof HTMLElement);try {  new HTMLElement();} catch (error) {  console.log(error.name);}

Line 1 prints function. Line 3 tries to make a new element with new HTMLElement(). The browser refuses, because HTML elements must be made by the document. The error is caught by catch, and line 5 prints its name, TypeError. So a function name for an interface does not always mean you can use new with it.

Every object also has a prototype: another object it can borrow methods from. That object has its own prototype, and so on. This line of objects is called the prototype chain. Object.getPrototypeOf(x) gives you the next object up from x.

Walk up the chain from bodyPop out in the code editor (opens in a new tab)JavaScript
let current = document.body;for (let step = 0; step < 5; step += 1) {  current = Object.getPrototypeOf(current);  console.log(current.constructor.name);}

Line 1 starts at the body object. The loop runs five times. Each time, line 3 moves one step up the chain. Line 4 prints the name of the interface that step belongs to. The five names are HTMLBodyElement, HTMLElement, Element, Node, and EventTarget.

The prototype chain of document.body: document.body, then HTMLBodyElement, HTMLElement, Element, Node, and EventTarget.each arrow is one step updocument.bodyHTMLBodyElementHTMLElementElementNodeEventTargettop of the chain
Each arrow is one turn of the loop: one step up to the next prototype. The cards after document.body show the five names line 4 prints, in order.
Step through: walk up the chain from the body
Step 0 of 13Ready
Your turn: follow the blue line

Step up the prototype chain from document.body, one prototype at a time. This replay copies what a real browser prints.

Running in
  1. script
Next: line 1
let current = document.body; for (let step = 0; step < 5; step += 1) {  current = Object.getPrototypeOf(current);  names.push(current.constructor.name);} console.log(names.join(" -> "));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

This chain is where shared methods come from. The body can call addEventListener because that method lives on the EventTarget prototype, higher up the chain. Each body object does not keep its own copy of every method.

The interface object and its prototype do different jobs:

  • HTMLElement is the interface object, the global name.
  • HTMLElement.prototype is the shared object in each element's prototype chain. The methods live here, so the browser does not need to put a copy of each one on every element.

A DOM method also checks which object it is called on. That object is called the receiver. Inside the method, it is this. You can pick the receiver yourself with .call(): method.call(obj, arg) runs method with obj as its receiver.

A DOM method checks its receiverPop out in the code editor (opens in a new tab)JavaScript
const button = document.createElement("button");const append = Node.prototype.appendChild;try {  append.call({}, button);} catch (error) {  console.log(error.name);}console.log(append.call(document.body, button) === button);

Line 1 creates a button. Line 2 saves the appendChild method in append. Line 4 calls it with a plain empty object, {}, as the receiver. That object is not a real node, so the binding refuses and line 6 prints TypeError. Line 8 calls the same method on document.body, a real node. It returns the button, so the line prints true. Some browsers write "Illegal invocation" in the error message. Check the error's name, not its message.

The check asks: is this object really a node made by the browser? Having the right prototype is not enough. A different rule, the [LegacyUnforgeable] marker, is used by a few old properties. It puts them right on each object, where they can't be removed or replaced, instead of on the shared prototype.

Back to the new HTMLElement() error. Every interface object is a function. But only interfaces that list a constructor in Web IDL can make objects with new. Elements are made by the document instead, with document.createElement. That way the document can set up each element correctly.

Live and static lists of elements

Some old browser objects act partly like normal objects and partly like lists. You can read their items by number, like list[0], or sometimes by name. Web IDL calls these legacy platform objects. "Legacy" means they are kept so old websites still work. New browser features are not designed this way.

Two methods find elements and give you a list back:

  • getElementsByTagName gives a live list, called an HTMLCollection. It keeps changing to match the page.
  • querySelectorAll gives a static list, called a NodeList. It keeps the matches it found when you called it.
A live list and a static listPop out in the code editor (opens in a new tab)JavaScript
const list = document.createElement("ul");list.innerHTML = "<li>tea</li>";const live = list.getElementsByTagName("li");const snapshot = list.querySelectorAll("li");list.append(document.createElement("li"));console.log(live.length, snapshot.length);

Lines 1 and 2 make a ul list with one list item: an li that says tea. Line 3 gets a live list of its li items. Line 4 gets a static list of the same items. Line 5 adds a second, empty item. Line 6 prints 2 1. The live list now sees two items. The static list still has the one it found on line 4.

After a new list item is added: the live collection holds tea and the new item, length 2. The snapshot still holds only tea, length 1.live collectionteanewlivelength 2snapshotteasnapshotlength 1
After line 5 adds a new list item, live holds both items. snapshot still holds only the item that was there when line 4 ran. So line 6 prints 2 1.
Playground: a live list and a static list
A live list and a static listJavaScript
const list = document.createElement("ul");
list.innerHTML = "<li>tea</li>";
const live = list.getElementsByTagName("li");
const snapshot = list.querySelectorAll("li");
list.append(document.createElement("li"));
console.log(live.length, snapshot.length);
How many items each list sees

Items in the list: 1

Live list (HTMLCollection): 1

Static list (NodeList): 1

Try it yourself

The live list sees 1 item. The static list still has 1 item: the one it found when the code ran.

Each press adds one more list item, like line 5 does. The counts show how many items each list would see then.

The table compares the three kinds of list objects. Read each row as: the object, what happens after the page changes, and an example.

Live list, static list, and legacy object
ObjectWhat happens after the page changes?Example
A live list, HTMLCollectionIt changes to match the page. The next time you read it, you see the new items.getElementsByTagName gives you one.
A static list, NodeListIt does not change. It keeps the items it found when you called the method.querySelectorAll gives you one.
A legacy platform objectThat depends on the object. What they share: you can read items by number, like list[0].Both lists above are legacy platform objects. They act like Arrays, but they are not Arrays.

"Live" does not mean it redraws anything on the screen. It only means that when you read the list later, you may get different items, because the page changed. If you need a list that stays the same, copy it into an Array with Array.from(live).

A live list answers collection[0] even though it is not an Array. That is the old legacy behavior at work. Reading items by name is another old rule. These are features of old browser lists. They are not a pattern to copy in your own code.

In the playground, you change one thing: how many items the list has. Reset goes back to one item. The live count follows the page. The static count stays at the number found when the query ran. When a list seems to change by itself, ask: is it live or static?

DOMException: the browser's error object

When a browser feature fails, it often throws a DOMException. This is an error object from the browser. It has a name, such as "AbortError", and a message. It is not one of JavaScript's own error types, like TypeError. But it works like an error, and you can check for it with instanceof DOMException. (x instanceof Y asks: was x made by Y?)

Make a DOMException yourselfPop out in the code editor (opens in a new tab)JavaScript
const stopped = new DOMException("Stopped", "AbortError");console.log(stopped.name, stopped instanceof DOMException);

Line 1 makes a DOMException. The first input is the message, "Stopped". The second is the name, "AbortError". Line 2 prints AbortError true. Your code should use the name to tell what went wrong. Don't use the old number codes.

A bad element name throws a DOMExceptionPop out in the code editor (opens in a new tab)JavaScript
try {  document.createElement("1abc");} catch (error) {  console.log(error.name);}

Line 2 asks the document for an element named 1abc. Element names can't start with a digit, so the browser throws. Line 4 prints InvalidCharacterError. That is the DOMException name for "this text has a character that isn't allowed here".

A DOMException tells you that you broke one of a feature's rules. But not every browser error is a DOMException. Some failures throw a normal JavaScript TypeError instead, like the receiver check earlier.

Look inside a DOMExceptionPop out in the code editor (opens in a new tab)JavaScript
const problem = new DOMException("Bad name", "InvalidCharacterError");
console.log(problem.name);
console.log(problem.code);
console.log(problem instanceof DOMException);

Line 1 makes a DOMException with a message and a name. Line 2 prints the name, InvalidCharacterError. Line 3 prints 5, an old number code for the same error. Line 4 prints true. In your own code, check the name. The number codes are only kept for old code.

The 1abc example got its DOMException from the browser. This example makes one directly. Either way, check a fixed name like InvalidCharacterError. Don't check the message: it can be different in each browser.

document.all, the odd one out

document.all is left over from very old browsers. It gives back a list of every element on the page, called an HTMLAllCollection. You can read its properties, like document.all.length. But the HTML standard gives it some strange rules, so that very old websites keep working.

An object that pretends to be undefinedPop out in the code editor (opens in a new tab)JavaScript
console.log(typeof document.all);console.log(Boolean(document.all));console.log(typeof document.all.length);

Line 1 prints undefined. Line 2 prints false, so document.all counts as false in an if. Line 3 prints number, because the list still has a real length. So it is not truly the value undefined.

Why? Old websites checked if (document.all) to find out which browser they were in. To keep those sites working, the standard gives this one object a special hidden flag, written [[IsHTMLDDA]]. The flag changes three things:

  • typeof says "undefined".
  • It counts as false in an if or in Boolean().
  • Loose equality, ==, treats it like undefined and null.

Don't use document.all to check for features in new code. It is only there so old pages keep working.

For example, document.all == null is true. But document.all === undefined is false, because strict equality, ===, is not fooled. You can't copy this trick with your own object, a Proxy, or a custom valueOf method.

The double square brackets are how the standard writes a hidden flag. JavaScript code can't add [[IsHTMLDDA]] to an object. The HTML standard gives it only to HTMLAllCollection.

When a browser API surprises you

Bindings are most useful to know when a browser feature does something you didn't expect. First, look up the method and the type of each input. Each of these surprises follows from the binding rules:

  • a number that comes back as text from an attribute,
  • a URL where a character was swapped for U+FFFD,
  • a list that changes length by itself.

Then pick the check that fits the question:

  • Is it the same object? Compare with ===.
  • Where does a method come from? Walk the chain with Object.getPrototypeOf.
  • Did a list change? Check if it is a live HTMLCollection or a static NodeList.
Sort the binding facts
  • setAttribute turns 5 into "5"
  • new URL swaps a lone surrogate for U+FFFD
  • document.body === document.body is true
  • HTMLElement.prototype holds shared methods
  • A list gets longer after you add an element to the page
  • An object that counts as false, with typeof giving "undefined"
Try it yourself
0 of 6 correct

Put each card in the group that explains it best.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

For everyday practice with these objects, see the lessons on the DOM tree, node properties, attributes and properties, and the prototype chain.

Common misconceptions

  • “The DOM is plain JavaScript.” DOM objects are browser objects. Your code reaches them through a binding.
  • “Every interface name works with new.” An interface object is a function, but new HTMLElement() still throws a TypeError.
  • “All DOM lists are Arrays.” They are special list objects. Some are live and some are static.
  • “document.all is undefined.” It acts like undefined in a few checks, but it is still a list with a length.
  • “DOMException is just Error.” It is the browser's own error type, with names like AbortError.

Knowing these differences tells you where to look when something goes wrong:

  • A value changed type? Look at how the input was converted.
  • A method is missing or odd? Look at the prototype chain.
  • A list changed? Check whether it is live.

Practice exercises

Exercise 1 · Warm-upPredict an attribute type

What does the last line print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const box = document.createElement("div");
box.setAttribute("data-count", 5);
console.log(typeof box.getAttribute("data-count"));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upName the replacement

    You pass new URL some text with a lone surrogate in it. The input is a USVString. What character takes the lone surrogate's place?

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeChoose the live query

      Your shopping list page adds new items after you look them up. You want a list that sees the new items too. Which method should you use?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRead the exception name

        What is the name of the DOMException made on line 1?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const stopped = new DOMException("Stopped", "AbortError");
        console.log(stopped.name, stopped instanceof DOMException);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeExplain document.all

          document.all counts as false in an if, but it still has a length. What hidden flag causes this?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeApply it to an app

            In your app, a form value comes back different after you pass it to a browser method. What should you look at before you write a fix?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, think about the binding rule first. Then predict the result.

              Bindings and Web IDL quiz · 8 questionsScore: first tries count
              1. Question 1 of 8What is Web IDL used for?

                Choose an answer to see the explanation.

              2. Question 2 of 8What does this print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log(new URL("https://shop.example/?q=\uD800").search);

                Choose an answer to see the explanation.

              3. Question 3 of 8Why does new HTMLElement() throw?

                Choose an answer to see the explanation.

              4. Question 4 of 8A live HTMLCollection holds one item. You add a matching element to the page. What happens to the list?

                Choose an answer to see the explanation.

              5. Question 5 of 8What does this code print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const error = new DOMException("Stopped", "AbortError");
                console.log(error.name, error instanceof DOMException);

                Choose an answer to see the explanation.

              6. Question 6 of 8Why is document.all unusual?

                Choose an answer to see the explanation.

              7. Question 7 of 8A browser method's input is a Web IDL sequence. What can you pass in?

                Choose an answer to see the explanation.

              8. Question 8 of 8Your code catches a DOMException. What should it check to tell what went wrong?

                Choose an answer to see the explanation.

              Key takeaways

              • Web IDL writes down how a browser feature shows up in JavaScript: its objects, methods, and properties.
              • The binding converts what you pass in. A DOMString is a normal string. A USVString swaps lone surrogates for U+FFFD. A sequence copies the values out of any iterable.
              • The same DOM node always gives the same JavaScript object. Its shared methods come from prototypes up the chain.
              • A live list changes when the page changes. A static list keeps what it found.
              • A DOMException has a name, like AbortError. Check the name, not the message.
              • document.all is an old special case, kept on purpose for old websites.

              Remember the one-liner.
              A binding is the set of rules that lets your JavaScript use the browser's own objects.

              Coming next: Realms, globals & WindowProxy.

              CompleteFrontend Clear concepts. Working examples.