web-dev-qa-db-fra.com

jQuery Mobile: document prêt par rapport aux événements de page

J'utilise jQuery Mobile et je ne parviens pas à comprendre les différences entre les événements de page classiques prêts pour le document 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 puis-je 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?

260
user2001897

mise à jour de jQuery Mobile 1.4:

Mon article original était destiné aux anciennes méthodes de traitement des pages, essentiellement à toutes les versions antérieures à jQuery Mobile 1.4. Les anciennes méthodes de traitement sont maintenant obsolètes et restent actives jusqu’à jQuery Mobile 1.5 (inclus). 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, y compris pageinit n'existent plus, ils sont remplacés par pagecontainer widget. Pageinit est complètement effacé et vous pouvez utiliser pagecreate , cet événement est resté identique et ne sera pas modifié.

Si vous êtes intéressé par une nouvelle façon de gérer les événements de page, consultez ici, dans 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 +, cela va au-delà des événements de page, vous y trouverez probablement beaucoup d'informations utiles.

Contenu plus ancien:

Cet article peut également être trouvé dans le cadre de mon blog HERE.

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

La première chose que vous apprenez dans jQuery est d'appeler le code à l'intérieur de la fonction $(document).ready() afin que tout soit exécuté dès que le DOM est chargé. . Cependant, dans jQuery Mobile , Ajax est utilisé pour charger le contenu de chaque page dans le DOM lors de la navigation. Pour cette raison $(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 causer des erreurs irrégulières et difficiles à répéter.

Syntaxe jQuery classique:

_$(document).ready(function() {

});
_

Pour résoudre ce problème (et croyez-moi, il s'agit d'un problème) jQuery Mobile les développeurs ont créé des événements de page. En résumé, les événements de page sont des événements déclenchés à un point particulier de l'exécution de la 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 document. Disons que nous avons une page jQuery Mobile avec un identifiant index :

_<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 sera uniquement disponible pour la page d'index, nous pourrions utiliser cette syntaxe:

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

});
_

Pageinit l'événement sera exécuté chaque fois que la page doit être chargée et affichée pour la première fois. Il ne se déclenchera pas à moins que la page ne soit actualisée manuellement ou que le chargement de page Ajax ne soit pas désactivé. Si vous souhaitez 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.

Quelques notes supplémentaires sur cette question. Peu importe si vous utilisez le paradigme 1 page HTML multiple ou plusieurs fichiers HTML, il est conseillé de séparer l’ensemble de vos pages JavaScript personnalisées dans un fichier JavaScript distinct. Cela améliorera 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 existe également un autre événement spécial jQuery Mobile , appelé mobileinit . Lorsque jQuery Mobile , il déclenche un événement mobileinit sur l'objet de document. Pour remplacer les paramètres par défaut, associez-les à mobileinit . Un des bons exemples d'utilisation de mobileinit consiste à désactiver le chargement de page Ajax ou à 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 - événement pagebeforecreate

  2. page B - événement pagecreate

  3. page B - événement pageinit

  4. page A - événement pagebeforehide

  5. page A - événement pageremove

  6. page A - événement masquer page

  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, pageload et pageloadfailed sont déclenchés lorsqu'une page externe est chargée
  • pagebeforechange, pagechange et pagechangefailed sont des événements de changement de page. Ces événements sont déclenchés lorsqu'un utilisateur navigue entre les pages des applications.
  • pagebeforeshow, pagebeforehide, pageshow et pagehide sont des événements de transition de page. Ces événements sont déclenchés avant, pendant et après une transition et sont nommés.
  • pagebeforecreate, pagecreate et pageinit sont destinés à l'initialisation de la page.
  • pageremove peut être déclenché puis traité lorsqu'une page est supprimée du DOM

Exemple de chargement de la 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, vous pouvez le faire avec le code suivant:

_$(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 déclenchera chaque changement de page et ce qui est le plus important: il empêchera le changement de page avant que la transition de page ne puisse avoir lieu.

Voici un exemple de travail:

Empêcher plusieurs événements de liaison/déclenchement

jQuery Mobile fonctionne différemment des applications Web classiques. Selon la façon dont vous avez réussi à lier vos événements chaque fois que vous visitez une page, les événements seront liés à plusieurs reprises. Ce n'est pas une erreur, c'est simplement la façon dont jQuery Mobile gère ses pages. Par exemple, regardez l'extrait de code suivant:

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

Exemple de travail jsFiddle: http://jsfiddle.net/Gajotres/CCfL4/

Chaque fois que vous visiterez page # index , l'événement click sera lié au bouton # test-button . Testez-le en passant plusieurs fois de la page 1 à la page 2. Il existe plusieurs moyens d'éviter ce problème:

Solution 1

La meilleure solution serait d’utiliser pageinit pour lier des événements. Si vous consultez une documentation officielle, vous constaterez que pageinit ne se déclenchera qu'une seule fois, tout comme un document prêt, il est donc impossible que les événements soient liés à nouveau. C'est la meilleure solution car vous n'avez pas de surcharge de traitement, contrairement à la suppression d'événements avec la méthode off.

Exemple de travail 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

Supprimer 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 de travail 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
});
_

Le filtre d'événements ne faisant pas partie du cadre officiel de jQuery, il peut être trouvé ici: http://www.codenothing.com/archives/2009/event-filter/

