Chapter 41: DOM Get Values
XML DOM – Getting Node Values.
I will explain it as if I am your personal teacher sitting next to you — going slowly, using simple analogies, drawing small trees on the whiteboard, showing many different situations, pointing out traps beginners fall into, and giving you copy-paste-ready code you can test immediately.
1. The most important sentence to remember first
There is no single “get value” method that works the same way for every kind of node.
Each type of node has its own meaning for “value”, so you have to choose the right property depending on:
- whether you are on an element node
- a text node
- an attribute node
- a comment node
- a CDATA section
- etc.
2. Quick overview – Which property to use for which node type
| Node Type | nodeType | You usually want… | Recommended property / method | Returns | Typical result example | Notes / Traps |
|---|---|---|---|---|---|---|
| Element | 1 | All readable text inside + descendants | textContent | string | “Atomic HabitsJames Clear499.00” | Includes all descendant text + whitespace |
| Element | 1 | Only direct text (no child elements) | firstChild?.nodeValue?.trim() or data | string or null | “499.00” | May be null if no text child |
| Text | 3 | The text itself | nodeValue or data | string | “Atomic Habits” | data is alias for nodeValue on text nodes |
| Attribute | 2 | The attribute value | value or nodeValue | string | “INR” | Both are equivalent |
| Comment | 8 | The comment text | nodeValue or data | string | ” Popular book “ | Usually ignored in output |
| CDATASection | 4 | The raw CDATA content | nodeValue or data | string | “not parsed“ | Preserves markup as text |
| Document | 9 | — | — | null | — | No value |
| ProcessingInstruction | 7 | The instruction data | nodeValue or data | string | “version=”1.0″ encoding=”UTF-8″” | Rare in practice |
Golden rule for beginners
If you are on an element (nodeType 1) and you just want the readable text → use textContent If you are on a text node (nodeType 3) → use nodeValue If you are on an attribute (nodeType 2) → use value or nodeValue
3. Visual example – XML and its nodes with values
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
<book id="B101"> <!-- Popular book --> <title>Atomic Habits</title> <author>James Clear</author> <price currency="INR">499.00</price> <![CDATA[<b>special offer</b>]]> </book> |
Let’s label the nodes and their values:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Element: book ├── Attribute: id = "B101" ← value = "B101" ├── Comment: <!-- Popular book --> ← value = " Popular book " ├── Element: title │ └── Text: "Atomic Habits" ← nodeValue = "Atomic Habits" ├── Element: author │ └── Text: "James Clear" ← nodeValue = "James Clear" ├── Element: price │ ├── Attribute: currency = "INR" ← value = "INR" │ └── Text: "499.00" ← nodeValue = "499.00" └── CDATASection: "<b>special offer</b>" ← nodeValue = "<b>special offer</b>" |
4. Practical code – Getting values from different starting points
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// Assume xmlDoc is already parsed (from string or responseXML) const book = xmlDoc.querySelector("book"); const price = xmlDoc.querySelector("price"); const titleText = xmlDoc.querySelector("title").firstChild; const currencyAttr = price.attributes.getNamedItem("currency"); const comment = book.firstChild; // assuming comment is first child // ──────────────────────────────────────────────── // 1. From ELEMENT node – most common case // ──────────────────────────────────────────────── console.log("price.textContent:", price.textContent); // "499.00" console.log("price.nodeValue:", price.nodeValue); // null console.log("price.firstChild.nodeValue:", price.firstChild.nodeValue.trim()); // "499.00" // All text inside book (recursive) console.log("book.textContent:", book.textContent.trim()); // "Atomic HabitsJames Clear499.00special offer" // ──────────────────────────────────────────────── // 2. From TEXT node // ──────────────────────────────────────────────── console.log("titleText.nodeValue:", titleText.nodeValue); // "Atomic Habits" console.log("titleText.data:", titleText.data); // "Atomic Habits" (same) console.log("titleText.textContent:", titleText.textContent); // "Atomic Habits" // ──────────────────────────────────────────────── // 3. From ATTRIBUTE node // ──────────────────────────────────────────────── console.log("currencyAttr.value:", currencyAttr.value); // "INR" console.log("currencyAttr.nodeValue:", currencyAttr.nodeValue); // "INR" (same) // ──────────────────────────────────────────────── // 4. From COMMENT node // ──────────────────────────────────────────────── console.log("comment.nodeValue:", comment.nodeValue.trim()); // "Popular book" console.log("comment.textContent:", comment.textContent.trim()); // same // ──────────────────────────────────────────────── // 5. From CDATA section // ──────────────────────────────────────────────── const cdata = xmlDoc.querySelector("book").lastChild; console.log("cdata.nodeValue:", cdata.nodeValue); // "<b>special offer</b>" |
5. The most common real-world patterns (copy-paste these)
Pattern 1 – Safe “get text from element” function (most useful)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
function getElementText(element) { if (!element) return ""; return element.textContent?.trim() || ""; } console.log(getElementText(xmlDoc.querySelector("title"))); // "Atomic Habits" console.log(getElementText(xmlDoc.querySelector("price"))); // "499.00" |
Pattern 2 – Get direct text only (when you know there are no child elements)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
function getDirectText(element) { if (!element || !element.firstChild) return ""; const first = element.firstChild; return first.nodeType === 3 ? first.nodeValue.trim() : ""; } console.log(getDirectText(xmlDoc.querySelector("price"))); // "499.00" |
Pattern 3 – Get attribute safely with fallback
|
0 1 2 3 4 5 6 7 8 9 10 11 |
function getAttr(element, attrName, fallback = "") { return element?.hasAttribute(attrName) ? element.getAttribute(attrName) : fallback; } console.log(getAttr(xmlDoc.querySelector("price"), "currency", "USD")); // "INR" console.log(getAttr(xmlDoc.querySelector("title"), "currency", "USD")); // "USD" |
Pattern 4 – Get all text values from matching elements
|
0 1 2 3 4 5 6 7 8 |
const prices = xmlDoc.querySelectorAll("price"); const values = Array.from(prices).map(p => p.textContent.trim()); console.log("All prices:", values); // ["499.00", ...] |
6. Common beginner mistakes & how to fix them
Mistake 1 — Using nodeValue on an element
|
0 1 2 3 4 5 6 |
console.log(price.nodeValue); // null — elements don't have nodeValue |
Fix: Use textContent or firstChild.nodeValue
Mistake 2 — Forgetting to trim whitespace
|
0 1 2 3 4 5 6 |
console.log(title.firstChild.nodeValue); // may have leading/trailing spaces/newlines |
Fix: Always .trim()
Mistake 3 — Using innerHTML on pure XML DOM
|
0 1 2 3 4 5 6 |
console.log(price.innerHTML); // often empty or undefined |
Fix: textContent or nodeValue on text nodes
Mistake 4 — Assuming every element has exactly one text child
|
0 1 2 3 4 5 6 |
<name>John <middle>Doe</middle> Smith</name> |
→ multiple text nodes + element child
Fix: Use textContent when you want all text
Summary – Quick reference table
| Starting node type | You probably want… | Recommended property | Returns | Notes / Traps |
|---|---|---|---|---|
| Element | All text inside + descendants | textContent | string | Includes whitespace & all descendants |
| Element | Only direct text | firstChild?.nodeValue?.trim() | string or null | May be null if no text or first child is element |
| Text | The text itself | nodeValue or data | string | Same value |
| Attribute | The attribute value | value or nodeValue | string | Same value |
| Comment | The comment text | nodeValue or data | string | Usually ignored |
| CDATA | The raw CDATA content | nodeValue or data | string | Preserves markup as text |
Would you like to continue with one of these next?
- How to clean & normalize text values (remove extra spaces, collapse newlines)
- Getting typed values (number, boolean, date) from nodes safely
- Handling namespaces when getting values (very common in real XML)
- Real-world patterns — extracting price + currency pairs, collecting all titles, etc.
- Debugging when nodeValue / textContent gives unexpected results
- Comparing nodeValue vs textContent vs innerText (differences & pitfalls)
Just tell me which direction you want to go next! 😊
