jQuery $ (documento) .ready e UpdatePanels?

475

Estou usando o jQuery para conectar alguns efeitos de mouseover em elementos que estão dentro de um UpdatePanel. Os eventos estão vinculados $(document).ready. Por exemplo:

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

Obviamente, isso funciona bem na primeira vez em que a página é carregada, mas quando o UpdatePanel faz uma atualização parcial da página, ela não é executada e os efeitos do mouseover não funcionam mais no UpdatePanel.

Qual é a abordagem recomendada para instalar o jQuery não apenas no carregamento da primeira página, mas sempre que um UpdatePanel dispara uma atualização parcial da página? Devo usar o ciclo de vida do ASP.NET ajax em vez de $(document).ready?

Caudill de ervas
fonte
Tentar dar um presente eu acho que vai resolver o seu problema codeproject.com/Articles/21962/...
Baxterboom
1
Semelhante a esta pergunta: stackoverflow.com/questions/301473/…
Por Hornshøj-Schierbeck 20/04/2015

Respostas:

545

Um UpdatePanel substitui completamente o conteúdo do painel de atualização em uma atualização. Isso significa que os eventos nos quais você se inscreveu não são mais inscritos porque há novos elementos nesse painel de atualização.

O que eu fiz para contornar isso é reinscrever os eventos necessários após cada atualização. Utilizo $(document).ready()o carregamento inicial e depois o da Microsoft PageRequestManager(disponível se você tiver um painel de atualização em sua página) para assinar novamente todas as atualizações.

$(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
});

O PageRequestManageré um objeto javascript que está disponível automaticamente se um painel de atualização estiver na página. Você não precisa fazer nada além do código acima para usá-lo enquanto o UpdatePanel estiver na página.

Se você precisar de um controle mais detalhado, esse evento passará argumentos semelhantes à maneira como os eventos do .NET são passados, (sender, eventArgs)para que você possa ver o que gerou o evento e somente reativar, se necessário.

Aqui está a versão mais recente da documentação da Microsoft: msdn.microsoft.com/.../bb383810.aspx


Uma opção melhor que você pode ter, dependendo de suas necessidades, é usar o jQuery .on(). Esse método é mais eficiente do que se inscrever novamente nos elementos DOM a cada atualização. Leia toda a documentação antes de usar essa abordagem, pois ela pode ou não atender às suas necessidades. Existem muitos plugins jQuery que não seriam razoáveis ​​para refatorar o uso .delegate()ou .on(), portanto, nesses casos, é melhor você se inscrever novamente.

Dan Herbert
fonte
1
Seja mais preciso quando estiver dando explicações. Quero dizer, um exemplo de uso é muito melhor do que algumas linhas de código dispersas. Veja a explicação de Brian MacKay. É perfeito!
truthseeker
3
@ Dan, a partir do jQuery 1.7, .delegate()foi substituído pelo.on()
Gabriele Petrioli
@ GabyakaG.Petrioli Sei que foi substituído, mas como essa resposta parece resolver um problema comum, eu queria maximizar a compatibilidade com versões mais antigas do jQuery que ainda não foram atualizadas. Minha resposta recomendou anteriormente, .live(...)que foi oficialmente descontinuada na versão 1.7 e pode ser removida em uma versão futura. Eu escolhi intencionalmente, .delegate()já que ele é suportado no jQuery há um tempo e provavelmente não será removido tão cedo. No entanto, atualizarei minha resposta para mencionar .on()também para aqueles que podem usá-la.
Dan Herbert
12
É melhor declarar e conectar o prm var dentro de $ (document) .ready, pois é possível que o Sys ainda não tenha sido declarado (dependendo de onde está o script).
drake7707
1
@AhmedSamy Não é mais recomendado o uso jQuery.delegate(). Foi substituída pela jQuery.on()qual é a API preferida a ser usada. delegate()é simplesmente um invólucro para um uso específico de on(), e é possível que fique obsoleto no futuro. Meu comentário anterior delegate()foi escrito há mais de um ano quando o jQuery 1.7 (que introduziu a on()API) foi lançado recentemente. Com o jQuery 1.9 já lançado e o 2.0 na versão beta sendo preparado para o lançamento, é uma boa idéia evitar o uso delegate()de qualquer novo código.
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>

A área que será atualizada.

<asp:UpdatePanel...
<ContentTemplate
     <script type="text/javascript">
                    Sys.Application.add_load(BindEvents);
     </script>
 *// Staff*
</ContentTemplate>
    </asp:UpdatePanel>
