我似乎发现了Internet Explorer 8的问题,在查找文本范围的位置时 - 例如当前选定的文本。我发现无法在StackOverflow或其他地方找到有关此问题的任何报告。
TextRange.offsetLeft 和TextRange.offsetTop 报告范围的左角和上角,在我在 IE8 中看到的所有情况下,它们大致正确,除非范围在 IFrame 内。当范围在 IFrame 内时,偏移量左和偏移量相对于 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>
- 我仍然认为这是IE8中的一个错误,但是有一个解决方法
- 在标准模式下,使用
boundingLeft
和boundingRight
。但在怪癖模式下使用offsetLeft
和offsetRight
我已经在我的IE 8(在Windows XP上(中测试了您的示例页面,并且我已经能够重现该问题,因此我可以确认问题,但我不能说这是否是已知问题。
我找到了一个可行的解决方案(至少在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图书馆:
跨浏览器的 JavaScript 范围和选择库。它提供了一个 简单的基于标准的 API,用于执行常见的 DOM 范围和 所有主流浏览器中的选择任务,疯狂抽象 互联网之间此功能的不同实现 资源管理器最高版本8和DOM兼容浏览器。
当试图在浏览器上对选择位置进行一致的读取时,我已经退回到在选择所在的位置插入 span 元素并从该元素读取......它有点笨拙,但似乎更可靠。
通过以这种方式处理事情,您应该完全绕过该特定错误......我不知道这是否会被归类为理智,但是;)尽管它已经过测试并且可以在所有这些用户代理中工作:
- Mac OSX FF15/FF16
- Mac OSX Safari 5.1.7
- Mac OSX Chrome 22
- Mac OSX Opera 12
- 赢取经验值FF 3.6
- 赢得XP野生动物园3.1
- 赢XP IE7/IE8
- 赢 7 IE9
- 赢 7 FF15
- 赢 7 铬 22
(以下代码依赖jQuery,1.8+最好(
斯菲德尔
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>