completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Node properties & contents

Read DOM node types, names, text, HTML, and visibility safely with nodeType, tagName, innerHTML, outerHTML, textContent, innerText, and hidden.

By the end, you can
  • 01
    Tell what kind of node you haveRead nodeType, nodeName, tagName, and localName, and find the words inside a text node.
  • 02
    Pick the right content propertyKnow when to use textContent, innerText, innerHTML, or outerHTML.
  • 03
    Show user text safelyPut text from users on the page as plain text, and explain how an HTML string can run code (XSS).

Questions you can ask a node

The browser reads your HTML and builds a tree of objects called the DOM. Each object in the tree is a node. Some nodes are elements, like a heading or a paragraph. Some are text, the words between tags. Some are comments.

Once you have found a node, you can ask it questions through its properties. A property is a named value on an object, like node.nodeType. This lesson covers three groups:

  • What kind of node is this? nodeType, nodeName, tagName, and localName.
  • What is inside it? textContent, innerText, innerHTML, and outerHTML. You can read these, and you can also set them to replace what is inside.
  • Should it be hidden? hidden.
The main idea

Node properties answer three questions: "What is this node?", "What text or HTML is inside it?", and "Should this element be hidden?"

Real-life analogyA display box in a museum

Picture a display box in a museum. You can read every note inside it, even notes tucked out of sight. You can read only what a visitor sees through the glass. You can hand over a written plan and have the contents rebuilt. Or you can swap the whole box for a new one.

In real life: Reading every note inside the box
In JavaScript: textContent gives the words of every text node, even hidden ones
In real life: Reading only what a visitor can see
In JavaScript: innerText gives the words shown on screen
In real life: Handing over a written plan for new contents
In JavaScript: innerHTML = "<b>bold</b>" throws away the old children and builds new ones from the plan
In real life: Swapping the whole box for a new one
In JavaScript: outerHTML = "<section>..." replaces the element itself

Start with a small step-through. The code uses a plain object, card, that holds three strings. Each string is what one real content property would give back for the same paragraph. Pick a property, then step through to see which string comes out.

Step through one property read
Step 0 of 3Ready
Your turn: follow the blue line

Pick one property, then step through the read. Each string is what that property gives back for the same paragraph. The experiments further down use real page nodes.

Running in
  1. script
Next: line 1
  textContent: "Hello hidden note",  innerHTML: "Hello <span hidden>hidden note</span>",  outerHTML: "<p>Hello <span hidden>hidden note</span></p>",};const picked = card.textContent;console.log(picked);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Which property should line 6 read?

Pick a property, then step through from the top again. The experiments further down use real page nodes.

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.

nodeType, nodeName, tagName, and localName

INTERACTIVE

The first question is "what kind of node is this?". node.nodeType answers with a number. Each kind of node has its own number. The table shows the common kinds.

Read each row as one kind of node. The columns show what each property gives back for that kind.

What each kind of node gives back
Kind of nodenodeTypenodeNametagNamelocalNameWhere its contents are
Element1The tag name in capitals, like DIV.The same as nodeName.The tag name in small letters, like div.In textContent, or in properties for that element.
Text3#textNone. Reading it gives undefined.None. Reading it gives undefined.In nodeValue or data.
Comment8#commentNone. Reading it gives undefined.None. Reading it gives undefined.In nodeValue or data.
Document9#documentNone. Reading it gives undefined.None. Reading it gives undefined.Its textContent is null.
Doctype10The doctype name, usually html.None. Reading it gives undefined.None. Reading it gives undefined.In doctype properties such as name.
Fragment11#document-fragmentNone. Reading it gives undefined.None. Reading it gives undefined.In its children and textContent.

An element has three name properties. Here they are on one heading.

The three names of a headingPop out in the code editor (opens in a new tab)JavaScript
const title = document.querySelector("h2");
console.log(title.nodeType);
console.log(title.nodeName, title.tagName, title.localName);
  1. Line 1 finds the first h2 heading on the page.
  2. Line 2 prints 1, the number for an element.
  3. Line 3 prints H2 H2 h2. nodeName and tagName use capital letters. localName uses small letters, the way you usually type the tag.

That rule is for HTML elements in an HTML page. SVG elements are different. SVG is the drawing language for shapes inside a page. An SVG circle gives circle in small letters for both tagName and localName. You can check this in the inspector below.

