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