Getting Started
Page Structure
jQuery Mobile uses data-role attributes to enhance HTML. A page is a div with data-role=page containing header, main, and footer sections. The viewport meta tag is essential for proper scaling on mobile devices. Load jQuery core before jquery.mobile.js.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
<div data-role="page" id="home">
<div data-role="header"><h1>Home</h1></div>
<div data-role="main" class="ui-content">Content</div>
<div data-role="footer"><h4>Footer</h4></div>
</div>
</body>
</html>Multiple Pages in One Document
You can place multiple data-role=page divs in a single HTML file. Link between them with anchor hrefs (#about). jQuery Mobile shows one page at a time and animates transitions. Only the first page in source order is shown on initial load.
<div data-role="page" id="home">
<div data-role="header"><h1>Home</h1></div>
<div data-role="main" class="ui-content">
<a href="#about" class="ui-btn">Go to About</a>
</div>
</div>
<div data-role="page" id="about" data-theme="b">
<div data-role="header"><h1>About</h1></div>
<div data-role="main" class="ui-content">
<a href="#home" class="ui-btn" data-direction="reverse">Back</a>
</div>
</div>AJAX Navigation & Links
By default, links to internal (#id) and same-origin pages are fetched via AJAX and swapped with a transition. Use data-ajax=false to force a normal full-page load (useful for non-jQuery Mobile pages). data-prefetch preloads the target so the transition is instant.
<!-- Default: links load via AJAX and animate a transition -->
<a href="detail.html" class="ui-btn">Open detail (AJAX)</a>
<!-- Force a full page reload (no AJAX) -->
<a href="external.html" data-ajax="false">Full reload</a>
<!-- Open in new window/tab -->
<a href="https://example.com" rel="external">External site</a>
<!-- Prefetch a page when this page loads -->
<a href="next.html" data-prefetch="true" class="ui-btn">Prefetch next</a>Prefetching & DOM Caching
Prefetching loads linked pages in the background so navigation feels instant. By default visited pages are removed from the DOM, but domCache keeps them for faster back-navigation at the cost of memory. Enable per-page with data-dom-cache or globally via the page prototype option.
<!-- Prefetch several pages -->
<a href="page1.html" data-prefetch="true">Page 1</a>
<a href="page2.html" data-prefetch="true">Page 2</a>
<!-- Globally enable DOM caching of visited pages -->
<script>
$(document).on("mobileinit", function () {
$.mobile.page.prototype.options.domCache = true;
});
</script>
<!-- Cache a single page -->
<div data-role="page" id="cached" data-dom-cache="true">...</div>Dialog Pages
Add data-rel=dialog to a link (or data-dialog=true on the page) to open a page styled as a modal dialog with a close button and transition. Use data-rel=back on a link to close it and return to the previous page.
<!-- Open a page as a dialog -->
<a href="#dialog" class="ui-btn" data-rel="dialog">Open dialog</a>
<div data-role="page" id="dialog" data-dialog="true">
<div data-role="header"><h1>Dialog</h1></div>
<div data-role="main" class="ui-content">
<p>This page is styled as a dialog.</p>
<a href="#" class="ui-btn" data-rel="back">Close</a>
</div>
</div>Global Configuration (mobileinit)
The mobileinit event fires after jQuery core loads but before jQuery Mobile initializes — bind your config here, before including jquery.mobile.js. Common overrides: default transitions, AJAX settings, and loading message text. Setting autoInitializePage=false lets you control when the first page initializes.
<script src="jquery-1.11.1.min.js"></script>
<script>
// Bind BEFORE loading jquery.mobile.js
$(document).on("mobileinit", function () {
$.mobile.defaultPageTransition = "slide";
$.mobile.defaultDialogTransition = "pop";
$.mobile.loadingMessage = "Loading...";
$.mobile.ajaxEnabled = true;
$.mobile.linkBindingEnabled = true;
$.mobile.autoInitializePage = true;
});
</script>
<script src="jquery.mobile-1.4.5.min.js"></script>Page Transitions
Basic Transitions
Add data-transition to a link to choose the animation when navigating. Transitions are CSS-based (hardware-accelerated where possible). 'none' skips animation for instant navigation. The same attribute works for dialogs and popups.
<!-- Apply a transition to a link -->
<a href="#page2" data-transition="slide">Slide</a>
<a href="#page2" data-transition="fade">Fade</a>
<a href="#page2" data-transition="pop">Pop</a>
<a href="#page2" data-transition="flip">Flip</a>
<a href="#page2" data-transition="none">None (instant)</a>Transition Types
jQuery Mobile ships with a set of 2D/3D CSS transitions. 3D transitions (turn, flow, flip) look great on capable devices but fall back gracefully on older hardware. pop and fade are the most performant and are recommended for dialogs and popups.
<!-- Available transitions in jQuery Mobile 1.4 -->
fade <!-- default, fade in/out -->
pop <!-- scale in from center (good for dialogs) -->
slide <!-- slide left to right -->
slideup <!-- slide up from bottom -->
slidedown <!-- slide down from top -->
slidfade <!-- slide + fade -->
turn <!-- 3D turn -->
flow <!-- 3D flow -->
flip <!-- 3D flip -->
none <!-- no animation -->Direction & Reverse
data-direction=reverse plays the transition backwards — typically used on Back buttons so the animation mirrors the forward navigation. This pairs with data-transition; if no transition is set, the default is reversed.
<!-- Reverse a transition (e.g., for Back buttons) -->
<a href="#home" data-transition="slide" data-direction="reverse">Back</a>
<!-- Globally reverse the default -->
<a href="#home" data-direction="reverse">Back (uses default transition reversed)</a>Dialog Transitions
Dialogs can use any transition, but pop and slidedown feel most natural for modals. Set a global default with $.mobile.defaultDialogTransition during mobileinit. The dialog's close action reverses the transition automatically.
<!-- Combine data-rel=dialog with a transition -->
<a href="#settings" data-rel="dialog" data-transition="pop">Settings</a>
<a href="#settings" data-rel="dialog" data-transition="slidedown">Settings (slide down)</a>
<!-- Set a global default dialog transition -->
<script>
$(document).on("mobileinit", function () {
$.mobile.defaultDialogTransition = "pop";
});
</script>Disabling & Fallback
Set data-transition=none or the global default to 'none' to disable animations — helpful on low-end devices or for accessibility. jQuery Mobile automatically detects 3D transform support and degrades 3D transitions (flip, turn, flow) to a simple fade when unsupported.
<!-- Disable transitions for a specific link -->
<a href="#page2" data-transition="none">No animation</a>
<!-- Globally disable (useful for older devices / testing) -->
<script>
$(document).on("mobileinit", function () {
$.mobile.defaultPageTransition = "none";
$.mobile.defaultDialogTransition = "none";
});
</script>
<!-- 3D transitions fall back to fade when not supported -->Toolbars (Header & Footer)
Fixed Header & Footer
Add data-position=fixed to keep a header or footer pinned at the top/bottom of the viewport while the page scrolls. Tapping the page toggles the toolbars' visibility by default. The page content gets padding so it isn't hidden behind the toolbars.
<div data-role="page" id="home">
<div data-role="header" data-position="fixed">
<h1>Fixed Header</h1>
</div>
<div data-role="main" class="ui-content">
<p>Scroll the page — the toolbars stay in place.</p>
</div>
<div data-role="footer" data-position="fixed">
<h4>Fixed Footer</h4>
</div>
</div>Fullscreen Toolbars
data-fullscreen=true overlays the fixed toolbars on top of the content (semi-transparent), instead of reserving space. Great for photo viewers or maps where content should fill the screen. The toolbars appear/disappear on tap just like fixed toolbars.
<div data-role="page" id="photo">
<div data-role="header" data-position="fixed" data-fullscreen="true">
<h1>Photos</h1>
</div>
<div data-role="main" class="ui-content">
<img src="photo.jpg" style="width:100%">
</div>
<div data-role="footer" data-position="fixed" data-fullscreen="true">
<h4>Footer over content</h4>
</div>
</div>Persistent Toolbars
When two pages have footers (or headers) with the same data-id and data-position=fixed, the toolbar persists across page transitions instead of animating with the page. This is the standard pattern for a persistent bottom navigation bar — only the active link changes per page.
<!-- Both pages use the SAME data-id so the footer persists -->
<div data-role="page" id="home">
<div data-role="footer" data-id="main-nav" data-position="fixed">
<div data-role="navbar">
<ul><li><a href="#home" class="ui-btn-active">Home</a></li>
<li><a href="#settings">Settings</a></li></ul>
</div>
</div>
</div>
<div data-role="page" id="settings">
<div data-role="footer" data-id="main-nav" data-position="fixed">
<div data-role="navbar">
<ul><li><a href="#home">Home</a></li>
<li><a href="#settings" class="ui-btn-active">Settings</a></li></ul>
</div>
</div>
</div>Tap Toggle Behavior
Fixed toolbars toggle visibility when the user taps the page. Set data-tap-toggle=false to keep a toolbar always visible. Related per-element options: data-hide-during-focus (hide when inputs are focused) and data-update-page-padding (re-apply page padding on resize).
<!-- Disable tap-to-toggle on a fixed toolbar -->
<div data-role="header" data-position="fixed" data-tap-toggle="false">
<h1>Always visible</h1>
</div>
<!-- Globally turn off tap toggle -->
<script>
$(document).on("mobileinit", function () {
$.mobile.toolbar.prototype.options.tapToggle = false;
});
</script>Toolbar Theme & Buttons
Headers can hold buttons positioned with the ui-btn-left / ui-btn-right classes. data-theme sets the color swatch of the bar. data-iconpos=notext shows only an icon for a compact look. Buttons inside a header are auto-styled as inline buttons.
<div data-role="header" data-theme="b">
<a href="#" class="ui-btn-left" data-icon="home" data-iconpos="notext">Home</a>
<h1>Title</h1>
<a href="#" class="ui-btn-right" data-icon="gear" data-iconpos="notext">Settings</a>
</div>Dynamically Updating Toolbars
After changing toolbar markup at runtime, call $(el).toolbar('refresh') to re-apply styling and padding. The toolbar widget (introduced in 1.4) manages fixed positioning and tap-toggle; initialize it manually with $().toolbar() if you add a toolbar after page create.
<script>
// Update the header title and re-enhance after DOM change
$(function () {
$("#my-header h1").text("New Title");
$("#my-header").toolbar("refresh");
});
</script>
<div data-role="header" id="my-header" data-position="fixed">
<h1>Old Title</h1>
</div>List Views
Basic Listview
Add data-role=listview to a <ul> to turn it into a touch-friendly list with full-width tappable rows. Each <li> containing an <a> becomes a linked row with a right arrow. data-inset=true insets the list with rounded corners and margins instead of edge-to-edge.
<ul data-role="listview" data-inset="true">
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
<li><a href="#">Cherry</a></li>
</ul>Inset Lists
Edge-to-edge lists span the full width (good for full-screen menus); inset lists have rounded corners and surrounding margins (good for content sections within a page). Use data-inset=true for inset lists, which look more like grouped settings panels.
<!-- Edge-to-edge (default) -->
<ul data-role="listview">
<li><a href="#">Item 1</a></li>
</ul>
<!-- Inset: rounded, with margins -->
<ul data-role="listview" data-inset="true">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
</ul>List Dividers
An <li> with data-role=list-divider becomes a non-clickable section header. data-autodividers=true automatically inserts dividers based on the first letter of each item's text — perfect for an alphabetical contact list.
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">Fruits</li>
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
<li data-role="list-divider">Vegetables</li>
<li><a href="#">Carrot</a></li>
</ul>
<!-- Auto dividers from first letter -->
<ul data-role="listview" data-autodividers="true">
<li><a href="#">Alice</a></li>
<li><a href="#">Bob</a></li>
</ul>Count Bubbles & Thumbnails
Add a span with class=ui-li-count inside an <li> to show a numeric count bubble on the right. Use an <img> with class=ui-li-thumb (typically 80x80) for a left thumbnail. Combine with <h3>/<p> for rich, app-like list rows.
<ul data-role="listview" data-inset="true">
<!-- Count bubble -->
<li><a href="#">Inbox <span class="ui-li-count">12</span></a></li>
<!-- Thumbnail on the left -->
<li>
<a href="#">
<img src="thumb.jpg" class="ui-li-thumb">
<h3>Title</h3>
<p>Description</p>
<span class="ui-li-count">3</span>
</a>
</li>
</ul>Search Filter
data-filter=true adds a search input above the list that filters items by text in real time. data-filter-placeholder customizes the prompt. data-filter-reveal=true hides all items until the user types — useful for an autocomplete-style lookup.
<ul data-role="listview" data-filter="true"
data-filter-placeholder="Search fruits...">
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
<li><a href="#">Cherry</a></li>
</ul>
<!-- Filter reveals hidden items (collapsible search) -->
<ul data-role="listview" data-filter="true" data-filter-reveal="true">
<li><a href="#">Hidden until searched</a></li>
</ul>Split Buttons & Nested Lists
When an <li> contains two <a> elements, jQuery Mobile renders a split button: the main row navigates to the first link, and a separate icon button (set via data-split-icon) on the right opens the second. A nested <ul> inside an <li> becomes a drill-down sub-page automatically.
<!-- Split button: row link + icon link on the right -->
<ul data-role="listview" data-split-icon="gear" data-inset="true">
<li>
<a href="#detail">Go to detail</a>
<a href="#edit">Edit</a>
</li>
</ul>
<!-- Nested list (child <ul> becomes a sub-page) -->
<ul data-role="listview">
<li>Fruits
<ul>
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
</ul>
</li>
</ul>Form Controls
Form Structure & AJAX Submission
jQuery Mobile auto-enhances form inputs and submits forms via AJAX by default, showing a loading spinner and transitioning to the result page. Set data-ajax=false to do a traditional full-page submit (required for file uploads that don't use a modern API).
<form action="/submit" method="post" data-ajax="true">
<label for="name">Name:</label>
<input type="text" name="name" id="name" placeholder="Your name">
<button type="submit" class="ui-btn">Submit</button>
</form>
<!-- Disable AJAX for this form (normal full submit) -->
<form action="/upload" method="post" data-ajax="false">...</form>Text Inputs & Clear Button
All HTML5 input types (text, email, tel, number, password, date, etc.) are styled consistently. data-clear-btn=true adds an in-field clear (x) button. Use data-role=none to keep a control as a plain native input without jQuery Mobile styling.
<label for="email">Email</label>
<input type="email" id="email" data-clear-btn="true" placeholder="[email protected]">
<label for="phone">Phone</label>
<input type="tel" id="phone" data-clear-btn="true" data-clear-btn-text="Clear">
<!-- Non-enhanced native input -->
<input type="text" data-role="none">Field Containers
Wrap a label + control pair in a div with class=ui-field-contain. On wide screens the label and input sit side by side; on narrow phones they stack vertically. This responsive behavior replaces the older data-role=fieldcontain attribute.
<div class="ui-field-contain">
<label for="user">Username</label>
<input type="text" id="user">
</div>
<div class="ui-field-contain">
<label for="pwd">Password</label>
<input type="password" id="pwd">
</div>Search Input
An <input type=search> is enhanced into a search field with a magnifying-glass icon. Pair it with a listview by giving the input an id and setting data-input=#id on the listview's filter — this lets you place the search box anywhere on the page.
<label for="q">Search</label>
<input type="search" id="q" placeholder="Search...">
<!-- Search input feeding a listview filter -->
<input type="search" id="my-filter" data-type="search">
<ul data-role="listview" data-filter="true"
data-input="#my-filter">
<li><a href="#">Apple</a></li>
<li><a href="#">Banana</a></li>
</ul>Textarea & Hidden Inputs
Textareas are styled to match other inputs and auto-grow as you type by default; set data-autogrow=false to fix the height with a scrollbar instead. Hidden inputs are passed through untouched. Like all inputs, wrap them in ui-field-contain for responsive labels.
<label for="bio">Bio</label>
<textarea id="bio" placeholder="Tell us about you"></textarea>
<!-- Auto-grow textarea -->
<textarea id="bio" data-autogrow="false"></textarea>
<!-- Hidden inputs are not styled -->
<input type="hidden" name="id" value="42">Disabling Auto-enhancement
Add data-role=none to any control to skip jQuery Mobile enhancement and keep it native. For a project-wide rule, set keepNative during mobileinit to a selector string of elements that should never be enhanced — useful when mixing custom-styled controls with jQM pages.
<!-- Keep a control native (no jQM styling) -->
<select data-role="none">...</select>
<input type="checkbox" data-role="none">
<!-- Globally disable enhancement for a tag -->
<script>
$(document).on("mobileinit", function () {
$.mobile.page.prototype.options.keepNative = "select, input[type=checkbox]";
});
</script>Sliders
Basic Range Slider
An <input type=range> with min, max, value, and step is enhanced into a touch-friendly slider with a value bubble. The value attribute sets the initial position. The slider writes its value back to the input, so it submits normally with the form.
<label for="volume">Volume</label>
<input type="range" name="volume" id="volume"
value="50" min="0" max="100" step="1">Slider with Highlight
data-highlight=true fills the track to the left of the handle with the active theme color, giving a clear visual of the current level. Without it the track is a single flat color. Useful for settings like volume or brightness.
<label for="brightness">Brightness</label>
<input type="range" id="brightness"
value="70" min="0" max="100"
data-highlight="true">Mini Slider
data-mini=true renders a smaller, more compact slider that takes less vertical space — handy when grouping several controls in a settings panel. It behaves identically to the regular slider, just at a reduced size.
<label for="age">Age</label>
<input type="range" id="age" value="25" min="0" max="120"
data-mini="true" data-highlight="true">Step & Multiple Sliders
step controls the increment (e.g., step=5 snaps to 0, 5, 10, ...). Each range input is an independent slider; for a dual-handle range you must build a custom widget or use a third-party plugin. The native value, min, max attributes drive the behavior.
<label for="temp">Temperature (step 5)</label>
<input type="range" id="temp" value="20" min="0" max="100" step="5">
<!-- Two independent sliders -->
<label for="low">Min</label>
<input type="range" id="low" value="10" min="0" max="100">
<label for="high">Max</label>
<input type="range" id="high" value="90" min="0" max="100">Slider Events
The slider emits slidestart when the user begins dragging the handle and slidestop when the drag ends. For continuous updates during the drag, bind to the native 'input' or 'change' event on the underlying range input. Read/write the value with $(el).val().
<label for="vol">Volume</label>
<input type="range" id="vol" value="50" min="0" max="100">
<script>
$("#vol").on("slidestart", function () {
console.log("User started dragging");
});
$("#vol").on("slidestop", function () {
console.log("Value is now " + $(this).val());
});
</script>Flip Toggle Switches
Basic Flip Toggle
A flip switch is a <select> with exactly two <option> elements and data-role=slider. It renders as an iOS-style on/off toggle. The selected option determines the initial state, and the value submits like a normal select (the value of the chosen option).
<label for="notify">Notifications</label>
<select name="notify" id="notify" data-role="slider">
<option value="off">Off</option>
<option value="on" selected>On</option>
</select>Custom Labels
The text of the two options becomes the labels on each side of the switch. You can use any short text (On/Off, Yes/No, Enabled/Disabled). Keep labels short so they fit within the toggle without truncation on small screens.
<label for="wifi">Wi-Fi</label>
<select id="wifi" data-role="slider">
<option value="disabled">Disabled</option>
<option value="enabled" selected>Enabled</option>
</select>Mini Flip Switch
data-mini=true renders a smaller flip switch that takes less vertical space, matching mini sliders and other mini controls. Pair mini controls together in dense settings panels for a consistent compact look.
<label for="sync">Auto-sync</label>
<select id="sync" data-role="slider" data-mini="true">
<option value="no">No</option>
<option value="yes" selected>Yes</option>
</select>Flip Theme
data-theme sets the color of the switch handle, and data-track-theme sets the color of the track behind it. Mixing swatches (e.g., a dark handle on a light track) makes the active state more visually distinct.
<label for="darkmode">Dark mode</label>
<select id="darkmode" data-role="slider"
data-theme="b" data-track-theme="a">
<option value="off">Off</option>
<option value="on" selected>On</option>
</select>Programmatic Toggle
Set the value with $(el).val(value) then call .slider('refresh') to update the visual toggle to match. Read the current state with .val(). Always refresh after changing the underlying select's value programmatically, or the handle won't move.
<select id="power" data-role="slider">
<option value="off">Off</option>
<option value="on">On</option>
</select>
<script>
// Turn the switch on programmatically
$("#power").val("on").slider("refresh");
// Read current state
var state = $("#power").val(); // "on" or "off"
</script>Checkboxes & Radios
Checkboxes
A checkbox is a normal <input type=checkbox> wrapped by (or adjacent to) a <label for=id>. jQuery Mobile styles the label as a tappable block. When the label wraps the input, the for attribute is optional. Checked state submits as usual with the form.
<label for="agree"><input type="checkbox" id="agree" name="agree"> I agree</label>
<label for="news"><input type="checkbox" id="news" name="news" checked> Newsletter</label>
<!-- Wrap in ui-field-contain for layout -->
<div class="ui-field-contain">
<label for="agree">I agree to terms</label>
<input type="checkbox" id="agree">
</div>Radio Buttons
Group radios by giving them the same name attribute. Wrap the group in a fieldset with data-role=controlgroup (and a <legend>) to cluster them visually with shared corners. Only one radio in a same-named group can be checked at a time.