Selection Ranges and Nodes

M

matth

Anyone know how to iterate over every node in a range? Moz's
documentation seems a little scant on this particular issues, unless
I'm overlooking something.
 
P

pr

matth said:
Anyone know how to iterate over every node in a range? Moz's
documentation seems a little scant on this particular issues, unless
I'm overlooking something.

If you don't care whether the nodes are in place, the quick way is

f = r.cloneContents();

to create a document fragment and proceed from f.firstChild using
nextSibling, visiting childNodes along the way.

If you want to use the nodes in context, then it's something horrible
like this (bookmarklet):

javascript:var nothing={
visited: [],
iterate: function (startNode, endNode) {
var n = startNode;

while (n) {
this.visited.push(n.nodeName);
if (n == endNode) { /* done */
return false;
}

if (n.hasChildNodes()) {
if (!this.iterate(n.firstChild, endNode)) {
return false;
}
}

n = n.nextSibling;
}

n = startNode;
while (!n.nextSibling ) { /* go up to find next content */
n = n.parentNode;
if (n.nodeType == 9){
alert('something badly wrong');
return false;
}
}
this.iterate(n.nextSibling, endNode);
},
go: function(r) {
this.iterate(r.startContainer, r.endContainer);
var a = this.visited.join('\n');
alert('visited ' + this.visited.length + ' nodes between\n' +
a.substring(0, 50) + '...\nand\n...' + a.substring(a.length - 50));
}
}.go(window.getSelection().getRangeAt(0));


This is only a quickie and I suspect it's more recursive than it needs
to be, but the critical bit is that the range may end at a shallower
nesting level than the start; a bit unfamiliar if you're used to working
with well-formed fragments.
 
P

pr

pr said:
matth said:
Anyone know how to iterate over every node in a range? Moz's
documentation seems a little scant on this particular issues, unless
I'm overlooking something.

If you don't care whether the nodes are in place, the quick way is

f = r.cloneContents();

to create a document fragment and proceed from f.firstChild using
nextSibling, visiting childNodes along the way.

If you want to use the nodes in context, then it's something horrible
like this (bookmarklet):
[snipped code]

Sorry, that *was* horrible. This is better:

javascript:({
visited: [],
iterate: function(startNode, endNode) {
var n = startNode;

while (n) {
this.visited.push('[' + n.nodeName + ']: ' + (n.nodeType == 3 ?
n.nodeValue : ''));

if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.nextSibling;
}
}
},
go: function(r) {
this.iterate(r.startContainer, r.endContainer);
var w = window.open(), d = w.document;

d.write(this.visited.join('<br>'));
d.close();
}
}).go(window.getSelection().getRangeAt(0));
 
M

matth

Hmm.. what you have will be a great start for me.

function getElementsFromSelection() {
var sel, range;
if (window.getSelection) {
sel = window.getSelection();
}

if (0 < sel.rangeCount) {
range = document.createRange();
range.setStart(sel.anchorNode,sel.anchorOffset);
range.setEnd(sel.focusNode,sel.focusOffset);
document.getElementById("result").innerHTML = range;
document.getElementById("result2").innerHTML = "anchorNode:" +
sel.anchorNode + "<br>anchorOffset:" + sel.anchorOffset +
"<br>focusNode:" + sel.focusNode + "<br>focusOffset:" +
sel.focusOffset;
}
}

I can finally create a Moz/Opera range and it works beautifully, I'm
going to adapt what you have into my code and see what happens. I'll
keep you posted (no pun). If you spot anything, let me know. I hate
working with JS.
 
M

matth

Actually, forget about my code, what you posted is close to exactly
what I've been searching for. My only qualm is that I need only the
text in each node that is selected, as opposed to what nodeValue
returns.

I'm hard at work, but if you find a solution please get in touch.
 
P

pr

matth said:
Actually, forget about my code, what you posted is close to exactly
what I've been searching for. My only qualm is that I need only the
text in each node that is selected, as opposed to what nodeValue
returns.

I'm hard at work, but if you find a solution please get in touch.

If you mean that you want partially selected text in the range's
start/endContainers, then use the start/endOffset. Something like

if (n == r.startContainer && n.nodeType == 3) {
var text = n.nodeValue.substring(r.startOffset);
}

or at the end:

text = n.nodeValue.substring(0, r.endOffset);

If in doubt:

http://www.w3.org/TR/DOM-Level-2-Traversal-Range
 
M

matth

