2008-12-16 5 views
7

J'utilise les bibliothèques dragable et droppable de jQuery UI dans une simple application de preuve de concept ASP.NET. Cette page utilise ASP.NET AJAX UpdatePanel pour effectuer des mises à jour partielles de page. La page permet à un utilisateur de déposer un élément dans une div poubelle, ce qui invoquera une publication qui supprime un enregistrement de la base de données, puis redéfinit la liste (et les autres contrôles) dont l'article était la drogue. Tous ces éléments (les éléments draggables et la div trashcan) sont à l'intérieur d'un UpdatePanel ASP.NET.Y at-il une solution de contournement pour IE 6/7 "Erreur non spécifiée" bogue lors de l'accès offsetParent

Voici le glisser-déposer script d'initialisation:

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

    } 

Lorsque les messages de page en arrière, mise à jour partielle du contenu du UpdatePanel, je redéfinissez les accès draggables et droppables. Lorsque je prends un glisser-déplacer avec mon curseur, je reçois un "htmlfile: Erreur non spécifiée". exception. Je peux résoudre ce problème dans la bibliothèque jQuery en remplaçant elem.offsetParent avec des appels à cette fonction que j'ai écrit:

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

Je dois aussi éviter les appels à elem.getBoundingClientRect() car il jette la même erreur. Pour ceux qui étaient intéressés, je devais seulement faire ces changements dans la fonction jQuery.fn.offset dans le Dimensions Plugin.

Mes questions sont les suivantes:

  • Bien que cela fonctionne, il existe de meilleures façons (plus propre, sans avoir à modifier la bibliothèque jQuery, une meilleure performance) pour résoudre ce problème?
  • Sinon, quelle est la meilleure façon de gérer la synchronisation de mes modifications lorsque je mettrai à jour les bibliothèques jQuery à l'avenir? Par exemple, puis-je étendre la bibliothèque ailleurs que dans les fichiers que je télécharge à partir du site Web jQuery.

Mise à jour:

@some Ce n'est pas accessible au public, mais je vais voir s'il en sera de me laisser poster le code correspondant dans cette réponse. Il suffit de créer une application Web ASP.NET (nommez-la DragAndDrop) et créez ces fichiers. N'oubliez pas de définir Complex.aspx comme page de démarrage. Vous aurez également besoin de télécharger le jQuery UI drag and drop plug in ainsi que 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

Pouvez-vous fournir un lien vers une page de test avec le strict minimum? – some

Répondre

6

@arilanto - J'inclus ce script après mes scripts jquery. Performance sage, ce n'est pas la meilleure solution, mais c'est un travail rapide et facile.

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

Je reçois l'erreur 'safari undefined' dans la ligne - "safari2 = safari && parseInt (version) <522 &&! /adobeair/i.test (userAgent)". Des idées? – NLV

1

i essayé la solution suivante pour le getBoundingClientRect() erreur non spécifiée tout en glisser-déposer n, et il fonctionne très bien.

dans les jquery.1.4.2.js (fichier base de jquery-à-dire où l'erreur est renvoyée exactement)

remplacer le elem.getBoundingClientRect() Appel de fonction dans le fichier js

// la ligne qui jette l'erreur non spécifiée

var box = elem.getBoundingClientRect(),

avec ce ..

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

Cela résout le problème et faites glisser n drop fonctionnera quitely même après le post retour via le panneau de mise à jour

Cordialement

Raghu

1

@Raghu

Merci pour ce bit de code! J'avais le même problème dans IE et cela l'a réparé.

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

Cela a fonctionné parfaitement pour moi. Merci! –

2

Si vous souhaitez fixer le minified/fichier .js comprimé pour la version jQuery 1.4.2, remplacer:

var d=b.getBoundingClientRect(), 

avec

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

(notez qu'il n'y a virgule après l'accolade fermante maintenant)

+0

ça marche bien pour moi ... merci pour la part ... – Naveen

1

Ma version est:

  1. fonction Ajouter:

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

    var box = this[0].getBoundingClientRect() 
    

    avec

    var box = getOffsetSum(this[0]) 
    

PS: jquery-1.3.2.

1

Il ne s'agit pas simplement d'une erreur jQuery. Je l'ai rencontré en utilisant ExtJS 4.0.2a sur IE8. Il semble que IE tombera presque toujours sur element.getBoundingClientRect() si l'élément a été remplacé dans le DOM. Votre bidouille try/catch est à peu près le seul moyen de contourner cela. Je suppose que la solution réelle serait de tomber éventuellement support IE <.

Relevent ExtJS v4.0.2a Source Code (lignes 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 { 
... 

Avec une solution 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 { 
... 
Questions connexes