10

I have two jQuery mobile pages (#list and #show). There are several items on the #list page with different IDs. If I click on item no.5, the ID no5 will be stored in localStorage and I will be redirected to page #show

Now the problem: Storing the ID in localStorage works, but the next page shows me not the item no.5, but it shows me an old item, that was in the localStorage before.

script from page #list

localStorage.setItem("garageID", $(this).attr('id'));                           
window.location.replace("#show");
Struct
  • 950
  • 2
  • 14
  • 41
  • 2
    That does not sound like a "too slow" problem. (Side note: you can always use `this.id` in place of `$(this).attr('id')`, and that _will_ be faster, always.) – Matt Ball Jan 15 '14 at 14:10
  • Why don't use changePage? – Omar Jan 15 '14 at 14:41
  • Hi guys. I know it's not a "too slow" problem, but I don't know how to describe :) @Omar. I thought window.location.replace is the same? – Struct Jan 15 '14 at 15:43
  • `changePage` function isn't only to _switch_ pages, it updates history, transmit data...etc – Omar Jan 15 '14 at 15:55
  • OK, and if I have a page which I don't want to see in the history, I can use window.location.replace .. right? – Struct Jan 15 '14 at 15:57
  • jQM listens to hash changes. What do you want to achieve? transfer data from page to another? – Omar Jan 15 '14 at 16:36
  • I want to pass a variable from one page to another. Thats my question http://stackoverflow.com/questions/21076202/store-id-from-listview-in-localstorage/21076588#21076588 ... storing the variable works, but on the other page the localstorage is not refreshed and therefore the shown item is wrong – Struct Jan 15 '14 at 16:45
  • which version of jQM r u using? – Omar Jan 15 '14 at 16:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/45322/discussion-between-omar-and-struct) – Omar Jan 15 '14 at 22:03

2 Answers2

15

I encountered this problem too (and not on a mobile : on Chromium/linux).

As there doesn't seem to be a callback based API, I "fixed" it with a timeout which "prevents" the page to be closed before the setItem action is done :

localStorage.setItem(name, value);                           
setTimeout(function(){
     // change location
}, 50);

A timeout of 0 might be enough but as I didn't find any specification (it's probably in the realm of bugs) and the problem isn't consistently reproduced I didn't take any chance. If you want you might test in a loop :

function setLocalStorageAndLeave(name, value, newLocation){
    value = value.toString(); // to prevent infinite loops
    localStorage.setItem(name, value);
    (function one(){
         if (localStorage.getItem(name) === value) {
            window.location = newLocation;
         } else {
            setTimeout(one, 30);
         }
    })();
}

But I don't see how the fact that localStorage.getItem returns the right value would guarantee it's really written in a permanent way as there's no specification of the interruptable behavior, I don't know if the following part of the spec can be legitimately interpreted as meaning the browser is allowed to forget about dumping on disk when it leaves the page :

This specification does not require that the above methods wait until the data has been physically written to disk. Only consistency in what different scripts accessing the same underlying list of key/value pairs see is required.

In your precise case, a solution might be to simply scroll to the element with that given name to avoid changing page.

Note on the presumed bug :

I didn't find nor fill any bug report as I find it hard to reproduce. In the cases I observed on Chromium/linux it happened with the delete operation.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • It seems to me that it should be enough to do a `setTimeout(f, 0)`, just to give the other stuff in the execution queue a chance to run. – Tibos Jan 15 '14 at 14:12
  • 1
    If you already do a solution like this, I'd at least do a constant check if the variable was written during the time and then go on. Otherwise you have an ugly setTimeout and still don't know if the variable is in the localStorage. – Michael Kunst Jan 15 '14 at 14:12
  • 4
    [I see nothing that suggests that `localStorage.setItem()` is asynchronous.](http://www.w3.org/TR/webstorage/#dom-storage-setitem) Is this behavior a browser bug, perhaps? – Matt Ball Jan 15 '14 at 14:13
  • 2
    @MichaelKunst A test won't guarantee anything : the local version of localStorage might be updated but the hard storage not. – Denys Séguret Jan 15 '14 at 14:16
  • You've got a point there. Downvote removed, as I can't come up with something better. I still find the setTimeout very ugly though. – Michael Kunst Jan 15 '14 at 14:19
  • 1
    @MichaelKunst It **is** ugly, hence the quotes around "fixed". – Denys Séguret Jan 15 '14 at 14:19
  • 1
    I've tested your solution and it does not work. Also with a timeout of 500. Only if I refresh the page, it works – Struct Jan 15 '14 at 15:47
  • "_I encountered this problem too (and not on a mobile : on Chromium/linux)._", the problem concerns _mobile_ :) – Omar Jan 16 '14 at 13:19
  • Stumbled upon this problem in my app. Yep, this is very nasty. Workedaround it with `setTimeout` too. – arrowd Oct 05 '16 at 07:51
3

Disclaimer: This solution isn't official and only tested for demo, not for production.

You can pass data between pages using $.mobile.changePage("target", { data: "anything" });. However, it only works when target is a URL (aka single page model).

Nevertheless, you still can pass data between pages - even if you're using Multi-page model - but you need to retrieve it manually.

When page is changed, it goes through several stages, one of them is pagebeforechange. That event carries two objects event and data. The latter object holds all details related to the page you're moving from and the page you're going to.

Since $.mobile.changePage() would ignore passed parameters on Multi-page model, you need to push your own property into data.options object through $.mobile.changePage("#", { options }) and then retrieve it when pagebeforechange is triggered. This way you won't need localstorage nor will you need callbacks or setTimeout.

  1. Step one:

    Pass data upon changing page. Use a unique property in order not to conflict with jQM ones. I have used stuff.

    /* jQM <= v1.3.2 */
    $.mobile.changePage("#page", { stuff: "id-123" });
    
    /* jQM >= v1.4.0 */
    $.mobile.pageContainer.pagecontainer("change", "#page", { stuff: "id-123" });
    
  2. Step two:

    Retrieve data when pagebeforechange is triggered on the page you're moving to, in your case #show.

    $(document).on("pagebeforechange", function (event, data) {
    
      /* check if page to be shown is #show */
      if (data.toPage[0].id == "show") {
    
        /* retrieve .stuff from data.options object */
        var stuff = data.options.stuff;
    
        /* returns id-123 */
        console.log(stuff);
      }
    });
    

Demo

Omar
  • 32,302
  • 9
  • 69
  • 112