jQuery Mobile: documentos listos frente a eventos de página

269

Estoy usando jQuery Mobile, y tengo problemas para entender las diferencias entre los documentos clásicos preparados y los eventos de página de jQuery Mobile.

  1. ¿Cuál es la diferencia real?

    Porque deberia

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

    ser mejor que

    $(document).on('pageinit') {
    
    });
  2. ¿Cuál es el orden de los eventos de la página cuando realiza la transición de una página a otra?

  3. ¿Cómo puedo enviar datos de una página a otra y es posible acceder a los datos de la página anterior?

usuario2001897
fuente
Bajo la pregunta 1, ambos son lo mismo. ¿Puedes alterarlo o explicar un poco más de lo que quieres decir?
Kirk
Entonces, menos de un año después con respecto al evento pageinit, "Este evento ha quedado en desuso en 1.4.0 a favor de pagecreate". Ver api.jquerymobile.com/pageinit
DOK

Respuestas:

439

Actualización de jQuery Mobile 1.4:

Mi artículo original estaba destinado a la forma antigua de manejo de páginas, básicamente todo antes de jQuery Mobile 1.4. La antigua forma de manejo ahora está en desuso y permanecerá activa hasta (incluido) jQuery Mobile 1.5, por lo que aún puede usar todo lo mencionado a continuación, al menos hasta el próximo año y jQuery Mobile 1.6.

Los eventos antiguos, incluido pageinit, ya no existen, se reemplazan con el widget pagecontainer . Pageinit se borra por completo y puede usar pagecreate en su lugar, ese evento se mantuvo igual y no se va a cambiar.

Si está interesado en una nueva forma de manejo de eventos de página, eche un vistazo aquí , en cualquier otro caso, no dude en continuar con este artículo. Debería leer esta respuesta incluso si está utilizando jQuery Mobile 1.4 +, va más allá de los eventos de la página, por lo que probablemente encontrará mucha información útil.

Contenido anterior:

Este artículo también se puede encontrar como parte de mi blog AQUÍ .

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

Lo primero que aprende en jQuery es llamar al código dentro de la $(document).ready()función para que todo se ejecute tan pronto como se cargue el DOM. Sin embargo, en jQuery Mobile , Ajax se usa para cargar el contenido de cada página en el DOM mientras navega. Debido a esto, $(document).ready()se activará antes de que se cargue su primera página y cada código destinado a la manipulación de la página se ejecutará después de una actualización de la página. Esto puede ser un error muy sutil. En algunos sistemas puede parecer que funciona bien, pero en otros puede causar rarezas erráticas y difíciles de repetir.

Sintaxis clásica de jQuery:

$(document).ready(function() {

});

Para resolver este problema (y confía en mí, este es un problema), los desarrolladores de jQuery Mobile crearon eventos de página. En pocas palabras, los eventos de página son eventos desencadenados en un punto particular de ejecución de página. Uno de esos eventos de página es un evento pageinit y podemos usarlo así:

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

});

Podemos ir aún más lejos y usar una identificación de página en lugar del selector de documentos. Digamos que tenemos la página jQuery Mobile con un índice de identificación :

<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 ejecutar código que solo estará disponible para la página de índice, podríamos usar esta sintaxis:

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

});

El evento Pageinit se ejecutará cada vez que se cargue la página y se muestre por primera vez. No se activará nuevamente a menos que la página se actualice manualmente o la carga de la página Ajax esté desactivada. En caso de que desee que el código se ejecute cada vez que visita una página, es mejor usar pagebeforeshow event.

Aquí hay un ejemplo de trabajo: http://jsfiddle.net/Gajotres/Q3Usv/ para demostrar este problema.

Pocas notas más sobre esta pregunta. No importa si está utilizando 1 html de múltiples páginas o paradigma de múltiples archivos HTML, se recomienda separar todo el manejo personalizado de su página JavaScript en un solo archivo JavaScript separado. Esto hará que su código sea mejor, pero tendrá una descripción general del código mucho mejor, especialmente al crear una aplicación jQuery Mobile .

