@newswire/scroller
@newswire/scroller
is a super-tiny library for your scrollytelling needs.
Key features
Examples
Installation
@newswire/scroller
is available via npm
.
npm install @newswire/scroller
You can also use it directly via unpkg.com.
<script src="https://unpkg.com/@newswire/scroller/dist/index.umd.js"></script>
You can also import it as a module via unpkg!
<script type="module">
import Scroller from 'https://unpkg.com/@newswire/scroller/dist/index.mjs';
const scroller = new Scroller({ selector: '.scene' });
</script>
Usage
Assume for the following examples that our HTML is as follows:
<div class="container">
<div class="scene"></div>
<div class="scene"></div>
<div class="scene"></div>
<div class="scene"></div>
<div class="scene"></div>
</div>
To begin tracking the progression of the scenes, we need to set up our Scroller
.
import Scroller from '@newswire/scroller';
const scroller = new Scroller({
scenes: document.querySelectorAll('.scene'),
});
scroller.on('scene:enter', d => {
d.element.classList.add('active');
});
scroller.on('scene:exit', d => {
d.element.classList.remove('active');
});
scroller.on('init', () => {
console.log('Everything is ready to go!');
});
scroller.init();
If you need to additionally track the "container" of your scenes, Scroller
can track and alert you about that too.
import Scroller from '@newswire/scroller';
const scroller = new Scroller({
container: document.querySelector('.container'),
scenes: document.querySelectorAll('.scene'),
});
scroller.on('scene:enter', d => {
d.element.classList.add('active');
});
scroller.on('scene:exit', d => {
d.element.classList.remove('active');
});
scroller.on('container:enter', d => {
console.log("Let's go!");
});
scroller.on('container:exit', d => {
console.log('We are done here.');
});
scroller.init();
Known quirks
iOS Safari
Due to how iOS Safari does not support Intersection Observer (yet), its calculation of distance from the top/bottom of the page is a little funky due to the disappearing/reappearing bars as you scroll. Practically this won't matter (it's triggering in a consistent way, but not exactly how you'd expect), but it may drive you mad if you're looking at the examples and are baffled as to why it's not synced up perfectly.
Intersection Observer pre-check
This is less a quirk (and in some ways a feature) and more how Intersection Observer works. Whenever an element gets added to an Intersection Observer instance, it is immediately checked for intersection. In the context of Scroller
, this means that if it is instantiated on load of a page and none of its elements are currently intersecting, scene:exit
(and possibly container:exit
if you provided one) are going to all fire as they fail that initial check. The good thing is this is also true for the *:enter
events, so nothing special is necessary for detecting if someone loads in the middle of your interactive. Make sure the code in your event listeners are prepared for this and have some way to determine whether anything in your *:exit
listeners are needed yet!
Intersection Observer polyfill?
To keep the library lean, @newswire/scroller
intentionally does not attempt to polyfill IntersectionObserver
and leaves that task up to the user if necessary. Browser support is actually pretty good! But our friends using Safari (both desktop and iOS) still need a polyfill for @newswire/scroller
to work. (Good news! They're working on it.)
There are a few good ways to ensure the polyfill is in place.
Load it within your bundle
The spec-based Intersection Observer polyfill is available on npm
.
npm install intersection-observer
Once that is installed, you need to make sure that it is loaded before any code that'd depends on it will run. The polyfill is smart - it won't affect browsers who already have support!
import 'intersection-observer';
require('intersection-observer');
Before loading your scripts, you can include a link to polyfill.io
. This service uses signals from the browser to determine what polyfills are needed and loads them in the environment. You can set flags on the URL to limit what polyfill.io
attempts to load.
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<script src="<your-code>"></script>
This is similar to the polyfill.io
method, but without a service in-between determining whether it is necessary. (Every browser will get the code! But it still checks if the browser supports IntersectionObserver
before activating.)
<script src="https://unpkg.com/intersection-observer/intersection-observer"></script>
<script src="<your-code>"></script>
API
Table of Contents
Scroller
Uses Intersection Observer to monitor the page location of a series of
elements for scrollytelling.
Parameters
options
object
options.container
Element? Optionally pass in what should be
considered the containing element of all the scenes - this gets added to the
Intersection Observer instance and additionally fires its own events
options.offset
Number? How far from the top/bottom of the viewable
area to trigger enters/exits of scenes, represented as a value between
0 and 1 (optional, default 0.5
)
options.scenes
Array<Element> An array of all the Elements to be
considered scenes of this Scroller
Properties
observer
(IntersectionObserver | null) Once initialized, a reference
to the Scroller's instance of IntersectionObserver
Examples
import Scroller from '@newswire/scroller';
const scroller = new Scroller({
scenes: document.querySelectorAll('.scenes'),
});
scroller.init();
on
Adds a callback to the queue of a given event listener.
Parameters
type
string Name of the event
handler
Function Callback function added to the listener
Examples
const scroller = new Scroller({
scenes: document.querySelectorAll('.scenes')
});
const fn = (...) => {...};
scroller.on('scene:enter', fn);
Returns void
off
Removes a callback from the queue of a given event listener.
Parameters
type
string Name of the event
handler
Function Callback function removed from the listener
Examples
const scroller = new Scroller({
scenes: document.querySelectorAll('.scenes')
});
const fn = (...) => {...};
scroller.on('scene:enter', fn);
scroller.off('scene:enter', fn);
Returns void
init
Initializes a Scroller's IntersectionObserver on a page and begins sending
any intersection events that occur.
Examples
const scroller = new Scroller({
scenes: document.querySelectorAll('.scenes'),
});
scroller.init();
Returns void
Scroller#container:enter
Container enter event. Fires whenever the container begins intersecting.
Type: object
Properties
bounds
DOMRectReadOnly The bounds of the active element
element
Element The element that intersected
index
number This is always -1 on the container
isScrollingDown
boolean Whether the user triggered this element
while scrolling down or not
Scroller#scene:enter
Scene enter event. Fires whenever a scene begins intersecting.
Type: object
Properties
bounds
DOMRectReadOnly The bounds of the active element
element
Element The element that intersected
index
number The index of the active element
isScrollingDown
boolean Whether the user triggered this element
while scrolling down or not
Scroller#container:exit
Container exit event. Fires whenever the container has exited.
Type: object
Properties
bounds
DOMRectReadOnly The bounds of the exiting element
element
Element The element that exited
index
number This is always -1 on the container
isScrollingDown
boolean Whether the user triggering the exit
while scrolling down or not
Scroller#scene:exit
Scene enter event. Fires whenever a scene has exited.
Type: object
Properties
bounds
DOMRectReadOnly The bounds of the exiting element
element
Element The element that exited
index
number The index of the exiting element
isScrollingDown
boolean Whether the user triggering the exit
while scrolling down or not
Scroller#init
Init event. Fires once Scroller has finished setting up.
License
MIT