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:
página B - página de evento antes de crear
página B - evento pagecreate
página B - evento pageinit
página A - página del evento antes
página A - página de eventosremove
página A - evento pagehide
página B - página del evento antes de mostrar
página B - evento páginashow
Para una mejor comprensión de los eventos de la página, lea esto:
pagebeforeload
, pageload
y pageloadfailed
se disparan cuando se carga una página externa
pagebeforechange
, pagechange
y pagechangefailed
son eventos de cambio de página. Estos eventos se activan cuando un usuario navega entre las páginas de las aplicaciones.
pagebeforeshow
, pagebeforehide
, pageshow
Y pagehide
son eventos de transición de página. Estos eventos se disparan antes, durante y después de una transición y se nombran.
pagebeforecreate
, pagecreate
y pageinit
son 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 Mobile
funciona 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 Mobile
maneja 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 pageinit
para vincular eventos. Si echa un vistazo a una documentación oficial, descubrirá que pageinit
se 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&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);
});
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 alert
sistema de notificación (cada "página de cambio" le mostrará estos datos deteniendo la aplicación) y que cambie para que console.log
funcione.
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