También hay otro evento especial de jQuery Mobile y se llama mobileinit . Cuando se inicia jQuery Mobile , desencadena un evento mobileinit en el objeto del documento. Para anular la configuración predeterminada, vincúlela a mobileinit . Uno de los buenos ejemplos del uso de mobileinit es desactivar la carga de la página de Ajax o cambiar el comportamiento predeterminado del cargador de Ajax.

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

Orden de transición de eventos de página

Primero, todos los eventos se pueden encontrar aquí: http://api.jquerymobile.com/category/events/

Digamos que tenemos una página A y una página B, este es un orden de descarga / carga:

  1. página B - página de evento antes de crear

  2. página B - evento pagecreate

  3. página B - evento pageinit

  4. página A - página del evento antes

  5. página A - página de eventosremove

  6. página A - evento pagehide

  7. página B - página del evento antes de mostrar

  8. página B - evento páginashow

Para una mejor comprensión de los eventos de la página, lea esto:

  • pagebeforeload, pageloady pageloadfailedse disparan cuando se carga una página externa
  • pagebeforechange, pagechangey pagechangefailedson eventos de cambio de página. Estos eventos se activan cuando un usuario navega entre las páginas de las aplicaciones.
  • pagebeforeshow, pagebeforehide, pageshowY pagehideson eventos de transición de página. Estos eventos se disparan antes, durante y después de una transición y se nombran.
  • pagebeforecreate, pagecreatey pageinitson para la inicialización de la página.
  • pageremove puede ser despedido y luego manejado cuando una página se elimina del DOM

Ejemplo de carga de página jsFiddle: http://jsfiddle.net/Gajotres/QGnft/

Si AJAX no está habilitado, algunos eventos pueden no activarse.

Prevenir la transición de página

Si por alguna razón la transición de página necesita ser prevenida en alguna condición, se puede hacer con 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 ejemplo funcionará en cualquier caso porque se activará al comienzo de cada transición de página y, lo que es más importante, evitará el cambio de página antes de que pueda ocurrir la transición de página.

Aquí hay un ejemplo de trabajo:

Prevenir múltiples eventos vinculantes / disparadores

jQuery Mobilefunciona de manera diferente a las aplicaciones web clásicas. Dependiendo de cómo logró vincular sus eventos cada vez que visita alguna página, los vinculará una y otra vez. Esto no es un error, es simplemente cómo jQuery Mobilemaneja sus páginas. Por ejemplo, eche un vistazo a este fragmento de código:

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

Ejemplo de trabajo de jsFiddle: http://jsfiddle.net/Gajotres/CCfL4/

Cada vez que visite la página #index click event estará vinculado al botón # test-button . Pruébelo moviéndose de la página 1 a la página 2 y viceversa varias veces. Hay pocas formas de prevenir este problema:

Solución 1

La mejor solución sería utilizar pageinitpara vincular eventos. Si echa un vistazo a una documentación oficial, descubrirá que pageinitse activará SOLO una vez, al igual que el documento listo, por lo que no hay forma de que los eventos se vuelvan a vincular nuevamente. Esta es la mejor solución porque no tiene gastos generales de procesamiento, como al eliminar eventos con el método off.

Ejemplo de trabajo de jsFiddle: http://jsfiddle.net/Gajotres/AAFH8/

Esta solución de trabajo se basa en un ejemplo problemático anterior.

Solución 2

Eliminar evento antes de vincularlo:

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

Ejemplo de trabajo de jsFiddle: http://jsfiddle.net/Gajotres/K8YmG/

Solución 3

Use un selector de filtro jQuery, como este:

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

Debido a que el filtro de eventos no es parte del marco oficial de jQuery, se puede encontrar aquí: http://www.codenothing.com/archives/2009/event-filter/

En pocas palabras, si la velocidad es su principal preocupación, entonces la Solución 2 es mucho mejor que la Solución 1.

Solución 4

Una nueva, probablemente la más fácil de todas.

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

Ejemplo de trabajo de jsFiddle: http://jsfiddle.net/Gajotres/Yerv9/

Gracias al sholsinger para esta solución: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/

peculiaridades del evento pageChange: se activa dos veces

A veces, el evento de cambio de página puede activarse dos veces y no tiene nada que ver con el problema mencionado anteriormente.

