108

What is the correct way (to this date) to use JQuery Mobile and Phonegap together?

Both frameworks need to load before they can be used. How can I be sure that both are loaded before I can use them?

coderslay
  • 13,960
  • 31
  • 73
  • 121
Juw
  • 2,059
  • 4
  • 18
  • 23
  • 11
    please ! choose an answer !!! – realtebo Mar 20 '13 at 18:00
  • even though it deserves it, I'm not going to +1 this until an answer is chosen <3 – uɥƃnɐʌuop Jul 08 '13 at 22:12
  • 1
    What is the actual problem being solved here - what if I just provided references to the required js files for jQuery and Cordova in my index.html and then redirect to say login page from a 3rd js file using jQuery's $.mobile.changePage? I mean what stops this design from working and why do I need the solutions outlined below? Is it because there are asynchronous loads inside jQuery and/or Cordova and my 3rd js file could be loaded even before the 2 frameworks are loaded? Please suggest. Thanks – Asif Aug 13 '13 at 00:01
  • @Mustafa for example you might try to access the database BEFORE the `ondeviceReady` event is triggered from your JQM code... – Paranoid Android Sep 10 '13 at 21:17

9 Answers9

174

You can use deferred feature of JQuery.

var deviceReadyDeferred = $.Deferred();
var jqmReadyDeferred = $.Deferred();

document.addEventListener("deviceReady", deviceReady, false);

function deviceReady() {
  deviceReadyDeferred.resolve();
}

$(document).one("mobileinit", function () {
  jqmReadyDeferred.resolve();
});

$.when(deviceReadyDeferred, jqmReadyDeferred).then(doWhenBothFrameworksLoaded);

function doWhenBothFrameworksLoaded() {
  // TBD
}
Octavian
  • 1,871
  • 1
  • 12
  • 6
  • 3
    this answer should get more votes and be marked as the correct one. – memical Oct 15 '12 at 17:06
  • 4
    Could you elaborate a little more please? What's the hierarchy of file references look like? Thanks – farjam Nov 26 '12 at 21:55
  • Not only should this be the accepted answer, it should also be the template for cordova as it ships, callback hell is for a different era. – ocodo Feb 26 '13 at 12:55
  • 2
    Please, could you add the order of the script loading, using latest version ?? – realtebo Mar 20 '13 at 18:11
  • jQM should be loaded after the code in this example. i.e: assuming the code is in a file called environment.js, the scripts should be loaded in this order: jquery, environment, jquery.mobile. Hope that helps. – rilla Mar 21 '13 at 19:32
  • 7
    For all those who say it doesn't work - the order of declaring script matters. First include jquery, THEN THIS CODE inside a script element, then jquery mobile js. – Manish May 18 '13 at 20:00
  • Please can you give us the order of scripts loading for this ? Because for me, this is not working fine (PG 3.3 & JQM 1.4). – Louis Oct 06 '14 at 19:39
  • 1
    What about `cordova.js`? Should it be loaded before or after JQM? – Ferdinand.kraft Oct 31 '15 at 20:51
17

Here's how it worked for me, based on the example above

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta name="format-detection" content="telephone=no" />
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
        <link rel="stylesheet" type="text/css" href="css/bootstrap.css" />
        <title>InforMEA</title>
    </head>
    <body>
        <script type="text/javascript" src="js/jquery-1.8.3.js"></script>
        <script type="text/javascript">
            var dd = $.Deferred();
            var jqd = $.Deferred();
            $.when(dd, jqd).done(doInit);

            $(document).bind('mobileinit', function () {
                jqd.resolve();
            });
        </script>
        <script type="text/javascript" src="js/jquery.mobile-1.2.0.js"></script>
        <script type="text/javascript" src="cordova-2.2.0.js"></script>
        <script type="text/javascript">
            document.addEventListener('deviceready', deviceReady, false);
            function deviceReady() {
                dd.resolve();
            }

            function doInit() {
                alert('Ready');
            }
        </script>
    </body>
