JS:获取contentEditable div中所有选定节点的数组

时间:2011-10-16 02:30:21

标签: javascript html wysiwyg

您好我已经使用contentEditable了一段时间了,我觉得我对它有很好的处理能力。回避我的一件事是如何获得部分或完全在用户选择范围内的所有节点的引用数组。有人有想法吗?

这是从一开始:

<!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>

3 个答案:

答案 0 :(得分:27)

这是一个版本,它为您提供实际选定和部分选择的节点而不是克隆。或者,您可以使用我的Rangy库,该库具有其Range对象的getNodes()方法,并且可以在IE&lt; 1中工作。 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 [];
}

答案 1 :(得分:1)

你真是太近了!当您将Document Fragment附加到临时span元素时,您已将其转换为可管理的组,可通过可靠的childNodes数组访问。

    var selnodes = tempspan.childNodes;

此外,您正在为for(i in selnodes)循环设置一些问题,这将返回数组中的元素, PLUS length属性,以及__proto__属性,以及对象可能具有的任何其他属性。

在循环对象中的属性时,您应该只使用这些for循环,然后总是if (obj.hasOwnProperty[i])一起过滤掉从原型继承的属性。

循环遍历数组时,请使用:

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

最后,一旦加载了该数组,您实际上需要先检查每个元素,看它是否为DOM节点是一个Text节点,然后才能处理它。我们可以通过检查它是否支持tagName属性来实现。

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

以下是整个事情:

<!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>

答案 2 :(得分:0)

下面的代码是解决问题的示例,下面的代码返回范围内所有选定的节点

<!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's Netanyahu pushes back against Obama diplomacy.</li>
</ol>
</div>
</body>
</html>