I'm trying to get all the DOM nodes that are within a range object, what's the best way to do this?
var selection = window.getSelection(); //what the user has selected
var range = selection.getRangeAt(0); //the first range of the selection
var startNode = range.startContainer;
var endNode = range.endContainer;
var allNodes = /*insert magic*/;
I've been been thinking of a way for the last few hours and came up with this:
var getNextNode = function(node, skipChildren){
//if there are child nodes and we didn't come from a child node
if (node.firstChild && !skipChildren) {
return node.firstChild;
}
if (!node.parentNode){
return null;
}
return node.nextSibling
|| getNextNode(node.parentNode, true);
};
var getNodesInRange = function(range){
var startNode = range.startContainer.childNodes[range.startOffset]
|| range.startContainer;//it's a text node
var endNode = range.endContainer.childNodes[range.endOffset]
|| range.endContainer;
if (startNode == endNode && startNode.childNodes.length === 0) {
return [startNode];
};
var nodes = [];
do {
nodes.push(startNode);
}
while ((startNode = getNextNode(startNode))
&& (startNode != endNode));
return nodes;
};
However when the end node is the parent of the start node it returns everything on the page. I'm sure I'm overlooking something obvious? Or maybe going about it in totally the wrong way.
MDC/DOM/range
Here's an implementation I came up with to solve this:
function getNextNode(node)
{
if (node.firstChild)
return node.firstChild;
while (node)
{
if (node.nextSibling)
return node.nextSibling;
node = node.parentNode;
}
}
function getNodesInRange(range)
{
var start = range.startContainer;
var end = range.endContainer;
var commonAncestor = range.commonAncestorContainer;
var nodes = [];
var node;
// walk parent nodes from start to common ancestor
for (node = start.parentNode; node; node = node.parentNode)
{
nodes.push(node);
if (node == commonAncestor)
break;
}
nodes.reverse();
// walk children and siblings from start until end is found
for (node = start; node; node = getNextNode(node))
{
nodes.push(node);
if (node == end)
break;
}
return nodes;
}
The getNextNode will skip your desired endNode recursively if its a parent node.
Perform the conditional break check inside of the getNextNode instead:
var getNextNode = function(node, skipChildren, endNode){
//if there are child nodes and we didn't come from a child node
if (endNode == node) {
return null;
}
if (node.firstChild && !skipChildren) {
return node.firstChild;
}
if (!node.parentNode){
return null;
}
return node.nextSibling
|| getNextNode(node.parentNode, true, endNode);
};
and in while statement:
while (startNode = getNextNode(startNode, false , endNode));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With