jQuery Mobile: pronto para documento x eventos da página

269

Estou usando o jQuery Mobile e estou tendo problemas para entender as diferenças entre os eventos de páginas prontas para documentos clássicos e jQuery Mobile.

  1. Qual é a diferença real?

    Porque deveria

    <!-- language: lang-js -->
    
    $(document).ready() {
    
    });

    ser melhor que

    $(document).on('pageinit') {
    
    });
  2. Qual é a ordem dos eventos da página, quando você faz a transição de uma página para outra?

  3. Como posso enviar dados de uma página para outra e é possível acessar dados da página anterior?

user2001897
fonte
Na questão 1, eles são os mesmos. Você pode alterá-lo ou explicar um pouco mais do que você quer dizer?
Kirk
Portanto, menos de um ano depois, em relação ao evento pageinit, "Este evento foi descontinuado na 1.4.0 em favor da criação de paginação". Veja api.jquerymobile.com/pageinit
DOK

Respostas:

439

Atualização do jQuery Mobile 1.4:

Meu artigo original foi projetado para o modo antigo de manipulação de páginas, basicamente tudo antes do jQuery Mobile 1.4. O modo antigo de manipulação agora está obsoleto e permanecerá ativo até (incluindo) o jQuery Mobile 1.5, para que você ainda possa usar tudo o que foi mencionado abaixo, pelo menos até o próximo ano e o jQuery Mobile 1.6.

Eventos antigos, incluindo o pageinit , não existem mais, são substituídos pelo widget de contêiner de paginação . O Pageinit é apagado completamente e você pode usar a criação de paginação , esse evento permaneceu o mesmo e não será alterado.

Se você estiver interessado em uma nova maneira de lidar com eventos da página, dê uma olhada aqui , em qualquer outro caso, sinta-se à vontade para continuar com este artigo. Você deve ler esta resposta, mesmo se estiver usando o jQuery Mobile 1.4 +, além dos eventos da página, provavelmente encontrará muitas informações úteis.

Conteúdo mais antigo:

Este artigo também pode ser encontrado como parte do meu blog AQUI .

$(document).on('pageinit') vs $(document).ready()

A primeira coisa que você aprende no jQuery é chamar o código dentro da $(document).ready()função para que tudo seja executado assim que o DOM for carregado. No entanto, no jQuery Mobile , o Ajax é usado para carregar o conteúdo de cada página no DOM enquanto você navega. Por isso, $(document).ready()será acionado antes que sua primeira página seja carregada e todos os códigos destinados à manipulação de páginas serão executados após uma atualização da página. Este pode ser um bug muito sutil. Em alguns sistemas, pode parecer que funciona bem, mas em outros pode causar estranheza irregular e difícil de repetir.

Sintaxe clássica do jQuery:

$(document).ready(function() {

});

Para resolver esse problema (e acredite, isso é um problema) Os desenvolvedores do jQuery Mobile criaram eventos de página. Em resumo, os eventos da página são eventos acionados em um determinado ponto de execução da página. Um desses eventos de página é um evento de pageinit e podemos usá-lo assim:

$(document).on('pageinit', function() {

});

Podemos ir ainda mais longe e usar um ID da página em vez do seletor de documentos. Digamos que temos a página do jQuery Mobile com um índice de identificação :

<div data-role="page" id="index">
    <div data-theme="a" data-role="header">
        <h3>
            First Page
        </h3>
        <a href="#second" class="ui-btn-right">Next</a>
    </div>

    <div data-role="content">
        <a href="#" data-role="button" id="test-button">Test button</a>
    </div>

    <div data-theme="a" data-role="footer" data-position="fixed">

    </div>
</div>

Para executar o código que estará disponível apenas para a página de índice, poderíamos usar esta sintaxe:

$('#index').on('pageinit', function() {

});

O evento Pageinit será executado sempre que a página for carregada e mostrada pela primeira vez. Ele não será acionado novamente, a menos que a página seja atualizada manualmente ou o carregamento da página Ajax seja desativado. Caso você queira que o código seja executado toda vez que você visitar uma página, é melhor usar o evento pagebeforeshow .

Aqui está um exemplo de trabalho: http://jsfiddle.net/Gajotres/Q3Usv/ para demonstrar esse problema.

