jQuery Mobile: documenti pronti contro eventi di pagina


269

Sto usando jQuery Mobile e ho difficoltà a comprendere le differenze tra gli eventi di pagina pronti per documenti classici e jQuery Mobile.

  1. Qual è la vera differenza?

    Perché dovrebbe

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

    essere meglio di

    $(document).on('pageinit') {
    
    });
    
  2. Qual è l'ordine degli eventi di pagina quando si passa da una pagina all'altra?

  3. Come posso inviare dati da una pagina all'altra ed è possibile accedere ai dati dalla pagina precedente?


Sotto la domanda 1, sono entrambi uguali. Puoi modificarlo o spiegare un po 'di più di cosa intendi?
Kirk,

Quindi, meno di un anno dopo, per quanto riguarda l'evento pageinit, "Questo evento è stato deprecato in 1.4.0 a favore di pagecreate". Vedi api.jquerymobile.com/pageinit
DOK

Risposte:


439

Aggiornamento di jQuery Mobile 1.4:

Il mio articolo originale era destinato al vecchio modo di gestire le pagine, praticamente tutto prima di jQuery Mobile 1.4. Il vecchio modo di gestire è ora deprecato e rimarrà attivo fino (incluso) a jQuery Mobile 1.5, quindi è ancora possibile utilizzare tutto quanto indicato di seguito, almeno fino al prossimo anno e jQuery Mobile 1.6.

I vecchi eventi, incluso pageinit , non esistono più, vengono sostituiti con il widget pagecontainer . Pageinit viene completamente cancellato e puoi usare invece pagecreate , quell'evento è rimasto lo stesso e non verrà modificato.

Se sei interessato a un nuovo modo di gestire gli eventi della pagina dai un'occhiata qui , in ogni caso sentiti libero di continuare con questo articolo. Dovresti leggere questa risposta anche se stai usando jQuery Mobile 1.4 +, va oltre gli eventi di pagina, quindi troverai probabilmente molte informazioni utili.

Contenuto precedente:

Questo articolo può anche essere trovato come una parte del mio blog QUI .

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

La prima cosa che impari in jQuery è chiamare il codice all'interno della $(document).ready()funzione in modo che tutto venga eseguito non appena viene caricato il DOM. Tuttavia, in jQuery Mobile , Ajax viene utilizzato per caricare i contenuti di ciascuna pagina nel DOM durante la navigazione. Per questo $(document).ready()motivo si attiverà prima che la tua prima pagina venga caricata e ogni codice destinato alla manipolazione della pagina verrà eseguito dopo un aggiornamento della pagina. Questo può essere un bug molto sottile. In alcuni sistemi può sembrare che funzioni bene, ma in altri può causare stranezze irregolari, difficili da ripetere.

Sintassi classica di jQuery:

$(document).ready(function() {

});

Per risolvere questo problema (e credetemi, questo è un problema) gli sviluppatori di jQuery Mobile hanno creato eventi di pagina. In breve, gli eventi della pagina sono eventi attivati ​​in un particolare punto dell'esecuzione della pagina. Uno di quegli eventi di pagina è un evento di pageinit e possiamo usarlo in questo modo:

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

});

Possiamo andare ancora oltre e utilizzare un ID pagina anziché il selettore documenti. Supponiamo di avere una pagina jQuery Mobile con un indice 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>

Per eseguire il codice che sarà disponibile solo per la pagina dell'indice, potremmo usare questa sintassi:

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

});

L' evento Pageinit verrà eseguito ogni volta che la pagina sta per essere caricata e mostrata per la prima volta. Non si attiverà più a meno che la pagina non venga aggiornata manualmente o il caricamento della pagina Ajax sia disattivato. Nel caso in cui desideri che il codice venga eseguito ogni volta che visiti una pagina, è meglio utilizzare l' evento pagebeforeshow .

Ecco un esempio funzionante: http://jsfiddle.net/Gajotres/Q3Usv/ per dimostrare questo problema.

