Node properties & contents
Read DOM node types, names, text, HTML, and visibility safely with nodeType, tagName, innerHTML, outerHTML, textContent, innerText, and hidden.
- 01Identify node kindsRead
nodeType,nodeName,tagName,localName, and text-node data without guessing. - 02Choose a content propertyDecide when to use
textContent,innerText,innerHTML, orouterHTML. - 03Avoid dangerous renderingShow untrusted text safely and understand why HTML strings can become XSS.
The properties dashboard
The DOM tree gives JavaScript objects for everything the browser built: the document, elements, words, comments, and fragments. This lesson is about the small dashboard of properties you read most often once you have a node in your hand.
Some properties identify what kind of node you found: nodeType, nodeName, tagName, and localName. Other properties read or replace the contents: textContent, innerText, innerHTML, and outerHTML. Finally, hidden is a convenient property for toggling the matching HTML attribute.
Node properties let you ask, “what is this node?”, “what text or HTML is inside it?”, and “should this element be hidden?”
Imagine a display box in a museum. You can ask for the label on the box, the words written inside it, or you can replace its contents entirely. DOM content properties feel similar, but the details matter because the browser is not just storing words; it can parse those words as markup.
- In real life: Describing new contents of a box in HTML words
- In JavaScript:
innerHTML = "<b>bold</b>": the browser throws away old children and rebuilds from the description - In real life: Replacing the box itself
- In JavaScript:
outerHTML = "<section>...": the old variable still points at the old detached box - In real life: Reading every note inside the box
- In JavaScript:
textContent: all text node data, including hidden notes - In real life: Reading what a visitor sees
- In JavaScript:
innerText: rendered text, affected by layout and CSS
Where the analogy stops: A real box does not parse instructions. A browser does: HTML strings become nodes, and that power is exactly why untrusted strings are dangerous.
Start with this tiny replay. It is not a browser debugger; it is a safe model that isolates one question: which string comes back when you read a content property?
Pick one property, then step through the read. The real browser experiments below use actual DOM nodes; this replay isolates the string each property would expose.
script
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);nodeType, nodeName, tagName, and localName
INTERACTIVEThe broadest test is node.nodeType. It is a number because it comes from older DOM APIs, but the constants are still useful:
| Node kind | nodeType | nodeName | tagName | localName | Useful value property |
|---|---|---|---|---|---|
| Element | 1 | Uppercase for HTML elements, like DIV | Same as nodeName for elements | Lowercase HTML name, like div | Usually textContent or element-specific properties |
| Text | 3 | #text | Does not exist | Does not exist | nodeValue or data is the text |
| Comment | 8 | #comment | Does not exist | Does not exist | nodeValue or data is the comment |
| Document | 9 | #document | Does not exist | Does not exist | document.textContent is null |
| Doctype | 10 | The doctype name, usually html | Does not exist | Does not exist | doctype fields such as name |
| Fragment | 11 | #document-fragment | Does not exist | Does not exist | children and textContent |
For HTML elements in an HTML document, nodeName and tagName are uppercase: a paragraph reports P. localName is usually the lowercase name you typed, such as p. SVG elements are different: in an HTML document an inline SVG circle reports lowercase tagName and localName. Verify it below in your own browser.
const node = event.target;console.log(node.nodeType);console.log(node.nodeName);console.log(node.tagName);console.log(node.localName);console.log(node.nodeValue);nodeType constants1 element · 3 text · 8 comment · 9 document · 10 doctype · 11 fragment
- nodeType
- —
- nodeName
—- tagName
—- localName
—- nodeValue / data
—
Click the heading, paragraph words, bold word, SVG badge, or the card itself. Text and comment nodes are harder to click directly, so this mini page includes them and the explanation shows how their properties differ. In the SVG, tagName is lowercase in an HTML document.
Notice one professional habit: check whether you have an element before using element-only properties. tagName exists on elements; a text node has nodeName #text and stores its words in nodeValue or data instead.
innerHTML and outerHTML replace by parsing strings
INTERACTIVEinnerHTML is a powerful shortcut: it serializes or parses the children inside an element. Assigning to it is not a tiny edit. The browser throws away the old children and builds new ones from the HTML string. That means old listeners, focus, references, and typed input property values are lost.
If you tell a builder “make the room contain this new list of furniture,” they do not carefully preserve the exact old chair. DOM HTML setters work the same way from your code’s point of view.
- In real life: Handing a builder a new room description
- In JavaScript:
box.innerHTML = "<b>bold</b> text" - In real life: The builder removes the old furniture first
- In JavaScript: Old child nodes are discarded
- 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.isConnectedbecomesfalse
Where the analogy stops: Browsers can optimize internally, but the observable behavior is a rebuild: old child node identity and event listeners do not survive.
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);outerHTML?not replaced yetType in the input, then try innerHTML +=.
innerHTML +=, then click the new button. The listener and typed property value are gone because the children were rebuilt.outerHTML goes one step farther: it replaces the element itself, not only its children. After old.outerHTML = "...", the page contains the replacement, while old still points at the detached original object. In the browser verified for this lesson, setting outerHTML on document.documentElement throws NoModificationAllowedError because its parent is the document. A detached element assignment did not throw in that browser, but it also did not insert anything because there was no parent to replace it in.
Scripts inserted with innerHTML do not run as normal script tags. That does not make innerHTML safe for user input: event-handler attributes such as onerror can still run.
textContent vs innerText
INTERACTIVEtextContent is about node data. It reads the text of all descendant text nodes, including hidden elements and text inside style or script elements. Setting it replaces the children with one text node, so markup-looking characters are shown literally.
innerText is about rendered text. It is CSS-aware: it skips display: none content, applies effects such as text-transform: uppercase, and turns block boundaries or <br> into line breaks. Because it asks layout what a reader sees, it can trigger layout work. If an element is not being rendered, browsers return the same value as textContent.
const panel = document.querySelector("#panel");console.log(panel.textContent);console.log(panel.innerText);""innerText
""
textContent reads text nodes exactly enough to include hidden text and the style element. innerText asks what rendered text a reader would see: hidden text disappears, CSS uppercase is applied, and line breaks are normalized by layout.
innerText depends on rendering.| Property | Reads | Writes | Best use |
|---|---|---|---|
textContent | All descendant text node data | Text only; replaces children with text | Fast, safe text updates |
innerText | Rendered, human-visible text | Text only, with rendering rules | Copy-like visible text checks |
innerHTML | Serialized child markup | Parsed child markup | Trusted templates or demos only |
outerHTML | Serialized element plus children | Parsed replacement for the element | Rare full-element replacement |
One odd but useful edge case: document.textContent is null. The document is the root container, not a normal element with text children to concatenate.
Untrusted text and XSS
SAFE SANDBOXCross-site scripting, usually shortened to XSS, happens when a string from outside your code becomes instructions in your page. A comment, profile name, search term, or chat message should be text. If you put it into innerHTML, you let the string become markup.
You can safely write a visitor's name on your shop display. Letting the visitor write on it directly is different: the browser may treat their text as page instructions.
- In real life: A visitor gives you their name
- In JavaScript: User input as plain text
- In real life: You write the name on the display
- In JavaScript:
textContent = userName - In real life: The visitor writes on the display directly
- In JavaScript:
innerHTML = userName
Where the analogy stops: A real display board cannot run code. The browser can treat untrusted HTML as active instructions.
nameSlot.innerHTML = userName; // unsafe for untrusted textnameSlot.textContent = userName; // safe: shows characters<img src="data:," onerror="parent.postMessage('ran', '*')">No payload message received. With textContent, markup-looking characters appear as text instead of becoming an image with an event handler.
event.source before trusting the message.textContentinnerTextinnerHTMLouterHTMLinsertAdjacentHTMLcreateTextNode
Sort each API by whether it treats a string as plain text or as HTML.
Where you will use this
These properties show up in small everyday tasks. A search result title from your server should go into textContent. A trusted icon template might be cloned or inserted with a safe framework mechanism, not assembled from user input. A disclosure panel can use button.ariaExpanded together with panel.hidden so assistive technology and sighted users agree.
function showMessage(slot, message) {
slot.textContent = message;
}
function toggleDetails(button, panel) {
const willOpen = panel.hidden;
panel.hidden = !willOpen;
button.setAttribute("aria-expanded", String(willOpen));
}Use the identity properties for defensive code. If a function may receive a text node, a comment, or an element, check node.nodeType before reaching for tagName.
Common misconceptions
- “Every node has
tagName.” Text, comment, document, doctype, and fragment nodes do not. UsenodeNameor checknodeTypefirst. - “
innerHTML +=appends safely.” It is still an assignment toinnerHTML, so existing children are rebuilt. - “Scripts do not run, so
innerHTMLis safe.” Event-handler attributes and dangerous URLs can still execute behavior. Avoid parsing untrusted strings. - “
innerTextandtextContentare synonyms.”innerTextasks layout for visible text;textContentreads text nodes. - “
hiddenalways wins.” The default hiding comes from CSS. Stronger author CSS can override it. - “After
outerHTML, my variable points at the new element.” It still points at the old object; query again to get the replacement.
Practice exercises
5 EXERCISESPredict the three console lines.
const node = { nodeType: 8, nodeName: "#comment", data: "todo" };
console.log(node.nodeType);
console.log(node.nodeName);
console.log(node.data);The object models a comment node, so it prints 8, then #comment, then its data string todo.
A profile name came from a form. Which property should receive it?
const userName = "<img src=x onerror=alert(1)>";
console.log(userName);const name = "<img src=x onerror=alert(1)>";
const slot = document.querySelector("#name");
slot.textContent = name;textContent treats the whole string as characters, so the image tag is displayed instead of parsed.
Write a helper for the browser console that describes a node without crashing on text nodes.
function describe(node) {
// Return type, name, tag, and text data when relevant.
}function describe(node) {
return {
type: node.nodeType,
name: node.nodeName,
tag: node.tagName ?? "not an element",
text: node.nodeType === Node.TEXT_NODE ? node.data : null,
};
}The function reports identity for any node and only reads text data when the node is actually a text node.
After an element is replaced with outerHTML, is the original variable still connected to the document?
The old variable points at the removed element, so old.isConnected is false. Query the parent again to find the replacement.
What selector would you add to a stylesheet to force hidden elements to stay hidden?
.panel[hidden] {
display: flex;
}[hidden] {
display: none !important;
}A global [hidden] rule with !important restores the expected hiding when other author rules set display.
Check your understanding
7 QUESTIONSQuestion 1 of 7Which
nodeTypenumber means an element node?Choose an answer to see the explanation.
Question 2 of 7What does the name comparison print?
Read the code, then predictconsole.log("P"); console.log("p");Choose an answer to see the explanation.
Question 3 of 7Which property includes hidden text and the text inside
<style>or<script>?Choose an answer to see the explanation.
Question 4 of 7What does the detached-node check print?
Read the code, then predictconst old = { isConnected: false }; console.log(old.isConnected);Choose an answer to see the explanation.
Question 5 of 7Why is
element.innerHTML += morerisky for existing children?Choose an answer to see the explanation.
Question 6 of 7Which is safest for showing a user-typed name?
Choose an answer to see the explanation.
Question 7 of 7What does the
hiddenproperty reflect?Choose an answer to see the explanation.
Key takeaways
nodeTypetells you the broad kind: element 1, text 3, comment 8, document 9, doctype 10, fragment 11.tagNameis element-only;nodeNameworks on every node and uses names like#text.innerHTMLandouterHTMLparse strings and rebuild nodes. They are for trusted markup, not user text.textContentreads raw text node data.innerTextreads rendered text and can trigger layout.hiddenreflects an attribute, but CSS decides whether that attribute actually hides the element.
Final definition: Node content properties are the DOM’s read/write controls for a node’s identity, text, HTML, and hidden state.
Up next: Attributes & properties.