JS: get an array of all selected nodes in a contentEditable div

Hi, I have been working with contentEditable for a long time and I think I have a nice pen. One thing that dodges me is to get an array of links to all nodes that are partially or completely within the user's choice. Anyone got an idea?

Here's something to start with:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript"> function getSelectedNodes(){ var sel = window.getSelection(); try{var frag=sel.getRangeAt(0).cloneContents()}catch(e){return(false);} var tempspan = document.createElement("span"); tempspan.appendChild(frag); var selnodes = Array() //<<- how do I fill this array?? var output = '' for(i in selnodes){ output += "A "+selnodes[i].tagName+" was found\n" //do something cool with each element here... } return(output) } </script> </head> <body contentEditable="true" onkeypress="return(keypress(event))"> <div>This <strong>div</strong> is <em>content</em> <span class='red'>editable</span> and has a couple of <em><strong>child nodes</strong></em> within it</div> <br /> <br /> <a href="#" onmouseover="alert(getSelectedNodes())">hover here</a> </body> </html> 
+8
javascript html wysiwyg
source share
3 answers

Here is a version that gives you selected and partially selected nodes, not clones. Alternatively, you can use my Rangy library, which has the getNodes() method of its range objects and works in IE <9.

 function nextNode(node) { if (node.hasChildNodes()) { return node.firstChild; } else { while (node && !node.nextSibling) { node = node.parentNode; } if (!node) { return null; } return node.nextSibling; } } function getRangeSelectedNodes(range) { var node = range.startContainer; var endNode = range.endContainer; // Special case for a range that is contained within a single node if (node == endNode) { return [node]; } // Iterate nodes until we hit the end container var rangeNodes = []; while (node && node != endNode) { rangeNodes.push( node = nextNode(node) ); } // Add partially selected nodes at the start of the range node = range.startContainer; while (node && node != range.commonAncestorContainer) { rangeNodes.unshift(node); node = node.parentNode; } return rangeNodes; } function getSelectedNodes() { if (window.getSelection) { var sel = window.getSelection(); if (!sel.isCollapsed) { return getRangeSelectedNodes(sel.getRangeAt(0)); } } return []; } 
+21
source share

You are so close! When you add a Document Fragment to a temporary span , you turned them into a managed group, accessible through an array.

  var selnodes = tempspan.childNodes; 

In addition, you are setting yourself up for some problems with this for(i in selnodes) , which returns the elements in the array, the PLUS property length and the __proto__ property, and any other properties that the object may have.

You should use only those types of for loops when navigating through object properties, and then always with if (obj.hasOwnProperty[i]) to filter properties inherited from the prototype.

When navigating through arrays, use:

  for(var i=0,u=selnodes.length;i<u;i++) 

Finally, once you load this array, you really need to check each element to see if it is a DOM node or a Text node before you can process it. We can do this by checking to see if it supports the tagName property.

  if (typeof selnodes[i].tagName !== 'undefined') 

Everybody is here:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript"> function getSelectedNodes(){ var sel = window.getSelection(); try{var frag=sel.getRangeAt(0).cloneContents()}catch(e){return(false);} var tempspan = document.createElement("span"); tempspan.appendChild(frag); console.log(tempspan); window.selnodes = tempspan.childNodes; var output = '' for(var i=0, u=selnodes.length;i<u;i++){ if (typeof selnodes[i].tagName !== 'undefined'){ output += "A "+selnodes[i].tagName+" was found\n" } else output += "Some text was found: '"+selnodes[i].textContent+"'\n"; //do something cool with each element here... } return(output) } </script> </head> <body contentEditable="true" onkeypress="return(keypress(event))"> <div>This <strong>div</strong> is <em>content</em> <span class='red'>editable</span> and has a couple of <em><strong>child nodes</strong></em> within it</div> <br /> <br /> <a href="#" onmouseover="alert(getSelectedNodes())">hover here</a> </body> </html> 
+1
source share

Below code is a model for solving your problem, below code returns all selected node, which is in the range

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>payam jabbari</title> <script src="http://code.jquery.com/jquery-2.0.2.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function(){ var startNode = $('p.first').contents().get(0); var endNode = $('span.second').contents().get(0); var range = document.createRange(); range.setStart(startNode, 0); range.setEnd(endNode, 5); var selection = document.getSelection(); selection.addRange(range); // below code return all nodes in selection range. this code work in all browser var nodes = range.cloneContents().querySelectorAll("*"); for(var i=0;i<nodes.length;i++) { alert(nodes[i].innerHTML); } }); </script> </head> <body> <div> <p class="first">Even a week ago, the idea of a Russian military intervention in Ukraine seemed far-fetched if not totally alarmist. But the arrival of Russian troops in Crimea over the weekend has shown that he is not averse to reckless adventures, even ones that offer little gain. In the coming days and weeks</p> <ol> <li>China says military will respond to provocations.</li> <li >This Man Has Served 20 <span class="second"> Years—and May Die—in </span> Prison for Marijuana.</li> <li>At White House, Israel Netanyahu pushes back against Obama diplomacy.</li> </ol> </div> </body> </html> 
0
source share

All Articles