Every element also belongs to a chain of browser classes. A class is a blueprint that says which properties and methods an object gets. For an input box, the chain is:

  1. EventTarget: it can listen for events, like clicks.
  2. Node: it has node properties, like nodeType.
  3. Element: it has element properties, like tagName.
  4. HTMLElement: it has properties every HTML element shares, like hidden.
  5. HTMLInputElement: it has input properties, like value.

Each class adds to the one before it. So one input is all five at once.

One input belongs to five classesPop out in the code editor (opens in a new tab)JavaScript
const input = document.querySelector("#lesson-input");
console.log(input instanceof EventTarget);
console.log(input instanceof Node);
console.log(input instanceof Element);
console.log(input instanceof HTMLElement);
console.log(input instanceof HTMLInputElement);

Line 1 finds the input. instanceof asks "was this object made from this class, or from a class built on it?". Lines 2 to 6 ask that for each class in the chain. Every line prints true. This example needs a browser. Node.js does not have these DOM classes on its own.

Text and comment nodes are not elements. They keep their characters in a different place.

Where text and comments keep their wordsPop out in the code editor (opens in a new tab)JavaScript
const text = document.createTextNode("tea");
const comment = document.createComment("later");
console.log(text.nodeValue, text.data);
console.log(comment.nodeName, comment.data);
  1. Line 1 makes a text node that holds tea.
  2. Line 2 makes a comment node that holds later.
  3. Line 3 prints tea tea. nodeValue and data both hold the text.
  4. Line 4 prints #comment later: the comment's nodeName, then its data.
Left: the h2 element. nodeType is 1, nodeName is H2, tagName is H2, localName is h2. Right: a text node holding tea. nodeType is 3, nodeName is #text, tagName is undefined, data is tea.h2 elementnodeType: 1nodeName: H2tagName: H2localName: h2text nodenodeType: 3nodeName: #texttagName: undefineddata: "tea"
The same questions, asked of two kinds of node. Only the element has a tagName. The text node keeps its words in data.
Node inspector
Read the clicked nodePop out in the code editor (opens in a new tab)JavaScript
const node = event.target;console.log(node.nodeType);console.log(node.nodeName);console.log(node.tagName);console.log(node.localName);console.log(node.nodeValue);
Common nodeType numbers

1 element · 3 text · 8 comment · 9 document · 10 doctype · 11 fragment

Real page nodesClick one
nodeType
—
nodeName
—
tagName
—
localName
—
nodeValue / data
—
Try it yourself
Clicked: nothing picked yet

Click the heading, the paragraph, the bold word, the SVG badge, or the card around them. The panel shows each property of the node you clicked. A click always lands on an element, so text and comment nodes can't be clicked here; the table above lists their values. In the SVG badge, tagName is in small letters.

The mini page is built with plain DOM code inside an empty box, so React does not change the nodes you inspect.

A good habit: check that you have an element before you read an element-only property. tagName exists only on elements. A text node has the nodeName #text, and keeps its words in nodeValue or data.

innerHTML and outerHTML rebuild from a string

INTERACTIVE

innerHTML holds the HTML inside an element, written as a string. HTML written as text like this is called markup.

  • When you read innerHTML, the browser writes the element's children out as a markup string.
  • When you set innerHTML, the browser parses your string. To parse means to read the markup and build real nodes from it.

Setting innerHTML is not a small edit. The browser throws away all the old children and builds new ones from your string. Everything attached to the old children is lost:

  • Listeners: functions that run when you click or type.
  • Focus: which box the cursor was in.
  • Variables in your code that pointed at the old children.
  • Text a person typed into an input.
box.innerHTML is set to the string b bold, then text. Before, the box holds three children: label, button, and p. After, the box holds two new children: a b element and the text node, space text. The old children are thrown away.box.innerHTML = "<b>bold</b> text"labelbuttonpboxthrown awayrebuildb" text"boxbuilt new
Setting innerHTML does not edit the old children. The browser throws them away and builds new ones from the string.
Real-life analogyinnerHTML is an order to rebuild a room

You hand a builder a plan: "make the room hold this furniture." The builder does not keep the old chair. They carry everything out, then bring in new furniture that matches the plan.

In real life: Handing a builder a new plan for the room
In JavaScript: box.innerHTML = "<b>bold</b> text"
In real life: The builder carries out all the old furniture first
In JavaScript: The old child nodes are thrown away
In real life: Your note about the old chair still exists
In JavaScript: A variable can still point at an old node
In real life: But the chair is no longer in the room
In JavaScript: oldChild.isConnected becomes false
Rebuild with innerHTML and outerHTML
Rebuild the boxPop out in the code editor (opens in a new tab)JavaScript
const box = document.querySelector("#box");box.innerHTML = "<b>bold</b> text";box.innerHTML += "<em> rebuilt</em>";const old = box.querySelector("b");old.outerHTML = "<strong>new</strong>";console.log(old.isConnected);
The box on the pageButton clicks: 0
Old element still on the page after outerHTML?
not replaced yet
Try it yourself

