jQuery $ (document) .ready y UpdatePanels?


475

Estoy usando jQuery para conectar algunos efectos de mouseover en elementos que están dentro de un UpdatePanel. Los eventos están vinculados $(document).ready. Por ejemplo:

$(function() {    
    $('div._Foo').bind("mouseover", function(e) {
        // Do something exciting
    });    
});

Por supuesto, esto funciona bien la primera vez que se carga la página, pero cuando UpdatePanel realiza una actualización parcial de la página, no se ejecuta y los efectos de mouseover ya no funcionan dentro de UpdatePanel.

¿Cuál es el enfoque recomendado para conectar cosas en jQuery no solo en la carga de la primera página, sino cada vez que un UpdatePanel dispara una actualización parcial de la página? ¿Debo usar el ciclo de vida ASP.NET ajax en lugar de $(document).ready?



Respuestas:


545

Un UpdatePanel reemplaza completamente el contenido del panel de actualización en una actualización. Esto significa que los eventos a los que se suscribió ya no están suscritos porque hay nuevos elementos en ese panel de actualización.

Lo que he hecho para solucionar esto es volver a suscribirme a los eventos que necesito después de cada actualización. Lo uso $(document).ready()para la carga inicial, luego uso Microsoft PageRequestManager(disponible si tiene un panel de actualización en su página) para volver a suscribirse a cada actualización.

$(document).ready(function() {
    // bind your jQuery events here initially
});

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_endRequest(function() {
    // re-bind your jQuery events here
});

El PageRequestManageres un objeto de JavaScript que está disponible automáticamente si hay un panel de actualización en la página. No debería necesitar hacer nada más que el código anterior para usarlo siempre que UpdatePanel esté en la página.

Si necesita un control más detallado, este evento pasa argumentos similares a cómo se pasan los eventos .NET (sender, eventArgs)para que pueda ver qué provocó el evento y solo volver a vincular si es necesario.

Aquí está la última versión de la documentación de Microsoft: msdn.microsoft.com/.../bb383810.aspx


Una mejor opción que pueda tener, según sus necesidades, es usar jQuery's .on(). Estos métodos son más eficientes que volver a suscribirse a elementos DOM en cada actualización. Sin embargo, lea toda la documentación antes de utilizar este enfoque, ya que puede o no satisfacer sus necesidades. Hay muchos complementos de jQuery que no sería razonable refactorizar para usar .delegate()o .on(), por lo tanto, en esos casos, es mejor volver a suscribirse.


1
Sea más preciso cuando dé una explicación. Quiero decir, un ejemplo de uso es mucho mejor que un par de líneas de código dispersas. Ver la explicación de Brian MacKay. ¡Es perfecto!
buscador de la verdad

3
@ Dan, a partir de jQuery 1.7, .delegate()ha sido reemplazado por el.on()
Gabriele Petrioli

@ GabyakaG.Petrioli Me doy cuenta de que ha sido reemplazado, pero como esta respuesta parece resolver un problema común, quería maximizar la compatibilidad con versiones anteriores de jQuery que aún no se han actualizado. Mi respuesta recomendó previamente .live(...)que se ha desaprobado oficialmente en 1.7 y se puede eliminar en una versión futura. Elegí intencionalmente .delegate()ya que ha sido compatible con jQuery desde hace un tiempo y es probable que no se elimine pronto. Sin embargo, actualizaré mi respuesta para mencionarla .on()también para aquellos que puedan usarla.
Dan Herbert

12
Es mejor declarar y conectar el prm var dentro del $ (document) .ready, ya que es posible que el Sys aún no se haya declarado (dependiendo de dónde esté el script).
drake7707

1
@AhmedSamy No se recomienda usar jQuery.delegate()más. Ha sido reemplazado por jQuery.on()cuál es la API preferida para usar. delegate()es simplemente un contenedor para un uso específico de on(), y es posible que se vuelva obsoleto en el futuro. Mi comentario anterior mencionando delegate()fue escrito hace más de un año cuando jQuery 1.7 (que introdujo la on()API) fue lanzado recientemente. Con jQuery 1.9 ya disponible y 2.0 en beta en preparación para su lanzamiento, es una buena idea evitar usarlo delegate()en cualquier código nuevo.
Dan Herbert

