访问offsetParent时是否存在IE 6/7“未指定错误”错误的解决方法

我在一个简单的ASP.NET概念validation应用程序中使用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的内容时,我重新绑定了draggables和droppables。 当我用光标抓住一个draggable时,我得到一个“htmlfile:Unspecified error”。 例外。 我可以通过将elem.offsetParent替换为我写的这个函数的调用来解决jQuery库中的这个问题:

 function IESafeOffsetParent(elem) { try { return elem.offsetParent; } catch(e) { return document.body; } } 

我还必须避免调用elem.getBoundingClientRect(),因为它会抛出相同的错误。 对于那些感兴趣的人,我只需要在Dimensions插件中的jQuery.fn.offset函数中进行这些更改。

我的问题是:

  • 虽然这有效,但有没有更好的方法(更干净;更好的性能;无需修改jQuery库)来解决这个问题?
  • 如果没有,在将来更新jQuery库时,管理保持我的更改同步的最佳方法是什么? 例如,我可以将库扩展到我从jQuery网站下载的文件中的内联之外的其他位置。

更新:

@some它不公开,但我会看到SO是否允许我将相关代码发布到这个答案中。 只需创建一个ASP.NET Web应用程序(将其命名为DragAndDrop )并创建这些文件即可。 不要忘记将Complex.aspx设置为起始页面。 您还需要下载jQuery UI拖放插件以及jQuery核心

Complex.aspx

     Untitled Page    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',''); } }    
<asp:HiddenField ID="hidID" runat="server" Value='' /> Name: <asp:Label ID="lblName" runat="server" Text='' />

drop here to delete
ID: <asp:Label ID="IDLabel" runat="server" Text='' />
Name: <asp:Label ID="NameLabel" runat="server" Text='' />

Complex.aspx.cs

 namespace DragAndDrop { public partial class Complex : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected List DeleteList { get { if (ViewState["dl"] == null) { List dl = new List(); ViewState["dl"] = dl; return dl; } else { return (List)ViewState["dl"]; } } } public class DataAccess { public IEnumerable SelectAllPeople(IEnumerable filter) { return Database.SelectAll().Where(p => !filter.Contains(p.ID)); } public IEnumerable GetDeleteList(IEnumerable 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(); } } } 

Database.cs

 namespace DragAndDrop { public static class Database { private static Dictionary _people = new Dictionary(); 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 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; } } } 

@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() { ///  /// Gets the current offset of the first matched element relative to the viewport. ///  /// An object with two Integer properties, 'top' and 'left'. 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) { ///  /// This method is internal. ///  ///  add( jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true) ); } function add(l, t) { ///  /// This method is internal. ///  ///  left += parseInt(l, 10) || 0; top += parseInt(t, 10) || 0; } return results; }; 

如果您想修复jQuery版本1.4.2的minified / compressed .js文件,请替换:

 var d=b.getBoundingClientRect(), 

 var d = null; try { d = b.getBoundingClientRect(); } catch(e) { d = { top : b.offsetTop, left : b.offsetLeft } ; } 

(注意现在关闭括号后没有逗号)

我尝试了下面的解决方法来解决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 } ; }

这解决了问题,即使在通过更新面板回发后,拖放也会完全正常工作

问候

Raghu

我的版本是:

  1. 添加function:

     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 } }; 
  2. 更换

     var box = this[0].getBoundingClientRect() 

     var box = getOffsetSum(this[0]) 

PS:jquery-1.3.2。

@Raghu

谢谢你的代码! 我在IE中遇到了同样的问题并且修复了它。

 var box = null; try { box = elem.getBoundingClientRect(); } catch(e) { box = { top : elem.offsetTop, left : elem.offsetLeft }; } 

这不仅仅是一个jQuery错误。 我在IE8上使用ExtJS 4.0.2a遇到了它。 如果在DOM中替换了元素,IE似乎几乎总是偶然发现element.getBoundingClientRect() 。 你的try / catch hack几乎是解决这个问题的唯一方法。 我想实际的解决方案是最终放弃IE <9支持。

Relevent 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 { ...