3

I have created the following application using iScroll: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-0

As the user flicks left and right or clicks the arrows in the bottom corners, the application moves the content sections it updates the history by changing the hash so that the user can move back and forth to other sections and bookmark them etc.

However! If you access a hash like: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-2 and then navigate a few sections and then use the back buttons two issues happen:

1.) It scrolls to the first screen (even though currentSection is correct, and iScroll has been told the correct section).

2.) If you click the back or forward button multiple times, you stop the animation and cause it to become confused and stick in between two sections.

Looking into the code, and seeing that the correct indexes and elements are being passed to iScroll on hashchange, and console logging out the offsets, I've discovered the issue is cause because the offsets are incorrectly set... however just doing refresh() won't fix the issue, as it will then reset the position.

Can anyone see where the problem is or see a way to fix this?

I should note that this bug ONLY happens if you come into the application on a URL that isn't section 0 and then scroll around the application. This is because the offsets will be created correctly by your interactions. But if you come into a URL like section 3, then the offsets will be incorrect and so the hashchanges don't work correctly, if that makes sense.

The hashchange method looks like:

// handle hashchange events
$(window).hashchange( function(){

    // read the hash to find out what the new section number is
    var nums = location.href.match(/(section)-\d+/g).map(
        function(x){ return +x.replace(/\D/g,"") }
    );
    // set currentSection
    currentSection = nums[0];

    // if the hashchange was called by user scrolling
    if(hashCalledByScroll){
        // no need to anything as they have already updated hash and scrolled
        hashCalledByScroll = false;
    } else {
        // find the section to scrollTo
        sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
        // tell iscroll to scroll to the section
        horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true );
    }

    // hide the menu on hashchange
    hideMenu();

});
Cameron
  • 27,963
  • 100
  • 281
  • 483

2 Answers2

3

Testing your site, I noticed the following: Whenever I access the site via section-3 and then enter the url for section-2, the navigation would instead send me to section-0.

I believe this is the same behaviour as you are experiencing in 1).

So I investigated and came to the following analysis:

In the function horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true ) iScroll retrieves the utils.offset(el) [iScroll.js#772] for the given el-ement. This offset tells it, where the element to scroll to is.

iScroll goes through the element and all of its offsetParents to add up their offsets. This is where things are breaking: <div class="sections"> has a negative offset to its parent, which imho it should not have.

This, in turn, messes up the scrollTo-coordinates.

To see what I am talking about: document.querySelector('.sections').offsetLeft

This has all just been analysis. My approach to fix this would be to avoid scrollToElement() and instead use scrollTo():

    ...
    } else {
        // find the section to scrollTo
        sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
        // tell iscroll to scroll to the section
        var posLeft = -$('#' + sectionToScrollTo)[0].offsetLeft;
        var posTop = -$('#' + sectionToScrollTo)[0].offsetTop;
        horizontal.scrollTo(posLeft, posTop, 1000);
    }
    // hide the menu on hashchange
    hideMenu();
});

Thus, just calculate the location of the section you want to go to yourself.

About 2) I am not sure if there is much one can do about it. Jumping around quickly breaks a lot of carousels. Maybe a delayed callback to scrollEnd, verifying the validity of the current state.

Another thing I noticed is that you can accidentally stop the transition. Try to click, hold and release the cursor midway a transition - you need to be quick.

Hope this helps.

Marius
  • 893
  • 1
  • 8
  • 12
2

Found not best solution and it doesn't solve main problem, but it works.

$(window).hashchange(function () {
    if (hashCalledByScroll) {
        hashCalledByScroll = false;
    } else {
        var hpage = window.location.hash;
        var hpage = hpage.replace('#/section-', ''); //get number of target page
        var cpage = currentSection; //number of current page
        var count = parseInt(hpage) - parseInt(cpage); //difference
        while (count > 0) { //if difference positive: go forward count-times
            horizontal.next();
            count--;
        }
        while (count < 0) { //if difference negative: go backward count-times
            horizontal.prev();
            count++;
        }
    }
    hideMenu();
});

FIDDLE

raskalbass
  • 740
  • 4
  • 21
  • If it works (not fully tested yet) what do you mean it doesn't solve the main problem? Thanks. – Cameron Jun 30 '14 at 09:03
  • I mean that this solution solve your problem with history back button, but it doesn't solve iScroll conflict with hash – raskalbass Jun 30 '14 at 09:11