A small, fast, no-dependency, horizontal pager.
https://localnerve.github.io/ui-elements/dist/horizontal-pager/
- Horizontal touch or exposed methods move to next/prev pages.
- Interface allows animated moves by relative distance, or absolute index.
- Initial render at any page.
- Uses
requestAnimationFrame
aligned (decoupled) animations. - Does not interfere with other vertical/complex page interactions.
- Tracks finger when down, then ease-out.
- Optional continuous scroll (last wraps to first).
- Edge resistance (for when continuous is disabled).
- Minimal DOM update approach.
- Passive event listeners.
- When navigation certain to complete, calls optional
willComplete
callback. - Optional
done
callback for notification after navigation complete. - A css class identifies scroll level items (pages).
- ~11k min bundle, ~3k gzip.
- No touch velocity considerations.
To support older browsers, the following polyfills must be supplied by you (polyfills are not supplied with this lib):
- Promise
- requestAnimationFrame/cancelAnimationFrame
- Object.assign
- Math.trunc
- Array.includes
For example, here is the v3 polyfill.io tag required prior to loading/parsing this library:
<script src="https://polyfill.io/v3/polyfill.min.js?features=Promise,requestAnimationFrame,Object.assign,Math.trunc,Array.prototype.includes"></script>
Since this uses requestAnimationFrame, you may benefit from knowing that RAF will be suspended by browsers under some conditions. There is an answer here that sheds some good light on this. For example, I discovered via this project's unit tests, that firefox 54 (linux) disables RAF when a "page" (an element identified by options.targetClass
) has been scrolled beyond viewport visibility.
Returns an API to a horizontal-pager instance, sets the instance options, starts event listening, and renders initial styles on the elements found by the given options.targetClass
.
targetClass
is the only required option.
Requires a global document
to be available when called.
Option Name | Data Type | Description |
---|---|---|
targetClass |
String | The class that identifies the scroll targets (pages in the horizontal-pager). Must be supplied. Scroll targets (pages) must be sibling elements. |
[startIndex] |
Number | Which scroll target to show initially. Optional, defaults to 0, the first element returned from querySelectorAll on targetClass. |
[continuous] |
Boolean | True allows scroll to wrap around the ends. Optional, defaults to false. |
[willComplete] |
Function | A function to call when a scroll will complete shortly (called before completion). For touch, called when scrollThreshold is surpassed and touchend is fired. The function will receive a moveResult object. |
[scrollThreshold] |
Number | Less than 1, the percentage of width beyond which a touch will cause a complete scroll to the next page. Optional, defaults to 0.35 (35 percent). |
[doneThreshold] |
Number | The translateX pixel value below which to stop animations. Optional, defaults to 1 (Will not animate below 1px). |
[addParentStyles] |
Boolean | False disables the automatic adding of styles to the parent of the targetClass elements. Defaults to true. If disabled, you must supply the parent styles. |
[done] |
Function | A function to call after a scroll has completed. Optional. Redundant if Instance API used. The function will receive a moveResult object. |
Moves to the next target as identified by targetClass
. If the current target is the end, nothing happens. No arguments. Returns a Promise that resolves after the move has completed. The resolver receives a moveResult object on success, or null if the animation could not be executed because one is already in progress.
Moves to the previous target as identified by targetClass
. If the current target is the beginning, nothing happens. No arguments. Returns a Promise that resolves after the move has completed. The resolver receives a moveResult object on success, or null if the animation could not be executed because one is already in progress.
Moves distance
targets away from the current targetClass
. If the specified distance would move out of bounds, nothing happens. A distance
of -1 is a synonym for prev
. If continuous mode, then absolute value of the distance cannot exceed the number of scroll targets. Returns a Promise that resolves after the move has completed. The resolver receives a moveResult object on success, or null if the animation could not be executed because one is already in progress or the argument is not acceptable.
Moves to the targetClass
at the zero-based index. If an out of bounds or current index is specified, nothing happens. Returns a Promise that resolves after the move has completed. The resolver receives a moveResult object on success, or null if the animation could not be executed because one is already in progress or the argument is not acceptable.
Returns the number of targetClass
items found by the horizontal-pager.
Returns the index of the current target.
Returns the index of the previous target.
Stops event listening and any pending animations. Requires a global document
to be available when called. No arguments, no return.
The move animation commands, next
, prev
, moveRel
, and moveAbs
return a Promise that resolves to null on failure, or a moveResult
object on success. The move result object contains the following properties:
// moveResult
{
currTargetIndex, // The new, current target index
prevTargetIndex, // The previous target index
distance // The distance moved, eg: -1 for one back, 2 for two forward
}
Here are the styles automatically added to the parent element of the targetClass
scroll target siblings (the pages), unless the addParentStyles
option is set to false:
{
position: relative;
width: 100%;
overflow-x: hidden;
}
If you disable addParentStyles
, you must supply at least these styles to a container element of the pages identified by targetClass
for horizontal-pager to work.
See DOMContentLoaded
, unload
event handlers in the example.
Example markup and styles are supplied in the example.
Deliver the horizontal-pager.js
script with your page. On the server, you will want to deliver the startIndex
to the client to initially render the proper targetClass
page for the current route. Also, you need to initially render the markup for the targetClass
elements (the pages) and their common parent, but not the content of the pages (unless they need to be visible immediately). On the client, give the startIndex
and targetClass
options to the top-level api function createHorizontalPager
prior to the first client render. Also, you should use the willComplete
callback to get the page content if it is not already rendered on the client.
- Call the top-level api function
createHorizontalPager
(the default export), and give it the options. This starts listening to events, and returns an interface to the horizontal-pager instance.
- Requires a global
document
to be available.- In
React
, a good place to do this is incomponentDidMount
.
- In
- When you're done, call
destroy
on the horizontal-pager instance to stop animations and events. + Requires a globaldocument
to be available.- In
React
, a good place to do this is incomponentWillUnmount
.
- In