Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to not scroll to top when using back/forward popstates? #48

Closed
23d1 opened this issue Sep 23, 2018 · 13 comments
Closed

Any way to not scroll to top when using back/forward popstates? #48

23d1 opened this issue Sep 23, 2018 · 13 comments

Comments

@23d1
Copy link

23d1 commented Sep 23, 2018

As the title...

Just curious how to achieve this, as I'm trying to get as close to native browser behavior as possible on back/forward—and currently it always goes back to the top when going back or forward.

Keep up the good work, btw!

@gmrchk
Copy link
Member

gmrchk commented Sep 23, 2018

While swup does replace the content on back/forward button and in this way changes the browser native behavior, it leaves the scroll control completely to the browser and doesn't rewrite it in any way. If your page scrolls to the top on back/forward, it should mean you were at the top or there is some other problem.

Maybe overflow/height set on html and/or body or something similar that would break browsers native behavior?

Here is a discussion about swup currently preserving browsers native behavior as much as possible on popstate, any cotributions on that matter is more than welcome.

Cheers!

@23d1
Copy link
Author

23d1 commented Sep 23, 2018

Ah, yes. Just realized I have a smooth scrolling library, so I'll need to save the positions between pageviews and apply to popstates somehow.

@gmrchk
Copy link
Member

gmrchk commented Sep 23, 2018

Yep, the easiest way is probably to save the scroll for current URL somewhere on each swup:clickLink and set it back on swup:popState, again based on URL (this will probably need some small delay using setTimeout).

@23d1
Copy link
Author

23d1 commented Sep 23, 2018

Hmm. That's where my JavaScript knowledge hits a wall. Haha.

Any pointers? Perhaps;

document.addEventListener('swup:clickLink', event => {
    let scrollSave = document.body.offsetTop;
});
document.addEventListener('swup:popState', event => {
    scrollVal = scrollSave;
});

I guess the main question is how to save it to an array that corresponds to multiple popstates with multiple scroll values...

@gmrchk
Copy link
Member

gmrchk commented Sep 23, 2018

Didn't try, but something like this should do the trick.

let scrollValues = {};

document.addEventListener('swup:clickLink', event => {
    scrollValues[window.location.href] = window.scrollY;  
});

document.addEventListener('swup:popState', event => {
    setTimeout(function() {
        window.scrollTo(0, scrollValues[window.location.href]);
    }, 100);
});

Note that reading and setting the scroll amount might be different for you, as you mentioned that you are using some scroll library, which probably has it's own API for that.

@23d1
Copy link
Author

23d1 commented Sep 24, 2018

Sweet, thanks for that! Gonna have to wrangle that beast later, but the numbers look right, nonetheless. :)

@aerni
Copy link

aerni commented Jul 15, 2019

I would like to always go back to top when clicking a link and only go back to the saved scroll position when navigating with the back/forward button. How can I achieve this? I played around with the different events but can't seem to figure it out.

@aerni
Copy link

aerni commented Jul 15, 2019

Never mind. Found that I had to integrate the Scroll Plugin to achieve scrolling to the top when clicking a link.

But I'm having a different issue now. When using the back/forward button, the scroll position is now immediately restored to the saved value upon click. But when the transition ends, the scroll position is again at the top of the page instead of the saved scroll value.

This is my code:

const options = {
	animationSelector: '[class*="page-transition-"]',
	animateHistoryBrowsing: true,
	plugins: [
		new SwupDebugPlugin(),
		new SwupScrollPlugin({
			animateScroll: false
		})
	]
};

const swup = new Swup(options);

let scrollValues = {};

swup.on('clickLink', () => {
	scrollValues[window.location.href] = window.scrollY;
});

swup.on('popState', () => {
	setTimeout(function() {
		window.scrollTo(0, scrollValues[window.location.href]);
	}, 100);
});

@aerni
Copy link

aerni commented Jul 15, 2019

I think the problem is with the setTimeout function. When increasing the timeout to a higher value like 2000ms, the scroll position is restored properly. Though 2000ms too late. I tried to listen to different event handlers instead of relying on setTimeout. Like this:

swup.on('popState', () => {
	swup.on('animationInStart', () => {
		window.scrollTo(0, scrollValues[window.location.href]);
	});
});

But this way, the scoll position is retrieved on every animationInStart event. I would like it to only be fired when a popState event happens first. Is this possible somehow?

@gmrchk
Copy link
Member

gmrchk commented Jul 15, 2019

Swup scroll control basically doesn't work properly when animateHistoryBrowsing is set to true, as it's normally left for a browser-native functionality to deal with it. That said, you can disable this option or use events as you do.

The problem there is probably the use of popstate event which happens before content replace. What you are going for is probably something like:

  1. On popstate event, setup a listener for contentReplaced event and execute the setting for scroll in handler.
  2. When the scroll is done, we need to disable that contentReplaced listener. Remove it on animationInDone event (simple swup.off('contentReplaced') should be enough, unless you're using that event for something else).

Here is a list of all events (not including the ones added by plugins).

@aerni
Copy link

aerni commented Jul 16, 2019

Anyone looking to make this work, I ended up doing it like this:

let scrollPositions = [];
let scrollToSavedPosition = null;

swup.on('clickLink', () => {
	scrollPositions[window.location.href] = window.scrollY;
});

swup.on('popState', () => {
	scrollToSavedPosition = true;
});

swup.on('animationInStart', () => {
	if (scrollToSavedPosition)
		window.scrollTo(0, scrollPositions[window.location.href]);
	scrollToSavedPosition = false;
});

@aerni
Copy link

aerni commented Jul 16, 2019

@gmrchk I understand, but I'm not sure if I understand it right. Even when I set animateHistoryBrowsing to false the native browser scroll behavior doesn't work. When scrolling on a page and then clicking on a link to another page, the scroll of the previous page remains on the new page. Is this as intended? I figured I might have to install the Scroll Plugin. But when using this plugin, the page will always scroll to the top and native browser scroll history doesn't work either.

@franboud
Copy link

franboud commented Apr 28, 2020

The code shown in this thread doesn't work well with the "Next" button. I'm on Windows/Chrome. This code works better for me. I'm animating with GSAP.

// SCROLL BEHAVIOR .....................................................
// Click link = scroll to top
// Back / next buttons = scroll where the user was
this.scrollValues = {};
this.currentPage = window.location.href;

// triggers when link is clicked
swup.on('clickLink', (event) => {
    this.scrollBehavior = 'top';
});

// triggers on popstate events (back/forward button)
swup.on('popState', (event) => {
    this.scrollBehavior = 'keep';
});

// triggers when animation OUT starts (class is-animating is added to html tag)
swup.on('animationOutStart', (event) => {
    this.scrollValues[this.currentPage] = window.scrollY;
});

// triggers right after the content of page is replaced
swup.on('contentReplaced', (event) => {
    this.currentPage = window.location.href;

    if (this.scrollBehavior == 'keep' && typeof this.scrollValues[window.location.href] !== 'undefined') {
        gsap.to(window, { duration: 0.2, scrollTo: this.scrollValues[window.location.href] });
    } else {
        gsap.to(window, { duration: 0.2, scrollTo: 0 });
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants