tiistai 27. maaliskuuta 2012

Coding quickie: jQuery Mobile and dynamically created content.

I think I don't have to start telling how good jQuery Mobile(JQM) framework is. I think it's the fastest way to create mobile-webpage for newer smartphones. I have tested this framework with iPhone 4 (iOS), iPad 1 and 2 (iOS), HTC Desire HD (Android), Samsung Galaxy Tab (Android), HTC Mozart (WP7), Nokia Lumia 800 (WP7) and amazing Nokia 5230 (Symbian).
I even got my hands on a Blackberry tablet for few minutes. Unfortunately my mobile detection didn't count it as a mobile device so I can't tell how well JQM works on it.
Devices that understand JQM well and run it smoothly are iOS and Android devices. Amazingly JQM works in Nokia 5230's own browser. It doesn't look as good as on iOS or Android devices, but it’s functional. JQM works with WP7 also. Full list of supported devices can be seen here.

#Skip this part if you are not interested in my opinions#
I'm from Finland, the land of Nokia. The great Nokia has employed Finns for decades and is the most known brand from Finland.
Actually it's not too long time ago that I admired Nokia myself. I was very proud of that we had something like Nokia in this little cold country. This is the time when Nokia had just published E63 and E71 "smartphones". I still think that those were one of the best business smartphones of that time. Those were little bit like Blackberry rip-offs but what can you do if you can’t buy Blackberry from Finland.
To the point. For a long time now I haven’t had faith in Nokia. Finland or Finnish shareholders own only a little fraction of Nokia. Well, head office of Nokia is still located in Finland, but I think that not for long anymore. Deal (or alliance) with Microsoft was the last nail in the coffin.
As a developer, WP7 operating system looked promising. I had my first glance of it at Geeks On Wheels-seminar at Tampere. It's promising but I think that it's too raw operating system for Nokia. Android or Maemo would have been better choices. Personally I think that engineers that have worked at Nokia have that kind of knowledge that when they leave Nokia, we will soon have lots of new innovative companies at Finland. These companies will do the same for the technology world that Angry Birds did for the gaming world.
These are just my own opinions and the point of this blog text was not to tell how sad I'm because of Nokia.
#Skip this part if you are not interested of my opinions#

The problem that this blog-post solves is explained next. If content like list items is fetched with ajax the JQM-theme won’t work on it. This is because the JQM does different kind of changes at start to the content that is inside the pages.

JQM will change next button from this:
<a href="#" class="notheme_button" data-role="button">No theme button</a>
in to this:
<a class="notheme_button ui-btn ui-btn-up-c ui-btn-corner-all ui-shadow ui-btn-up-undefined" data-role="button" href="#" data-corners="true" data-shadow="true" data-iconshadow="true" data-inline="false" data-wrapperels="span">
<span class="ui-btn-inner ui-btn-corner-all">
<span class="ui-btn-text">No theme button</span>
</span>
</a>


If the content is fetched with ajax and placed to some JQM-page it won't do these changes. Thats why I'm introducing .trigger("create").

This is the normal JQM page:
<div data-role="page"  data-theme="c" id="main">
    <!-- header -->
    <div data-role="header" data-theme="a">          
        <h1>.trigger("create");</h1>
    </div>
    <!-- header -->
    <!-- content -->
    <div data-role="content">
        <a href="#" class="notheme_button" data-role="button">No theme button</a>
        <a href="#" class="button" data-role="button">Refresh theme button</a>
        <div id="job_list">
        </div>
    </div>
    <!-- content -->
</div>

Let's say that we want to put content inside to div#job_list with ajax. Content that I want is links styled with listview.
Let’s fetch the content like this:
$(".button").click(function(){
    ajax_procedure("ajax.php?giveLinks=yes","theme");
});

Simple javascript ajax function(You can use jQuery .ajax() or .get() too).
function ajax_procedure(ajax_url,style){
        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.open("GET",ajax_url,false);
        xmlhttp.send(null);
        //alert(xmlhttp.responseText);
        if(style == "theme"){
            theme(xmlhttp.responseText);
        }
        else{
            notheme(xmlhttp.responseText);
        }
}

Here is the php file that gives the list of links as a response:
<?php
    if($_GET['giveLinks'] == "yes"){
        echo '<ul data-role="listview" data-inset="true" data-theme="d">';
            echo "<li><a href='http://www.google.fi' rel='external'>Google</a></li>";
            echo "<li><a href='http://www.yahoo.fi' rel='external'>Yahoo</a></li>";
        echo '</ul>';
    }
?>

and the functions that put response to div#job_list.
function theme(response){
        $('#job_list').html(response);
        $('#main').trigger("create");
}

function notheme(response){
        $('#job_list').html(response);
}

This solution is not big secret and it can be found from stackoverflow also. I just explained and simplified the procedure a little. 
Why? If you haven't encountered this problem yet and you are building JQM based apps you are about to struggle with this problem.

Here's the working example of.trigger("create").








2 kommenttia:

  1. hi, i think that you can use refresh and the result of it much better than recreate the page again ..

    VastaaPoista
    Vastaukset
    1. See that content is fetched with ajax. There is no possibility to enchance the new markup by refreshing the site. See also that .trigger("create") won't create the whole page again. It can be even applied to particular element instead of the whole page.

      Here is direct quote from JQM-docs:
      "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. "

      Poista