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

时间:2008-12-16 14:28:43

标签: asp.net jquery asp.net-ajax updatepanel

我在简单的ASP.NET概念验证应用程序中使用jQuery UI的可拖动和可放置库。此页面使用ASP.NET AJAX UpdatePanel进行部分页面更新。该页面允许用户将项目放入垃圾桶div中,该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 PluginjQuery.fn.offset函数中进行这些更改。

我的问题是:

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

更新

@some它无法公开访问,但我会看到SO是否允许我将相关代码发布到此答案中。只需创建一个ASP.NET Web应用程序(将其命名为 DragAndDrop )并创建这些文件。不要忘记将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();
        }
    }
}

Database.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 个答案:

答案 0 :(得分:6)

@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;
};

答案 1 :(得分: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 } ; }

(请注意,现在结束括号后没有逗号)

答案 2 :(得分:1)

我在拖放时尝试了以下解决方法来解决 getBoundingClientRect()未指定的错误,并且工作正常。

在jquery.1.4.2.js(即base 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

答案 3 :(得分:1)

@Raghu

感谢您提供这段代码!我在IE中遇到了同样的问题并且修复了它。

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

答案 4 :(得分:1)

我的版本是:

  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])
    
  3. PS:jquery-1.3.2。

答案 5 :(得分:1)

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