La razón por la cual el evento pagebeforechange ocurre dos veces se debe a la llamada recursiva en changePage cuando toPage no es un objeto DOM mejorado jQuery. Esta recursión es peligrosa, ya que el desarrollador puede cambiar el toPage dentro del evento. Si el desarrollador establece consistentemente toPage en una cadena, dentro del controlador de eventos pagebeforechange, independientemente de si se trata de un objeto o no, se producirá un bucle recursivo infinito. El evento de carga de página pasa la nueva página como la propiedad de la página del objeto de datos (Esto debe agregarse a la documentación, actualmente no figura en la lista). Por lo tanto, el evento de carga de página podría usarse para acceder a la página cargada.

En pocas palabras, esto está sucediendo porque está enviando parámetros adicionales a través de pageChange.

Ejemplo:

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

Para solucionar este problema, use cualquier evento de página que se encuentre en el orden de transición de eventos de página .

Tiempos de cambio de página

Como se mencionó, cuando cambia de una página de jQuery Mobile a otra, generalmente haciendo clic en un enlace a otra página de jQuery Mobile que ya existe en el DOM o llamando manualmente a $ .mobile.changePage, se producen varios eventos y acciones posteriores. En un nivel alto se producen las siguientes acciones:

  • Se inicia un proceso de cambio de página.
  • Se carga una nueva página.
  • El contenido de esa página está "mejorado" (con estilo)
  • Se produce una transición (diapositiva / pop / etc) de la página existente a la nueva página

Este es un punto de referencia de transición de página promedio:

Carga y procesamiento de la página: 3 ms

Mejora de página: 45 ms

Transición: 604 ms

Tiempo total: 670 ms

* Estos valores están en milisegundos.

Como puede ver, un evento de transición está consumiendo casi el 90% del tiempo de ejecución.

Manipulación de datos / parámetros entre transiciones de página

Es posible enviar un parámetro / s de una página a otra durante la transición de página. Se puede hacer de varias maneras.

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

Solución 1:

Puede pasar valores con changePage:

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

Y léelos así:

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

Ejemplo :

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>

Solución 2:

O puede crear un objeto JavaScript persistente para un propósito de almacenamiento. Mientras Ajax se use para cargar la página (y la página no se vuelve a cargar de ninguna manera), ese objeto permanecerá activo.

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

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

Solución 3:

También puede acceder a los datos de la página anterior de esta manera:

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

El objeto prevPage contiene una página anterior completa.

Solución 4:

Como última solución, tenemos una ingeniosa implementación HTML de localStorage. Solo funciona con navegadores HTML5 (incluidos los navegadores Android e iOS) pero todos los datos almacenados son persistentes a través de la actualización de la página.

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

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

Probablemente la mejor solución, pero fallará en algunas versiones de iOS 5.X. Es un error bien conocido.

No use .live()/ .bind()/.delegate()

Olvidé mencionar (y tnx andleer por recordarme) usar on / off para la vinculación / desvinculación de eventos, live / die y bind / unbind están en desuso.

El método .live () de jQuery fue visto como un regalo del cielo cuando se introdujo en la API en la versión 1.3. En una aplicación típica de jQuery puede haber mucha manipulación DOM y puede ser muy tedioso enganchar y desenganchar a medida que los elementos van y vienen. El .live()método permitió enganchar un evento para la vida de la aplicación en función de su selector. Genial verdad? Incorrecto, el .live()método es extremadamente lento. El .live()método en realidad conecta sus eventos al objeto del documento, lo que significa que el evento debe burbujear desde el elemento que generó el evento hasta que llegue al documento. Esto puede llevar mucho tiempo.

Ahora está en desuso. La gente del equipo de jQuery ya no recomienda su uso y yo tampoco. Aunque puede ser tedioso enganchar y desenganchar eventos, su código será mucho más rápido sin el .live()método que con él.

En lugar de .live()usted debe usar .on(). .on()es aproximadamente 2-3 veces más rápido que .live () . Eche un vistazo a este punto de referencia de enlace de eventos: http://jsperf.com/jquery-live-vs-delegate-vs-on/34 , todo estará claro a partir de ahí.

