Iterating though nodes

M

matth

I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range aren't
being hit. It seems like deeply nested nodes aren't being hit for some
reason.

Here's the code I'm using.

var n = startNode;
while (n) {
this.visited.push('[' + n.nodeName + ']');

if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.nextSibling;
}
}
 
E

Evertjan.

matth wrote on 01 nov 2007 in comp.lang.javascript:
I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range aren't
being hit. It seems like deeply nested nodes aren't being hit for some
reason.

Here's the code I'm using.

var n = startNode;
while (n) {
this.visited.push('[' + n.nodeName + ']');

if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.nextSibling;
}
}

Ty this reentrant function:

========== test.html ========

<body id=b>
<div id=d1>qq</div>
<div id=d2><div id=d21><span id=211>x</span></div>
<div id=d22>qq</div><div id=d23>qq</div></div>
<div id=d3>qq</div>
</body>

<script type='text/javascript'>

var a = [];
travel(document.body);
alert(a.join('\n'))

function travel(n){
if (n.tagName)
a.push(' tagName: '+n.tagName+ ' id: '+ n.id)
if (n.firstChild)
travel(n.firstChild)
if (n.nextSibling)
travel(n.nextSibling)
}

</script>

=============================
 
P

pr

matth said:
I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range aren't
being hit. It seems like deeply nested nodes aren't being hit for some
reason.

Here's the code I'm using.

var n = startNode;
while (n) {
this.visited.push('[' + n.nodeName + ']');

if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.nextSibling;
}
}

Hello again. That code won't retrieve nodes that are children of the
startNode, because you only visit firstChild for subsequent nodes. Is
that the difficulty? Could you supply URLs of pages that fail?
 
P

pr

Evertjan. said:
matth wrote on 01 nov 2007 in comp.lang.javascript:
I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range aren't
being hit. It seems like deeply nested nodes aren't being hit for some
reason.
[...]
<script type='text/javascript'>

var a = [];
travel(document.body);
alert(a.join('\n'))

function travel(n){
if (n.tagName)
a.push(' tagName: '+n.tagName+ ' id: '+ n.id)
if (n.firstChild)
travel(n.firstChild)
if (n.nextSibling)
travel(n.nextSibling)
}

</script>

Nice idea, but a DOM Range can contain some distinctly un-well-formed
HTML. We've already had some fun with this in a recent thread if you
want to take a look.
 
E

Evertjan.

pr wrote on 01 nov 2007 in comp.lang.javascript:
Evertjan. said:
matth wrote on 01 nov 2007 in comp.lang.javascript:
I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range
aren't being hit. It seems like deeply nested nodes aren't being hit
for some reason.
[...]
<script type='text/javascript'>

var a = [];
travel(document.body);
alert(a.join('\n'))

function travel(n){
if (n.tagName)
a.push(' tagName: '+n.tagName+ ' id: '+ n.id)
if (n.firstChild)
travel(n.firstChild)
if (n.nextSibling)
travel(n.nextSibling)
}

</script>

Nice idea, but a DOM Range can contain some distinctly un-well-formed
HTML.

This 'but' is nonsense IMHO, without explanation or example.

My code travels through the DOM-tree and documents all tagNamed nodes.

The given base node of the travel simply is the parameter of travel().

It does not matter if the HTML is un-well-formed, DOM nodes either have
children and/or siblings, or they have not, and they always either have a
parent or or they are at the top or they are not part of the DOM-tree.
We've already had some fun with this in a recent thread if you
want to take a look.

I better don't wnat to look,
it will spoil your passed fun,
taking your sentence litterally. ;-)

=====================

Documenting the depth goes like this:

<script type='text/javascript'>

var a = [];
var depth = 1;
travel(document.body);
alert(a.join('\n'));

function travel(n){
if (n.tagName)
a.push('['+depth+'] tagName: '+n.tagName+' id: '+n.id);
if (n.firstChild){
depth++;
travel(n.firstChild);
depth--;
};
if (n.nextSibling)
travel(n.nextSibling);
};

</script>
 
M

matth

pr wrote on 01 nov 2007 in comp.lang.javascript:


Evertjan. said:
matth wrote on 01 nov 2007 in comp.lang.javascript:
I'm trying to iterate through nodes in a Selection Range, but I'm
having a bit of trouble determining why all nodes in the range
aren't being hit. It seems like deeply nested nodes aren't being hit
for some reason.
[...]
<script type='text/javascript'>
var a = [];
travel(document.body);
alert(a.join('\n'))
function travel(n){
if (n.tagName)
a.push(' tagName: '+n.tagName+ ' id: '+ n.id)
if (n.firstChild)
travel(n.firstChild)
if (n.nextSibling)
travel(n.nextSibling)
}
</script>
Nice idea, but a DOM Range can contain some distinctly un-well-formed
HTML.

