有没有方法可以在Chrome浏览器中防止execCommand("insertHTML")删除属性?

24

环境是Chrome 37.0.2062.120 m。

我正在使用execCommand将HTML插入可编辑div中。我的execCommand调用如下:

function insertHTML(){
    document.execCommand('insertHTML', false, '<span id="myId">hi</span>');
}

当可编辑的 div 看起来像这样:

<div contenteditable="true">
    some [insertion point] content
</div> 

我使用execCommand将HTML插入到一个可编辑的div中,所有HTML的属性都按预期插入,最终得到这个:

<div contenteditable="true">
    some <span id="myId">hi</span> content
</div> 

然而,当我将完全相同的HTML代码插入到这个结构中时:

<div contenteditable="true">
    some content
    <div>more [insertion point] content</div>
</div>
被插入的标签将删除其属性,最终看起来像这样:
<div contenteditable="true">
    some content
    <div>more <span style="font-size: 10pt;">hi</span> content</div>
</div>

有没有任何方法可以防止这种情况发生?

2个回答

29

在这种特定情况下,我建议使用 Range.insertNode,它可以让你完全控制插入的内容:

function insertHTML() {
    var sel, range;
    if (window.getSelection && (sel = window.getSelection()).rangeCount) {
        range = sel.getRangeAt(0);
        range.collapse(true);
        var span = document.createElement("span");
        span.id = "myId";
        span.appendChild( document.createTextNode("hi") );
        range.insertNode(span);

        // Move the caret immediately after the inserted span
        range.setStartAfter(span);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }
}

function isOrIsAncestorOf(ancestor, descendant) {
  var n = descendant;
  while (n) {
    if (n === ancestor) {
      return true;
    } else {
      n = n.parentNode;
    }
  }
  return false;
}

function nodeContainsSelection(node) {
  var sel, range;
  if (window.getSelection && (sel = window.getSelection()).rangeCount) {
    range = sel.getRangeAt(0);
    return isOrIsAncestorOf(node, range.commonAncestorContainer);
  }
  return false;
}

function insertHTML() {
  var sel, range;
  if (window.getSelection && (sel = window.getSelection()).rangeCount) {
    range = sel.getRangeAt(0);
    range.collapse(true);
    var span = document.createElement("span");
    span.id = "myId";
    span.appendChild( document.createTextNode("hi") );
    range.insertNode(span);

    // Move the caret immediately after the inserted span
    range.setStartAfter(span);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
  }
}

window.onload = function() {
  document.getElementById("inserter").onmousedown = function() {
    var editor = document.getElementById("editor");
    if (nodeContainsSelection(editor)) {
      insertHTML();
      return false;
    }
  };
};
span {
  font-weight: bold;
  color: green;
}
<input type="button" id="inserter" value="Insert span">
<div contenteditable="true" id="editor">
    some content
</div>


1
这是一个很好的替代方案,非常感谢。我注意到在代码片段中,如果选择不在可编辑的 div 中,我可以在按钮旁边以及 div 中添加 span。有没有一种方法来限制它? - sonicblis
1
@sonicblis:是的:你可以检查选择范围的commonAncestorContainer属性是否包含在可编辑的div中。我已经将示例代码添加到片段中。 - Tim Down
6
注意:与 execCommand 不同的是,这不会修改浏览器的撤销栈。这意味着您无法使用 Ctrl+Z 撤消在可编辑内容元素中插入的内容。 - Danilo Bargen
1
保留撤销堆栈的另一种方法是在插入后通过单独的JS手动添加/修改属性。属性修改本身将不在浏览器的撤销堆栈中,但其余HTML的插入在撤销/重做堆栈的控制范围内。 - asinix
2
这个例子遗憾地没有保留 contenteditable 区域的撤销历史记录(因此无法撤销)。 - SuperNova
显示剩余2条评论

1

一个解决方案可能是不使用即使用非元素。我已经使用了<em>元素来解决Chrome中类似的问题,因为斜体对我来说很好 - 因此,在Chrome中<span>调整“错误”不会影响我。在这里查看讨论:讨论CKEdit bug,但这里也相关


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