Poche altre note su questa domanda. Indipendentemente se si utilizza 1 html di più pagine o paradigma di più file HTML, si consiglia di separare tutta la gestione della pagina JavaScript personalizzata in un singolo file JavaScript separato. Questo renderà il tuo codice ancora migliore, ma avrai una panoramica del codice molto migliore, specialmente durante la creazione di un'applicazione jQuery Mobile .

C'è anche un altro evento speciale su jQuery Mobile e si chiama mobileinit . All'avvio di jQuery Mobile , viene attivato un evento mobileinit sull'oggetto documento. Per sovrascrivere le impostazioni predefinite, associale a mobileinit . Uno dei buoni esempi dell'utilizzo di mobileinit è la disattivazione del caricamento della pagina Ajax o la modifica del comportamento predefinito del caricatore Ajax.

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

Ordine di transizione degli eventi della pagina

Innanzitutto tutti gli eventi possono essere trovati qui: http://api.jquerymobile.com/category/events/

Diciamo che abbiamo una pagina A e una pagina B, questo è un ordine di scarico / caricamento:

  1. pagina B - pagina dell'evento per creare

  2. pagina B - pagecreate evento

  3. pagina B - evento pageinit

  4. pagina A - pagina dell'evento prima

  5. pagina A - evento pageremove

  6. pagina A - evento pagehide

  7. pagina B - evento pagebeforeshow

  8. pagina B - mostra pagine dell'evento

Per una migliore comprensione degli eventi della pagina leggi questo:

  • pagebeforeload, pageloadE pageloadfailedsono attivato quando una pagina esterna viene caricata
  • pagebeforechange, pagechangeE pagechangefailedsono pagina eventi di modifica. Questi eventi vengono generati quando un utente sta navigando tra le pagine delle applicazioni.
  • pagebeforeshow, pagebeforehide, pageshowE pagehidesono gli eventi di transizione di pagina. Questi eventi vengono generati prima, durante e dopo una transizione e vengono denominati.
  • pagebeforecreate, pagecreateE pageinitsono per la pagina di inizializzazione.
  • pageremove può essere attivato e quindi gestito quando una pagina viene rimossa dal DOM

Caricamento pagina js Esempio di esempio: http://jsfiddle.net/Gajotres/QGnft/

Se AJAX non è abilitato, alcuni eventi potrebbero non attivarsi.

Impedisci la transizione della pagina

Se per qualche motivo la transizione della pagina deve essere impedita in alcune condizioni, può essere eseguita con questo codice:

$(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');;
        }
    }
});

Questo esempio funzionerà in ogni caso perché si attiverà in seguito a una supplica di ogni transizione di pagina e ciò che è più importante impedirà il cambio di pagina prima che possa avvenire la transizione di pagina.

Ecco un esempio funzionante:

Prevenire l'associazione / l'attivazione di più eventi

jQuery Mobilefunziona in modo diverso rispetto alle classiche applicazioni Web. A seconda di come sei riuscito a legare i tuoi eventi ogni volta che visiti una pagina, rileverà gli eventi più e più volte. Questo non è un errore, è semplicemente come jQuery Mobilegestisce le sue pagine. Ad esempio, dai un'occhiata a questo frammento di codice:

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

Esempio js funzionante: http://jsfiddle.net/Gajotres/CCfL4/

Ogni volta che visiti la pagina #index, l' evento sarà associato al pulsante # test-pulsante . Provalo passando da pagina 1 a pagina 2 e viceversa più volte. Esistono alcuni modi per prevenire questo problema:

Soluzione 1

La soluzione migliore sarebbe utilizzare pageinitper associare gli eventi. Se dai un'occhiata a una documentazione ufficiale, scoprirai che pageinitsi attiverà SOLO una volta, proprio come il documento pronto, quindi non c'è modo che gli eventi vengano rilegati di nuovo. Questa è la soluzione migliore perché non hai un overhead di elaborazione come quando rimuovi eventi con il metodo off.

Esempio js funzionante: http://jsfiddle.net/Gajotres/AAFH8/

Questa soluzione funzionante è realizzata sulla base di un precedente esempio problematico.

Soluzione 2

