在contentEditable元素中插入HTML元素

6

我有一个contentEditable的div,我想插入HTML标签(一个简单的span元素)。

是否有跨浏览器的解决方案,允许我在div选择或光标位置上插入这些标签。如果页面上选中了其他内容(不在div中),我希望将标签附加到div的末尾。

谢谢


你使用JS库吗? - Šime Vidas
2个回答

9

以下是一个快速入门

// get the selection range (or cursor     position)
var range = window.getSelection().getRangeAt(0); 
// create a span
var newElement = document.createElement('span');
newElement.id = 'myId';
newElement.innerHTML = 'Hello World!';

// if the range is in #myDiv ;)
if(range.startContainer.parentNode.id==='myDiv') {
   // delete whatever is on the range
   range.deleteContents();
   // place your span
   range.insertNode(newElement);
}

我没有IE,但在firefox、chrome和safari上运行良好。也许您想玩一下range.startContainer,只有在selection是在contentEditable div上进行时才继续。

编辑:根据quirksmode range intro的说法,您需要更改window.getSelection()部分以使其兼容IE。

var userSelection;
if (window.getSelection) {
    userSelection = window.getSelection();
}
else if (document.selection) { // should come last; Opera!
    userSelection = document.selection.createRange();
}

感谢您的快速回复。这并没有解决我的问题,还有一个缺失的部分:我想只在 div 中插入节点,即使用户试图选择其他地方... 我该如何检查用户是否在 div 中进行选择? - Elie
range.startContainer(如我在答案中所写)返回范围开始的DOM元素。您可以在插入新内容之前检查该元素是否为您的div。 - Mauricio
你并没有真正解决IE的问题,而且检查范围是否包含在<div>中的方法不够严谨:如果范围在嵌套元素内开始,或者范围不是在文本节点内开始,或者范围结束在<div>外部,它将会给出错误的结果。 - Tim Down
是的,那是一个良好的开端。你的答案很棒。 - Mauricio

7
以下代码适用于所有主流浏览器(包括IE 6),还可以处理选择结束位置在您的<div>之外或选择包含在<div>内的子元素(或更深嵌套)的情况。
2019年补充说明:insertNodeOverSelection的第二个分支仅适用于IE <= 8,现在可以删除。

function isOrContainsNode(ancestor, descendant) {
    var node = descendant;
    while (node) {
        if (node === ancestor) return true;
        node = node.parentNode;
    }
    return false;
}

function insertNodeOverSelection(node, containerNode) {
    var sel, range, html;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            if (isOrContainsNode(containerNode, range.commonAncestorContainer)) {
                range.deleteContents();
                range.insertNode(node);
            } else {
                containerNode.appendChild(node);
            }
        }
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        if (isOrContainsNode(containerNode, range.parentElement())) {
            html = (node.nodeType == 3) ? node.data : node.outerHTML;
            range.pasteHTML(html);
        } else {
            containerNode.appendChild(node);
        }
    }
}
<input type="button" onmousedown="insertNodeOverSelection(document.createTextNode('[NODE]'), document.getElementById('test'));" value="insert">

<div contenteditable="true">
    <div id="test" style="background-color: lightgoldenrodyellow">
        This is the editable element where the insertion will happen. Select something or place the cursor in here, then hit the button above
    </div>
    <div>
        No insertion will happen here
    </div>
</div>


嗨,蒂姆,距离这个答案已经过去8年了,我们是否仍需要“insertNodeOverSelection”函数的最后一部分来支持旧浏览器/ IE?...从“else if”开始? - Norman
1
@Norman:不是这样的。自IE9版本以来,它已经具备了必要的Range和Selection API。 - Tim Down

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接