过去,当尝试在浏览器中对选择位置进行一致的读取时,我已经退回到在选择所在的位置插入 span 元素并从该元素中读取……这有点笨拙,但似乎更可靠。
通过以这种方式处理事情,您应该完全规避那个特定的错误......我不知道这是否会被归类为理智;)虽然它已经过测试并且适用于所有这些用户代理:
- Mac OSX FF15/FF16
- Mac OSX Safari 5.1.7
- Mac OSX 铬 22
- Mac OSX 歌剧 12
- 赢 XP FF 3.6
- 赢 XP Safari 3.1
- 赢XP IE7/IE8
- 赢 7 IE9
- 赢 7 FF15
- 赢 7 铬 22
(以下代码依赖于 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>