</html>
  • This worked for me too but if I add
    to the same page before html tag closes the page doesn't load and I get errors. I want to start using both frameworks using a third js file from the point both are loaded. How do I do that?
    – Asif Aug 16 '13 at 01:22
  • Of course I tried loading the 3rd js file that has business logic for my app in the doInit() but it didn't work. That file has event binding logic and function declarations e.g. $( document ).delegate( '#fakhera-index-page' , 'pageinit',function(event){ ... }. How can I do this? – Asif Aug 16 '13 at 01:24
7

In order to use phonegap along with jquery mobile, you need to use it like this

<head>
<title>Index Page</title>

<!-- Adding viewport -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no">

<!-- Adding jQuery scripts -->
<script type="text/javascript" src="jquery/jquery-1.7.1.min.js"></script>

<!-- Since jQuery Mobile relies on jQuery core's $.ajax() functionality,
 $.support.cors & $.mobile.allowCrossDomainPages must be set to true to tell
 $.ajax to load cross-domain pages. -->
<script type="text/javascript">
    $(document).bind("mobileinit", function() {
        $.support.cors = true;
        $.mobile.allowCrossDomainPages = true;
    });
</script>

<!-- Adding Phonegap scripts -->
<script type="text/javascript" charset="utf-8"
    src="cordova/cordova-1.8.0.js"></script>

<!-- Adding jQuery mobile scripts & CSS -->
<link rel="stylesheet" href="jquerymobile/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript"
    src="jquerymobile/jquery.mobile-1.1.0.min.js"></script>

</head>
<script type="text/javascript">
    // Listener that will invoke the onDeviceReady() function as soon as phonegap has loaded properly
    document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
        navigator.splashscreen.hide();

        document.addEventListener("backbutton", onBackClickEvent, false); // Adding the back button listener    

    }
    </script>
<body>
<div data-role="page" id="something" data-ajax="false">
        <script type="text/javascript">
            $("#something").on("pageinit", function(e) {

            });

            $("#something").on("pageshow", function(e) {

            });

            $("#something").on("pagebeforeshow", function(e) {

            });
        </script>

        <div data-role="header">            
        </div>

        <div data-role="content">           
        </div>      
    </div>
</body>  
coderslay
  • 13,960
  • 31
  • 73
  • 121
6

As many people suggested using a deferred is an okay option as long as you don't care what order deviceready and mobileinit happe in. But in my case, I needed a few pageshow events when the application first loaded and mobileinit and by extension those pageshow/pagebeforeshow/etc events were all firing before deviceready finished, so I couldn't bind to them properly using a deferred on them. This race condition was not a good thing.

What I needed to do was make sure 'mobileinit' didn't take place until after 'deviceready' was already fired. Because mobileinit fires immediately when you load JQM I chose to use jQuery.getScript to load it AFTER deviceready was already finished.

<script src="cordova-2.2.0.js"></script>
<script src="js/jquery-1.8.2.min.js"></script>
<script src="js/async.min.js"></script>
<script src="js/app.js"></script>
<script>
  document.addEventListener(
    'deviceready',
    function () {
      $('body').css('visibility', 'hidden');
      $(document).one("mobileinit", function () {
        app.init();
        $('body').css('visibility', '');
      });
      $.getScript('js/jquery.mobile-1.2.0.min.js');
    },
    false
  );
</script>

The reason I'm hiding the body is that a side effect of this method is a half second of visibility of the original HTML document before jquery.mobile loads. In this case hiding it an extra half second of empty space is preferred to seeing the unstyled document.

  • 1
    +1 on your answer for it inspired me to solve my problem with some slight change. First, move body.hide() code to very first line of onBodyLoad(); Second, move body.show() code to be after getScript( jQM_PATH); Because, mobileInit() is called on each JQM page transition. Not ideal. Hope this helps others. – GeorgeW Feb 22 '13 at 22:54
  • Can you just include the rest of your `index.html` – JGallardo Nov 02 '13 at 21:52
  • This did not work for me because cordova was removing all files there weren't included using the ` – Chris Snow Feb 21 '15 at 11:05
