Adding Pages Dynamically within a jQuery Mobile Project in PhoneGap

Share

Code’s towards the bottom if you can’t be buggered to read my ramblings.

The Challenge: Add new “pages” (page divs) during execution in a mobile app. This was part of a project using jQuery Mobile and PhoneGap, and the information that I wanted to display wouldn’t be available until the app ran and was subject to change. In this case, the app lists a number of companies, and each was supposed to have their own “page” within the app. Tap the company name, see the company info. Simple, right?

The Reason: The app I was working on had a basic layout that could be coded beforehand, but much of the data would be unknown until the app actually ran and pulled that data from a server. Since the various companies that the app listed could change at any time, this needed to be dynamic. While the approval time for apps on mobile devices is enough of a burden (up to three weeks or more), I had the additional hurdle that an update for every new company might degrade user confidence. Yes, updating your apps too frequently can erode confidence, because the users start wondering what, exactly, you’re screwing up so bad that you need to patch it every week or two.

The Failed Approach: On an initial build of the app, I used a simple method to accomplish this: update a single page. Yep. When the user clicked the button to see a particular company, the app would have to dig through it’s little database, pull out the company name, logo, and other information, then shove it into containers for each piece of info. Sounds great. Well, it worked, but it was slow. Local storage on a device can be quite slow, especially when it comes to images (the logo, in this case). The speed gets worse if you’re pulling the images from the web. It was slow enough that users would sometimes see the logo of another company before the app had a chance to update the container with the new image. Nasty.

The Goal: Make a “page” (a page div) for each company as soon as the data comes in. Modern browsers, including the browser implementation within a PhoneGap project, lives and dies by the DOM. If you’re not familiar with it, long story short is that it is the “living” version of web pages and web data that your browser actually interacts with. My goal, then, was to find a good, reliable way to make new pages inside the DOM, as the data came in.

The Solution: I tried a lot of things that did not work. This included trying to create new page divs and append them to the page’s body element. No dice. By the way, as the title says, this was for a jQuery Mobile project on PhoneGap. PhoneHap is not necessary for this to work, but jQuery Mobile and jQuery are. Here’s the one method that did work:

1) Create a (just one) static “template” page in your html document. It can be as simple or as in-depth as you want. It can be empty, or you can structure the whole thing the way the rest of the pages will look. In my case, I could create a header area for the company name, an area for the logo, a text box for additional information about the company, and another text box for the phone number. Whatever you like. The below example is a simple one, but it doesn’t have to be. Important: assign the template div an id so it will be easily referenced later in code.

<!–// BASIC COMPANY VIEW PAGE //–>
<div data-role=”page” id=”page_basic_company”>

<div data-role=”header”> <h1></h1> </div>
<div data-role=”content”> </div>

</div>
<!–// END BASIC COMPANY VIEW PAGE//–>

2) Clone the template div, change its id  and data-url values to the same thing, and then attach it to the body. Yes, all in one step. For editing simplicity, I’ve also assigned the new div to a jQuery variable. By the way, the [a] below is because I was iterating through an array of companies (called company_list). The array came in as JSON via AJAX, and here we go.

var $new_company_page = $(‘#page_basic_company’).clone().attr(‘data-url’, company_list[a].business_id).attr(‘id’, company_list[a].business_id).appendTo(‘body’);

3) Use the jQuery variable to reference the new page div and go to town. Add content. This can be anything from completely replacing the contents with new html to picking specific children elements to modify.

$new_company_page.children(‘.phone_box’).text(‘555-555-5555’);

 

Hope this helps. If anyone figures out a better way, please do let me know. Enjoy.

 

Share