159
<script type="text/javascript">

        function BindEvents() {
            $(document).ready(function() {
                $(".tr-base").mouseover(function() {
                    $(this).toggleClass("trHover");
                }).mouseout(function() {
                    $(this).removeClass("trHover");
                });
         }
</script>

El área que se actualizará.

<asp:UpdatePanel...
<ContentTemplate
     <script type="text/javascript">
                    Sys.Application.add_load(BindEvents);
     </script>
 *// Staff*
</ContentTemplate>
    </asp:UpdatePanel>

Funciona bien para asp: GridView TextBox dentro de <EditItemTemplate>
Dan Randolph

El problema con esta solución es que, mientras funciona, mata la publicación asincrónica de UpdatePanel, lo que hace que UpdatePanel sea inútil una vez más. (suspiro)
MC9000

63

Control de usuario con jQuery dentro de un UpdatePanel

Esta no es una respuesta directa a la pregunta, pero armé esta solución leyendo las respuestas que encontré aquí, y pensé que alguien podría encontrarla útil.

Estaba tratando de usar un limitador de área de texto jQuery dentro de un Control de usuario. Esto fue complicado, porque el Control de usuario se ejecuta dentro de un UpdatePanel, y estaba perdiendo sus enlaces en la devolución de llamada.

Si esto fuera solo una página, las respuestas aquí se habrían aplicado directamente. Sin embargo, los controles de usuario no tienen acceso directo a la etiqueta principal, ni tampoco tienen acceso directo al UpdatePanel, como suponen algunas de las respuestas.

Terminé colocando este bloque de script directamente en la parte superior del marcado de Control de usuario. Para el enlace inicial, usa $ (document) .ready, y luego usa prm.add_endRequest desde allí:

<script type="text/javascript">
    function BindControlEvents() {
        //jQuery is wrapped in BindEvents function so it can be re-bound after each callback.
        //Your code would replace the following line:
            $('#<%= TextProtocolDrugInstructions.ClientID %>').limit('100', '#charsLeft_Instructions');            
    }

    //Initial bind
    $(document).ready(function () {
        BindControlEvents();
    });

    //Re-bind for callbacks
    var prm = Sys.WebForms.PageRequestManager.getInstance(); 

    prm.add_endRequest(function() { 
        BindControlEvents();
    }); 

</script>

Entonces ... Solo pensé que a alguien le gustaría saber que esto funciona.


2
No pude hacer que esto funcionara por mi vida. Luego lo moví de la cabeza al pie de página y funcionó. No es positivo, pero creo que tenía que venir después del ScriptManager que estoy usando.
Adam Youngers

En mi caso, estaba agregando el script usando ScriptManager.RegisterClientScriptBlock que agrega el script antes de que se defina Sys, así que terminé usando RegisterStartupScript en su lugar (vea la diferencia aquí )
Firas Assaad

2
Buena respuesta! funcionó de maravilla en mi aplicación web asp.net. + para ti
Pranesh Janarthanan

33

Actualice a jQuery 1.3 y use:

$(function() {

    $('div._Foo').live("mouseover", function(e) {
        // Do something exciting
    });

});

Nota: Live funciona con la mayoría de los eventos, pero no con todos. Hay una lista completa en la documentación .


El uso de Live corrige el problema con los paneles de actualización. No se necesitan aros para saltar.
Tim Scarborough

¿Qué pasa con algo que debe suceder en la carga de la página? Por ejemplo mesas de rayas de cebra? $ ('TABLE TR: nth-child (impar)'). AddClass ('alt-row');
Adam Youngers

3
Solo para actualizar, desde jQuery 1.7 ahora se recomienda usar .on () en su lugar
George

1
Acabo de encontrar una falla grave al usar el live()método en IE7 (proyecto jQuery 1.5.1 / VS2010). Cuando se usa live()dentro de un UpdatePanel en v3.5 ASP.NET, una causa regular AutoPostbackde <asp:DropDownList />2 devoluciones parciales parciales en comparación con la esperada 1. La devolución de datos adicional emitida por el navegador carece del contenido (abortado) lo que causa Page.IsPostBackque sea false(lo que mata el estado dentro de mi límite control S). Encontré que el culpable es el método live (). Me doy cuenta de que UpdatePanel cancelará la primera devolución de datos por especificación, pero solo si hay otra en la cola de la que no existe.
timmi4sa

20

También puedes probar:

<asp:UpdatePanel runat="server" ID="myUpdatePanel">
    <ContentTemplate>

        <script type="text/javascript" language="javascript">
        function pageLoad() {
           $('div._Foo').bind("mouseover", function(e) {
               // Do something exciting
           });
        }
        </script>

    </ContentTemplate>
</asp:UpdatePanel>

, ya que pageLoad () es un evento ajax de ASP.NET que se ejecuta cada vez que la página se carga en el lado del cliente.


pageLoad duplica los eventos en cada devolución de datos ASync del Panel de actualización.
Zo tiene el

17

¿Mi respuesta?

function pageLoad() {

  $(document).ready(function(){

etc.

Funcionó de maravilla, donde varias otras soluciones fallaron miserablemente.


Perfecto para cambiar el tamaño de las áreas de texto en mi Listview en mi UpdatePanel en mi UserControl según la cantidad de texto.
Recurso

Luché para que las funciones dom jquery funcionaran correctamente en la devolución de datos con paneles de actualización, esta solución funcionó y no tuve que duplicar mis llamadas a funciones en 2 escenarios. Esto mantuvo las funciones jquery y el oyente disponibles. Tenga en cuenta que puse todo mi script y la biblioteca jquery incluye justo antes de la etiqueta FORM [final] en la parte inferior de la página. ¡¡¡Gracias!!!
moto_geek

7

Yo usaría uno de los siguientes enfoques:

  1. Encapsule el enlace del evento en una función y ejecútelo cada vez que actualice la página. Siempre puede contener el enlace de eventos a elementos específicos para no vincular eventos varias veces a los mismos elementos.

  2. Utilice el complemento livequery , que básicamente realiza el método uno automáticamente. Su preferencia puede variar según la cantidad de control que desee tener sobre el enlace del evento.


7

Esto funcionó para mí:

$(document).ready(function() {

    // Do something exciting

    var prm = Sys.WebForms.PageRequestManager.getInstance();

    prm.add_endRequest(function() {
        // re-bind your jQuery events here
    });

});

6

La función pageLoad () es muy peligrosa de usar en esta situación. Puede hacer que los eventos se conecten varias veces. También me mantendría alejado de .live (), ya que se adjunta al elemento del documento y tiene que atravesar toda la página (lento y horrible).

La mejor solución que he visto hasta ahora es usar la función jQuery .delegate () en un contenedor fuera del panel de actualización y hacer uso de burbujeo. Aparte de eso, siempre puede conectar los controladores utilizando la biblioteca Ajax de Microsoft, que fue diseñada para funcionar con UpdatePanels.


6

Cuando $(document).ready(function (){...})no funciona después de la publicación de la página, use la función JavaScript pageLoad en Asp.page de la siguiente manera:

<script type="text/javascript" language="javascript">
function pageLoad() {
// Initialization code here, meant to run once. 
}
</script>

Sí, verifique también isPartialLoad: stackoverflow.com/questions/301473/…
Steve Greene

4

Tuve un problema similar y descubrí que la forma que mejor funcionaba era confiar en Event Bubbling y la delegación de eventos para manejarlo. Lo bueno de la delegación de eventos es que, una vez configurada, no es necesario volver a vincular los eventos después de una actualización AJAX.

Lo que hago en mi código es configurar un delegado en el elemento principal del panel de actualización. Este elemento principal no se reemplaza en una actualización y, por lo tanto, el enlace del evento no se ve afectado.

Hay una serie de buenos artículos y complementos para manejar la delegación de eventos en jQuery y la característica probablemente se incluirá en la versión 1.3. El artículo / complemento que uso como referencia es:

http://www.danwebb.net/2008/2/8/event-delegation-made-easy-in-jquery

Una vez que comprenda lo que está sucediendo, creo que encontrará una solución mucho más elegante que es más confiable que recordar volver a vincular eventos después de cada actualización. Esto también tiene el beneficio adicional de darle un evento para desvincular cuando la página se descarga.


4

FWIW, experimenté un problema similar con mootools. Volver a adjuntar mis eventos fue el movimiento correcto, pero tenía que hacerse al final de la solicitud ... por ejemplo

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function() {... 

Es algo a tener en cuenta si beginRequest hace que obtenga excepciones de JS de referencia nula.

Salud



3
pageLoad = function () {
    $('#div').unbind();
    //jquery here
}

La función pageLoad es perfecta para este caso, ya que se ejecuta en la carga de la página inicial y en cada devolución de datos asíncrona del panel de actualización. Solo tuve que agregar el método de desvinculación para hacer que jquery funcione en las devoluciones de datos del panel de actualización.

http://encosia.com/document-ready-and-pageload-are-not-the-same/


2

Mi respuesta se basa en todos los comentarios de expertos anteriores, pero a continuación se muestra el siguiente código que cualquiera puede usar para asegurarse de que en cada devolución de datos y en cada devolución de datos asincrónica el código JavaScript aún se ejecute.

En mi caso, tenía un control de usuario dentro de una página. Simplemente pegue el siguiente código en su control de usuario.

<script type="text/javascript"> 
        var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_endRequest(EndRequestHandler);
    function EndRequestHandler(sender, args) {
        if (args.get_error() == undefined) {
            UPDATEPANELFUNCTION();
        }                   
    }

    function UPDATEPANELFUNCTION() {
        jQuery(document).ready(function ($) {
            /* Insert all your jQuery events and function calls */
        });
    }

    UPDATEPANELFUNCTION(); 

</script>

2

El Panel de actualización siempre reemplaza su Jquery con los scripts incorporados de Scriptmanager después de cada carga. Es mejor si usa métodos de instancia de pageRequestManager como este ...

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(onEndRequest)
    function onEndRequest(sender, args) {
       // your jquery code here
      });

funcionará bien ...


Una publicación antigua, pero que me ayudó. Una cosa que me gustaría agregar a esto: la ubicación del código add_endRequest tiene que estar dentro del UpdatePanel, ya que Sys.WebForms.PageRequestManager solo está disponible en ese punto. Sin embargo, la función real no necesita estar en esa ubicación. Entonces, en mi caso, tengo un archivo script.js que contiene el código que quiero enlazar, contenido dentro de una función. En ese archivo script.js, tengo una llamada document.ready, que invoca la función anterior. Luego, en mi UpdatePanel, tengo el código add_endRequest, que también invoca la función anterior.
Kiran Ramaswamy

1

Utilice el guión a continuación y cambie el cuerpo del guión en consecuencia.

       <script>
        //Re-Create for on page postbacks
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(function () {
           //your codes here!
        });
    </script>

0

En respuesta a la respuesta de Brian MacKay:

Inyecto el JavaScript en mi página a través del ScriptManager en lugar de ponerlo directamente en el HTML del UserControl. En mi caso, necesito desplazarme a un formulario que se hace visible después de que UpdatePanel haya finalizado y devuelto. Esto va en el código detrás del archivo. En mi muestra, ya he creado la variable prm en la página de contenido principal.

private void ShowForm(bool pShowForm) {
    //other code here...
    if (pShowForm) {
        FocusOnControl(GetFocusOnFormScript(yourControl.ClientID), yourControl.ClientID);
    }
}

private void FocusOnControl(string pScript, string pControlId) {
    ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "focusControl_" + pControlId, pScript, true);
}

/// <summary>
/// Scrolls to the form that is made visible
/// </summary>
/// <param name="pControlId">The ClientID of the control to focus on after the form is made visible</param>
/// <returns></returns>
private string GetFocusOnFormScript(string pControlId) {
    string script = @"
    function FocusOnForm() {
        var scrollToForm = $('#" + pControlId + @"').offset().top;
        $('html, body').animate({ 
            scrollTop: scrollToForm}, 
            'slow'
        );
        /* This removes the event from the PageRequestManager immediately after the desired functionality is completed so that multiple events are not added */
        prm.remove_endRequest(ScrollFocusToFormCaller);
    }
    prm.add_endRequest(ScrollFocusToFormCaller);
    function ScrollFocusToFormCaller(sender, args) {
        if (args.get_error() == undefined) {
            FocusOnForm();
        }
    }";
    return script;
}

0
Sys.Application.add_load(LoadHandler); //This load handler solved update panel did not bind control after partial postback
function LoadHandler() {
        $(document).ready(function () {
        //rebind any events here for controls under update panel
        });
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.