Type in the input box, then try innerHTML +=.

Try this order: type in the input box, click the button, press Run innerHTML +=, then click the new button. The count stops and your typing is gone, because every child was built again.

outerHTML goes one step further. It holds the element itself plus its children. Setting it replaces the whole element, not only what is inside.

outerHTML leaves the old variable behindPop out in the code editor (opens in a new tab)JavaScript
const old = document.querySelector("#outer-old");
old.outerHTML = "<p id='outer-new'>New text</p>";
console.log(old.isConnected);
console.log(document.querySelector("#outer-new").textContent);
  1. Line 1 finds the old element and keeps it in old.
  2. Line 2 replaces that element on the page with a new paragraph.
  3. Line 3 prints false. isConnected says whether a node is still on the page. old still points at the removed element.
  4. Line 4 finds the new paragraph and prints New text.

So after setting outerHTML, search the page again to get the new element. A node that is no longer on the page is called detached.

Two edge cases, checked in a real browser for this lesson:

  • Setting outerHTML on document.documentElement (the html element) throws a NoModificationAllowedError. Its parent is the document itself, which cannot take a replacement.
  • Setting outerHTML on an element that is not on the page does not throw. It also does nothing, because there is no parent to put the replacement in.
Script tags don't run, but other code can

A script tag added with innerHTML does not run. That does not make innerHTML safe for text from users. Attributes like onerror hold code, and that code can still run.

textContent vs innerText

INTERACTIVE

textContent reads the words stored in the nodes. It joins the text of every text node inside the element, in order:

  • It includes text in hidden elements.
  • It includes the text inside style and script elements.
  • Setting it replaces all the children with one text node. Characters like <b> are shown as they are. They do not become tags.

innerText reads the words a person sees on screen. It follows the CSS:

  • It skips anything with display: none.
  • It applies CSS changes such as text-transform: uppercase.
  • It puts line breaks between blocks, like paragraphs, and for each <br>.

To know what is shown, innerText asks the browser to work out the page layout: where every box sits on screen. That can take extra work. If the element is not shown at all, innerText gives the same value as textContent.

textContent vs innerText
Read stored text and shown textPop out in the code editor (opens in a new tab)JavaScript
const panel = document.querySelector("#panel");console.log(panel.textContent);console.log(panel.innerText);
What each one givesFrom your browser
textContent
""
innerText
""
Try it yourself

textContent gives the text of every text node, so it includes the hidden note and the CSS inside the style element. innerText gives what a reader sees: the hidden note is gone, the CSS uppercase is applied, and blocks get line breaks.

Your browser works out both values when the page loads, because innerText depends on how the page is drawn.
Read stored text or shown textPop out in the code editor (opens in a new tab)JavaScript
const panel = document.querySelector("#panel");
console.log(panel.textContent);
console.log(panel.innerText);
  1. Line 1 finds the panel. It holds Hello, a hidden hidden note, and a paragraph, soft words.
  2. Line 2 prints Hello hidden notesoft words. The hidden note is included.
  3. Line 3 prints Hello, a blank line, then soft words. The hidden note is left out.
The same panel read two ways. panel.textContent gives Hello hidden notesoft words, with the hidden note included. panel.innerText gives Hello and soft words on separate lines, without the hidden note.panel.textContentHello hidden notesoft wordsincludes the hidden notepanel.innerTextHellosoft wordsonly what is shown
textContent joins the words of every text node, even hidden ones. innerText gives only the words a reader can see, with line breaks between blocks.

Use innerText only when you need to know what is shown. For everything else, use textContent.

Read each row as one property. The columns say what it does when you read it, when you set it, and when to use it.

Which content property to use
PropertyWhen you read itWhen you set itUse it for
textContentIt gives the text of every text node inside.It replaces the children with one text node.Showing text quickly and safely.
innerTextIt gives only the text shown on screen.It replaces the children with text, and turns line breaks into <br>.Checking what a person can see.
innerHTMLIt gives the children written as markup.It parses the string and builds new children.Markup you wrote yourself, never user text.
outerHTMLIt gives the element and its children as markup.It parses the string and replaces the whole element.Rare cases where the whole element must change.

One odd case: document.textContent is null. The document is the root of the whole tree, not an element, so it does not join up text the way an element does.

