IE TextRange选择方法无法正常工作

时间:2020-03-06 14:41:29  来源:igfitidea点击:

我将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 -&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,但是光标会移动。除非选择为空,否则似乎不会发生(即,我的光标闪烁,但没有文本),并且似乎仅在光标位于紧接块节点之前的文本节点的末尾时才会发生。

似乎该范围仍处于正确的位置(如果我在该范围上调用parentElement,它将返回div),但是如果我从当前选择中获得一个新范围,则该新范围位于该段落标记内,也就是它的parentElement。

如何解决此问题,并始终在Internet Explorer中保存和还原选择?

解决方案

最近,我在一个使用Microsoft CMS和" MSIB +包"控件的网站上工作,该控件包括在Internet Explorer中运行的WYSIWYG编辑器。

我似乎还记得编辑器客户端Javascript中的一些注释,这些注释与IE中的此错误以及Range.Select()方法特别相关。

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

祝你好运

我有点发掘,但不幸的是看不到解决方法。尽管我在调试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步之间做一些事情,这涉及到移动选择,因此需要重新设置选择。但以防万一。 :)

因此,我能做的最好的就是对我们所创建的错误进行投票,并希望他们会对其进行修复。

我已经想出了几种处理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);

希望这可以帮助。

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

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

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