Internet Explorer 8标准模式(IE8)中的TextRange offsetLeft和offsetTop中断

时间:2012-05-14 22:30:37

标签: internet-explorer-8 textrange

在查找文本范围的位置时,我似乎发现Internet Explorer 8存在问题 - 例如当前选定的文本。我还没有找到能在StackOverflow或其他地方找到任何关于此问题的报告。

TextRange.offsetLeft和TextRange.offsetTop报告范围的左上角,在IE8中我看到的所有情况都是大致正确的,除了范围在IFrame中的情况< / em>的。当范围在IFrame内时,offsetLeft和offsetTop的值相对于其父级内的IFrame的位置移动负值。 (见下面的例子)

此问题仅在以下情况下出现:

  • 浏览器是IE8
  • 页面处于标准模式

在以下情况下,会出现此问题:

  • 浏览器是IE7或IE10
  • 页面处于怪癖模式

我的问题:

  • 其他人可以确认这个问题还是我疯了?
  • 这是一个已知问题吗?
  • 是否有任何理智的解决方案或解决方法? (一个理智的解决方案是框架中的JS不需要了解其父窗口的任何内容)

感谢。

问题的一个例子:(参见IE8与IE9的区别)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <title>IE8 IFrame Text Range Position Test Page</title>
        <style type="text/css">
            body {
                font-family: Tahoma;
            }
            #target {
                background-color: #CCC;
                position: absolute;
                left: 50px;
                top: 50px;
            }

            #bullsEye {
                position: absolute;
                background-color: red;
                width: 5px;
                height: 5px;
            }

            iframe {
                margin: 10px 75px;
            }
        </style>
        <script type="text/javascript" charset="utf-8">
            function target() {
                var range = document.body.createTextRange();
                range.moveToElementText(document.getElementById('target'));
                range.select();
                var bullsEye = document.createElement('div');
                bullsEye.id = 'bullsEye';
                bullsEye.style.left = range.offsetLeft + 'px';
                bullsEye.style.top = range.offsetTop + 'px';
                document.body.appendChild(bullsEye);
                document.getElementById('output').innerHTML = 'range.offsetLeft = ' + range.offsetLeft + ', range.offsetTop = ' + range.offsetTop;
            }
        </script>
    </head>
    <body>
        <div id="target">Target</div>
        <input type="button" value="Hit Target" onclick="target();"> <span id="output"></span>
        <br><br><br><br><br>
        <script>
            if (window.parent == window) document.write('<iframe src="?tfr" height="150" width="500"></iframe>');
        </script>
    </body>
</html>

3 个答案:

答案 0 :(得分:3)

  • 我仍然认为这是IE8中的一个错误,但有一种解决方法
  • 在标准模式下,使用boundingLeftboundingRight。但在怪癖模式下,请使用offsetLeftoffsetRight

答案 1 :(得分:3)

过去当我试图在浏览器中为选择位置进行一致读取时,我已经退回到插入选择范围的span元素并从该元素读取...它有点hacky但似乎更可靠