This 'but' is nonsense IMHO, without explanation or example.

My code travels through the DOM-tree and documents all tagNamed nodes.

The given base node of the travel simply is the parameter of travel().

It does not matter if the HTML is un-well-formed, DOM nodes either have
children and/or siblings, or they have not, and they always either have a
parent or or they are at the top or they are not part of the DOM-tree.
We've already had some fun with this in a recent thread if you
want to take a look.

I better don't wnat to look,
it will spoil your passed fun,
taking your sentence litterally. ;-)

=====================

Documenting the depth goes like this:

<script type='text/javascript'>

var a = [];
var depth = 1;
travel(document.body);
alert(a.join('\n'));

function travel(n){
if (n.tagName)
a.push('['+depth+'] tagName: '+n.tagName+' id: '+n.id);
if (n.firstChild){
depth++;
travel(n.firstChild);
depth--;
};
if (n.nextSibling)
travel(n.nextSibling);

};

</script>

Thank you both for your help.
I'm going to include the full bookmarklet code I'm using.
A specific instance that fails is when I select the links and image
typically present at the top of drudgereport.com (the stuff right
above Drudge's header graphic).

I'll try implementing your code Evertjan, and report back.

javascript:({
visited: [],
iterate: function(r, startNode, endNode) {
var n = startNode;
var sel = window.getSelection().toString();
var text = null;
while (n) {
if ((n == r.startContainer || n == r.endContainer) && n.nodeType ==
3) {
if (r.startContainer == r.endContainer) {
text = n.nodeValue.substring(r.startOffset, r.endOffset);
} else if (n == r.startContainer) {
text = n.nodeValue.substring(r.startOffset);
} else if (n == r.endContainer) {
text = n.nodeValue.substring(0, r.endOffset);
}
} else if (n.nodeType == 3) {
text = n.nodeValue;
}

this.visited.push('[' + n.nodeName + ']: ' + (n.nodeType == 3 &&
text != null ? text : ''));
text = null;

if (n == endNode) {
break;
}

if (n.hasChildNodes()) {
n = n.firstChild;
} else if (n.nextSibling){
n = n.nextSibling;
} else {
n = n.parentNode.nextSibling;
}
}
},
go: function(r) {
this.iterate(r, r.startContainer, r.endContainer);
var w = window.open(), d = w.document;
var sel = window.getSelection().toString();
d.write(this.visited.join('<br>'));
d.write("<br><br>Selection:<br>" + sel);
d.close();
}
}).go(window.getSelection().getRangeAt(0));
 
P

pr

matth said:
Thank you both for your help.
I'm going to include the full bookmarklet code I'm using.
A specific instance that fails is when I select the links and image
typically present at the top of drudgereport.com (the stuff right
above Drudge's header graphic).

I'll try implementing your code Evertjan, and report back.
[...]

Here's my latest take on the question (another bookmarklet), which you
may find useful. As far as I'm aware, it lists all the nodes beneath a
range's commonAncestorContainer, but the bug is that (so far) it doesn't
indicate whether it's arriving at a node or leaving it. Still, it does
show a couple of things missing in your code:

- child offsets of the startContainer and endContainer when the parent
is an element object or another node having children.

- a slightly different approach to ascending node ancestors in
search of nextSibling (your version quits if there is no
parentNode.nextSibling).

- escaped debug output! This may explain the funny things at Drudge
Report (a couple of <noscript>'s in there).

See what you think...


javascript:

function RangeIterator() {
this.onNode = function (str, node) {}; /* default function: supply
your own */
this.iterate = function (r) {
var me = this, node = r.startContainer, offset = r.startOffset,
finalNode = r.endContainer, finalOffset = r.endOffset;

function visitNode(node, offset) {
var isFinal = (node == finalNode), lastChildIndex, i, c,
text = '';

switch (node.nodeType) {
case 3: /* text, CDATA, processing instruction, comment */
case 4:
case 7:
case 8:
text = node.nodeValue;

if (isFinal) {
text = text.substring(0, finalOffset);
}
if (offset) {
text = text.substring(offset);
}
me.onNode(text, node);
break;
default:
me.onNode(text, node);
lastChildIndex = isFinal ? finalOffset :
node.childNodes.length;

for (i = offset, c = node.childNodes.item(i);
i < lastChildIndex;
c = c.nextSibling, i++) {
if (!visitNode(c, 0)) {
return false;
}
}
}
return !isFinal;
}
/* end function */

while (visitNode(node, offset)) {
if (!node.nextSibling) {
node = node.parentNode;
/* doesn't indicate when it's leaving an incompletely
selected node */
offset = node.childNodes.length;
} else {
node = node.nextSibling;
offset = 0;
}
}
return true;
};
}
/* for testing: */
({
iterator: new RangeIterator(),
visited: [],
toHTML: function (a) {
return ['&lt;', '&gt;', '&amp;']['<>&'.indexOf(a)];},
go: function () {
var timer, w = window, s = w.getSelection(), i, wd, me = this;

this.iterator.onNode = function (str, node) {
me.visited.push(node.nodeName + ': ' + str);};
timer = +new Date();
if (s.rangeCount && this.iterator.iterate(s.getRangeAt(0))) {
w.alert(this.visited.length + ' node(s) visited in ' +
(+new Date() - timer) + ' ms.');

for (i = this.visited.length; i--; ) {
this.visited = this.visited.replace(/[<>&]/g,
this.toHTML);
}

w = window.open();
wd = w.document;
wd.write(this.visited.join('<br>'));
wd.close();
} else {
w.alert("No selection");
}
}
}).go();
 
M

matth

matth said:
Thank you both for your help.
I'm going to include the full bookmarklet code I'm using.
A specific instance that fails is when I select the links and image
typically present at the top of drudgereport.com (the stuff right
above Drudge's header graphic).
I'll try implementing your code Evertjan, and report back.

[...]

Here's my latest take on the question (another bookmarklet), which you
may find useful. As far as I'm aware, it lists all the nodes beneath a
range's commonAncestorContainer, but the bug is that (so far) it doesn't
indicate whether it's arriving at a node or leaving it. Still, it does
show a couple of things missing in your code:

- child offsets of the startContainer and endContainer when the parent
is an element object or another node having children.

- a slightly different approach to ascending node ancestors in
search of nextSibling (your version quits if there is no
parentNode.nextSibling).

- escaped debug output! This may explain the funny things at Drudge
Report (a couple of <noscript>'s in there).

See what you think...

javascript:

function RangeIterator() {
this.onNode = function (str, node) {}; /* default function: supply
your own */
this.iterate = function (r) {
var me = this, node = r.startContainer, offset = r.startOffset,
finalNode = r.endContainer, finalOffset = r.endOffset;

function visitNode(node, offset) {
var isFinal = (node == finalNode), lastChildIndex, i, c,
text = '';

switch (node.nodeType) {
case 3: /* text, CDATA, processing instruction, comment */
case 4:
case 7:
case 8:
text = node.nodeValue;

if (isFinal) {
text = text.substring(0, finalOffset);
}
if (offset) {
text = text.substring(offset);
}
me.onNode(text, node);
break;
default:
me.onNode(text, node);
lastChildIndex = isFinal ? finalOffset :
node.childNodes.length;

for (i = offset, c = node.childNodes.item(i);
i < lastChildIndex;
c = c.nextSibling, i++) {
if (!visitNode(c, 0)) {
return false;
}
}
}
return !isFinal;
}
/* end function */

while (visitNode(node, offset)) {
if (!node.nextSibling) {
node = node.parentNode;
/* doesn't indicate when it's leaving an incompletely
selected node */
offset = node.childNodes.length;
} else {
node = node.nextSibling;
offset = 0;
}
}
return true;
};
}
/* for testing: */
({
iterator: new RangeIterator(),
visited: [],
toHTML: function (a) {
return ['&lt;', '&gt;', '&amp;']['<>&'.indexOf(a)];},
go: function () {
var timer, w = window, s = w.getSelection(), i, wd, me = this;

this.iterator.onNode = function (str, node) {
me.visited.push(node.nodeName + ': ' + str);};
timer = +new Date();
if (s.rangeCount && this.iterator.iterate(s.getRangeAt(0))) {
w.alert(this.visited.length + ' node(s) visited in ' +
(+new Date() - timer) + ' ms.');

for (i = this.visited.length; i--; ) {
this.visited = this.visited.replace(/[<>&]/g,
this.toHTML);
}

w = window.open();
wd = w.document;
wd.write(this.visited.join('<br>'));
wd.close();
} else {
w.alert("No selection");
}
}
}).go();


Don't think I'm ignoring you! I'm doing my best. You threw up a Hail
Mary there, I'm going to need a minute to catch up. :)
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top