2

I'm creating a jQuery Mobile app with a glossary. Any time the user touches a term that is in the glossary I want it to open the glossary, open the collapsible with that id, and scroll to it.

// get all links within an element, assign click handler
var links = element.find(".glossaryLink");
links.each(function(index){
    $(this).on("click", function(){
        var regex = /\W/g;
        var searchID = this.innerHTML.replace(regex, "").toLowerCase();
        // open the glossary
        window.location = "#glossary";
        var entry = $("#" + searchID);
        // This is working fine, the correct collapsible is opening
        entry.collapsible( "option", "collapsed", false );

        // But this always returns 0...
        var pos = entry.offset().top;
        // ...meaning this does nothing
        $.mobile.silentScroll(pos);
    }
});

The code seems to be working OK in that I can open the correct collapsible (I'm getting the correct <div> within the collapsible-set) but its value of pos is always 0, so it doesn't scroll.

The jQuery API for offset() says that it doesn't support hidden elements but this post seems to suggest that auto-scrolling is possible on a collapsible.

Any help much appreciated!

EDIT

As I've said in the comments below, the setTimeout() trick isn't working if the time < 500ms. Using @Omar ingenious solution of assigning a listener then immediately kicking it off works...

function addGlossaryClickHandlers(element){
    var links = element.find(".glossaryLink");
    links.each(function(index){
        $(this).on("click", function(){
            var regex = /\W/g;
            var searchID = this.innerHTML.replace(regex, "").toLowerCase();
            window.location = "#glossary";
            var entry = $("#" + searchID);
            // assign listener then kick it off by expanding the collapsible
            entry.on("collapsibleexpand", function(){
                var pos = entry.offset().top;
                $.mobile.silentScroll(pos);
            }).collapsible("expand");
        });
    });
}

...but pos above is again returning 0. Stumped again, can anyone help? Stripped down JSFiddle although for some reason the last link in the collapsible is broken...

Community
  • 1
  • 1
cortexlock
  • 1,446
  • 2
  • 13
  • 26
  • Try placing the logic that calculates the `offset` and calls `silentScroll` in a `setTimeout`. That will give the browser a chance to open the glossary before calculating the offset. – Jack Mar 24 '14 at 23:54
  • @JackPattishallJr. Thanks very much, that did the trick! Do you want to post as an answer so I can accept it? Also, is there a generally accepted timeout needed to allow the content to be built? (I'm guessing this technique will be needed elsewhere in JQM!) – cortexlock Mar 25 '14 at 00:19
  • You dont need to use `setTimeout()` http://jsfiddle.net/Palestinian/N46rM/ listen to `collapsibleexpand` function, on that stage, elements within are visible. – Omar Mar 25 '14 at 11:03
  • @Omar What's the benefit of both approaches? Is one better than the other? – cortexlock Mar 25 '14 at 19:32
  • setTimeout delay varies from platform/device to another, depending on their processing speed. When listening to an event, you run code whenever the event occurs, whether the device is fast or slow. – Omar Mar 25 '14 at 19:42
  • @Omar I may have been a bit hasty in accepting the setTimeout solution, you're right...setting the time to anything < 500ms means the scroll doesn't happen. I can see that the listener would be better, but I need to set it on the entire collapsible-set container...how would I get a handle on which collapsible had triggered the event listener? – cortexlock Mar 26 '14 at 19:50
  • @Omar would I instead attach an event listener to **every** collapsible within the collapsible-set? – cortexlock Mar 26 '14 at 19:53
  • You can delegate it to all collapsibles as in my code in the link on your post. Listening to event is much better always :) – Omar Mar 26 '14 at 19:59
  • @Omar it's a great solution...but pos is still returning 0 in the edited code above, using your trick of assigning a listener then immediately kicking it off. Can you help? Sorry for the question spam! – cortexlock Mar 26 '14 at 20:29
  • Dont worry, we will fix it. Can you setup a dummy fiddle? – Omar Mar 26 '14 at 20:48
  • @Omar Thanks, really appreciate it. Link to fiddle added above, took me ages to get it to work! – cortexlock Mar 26 '14 at 22:52
  • Nice work, I like it. I'll check it tomorrow, hopefully, come out with a solution :) – Omar Mar 26 '14 at 22:58
  • @JackPattishallJr. Sorry I've had to reopen the question as the timeout technique didn't work...it needed to be > 500ms, anything less and the old behaviour resurfaced. – cortexlock Mar 26 '14 at 23:21
  • I hope this is what you're looking for http://jsfiddle.net/Palestinian/tXg7x/ you had some mistakes: 1) duplicate ID `glossary` collapsible and collapsible-set 2) enhance collapsible set using `.collapsibleset()`. – Omar Mar 27 '14 at 09:05
  • @Omar really sorry, been working nights. Thanks for the code, it works perfectly in the fiddle but for some odd reason still getting pos of 0 in the app code. The fiddle HTML was simplified and I need to check it out and see why the behavior is different. Puzzling but I know the solution works. Thanks again! – cortexlock Mar 28 '14 at 19:28
  • @Omar do you want to post as an answer so I can accept it? – cortexlock Mar 28 '14 at 19:29
  • Make sure you're using unique IDs, if you want, I can review your code if you upload it to Dropbox. You're welcome :) – Omar Mar 28 '14 at 20:39

1 Answers1

0

You'll want to move the logic that calculates offset and silentScroll to a setTimeout.

As for your comment: Typically, setTimeout(func, 0) is the norm. However, HTML5 spec states 4ms, so you'll see some code samples where setTimeout(func, 4) is used.

If you're interested why this works, here's a great break down into what's happening and why setTimeout addresses the issue (it's all due to events):

https://stackoverflow.com/a/4575011/1253479

Thanks!

Community
  • 1
  • 1
Jack
  • 9,151
  • 2
  • 32
  • 44