Mise à jour de jQuery Mobile 1.4:
Mon article d'origine était destiné à l'ancienne façon de gérer les pages, essentiellement tout avant jQuery Mobile 1.4. L'ancienne façon de gérer est désormais obsolète et elle restera active jusqu'à (y compris) jQuery Mobile 1.5, vous pouvez donc toujours utiliser tout ce qui est mentionné ci-dessous, au moins jusqu'à l'année prochaine et jQuery Mobile 1.6.
Les anciens événements, dont pageinit n'existent plus, ils sont remplacés par le widget pagecontainer . Pageinit est complètement effacé et vous pouvez utiliser pagecreate à la place, cet événement est resté le même et ne va pas être modifié.
Si vous êtes intéressé par une nouvelle façon de gérer les événements de page, jetez un œil ici , dans tous les autres cas, n'hésitez pas à continuer avec cet article. Vous devriez lire cette réponse même si vous utilisez jQuery Mobile 1.4 +, elle va au-delà des événements de page et vous trouverez probablement beaucoup d'informations utiles.
Contenu plus ancien:
Cet article peut également être trouvé dans le cadre de mon blog ICI .
$(document).on('pageinit')
contre $(document).ready()
La première chose que vous apprenez dans jQuery est d'appeler du code à l'intérieur de la $(document).ready()
fonction pour que tout s'exécute dès que le DOM est chargé. Cependant, dans jQuery Mobile , Ajax est utilisé pour charger le contenu de chaque page dans le DOM pendant que vous naviguez. À cause de cela, il $(document).ready()
se déclenchera avant le chargement de votre première page et chaque code destiné à la manipulation de la page sera exécuté après un rafraîchissement de la page. Cela peut être un bug très subtil. Sur certains systèmes, il peut sembler que cela fonctionne bien, mais sur d'autres, cela peut provoquer une bizarrerie erratique et difficile à répéter.
Syntaxe jQuery classique:
$(document).ready(function() {
});
Pour résoudre ce problème (et croyez-moi, c'est un problème), les développeurs de jQuery Mobile ont créé des événements de page. En résumé, les événements de page sont des événements déclenchés dans un point particulier d'exécution de page. L'un de ces événements de page est un événement pageinit et nous pouvons l'utiliser comme ceci:
$(document).on('pageinit', function() {
});
Nous pouvons aller encore plus loin et utiliser un identifiant de page au lieu du sélecteur de documents. Disons que nous avons une page jQuery Mobile avec un index id :
<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>
Pour exécuter du code qui ne sera disponible que pour la page d'index, nous pourrions utiliser cette syntaxe:
$('#index').on('pageinit', function() {
});
L' événement Pageinit sera exécuté à chaque fois que la page est sur le point d'être chargée et affichée pour la première fois. Il ne se déclenchera à nouveau que si la page est actualisée manuellement ou si le chargement de la page Ajax est désactivé. Si vous voulez que le code s'exécute à chaque fois que vous visitez une page, il est préférable d'utiliser l' événement pagebeforeshow .
Voici un exemple de travail: http://jsfiddle.net/Gajotres/Q3Usv/ pour illustrer ce problème.
Encore quelques notes sur cette question. Peu importe si vous utilisez 1 html plusieurs pages ou plusieurs paradigmes de fichiers HTML, il est conseillé de séparer l'ensemble de votre gestion de page JavaScript personnalisée dans un seul fichier JavaScript distinct. Cela permettra d'améliorer votre code, mais vous aurez une bien meilleure vue d'ensemble du code, en particulier lors de la création d'une application jQuery Mobile .
Il y a aussi un autre événement spécial jQuery Mobile et il s'appelle mobileinit . Lorsque jQuery Mobile démarre, il déclenche un événement mobileinit sur l'objet document. Pour remplacer les paramètres par défaut, liez-les à mobileinit . L'un des bons exemples d' utilisation de mobileinit est de désactiver le chargement des pages Ajax ou de modifier le comportement du chargeur Ajax par défaut.
$(document).on("mobileinit", function(){
//apply overrides here
});
Ordre de transition des événements de page
Tout d'abord, tous les événements peuvent être trouvés ici: http://api.jquerymobile.com/category/events/
Disons que nous avons une page A et une page B, il s'agit d'un ordre de déchargement / chargement:
page B - page de l'événement avant de créer
page B - événement pagecreate
page B - événement pageinit
page A - événement pagebeforehide
page A - pageremove d' événement
page A - événement pagehide
page B - événement pagebeforeshow
page B - événement pageshow
Pour une meilleure compréhension des événements de page, lisez ceci:
pagebeforeload
, pageload
Et pageloadfailed
sont déclenché lorsque une page externe est chargée
pagebeforechange
, pagechange
Et pagechangefailed
sont la page des événements de changement. Ces événements sont déclenchés lorsqu'un utilisateur navigue entre les pages des applications.
pagebeforeshow
, pagebeforehide
, pageshow
Et pagehide
sont la page des événements de transition. Ces événements sont déclenchés avant, pendant et après une transition et sont nommés.
pagebeforecreate
, pagecreate
Et pageinit
sont pour l' initialisation de la page.
pageremove
peut être renvoyé puis géré lorsqu'une page est supprimée du DOM
Exemple de chargement de page jsFiddle: http://jsfiddle.net/Gajotres/QGnft/
Si AJAX n'est pas activé, certains événements peuvent ne pas se déclencher.
Empêcher la transition de page
Si pour une raison quelconque, la transition de page doit être empêchée à certaines conditions, cela peut être fait avec ce code:
$(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');;
}
}
});
Cet exemple fonctionnera dans tous les cas, car il se déclenchera à la mendicité de chaque transition de page et ce qui est le plus important, il empêchera le changement de page avant que la transition de page puisse se produire.
Voici un exemple de travail:
Empêcher la liaison / le déclenchement d'événements multiples
jQuery Mobile
fonctionne d'une manière différente des applications Web classiques. Selon la façon dont vous avez réussi à lier vos événements à chaque fois que vous visitez une page, cela liera les événements à plusieurs reprises. Ce n'est pas une erreur, c'est simplement la façon dont jQuery Mobile
gère ses pages. Par exemple, jetez un œil à cet extrait de code:
$(document).on('pagebeforeshow','#index' ,function(e,data){
$(document).on('click', '#test-button',function(e) {
alert('Button click');
});
});
Exemple d'utilisation de jsFiddle: http://jsfiddle.net/Gajotres/CCfL4/
Chaque fois que vous visiterez la page #index, un événement de clic sera lié au bouton # test-button . Testez-le en passant de la page 1 à la page 2 et inversement plusieurs fois. Il existe plusieurs façons d'éviter ce problème:
Solution 1
La meilleure solution serait d'utiliser pageinit
pour lier des événements. Si vous jetez un œil à une documentation officielle, vous découvrirez qu'elle pageinit
ne se déclenchera qu'une seule fois, tout comme le document est prêt, il n'y a donc aucun moyen que les événements soient à nouveau liés. C'est la meilleure solution car vous n'avez pas de surcharge de traitement comme lors de la suppression d'événements avec la méthode off.
Exemple d'utilisation de jsFiddle: http://jsfiddle.net/Gajotres/AAFH8/
Cette solution de travail est faite sur la base d'un exemple problématique précédent.
Solution 2
Supprimez l'événement avant de le lier:
$(document).on('pagebeforeshow', '#index', function(){
$(document).off('click', '#test-button').on('click', '#test-button',function(e) {
alert('Button click');
});
});
Exemple d'utilisation de jsFiddle: http://jsfiddle.net/Gajotres/K8YmG/
Solution 3
Utilisez un sélecteur de filtre jQuery, comme ceci:
$('#carousel div:Event(!click)').each(function(){
//If click is not bind to #carousel div do something
});
Étant donné que le filtre d'événements ne fait pas partie du cadre jQuery officiel, il peut être trouvé ici: http://www.codenothing.com/archives/2009/event-filter/
En un mot, si la vitesse est votre principale préoccupation, la solution 2 est bien meilleure que la solution 1.
Solution 4
Un nouveau, probablement le plus simple de tous.
$(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;
}
});
});
Exemple de travail avec jsFiddle: http://jsfiddle.net/Gajotres/Yerv9/
Tnx au sholsinger pour cette solution: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/
pageChange bizarreries d'événement - déclenchement deux fois
Parfois, un événement pagechange peut se déclencher deux fois et n'a rien à voir avec le problème mentionné précédemment.
La raison pour laquelle l'événement pagebeforechange se produit deux fois est due à l'appel récursif dans changePage lorsque toPage n'est pas un objet DOM amélioré jQuery. Cette récursivité est dangereuse, car le développeur est autorisé à modifier le toPage dans l'événement. Si le développeur définit systématiquement à Page une chaîne, dans le gestionnaire d'événements pagebeforechange, qu'il s'agisse ou non d'un objet, une boucle récursive infinie en résultera. L'événement pageload passe la nouvelle page en tant que propriété de page de l'objet de données (cela doit être ajouté à la documentation, il n'est pas répertorié actuellement). L'événement pageload pourrait donc être utilisé pour accéder à la page chargée.
En quelques mots, cela se produit car vous envoyez des paramètres supplémentaires via pageChange.
Exemple:
<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>
Pour résoudre ce problème, utilisez n'importe quel événement de page répertorié dans l' ordre de transition des événements de page .
Temps de changement de page
Comme mentionné, lorsque vous passez d'une page jQuery Mobile à une autre, généralement soit en cliquant sur un lien vers une autre page jQuery Mobile qui existe déjà dans le DOM, soit en appelant manuellement $ .mobile.changePage, plusieurs événements et actions ultérieures se produisent. À un niveau élevé, les actions suivantes se produisent:
- Un processus de changement de page est commencé
- Une nouvelle page est chargée
- Le contenu de cette page est «amélioré» (stylisé)
- Une transition (diapo / pop / etc) de la page existante vers la nouvelle page se produit
Il s'agit d'une référence de transition de page moyenne:
Chargement et traitement des pages: 3 ms
Amélioration de la page: 45 ms
Transition: 604 ms
Temps total: 670 ms
* Ces valeurs sont en millisecondes.
Comme vous pouvez le voir, un événement de transition consomme près de 90% du temps d'exécution.
Manipulation des données / paramètres entre les transitions de page
Il est possible d'envoyer un / des paramètre (s) d'une page à une autre pendant la transition de page. Cela peut se faire de plusieurs manières.
Référence: https://stackoverflow.com/a/13932240/1848600
Solution 1:
Vous pouvez transmettre des valeurs avec changePage:
$.mobile.changePage('page2.html', { dataUrl : "page2.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : true, changeHash : true });
Et lisez-les comme ceci:
$(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>
Solution 2:
Ou vous pouvez créer un objet JavaScript persistant à des fins de stockage. Tant que Ajax est utilisé pour le chargement de la page (et que la page n'est pas rechargée de quelque façon), cet objet restera actif.
var storeObject = {
firstname : '',
lastname : ''
}
Exemple: http://jsfiddle.net/Gajotres/9KKbx/
Solution 3:
Vous pouvez également accéder aux données de la page précédente comme ceci:
$(document).on('pagebeforeshow', '#index',function (e, data) {
alert(data.prevPage.attr('id'));
});
L' objet prevPage contient une page précédente complète.
Solution 4:
Comme dernière solution, nous avons une implémentation HTML astucieuse de localStorage. Il ne fonctionne qu'avec les navigateurs HTML5 (y compris les navigateurs Android et iOS) mais toutes les données stockées sont persistantes lors de l'actualisation de la page.
if(typeof(Storage)!=="undefined") {
localStorage.firstname="Dragan";
localStorage.lastname="Gaic";
}
Exemple: http://jsfiddle.net/Gajotres/J9NTr/
Probablement la meilleure solution mais elle échouera dans certaines versions d'iOS 5.X. C'est une erreur bien connue.
Ne pas utiliser .live()
/ .bind()
/.delegate()
J'ai oublié de mentionner (et tnx andleer pour me le rappeler) que l'utilisation on / off pour la liaison / dissociation d'événements, live / die et bind / unbind sont obsolètes.
La méthode .live () de jQuery était considérée comme une aubaine lorsqu'elle a été introduite dans l'API dans la version 1.3. Dans une application jQuery typique, il peut y avoir beaucoup de manipulations DOM et il peut devenir très fastidieux de raccrocher et de décrocher lorsque les éléments vont et viennent. La .live()
méthode a permis d'accrocher un événement pour la durée de vie de l'application en fonction de son sélecteur. Parfait non? Mal, la .live()
méthode est extrêmement lente. La .live()
méthode accroche en fait ses événements à l'objet document, ce qui signifie que l'événement doit remonter de l'élément qui a généré l'événement jusqu'à ce qu'il atteigne le document. Cela peut prendre énormément de temps.
Il est désormais obsolète. Les gens de l'équipe jQuery ne recommandent plus son utilisation et moi non plus. Même s'il peut être fastidieux de connecter et de décrocher des événements, votre code sera beaucoup plus rapide sans la .live()
méthode qu'avec lui.
Au lieu de .live()
vous devez utiliser .on()
. .on()
est environ 2 à 3 fois plus rapide que .live () . Jetez un œil à cette référence de liaison d'événements: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , tout sera clair à partir de là.
Analyse comparative:
Il existe un excellent script conçu pour l' analyse comparative des événements de la page jQuery Mobile . Il peut être trouvé ici: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Mais avant de faire quoi que ce soit avec elle, je vous conseille de supprimer son alert
système de notification (chaque «page de changement» va vous montrer ces données en arrêtant l'application) et de la changer pour qu'elle console.log
fonctionne.
Fondamentalement, ce script enregistrera tous vos événements de page et si vous lisez attentivement cet article (descriptions des événements de page), vous saurez combien de temps jQm a consacré aux améliorations de page, aux transitions de page ...
Notes finales
Toujours, et je veux dire toujours lire la documentation officielle de jQuery Mobile . Il vous fournira généralement les informations nécessaires, et contrairement à d'autres documentations, celle-ci est plutôt bonne, avec suffisamment d'explications et d'exemples de code.
Changements:
- 30.01.2013 - Ajout d'une nouvelle méthode de prévention du déclenchement d'événements multiples
- 31.01.2013 - Ajout d'une meilleure clarification pour le chapitre Manipulation des données / paramètres entre les transitions de page
- 03.02.2013 - Ajout de nouveaux contenus / exemples au chapitre Manipulation des données / paramètres entre les transitions de page
- 22.05.2013 - Ajout d'une solution pour la transition de page / prévention des modifications et ajout de liens vers la documentation officielle de l'API des événements de page
- 18.05.2013 - Ajout d'une autre solution contre la liaison d'événements multiples