2008-12-16 9 views
7

Estoy usando las bibliotecas arrastrables y droppable de jQuery UI en una simple aplicación de prueba de concepto de ASP.NET. Esta página utiliza ASP.NET AJAX UpdatePanel para realizar actualizaciones de página parciales. La página permite a un usuario colocar un elemento en un div de basura, que invocará una devolución de datos que borrará un registro de la base de datos, y luego volverá a vincular la lista (y otros controles) de los que el artículo era droga. Todos estos elementos (los elementos que se pueden arrastrar y el div de trashcan) están dentro de un UpdatePanel de ASP.NET.Existe una solución para IE 6/7 error "Error no especificado" al acceder a offsetParent

Aquí está el script de inicialización de arrastrar y soltar:

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',''); 
     } 

    } 

cuando la página de nuevo, la actualización parcialmente el contenido del UpdatePanel, que volver a vincular el draggables y droppables. Cuando agarro un arrastrable con mi cursor, aparece un "htmlfile: error no especificado". excepción. Puedo resolver este problema en la biblioteca jQuery reemplazando elem.offsetParent con llamadas a esta función que escribí:

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

también tengo que evitar las llamadas a elem.getBoundingClientRect(), ya que arroja el mismo error. Para aquellos interesados, solo tuve que hacer estos cambios en la función jQuery.fn.offset en el Dimensions Plugin.

Mis preguntas son:

  • Aunque esto funciona, hay mejores maneras (más limpia; un mejor rendimiento, sin tener que modificar la biblioteca jQuery) para resolver este problema?
  • De no ser así, ¿cuál es la mejor forma de administrar la sincronización de mis cambios cuando actualizo las bibliotecas jQuery en el futuro? Por ejemplo, ¿puedo extender la biblioteca en algún lugar que no sea solo en línea en los archivos que descargo del sitio web de jQuery?

Actualización:

@some No es públicamente accesible, pero voy a ver si también lo hará me deja publicar el código en cuestión en esta respuesta. Simplemente cree una aplicación web ASP.NET (llámela DragAndDrop) y cree estos archivos. No olvides configurar Complex.aspx como página de inicio. También tendrá que descargar el jQuery UI drag and drop plug in, así como 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; 
     } 
    } 
} 
+0

Podría dar un enlace a una página de prueba con la cantidad mínima? – some

Respuesta

6

@arilanto: incluyo este script después de mis scripts de jquery. En cuanto a rendimiento, no es la mejor solución, pero es un trabajo fácil y rápido.

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; 
}; 
+4

Recibo el error 'safari indefinido' en la línea - "safari2 = safari && parseInt (versión) <522 &&! /adobeair/i.test (userAgent)". ¿Algunas ideas? – NLV

1

Probé la solución siguiente para la getBoundingClientRect() error no especificado mientras arrastrar y soltar, y funciona bien.

en los jquery.1.4.2.js (es decir archivo de jQuery base, donde se lanza el error exactamente)

sustituir el elem.getBoundingClientRect() llamada función en el archivo js

// la línea que lanza el error no especificado

var box = elem.getBoundingClientRect(),

con este ..

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

Esto resuelve el problema de caída y arrastre n trabajará Quitely incluso después de puesto de nuevo a través del panel de actualización

Saludos

Raghu

1

@Raghu

Gracias por este bit de código! Estaba teniendo el mismo problema en IE y esto lo solucionó.

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

Esto funcionó perfectamente para mí. ¡Gracias! –

2

Si desea fijar el archivo minified/comprimido Js para jQuery la versión 1.4.2, sustitúyase:

var d=b.getBoundingClientRect(), 

con

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

(tenga en cuenta que no hay coma después de la llave de cierre ahora)

+0

funciona bien para mí ... gracias por compartir ... – Naveen

1

Mi versión es:

  1. Agregar función:

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

    var box = this[0].getBoundingClientRect() 
    

    con

    var box = getOffsetSum(this[0]) 
    

PS: jquery-1.3.2.

1

Esto no es solo un error jQuery. Lo encontré usando ExtJS 4.0.2a en IE8. Parece que IE casi siempre tropieza con element.getBoundingClientRect() si el elemento ha sido reemplazado en el DOM. Tu intento/catch hack es casi la única forma de evitar esto. Supongo que la solución real sería eventualmente soltar el soporte de IE < 9.

Relevent ExtJS v4.0.2a código fuente (líneas): 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 { 
... 

Con una solución 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 { 
... 
Cuestiones relacionadas