2

I believe that it isn't necessary to use the deferred feature. (Maybe this isn't necessary with newer versions of phonegap?) I have this in the head of my index.html file and everything works fine. I do think that the order of including jquery, phonegap and jquery mobile are important.

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />

    <!-- Adding jQuery -->
    <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>

    <!-- Add Phonegap scripts -->
    <script type="text/javascript" src="phonegap.js"></script>

    <!-- Add jQuery mobile -->
    <link rel="stylesheet" href="css/jquery.mobile-1.3.2.css" />
    <script type="text/javascript" src="js/jquery.mobile-1.3.2.min.js"></script>

    <title>MY TITLE</title>
</head>
Allie Hoch Janoch
  • 1,156
  • 8
  • 9
1

this is work for me. base on dhaval,this sample when I learn using sqlite

<!DOCTYPE html>
<html>
 <head>
<title>Cordova Sqlite+Jquery</title>
<script type="text/javascript" charset="utf-8" src="js/jquery-1.8.3.min.js"></script>   
<script type="text/javascript" charset="utf-8" src="cordova-2.2.0.js"></script>
<script type="text/javascript" charset="utf-8">`

// Call onDeviceReady when Cordova is loaded.
//
// At this point, the document has loaded but cordova-1.8.0.js has not.
// When Cordova is loaded and talking with the native device,
// it will call the event `deviceready`.
//
function onLoad() {
    document.addEventListener("deviceready", onDeviceReady, false);
}

// Populate the database 
//
function populateDB(tx) {
    tx.executeSql('DROP TABLE IF EXISTS DEMO');
    tx.executeSql('CREATE TABLE IF NOT EXISTS DEMO (id unique, data)');
    tx.executeSql('INSERT INTO DEMO (id, data) VALUES (1, "First row")');
    tx.executeSql('INSERT INTO DEMO (id, data) VALUES (2, "Second row")');
}

// Query the database
//
function queryDB(tx) {
    tx.executeSql('SELECT * FROM DEMO', [], querySuccess, errorCB);
}

// Query the success callback
//
function querySuccess(tx, results) {
    var len = results.rows.length;
    //console.log("DEMO table: " + len + " rows found.");
    $('#result').html("DEMO table: " + len + " rows found.");
    var listval = '';
    for (var i=0; i<len; i++){
        //console.log("Row = " + i + " ID = " + results.rows.item(i).id + " Data =  " + results.rows.item(i).data);
         listval += '<li>'+ results.rows.item(i).data + '[' + results.rows.item(i).id + '] </li>';
    }

    $('#listItem').html(listval);

}

// Transaction error callback
//
function errorCB(err) {
    console.log("Error processing SQL: "+err.code);
}

// Transaction success callback
//
function successCB() {
    var db = window.openDatabase("Database", "1.0", "PhoneGap Demo", 200000);
    db.transaction(queryDB, errorCB);
}

// Cordova is loaded and it is now safe to make calls Cordova methods
//
function onDeviceReady() {
    // Now safe to use the Cordova API
    //alert('ready');
    var db = window.openDatabase("Database", "1.0", "PhoneGap Demo", 200000);
    db.transaction(populateDB, errorCB, successCB);
    //$('#result').html('hello');
}

</script>
  </head>
 <body onload="onLoad()">
  <div>result:</div><div id="result"></div>
  <ul id="listItem">
  </ul>
 </body>
 </html>
Ztuons Ch
  • 149
  • 1
  • 7
0

To build on @Jeffrey's answer, I found a much cleaner way which hides the HTML markup until JQM has finished processing the page and renders the first Page element, since I've noticed that 1/2 second flicker of bare markup before JQM renders.

You only need to hide all the markup with css...PageShow() by JQM will toggle the visibility for you.



//snip
<style type="text/css">
.hide {
  display:none;
}
</style>

//snip - now the markup notice the hide class
<div id="page1" data-role="page" class="hide">
     //all your regular JQM / html form markup
</div>

//snip -- down to the end of /body
<script src="cordova-2.2.0.js"></script>
<script src="js/jquery-1.8.2.min.js"></script>
<script>
   document.addEventListener(
     'deviceready',
      function () {
         $(document).one("mobileinit", function () {
         //any JQM init methods

       });
      $.getScript('js/jquery.mobile-1.2.0.min.js');
   },
   false);
</script>

Erik
  • 61
  • 4
  • tried your suggestion and it didn't work for me. JQM pageshow won't show the hidden first page. Moreover, the standard html still show up before being hidden. I eventually made it work based on Jeffrey's solution with slight changed in timing. see my comment below his answer. – GeorgeW Feb 22 '13 at 22:50
0

The following worked for me on PG 2.3 and JQM 1.2, incl. Facebook Connect Plugin:

<head>
<script src="./js/jquery-1.8.2.min.js"></script>
<script>
    $.ajaxSetup({
        dataType : 'html'
    });

    var dd = $.Deferred();
    var jqd = $.Deferred();
    $.when(dd, jqd).done(function() {                

        FB.init({ appId: auth.fbId, nativeInterface: CDV.FB, useCachedDialogs: false });
    });

    $(document).bind('mobileinit', function () {
        jqd.resolve();
    });                        
</script>
<script src="./js/jquery.mobile-1.2.0.min.js"></script>
<script>
    $.mobile.loader.prototype.options.text = "loading";
    $.mobile.loader.prototype.options.textVisible = true;
    $.mobile.loader.prototype.options.theme = "a";
    $.mobile.loader.prototype.options.html = "";

    $.mobile.ajaxEnabled = false;
    $.mobile.allowCrossDomainPages = true;
    $.support.cors = true;       

    $('[data-role=page]').live('pagecreate', function(event) {                      
        tpl.renderReplace('login', {}, '#content-inner', function() {                   
            auth.init();
        });
    });
</script>
<script src="./js/cordova-2.3.0.js"></script>
<script src="./js/cdv-plugin-fb-connect.js"></script>
<script src="./js/facebook_js_sdk.js"></script>                     
<!--some more scripts -->
<script>        
    document.addEventListener('deviceready', function() {
        dd.resolve();
    }, false);                        
</script>  
<head>
Jens Peters
  • 2,075
  • 1
  • 22
  • 30
-1

Loading of PhoneGap is slightly different than loading of jQuery. jQuery works more as a utility library so you include that and it is available for use immediately. On the other hand PhoneGap requires support from native code for proper initialization so it is not ready to use soon after included in the page.

Phonegap suggests to register and wait for deviceready event executing any native specific code.

<!DOCTYPE html>
<html>
  <head>
    <title>PhoneGap Example</title>

    <script type="text/javascript" charset="utf-8" src="lib/jquery.min.js"></script>
    <script type="text/javascript">
        // jquery code here
    </script>
    <script type="text/javascript" charset="utf-8" src="lib/android/cordova-1.7.0.js"></script>
    <script type="text/javascript" charset="utf-8">

    function onLoad(){
        document.addEventListener("deviceready", onDeviceReady, false);
    }

    // Cordova is ready
    function onDeviceReady() {
        // write code related to phonegap here
    }
    </script>
  </head>
  <body onload="onLoad()">
    <h1>Phonegap Example</h1>
  </body>
</html>

For more info check doc

dhaval
  • 7,611
  • 3
  • 29
  • 38
  • 1
    But the problem is that i want to use phonegap stuff in my jquery code. In your example all jquery code would be run before phonegap is even loaded. Maybe if i put all the code inside onDeviceReady() function? Like this: $("#form").live("pageinit",function(event) { //phonegapp stuff here }); – Juw Jun 08 '12 at 09:31
  • if your `#form` is the first page then you wont receive `pageinit` callback as it is too late – dhaval Jun 08 '12 at 09:48