Mais algumas notas sobre esta questão. Independentemente de você estar usando um paradigma de várias páginas ou vários arquivos HTML, é recomendável separar toda a manipulação de sua página JavaScript personalizada em um único arquivo JavaScript separado. Isso fará com que seu código melhore, mas você terá uma visão geral muito melhor, especialmente ao criar um aplicativo jQuery Mobile .

Há também outro evento especial do jQuery Mobile , chamado mobileinit . Quando o jQuery Mobile é iniciado, ele dispara um evento mobileinit no objeto do documento. Para substituir as configurações padrão, vincule-as ao mobileinit . Um dos bons exemplos de uso do mobileinit é desativar o carregamento da página do Ajax ou alterar o comportamento padrão do carregador do Ajax.

$(document).on("mobileinit", function(){
  //apply overrides here
});

Ordem de transição de eventos da página

Primeiro, todos os eventos podem ser encontrados aqui: http://api.jquerymobile.com/category/events/

Digamos que temos uma página A e uma página B, esta é uma ordem de descarregamento / carregamento:

  1. página B - pagebeforecreate de eventos

  2. página B - pagecreate de eventos

  3. página B - evento pageinit

  4. página A - pagebeforehide do evento

  5. página A - pageremove do evento

  6. página A - ocultar página do evento

  7. página B - pagebeforeshow do evento

  8. página B - pageshow de eventos

Para entender melhor os eventos da página, leia isto:

  • pagebeforeload, pageloadE pageloadfailedsão disparados quando uma página externa é carregada
  • pagebeforechange, pagechangeE pagechangefailedsão eventos de mudança de página. Esses eventos são disparados quando um usuário está navegando entre as páginas nos aplicativos.
  • pagebeforeshow, pagebeforehide, pageshowE pagehidesão eventos de transição de página. Esses eventos são disparados antes, durante e após uma transição e são nomeados.
  • pagebeforecreate, pagecreateE pageinitsão para a inicialização da página.
  • pageremove pode ser acionado e tratado quando uma página é removida do DOM

Exemplo de carregamento da página jsFiddle: http://jsfiddle.net/Gajotres/QGnft/

Se o AJAX não estiver ativado, alguns eventos poderão não ser acionados.

Impedir a transição da página

Se, por algum motivo, a transição da página precisar ser impedida sob alguma condição, isso poderá ser feito com este código:

$(document).on('pagebeforechange', function(e, data){
    var to = data.toPage,
        from = data.options.fromPage;

    if (typeof to  === 'string') {
        var u = $.mobile.path.parseUrl(to);
        to = u.hash || '#' + u.pathname.substring(1);
        if (from) from = '#' + from.attr('id');

        if (from === '#index' && to === '#second') {
            alert('Can not transition from #index to #second!');
            e.preventDefault();
            e.stopPropagation();

            // remove active status on a button, if transition was triggered with a button
            $.mobile.activePage.find('.ui-btn-active').removeClass('ui-btn-active ui-focus ui-btn');;
        }
    }
});

Este exemplo funcionará em qualquer caso, pois será acionado no início de cada transição de página e o mais importante será impedir a alteração da página antes que ocorra a transição da página.

Aqui está um exemplo de trabalho:

Impedir a associação / acionamento de vários eventos

jQuery Mobilefunciona de maneira diferente dos aplicativos da web clássicos. Dependendo de como você conseguiu vincular seus eventos cada vez que você visita uma página, ele vincula os eventos repetidamente. Isso não é um erro, é simplesmente como jQuery Mobilelida com suas páginas. Por exemplo, dê uma olhada neste snippet de código:

$(document).on('pagebeforeshow','#index' ,function(e,data){
    $(document).on('click', '#test-button',function(e) {
        alert('Button click');
    });
});

Exemplo de jsFiddle de trabalho: http://jsfiddle.net/Gajotres/CCfL4/

Cada vez que você visitar a página #index click event será vinculado ao botão # test-button . Teste-o movendo da página 1 para a página 2 e retornando várias vezes. Existem algumas maneiras de evitar esse problema:

Solução 1

A melhor solução seria usar pageinitpara vincular eventos. Se você der uma olhada em uma documentação oficial, descobrirá que pageinitserá acionada APENAS uma vez, assim como o documento está pronto, portanto não há como os eventos serem vinculados novamente. Esta é a melhor solução, porque você não possui sobrecarga de processamento, como ao remover eventos com o método off.

Exemplo de jsFiddle de trabalho: http://jsfiddle.net/Gajotres/AAFH8/