Text from users and XSS

SAFE SANDBOX

Some text comes from outside your code: a comment, a profile name, a search word, or a chat message. This is called untrusted text, because anyone could have typed it.

Cross-site scripting, or XSS, is an attack. Someone types markup that holds code. If your page puts that text into innerHTML, the browser builds real elements from it, and the code can run in your page.

One userName string, an img tag with an onerror attribute. Left: nameSlot.innerHTML turns it into a real img element, and its onerror code runs. Right: nameSlot.textContent turns it into a text node, so the page only shows the characters.<img src=x onerror=alert(1)>nameSlot.innerHTML<img> elementonerror code runsnameSlot.textContenttext nodeshown as text
The same userName string, two ways in. innerHTML builds a real element whose code can run. textContent only shows the characters.
Real-life analogyWho writes on your shop sign?

You can write a visitor's name on your shop sign yourself. That is safe: you only copy the letters. Handing the visitor the pen is different. They can write anything, including orders to your staff.

In real life: A visitor tells you their name
In JavaScript: Text a user typed
In real life: You write the name on the sign yourself
In JavaScript: textContent = userName
In real life: The visitor gets the pen and writes anything they like
In JavaScript: innerHTML = userName
XSS sandbox: text or code?
Unsafe and safe ways to show a namePop out in the code editor (opens in a new tab)JavaScript
nameSlot.innerHTML = userName; // unsafe for text from usersnameSlot.textContent = userName; // safe: shows the characters
The name a user typed
<img src="data:," onerror="parent.postMessage('ran', '*')">
Sandbox pageinnerHTML
Did the onerror code run? no
Try it yourself

No message came back, so no code ran. With textContent, the tag is shown as plain characters and no image is built.

The sandbox is a separate small page (an iframe) that may run scripts only inside itself. This page checks that a message really came from that frame before trusting it.
Safe with text from users?
  • textContent
  • innerText
  • innerHTML
  • outerHTML
  • insertAdjacentHTML
  • createTextNode
Try it yourself
0 of 6 correct

Sort each tool by whether it treats a string as plain text or as HTML.

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

The hidden property

INTERACTIVE

HTML has a hidden attribute. An attribute is a setting written in the tag, like <div hidden>. The element.hidden property is linked to that attribute:

  • Set hidden to true, and the attribute is added to the tag.
  • Set it to false, and the attribute is removed.

The hiding itself comes from CSS. Every browser has its own built-in stylesheet. It has a rule like [hidden] { display: none; }, which hides every element with the attribute.

Your own CSS can beat that built-in rule. Say your stylesheet gives a panel display: flex. Then the panel stays visible, even with hidden on. To fix it, add a stronger rule, such as [hidden] { display: none !important; }, or switch a class instead.

hidden adds an attribute; CSS does the hiding
Switch hidden on and offPop out in the code editor (opens in a new tab)JavaScript
panel.hidden = !panel.hidden;// If your CSS says display: flex, the element can still show.// Fix with: [hidden] { display: none !important; }
The panelhidden: true
Try it yourself

The panel has the hidden attribute. But this page's own CSS gives it display: flex, which beats the browser's rule, so it still shows. Turn on the fix to hide it.

hidden="until-found" is a newer value. It hides content until find-in-page shows it. Not every browser supports it yet.
hidden adds and removes the attributePop out in the code editor (opens in a new tab)JavaScript
const panel = document.querySelector("#hidden-panel");
panel.hidden = true;
console.log(panel.hasAttribute("hidden"), panel.hidden);
panel.hidden = false;
console.log(panel.hasAttribute("hidden"), panel.hidden);
  1. Line 1 finds the panel.
  2. Line 2 sets hidden to true.
  3. Line 3 prints true true: the attribute is there, and the property is on.
  4. Line 4 sets hidden to false.
  5. Line 5 prints false false: the attribute is gone, and the property is off.

You may also see hidden="until-found". It hides content until the browser's find-in-page search, or a link to that spot, shows it. Not every browser supports it yet. For a normal show-and-hide switch, use hidden as true or false, and test your CSS.

Where you will use this

These properties show up in small, everyday jobs:

  • A search result title from your server goes into textContent.
  • Markup for an icon comes from your own code, never from user text.
  • A "show details" button can set panel.hidden and the button's aria-expanded attribute together. aria-expanded tells screen readers whether the panel is open, so every user hears the same thing they see.
Show a message and open a panel safelyPop out in the code editor (opens in a new tab)JavaScript
function showMessage(slot, message) {
  slot.textContent = message;
}

