Question

Quick backstory:

I had everything working perfectly, but after some time, I noticed it no longer functioned properly. The ability to go back a "page" is extremely important to me, so I set about undoing any recent changes I'd made. That didn't work, so I decided to recreate the framework (hoping to piece in the content to find the trouble spot), yet still the problem persists. I've distilled the code down to the basics below.

I used to be able to hit the back button, and I would be immediately taken to the previous panel. NOW, nothing happens except that the "#page_name" is changed and the current panel shifts 1px. If I've clicked a few links (thereby storing more of a history "trail"), then once I hit the back button a second time, I finally go back a state, but not to the right one.

Here's a fiddle: http://jsfiddle.net/gz9nW/

JQ

$(document).ready(function(){
    $('.main-nav').on('click',function (e) {
       e.preventDefault();

       var target = $(this).attr("href");
       $target = $(target);

       $('html, body').stop().animate({
           'scrollLeft': $target.offset().left,
           'scrollTop': $target.offset().top
       }, 900, 'swing', function () {
           window.location.hash = target;
       });
   });
});

CSS

html {
font: 100% 'PT Sans', sans-serif;
height:100%;
width:100%;
margin:0%;
padding:0%;
}
body {
    font-size:1.25em;
    width:100%;
    margin:0%;
    padding:0%;
    overflow:hidden;
}

    header {
        width:100%;
        position:fixed;
        z-index:5000;
        top:0%;
        left:0%;
        padding:0%;
        margin:0%;
        background:silver;
        }


    /*################################ NAV ################################*/

        nav ul {
            list-style:none;
        }
            nav ul li {
                display:inline;
                margin-right:5px;
            }


/*################################ WRAPPER ################################*/

    .wrapper {
        width:1000%; /* #PAGES X 100% */
        height:100%;
    }

/*################################ PAGES ################################*/

        .page-container {
            width:10%; /* 1 / #PAGES */
            display:inline-block;
            vertical-align:top;
            padding:0%;
            margin:0%;
            margin-right:-5px;
        }
            .page-container:nth-child(even) {
                background:lightgreen;
            }
            .page-container:nth-child(odd) {
                background:lightblue;
            }
            .page-contents {
                padding:10% 0%;
                width:61%;
                margin-left:auto;
                margin-right:auto;
                background:grey;
            }

HTML

<body>
    <header>
        <nav>
            <ul>
                <li><a class="main-nav" href="#home">Home</a></li>
                <li><a class="main-nav" href="#products">Products</a></li>
                <li><a class="main-nav" href="#services">Services</a></li>
                <li><a class="main-nav" href="#quote">Quote</a></li>
                <li><a class="main-nav" href="#about">About</a></li>
                <li><a class="main-nav" href="#contact">Contact</a></li>
            </ul>
        </nav>
    </header>
    <div class="wrapper">
        <div class="page-container" id="home">
            <div class="page-contents">
            home
            </div>
        </div>
        <div class="page-container" id="products">
            <div class="page-contents">        
            products
            </div>
        </div>
        <div class="page-container" id="part-list">
            <div class="page-contents">        
            part list catalog
            </div>
        </div>        
        <div class="page-container" id="services">
            <div class="page-contents">        
            services
            </div>
        </div>
        <div class="page-container" id="quote">
            <div class="page-contents">        
            quote request
            </div>
        </div>
        <div class="page-container" id="about">
            <div class="page-contents">        
            about
            </div>
        </div>
        <div class="page-container" id="contact">
            <div class="page-contents">        
            contact page
            </div>
        </div>
        <div class="page-container" id="inquiries">
            <div class="page-contents">        
            solution inquiries
            </div>
        </div>
        <div class="page-container" id="news">
            <div class="page-contents">        
            news
            </div>
        </div>
        <div class="page-container" id="legal">
            <div class="page-contents">        
            legal info
            </div>
        </div>
    </div>
</body>

It's a huge blow to this project, as this specific functionality was something I was after. Any help would be greatly appreciated!

Was it helpful?

Solution

This should fix your issue:

DEMO

$(document).ready(function () {
    $('.main-nav').on('click', function (e) {
        e.preventDefault();
        var toTarget = $(this).attr('href');
        history.pushState(null, null, toTarget);
        $(window).triggerHandler('hashchange');
    });
});

$(window).on('hashchange', function () {
    if(!window.location.hash) return;
    var $target = $(window.location.hash);
    $('html, body').stop().animate({
        scrollLeft: $target.offset().left,
        scrollTop: $target.offset().top
    }, 900, 'swing');
});

OTHER TIPS

I wanted to add this so that those who have trouble with cross-browser compliance when using Wolff's code can have some reprieve from banging their head against [...dealer's choice].

Back to Home Issue

Some browsers don't play nice when trying to go back to the home page, as it has no hashvalue.

Home Page Hash Fix

function hashHome() {
    var currentHash = location.hash;
    var homeHash = '#home';
    if (currentHash == '') {
        history.pushState(null, null, homeHash);
    }
}
hashHome();