12 thoughts on “Adding Pages Dynamically within a jQuery Mobile Project in PhoneGap

  1. Thanks! This is really useful.

    Quick question: does this method get jQuery Mobile to dynamically enhance the new content? That’s been (one of) my big challenges with dynamic pages with jQm. I wrote a patch to jQuery mobile to let you enhance content on demand, but if there’s a way to work within the existing system that might be better.

  2. So given a company list of 10, you’d have a page with 10 distinct page divs contained in it?

    How did you handle which “page” was being displayed at any one time?

    Why weren’t you working with SQLite on the device? Were you limited to working with localStorage because you could get at that with Phonegap? Could you have worked with a project such as Lawnchair by Brian Leroux to make retrieving data easier?

  3. Alex,

    As far as I can tell, yes, the new pages have all the jQm goodness we love. From my own experimenting and as far as I can tell, clone() gets credit for that. Because the “template” div gets all the jQm enhancements at launch, the subsequent cloned pages come with it as well. I think that was part of the failing of the “just piece together the html and .append() it to the body” approach; jQm didn’t enhance it, so while it might have known about it, it wasn’t really able to use it.

  4. Andy,

    Yes, 10 companies would mean 10 div pages. In all likelihood, you’d have more than that. Other pages, probably not dynamic, might include a landing page, info about the app, a feedback submission page, and a searchable list of the companies.

    Handling which page was being displayed was easy: I let jQm do the work. While cloning the div, I replace the id value with a new one. After the clone and append, I appended a new <li> to an existing <ul&ht; on the company list page. The <li> contained a link who’s href was aimed at “#the_id_value”.

    I could have used SQLite, yes. And it wasn’t a limitation of PhoneGap; as far as I know PhoneGap doesn’t have any problems with SQLite. Mostly, I suppose, it was just personal choice. By the time I got to the app, I’d already built the server components and more SQL anything made me want to chew my fingers off. As for Lawnchair, thank you for suggesting it. I wasn’t aware of it, but I will definitely be playing with it. From just glancing at the usage examples, it looks like an excellent project and like it would save a fair bit of time and headache.

  5. Nice article. Just one thing:

    $new_company_page.children(‘#phone_box’).text(’555-555-5555′);

    You should use classes for this, otherwise there’ll be more than one DOM element with the same id in one DOM. :)

  6. Interesting post. I’m in the final throes of a jQM-based web app for mobile devices that does something similar. I have a data collection form which users fill out. They can save the draft data back to the local SQLite database. The challenge was to come up with a way of “loading” saved data back into the form from the database (and this all had to work off-line). Not having a server means that querystring / hash changes are out of the question. In the end, I did this:

    – The form (just a normal web page) presents saved draft records as a jQM list at the top (in a collapsible div). This list is built dynamically on load.
    – When users tap a list item, that data is pulled from the relevant database table, and injected into the form (a simple $.each loop). Users can then edit, re-save, do what they want.

  7. Nice solution, however i have one question.

    When do you call the “create new page function”? dou you have the links pointing to the page you’re about to create, and the create-function on a eventlistener for clicks on the link?

    this is how i did it, and it works, but soetimes you se the info beeing filled out after the page has loaded. and the animation to the page isn’t supersmooth.

  8. A,

    For my project, I was creating the pages as soon as I got the info from the server. You can, of course, do it at your own convenience. The links to the pages were also created, dynamically, at the same time, in the same loop. The links were just list items added to an unordered list that already existed, though empty. I ran into the “smooth” problem, as well. My solution was to track if the creation of pages and links was done or not. If not, tapping the button to access that portion of the app would cause the loading spinner to show until it was done. If everything was loaded, game on.

  9. Ben,

    That’s a good solution and I’m glad you got it working. For my part, I’ve minimized the use of the local SQLite db, but that was definitely personal preference over function. For simple form elements (name, email, etc), I found local storage much more convenient. For more complex stuff, though, SQLite all the way. Best of luck on your project.

  10. Hi, I found this article which got me started with my problem. I felt like the solution was a little too hackish for me, though, and the new pages I was generating didn’t necessarily have all the same elements as any of existing pages (was generating them through a number of icanhaz.js templates; and not all the pages used the same template, and even on those that did, the values were very different).

    What I found worked well was: append the new page to the body, making sure its wrapped in a div with data-role=”page” and matching id and data-url attributes, populate it with content, and just call the .page() method on it. You can do that on the pagebeforecreate hook and switch to that page on completion.

    Cheers, and thanks for the post.

  11. You can use any javascript lbairry you want, but the animations themselves must be written in CSS, not javascript. It is absolutely vital for mobile developers to learn CSS animation instead of javascript. The reason is that Javascript is actually very inefficient for animation, and it’s only recently, with the proliferation of mobile browsers, that we are starting to notice this. You see, with a JS animation (e.g. created using jQuery’s animate function), the javascript lbairry sets an interval timer (lets say, 33ms, which is about 30 frames per second), and on that interval, it figures out where the animated element should be, using complex math calculated by the CPU, and then puts it there. Unfortunately, if the CPU is too slow to do that calculation every interval, it gets backed up and the animation stutters. jQuery and other JS animation tools do some work to combat this, sensing a drop in frame rate and trying to make the interval wider, to give it more time to complete the calculations, but it’s often too-little-too-late.On the other hand, when you do a CSS animation (e.g. using the transform or webkit-transform css properties), you leave it to the GPU to simply calculate where the element should be as often as it can. As soon as it calculates the element’s new position and places it there, the GPU starts the calculation again. This means the animation is always as smooth as the GPU can possibly make it, no matter what. If you animate too many elements for the GPU to handle it may still have trouble, but instead of ugly stuttering, it will be a more graceful drop in framerate.Hope that helps.

Leave a Reply

Your email address will not be published. Required fields are marked *