Barbaros Alp
fonte
Funciona bem para asp: GridView dentro de caixa de texto <EditItemTemplate>
Dan Randolph
O problema com esta solução é que, enquanto funciona, mata a postagem assíncrona do UpdatePanel - tornando o UpdatePanel inútil novamente. (suspiro)
MC9000 07/09
63

Controle de usuário com jQuery dentro de um UpdatePanel

Essa não é uma resposta direta à pergunta, mas eu montei essa solução lendo as respostas que encontrei aqui e achei que alguém poderia achar útil.

Eu estava tentando usar um limitador de área de texto jQuery dentro de um controle de usuário. Isso foi complicado, porque o Controle do Usuário é executado dentro de um UpdatePanel e estava perdendo suas ligações no retorno de chamada.

Se fosse apenas uma página, as respostas aqui seriam aplicadas diretamente. No entanto, os Controles de Usuário não têm acesso direto à tag head, nem acesso direto ao UpdatePanel, como algumas das respostas supõem.

Acabei colocando esse bloco de script na parte superior da marcação do meu Controle de Usuário. Para a ligação inicial, ele usa $ (document) .ready e, em seguida, usa prm.add_endRequest a partir daí:

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

Então ... Só pensei que alguém gostaria de saber que isso funciona.

Brian MacKay
fonte
2
Não consegui fazer isso funcionar pela minha vida. Então eu mudei da cabeça para o rodapé e funcionou. Não é positivo, mas acho que precisava vir depois do ScriptManager que estou usando.
Adam Youngers
No meu caso eu estava adicionando o script usando ScriptManager.RegisterClientScriptBlock que adiciona o script antes Sys é definido, então acabei usando RegisterStartupScript vez (ver diferença aqui )
Firas Assaad
2
Resposta legal! funcionou como um encanto no meu aplicativo da web asp.net. + para você
Pranesh Janarthanan
33

Atualize para o jQuery 1.3 e use:

$(function() {

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

});

Nota: o live funciona com a maioria dos eventos, mas não com todos. Há uma lista completa na documentação .

svinto
fonte
O uso ao vivo corrige o problema com os painéis de atualização. Não há aros necessários para pular.
22360 Tim
E algo que precisa acontecer no carregamento de página? Por exemplo, tabelas de distribuição de zebra? $ ('TABELA TR: enésimo filho (ímpar)'). AddClass ('alt-row');
Adam Youngers 08/09
3
Só para atualização, uma vez que jQuery 1.7 agora é recomendado o uso de .no () em vez
George
1
Acabei de encontrar uma falha grave ao usar o live()método no IE7 (projeto jQuery 1.5.1 / VS2010). Ao usar live()dentro de um UpdatePanel em v3.5 ASP.NET um regular AutoPostbackde <asp:DropDownList />causas 2 postbacks parciais, em oposição à 1. Espera O postback adicional emitido pelo navegador não tem o conteúdo (abortado) causando a Page.IsPostBackser false(que mata estado dentro do meu limite controles). Eu achei o culpado o método live (). Percebo que o 1º postback será abortado pelo UpdatePanel por especificação, mas apenas se houver outro na fila do qual não exista.
timmi4sa
20

Você também pode tentar:

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

, pois pageLoad () é um evento ajax do ASP.NET que é executado sempre que a página é carregada no lado do cliente.

Daniel Hursan
fonte
pageLoad duplicar os eventos em cada postagem do ASync no Painel de Atualização.
perfil
17

Minha resposta?