Webkit Simultaneous Scrolling Issue

A HUGE problem in some webkit browsers, specifically mobile (read iOS!!!), is that when you simultaneously scroll horizontally and vertically, what you end up with is a sputtering, jittery mess that lands you back where you started. Quite devastating, and very perplexing.

I initially thought this was due to the browser not hashing the new values, but I later realized that the new values were there, just not "reported" to the viewport. The iOS address bar (7+) doesn't reveal changes as they happen, only after tapping the address bar (DOI! whoops, should've tried that first thing...) did I figure it out. Call me crazy, but if your code relies on one thing (the new hashvalue), and that thing isn't visibly happening, you might assume (like I did) that the reason the code isn't working is because of the one thing you're "seeing" isn't happening.

For complete explanation: Horizontal One-Page Site: Mobile-Webkit Scrolling & Swiping Issues

The click function remains the same as Wolff's.

Cross-browser Fix

$(window).on('hashchange', function() {

    if(!window.location.hash) return;
    var target = $(window.location.hash);
    var targetHash = window.location.hash;

    var iOS = ( navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false );

    var currentPosition = $(window).scrollLeft();

    var targetPosLeft = target.offset().left;

    var targetPosTop = target.offset().top;

    function unbindWindow() { $(window).unbind('scroll'); }

    function repositionWin() {
        unbindWindow();
        $(window).on('scroll', function() {
            var alteredPosLeft = $(window).scrollLeft();
            var alteredPosTop = $(window).scrollTop();          
            if (alteredPosLeft != targetPosLeft) {
                window.scrollTo(targetPosLeft, alteredPosTop),
                unbindWindow(),
                repositionWin();
            }           
        });
    }

    function fadePages() {
        if (targetPosLeft == currentPosition) {
        }
        else {
            function fadePageOut() {
                $('.page-container').stop(true,false).animate({
                    opacity: "0.25",
                    transition: "opacity 0.1s 0.0s ease"
                });
            }
            function fadePageIn() {
                $('.page-container').stop(true,false).animate({
                    opacity: "1.0",
                    transition: "opacity 0.3s 0.0s ease"
                });
            }
            fadePageOut();
            setTimeout (fadePageIn, 900);
        }
    }

    function pageChange() {
        if (jQuery.browser.mobile === true) {
            if (iOS === true) {
                unbindWindow();
                $('html,body').stop(true,false).animate({
                    scrollLeft: targetPosLeft}, 1400);
                    setTimeout (repositionWin, 1500);
            }
            else {
                unbindWindow();
                $('html,body').stop(true,false).animate({
                    scrollLeft: targetPosLeft}, 1200, function() {
                        $(this).stop(true,false).animate({
                            scrollTop: targetPosTop
                        }, 200, repositionWin);
                });
            }
        }
        else {
            fadePages();
            unbindWindow();
            $('html,body').stop(true,false).delay(100).animate({
                scrollLeft: targetPosLeft,
                scrollTop: targetPosTop
            }, 1300, repositionWin);
        }
    }   
    if ($('#mini-site-menu-button-container').is(':visible') === true && $('#main-menu-wrapper').hasClass('show-main-menu') === true) {
        setTimeout (pageChange, 300)
    }
    if ($('.footer-container').is(':visible') === true) {
        setTimeout (pageChange, 500)
    }
    if ($('.form-instructions-wrapper').hasClass('expand-form-instruct') === true) {
        setTimeout (pageChange, 500)
    }
    if ($('.quick-quote-container').hasClass('toggle-open') === true) {
        setTimeout (pageChange, 500)
    }
    if ($('#mini-site-menu-button-container').is(':visible') === false && $('.footer-container').is(':visible') === false && $('.form-instructions-wrapper').hasClass('expand-form-instruct') === false && $('.quick-quote-container').hasClass('toggle-open') === false) {
        pageChange();
    }
    if ($('#main-menu-wrapper').hasClass('show-main-menu') === false && $('.footer-container').is(':visible') === false && $('.form-instructions-wrapper').hasClass('expand-form-instruct') === false && $('.quick-quote-container').hasClass('toggle-open') === false) {
        pageChange();
    }

});

Additional Notes

I removed most of my additional code, but left the conditional statements on end to show how you might call the page change on a mobile site. Depending on how you choose to allow the user to navigate your page (I chose to pop my menu out from the left, hence the "toggle-open"), you'll want to allow your other animations time to complete before you fire the scrolling animation. It tends to be a lot for most mobile devices to handle all at once. I'm still too green to understand how animations que, so this is how I addressed it.

I also added a function to fade the page container while it's scrolling. If you're going from one end of the site to the other, there's quite a bit of leg work involved in traveling that far. That much content whizzing by can be a bit off-putting to watch. Fading it a little seems to dull the blow, and I daresay it adds a little something, as well.

Lastly, for touch enabled devices, I added a function that monitors and corrects the horizontal scroll position in between page changes. It's really easy to accidentally scroll left and right by swiping on a phone, so it seemed necessary.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top