Getting the complete text content of a node...

A

Arancaytar

(Note: I am a Javascript newbie. I can handle PHP and Java, but this
is unfamiliar territory.)

For a wordcount feature, I need to collect the complete text content
of a 'div' element inside a variable. Because of the issues with
paragraphs and markup, the content is split into different nodes in
the DOM.

For example:

<div>Hello, <p>this text is <span style="font-style:italic">italic</
span></p></div>

This will cause (more or less) a DOM tree like this:

[div]
-> Hello,
- > [p]
-> this text is
-> [span]
-> italic

Now, my function has, as a starting point, the [div] node that is the
top parent here.

function aggregateTextNode(textNode) {
...
return allText;
}

Since I don't know the depth of the nodes, I am trying to build this
as a recursive function. "depth" is a parameter that ensures I can
limit the recursion to a certain level.

function aggregateTextNode(textNode,depth) {
var text=textNode.nodeValue; // get the text value of the current
node
if (depth==0) return text; // recursion limit reached
for (i=0;i<textNode.childNodes.length;i++) { // if the node has
child nodes, aggregate these
text+=aggregateTextNode(textNode.childNodes,depth-1); //
append aggregated text
}
return text;
}

However, no matter where I set the recursion limit, the script
invariably freezes Firefox until the timeout is reached and I can
abort it - infinite loop, apparently.

Can you see what's wrong with my code? It's very clearly the recursion
that causes it, because if the node has no child nodes at all (say
"<div>Just text</div>"), it succeeds. But if there is only a single
child node, it hangs itself.

Meanwhile, I've managed to do it with a very ugly nested loop that can
go three levels deep, but I'd really rather use the recursive approach
if at all possible.
 
C

Christoph Burschka

Arancaytar said:
However, no matter where I set the recursion limit, the script
invariably freezes Firefox until the timeout is reached and I can
abort it - infinite loop, apparently.


I forgot to add: The Error Console shows no warnings or notices - I
don't have any indication that the script is crashing apart from the
freeze itself.

Also, a more readable version of the function with shorter, non-wrapped
lines:

function aggregateTextNode(node,depth) {
var text=node.nodeValue;
if (depth==0) return text;
for (i=0;i<node.childNodes.length;i++) {
text+=aggregateTextNode(node.childNodes,depth-1);
}
return text;
}
 
P

p.lepin

For a wordcount feature, I need to collect the complete
text content of a 'div' element inside a variable.
Because of the issues with paragraphs and markup, the
content is split into different nodes in the DOM.

Since I don't know the depth of the nodes, I am trying to
build this as a recursive function. "depth" is a
parameter that ensures I can limit the recursion to a
certain level.

[code snipped]
Meanwhile, I've managed to do it with a very ugly nested
loop that can go three levels deep, but I'd really rather
use the recursive approach if at all possible.

I'm not sure what the problem with your code is, but I was
a bit surprised you don't check the nodeType. Anyway, the
following works for me in Firefox 1.5.0.7 and Konqueror
3.5.4. Cannot check it with other browsers at the moment.

<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<script type="text/javascript">
function grabText ( node , maxDepth )
{
if ( 3 == node . nodeType )
{
return node . nodeValue ;
}
else if
(
( 1 == node . nodeType ) && ( 0 < maxDepth )
)
{
var result = '' ;
for
(
var i = 0 ;
i < node . childNodes . length ;
i ++
)
{
result +=
grabText
(
node . childNodes [ i ] , maxDepth - 1
) ;
}
return result ;
}
return '' ;
}
</script>
</head>
<body>
<div onclick=" alert ( grabText ( this , 3 ) ) ; ">
Some <i>stupid</i> text with
<b><span>fa<i>n</i>cy</span> formatting</b>!
</div>
</body>
</html>
 
R

RobG

(Note: I am a Javascript newbie. I can handle PHP and Java, but this
is unfamiliar territory.)

For a wordcount feature, I need to collect the complete text content
of a 'div' element inside a variable. Because of the issues with
paragraphs and markup, the content is split into different nodes in
the DOM.

Here's one I prepared earlier...

It tries the W3C compliant textContent first, if that isn't
supported, it tries IE's innerText. Finally, it tries innerHTML with
a regular expression to strip HTML tags. The final method should only
be used in a very small number of browsers, and may fail in those in a
few cases.

If you really want a recursive function, that is included further
down.

// Using textConent || innerText || innerHTML = regEx
function getText (el) {
if (el.textContent) {return el.textContent;}
if (el.innerText) {return el.innerText;}
if (typeof el.innerHTML == 'string') {
return el.innerHTML.replace(/<[^<>]+>/g,'');
}
}


// Using textConent || innerText || recursion
function getText(el)
{
if (el.textContent) return el.textContent;
if (el.innerText) return el.innerText;
return getText2(el);

function getText2(el) {
var x = el.childNodes;
var txt = '';
for (var i=0, len=x.length; i<len; ++i){
if (3 == x.nodeType) {
txt += x.data;
} else if (1 == x.nodeType){
txt += getText2(x);
}
}
return txt.replace(/\s+/g,' ');
}
}
 
C

Christoph Burschka

RobG said:
If you really want a recursive function, that is included further
down.

I don't want recursion at any cost - I just assumed it's necessary
because of the way the DOM tree stores text. If it's possible to get the
"flat" text content of the node in another way, that would be just great.

I haven't tried out your code yet, but from what I see, I guess
"textContent" and "innerText" can do just that without a need for a
messy recursion.

So by trying "textContent", "innerText" and stripped "innerHTML" in that
order, I can support almost all browsers that matter?
 
R

RobG

Christoph said:
I don't want recursion at any cost - I just assumed it's necessary
because of the way the DOM tree stores text. If it's possible to get the
"flat" text content of the node in another way, that would be just great.

I haven't tried out your code yet, but from what I see, I guess
"textContent" and "innerText" can do just that without a need for a
messy recursion.

So by trying "textContent", "innerText" and stripped "innerHTML" in that
order, I can support almost all browsers that matter?

Yes.

I don't know of any recent browser that doesn't support either
textContent or innerHTML, maybe there are some mobile browsers in that
category. If you keep the tag content simple (no '<' or '>' characters
in attribute values) then the fall-back to innerHTML should be pretty
solid too.
 
E

Elegie

RobG wrote:

Hi Rob,

Finally, it tries innerHTML with
a regular expression to strip HTML tags. The final method should only
be used in a very small number of browsers, and may fail in those in a
few cases.

I think so, too: innerHTML returns some text in which HTML entities
should logically not be expanded (as it normally represents a valid HTML
fragment). Therefore, if the code were to include some, those entities
would appear "as is" in the returned text.

For that very reason, while I'd admit the innerHTML approach is
definitely appealing, I think I'd prefer to stick to the recursion model
as the third fall back technique.


Kind regards,
Elegie.
 
C

Christoph Burschka

RobG said:
Yes.

I don't know of any recent browser that doesn't support either
textContent or innerHTML, maybe there are some mobile browsers in that
category. If you keep the tag content simple (no '<' or '>' characters
in attribute values) then the fall-back to innerHTML should be pretty
solid too.

Well, since the wordcount is a cosmetic feature, it won't break the page
if by some chance the browser doesn't support it.

Anyway, I've replaced my current nested loop with this function, and it
works perfectly. Thanks a lot!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top