访问 offsetParent 时是不是有针对 IE 6/7“未指定错误”错误的解决方法
Posted
技术标签:
【中文标题】访问 offsetParent 时是不是有针对 IE 6/7“未指定错误”错误的解决方法【英文标题】:Is there a workaround for IE 6/7 "Unspecified Error" bug when accessing offsetParent访问 offsetParent 时是否有针对 IE 6/7“未指定错误”错误的解决方法 【发布时间】:2010-09-27 03:41:35 【问题描述】:我在一个简单的 ASP.NET 概念验证应用程序中使用 jQuery UI 的可拖放库。此页面使用 ASP.NET AJAX UpdatePanel 进行部分页面更新。该页面允许用户将项目放入垃圾箱 div,这将调用从数据库中删除记录的回发,然后重新绑定项目所在的列表(和其他控件)。所有这些元素(可拖动项和垃圾桶 div)都在 ASP.NET UpdatePanel 中。
这里是拖放初始化脚本:
function initDragging()
$(".person").draggable(helper:'clone');
$("#trashcan").droppable(
accept: '.person',
tolerance: 'pointer',
hoverClass: 'trashcan-hover',
activeClass: 'trashcan-active',
drop: onTrashCanned
);
$(document).ready(function()
initDragging();
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function()
initDragging();
);
);
function onTrashCanned(e,ui)
var id = $('input[id$=hidID]', ui.draggable).val();
if (id != undefined)
$('#hidTrashcanID').val(id);
__doPostBack('btnTrashcan','');
当页面回发时,部分更新了 UpdatePanel 的内容,我重新绑定了可拖动对象和可放置对象。然后当我用光标抓取一个可拖动对象时,我得到一个“htmlfile:未指定的错误”。例外。我可以在 jQuery 库中解决这个问题,方法是将 elem.offsetParent
替换为我编写的对这个函数的调用:
function IESafeOffsetParent(elem)
try
return elem.offsetParent;
catch(e)
return document.body;
我还必须避免调用 elem.getBoundingClientRect(),因为它会引发相同的错误。对于那些感兴趣的人,我只需要在Dimensions Plugin 中的jQuery.fn.offset
函数中进行这些更改。
我的问题是:
虽然这可行,但是否有更好的方法(更简洁;更好的性能;无需修改 jQuery 库)来解决这个问题? 如果没有,当我将来更新 jQuery 库时,管理保持更改同步的最佳方法是什么?例如,我可以在从 jQuery 网站下载的文件中内联之外的地方扩展库。更新:
@some 它不可公开访问,但我会看看 SO 是否会让我将相关代码发布到这个答案中。只需创建一个 ASP.NET Web 应用程序(将其命名为 DragAndDrop)并创建这些文件。不要忘记将 Complex.aspx 设置为您的起始页。您还需要下载jQuery UI drag and drop plug in 和jQuery core
复杂的.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Complex.aspx.cs" Inherits="DragAndDrop.Complex" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script src="jquery-1.2.6.min.js" type="text/javascript"></script>
<script src="jquery-ui-personalized-1.5.3.min.js" type="text/javascript"></script>
<script type="text/javascript">
function initDragging()
$(".person").draggable(helper:'clone');
$("#trashcan").droppable(
accept: '.person',
tolerance: 'pointer',
hoverClass: 'trashcan-hover',
activeClass: 'trashcan-active',
drop: onTrashCanned
);
$(document).ready(function()
initDragging();
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function()
initDragging();
);
);
function onTrashCanned(e,ui)
var id = $('input[id$=hidID]', ui.draggable).val();
if (id != undefined)
$('#hidTrashcanID').val(id);
__doPostBack('btnTrashcan','');
</script>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:UpdatePanel ID="updContent" runat="server" UpdateMode="Always">
<ContentTemplate>
<asp:LinkButton ID="btnTrashcan" Text="trashcan" runat="server" CommandName="trashcan"
onclick="btnTrashcan_Click" style="display:none;"></asp:LinkButton>
<input type="hidden" id="hidTrashcanID" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Save" onclick="Button1_Click" />
<table>
<tr>
<td style="width: 300px;">
<asp:DataList ID="lstAllPeople" runat="server" DataSourceID="odsAllPeople"
DataKeyField="ID">
<ItemTemplate>
<div class="person">
<asp:HiddenField ID="hidID" runat="server" Value='<%# Eval("ID") %>' />
Name:
<asp:Label ID="lblName" runat="server" Text='<%# Eval("Name") %>' />
<br />
<br />
</div>
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="odsAllPeople" runat="server" SelectMethod="SelectAllPeople"
TypeName="DragAndDrop.Complex+DataAccess"
onselecting="odsAllPeople_Selecting">
<SelectParameters>
<asp:Parameter Name="filter" Type="Object" />
</SelectParameters>
</asp:ObjectDataSource>
</td>
<td style="width: 300px;vertical-align:top;">
<div id="trashcan">
drop here to delete
</div>
<asp:DataList ID="lstPeopleToDelete" runat="server"
DataSourceID="odsPeopleToDelete">
<ItemTemplate>
ID:
<asp:Label ID="IDLabel" runat="server" Text='<%# Eval("ID") %>' />
<br />
Name:
<asp:Label ID="NameLabel" runat="server" Text='<%# Eval("Name") %>' />
<br />
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="odsPeopleToDelete" runat="server"
onselecting="odsPeopleToDelete_Selecting" SelectMethod="GetDeleteList"
TypeName="DragAndDrop.Complex+DataAccess">
<SelectParameters>
<asp:Parameter Name="list" Type="Object" />
</SelectParameters>
</asp:ObjectDataSource>
</td>
</tr>
</table>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
复杂的.aspx.cs
namespace DragAndDrop
public partial class Complex : System.Web.UI.Page
protected void Page_Load(object sender, EventArgs e)
protected List<int> DeleteList
get
if (ViewState["dl"] == null)
List<int> dl = new List<int>();
ViewState["dl"] = dl;
return dl;
else
return (List<int>)ViewState["dl"];
public class DataAccess
public IEnumerable<Person> SelectAllPeople(IEnumerable<int> filter)
return Database.SelectAll().Where(p => !filter.Contains(p.ID));
public IEnumerable<Person> GetDeleteList(IEnumerable<int> list)
return Database.SelectAll().Where(p => list.Contains(p.ID));
protected void odsAllPeople_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
e.InputParameters["filter"] = this.DeleteList;
protected void odsPeopleToDelete_Selecting(object sender, ObjectDataSourceSelectingEventArgs e)
e.InputParameters["list"] = this.DeleteList;
protected void Button1_Click(object sender, EventArgs e)
foreach (int id in DeleteList)
Database.DeletePerson(id);
DeleteList.Clear();
lstAllPeople.DataBind();
lstPeopleToDelete.DataBind();
protected void btnTrashcan_Click(object sender, EventArgs e)
int id = int.Parse(hidTrashcanID.Value);
DeleteList.Add(id);
lstAllPeople.DataBind();
lstPeopleToDelete.DataBind();
数据库.cs
namespace DragAndDrop
public static class Database
private static Dictionary<int, Person> _people = new Dictionary<int,Person>();
static Database()
Person[] people = new Person[]
new Person("Chad")
, new Person("Carrie")
, new Person("Richard")
, new Person("Ron")
;
foreach (Person p in people)
_people.Add(p.ID, p);
public static IEnumerable<Person> SelectAll()
return _people.Values;
public static void DeletePerson(int id)
if (_people.ContainsKey(id))
_people.Remove(id);
public static Person CreatePerson(string name)
Person p = new Person(name);
_people.Add(p.ID, p);
return p;
public class Person
private static int _curID = 1;
public int ID get; set;
public string Name get; set;
public Person()
ID = _curID++;
public Person(string name)
: this()
Name = name;
【问题讨论】:
您能否提供一个指向最低限度的测试页面的链接? 【参考方案1】:@arilanto - 我在我的 jquery 脚本之后包含了这个脚本。就性能而言,这不是最好的解决方案,但它是一种快速简单的解决方法。
function IESafeOffsetParent(elem)
try
return elem.offsetParent;
catch(e)
return document.body;
// The Offset Method
// Originally By Brandon Aaron, part of the Dimension Plugin
// http://jquery.com/plugins/project/dimensions
jQuery.fn.offset = function()
/// <summary>
/// Gets the current offset of the first matched element relative to the viewport.
/// </summary>
/// <returns type="Object">An object with two Integer properties, 'top' and 'left'.</returns>
var left = 0, top = 0, elem = this[0], results;
if ( elem ) with ( jQuery.browser )
var parent = elem.parentNode,
offsetChild = elem,
offsetParent = IESafeOffsetParent(elem),
doc = elem.ownerDocument,
safari2 = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
css = jQuery.curCSS,
fixed = css(elem, "position") == "fixed";
// Use getBoundingClientRect if available
if (false && elem.getBoundingClientRect)
var box = elem.getBoundingClientRect();
// Add the document scroll offsets
add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
box.top + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
// IE adds the HTML element's border, by default it is medium which is 2px
// IE 6 and 7 quirks mode the border width is overwritable by the following css html border: 0;
// IE 7 standards mode, the border is always 2px
// This border/offset is typically represented by the clientLeft and clientTop properties
// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
// Therefore this method will be off by 2px in IE while in quirksmode
add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
// Otherwise loop through the offsetParents and parentNodes
else
// Initial element offsets
add( elem.offsetLeft, elem.offsetTop );
// Get parent offsets
while ( offsetParent )
// Add offsetParent offsets
add( offsetParent.offsetLeft, offsetParent.offsetTop );
// Mozilla and Safari > 2 does not include the border on offset parents
// However Mozilla adds the border for table or table cells
if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
border( offsetParent );
// Add the document scroll offsets if position is fixed on any offsetParent
if ( !fixed && css(offsetParent, "position") == "fixed" )
fixed = true;
// Set offsetChild to previous offsetParent unless it is the body element
offsetChild = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
// Get next offsetParent
offsetParent = offsetParent.offsetParent;
// Get parent scroll offsets
while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) )
// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
if ( !/^inline|table.*$/i.test(css(parent, "display")) )
// Subtract parent scroll offsets
add( -parent.scrollLeft, -parent.scrollTop );
// Mozilla does not add the border for a parent that has overflow != visible
if ( mozilla && css(parent, "overflow") != "visible" )
border( parent );
// Get next parent
parent = parent.parentNode;
// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
if ( (safari2 && (fixed || css(offsetChild, "position") == "absolute")) ||
(mozilla && css(offsetChild, "position") != "absolute") )
add( -doc.body.offsetLeft, -doc.body.offsetTop );
// Add the document scroll offsets if position is fixed
if ( fixed )
add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
// Return an object with top and left properties
results = top: top, left: left ;
function border(elem)
/// <summary>
/// This method is internal.
/// </summary>
/// <private />
add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) );
function add(l, t)
/// <summary>
/// This method is internal.
/// </summary>
/// <private />
left += parseInt(l, 10) || 0;
top += parseInt(t, 10) || 0;
return results;
;
【讨论】:
我在行中收到错误“safari undefined” - “safari2 = safari && parseInt(version) 【参考方案2】:如果您想为 jQuery 版本 1.4.2 修复缩小/压缩的 .js 文件,请替换:
var d=b.getBoundingClientRect(),
与
var d = null;
try d = b.getBoundingClientRect();
catch(e) d = top : b.offsetTop, left : b.offsetLeft ;
(注意现在右大括号后面没有逗号)
【讨论】:
【参考方案3】:我在拖放时尝试了以下解决方法来解决 getBoundingClientRect() 未指定错误,并且效果很好。
在 jquery.1.4.2.js 中(即基本 jquery 文件,错误被准确抛出的地方)
替换js文件中的elem.getBoundingClientRect()函数调用
//抛出未指定错误的行
var box = elem.getBoundingClientRect(),
有了这个..
var box = null;
try
box = elem.getBoundingClientRect();
catch(e)
box = top : elem.offsetTop, left : elem.offsetLeft ;
这解决了问题,即使在通过更新面板回帖后,拖放也能正常工作
问候
拉古
【讨论】:
【参考方案4】:我的版本是:
添加功能:
function getOffsetSum(elem)
var top = 0, left = 0
while (elem)
top = top + parseInt(elem.offsetTop)
left = left + parseInt(elem.offsetLeft)
try
elem = elem.offsetParent
catch (e)
return top: top, left: left
return top: top, left: left
;
替换
var box = this[0].getBoundingClientRect()
与
var box = getOffsetSum(this[0])
PS:jquery-1.3.2.
【讨论】:
【参考方案5】:@Raghu
感谢这段代码!我在 IE 中遇到了同样的问题,这解决了它。
var box = null;
try
box = elem.getBoundingClientRect();
catch(e)
box =
top : elem.offsetTop,
left : elem.offsetLeft
;
【讨论】:
【参考方案6】:这不仅仅是一个 jQuery 错误。我在 IE8 上使用 ExtJS 4.0.2a 遇到了它。如果元素在 DOM 中被替换,IE 几乎总是会偶然发现element.getBoundingClientRect()
。您的 try/catch hack 几乎是解决此问题的唯一方法。我想实际的解决方案是最终放弃对 IE
相关 ExtJS v4.0.2a 源代码(第 11861-11869 行):
...
if(el != bd)
hasAbsolute = fly(el).isStyle("position", "absolute");
if (el.getBoundingClientRect)
b = el.getBoundingClientRect();
scroll = fly(document).getScroll();
ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
else
...
使用 try/catch 修复:
...
if(el != bd)
hasAbsolute = fly(el).isStyle("position", "absolute");
if (el.getBoundingClientRect)
try
b = el.getBoundingClientRect();
scroll = fly(document).getScroll();
ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
catch(e)
ret = [0,0];
else
...
【讨论】:
以上是关于访问 offsetParent 时是不是有针对 IE 6/7“未指定错误”错误的解决方法的主要内容,如果未能解决你的问题,请参考以下文章
parentNode和offsetParent的区别,仅仅只是定位相对不一样么
JavaScript问题——在浏览器中每一个元素都有一个offsetParent属性,这个属性是什么?