function pageLoad() {

  $(document).ready(function(){

etc.

Funcionou como um encanto, onde várias outras soluções falharam miseravelmente.

Jono
fonte
Perfeito para redimensionar as áreas de texto no meu Listview no meu UpdatePanel no meu UserControl de acordo com a quantidade de texto.
Resource
Lutei para fazer com que as funções dom jquery funcionassem corretamente no postback com painéis de atualização, esta solução funcionou e não precisei duplicar minhas chamadas de função em dois cenários. Isso manteve as funções do jquery e o ouvinte disponíveis. Nota: Coloquei todos os meus scripts e bibliotecas jquery antes da tag FORM [end] na parte inferior da página. Obrigado!!!
moto_geek
7

Eu usaria uma das seguintes abordagens:

  1. Encapsule a associação de eventos em uma função e execute-a sempre que você atualizar a página. Você sempre pode conter a associação de eventos a elementos específicos para não associar eventos várias vezes aos mesmos elementos.

  2. Use o plug-in livequery , que basicamente executa o método um para você automaticamente. Sua preferência pode variar dependendo da quantidade de controle que você deseja ter na associação de eventos.

Eran Galperin
fonte
7

Isso funcionou para mim:

$(document).ready(function() {

    // Do something exciting

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

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

});
Turbojohan
fonte
6

A função pageLoad () é muito perigosa para ser usada nessa situação. Você pode fazer com que os eventos sejam conectados várias vezes. Também ficaria longe de .live (), pois ele é anexado ao elemento de documento e precisa percorrer a página inteira (lenta e de baixa qualidade).

A melhor solução que eu já vi até agora é usar a função jQuery .delegate () em um invólucro fora do painel de atualização e usar bolhas. Além disso, você sempre pode conectar os manipuladores usando a biblioteca Ajax da Microsoft, projetada para funcionar com o UpdatePanels.

Norma
fonte
6

Quando $(document).ready(function (){...})não funcionar após a postagem da página, use a função JavaScript pageLoad no Asp.page da seguinte maneira:

<script type="text/javascript" language="javascript">
function pageLoad() {
// Initialization code here, meant to run once. 
}
</script>
Pradeep Mais
fonte
Sim, verifique também o isPartialLoad: stackoverflow.com/questions/301473/…
Steve Greene
4

Tive um problema semelhante e descobri que a maneira que funcionava melhor era confiar no Bubbling de Eventos e na delegação de eventos para lidar com isso. O bom da delegação de eventos é que, uma vez configurado, você não precisa religar os eventos após uma atualização do AJAX.

O que faço no meu código é configurar um delegado no elemento pai do painel de atualização. Este elemento pai não é substituído em uma atualização e, portanto, a ligação do evento não é afetada.

Há vários bons artigos e plug-ins para lidar com a delegação de eventos no jQuery e o recurso provavelmente será inserido na versão 1.3. O artigo / plugin que eu uso para referência é:

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

Depois de entender o que está acontecendo, acho que você encontrará uma solução muito mais elegante e mais confiável do que lembrar de vincular novamente os eventos após cada atualização. Isso também tem o benefício adicional de oferecer a você um evento para desvincular quando a página é descarregada.

Joe Brinkman
fonte
4

FWIW, experimentei um problema semelhante com o mootools. Voltar a anexar meus eventos foi a jogada correta, mas precisava ser feita no final da solicitação.

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

Apenas algo a ter em mente se beginRequest fizer com que você obtenha exceções de JS de referência nula.

Felicidades


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

A função pageLoad é perfeita para esse caso, pois é executada no carregamento inicial da página e em cada postagem assíncrona do updatepanel. Eu apenas tive que adicionar o método unbind para fazer o jquery funcionar nos postbacks do updatepanel.

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

Duane
fonte
2

Minha resposta é baseada em todos os comentários de especialistas acima, mas abaixo está o código a seguir que qualquer pessoa pode usar para garantir que em cada postback e em cada post assíncrono o código JavaScript ainda será executado.

No meu caso, eu tinha um controle de usuário em uma página. Basta colar o código abaixo no seu controle de usuário.

<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>
Abhishek Shrivastava
fonte
2

O Painel de Atualização sempre substitui seu Jquery pelos scripts do seu Scriptmanager embutido após cada carregamento. É melhor se você usar os métodos de instância do pageRequestManager como este ...

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

vai funcionar bem ...

Rohit Sharma
fonte
Um post antigo, mas que me ajudou. Uma coisa que eu gostaria de acrescentar a isso: a localização do código add_endRequest deve estar dentro do UpdatePanel, pois o Sys.WebForms.PageRequestManager está disponível apenas nesse momento. No entanto, a função real não precisa estar nesse local. Portanto, no meu caso, tenho um arquivo script.js que contém o código que quero vincular, contido em uma função. Nesse arquivo script.js, eu tenho uma chamada document.ready, que chama a função acima. Então, no meu UpdatePanel, tenho o código add_endRequest, que também chama a função acima.
precisa
1

Use o script abaixo e altere o corpo do script de acordo.

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

Em resposta à resposta de Brian MacKay:

Injeto o JavaScript na minha página por meio do ScriptManager, em vez de colocá-lo diretamente no HTML do UserControl. No meu caso, preciso rolar para um formulário que fique visível depois que o UpdatePanel for concluído e retornado. Isso vai no código por trás do arquivo. No meu exemplo, eu já criei a variável prm na página de conteúdo 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;
}
Fujiiface
fonte
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
        });
}
Tony Dong
fonte