代码之家  ›  专栏  ›  技术社区  ›  CodeChef

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

  •  7
  • CodeChef  · 技术社区  · 16 年前

    我在一个简单的ASP中使用jQuery UI的可拖动和可丢弃库。NET概念验证应用程序。此页面使用ASP。NET AJAX UpdatePanel进行部分页面更新。该页面允许用户将项目放入垃圾桶div中,这将调用从数据库中删除记录的回发,然后重新绑定该项目所在的列表(和其他控件)。所有这些元素(可拖动项和垃圾桶div)都在ASP中。NET更新面板。

    以下是拖放初始化脚本:

        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的内容,我重新绑定了draggable和droptables。当我用光标抓取一个可拖动对象时,我得到一个“htmlfile:Unspecified error.”异常。我可以通过替换jQuery库来解决这个问题 elem.offsetParent 调用我写的这个函数:

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

    我还必须避免调用elem.getBoundingClientRect(),因为它会抛出相同的错误。对于那些感兴趣的人,我只需要在 jQuery.fn.offset 功能在 Dimensions Plugin .

    我的问题是:

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

    更新:

    @一些它不是公开访问的,但我会看看SO是否会让我将相关代码发布到这个答案中。只需创建一个ASP。NET Web应用程序(命名它 拖放 )并创建这些文件。别忘了将Complex.aspx设置为起始页。您还需要下载 jQuery UI drag and drop plug in 以及 jQuery core

    Complex.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>
    

    Complex.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;
            }
        }
    }
    
    6 回复  |  直到 10 年前
        1
  •  6
  •   CodeChef    16 年前

    @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;
    };
    
        2
  •  2
  •   GDP Danny    13 年前

    如果你想修复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
  •  1
  •   Raghunathan    15 年前

    我尝试了以下解决方法 getBoundingClientRect() 拖放n时出现未指定的错误,并且运行良好。

    在jquery.1.4.2.js中( 即基本jquery文件,其中错误被准确抛出 )

    更换 elem.getBoundingClientRect() js文件中的函数调用

    //抛出未指定错误的行

    var box = elem.getBoundingClientRect(),

    有了这个。。

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

    这解决了这个问题,即使在通过更新面板回发后,拖放也会非常有效

    当做

    拉古

        4
  •  1
  •   takrl cck    14 年前

    我的版本是:

    1. 添加功能:

      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])
      

    附言:jquery-1.3.2。

        5
  •  1
  •   stealthyninja michkra    13 年前

    @拉古

    感谢这段代码!我在IE中遇到了同样的问题,这个解决了它。

    var box = null; 
    try { 
        box = elem.getBoundingClientRect(); 
    } catch(e) { 
        box = { 
            top : elem.offsetTop, 
            left : elem.offsetLeft 
        }; 
    }
    
        6
  •  1
  •   nelsonpecora    13 年前

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

    相关事件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 {
    ...
    
    推荐文章