4

Let say I have a mark up like this

<html id="test">
<body>
Some text node.
<div class="cool"><span class="try">This is another text node.</span></div>
Yet another test node.
</body>
</html>

my js code

function countText(node){
 var counter = 0;
 if(node.nodeType === 3){
     counter+=node.nodeValue.length;
     countText(node);
 }
 else{}
}

Now if I want to count the text nodes

console.log("count text : " + countText(document.getElementById("test"));

this should return me the count but its not working and moreover what should I put in else condition. I never used nodeType so kind of having problem using it . Any help will be appreciated.

user429035
  • 411
  • 1
  • 5
  • 14

2 Answers2

7

There are a couple of things wrong in your code:

  • Your HTML is malformed.
  • You are appending text to your counter instead of increasing it.
  • You never loop over the children of the a node, you always pass the same node to the recursive call.
  • You don't do anything if a node is not a text node.

This will work:

function countText(node){
    var counter = 0;
    if(node.nodeType === 3){
        counter++;
    }
    else if(node.nodeType === 1) { // if it is an element node, 
       var children = node.childNodes;    // examine the children
       for(var i = children.length; i--; ) {
          counter += countText(children[i]);
       }
    }
    return counter;  
}

alert(countText(document.body));

DEMO

Which number corresponds to which node type can be found here.


Update:

If you want to count the words, you have to split each text node into words first. In the following I assume that words are separated by white spaces:

if(node.nodeType === 3){
    counter = node.nodeValue.split(/\s+/g).length;
}

Update 2

I know you want to use a recursive function, but if you want to count the words only, then there is a much easier and more efficient way:

function countWords(node){
    // gets the text of the node and all its descendants
    var text = node.innerText || node.textContent
    return text.split(/\s+/g).length;
}
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • It's not really necessary to bother testing for any other node type. If it's a text node, count it. Then in any case, iterate over all children if any exist. – kqnr Apr 14 '11 at 00:45
  • @Felix I am not worried about the markup...but your function counts 3 whereas there are 12 text counts .....so I think have to use node.value.length instead of counter but your else statement make sense since elements most common nodeType – user429035 Apr 14 '11 at 01:12
  • 1
    @user429035: Ah, so you want to count the words? That is different than counting the text nodes. In your example above, there are 3 text nodes. You should have made this clear (and my script counts 3, not 2 text nodes). Please see my update. Btw valid markup is important, as only then you can know whether your script will work or not. E.g. in your HTML you are missing a closing quotation mark ` – Felix Kling Apr 14 '11 at 01:14
  • +1 @Felix...sorry for the confusion....Yes that was a mistake class="try" missed double quotes... – user429035 Apr 14 '11 at 01:29
  • @user429035: No worries :) Please see also my second update. There is a much more efficient way than using recursion... but whatever you want of course ;) – Felix Kling Apr 14 '11 at 01:31
  • @Felix thanks! but still I have one more confusion ...when you use the id of it gives me a different result ...any idea on that? – user429035 Apr 14 '11 at 03:23
  • @Felix it gives me 16 whereas actual count is 12 ...any idea ? – user429035 Apr 14 '11 at 03:27
  • @user429035: Well, I assume it counts something which is in the `head` of the page. There is no reason to the select the `html` element anyway. All the visible content (i.e. the content that matters) is inside the `body`. – Felix Kling Apr 14 '11 at 07:01
1

You want something like

function countTextNodes(node) {
    var n = 0;
    if(node.nodeType == 3)
        n = 1;
    for(var i = 0; i < node.childNodes.length; ++i)
        n += countTextNodes(node.childNodes[i]);
    return n;
}

This can be compressed into more compact code, but I went for legibility here.

Call this on the root in which you want to count text nodes. For example, to count text nodes throughout the entire document, you would want to call countTextNodes(document.getDocumentElement()).

kqnr
  • 3,596
  • 19
  • 17
  • @chomp thanks! but I don't want to use for loop instead just want to do it recursively ...and is Type valid because I think its nodeType.. – user429035 Apr 14 '11 at 00:40
  • 2
    @user429035: It is still recursive. You have to use a for loop, otherwise, how do you want to call the function on the child nodes? You have to iterate over the children. – Felix Kling Apr 14 '11 at 00:41
  • Do note that the loop only iterates over the immediate children, recursively calling `countTextNodes` on each of them and accumulating the results. As @Felix Kling suggests, you will find it impossible to accomplish your goal without such iteration. Also, you are correct about `nodeType`. Sorry about the typo! – kqnr Apr 14 '11 at 00:51
  • @chomp and @Felix agree... I have to use the for loop in else statement as @Felix suggested which makes sense – user429035 Apr 14 '11 at 01:15
  • Ok, but you don't need the `else`. – kqnr Apr 14 '11 at 01:17
  • @user429035: @chomp is right, it is not really necessary, but it doesn't hurt and might make the intention clearer (e.g. we don't want to examine comment nodes (which would not be a problem though as they would just be ignored)). – Felix Kling Apr 14 '11 at 01:21
  • @chomp you are right not necessary but it will just make things clearer ....@Felix thanks – user429035 Apr 14 '11 at 01:32
  • 1
    please use `=== Node.TEXT_NODE` instead of the magic number `3` –  Feb 23 '15 at 12:16