ContentEditable DIV中

时间:2015-07-13 19:13:56

标签: html internet-explorer contenteditable

我有一个ContentEditable设置为TRUE的DIV。在其中有几个跨度,ContentEditable设置为FALSE。

我捕获BackSpace键,以便如果光标下的元素是<span>我可以删除它。

问题是它只与奇数跨度交替工作。

因此,例如,使用下面的html代码,将光标放在DIV中文本的末尾,然后一直按退格键直到div的开头。观察它将选择/删除第一个跨度,然后离开第二个跨度,然后选择/删除第三个跨度,然后离开第四个跨度,依此类推。

此行为仅适用于Internet Explorer。它与Firefox上的预期完全一样。

我应该如何在Internet Explorer中使行为一致?

以下html代码可用于重现行为:

var EditableDiv = document.getElementById('EditableDiv');

EditableDiv.onkeydown = function(event) {
  var ignoreKey;
  var key = event.keyCode || event.charCode;
  if (!window.getSelection) return;
  var selection = window.getSelection();
  var focusNode = selection.focusNode,
    anchorNode = selection.anchorNode;

  if (key == 8) { //backspace
    if (!selection.isCollapsed) {
      if (focusNode.nodeName == 'SPAN' || anchorNode.nodeName == 'SPAN') {
        anchorNode.parentNode.removeChild(anchorNode);
        ignoreKey = true;
      }
    } else if (anchorNode.previousSibling && anchorNode.previousSibling.nodeName == 'SPAN' && selection.anchorOffset <= 1) {
      SelectText(event, anchorNode.previousSibling);
      ignoreKey = true;
    }
  }
  if (ignoreKey) {
    var evt = event || window.event;
    if (evt.stopPropagation) evt.stopPropagation();
    evt.preventDefault();
    return false;
  }
}

function SelectText(event, element) {
  var range, selection;
  EditableDiv.focus();
  if (document.body.createTextRange && element.nodeName == 'SPAN') {
    range = document.body.createTextRange();
    range.moveToElementText(element);
    range.select();
  } else if (window.getSelection) {
    selection = window.getSelection();
    range = document.createRange();
    range.selectNodeContents(element);
    selection.removeAllRanges();
    selection.addRange(range);
  }
  var evt = (event) ? event : window.event;
  if (evt.stopPropagation) evt.stopPropagation();
  if (evt.cancelBubble != null) evt.cancelBubble = true;
  return false;
}
#EditableDiv {
  height: 75px;
  width: 500px;
  font-family: Consolas;
  font-size: 10pt;
  font-weight: normal;
  letter-spacing: 1px;
  background-color: white;
  overflow-y: scroll;
  overflow-x: hidden;
  border: 1px solid black;
  padding: 5px;
}
#EditableDiv span {
  color: brown;
  font-family: Verdana;
  font-size: 8.5pt;
  min-width: 10px;
  _width: 10px;
}
#EditableDiv p,
#EditableDiv br {
  display: inline;
}
<div id="EditableDiv" contenteditable="true">
  &nbsp;(<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field1</span> < 500) <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>OR</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field2</span> > 100 <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>AND</span> (<span contenteditable='false' onclick='SelectText(event, this);'
    unselectable='on'>Field3</span> <=200) ) 
</div>

修改

仅供参考。我也在MSDN Forum中提出了这个问题。

1 个答案:

答案 0 :(得分:7)

对此的挑战是将IE11从右侧直接退格到&lt; span&gt;。然后下一个退格将选择并突出显示它。这似乎是一个如此简单的目标,但IE11只是不会合作。应该有一个快速简单的补丁,对吧?因此,错误开始了。

我想出的方法是将树向后移动到第一个非空节点,清除之间的空节点以安抚IE,然后评估几个条件。如果插入符号 应该的最右侧,那么手动执行(因为IE不会),方法是创建一个新的范围obj,其中的选择位于结尾处。 &lt; span&gt;。

online demo

我在两个跨度相互拖动的情况下为IE添加了额外的kludge。例如,Field2Field3。当你从右边退回到Field3上,然后再次退格以删除它时,IE会将插入符号向左跳过Field2。跳过Field2。哎呀。解决方法是拦截它并在这对跨距之间插入一个空格。我不相信你会对此感到高兴。但是,你知道,这是一种解决方法。无论如何,这又出现了另一个错误,IE将插入的空间更改为两个空文本节点。更多的。因此,解决方法的解决方法。请参阅非isCollapsed代码。

CODE SNIPPET