Rimuovi l'evento prima di associarlo:

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

Esempio js funzionante: http://jsfiddle.net/Gajotres/K8YmG/

Soluzione 3

Utilizzare un selettore filtro jQuery, in questo modo:

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

Poiché il filtro eventi non fa parte del framework jQuery ufficiale, è possibile trovarlo qui: http://www.codenothing.com/archives/2009/event-filter/

In breve, se la velocità è la tua preoccupazione principale, la Soluzione 2 è molto meglio della Soluzione 1.

Soluzione 4

Uno nuovo, probabilmente il più semplice di tutti.

$(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;
        }
    });
});

Esempio js funzionante: http://jsfiddle.net/Gajotres/Yerv9/

Grazie allo sholsinger per questa soluzione: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/

Quirks di eventi pageChange - attivazione due volte

A volte l'evento pagechange può attivarsi due volte e non ha nulla a che fare con il problema menzionato in precedenza.

Il motivo per cui l'evento pagebeforechange si verifica due volte è dovuto alla chiamata ricorsiva in changePage quando toPage non è un oggetto DOM avanzato jQuery. Questa ricorsione è pericolosa, in quanto allo sviluppatore è consentito modificare la pagina all'interno dell'evento. Se lo sviluppatore imposta costantemente su Pagina su una stringa, all'interno del gestore di eventi pagebeforechange, indipendentemente dal fatto che si trattasse o meno di un oggetto, si verificherà un ciclo ricorsivo infinito. L'evento pageload passa la nuova pagina come proprietà page dell'oggetto dati (questo dovrebbe essere aggiunto alla documentazione, non è attualmente elencato). L'evento pageload potrebbe quindi essere utilizzato per accedere alla pagina caricata.

In poche parole ciò accade perché si inviano parametri aggiuntivi tramite pageChange.

Esempio:

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

Per risolvere questo problema, utilizzare qualsiasi evento di pagina elencato nell'ordine di transizione degli eventi di pagina .

Tempi di cambio pagina

Come accennato, quando si passa da una pagina jQuery Mobile a un'altra, in genere facendo clic su un collegamento a un'altra pagina jQuery Mobile già esistente nel DOM o chiamando manualmente $ .mobile.changePage, si verificano numerosi eventi e azioni successive. Ad un livello elevato si verificano le seguenti azioni:

  • Viene avviato un processo di modifica della pagina
  • Viene caricata una nuova pagina
  • Il contenuto di quella pagina è "migliorato" (stile)
  • Si verifica una transizione (diapositiva / pop / ecc.) Dalla pagina esistente alla nuova pagina

Questo è un benchmark medio di transizione delle pagine:

Caricamento ed elaborazione della pagina: 3 ms

Miglioramento della pagina: 45 ms

Transizione: 604 ms

Tempo totale: 670 ms

* Questi valori sono espressi in millisecondi.

Come puoi vedere, un evento di transizione sta consumando quasi il 90% dei tempi di esecuzione.

Manipolazione di dati / parametri tra le transizioni di pagina

È possibile inviare un / i parametro / i da una pagina all'altra durante la transizione della pagina. Può essere fatto in pochi modi.

Riferimento: https://stackoverflow.com/a/13932240/1848600

Soluzione 1:

Puoi passare valori con changePage:

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

E leggili in questo modo:

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

Esempio :

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>

Soluzione 2:

Oppure puoi creare un oggetto JavaScript persistente a scopo di archiviazione. Finché Ajax viene utilizzato per il caricamento della pagina (e la pagina non viene ricaricata in alcun modo) quell'oggetto rimarrà attivo.

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

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

Soluzione 3:

Puoi anche accedere ai dati dalla pagina precedente in questo modo:

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

L' oggetto prevPage contiene una pagina precedente completa.

Soluzione 4:

Come ultima soluzione, abbiamo un'importante implementazione HTML di localStorage. Funziona solo con i browser HTML5 (inclusi i browser Android e iOS) ma tutti i dati memorizzati sono persistenti attraverso l'aggiornamento della pagina.

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

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

