Selection and deletion problems with Non-Editable Span inside ContentEditable DIV
我的DIV的ContentEditable设置为TRUE。在其中有几个跨度,其中ContentEditable设置为FALSE。
我捕获了BackSpace键,以便如果光标下的元素是
问题是它只能与奇数范围交替使用。
因此,例如,使用下面的html代码,将光标置于DIV中文本的末尾,然后一直按Backspace直到div的开头。观察到它将选择/删除第一个跨度,然后保留第二个,然后选择/删除第三个跨度,然后保留第四个,依此类推。
此行为仅在Internet Explorer上。它完全可以在Firefox上正常运行。
如何使行为在Internet Explorer中保持一致?
以下html代码可用于重现行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #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; } |
1 2 | (<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) ) |
编辑
仅供参考。我也在MSDN论坛中也问过这个问题。
面临的挑战是使IE11从右侧直接相对于退格。然后,下一个退格键将其选中并突出显示。这似乎是一个简单的目标,但是IE11不会合作。应该有一个快速简单的补丁,对不对?我想出的方法是将树向后移到第一个先前的非空节点,清除它们之间的空节点以安抚IE,然后评估一些条件。如果插入符应位于的右侧,则可以手动创建它(因为IE不会),方法是创建一个新的范围obj,并在的末尾进行选择。在线演示我添加了一个如果两个跨度相互拖动,则IE会产生额外的冲突。例如,Field2Field3。然后,当您从右侧退格到Field3上,然后再次退格以删除它时,IE会将插入符号向左跳到Field2上。跳过Field2。 rr解决方法是拦截该错误,并在一对跨度之间插入一个空格。我不相信你会对此感到满意。但是,您知道,这是一种解决方法。无论如何,这又出现了另一个错误,即IE将插入的空间更改为两个空的textnode。更多grrr。因此,一种变通办法。请参阅非isCollapsed代码。
代码片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | 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\t\t\t\t\t\t\t//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; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #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; } |
1 | (<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) ) |