function toggleDetails(button, panel) {
  const willOpen = panel.hidden;
  panel.hidden = !willOpen;
  button.setAttribute("aria-expanded", String(willOpen));
}
  • Line 2 shows the message as text, so any tags in it stay as plain characters.
  • Line 6 notes whether the panel is hidden now. If it is, the click will open it.
  • Line 7 flips hidden.
  • Line 8 sets aria-expanded to "true" or "false" to match.
Tags in textContent stay as textPop out in the code editor (opens in a new tab)JavaScript
const message = document.querySelector("#message");
message.textContent = "<b>Tea is ready</b>";
console.log(message.textContent);
console.log(message.firstChild.nodeType);
  1. Line 1 finds the message box.
  2. Line 2 sets its textContent to a string that looks like a tag.
  3. Line 3 prints the string as it is: <b>Tea is ready</b>.
  4. Line 4 prints 3. The box has one child, and it is a text node, not a real b element.

Use the "what kind of node" properties to write careful code. If a function might get a text node, a comment, or an element, check node.nodeType before you read tagName.

Common misconceptions

  • "Every node has tagName." Only elements have it. Text, comment, document, doctype, and fragment nodes don't. Use nodeName, or check nodeType first.
  • "innerHTML += just adds to the end." It still sets innerHTML. The browser reads the old markup, adds your string, and rebuilds every child.
  • "Script tags don't run, so innerHTML is safe." Attributes like onerror can still run code. Never put untrusted text into innerHTML.
  • "innerText and textContent are the same." innerText gives only the text shown on screen. textContent gives the text of every text node.
  • "hidden always hides." The hiding comes from a CSS rule. A stronger rule in your own CSS can beat it.
  • "After outerHTML, my variable points at the new element." It still points at the old, removed element. Search the page again to get the new one.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upRead a comment node

Predict the three lines the console prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const node = { nodeType: 8, nodeName: "#comment", data: "todo" };
console.log(node.nodeType);
console.log(node.nodeName);
console.log(node.data);

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

    Exercise 2 · PracticeChoose the safe property

    A profile name came from a form. Which property should you use to show it?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const userName = "<img src=x onerror=alert(1)>";
    console.log(userName);

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

      Exercise 3 · PracticeBuild a tiny inspector

      Write a function for the browser console that describes any node, and does not break on text nodes.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      function describe(node) {
        // Return type, name, tag, and text data when relevant.
      }
        Exercise 4 · PracticeFind the removed node

        You replace an element by setting its outerHTML. Is the old variable still on the page?

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

          Exercise 5 · ChallengeMake hidden win

          What selector would you add to your stylesheet so hidden elements always stay hidden?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          .panel[hidden] {
            display: flex;
          }

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

            Check your understanding

            7 QUESTIONS
            Node properties quiz · 7 questionsScore: first tries count
            1. Question 1 of 7Which nodeType number means an element node?

              Choose an answer to see the explanation.

            2. Question 2 of 7This object copies the names of a paragraph element in an HTML page. What does it print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const p = { tagName: "P", localName: "p" };
              console.log(p.tagName);
              console.log(p.localName);

              Choose an answer to see the explanation.

            3. Question 3 of 7Which property includes hidden text, and the text inside <style> or <script>?

              Choose an answer to see the explanation.

            4. Question 4 of 7This object copies an old element after outerHTML replaced it. What does it print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const old = { isConnected: false };
              console.log(old.isConnected);

              Choose an answer to see the explanation.

            5. Question 5 of 7Why is element.innerHTML += more a problem for the children already there?

              Choose an answer to see the explanation.

            6. Question 6 of 7Which is safest for showing a name a user typed?

              Choose an answer to see the explanation.

            7. Question 7 of 7What is the hidden property linked to?

              Choose an answer to see the explanation.

            Key takeaways

            • nodeType tells you the kind of node: element 1, text 3, comment 8, document 9, doctype 10, fragment 11.
            • tagName exists only on elements. nodeName works on every node and gives names like #text.
            • innerHTML and outerHTML parse a string and build new nodes. Use them only for markup you wrote, never for user text.
            • textContent gives the text of every text node. innerText gives only the text shown, and makes the browser work out the layout.
            • hidden adds or removes an attribute. CSS decides whether that attribute really hides the element.

            Final definition: Node properties let you read and change what kind of node you have, the text and HTML inside it, and whether it is hidden.

            Up next: Attributes & properties.

            CompleteFrontend Clear concepts. Working examples.