Probabilmente la soluzione migliore ma fallirà in alcune versioni di iOS 5.X. È un errore ben noto.

Non usare .live()/ .bind()/.delegate()

Ho dimenticato di menzionare (e tnx andleer per avermi ricordato) l'uso on / off per il binding / unbinding dell'evento, live / die e bind / unbind sono deprecati.

Il metodo .live () di jQuery è stato visto come una manna dal cielo quando è stato introdotto nell'API nella versione 1.3. In una tipica app jQuery può esserci molta manipolazione del DOM e può diventare molto noioso agganciarsi e sganciarsi quando gli elementi vanno e vengono. Il .live()metodo ha permesso di agganciare un evento per la vita dell'app in base al suo selettore. Ottimo vero? Sbagliato, il .live()metodo è estremamente lento. Il .live()metodo in realtà aggancia i suoi eventi all'oggetto documento, il che significa che l'evento deve passare dall'elemento che ha generato l'evento fino a raggiungere il documento. Questo può richiedere incredibilmente tempo.

Ora è deprecato. Le persone del team di jQuery non ne raccomandano più l'uso e nemmeno io. Anche se può essere noioso agganciare e sganciare gli eventi, il tuo codice sarà molto più veloce senza il .live()metodo che con esso.

Invece di .live()te dovresti usare .on(). .on()è circa 2-3x più veloce di .live () . Dai un'occhiata a questo benchmark vincolante per gli eventi: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , tutto sarà chiaro da lì.

Analisi comparativa:

Esiste un eccellente script creato per il benchmarking degli eventi di pagina jQuery Mobile . Può essere trovato qui: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Ma prima di fare qualsiasi cosa con esso, ti consiglio di rimuovere il suo alertsistema di notifica (ogni "pagina di modifica" ti mostrerà questi dati arrestando l'app) e cambiarlo per console.logfunzionare.

Fondamentalmente questo script registrerà tutti gli eventi della tua pagina e se leggi attentamente questo articolo (descrizioni degli eventi della pagina) saprai quanto tempo speso jQm per migliorare le pagine, le transizioni di pagina ....

Note finali

Sempre e intendo sempre leggere la documentazione ufficiale di jQuery Mobile . Di solito ti fornirà le informazioni necessarie e, diversamente da qualche altra documentazione, questa è piuttosto buona, con sufficienti spiegazioni ed esempi di codice.

I cambiamenti:

  • 30.01.2013 - Aggiunto un nuovo metodo per la prevenzione di eventi multipli
  • 31.01.2013 - Aggiunto un migliore chiarimento per la manipolazione dei dati / parametri del capitolo tra le transizioni di pagina
  • 03.02.2013 - Aggiunti nuovi contenuti / esempi al capitolo Manipolazione di dati / parametri tra le transizioni di pagina
  • 22.05.2013 - Aggiunta una soluzione per la transizione di pagina / prevenzione delle modifiche e collegamenti aggiunti alla documentazione API ufficiale degli eventi di pagina
  • 18.05.2013 - Aggiunta un'altra soluzione contro l'associazione di più eventi

2
$().live()è stato ammortizzato in jQuery 1.7 e rimosso in 1.9, quindi dovrebbe davvero far parte di qualsiasi soluzione jQuery Mobile. L'attuale versione minima del core per jQM 1.7.
andleer,

16
+1 riepilogo molto utile dei comportamenti critici relativi al caricamento della pagina
RedFilter

2
pagecreatel'evento viene generato solo una volta alla prima creazione della pagina. quindi se associamo gli eventi clic all'interno pagecreate, non si attiveranno più volte. Qualcosa che ho capito durante lo sviluppo dell'app. Ma non possiamo sempre usare pagecreateper associare gli eventi, quindi la soluzione che hai dato è la migliore. +1 dato
Jay Mayu

1
Hai pageBeforeShow elencato due volte. È elencato come numero 5 e numero 8. Viene chiamato due volte?
Chase Roberts,

Era un refuso, l'ho risolto, pagebeforeshow si innescherà solo una volta. Grazie per averlo notato.
Gajotres,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.