7

我在使用 contentEditable 设置为 true 的 IE 文档时遇到了一个不寻常的问题。在位于块元素之前的文本节点末尾的范围上调用 select() 会导致选择向右移动一个字符并出现在不应出现的位置。我已经向微软提交了一个针对 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 -&gt;
            <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),但是如果我从当前选择中获得一个新范围,则新范围在段落标记内,这就是它父元素。

如何解决此问题并始终保存和恢复 Internet Explorer 中的选择?

4

4 回答 4

12

我已经想出了一些处理这样的 IE 范围的方法。

如果你只想保存光标所在的位置,然后恢复它,你可以使用 pasteHTML 方法在光标的当前位置插入一个空 span,然后使用 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);

希望这可以帮助。

于 2009-05-05T22:48:10.040 回答
1

我进行了一些研究,不幸的是看不到解决方法...尽管我在调试 javascript 时注意到了一件事,但似乎问题实际上出在范围对象本身而不是后续的range.select(). 如果您查看selection.createRange()返回的范围对象上的值,是的,父 dom 对象可能是正确的,但定位信息已经指向下一行的开头(即 offsetLeft/Top、boundingLeft/Top 等已经错误)。

根据此处此处此处的信息,我认为您对 IE 不走运,因为您只能访问 Microsoft 的 TextRange 对象,该对象似乎已损坏。根据我的实验,您可以移动范围并将其准确定位在应有的位置,但是一旦您这样做,范围对象甚至在您尝试之前自动向下移动到下一行.select()。例如,您可以通过将此代码放在第 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 步和第 2 步之间做一些事情,这涉及移动选择,因此需要重新设置它。但以防万一。:)

所以,我能做的最好的就是去投票给你创建的错误......希望他们能修复它。

于 2009-05-05T00:44:24.607 回答
0

我最近在一个使用 Microsoft CMS 和“MSIB+ 包”控件的站点工作,其中包括在 Internet Explorer 中运行的所见即所得编辑器。

我似乎记得编辑器客户端 Javascript 中的一些评论,这些评论与 IE 中的这个错误和 Range.Select() 方法特别相关。

不幸的是,我不再在那里工作,所以我无法访问 Javascript 文件,但也许您可以从其他地方获取它们?

祝你好运

于 2008-09-29T15:49:20.813 回答
0

也许我误解了,但是如果我单击第一行的右侧,我的光标已经立即出现在第二行的开头,所以这不是 TextRange 问题,对吧?

添加一个文档类型,以便测试页面以标准模式呈现,而不是怪癖模式为我解决了这个问题。

而且我无法重现您的 myhandler 函数所做的事情,因为单击该按钮会将焦点移到该按钮上,因此我无法再看到光标并且也无法将其取回。在contentEditable区域中查找光标位置似乎完全是一个不同的问题

于 2009-05-06T10:36:58.353 回答