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:
página B - pagebeforecreate de eventos
página B - pagecreate de eventos
página B - evento pageinit
página A - pagebeforehide do evento
página A - pageremove do evento
página A - ocultar página do evento
página B - pagebeforeshow do evento
página B - pageshow de eventos
Para entender melhor os eventos da página, leia isto:
pagebeforeload
, pageload
E pageloadfailed
são disparados quando uma página externa é carregada
pagebeforechange
, pagechange
E pagechangefailed
sã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
, pageshow
E pagehide
sã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
, pagecreate
E pageinit
sã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 Mobile
funciona 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 Mobile
lida 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 pageinit
para vincular eventos. Se você der uma olhada em uma documentação oficial, descobrirá que pageinit
será 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&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);
});
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 alert
sistema de notificação (cada “página de alteração” mostrará esses dados interrompendo o aplicativo) e altere-o para console.log
funcionar.
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