If you mean that you want partially selected text in the range's
start/endContainers, then use the start/endOffset.

I'm aiming for the content in each node that is actually selected,
instead of the entire contents of the node (which is what .nodeValue
returns).

For example, if I selected "Hello W" in the text node "Hello World," I
want the former. I think I may have gotten closer with your the aide
of your previous post, but I seem to be off in some instances.
Sometimes text gets cut off and I'm not able to pin-point why.

javascript:({
visited: [],
iterate: function(r, startNode, endNode) {
var n = startNode;
var sel = window.getSelection().toString();
var text = null;
while (n) {
if (n.nodeType == 3) {
var text = n.nodeValue.substring(r.startOffset, r.endOffset);
}
this.visited.push('[' + n.nodeName + ']: ' + (n.nodeType == 3 &&
text != null ? text : ''));
text=null;
if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.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));
 
M

matth

Haha, I think I got it... you tell me :)

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 {
text = n.nodeValue;
}

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

if (n == endNode) {
break;
}

if (n != startNode && n.hasChildNodes()) {
n = n.firstChild;
} else {
while (!n.nextSibling) {
n = n.parentNode;
}
n = n.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));
 
M

matth

If you mean that you want partially selected text in the range's
start/endContainers, then use the start/endOffset. Something like

if (n == r.startContainer && n.nodeType == 3) {
var text = n.nodeValue.substring(r.startOffset);
}

or at the end:

text = n.nodeValue.substring(0, r.endOffset);

If in doubt:

http://www.w3.org/TR/DOM-Level-2-Traversal-Range

Strange, sometimes "n has no properties" according to the error
console. Also, the bookmarklet never seems to pick up on nodes.
Wonder what the deal is with both the problems. I'm trying to figure
out if the same applies to other types of nodes and I'm not picking up
on it. Weird.
 
T

Thomas 'PointedEars' Lahn

pr said:
Sorry, that *was* horrible. This is better:

javascript:({

Even better would be not to include the unnecessary and error-prone
`javascript' label in the first place.


PointedEars
 
P

pr

matth said:
Strange, sometimes "n has no properties" according to the error
console. Also, the bookmarklet never seems to pick up on nodes.
Wonder what the deal is with both the problems. I'm trying to figure
out if the same applies to other types of nodes and I'm not picking up
on it. Weird.
[/QUOTE]

I don't see that, but it could be dodgy HTML in your testing page (try
HTML Tidy) or the fact that debug output isn't escaped for
document.write() so if the browser encounters a '<' in the text, it
might regard it as the start of a tag.

Aside from that, I've discovered a fun difference in the way Firefox and
Opera represent Select All (CTRL + A) in window.getSelection(), but you
look to be well on your way to coping with it now.
 
M

matth

matth said:
Strange, sometimes "n has no properties" according to the error
console. Also, the bookmarklet never seems to pick up on nodes.
Wonder what the deal is with both the problems. I'm trying to figure
out if the same applies to other types of nodes and I'm not picking up
on it. Weird.[/QUOTE]

I don't see that, but it could be dodgy HTML in your testing page (try
HTML Tidy) or the fact that debug output isn't escaped for
document.write() so if the browser encounters a '<' in the text, it
might regard it as the start of a tag.

Aside from that, I've discovered a fun difference in the way Firefox and
Opera represent Select All (CTRL + A) in window.getSelection(), but you
look to be well on your way to coping with it now.[/QUOTE]

I am well on my way! Special thanks to you for coaching me through
this. Really, I can't thank you enough.

I'd be interested in hearing your novel approach... is it novel? :)

I'm not sure if it is dodgy HTML, because no matter the conditions, it
seems <IMG> tags just don't pop up in the output. I'm not sure if it's
problem with the way I'm walking the range tree or what. By the way,
I'm not using a test page. Instead, I use the code as a bookmarklet to
test different pages from around the web to see how nice (or not) the
code plays in different conditions. No matter the condition, I can't
view <IMG>. This is kind of why I'm scratching my head.

Again, thank you so much for your help, it's taken me an inordinate
amount of time to reach this point. If you have any final pointers,
I'd love to hear them, but I'm grateful regardless.

Have a good one!

PS - Here's what I use to help make the bookmarklet:
http://subsimple.com/bookmarklets/jsbuilder.htm

Very handy.
 

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

Forum statistics

Threads
474,148
Messages
2,570,838
Members
47,385
Latest member
Joneswilliam01

Latest Threads

Top