32

Is there a way to get innerText of only the top element (and ignore the child element's innerText) ?

Example:

<div> 
   top node text 
   <div> child node text </div>
</div>

How to get the "top node text" while ignoring "child node text" ? innerText property of top div seem to return concatenation of both inner , top text.

ivymike
  • 1,511
  • 2
  • 20
  • 27

6 Answers6

30

Just iterate over the child nodes and concatenate text nodes:

var el = document.getElementById("your_element_id"),
    child = el.firstChild,
    texts = [];

while (child) {
    if (child.nodeType == 3) {
        texts.push(child.data);
    }
    child = child.nextSibling;
}

var text = texts.join("");
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 1
    +1 Clearly better than cloning what may be a very large bit of DOM tree, just to discard most of it. Only improvement would be `text = []` at the start, and then `text.push(child.data)` per iteration, and finally `text = text.join('')` at the end to turn the array of pieces into a string, which tends to be faster than repeated concatenations to an ever-growing string. – Daniel Earwicker Feb 18 '12 at 11:50
  • @DanielEarwicker: I considered that, but I think the last benchmarks I saw suggested it was not clear cut that it actually improves performance so I went for the simpler version. I'll check now. – Tim Down Feb 18 '12 at 11:52
  • 1
    Nice one. PS. `data` is the defined in the [`CharacterData`](https://developer.mozilla.org/en/DOM/CharacterData) interface, which is implemented by `Text` (text nodes). – Rob W Feb 18 '12 at 11:52
  • 1
    @DanielEarwicker: Seems as though array join may well be slower in modern browsers but is way quicker in IE 7, which trumps everything since your slowest target browser is where you actually need performance gains. Amending... – Tim Down Feb 18 '12 at 11:56
  • 1
    Instead 3 you can use Node.TEXT_NODE [Source](https://www.w3schools.com/jsref/prop_node_nodetype.asp) (See named constants) – William Ardila Jul 19 '17 at 15:48
  • 1
    @WilliamArdila: True. It was true in 2012 except for IE <= 8, which hadn't gone away by that point. – Tim Down Jul 20 '17 at 08:16
13

This will work in your example: document.getElementById("item").firstChild.nodeValue;

Note: Keep in mind that this will work if you know you are dealing with that specific HTML. If your HTML can change, for example to:

<div> 
    <div class="item"> child node text </div>
    top node text 
</div>

then you should use the more generic solution by @Tim Down


Here is working code snippet:

window.onload = function() {
   var text = document.getElementById("item").firstChild.nodeValue;
   document.getElementById("result").innerText = text.trim();
};
#result {
  border: 1px solid red;
}
<div id="item">
  top node text 
   <div> child node text </div>
</div>



<strong>Result:</strong> <div id="result"></div>
Community
  • 1
  • 1
Alex Kolarski
  • 3,255
  • 1
  • 25
  • 35
4
  1. Clone the element.
  2. Loop through all child nodes (backwards, to avoid conflicts):
    If the element has a tagName attribute, then it's an element: Remove the node.
  3. Use innerText to get the textual contents (with fallback to textContent, when innerText is not supported).

Code:

var elem = document.getElementById('theelement');
elem = elem.cloneNode(true);
for (var i=elem.childNodes.length-1; i>=0; i--) {
    if (elem.childNodes[i].tagName) elem.removeChild(elem.childNodes[i]);
}
var innerText = elem['innerText' in elem ? 'innerText' : 'textContent'];
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Wow, works like charm in all browsers. Frankly, I didn't think it was possible before posting the question :) ... thank you... – ivymike Feb 18 '12 at 11:10
  • 2
    No need to clone or mutate the DOM at all. Just read the text node children. See my answer. – Tim Down Feb 18 '12 at 11:46
  • 3
    @ivymike Tims's method is better. I suggest to accept his answer instead of mine. It also works in all browsers (tested in IE6+, Chrome 1+, Safari 5, Firefox 3+). – Rob W Feb 18 '12 at 11:55
3

function getDirectInnerText(element) {
  var childNodes = element.childNodes;
  result = '';

  for (var i = 0; i < childNodes.length; i++) {
    if(childNodes[i].nodeType == 3) {
      result += childNodes[i].data;
    }
  }

  return result;
}

element = document.querySelector("div#element");
console.log(getDirectInnerText(element))
<div id="element"> 
   top node text 
   <div> child node text </div>
</div>
ehsaneha
  • 1,665
  • 13
  • 8
  • Avoid code-only answers. Also, you could change the `3` to `Node.TEXT_NODE` to use fewer literals and increase readability – LukasKroess Jul 14 '23 at 06:34
0

If you don't want to ignore the child element's inner text, use the following function:

function getInnerText(el) {
    var x = [];
    var child = el.firstChild;
    while (child) {
        if (child.nodeType == 3) {
            x.push(child.nodeValue);
        }
        else if (child.nodeType == 1) {
            var ii = getInnerText(child);
            if (ii.length > 0) x.push(ii);
        }
        child = child.nextSibling;
    }
    return x.join(" ");
}
johnwcb
  • 9
  • 1
0

As every other answer already explained, you need to check the type of the childnode.

Here is a neat one-liner based on @ehsaneha's answer:

Array.from(element.childNodes).reduce((x, y) => x + (y.nodeType == Node.TEXT_NODE ? y.data : ''), '').trim()

Or as a prototyped method:

Element.prototype.directInnerText = function () {
    return Array.from(this.childNodes).reduce((x, y) => x + (y.nodeType == Node.TEXT_NODE ? y.data : ''), '').trim();
}

usage:

console.log(document.body.querySelector('.element').directInnerText());

Compatibility reference:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType?retiredLocale=de

LukasKroess
  • 125
  • 2
  • 13