En bref, si la rapidité est votre principale préoccupation, alors Solution 2 est beaucoup mieux que la solution 1.

Solution 4

Un nouveau, probablement le plus facile 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 jsFiddle: http://jsfiddle.net/Gajotres/Yerv9/

Merci à sholsinger pour cette solution: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/

changements de pageChangement d’événement - déclenchement deux fois

Parfois, un événement de changement de page peut déclencher deux fois et cela 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 la toPage dans l'événement. Si le développeur définit systématiquement toPage sur une chaîne, dans le gestionnaire d'événements pagebeforechange, qu'il s'agisse ou non d'un objet, il en résultera une boucle récursive infinie. L'événement pageload transmet la nouvelle page en tant que propriété de page de l'objet de données (ceci devrait être ajouté à la documentation, il n'est pas répertorié pour le moment). L'événement pageload pourrait donc être utilisé pour accéder à la page chargée.

En quelques mots, cela se produit parce que 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 l'un des événements de page répertoriés dans Ordre de transition des événements de page .

Temps de changement de page

Comme indiqué précédemment, lorsque vous passez d'une page jQuery Mobile à une autre, généralement en cliquant sur un lien vers une autre page jQuery Mobile qui existe déjà dans le DOM ou 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é” (stylé)
  • Une transition (diapositive/pop/etc) de la page existante à la nouvelle page se produit

Il s'agit d'un repère de transition de page moyen:

Chargement et traitement de la page: 3 ms

Améliorer la page: 45 ms

Transition: 604 ms

Temps total: 670 ms

* Ces valeurs sont en millisecondes.

Comme vous pouvez le constater, 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 paramètre/s d’une page à une autre pendant la transition de page. Cela peut être fait de plusieurs manières.

Référence: https://stackoverflow.com/a/13932240/18486

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 dans un but de stockage. Tant que Ajax est utilisé pour le chargement de la page (et la page n'est pas rechargée de quelque façon que ce soit), 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'));
});
_

prevPage l'objet 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 Android et les navigateurs 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 cela échouera dans certaines versions d'iOS 5.X. C'est une erreur bien connue.

N'utilisez pas .live()/.bind()/.delegate()

J'ai oublié de mentionner (et tnx andleer pour me le rappeler), utiliser on/off pour la liaison d'événement/annulation, live/die et bind/unbind est déconseillé.

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 s'accrocher et de décrocher lorsque les éléments vont et viennent. La méthode .live() permet d’accrocher un événement pour la vie de l’app en fonction de son sélecteur. Super droit? Faux, la méthode .live() est extrêmement lente. La méthode .live() rattache en fait ses événements à l'objet document, ce qui signifie que l'événement doit remonter de l'élément qui l'a généré jusqu'à ce qu'il atteigne le document. Cela peut prendre énormément de temps.

Il est maintenant obsolète. Les membres de l’équipe jQuery ne recommandent plus son utilisation, pas plus que moi. Même s’il peut être fastidieux d’accrocher et de décrocher des événements, votre code sera beaucoup plus rapide sans la méthode .live() qu'avec.

Au lieu de .live(), vous devez utiliser .on(). .on() est environ 2-3 fois plus rapide que . Live () . Jetez un coup d'œil à cette référence d'événement: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , tout sera clair à partir de là.

Benchmarking:

Il existe un excellent script conçu pour l'analyse comparative des événements de page jQuery Mobile . Vous pouvez le trouver ici: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Mais avant de faire quoi que ce soit, je vous conseille de supprimer son système de notification alert (chaque "page de modification" va vous montrer ces données en arrêtant l'application) et le changer en console.log fonction.

Fondamentalement, ce script enregistrera tous vos événements de page. Si vous lisez attentivement cet article (description des événements de page), vous saurez combien de temps jQm a passé d'améliorations de page, de transitions de page, etc.

Notes finales

Toujours, et je veux dire, toujours lire la documentation officielle jQuery Mobile . Il vous fournira généralement les informations nécessaires et, contrairement à d’autres documents, celui-ci est plutôt bon, 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 de plusieurs événements
  • 31.01.2013 - Ajout d'une meilleure clarification pour le chapitre Manipulation de données/paramètres entre les transitions de page
  • 03.02.2013 - Ajout de nouveaux contenus/exemples au chapitre Manipulation de données/paramètres entre les transitions de page
  • 22.05.2013 - Ajout d'une solution pour la prévention de la transition de page/des modifications et ajout de liens vers la documentation officielle de l'API d'événements de page
  • 18.05.2013 - Ajout d'une autre solution contre la liaison d'événements multiples
438
Gajotres

Certains d'entre vous pourraient trouver cela utile. Copiez-le simplement 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 celle-ci est déclenchée lorsque la page est en train d'être déchargée (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.

17
Matas Vaitkevicius

C'est la bonne façon:

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

$(document).on('pageinit', "#index",  function() {
    ...
});
3
kecco

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

  1. L'événement document ready est utilisé pour la totalité de la page HTML.

    $(document).ready(function(e) {
        // Your code
    });
    
  2. Lorsqu'il y a un événement de page, utilisez-le 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 document pour gérer l'événement pageinit:

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

});
1
LeoMobDev