Benchmarking:

Hay un excelente script hecho para la evaluación comparativa de eventos de la página jQuery Mobile . Se puede encontrar aquí: https://github.com/jquery/jquery-mobile/blob/master/tools/page-change-time.js . Pero antes de hacer nada con él, le aconsejo que elimine su alertsistema de notificación (cada "página de cambio" le mostrará estos datos deteniendo la aplicación) y que cambie para que console.logfuncione.

Básicamente, este script registrará todos los eventos de su página y si lee este artículo detenidamente (descripciones de eventos de la página) sabrá cuánto tiempo pasó jQm de mejoras de la página, transiciones de la página ...

Notas finales

Siempre, y quiero decir, siempre leer la documentación oficial de jQuery Mobile . Por lo general, le proporcionará la información necesaria y, a diferencia de otra documentación, esta es bastante buena, con suficientes explicaciones y ejemplos de código.

Cambios:

  • 30.01.2013 - Se agregó un nuevo método de prevención de activación de eventos múltiples
  • 31.01.2013 - Se agregó una mejor aclaración para la manipulación de datos / parámetros del capítulo entre transiciones de página
  • 03.02.2013 - Se agregó nuevo contenido / ejemplos al capítulo Manipulación de datos / parámetros entre transiciones de página
  • 22.05.2013 - Se agregó una solución para la transición de página / prevención de cambios y se agregaron enlaces a la documentación oficial de la API de eventos de página
  • 18.05.2013 - Se agregó otra solución contra el enlace de eventos múltiples
Gajotres
fuente
2
$().live()se depreció en jQuery 1.7 y se eliminó en 1.9, por lo que realmente debería ser parte de cualquier solución de jQuery Mobile. La versión actual de núcleo mínimo para jQM 1.7.
andleer
16
+1 resumen muy útil de comportamientos críticos en torno a las cargas de página
RedFilter
2
pagecreateEl evento se activa solo una vez cuando se crea la página por primera vez. así que si vinculamos eventos de clic dentro pagecreate, no se disparará varias veces. Algo que descubrí mientras desarrollaba la aplicación. Pero no siempre podemos usar pagecreatepara vincular eventos, por lo que la solución que dio es la mejor. +1 dado
Jay Mayu
1
Tienes pageBeforeShow en la lista dos veces. Está listado como el número 5 y el número 8. ¿Se llama dos veces?
Chase Roberts
Eso fue un error tipográfico, lo arreglé, la página anterior se activará solo una vez. Gracias por notarlo.
Gajotres 01 de
17

Algunos de ustedes pueden encontrar esto útil. Simplemente copie y pegue en su página y obtendrá una secuencia en la que los eventos se activan en la consola de 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');});

No verá la descarga en la consola, ya que se dispara cuando se descarga la página (cuando se aleja de la página). Úselo así:

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

Y verás a qué me refiero.

Matas Vaitkevicius
fuente
4

Esta es la forma correcta:

Para ejecutar código que solo estará disponible para la página de índice, podríamos usar esta sintaxis:

$(document).on('pageinit', "#index",  function() {
    ...
});
kecco
fuente
10
la respuesta anterior dice lo mismo, ¿no te parece? :)
Omar
Gracias por la buena respuesta rápida y breve. :-)
SharpC
1

La diferencia simple entre el documento listo y el evento de página en jQuery-mobile es que:

  1. El evento de documento listo se usa para toda la página HTML,

    $(document).ready(function(e) {
        // Your code
    });
  2. Cuando hay un evento de página, utilícelo para manejar un evento de página en particular:

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

También puede usar el documento para manejar el evento pageinit:

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

});
LeoMobDev
fuente
-1

Mientras usa .on (), es básicamente una consulta en vivo la que está usando.

Por otro lado, .ready (como en su caso) es una consulta estática. Mientras lo usa, puede actualizar dinámicamente los datos y no tiene que esperar a que se cargue la página. Simplemente puede pasar los valores a su base de datos (si es necesario) cuando se ingresa un valor particular.

El uso de consultas en vivo es común en los formularios donde ingresamos datos (cuenta o publicaciones o incluso comentarios).

Pranjal
fuente