Esta solução de trabalho é feita com base em um exemplo problemático anterior.

Solução 2

Remova o evento antes de ligá-lo:

$(document).on('pagebeforeshow', '#index', function(){
    $(document).off('click', '#test-button').on('click', '#test-button',function(e) {
        alert('Button click');
    });
});

Exemplo de jsFiddle de trabalho: http://jsfiddle.net/Gajotres/K8YmG/

Solução 3

Use um seletor de filtro jQuery, assim:

$('#carousel div:Event(!click)').each(function(){
    //If click is not bind to #carousel div do something
});

Como o filtro de eventos não faz parte da estrutura oficial do jQuery, ele pode ser encontrado aqui: http://www.codenothing.com/archives/2009/event-filter/

Em poucas palavras, se a velocidade é sua principal preocupação, a Solução 2 é muito melhor que a Solução 1.

Solução 4

Um novo, provavelmente o mais fácil de todos.

$(document).on('pagebeforeshow', '#index', function(){
    $(document).on('click', '#test-button',function(e) {
        if(e.handled !== true) // This will prevent event triggering more than once
        {
            alert('Clicked');
            e.handled = true;
        }
    });
});

Exemplo de jsFiddle de trabalho: http://jsfiddle.net/Gajotres/Yerv9/

Tnx ao sholsinger para esta solução: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/

peculiaridades do evento pageChange - acionando duas vezes

Às vezes, o evento de troca de paginação pode ser acionado duas vezes e não tem nada a ver com o problema mencionado anteriormente.

O motivo pelo qual o evento pagebeforechange ocorre duas vezes é devido à chamada recursiva no changePage quando toPage não é um objeto DOM aprimorado para jQuery. Essa recursão é perigosa, pois o desenvolvedor pode alterar o toPage no evento. Se o desenvolvedor definir consistentemente toPage como uma sequência, no manipulador de eventos pagebeforechange, independentemente de ser um objeto ou não, um loop recursivo infinito resultará. O evento pageload passa a nova página como propriedade da página do objeto de dados (isso deve ser adicionado à documentação, não está listado no momento). O evento pageload pode, portanto, ser usado para acessar a página carregada.

Em poucas palavras, isso está acontecendo porque você está enviando parâmetros adicionais através do pageChange.

Exemplo:

<a data-role="button" data-icon="arrow-r" data-iconpos="right" href="#care-plan-view?id=9e273f31-2672-47fd-9baa-6c35f093a800&amp;name=Sat"><h3>Sat</h3></a>

Para corrigir esse problema, use qualquer evento da página listado na ordem de transição de eventos da página .

Tempos de mudança de página

Como mencionado, quando você muda de uma página do jQuery Mobile para outra, geralmente clicando em um link para outra página do jQuery Mobile que já existe no DOM ou chamando manualmente $ .mobile.changePage, ocorrem vários eventos e ações subseqüentes. Em um nível alto, ocorrem as seguintes ações:

  • Um processo de mudança de página é iniciado
  • Uma nova página é carregada
  • O conteúdo dessa página é "aprimorado" (com estilo)
  • Ocorre uma transição (slide / pop / etc) da página existente para a nova página

Este é um benchmark médio de transição de página:

Carregamento e processamento da página: 3 ms

Melhoria da página: 45 ms

Transição: 604 ms

Tempo total: 670 ms

* Esses valores estão em milissegundos.

Então, como você pode ver, um evento de transição está consumindo quase 90% do tempo de execução.

Manipulação de dados / parâmetros entre transições de página

É possível enviar um parâmetro / s de uma página para outra durante a transição da página. Isso pode ser feito de várias maneiras.

Referência: https://stackoverflow.com/a/13932240/1848600

Solução 1:

Você pode passar valores com changePage:

$.mobile.changePage('page2.html', { dataUrl : "page2.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : true, changeHash : true });

E leia-os assim:

$(document).on('pagebeforeshow', "#index", function (event, data) {
    var parameters = $(this).data("url").split("?")[1];;
    parameter = parameters.replace("parameter=","");
    alert(parameter);
});

Exemplo :

index.html

<!DOCTYPE html>
  <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="widdiv=device-widdiv, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title>
    </title>
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css" />
    <script src="http://www.dragan-gaic.info/js/jquery-1.8.2.min.js">
    </script>
    <script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>
    <script>
        $(document).on('pagebeforeshow', "#index",function () {
            $(document).on('click', "#changePage",function () {
                $.mobile.changePage('second.html', { dataUrl : "second.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : false, changeHash : true });
            });
        });

        $(document).on('pagebeforeshow', "#second",function () {
            var parameters = $(this).data("url").split("?")[1];;
            parameter = parameters.replace("parameter=","");
            alert(parameter);
        });
    </script>
   </head>
   <body>
    <!-- Home -->
    <div data-role="page" id="index">
        <div data-role="header">
            <h3>
                First Page
            </h3>
        </div>
        <div data-role="content">
          <a data-role="button" id="changePage">Test</a>
        </div> <!--content-->
    </div><!--page-->

  </body>
</html>

second.html

<!DOCTYPE html>
  <html>
    <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="widdiv=device-widdiv, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title>
    </title>
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.css" />
    <script src="http://www.dragan-gaic.info/js/jquery-1.8.2.min.js">
    </script>
    <script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"></script>
   </head>
   <body>
    <!-- Home -->
    <div data-role="page" id="second">
        <div data-role="header">
            <h3>
                Second Page
            </h3>
        </div>
        <div data-role="content">

        </div> <!--content-->
    </div><!--page-->

  </body>
</html>

Solução 2:

Ou você pode criar um objeto JavaScript persistente para fins de armazenamento. Enquanto o Ajax for usado para carregar a página (e a página não for recarregada de forma alguma), esse objeto permanecerá ativo.

var storeObject = {
    firstname : '',
    lastname : ''
}

Exemplo: http://jsfiddle.net/Gajotres/9KKbx/

Solução 3:

Você também pode acessar os dados da página anterior, assim:

$(document).on('pagebeforeshow', '#index',function (e, data) {
    alert(data.prevPage.attr('id'));
});

O objeto prevPage contém uma página anterior completa.

Solução 4:

Como última solução, temos uma implementação em HTML bacana do localStorage. Funciona apenas com navegadores HTML5 (incluindo navegadores Android e iOS), mas todos os dados armazenados são persistentes por meio da atualização da página.

if(typeof(Storage)!=="undefined") {
    localStorage.firstname="Dragan";
    localStorage.lastname="Gaic";
}

Exemplo: http://jsfiddle.net/Gajotres/J9NTr/

Provavelmente a melhor solução, mas falhará em algumas versões do iOS 5.X. É um erro bem conhecido.

Não use .live()/ .bind()/.delegate()

Eu esqueci de mencionar (e tnx andleer para me lembrar) o uso on / off para vinculação / desativação de eventos, viver / morrer e vincular / desativar são preteridos.

O método .live () do jQuery foi visto como uma dádiva de Deus quando foi introduzido na API na versão 1.3. Em um aplicativo jQuery típico, pode haver muita manipulação de DOM e pode ser muito entediante enganchar e desatar os elementos à medida que os elementos vão e vêm. O .live()método tornou possível ligar um evento para a vida útil do aplicativo com base em seu seletor. Ótimo, certo? Errado, o .live()método é extremamente lento. O .live()método realmente conecta seus eventos ao objeto do documento, o que significa que o evento deve aparecer do elemento que gerou o evento até chegar ao documento. Isso pode ser incrivelmente demorado.

Agora está obsoleto. As pessoas da equipe do jQuery não recomendam mais o seu uso, nem eu. Mesmo que possa ser entediante ligar e desconectar eventos, seu código será muito mais rápido sem o .live()método do que com ele.

Em vez de .live()você deve usar .on(). .on()é cerca de 2-3x mais rápido que .live () . Dê uma olhada neste benchmark de associação de eventos: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , tudo ficará claro a partir daí.

Avaliação comparativa:

Existe um excelente script feito para o benchmarking de eventos da página do jQuery Mobile . Pode ser encontrado aqui: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Mas antes de fazer qualquer coisa com ele, aconselho a remover o alertsistema de notificação (cada “página de alteração” mostrará esses dados interrompendo o aplicativo) e altere-o para console.logfuncionar.

Basicamente, esse script registrará todos os eventos da sua página e, se você ler este artigo atentamente (descrições dos eventos da página), saberá quanto tempo o jQm gastou em aprimoramentos e transições de página.

Notas finais

Sempre, quero dizer, sempre leia a documentação oficial do jQuery Mobile . Geralmente, ele fornece as informações necessárias e, ao contrário de outras documentações, essa é bastante boa, com explicações e exemplos de código suficientes.

Alterar:

  • 30.01.2013 - Adicionado um novo método de prevenção de acionamento de múltiplos eventos
  • 31.01.2013 - Foi adicionado um esclarecimento melhor ao capítulo Manipulação de dados / parâmetros entre transições de página
  • 03.02.2013 - Adicionado novo conteúdo / exemplos ao capítulo Manipulação de dados / parâmetros entre transições de página
  • 22.05.2013 - Adicionada uma solução para prevenção de transição / mudança de página e links adicionais para a documentação oficial da API de eventos da página
  • 18.05.2013 - Adicionada outra solução contra a associação de vários eventos
Gajotres
fonte
2
$().live()foi depreciado no jQuery 1.7 e removido no 1.9; portanto, ele realmente deve fazer parte de qualquer solução do jQuery Mobile. A versão principal mínima atual para o jQM 1.7.
andleer
16
+1 muito útil resumo dos comportamentos críticos em torno de carregamento da página
RedFilter
2
pagecreateO evento é acionado apenas uma vez quando a página é criada. portanto, se vincularmos os eventos de clique, pagecreateele não será acionado várias vezes. Algo que descobri ao desenvolver o aplicativo. Mas nem sempre podemos usar pagecreatepara vincular eventos, portanto a solução que você deu é a melhor. 1 dado
Jay Mayu 20/13
1
Você tem o pageBeforeShow listado duas vezes. Ele está listado como número 5 e número 8. Ele é chamado duas vezes?
Perseguição Roberts
Foi um erro de digitação, eu consertei, o pagebeforeshow será acionado apenas uma vez. Obrigado por perceber.
Gajotres
17

Alguns de vocês podem achar isso útil. Basta copiar e colar na sua página e você obterá uma sequência na qual os eventos são disparados no console do Chrome ( Ctrl+ Shift+ I).

$(document).on('pagebeforecreate',function(){console.log('pagebeforecreate');});
$(document).on('pagecreate',function(){console.log('pagecreate');});
$(document).on('pageinit',function(){console.log('pageinit');});
$(document).on('pagebeforehide',function(){console.log('pagebeforehide');});
$(document).on('pagebeforeshow',function(){console.log('pagebeforeshow');});
$(document).on('pageremove',function(){console.log('pageremove');});
$(document).on('pageshow',function(){console.log('pageshow');});
$(document).on('pagehide',function(){console.log('pagehide');});
$(window).load(function () {console.log("window loaded");});
$(window).unload(function () {console.log("window unloaded");});
$(function () {console.log('document ready');});

Você não verá descarregar no console, pois é acionado quando a página está sendo descarregada (quando você se afasta da página). Use-o assim:

$(window).unload(function () { debugger; console.log("window unloaded");});

E você verá o que quero dizer.

Matas Vaitkevicius
fonte
4

Esta é a maneira correta:

Para executar o código que estará disponível apenas para a página de índice, podemos usar esta sintaxe:

$(document).on('pageinit', "#index",  function() {
    ...
});
kecco
fonte
10
a resposta acima afirma o mesmo, você não acha? :)
Omar
Obrigado pela resposta rápida, agradável e rápida. :-)
SharpC 06/06
1

A diferença simples entre o documento pronto e o evento da página no jQuery-mobile é que:

  1. O evento pronto para o documento é usado para toda a página HTML,

    $(document).ready(function(e) {
        // Your code
    });
  2. Quando houver um evento de página, use para manipular um evento de página específico:

    <div data-role="page" id="second">
        <div data-role="header">
            <h3>
                Page header
            </h3>
        </div>
        <div data-role="content">
            Page content
        </div> <!--content-->
        <div data-role="footer">
            Page footer
        </div> <!--footer-->
    </div><!--page-->

Você também pode usar o documento para manipular o evento pageinit:

$(document).on('pageinit', "#mypage", function() {

});
LeoMobDev
fonte
-1

Enquanto você usa .on (), é basicamente uma consulta ao vivo que você está usando.

Por outro lado, .ready (como no seu caso) é uma consulta estática. Ao usá-lo, você pode atualizar dados dinamicamente e não precisa esperar a página carregar. Você pode simplesmente passar os valores para o seu banco de dados (se necessário) quando um valor específico for inserido.

O uso de consultas ativas é comum em formulários nos quais inserimos dados (conta ou postagens ou até mesmo comentários).

Pranjal
fonte