var EditableDiv = document.getElementById('EditableDiv');

       EditableDiv.onkeydown = function(event) {
         var ignoreKey;
         var key = event.keyCode || event.charCode;
         if (!window.getSelection) return;
         var selection = window.getSelection();
         var focusNode = selection.focusNode,
           anchorNode = selection.anchorNode;

         var anchorOffset = selection.anchorOffset;

         if (!anchorNode) return

         if (anchorNode.nodeName.toLowerCase() != '#text') {
           if (anchorOffset < anchorNode.childNodes.length)
             anchorNode = anchorNode.childNodes[anchorOffset]
           else {
             while (!anchorNode.nextSibling) anchorNode = anchorNode.parentNode // this might step out of EditableDiv to "justincase" comment node
             anchorNode = anchorNode.nextSibling
           }
           anchorOffset = 0
         }

         function backseek() {

           while ((anchorOffset == 0) && (anchorNode != EditableDiv)) {

             if (anchorNode.previousSibling) {
               if (anchorNode.previousSibling.nodeName.toLowerCase() == '#text') {
                 if (anchorNode.previousSibling.nodeValue.length == 0)
                   anchorNode.parentNode.removeChild(anchorNode.previousSibling)
                 else {
                   anchorNode = anchorNode.previousSibling
                   anchorOffset = anchorNode.nodeValue.length
                 }
               } else if ((anchorNode.previousSibling.offsetWidth == 0) && (anchorNode.previousSibling.offsetHeight == 0))
                 anchorNode.parentNode.removeChild(anchorNode.previousSibling)

               else {
                 anchorNode = anchorNode.previousSibling

                 while ((anchorNode.lastChild) && (anchorNode.nodeName.toUpperCase() != 'SPAN')) {

                   if ((anchorNode.lastChild.offsetWidth == 0) && (anchorNode.lastChild.offsetHeight == 0))
                     anchorNode.removeChild(anchorNode.lastChild)

                   else if (anchorNode.lastChild.nodeName.toLowerCase() != '#text')
                     anchorNode = anchorNode.lastChild

                   else if (anchorNode.lastChild.nodeValue.length == 0)
                     anchorNode.removeChild(anchorNode.lastChild)

                   else {
                     anchorNode = anchorNode.lastChild
                     anchorOffset = anchorNode.nodeValue.length
                       //break							//don't need to break, textnode has no children
                   }
                 }
                 break
               }
             } else
               while (((anchorNode = anchorNode.parentNode) != EditableDiv) && !anchorNode.previousSibling) {}
           }
         }

         if (key == 8) { //backspace
           if (!selection.isCollapsed) {

             try {
               document.createElement("select").size = -1
             } catch (e) { //kludge for IE when 2+ SPANs are back-to-back adjacent

               if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
                 backseek()
                 if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
                   var k = document.createTextNode(" ") // doesn't work here between two spans.  IE makes TWO EMPTY textnodes instead !
                   anchorNode.parentNode.insertBefore(k, anchorNode) // this works
                   anchorNode.parentNode.insertBefore(anchorNode, k) // simulate "insertAfter"
                 }
               }
             }


           } else {
             backseek()

             if (anchorNode == EditableDiv)
               ignoreKey = true

             else if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
               SelectText(event, anchorNode)
               ignoreKey = true
             } else if ((anchorNode.nodeName.toLowerCase() == '#text') && (anchorOffset <= 1)) {

               var prev, anchorNodeSave = anchorNode,
                 anchorOffsetSave = anchorOffset
               anchorOffset = 0
               backseek()
               if (anchorNode.nodeName.toUpperCase() == 'SPAN') prev = anchorNode
               anchorNode = anchorNodeSave
               anchorOffset = anchorOffsetSave

               if (prev) {
                 if (anchorOffset == 0)
                   SelectEvent(prev)

                 else {
                   var r = document.createRange()
                   selection.removeAllRanges()

                   if (anchorNode.nodeValue.length > 1) {
                     r.setStart(anchorNode, 0)
                     selection.addRange(r)
                     anchorNode.deleteData(0, 1) 
                   } 
                   else {
                     for (var i = 0, p = prev.parentNode; true; i++)
                       if (p.childNodes[i] == prev) break
                     r.setStart(p, ++i)
                     selection.addRange(r)
                     anchorNode.parentNode.removeChild(anchorNode)
                   }
                 }
                 ignoreKey = true
               }
             }
           }
         }
         if (ignoreKey) {
           var evt = event || window.event;
           if (evt.stopPropagation) evt.stopPropagation();
           evt.preventDefault();
           return false;
         }
       }

       function SelectText(event, element) {
         var range, selection;
         EditableDiv.focus();
         if (window.getSelection) {
           selection = window.getSelection();
           range = document.createRange();
           range.selectNode(element)
           selection.removeAllRanges();
           selection.addRange(range);
         } else {
           range = document.body.createTextRange();
           range.moveToElementText(element);
           range.select();
         }
         var evt = (event) ? event : window.event;
         if (evt.stopPropagation) evt.stopPropagation();
         if (evt.cancelBubble != null) evt.cancelBubble = true;
         return false;
       }
#EditableDiv {
          height: 75px;
          width: 500px;
          font-family: Consolas;
          font-size: 10pt;
          font-weight: normal;
          letter-spacing: 1px;
          background-color: white;
          overflow-y: scroll;
          overflow-x: hidden;
          border: 1px solid black;
          padding: 5px;
        }
        #EditableDiv span {
          color: brown;
          font-family: Verdana;
          font-size: 8.5pt;
          min-width: 10px;
          /*_width: 10px;*/
          /* what is this? */
        }
        #EditableDiv p,
        #EditableDiv br {
          display: inline;
        }
<div id="EditableDiv" contenteditable="true">
&nbsp;(<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field1</span> < 500)  <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>OR</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field2</span> > 100 <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>AND</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field3</span> <= 200) )
</div>