0

I have a single-page web app built with jQuery Mobile. After the user completes a certain action, I want to programmatically bring them back to a menu page, which involves going back in history and then performing some actions on elements of the menu page.

Simply doing

window.history.go(-1); //or $.mobile.back();
doSomethingWith(menuPageElement);

doesn't work, because the going-back action is asynchronous, i.e. I need a way of waiting for the page to load before calling doSomethingWith().

I ended up using window.setTimeout(), but I'm wondering if there's not an easier way (different pattern?) to do this in jQM. One other option is to listen for pageload events, but I find it worse from code organization point of view.

(EDIT: turns out native js promises are not supported on Mobile Safari; will need to substitute by a 3rd-party library)

//promisify window.history.go()
function go(steps, targetElement) {
    return new Promise(function(resolve, reject) {
        window.history.go(steps);
        waitUntilElementVisible(targetElement);

        //wait until element is visible on page (i.e. page has loaded)
        //resolve on success, reject on timeout
        function waitUntilElementVisible(element, timeSpentWaiting) {
            var nextCheckIn = 200;
            var waitingTimeout = 1000;

            timeSpentWaiting = typeof timeSpentWaiting !== 'undefined' ? timeSpentWaiting : 0;

            if ($(element).is(":visible")) {
                resolve();
            } else if (timeSpentWaiting >= waitingTimeout) {
                reject();
            } else { //wait for nextCheckIn ms
                timeSpentWaiting += nextCheckIn;
                window.setTimeout(function() {
                    waitUntilElementVisible(element, timeSpentWaiting);
                }, nextCheckIn);
            }
        }
    });
}

which can be used like this:

go(-2, menuPageElement).then(function() { 
    doSomethingWith(menuPageElement);
}, function() {
    handleError();
});

Posting it here instead of in Code Review since the question is about alternative ways to do this in jQM/js rather than performance/security of the code itself.

margold
  • 587
  • 2
  • 5
  • 16

1 Answers1

0

Update

To differntiate whether the user was directed from pageX, you can pass a custom parameter in pagecontainer change function. On pagecontainerchange, retrieve that parameter and according bind pagecontainershow one time only to doSomething().

$(document).on("pagecreate", "#fooPage", function () {
    $("#bar").on("click", function () {
        /* determine whether the user was directed */
        $(document).one("pagecontainerbeforechange", function (e, data) {
            if ($.type(data.toPage) == "string" && $.type(data.options) == "object" && data.options.stuff == "redirect") {
                /* true? bind pagecontainershow one time */
                $(document).one("pagecontainershow", function (e, ui) {
                    /* do something */
                    $(".ui-content", ui.toPage).append("<p>redirected</p>");
                });
            }
        });
        /* redirect user programmatically */
        $.mobile.pageContainer.pagecontainer("change", "#menuPage", {
            stuff: "redirect"
        });
    });
});

Demo


You need to rely on pageContainer events, you can choose any of these events, pagecontainershow, pagecontainerbeforeshow, pagecontainerhide and pagecontainerbeforehide.

The first two events are emitted when previous page is completely hidden and before showing menu page.

The second two events are emitted during hiding previous page but before the first two events.

All events carry ui object, with two different properties ui.prevPage and ui.toPage. Use these properties to determine when to run code.

Note the below code only works with jQM 1.4.3

$(document).on("pagecontainershow", function (e, ui) {
   var previous = $(ui.prevPage),
       next = $(ui.toPage);

   if (previous[0].id == "pageX" && next[0].id == "menuPage") {
       /* do something */
   }
});
Community
  • 1
  • 1
Omar
  • 32,302
  • 9
  • 69
  • 112
  • As I wrote in the question, I think using events will make the code more unreadable, + you will also need to differentiate between the above use case, and the use case where it's the user who is navigating from pageX to menuPage. – margold Aug 18 '14 at 15:00
  • @marrgold those events are continuously emitted on pagecontainer whether you navigate programmatically or manually. – Omar Aug 18 '14 at 15:22
  • yes, hence the need to differentiate between the two cases, since in the case where the user navigated there manually you don't want to doSomething() – margold Aug 19 '14 at 03:07
  • @marrgold you can pass a parameter in change page function to differentiate whether user was directed by system http://stackoverflow.com/questions/21139572/storing-a-variable-in-localstorage-is-too-slow/21174455#21174455 – Omar Aug 19 '14 at 06:37