对于将contentEditable设置为true的IE文档,我遇到了一个不寻常的问题。在位于紧邻块元素之前的文本节点末尾的范围上调用select()会使选择转移到右边的一个字符,并显示在不应该出现的位置。我已向Microsoft提交了针对IE8的错误。如果可以,请为此问题投票,以便修复。
https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=390995
我写了一个测试用例来证明效果:
<html>
<body>
<iframe id="editable">
<html>
<body>
<div id="test">
Click to the right of this line ->
<p id="par">Block Element</p>
</div>
</body>
</html>
</iframe>
<input id="mytrigger" type="button" value="Then Click here to Save and Restore" />
<script type="text/javascript">
window.onload = function() {
var iframe = document.getElementById('editable');
var doc = iframe.contentDocument || iframe.contentWindow.document;
// An IFRAME without a source points to a blank document. Here we'll
// copy the content we stored in between the IFRAME tags into that
// document. It's a hack to allow us to use only one HTML file for this
// test.
doc.body.innerHTML = iframe.textContent || iframe.innerHTML;
// Marke the IFRAME as an editable document
if (doc.body.contentEditable) {
doc.body.contentEditable = true;
} else {
var mydoc = doc;
doc.designMode = 'On';
}
// A function to demonstrate the bug.
var myhandler = function() {
// Step 1 Get the current selection
var selection = doc.selection || iframe.contentWindow.getSelection();
var range = selection.createRange ? selection.createRange() : selection.getRangeAt(0);
// Step 2 Restore the selection
if (range.select) {
range.select();
} else {
selection.removeAllRanges();
selection.addRange(range);
doc.body.focus();
}
}
// Set up the button to perform the test code.
var button = document.getElementById('mytrigger');
if (button.addEventListener) {
button.addEventListener('click', myhandler, false);
} else {
button.attachEvent('onclick', myhandler);
}
}
</script>
</body>
</html>
问题暴露在myhandler函数中。这就是我正在做的事情,在保存和恢复选择之间没有第3步,但光标移动了。除非选择为空(即我有一个闪烁的光标,但没有文本),似乎不会发生这种情况,并且只有当光标位于紧接在块节点之前的文本节点的末尾时,它才会发生。
似乎范围仍然在正确的位置(如果我在返回div的范围内调用parentElement),但是如果我从当前选择中获得新范围,则新范围在段落标记内,并且这是它的parentElement。
如何解决此问题并始终在Internet Explorer中保存和恢复选择?
答案 0 :(得分:12)
我已经找到了一些处理IE范围的方法。
如果要做的只是保存光标所在的位置,然后将其恢复,可以使用pasteHTML方法在光标的当前位置插入空跨度,然后使用moveToElementText方法将其放回在那个位置再次:
// Save position of cursor
range.pasteHTML('<span id="caret"></span>')
...
// Create new cursor and put it in the old position
var caretSpan = iframe.contentWindow.document.getElementById("caret");
var selection = iframe.contentWindow.document.selection;
newRange = selection.createRange();
newRange.moveToElementText(caretSpan);
或者,您可以计算当前光标位置前面的字符数并保存该数字:
var selection = iframe.contentWindow.document.selection;
var range = selection.createRange().duplicate();
range.moveStart('sentence', -1000000);
var cursorPosition = range.text.length;
要恢复光标,请将其设置为开头,然后将其移动该字符数:
var newRange = selection.createRange();
newRange.move('sentence', -1000000);
newRange.move('character', cursorPosition);
希望这有帮助。
答案 1 :(得分:1)
我有点挖掘&amp;遗憾的是看不到一个解决方法...虽然我在调试javascript时注意到了一件事,但似乎问题实际上是范围对象本身而不是后续的range.select()
。如果查看selection.createRange()
返回的范围对象上的值,是父dom对象可能是正确的,但定位信息已经指向下一行的开头(即offsetLeft / Top,boundingLeft / Top等等已经错了)。
根据信息here,here和here,我认为您运气不佳,因为您只能访问Microsoft的TextRange对象,该对象似乎是破碎。从我的实验中,你可以移动范围并将其准确定位在应有的位置,但是一旦你这样做,范围对象甚至在你尝试.select()
之前就会自动向下移动到下一行。例如,您可以通过将此代码放在步骤1和步骤1之间来查看问题。第2步:
if (range.boundingWidth == 0)
{
//looks like its already at the start of the next line down...
alert('default position: ' + range.offsetLeft + ', ' + range.offsetTop);
//lets move the start of the range one character back
//(i.e. select the last char on the line)
range.moveStart("character", -1);
//now the range looks good (except that its width will be one char);
alert('one char back: ' + range.offsetLeft + ', ' + range.offsetTop);
//calculate the true end of the line...
var left = range.offsetLeft + range.boundingWidth;
var top = range.offsetTop;
//now we can collapse back down to 0 width range
range.collapse();
//the position looks right
alert('moving to: ' + left + ', ' + top);
//move there.
range.moveToPoint(left, top);
//oops... on the next line again... stupid IE.
alert('moved to: ' + range.offsetLeft + ', ' + range.offsetTop);
}
所以,不幸的是,当你再次选择它时,看起来没有任何方法可以确保范围在正确的位置。
显然,上面的代码有一些简单的修复方法,将第2步更改为:
// Step 2 Restore the selection
if (range.select) {
if (range.boundingWidth > 0) {
range.select();
}
} else {
selection.removeAllRanges();
selection.addRange(range);
doc.body.focus();
}
但据推测,你实际上想要在第1步和第1步之间做点什么。实际的第2步涉及移动选择,因此需要重新设置它。但为了以防万一。 :)
所以,我能做的最好的事情就是去&amp;投票支持你创建的错误...希望他们能解决它。
答案 2 :(得分:0)
我最近在一个使用Microsoft CMS和“MSIB + pack”控件的网站上工作,其中包括一个在Internet Explorer中运行的WYSIWYG编辑器。
我似乎记得编辑器客户端Javascript中的一些注释,这些注释与IE和Range.Select()方法中的这个错误有关。
不幸的是,我不再在那里工作,所以我无法访问Javascript文件,但也许您可以从其他地方获取它们?
祝你好运答案 3 :(得分:0)
也许我误解了,但是如果我点击第一行的右边,我的光标已经立即出现在第二行的开头,所以这不是TextRange问题,对吗?
添加doctype以便测试页面以标准模式呈现而不是怪癖模式为我修复。
我无法重现myhandler函数的作用,因为单击该按钮会将焦点移动到按钮上,这样我就再也看不到光标了。在 contentEditable 区域seems to be a different problem altogether中查找光标位置。