通过这种方式处理事情你应该完全绕过那个特定的错误...我不知道这是否会被归类为理智;但是它已经过测试并适用于所有这些用户代理:

  1. Mac OSX FF15 / FF16
  2. Mac OSX Safari 5.1.7
  3. Mac OSX Chrome 22
  4. Mac OSX Opera 12
  5. Win XP FF 3.6
  6. Win XP Safari 3.1
  7. Win XP IE7 / IE8
  8. 赢得7 IE9
  9. Win 7 FF15
  10. Win 7 Chrome 22
  11. (以下代码依赖于jQuery,1.8 +最好)

    <强>的jsfiddle

    http://jsfiddle.net/vCWha/

    <强> CSS

    #__span__    {
      display: inline !important;
      *display: inline-block !important; /* IE7 works better with inline-block */
    }
    
    /* you can obviously ignore these, they are just used to show accuracy */
    .crosshair-v {
      height: 0;
      width: 20px;
      border-bottom: 1px solid red;
      position: absolute;
      margin-left: -10px;
    }
    
    .crosshair-h {
      height: 20px;
      width: 0;
      border-right: 1px solid red;
      position: absolute;
      margin-top: -9px;
    }
    

    <强>的javascript

    (function($){
      $(function(){
        var span = $('<span id="__span__" />').get(0),
            crv = $('<div class="crosshair-v" />'),
            crh = $('<div class="crosshair-h" />');
        $('body').append(crv).append(crh);
        var getSelectionTopLeft = function(){
          var s,e,a,p,o,r,t;
          try{
            /// IE9+, FF, Chrome, Safari, Opera
            if ( window.getSelection ){
              s = window.getSelection();
              r = s.getRangeAt(0);
              a = r.startContainer;
              p = a.parentNode;
              if ( a.nodeType == 3 ){
                t = a.splitText(r.startOffset);
                p.insertBefore(span, t);
              }
              else if ( a.nodeType == 1 ){
                p.insertBefore(span, a);
              }
              o = $(span).position();
            }
            /// IE8-
            else if ( (s = document.selection) && (s.type != 'Control') ) {
              r = s.createRange();
              r.move('character',0);
              $('#__span__').remove();
              r.pasteHTML(span.outerHTML);
              o = $('#__span__').position();
            }
            /// quick fallback for certain older browsers for 
            /// whom $().position() fails.
            if ( o && o.left === 0 && o.left === o.top ) {
              e = span;
              while( e.offsetParent ){
                o.left += e.offsetLeft;
                o.top += e.offsetTop;
                e = e.offsetParent;
              }
            }
          }catch(ex){}
          return o;
        }
        $(document).mouseup(function(e){
          /// execute our function to calculate the selection position
          var o = getSelectionTopLeft();
          if ( o ){
            /// update the crosshair
            crv.css(o);
            crh.css(o);
          }
        });
      });
    })(typeof jQuery != 'undefined' && jQuery);
    

    更新

    昨晚有更多的时间来处理这个问题,所以我的改进代码在你的例子中工作 - 以下内容应该是完全跨浏览器的(至少在合理范围内):

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
      <title>IE8 IFrame Text Range Position Test Page</title>
      <style type="text/css">
        body {
            font-family: Tahoma;
        }
    
        #__span__    {
          display: inline !important;
          display: inline-block !important;
          min-height: 1em;
        }
    
        #target {
            background-color: #CCC;
            position: absolute;
            left: 50px;
            top: 50px;
        }
    
        #bullsEye {
            position: absolute;
            background-color: red;
            width: 5px;
            height: 5px;
        }
    
        iframe {
            margin: 10px 75px;
        }
      </style>
      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
      <script type="text/javascript" charset="utf-8">
        (function($){
            var bullsEye = $('<div id="bullsEye" />'), span = $('<span id="__span__"></span>').get(0);
    
            /// var is missed here on purpose to make the function globally accessible
            target = function() {                
              moveSelectionToElement( document.getElementById('target') );
              bullsEye
                .css( getSelectionTopLeft() )
                .appendTo('body');
            }
    
            /// because selectNodeContents seems to select outside the node we 
            /// need our own rangeToNodeContents that only highlights text nodes
            /// this is a side effect of having code inserted ranges & selections.
            var rangeToNodeContents = function(r, node){
              var i, l, tns = [];
              if ( node.nodeType == 1 && node.childNodes && (l = node.childNodes.length) ){
                for ( i=0;i<l;i++ ){
                  if ( node.childNodes[i] && node.childNodes[i].nodeType == 3 ) {
                    tns.push(node.childNodes[i]);
                  }
                  if ( tns.length > 1 ) {
                    r.setStart(tns[0],0);
                    r.setEnd(tns[tns.length-1],tns[tns.length-1].nodeValue.length);
                  }
                  else {
                    r.selectNodeContents(node);
                  }
                }
              }
              else {
                r.selectNodeContents(node);
              }
            }
    
            /// cross browser selection creator
            var moveSelectionToElement = function(elm) {
              var s,w,r,d; w = window; d = document;
              if (w.getSelection && d.createRange) {
                s = w.getSelection();
                r = d.createRange();
                rangeToNodeContents( r, elm );
                s.removeAllRanges();
                s.addRange(r);
              } else if (d.selection && d.body && d.body.createTextRange) {
                r = elm.createTextRange();
                r.select();
              }
            }
    
            /// cross browser getSelectionTopLeft
            var getSelectionTopLeft = function(){
              var s,e,a,p,o,r,t; o = {left:0,top:0};
              try{
                if ( window.getSelection ){
                  s = window.getSelection();
                  r = s.getRangeAt(0);
                  a = r.startContainer;
                  p = a.parentNode;
                  if ( a.nodeType == 3 ){
                    t = a.splitText(r.startOffset);
                    p.insertBefore(span, t);
                  }
                  else if ( a.nodeType == 1 ){
                      p.insertBefore(span, a);
                  }
                  o = $(span).offset();
                }
                else if ( (s = document.selection) && (s.type != 'Control') ) {
                  r = s.createRange();
                  r.move('character',0);
                  $('#__span__').remove();
                  r.pasteHTML(span.outerHTML);
                  o = $('#__span__').offset();
                }
                if ( o && o.left === 0 && o.left === o.top ) {
                  e = span;
                  while( e.offsetParent ){
                    o.left += e.offsetLeft;
                    o.top += e.offsetTop;
                    e = e.offsetParent;
                  }
                }
              }catch(ex){}
              return o;
            }
    
        })(typeof jQuery != 'undefined' && jQuery);
      </script>
    </head>
    <body>
      <div id="target">Target <b>abc</b> test</div>
      <input type="button" value="Hit Target" onmouseup="target();"> <span id="output"></span>
      <br><br><br><br><br>
      <script>
        if (window.parent == window){
          document.write('<iframe src="?tfr" height="150" width="500"></iframe>');
        }
      </script>
    </body>
    </html>
    

