Keep Scroll Position on Reload in WordPress

When you style a section far down a WordPress page, every reload throws you back to the top and you scroll down again to see the change. Browsers try to keep the position, but animations, lazy-loaded images and page builders often break that.

This snippet saves the scroll position when the page is left and restores it after a reload. It is safe on a live site: a normal visit or a click on a link always starts at the top as usual. It is also handy with the Scripts Organizer preview, which reloads after every save.

The snippet

(function () {
	var key = 'dpcScroll:' + location.pathname + location.search;
	var storage;

	try {
		storage = window.sessionStorage;
		storage.getItem(key);
	} catch (e) {
		return; // Storage blocked: do nothing.
	}

	var nav = window.performance && performance.getEntriesByType ? performance.getEntriesByType('navigation')[0] : null;
	var saved = parseInt(storage.getItem(key), 10);
	storage.removeItem(key);

	// Restore only after a reload, once images and fonts have loaded.
	if (nav && nav.type === 'reload' && saved > 0) {
		var restore = function () {
			window.scrollTo(0, saved);
		};
		if (document.readyState === 'complete') {
			restore();
		} else {
			window.addEventListener('load', restore, { once: true });
		}
	}

	// Remember the position when the page is left or reloaded.
	window.addEventListener('pagehide', function () {
		try {
			storage.setItem(key, String(Math.round(window.scrollY)));
		} catch (e) {}
	});
})();

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Conditions, tick Footer under Script location, set Footer Mode to Javascript, paste the code into the footer editor and publish. Set Template to All, or better to Page, Post and pick the page you are working on, so the script only loads there.

Scripts Organizer prints header and footer code with the Conditions trigger on the front end, not with Everywhere. It wraps the code in a <script> tag itself, so paste it without one.

How it works

  • On pagehide (leaving or reloading the page) the script stores window.scrollY in sessionStorage, under a key that includes the page path. Each page remembers its own position, and the value is gone when the tab is closed.
  • On the next load it reads and removes the value. It scrolls back only when the Navigation Timing type is reload, so links and the back button behave normally.
  • It restores the position on the load event, after images and fonts have loaded and the page has its final height.
  • It adds an event listener instead of overwriting window.onbeforeunload, so other scripts that use that handler keep working. pagehide also fires on mobile Safari, where beforeunload often does not.
  • If storage is blocked (some privacy modes), the script does nothing.

Customise it

  • Restore on every visit, not only on reload: remove nav.type === 'reload' && from the condition.
  • Remember the position per tab across different query strings: remove + location.search from the key.

Gotchas

  • Smooth scrolling set in CSS (scroll-behavior: smooth) makes the jump animated. Use window.scrollTo({ top: saved, behavior: 'instant' }) if that bothers you.
  • Scroll-triggered animations above the restored position may not have run yet; that is a side effect of landing mid-page.
  • For more scroll helpers see Add and Remove a Class on Scroll in WordPress.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.