When updating a fragment you may control how Unpoly scrolls the page by
setting an [up-scroll] attribute,
or by passing a { scroll } option.
When navigating, Unpoly will try a sequence of scroll strategies that works for most cases:
#hash, scroll to a fragment matching that hash.You may configure this sequence in up.fragment.config.autoScroll.
Because following links and submitting forms are considered navigation, is the default scrolling strategy is applied automatically.
You can explicitly enable the strategy by setting [up-scroll="auto"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdetails" up-follow up-scroll="auto">Show more</a> <!-- mark: up-scroll="auto" -->
To apply the default scrolling strategy from JavaScripts, pass { scroll: 'auto' }:
up.render({ url: '/path', scroll: 'auto' }) // mark: scroll: 'auto'
We often want to draw attention to updated elements, by making sure they are visible in the viewport. This is called revealing an element.
Tip
When revealing an element, consider also focusing it to set the reading position for screen readers.
To scroll an updated fragment, set [up-scroll="target"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdetails" up-follow up-scroll="target">Show more</a> <!-- mark: up-scroll="target" -->
The fragment's viewport will be scrolled so the new fragment is visible.
You can control how far to scroll, how to handle large elements, or apply a scroll margin.
See tuning the scroll behavior for details.
To reveal a matching element, set [up-scroll] to any CSS selector string:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdetails" up-follow up-scroll="h1">Show more</a> <!-- mark: up-scroll="h1" -->
The selector can to match anywhere in the page, even outside the updated region.
From JavaScript you may also pass the Element object that should be revealed:
up.render({ url: '/path', scroll: document.body })
#hash targetTo focus the element matching the #hash in the updated URL, set [up-scroll="hash"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fproduct%2F12%23features" up-follow up-scroll="hash">Show features</a> <!-- mark: #features -->
To reveal the container element of the updated layer, set [up-scroll="layer"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdetails" up-layer="new" up-scroll="layer">Show more</a> <!-- mark: up-scroll="layer" -->
Most layer modes bring their own scrollbar, and will effectively be scrolled to the top.
A popup has no scrollbar, so its parent layer will scroll to reveal the popup frame.
To reveal the updated layer's main element, set [up-scroll="main"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fcontact" up-follow up-scroll="main">Contact us</a> <!-- mark: up-scroll="main" -->
The reset scroll positions to the top, set [up-scroll="top"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Flist" up-follow up-scroll="top">Back to list</a> <!-- mark: up-scroll="top" -->
This affects all viewports that are ancestors or descendants of the updated fragment.
The scroll to the bottom, set [up-scroll="bottom"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fchat" up-follow up-scroll="bottom">Open chat</a> <!-- mark: up-scroll="bottom" -->
This affects all viewports that are ancestors or descendants of the updated fragment.
To scroll a specific pixel position from the top, set a number value:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Flist" up-follow up-scroll="35">Back to list</a> <!-- mark: up-scroll="35" -->
To scroll to the bottom, but leave a margin of some pixels, set a negative number value:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fmessages" up-follow up-scroll="-40">Latest messages</a> <!-- mark: up-scroll="-40" -->
If you don't want Unpoly to touch any scroll positions, set [up-scroll=false].
For example, if you don't want to use the default scrolling strategy
when navigating, you can disable it like so:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdetails" up-follow up-scroll="false">Show more</a> <!-- mark: up-scroll="false" -->
When rendering without navigation, no scrolling will happen by default.
Note
DOM mutations may still cause scrolling, even when Unpoly does not touch scroll positions.
Set[up-scroll="keep"]to force the preservation of scroll positions.
Scroll positions will reset when you insert a new viewport element (as opposed to updating a child element). This is default browser behavior for newly inserted elements.
You can ask Unpoly to preserve the scroll positions of all viewports around the updated fragment.
To do so, set [up-scroll="keep"]:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Flist" up-follow up-scroll="keep">Reload list</a> <!-- mark: up-scroll="keep" -->
Internally, Unpoly will measure scroll positions before the update, and restore the same positions after the update.
For this to work, viewports must have a derivable target selector,
e.g. by setting an [id] attribute.
When revisiting an earlier page, you may want to restore the previous scroll position.
Set [up-scroll="restore"] to restore the last known scroll positions for the updated URL:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Flist" up-follow up-scroll="restore">Back to list</a> <!-- mark: up-scroll="restore" -->
Before a fragment update, Unpoly will save the scroll position for the current URL.
You can prevent this by setting [up-save-scroll="false"].
To attempt multiple scroll strategies, separate options with a comma:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fpath%23section" up-follow up-scroll="hash, top">Link label</a> <!-- mark: up-scroll"hash, top" -->
This would first try to reveal an element matching the URL's #hash.
If the URL has no hash (or if no element matches), the viewport will be scrolled to the top.
In JavaScript you can pass an array of options:
up.render({ url: '/path#section', scroll: ['hash', 'top'] }) // mark: scroll: ['hash', 'top']
When updating multiple fragments, any [up-scroll] attribute (or { scroll } option)
will only be applied to the first fragment. This is the behavior desired when you only have a single viewport,
and you don't want secondary fragments to influence the one scroll bar.
However, when you update fragments within multiple viewports, you can only scroll once that way:
<a href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdashboard" up-target="#fragment1, #fragment2" up-scroll="bottom"> <!-- mark: up-target="#left, #right" -->
Update fragments
</a>
<div id="viewport1" up-viewport style="overflow-y: scroll">
<!-- chip: ✔ Will be scrolled -->
<div id="fragment1">…</div>
</div>
<div id="viewport2" up-viewport style="overflow-y: scroll">
<!-- chip: ❌ Will not be scrolled -->
<div id="fragment2">…</div>
</div>
To scroll multiple viewports, set an [up-scroll-map] attribute.
Its value is a relaxed JSON object mapping selectors to scroll options:
<a
href="/?originalUrl=https%3A%2F%2Funpoly.com%2Fdashboard"
up-target="#fragment1, fragment2"
up-scroll-map="{ '#viewport1': 'bottom', '#viewport2': 'bottom' }"
>
Update fragments
</a>
You can also select viewports by any contained fragment. The following two scroll maps have the same effect:
<a … up-scroll-map="{ '#fragment1': 'bottom', '#fragment2': 'bottom' }">…</a>
<a … up-scroll-map="{ '#viewport1': 'bottom', '#viewport2': 'bottom' }">…</a>
In JavaScript you can pass an object as a { scrollMap } option:
up.render({
url: '/dashboard',
target: '#fragment1, #fragment2',
scrollMap: { '#viewport1': 'bottom', '#viewport2': 'bottom' }
})
To only scroll when a main target is updated,
you may append -if-main to any of the string options in this list.
E.g. { scroll: 'top-if-main' } will reset scroll positions, but only if a main target is updated.
To implement other conditions, pass a function instead.
To implement your custom scrolling logic, pass a function as { scroll } option.
The function will be called with the updated fragment and is expected to either:
Here is a scrolling function that scrolls to 15 pixels from the top:
up.render({
target: '#target',
url: '/path',
scroll: (fragment) => document.documentElement.scrollTop = 15 // mark-line
})
You can control how to handle large elements, apply a scroll margin or snap to screen edges.
See tuning the scroll behavior for details.