Working with jQuery Mobile Panels
Join the DZone community and get the full member experience.
Join For FreejQuery Mobile is an open-source framework that makes it easy for developers to build mobile-friendly websites and hybrid applications. Using (mostly) HTML, you can quickly create a single-page architecture with an easy-to-use page system and widgets for forms, popups and other UI elements. One of the newer widgets to enter the jQuery Mobile framework is the Panels widget. In this article, I'll explain how to use Panels and give you an example of how to create a panel with dynamic content.
It is my assumption that you already know a bit about how jQuery Mobile works. If you don't, then I highly recommend you check out their excellent Getting Started tutorial. Essentially, jQuery Mobile works with regular markup with a bit of flair added via HTML5 data-* attributes. These data attributes help jQuery Mobile define pages and other UI widgets. Here is a simple example jQuery Mobile application with two pages in it:
<!DOCTYPE html> <html> <head> <title></title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.css" /> <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script> </head> <body> <div data-role="page" id="home"> <div data-role="header"> <h1>Welcome to Foo</h1> </div> <div data-role="content"> <p> This is some simple content just to fill up some space. Nothing really exciting to see here. </p> <p> <a href="#second" data-role="button">Go to Second</a> </p> </div> <div data-role="footer"> <h4>Copyright NoOneImportant</h4> </div> </div> <div data-role="page" id="second"> <div data-role="header"> <a href="#home" data-icon="home" data-iconpos="notext">Home</a> <h1>Welcome to Foo</h1> </div> <div data-role="content"> <p> This is some other page. </p> </div> <div data-role="footer"> <h4>Copyright NoOneImportant</h4> </div> </div> </body> </html>
Even if this is your first time seeing jQuery Mobile, you can take a reasonable guess as to what is going on here. We've got two big blocks defined as data-role="page"
. Within each are headers, footers and main page contents. When jQuery Mobile loads this, it will automatically display the first page only and update your HTML to look nicer on a mobile device. Here's how it looks in iOS:
What isn't obvious from the screenshots are the nice transitions between each page. jQuery Mobile automatically detects links and converts them into Ajax-based GETs to help create a single-page architecture. Note: While I have two pages in the same HTML file above (and jQuery Mobile supports that) you absolutely do not—nor should you—build your entire application in one file. If I had linked to two.html and simply moved my page div into that file, the end result would have been exactly the same.
Send in the Panels
So, now that you've seen a basic example of jQuery Mobile, let's talk about the Panels widget. One of the ways that mobile designers have come up with to help handle the limited space on a mobile device is to use a side-loaded panel. Essentially, content that hides out on the left or right side of the viewport. When a particular user action is triggered (either a swipe or a button touch), the panel will slide out and reveal additional content.
jQuery Mobile 1.3.1 has support for all of the above. Let's look at a simple example. In the following code sample, I've added a new div to my page structure that uses a data-role of panel. Also note that I've given it an ID:
<div data-role="page" id="home"> <div data-role="panel" id="firstpanel"> This is my panel content. </div> <div data-role="header"> <h1>Welcome to Foo</h1> </div> <div data-role="content"> <p> This is some simple content just to fill up some space. Nothing really exciting to see here. </p> <p> <a href="#firstpanel" data-role="button">More Detail</a> </p> </div> <div data-role="footer"> <h4>Copyright NoOneImportant</h4> </div> </div>
When jQuery Mobile loads this page, it is going to notice that the div has been set to be a panel and automatically hide it. So how do we show it? Simple. The link inside my page directly points to the ID we used (firstpanel). Clicking (or touching) it will slide in the panel from the left.
How would you use a panel that came in from the right? Simple, add data-position to your div:
<div id="thePanel" data-role="panel" data-position="right">
And how about closing the panel? By default, you can close the panel by just clicking outside of it or by swiping. That may not be what you want though. You can disable either of those features by using additional data attributes. Use data-swipe-close to disable the "swipe to close" feature and data-dismissible to prevent a click/touch from hiding the panel. In the snippet below, both are disabled:
<div id="thePanel" data-role="panel" data-position="right" data-swipe-close="false" data-dismissible="false">
This, of course, raises the question: if you remove all the ways a user can dismiss a panel, how else can the user close it? If you want, you may simply add your own button in your panel that uses an attribute data-rel="close". Here is a simple panel that demonstrates this. (You can test it yourself in the .zip file attached to this article. The file is test2.html.)
<div data-role="panel" id="firstpanel" data-swipe-close="false" data-dismissible="false"> <h3>My Panel</h3> <p> This is my panel content. </p> <p> <a data-rel="close" data-role="button">Hide</a> </p> </div>
Finally, you can also control how a panel is displayed to the user when it comes out. This is referred to as the display mode and is specified with data-display. Your options are overlay, reveal, and push. Overlay does what you expect: It displays the panel over your content. Both reveal and push will nudge your content over, but reveal uses a 3D effect as if your panel had been hiding under the content. The default value is reveal.
Building a Dynamic Panel
So, now that you know the basics of how panels work with jQuery Mobile, let's look at a more advanced example. For our demo, we're going to build a panel that shows the current weather and forecast for a city. In theory we could build this so that users could enter their own location, but let's keep it simple. Our demo is for a local news station so we can simply use a hard-coded location.
For our weather data, we will use the excellent and free OpenWeatherMap service. This is a 100 percent free API that has a JSON/P interface.
First, let's just set up the template. In the sample below, you can see a new panel called weatherpanel. Also note that we've added a button to the header to display it. This snippet can be found in test3.html. There's no logic at all, yet—just the initial UI.
<div data-role="page" id="home"> <div data-role="panel" id="weatherpanel" data-theme="a"> <h3>Current Weather</h3> <p class="> </p> <p> <a data-rel="close" data-role="button" data-theme="a">Dismiss</a> </p> </div> <div data-role="header"> <a href="#weatherpanel" data-icon="gear" data-iconpos="notext">Weather</a> <h1>Welcome to KRKC</h1> </div> <div data-role="content"> <p> This is some simple content just to fill up some space. Nothing really exciting to see here. </p> </div> <div data-role="footer"> <h4>Copyright NoOneImportant</h4> </div> </div>
So now we need to actually make this work. I've added a new JavaScript file to my project. Let's look at it below.
var weatherloaded = false; var weathericonurl = "http://openweathermap.org/img/w/"; $(document).on("panelbeforeopen", "#weatherpanel", function(e, ui) { var panel = this; //Do we have our weather data? if(!weatherloaded) { $("p.weather", panel).html("<i>Fetching weather - please stand by!</i>"); $.get("http://api.openweathermap.org/data/2.5/weather?q=lafayette,la&units=imperial", function(res,code) { /* Generate a weather string. This could be nicer if it used something like Handlebars */ var s = ""; s += "<p>The current temperature is "+ Math.round(res.main.temp) + " degrees. Today's low will " + "be " + Math.round(res.main.temp_min) + " degrees with a high of " + Math.round(res.main.temp_max) + " degrees.</p>"; //weather is optional if(res.weather && res.weather.length >= 1) { s += "<p><img style='width:100px' src='" + weathericonurl + res.weather[0].icon + ".png'></p>"; } $("p.weather", panel).html(s); $(panel).trigger( "updatelayout" ); weatherloaded = true; }, "JSONP"); } });
Almost all of the snippet is the event handler for the panel. All jQuery Mobile widgets have various events you can listen to in your code. In our demo we want to listen to the panelbeforeopen event. As you can guess, this will run every time the panel is opened.
While the OpenWeatherMap API is easy to use, we really don't want to fetch the weather every time the panel is opened. Therefore we use a simple Boolean variable, weatherloaded, to signify that the weather has been loaded. If the weather has not yet been loaded, we make the API call. As we said above, we're using a hard-coded location (in this case my lovely hometown of Lafayette, LA). When the result is returned, we create an HTML string that can be inserted into the panel.
The jQuery Mobile docs recommend that you run the updatelayout event whenever you modify the contents of the panel, so we do that as well.
The result is a panel that will display the weather, but is also smart enough to only load the data when requested. If you don't ever open the panel, it skips making the call. Here's the result:
Where to Go Next
If you liked this and want to learn more about jQuery Mobile Panels, check out the widget reference. Also, you may want to consider the book “jQuery Mobile Web Development Essentials” written by myself and Andy Matthews. The book is currently being updated for the most recent version of jQuery Mobile (so honestly, you may want to wait!).
This article was commissioned by Intel and the Tizen Mobile Web Development team.
Opinions expressed by DZone contributors are their own.
Comments