答案 2 :(得分:3)

我已经在IE 8中测试了您的示例页面(在Windows XP上)并且我已经能够重现该问题,所以我可以确认问题,但我不能说这是一个已知问题还是不是。
我在this answer上找到了一个可行的解决方案(至少在IE 8上;我不能说它是否也适用于IE 7或IE 9,因为我没有针对这些版本的测试环境)。登记/> 这是我修改过的测试页面:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <title>IE8 IFrame Text Range Position Test Page</title>
        <style type="text/css">
            body {
                font-family: Tahoma;
            }
            #target {
                background-color: #CCC;
                position: absolute;
                left: 50px;
                top: 50px;
            }

            #bullsEye {
                position: absolute;
                background-color: red;
                width: 5px;
                height: 5px;
            }

            iframe {
                margin: 10px 75px;
            }
        </style>
        <script type="text/javascript" charset="utf-8">
            function getSelectionTopLeft() {     
                var x = 0, y = 0;     // Use standards-based method only if Range has getBoundingClientRect     
                if (window.getSelection && document.createRange && 
                    typeof document.createRange().getBoundingClientRect != "undefined") {         
                    var sel = window.getSelection();         
                    if (sel.rangeCount > 0) {             
                        var rect = sel.getRangeAt(0).getBoundingClientRect();             
                        x = rect.left;             
                        y = rect.top;         
                    }     
                } else if (document.selection && document.selection.type != "Control") {         // All versions of IE         
                    var textRange = document.selection.createRange();         
                    x = textRange.boundingLeft;         
                    y = textRange.boundingTop;     
                }     
                return { x: x, y: y }; 
            }                   

            function target() {
                var range = document.body.createTextRange();
                range.moveToElementText(document.getElementById('target'));
                range.select();
                var bullsEye = document.createElement('div');
                bullsEye.id = 'bullsEye';
                bullsEye.style.left = range.offsetLeft + 'px';
                bullsEye.style.top = range.offsetTop + 'px';
                document.body.appendChild(bullsEye);
                //document.getElementById('output').innerHTML = 'range.offsetLeft = ' + range.offsetLeft + ', range.offsetTop = ' + range.offsetTop;
                var tl = getSelectionTopLeft();
                document.getElementById('output').innerHTML = tl.x + ',' + tl.y;
            }
        </script>
    </head>
    <body>
        <div id="target">Target</div>
        <input type="button" value="Hit Target" onclick="target();"> <span id="output"></span>
        <br><br><br><br><br>
        <script>
            if (window.parent == window) document.write('<iframe src="?tfr" height="150" width="500"></iframe>');
        </script>
    </body>
</html>

还要查看Rangy library

  

跨浏览器的JavaScript范围和选择库。它提供了一个   基于标准的简单API,用于执行常见的DOM范围和   在所有主流浏览器中的选择任务,抽象地抽象   Internet之间的这种功能的不同实现   资源管理器包括版本8和符合DOM的浏览器。