jQuery Mobile: document ready vs. page events
I am using jQuery Mobile, and I am having trouble understanding differences between classic document ready and jQuery Mobile page events.
What is the real difference?
Why should
<!-- language: lang-js -->
$(document).ready() {
});
be better than
$(document).on('pageinit') {
});
What is the order of page events, when you transition from one page to another?
How can I send data from one page to another and is it possible to access data from previous page?
jQuery Mobile 1.4 Update:
My original article was intended for old way of page handling, basically everything before jQuery Mobile 1.4. Old way of handling is now deprecated and it will stay active until (including) jQuery Mobile 1.5, so you can still use everything mentioned below, at least until next year and jQuery Mobile 1.6.
Old events, including pageinit don't exist any more, they are replaced with pagecontainer widget. Pageinit is erased completely and you can use pagecreate instead, that event stayed the same and its not going to be changed.
If you are interested in new way of page event handling take a look here , in any other case feel free to continue with this article. You should read this answer even if you are using jQuery Mobile 1.4 +, it goes beyond page events so you will probably find a lot of useful information.
Older content:
This article can also be found as a part of my blog HERE .
$(document).on('pageinit')
vs $(document).ready()
The first thing you learn in jQuery is to call code inside the $(document).ready()
function so everything will execute as soon as the DOM is loaded. However, in jQuery Mobile , Ajax is used to load the contents of each page into the DOM as you navigate. Because of this $(document).ready()
will trigger before your first page is loaded and every code intended for page manipulation will be executed after a page refresh. This can be a very subtle bug. On some systems it may appear that it works fine, but on others it may cause erratic, difficult to repeat weirdness to occur.
Classic jQuery syntax:
$(document).ready(function() {
});
To solve this problem (and trust me this is a problem) jQuery Mobile developers created page events. In a nutshell page events are events triggered in a particular point of page execution. One of those page events is a pageinit event and we can use it like this:
$(document).on('pageinit', function() {
});
We can go even further and use a page id instead of document selector. Let's say we have jQuery Mobile page with an id 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>
To execute code that will only available to the index page we could use this syntax:
$('#index').on('pageinit', function() {
});
Pageinit event will be executed every time page is about be be loaded and shown for the first time. It will not trigger again unless page is manually refreshed or Ajax page loading is turned off. In case you want code to execute every time you visit a page it is better to use pagebeforeshow event.
Here's a working example: http://jsfiddle.net/Gajotres/Q3Usv/ to demonstrate this problem.
Few more notes on this question. No matter if you are using 1 html multiple pages or multiple HTML files paradigm it is advised to separate all of your custom JavaScript page handling into a single separate JavaScript file. This will note make your code any better but you will have much better code overview, especially while creating a jQuery Mobile application.
There's also another special jQuery Mobile event and it is called mobileinit . When jQuery Mobile starts, it triggers a mobileinit event on the document object. To override default settings, bind them to mobileinit . One of a good examples of mobileinit usage is turning off Ajax page loading, or changing default Ajax loader behavior.
$(document).on("mobileinit", function(){
//apply overrides here
});
Page events transition order
First all events can be found here: http://api.jquerymobile.com/category/events/
Lets say we have a page A and a page B, this is a unload/load order:
page B - event pagebeforecreate
page B - event pagecreate
page B - event pageinit
page A - event pagebeforehide
page A - event pageremove
page A - event pagehide
page B - event pagebeforeshow
page B - event pageshow
For better page events understanding read this:
pagebeforeload
, pageload
and pageloadfailed
are fired when an external page is loaded pagebeforechange
, pagechange
and pagechangefailed
are page change events. These events are fired when a user is navigating between pages in the applications. pagebeforeshow
, pagebeforehide
, pageshow
and pagehide
are page transition events. These events are fired before, during and after a transition and are named. pagebeforecreate
, pagecreate
and pageinit
are for page initialization. pageremove
can be fired and then handled when a page is removed from the DOM Page loading jsFiddle example: http://jsfiddle.net/Gajotres/QGnft/
If AJAX is not enabled, some events may not fire.
Prevent page transition
If for some reason page transition needs to be prevented on some condition it can be done with this 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');;
}
}
});
This example will work in any case because it will trigger at a begging of every page transition and what is most important it will prevent page change before page transition can occur.
Here's a working example:
Prevent multiple event binding/triggering
jQuery Mobile
works in a different way than classic web applications. Depending on how you managed to bind your events each time you visit some page it will bind events over and over. This is not an error, it is simply how jQuery Mobile
handles its pages. For example, take a look at this code snippet:
$(document).on('pagebeforeshow','#index' ,function(e,data){
$(document).on('click', '#test-button',function(e) {
alert('Button click');
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/CCfL4/
Each time you visit page #index click event will is going to be bound to button #test-button . Test it by moving from page 1 to page 2 and back several times. There are few ways to prevent this problem:
Solution 1
Best solution would be to use pageinit
to bind events. If you take a look at an official documentation you will find out that pageinit
will trigger ONLY once, just like document ready, so there's no way events will be bound again. This is best solution because you don't have processing overhead like when removing events with off method.
Working jsFiddle example: http://jsfiddle.net/Gajotres/AAFH8/
This working solution is made on a basis of a previous problematic example.
Solution 2
Remove event before you bind it:
$(document).on('pagebeforeshow', '#index', function(){
$(document).off('click', '#test-button').on('click', '#test-button',function(e) {
alert('Button click');
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/K8YmG/
Solution 3
Use a jQuery Filter selector, like this:
$('#carousel div:Event(!click)').each(function(){
//If click is not bind to #carousel div do something
});
Because event filter is not a part of official jQuery framework it can be found here: http://www.codenothing.com/archives/2009/event-filter/
In a nutshell, if speed is your main concern then Solution 2 is much better than Solution 1.
Solution 4
A new one, probably an easiest of them all.
$(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;
}
});
});
Working jsFiddle example: http://jsfiddle.net/Gajotres/Yerv9/
Tnx to the sholsinger for this solution: http://sholsinger.com/archive/2011/08/prevent-jquery-live-handlers-from-firing-multiple-times/
pageChange event quirks - triggering twice
Sometimes pagechange event can trigger twice and it does not have anything to do with the problem mentioned before.
The reason the pagebeforechange event occurs twice is due to the recursive call in changePage when toPage is not a jQuery enhanced DOM object. This recursion is dangerous, as the developer is allowed to change the toPage within the event. If the developer consistently sets toPage to a string, within the pagebeforechange event handler, regardless of whether or not it was an object an infinite recursive loop will result. The pageload event passes the new page as the page property of the data object (This should be added to the documentation, it's not listed currently). The pageload event could therefore be used to access the loaded page.
In few words this is happening because you are sending additional parameters through pageChange.
Example:
<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>
To fix this problem use any page event listed in Page events transition order .
Page Change Times
As mentioned, when you change from one jQuery Mobile page to another, typically either through clicking on a link to another jQuery Mobile page that already exists in the DOM, or by manually calling $.mobile.changePage, several events and subsequent actions occur. At a high level the following actions occur:
This is a average page transition benchmark:
Page load and processing: 3 ms
Page enhance: 45 ms
Transition: 604 ms
Total time: 670 ms
*These values are in milliseconds.
So as you can see a transition event is eating almost 90% of execution time.
Data/Parameters manipulation between page transitions
It is possible to send a parameter/s from one page to another during page transition. It can be done in few ways.
Reference: https://stackoverflow.com/a/13932240/1848600
Solution 1:
You can pass values with changePage:
$.mobile.changePage('page2.html', { dataUrl : "page2.html?paremeter=123", data : { 'paremeter' : '123' }, reloadPage : true, changeHash : true });
And read them like this:
$(document).on('pagebeforeshow', "#index", function (event, data) {
var parameters = $(this).data("url").split("?")[1];;
parameter = parameters.replace("parameter=","");
alert(parameter);
});
Example:
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>
Some of you might find this useful. Just copy paste it to your page and you will get a sequence in which events are fired in the Chrome console (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');});
You are not going see unload in the console as it is fired when the page is being unloaded (when you move away from the page). Use it like this:
$(window).unload(function () { debugger; console.log("window unloaded");});
And you will see what I mean.
This is the correct way:
To execute code that will only be available to the index page, we could use this syntax:
$(document).on('pageinit', "#index", function() {
...
});
链接地址: http://www.djcxy.com/p/61930.html