Scripting pages

Home Search

Scripting pages in jQuery Mobile

Since jQuery Mobile uses an Ajax-powered navigation system, there are a few helpful things to know when writing scripts that manipulate your content. You can explore the mobile API in more detail by reading up on global configuration options, events, and methods or dig into the technical details of the Ajax navigation model.

Scripts & styles in the head

When the user clicks a link in a jQuery Mobile-driven site, the default behavior of the navigation system is to use that link's href to formulate an Ajax request (instead of allowing the browser's default link behavior of requesting that href with full page load). When that Ajax request goes out, the framework will receive its entire text content, but it will only inject the contents of the response's body element (or more specifically the data-role="page" element, if it's provided), meaning nothing in the head of the page will be used (with the exception of the page title, which is fetched specifically). Please note that scripts loaded dynamically in this fashion do not guarantee a load order in the same way they would if the page was loaded via a normal http request.

This means that any scripts and styles referenced in the head of a page won't have any effect when a page is loaded via Ajax, but they will execute if the page is requested normally via HTTP. When scripting jQuery Mobile sites, both scenarios need to be considered. The reason that the head of a page is ignored when requested via Ajax is that the potential of re-executing the same JavaScript is very high (it's common to reference the same scripts in every page of a site). Due to the complexity of attempting to work around that issue, we leave the task of executing page-specific scripts to the developer, and assume head scripts are only expected to execute once per browsing session.

The simplest approach when building a jQuery Mobile site is to reference the same set of stylesheets and scripts in the head of every page. If you need to load in specific scripts or styles for a particular page, we recommend binding logic to the pageinit event (details below) to run necessary code when a specific page is created (which can be determined by its id attribute, or a number of other ways). Following this approach will ensure that the code executes if the page is loaded directly or is pulled in and shown via Ajax.

Another approach for page-specific scripting would be to include scripts at the end of the body element when no data-role=page element is defined, or inside the first data-role=page element. If you include your custom scripting this way, be aware that these scripts will execute when that page is loaded via Ajax or regular HTTP, so if these scripts are the same on every page, you'll likely run into problems. If you're including scripts this way, we'd recommend enclosing your page content in a data-role="page" element, and placing scripts that are referenced on every page outside of that element. Scripts that are unique to that page can be placed in that element, to ensure that they execute when the page is fetched via Ajax.

pageinit = DOM ready

One of the first things people learn in jQuery is to use the $(document).ready() function for executing DOM-specific code as soon as the DOM is ready (which often occurs long before the onload event). However, in jQuery Mobile site and apps, pages are requested and injected into the same DOM as the user navigates, so the DOM ready event is not as useful, as it only executes for the first page. To execute code whenever a new page is loaded and created in jQuery Mobile, you can bind to the pageinit event.

The pageinit event is triggered on a page when it is initialized, right after initialization occurs. Most of jQuery Mobile's official widgets auto-initialize themselves based on this event, and you can set up your code to do the same.


$( document ).delegate("#aboutPage", "pageinit", function() {
  alert('A page with an id of "aboutPage" was just created by jQuery Mobile!');
});

If you'd like to manipulate a page's contents before the pageinit event fires and widgets are auto-initialized, you can instead bind to the pagebeforecreate event:


$( document ).delegate("#aboutPage", "pagebeforecreate", function() {
  alert('A page with an id of "aboutPage" is about to be created by jQuery Mobile!');
});

Important note: pageCreate() vs pageInit()

Prior to Beta 2 the recommendation to users wishing to manipulate jQuery Mobile enhanced page and child widget markup was to bind to the pagecreate event. In Beta 2 an internal change was made to decouple each of the widgets by binding to the pagecreate event in place of direct calls to the widget methods. As a result, users binding to the pagecreate in mobileinit would find their binding executing before the markup had been enhanced by each of the plugins. In keeping with the lifecycle of the jQuery UI Widget Factory, the initialization method is invoked after the create method, so the pageinit event provides the correct timing for post enhancement manipulation of the DOM and/or Javascript objects. In short, if you were previously using pagecreate to manipulate the enhanced markup before the page was shown, it's very likely you'll want to migrate to 'pageinit'.

Changing pages

If you want to change the current active page with JavaScript, you can use the changePage method. There are a lot of methods and properties that you can set when changing pages, but here are two simple examples:


//transition to the "about us" page with a slideup transition
$.mobile.changePage( "about/us.html", { transition: "slideup"} );

//transition to the "search results" page, using data from a form with an id of "search"
$.mobile.changePage( "searchresults.php", {
	type: "post",
	data: $("form#search").serialize()
});

Loading pages

To load an external page, enhance its content, and insert it into the DOM, use the loadPage method. There are a lot of methods and properties that you can set when loading pages, but here is a simple example:


//load the "about us" page into the DOM
$.mobile.loadPage( "about/us.html" );

Enhancing new markup

The page plugin dispatches a pageInit event, which most widgets use to auto-initialize themselves. As long as a widget plugin script is referenced, it will automatically enhance any instances of the widgets it finds on the page.

However, if you generate new markup client-side or load in content via Ajax and inject it into a page, you can trigger the create event to handle the auto-initialization for all the plugins contained within the new markup. This can be triggered on any element (even the page div itself), saving you the task of manually initializing each plugin (listview button, select, etc.).

For example, if a block of HTML markup (say a login form) was loaded in through Ajax, trigger the create event to automatically transform all the widgets it contains (inputs and buttons in this case) into the enhanced versions. The code for this scenario would be:

$( ...new markup that contains widgets... ).appendTo( ".ui-page" ).trigger( "create" );

Create vs. refresh: An important distinction

Note that there is an important difference between the create event and refresh method that some widgets have. The create event is suited for enhancing raw markup that contains one or more widgets. The refresh method should be used on existing (already enhanced) widgets that have been manipulated programmatically and need the UI be updated to match.

For example, if you had a page where you dynamically appended a new unordered list with data-role=listview attribute after page creation, triggering create on a parent element of that list would transform it into a listview styled widget. If more list items were then programmatically added, calling the listview’s refresh method would update just those new list items to the enhanced state and leave the existing list items untouched.

data-enhance="false" data attribute

As of jQuery Mobile 1.0, all the markup within a page is scanned for elements to be enhanced. This is problematic for 3rd party widgets/libraries that don't want anybody enhancing their markup or attaching behavior. We heard requests for a data-* attribute that can be placed on an element container to tell the framework not to enhance anything inside it for these situations. In 1.1, we've added a new data-enhance="false" attribute that can be added to a container to prevent auto-initialization. This is also available via $.fn.jqmEnhanceable. It's important to note that because of the performance impact incurred by finding a parent element with the data-enhance="false" attribute this feature must be turned on explicitly with $.mobile.ignoreContentEnabled=true.

data-ajax="false" now works on containers

On a related topic, we've always offered the ability to disable the AJAX navigation system from hijacking a link or form submit via the data-ajax="false" attribute, but people have asked for a way to apply this exclusion more efficiently to a grouping of links. In 1.1, this is now possible by simply adding, and setting $.mobile.ignoreContentEnabled=true, the data-ajax attribute to a parent container and it will exclude all the child links or forms from the AJAX navigation system behavior.

Scrolling to a position within a page

Since we use the URL hash to preserve Back button behavior, using page anchors to jump down to a position on the page isn't supported by using the traditional anchor link (#foo). Use the silentScroll method to scroll to a particular Y position without triggering scroll event listeners. You can pass in a yPos argument to scroll to that Y location. For example:


//scroll to Y 300px
$.mobile.silentScroll(300);

Binding to mouse and touch events

One important consideration in mobile is handling mouse and touch events. These events differ significantly across mobile platforms, but the common denominator is that click events will work everywhere, but usually after a significant delay of 500-700ms. This delay is necessary for the browser to wait for double tap, scroll and extended hold tap events to potentially occur. To avoid this delay, it's possible to bind to touch events (ex. touchstart) but the issue with this approach is that some mobile platforms (WP7, Blackberry) don't support touch. To compound this issue, some platforms will emit both touch and mouse events so if you bind to both types, duplicate events will be fired for a single interaction.

Our solution is to create a set of virtual events that normalize mouse and touch events. This allows the developer to register listeners for the basic mouse events, such as mousedown, mousemove, mouseup, and click, and the plugin will take care of registering the correct listeners behind the scenes to invoke the listener at the fastest possible time for that device. This still retains the order of event firing in the traditional mouse environment, should multiple handlers be registered on the same element for different events. The virtual mouse system exposes the following virtual events to jQuery bind methods: vmouseover, vmousedown, vmousemove, vmouseup, vclick, and vmousecancel.

Passing parameters between pages

jQuery Mobile does not support query parameter passing to internal/embedded pages. For example, if the framework sees a link to "#somePage?someId=1" it interprets that as "#somePage" and navigates to the internal page div with an id of "somePage" and applies a data-url of "#somePage?someId=1" to that page container. Subsequent calls to other params such as "#somePage?someId=2" will find the same div because jQuery Mobile refers to the data-url on the div which is only set once and will remain at "#somePage?someId=1".

There are two plugins that can be added to your project if query parameters are needed between pages. There is a lightweight page params plugin and a more fully featured jQuery Mobile router plugin for use with backbone.js or spine.js.