jQuery Mobile: documents prêts contre événements de page

269

J'utilise jQuery Mobile et j'ai du mal à comprendre les différences entre les événements de page prêts pour les documents classiques et jQuery Mobile.

  1. Quelle est la vraie différence?

    Pourquoi devrais-je

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

    être meilleur que

    $(document).on('pageinit') {
    
    });
  2. Quel est l'ordre des événements de page, lorsque vous passez d'une page à une autre?

  3. Comment envoyer des données d'une page à une autre et est-il possible d'accéder aux données de la page précédente?

user2001897
la source
Sous la question 1, ils sont tous les deux identiques. Pouvez-vous le modifier ou expliquer un peu plus ce que vous voulez dire?
Kirk
Ainsi, moins d'un an plus tard concernant l'événement pageinit, "Cet événement a été déprécié en 1.4.0 au profit de pagecreate". Voir api.jquerymobile.com/pageinit
DOK

Réponses:

439

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:

  1. page B - page de l'événement avant de créer

  2. page B - événement pagecreate

  3. page B - événement pageinit

  4. page A - événement pagebeforehide

  5. page A - pageremove d' événement

  6. page A - événement pagehide

  7. page B - événement pagebeforeshow

  8. page B - événement pageshow

Pour une meilleure compréhension des événements de page, lisez ceci:

  • pagebeforeload, pageloadEt pageloadfailedsont déclenché lorsque une page externe est chargée
  • pagebeforechange, pagechangeEt pagechangefailedsont 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, pageshowEt pagehidesont 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, pagecreateEt pageinitsont 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 Mobilefonctionne 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 Mobilegè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 pageinitpour lier des événements. Si vous jetez un œil à une documentation officielle, vous découvrirez qu'elle pageinitne 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&amp;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);
});

Exemple :

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 alertsystè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.logfonctionne.

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
Gajotres
la source
2
$().live()a été déprécié dans jQuery 1.7 et supprimé dans 1.9 donc il devrait vraiment faire partie de toute solution jQuery Mobile. La version de base minimale actuelle pour jQM 1.7.
andleer
16
+1 résumé très utile des comportements critiques concernant les chargements de pages
RedFilter
2
pagecreatel'événement n'est déclenché qu'une seule fois lors de la création de la page. donc si nous lions des événements de clic à l'intérieur, pagecreateil ne se déclenchera pas plusieurs fois. Quelque chose que j'ai découvert lors du développement de l'application. Mais nous ne pouvons pas toujours utiliser pagecreatepour lier des événements, la solution que vous avez donnée est donc la meilleure. +1 donné
Jay Mayu
1
Vous avez pageBeforeShow répertorié deux fois. Il est répertorié comme numéro 5 et numéro 8. Est-il appelé deux fois?
Chase Roberts
C'était une faute de frappe, je l'ai corrigé, pagebeforeshow ne se déclenchera qu'une seule fois. Merci de l'avoir remarqué.
Gajotres
17

Certains d'entre vous pourraient trouver cela utile. Copiez-collez-le sur votre page et vous obtiendrez une séquence dans laquelle les événements sont déclenchés dans la console 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');});

Vous n'allez pas voir décharger dans la console car elle est déclenchée lorsque la page est en cours de déchargement (lorsque vous vous éloignez de la page). Utilisez-le comme ceci:

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

Et vous verrez ce que je veux dire.

Matas Vaitkevicius
la source
4

C'est la bonne façon:

Pour exécuter du code qui ne sera disponible que sur la page d'index, nous pourrions utiliser cette syntaxe:

$(document).on('pageinit', "#index",  function() {
    ...
});
kecco
la source
10
la réponse ci-dessus dit la même chose, tu ne crois pas? :)
Omar
Merci pour la réponse courte et agréable. :-)
SharpC
1

La différence simple entre un document prêt et un événement de page dans jQuery-mobile est que:

  1. L'événement document ready est utilisé pour toute la page HTML,

    $(document).ready(function(e) {
        // Your code
    });
  2. Lorsqu'il y a un événement de page, utilisez pour gérer un événement de page particulier:

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

Vous pouvez également utiliser le document pour gérer l'événement pageinit:

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

});
LeoMobDev
la source
-1

Pendant que vous utilisez .on (), c'est essentiellement une requête en direct que vous utilisez.

D'un autre côté, .ready (comme dans votre cas) est une requête statique. En l'utilisant, vous pouvez mettre à jour dynamiquement les données et ne pas avoir à attendre que la page se charge. Vous pouvez simplement transmettre les valeurs dans votre base de données (si nécessaire) lorsqu'une valeur particulière est entrée.

L'utilisation de requêtes en direct est courante dans les formulaires où nous saisissons des données (compte ou publications ou même commentaires).

Pranjal
la source