๐Ÿ“œ A tiny custom element for all your scrollytelling needs!

Overview

<scroll-scene> element

A tiny custom element for all your scrollytelling needs! The successor to @newswire/scroller.

Key features

  • ๐Ÿœ Less than 700 bytes brotli'ed, less than 800 bytes gzip'ed
  • ๐Ÿ‘€ Uses a highly-performant Intersection Observer to monitor scrolling changes
  • ๐Ÿ“š Smartly uses scroll events to calculate scroll progress only when needed
  • ๐ŸŒป Each <scroll-scene> element may have its own offset and opt-in to progress events
  • ๐Ÿ™…๐Ÿฝโ€ No dependencies

Examples

Installation

npm install scroll-scene-element
// or
yarn add scroll-scene-element
// or
pnpm add scroll-scene-element

Usage

In your HTML add <scroll-scene> container elements around every "scene" you want to track the progression of in your interactive. Feel free to use these elements as the containers of your graphics or other dynamic content and style them as needed - all progression and scroll depth changes will be tracked on the tag and its contents.

<div class="scrollytelling-container">
	<scroll-scene>
		<h2>Scene 1</h2>
		<p>This is the first scene.</p>
	</scroll-scene>
	<scroll-scene>
		<h2>Scene 2</h2>
		<p>This is the first scene.</p>
	</scroll-scene>
	<scroll-scene>
		<h2>Scene 3</h2>
		<p>This is the first scene.</p>
	</scroll-scene>
</div>

Then import the script as an ES module in your bundle or load via a script tag to upgrade the <scroll-scene> elements:

import 'scroll-scene-element';

or

<script src="https://unpkg.com/scroll-scene-element/dist/scroll-scene-element.js" type="module"></script>

If you have experience with Scrollama or @newswire/scroller it may be surprising that there's no "init" step. Thanks to custom elements the initalization happens automatically just by using <scroll-scene>.

Events with <scroll-scene> work just like others in JavaScript giving you the same amount of flexibility. (And familiarity!) scroll-scene-enter, scroll-scene-exit and scroll-scene-progress all bubble up to document. If you know there will only be a single set of <scroll-scene> elements on a page you may listen on document directly:

document.addEventListener('scroll-scene-enter', (event) => {
	// "event" is a CustomEvent, giving it has a `detail` property
	const detail = event.detail;

	// the triggering element
	const element = event.element;

	// just like in standard DOM events, "target" is also the triggering element
	const target = event.target;

	// the bounds of the triggering element
	const bounds = detail.bounds;

	// whether the page was scrolling up or down when the event was triggered
	const isScrollingDown = detail.isScrollingDown;

	// the offset used for this element
	const offset = detail.offset;
});

But what if you have more than one set of <scroll-scene> elements on a page? You might instead attach your listener to a parent element of each set of <scroll-scene> elements:

const container = document.querySelector('.scrollytelling-container');

container.addEventListener('scroll-scene-enter', (event) => {
	// no need to allow it to bubble up to `document`
	event.stopPropagation();

	// "event" is a CustomEvent, giving it has a `detail` property
	const detail = event.detail;

	// ...
});

And finally - you may attach your listener to each <scroll-scene> element individually:

const scenes = document.querySelectorAll('scroll-scene');

scenes.forEach((scene) => {
	scene.addEventListener('scroll-scene-enter', (event) => {
		// no need to allow it to bubble up to `document`
		event.stopPropagation();

		// "event" is a CustomEvent, giving it has a `detail` property
		const detail = event.detail;

		// ...
	});
});

Maybe you only need to know the first time a scene enters (or exits) the viewport? You can use the native once option with addEventListener:

const scene = document.querySelector('scroll-scene');

scene.addEventListener(
	'scroll-scene-enter',
	() => {
		console.log('Entered!');
	},
	{ once: true },
);

scene.addEventListener(
	'scroll-scene-exit',
	() => {
		console.log('Exited!');
	},
	{ once: true },
);

If a <scroll-scene> element opts-in to progress tracking, it will emit a scroll-scene-progress event when the progress changes once it enters and stop emitting the event once it exits. This event will bubble up to document:

<scroll-scene progress>
	<h2>Scene 1</h2>
	<p>This is the first scene.</p>
</scroll-scene>
const scene = document.querySelector('scroll-scene');

scene.addEventListener('scroll-scene-progress', (event) => {
	// "event" is a CustomEvent, giving it has a `detail` property
	const detail = event.detail;

	// the triggering element
	const element = event.element;

	// `event.target` is also the triggering element, just like in standard DOM events
	const target = event.target;

	// the bounds of the triggering element
	const bounds = detail.bounds;

	// the offset used for this element
	const offset = detail.offset;

	// the progress of the element from 0 to 1
	const progress = detail.progress;
});

Attributes

A <scroll-scene> element has two optional attributes:

  • offset: a number between 0 and 1 that determines how far from the top of the viewport the element must be before a scroll-scene-enter or scroll-scene-exit event is triggered. Defaults to 0.5.
  • progress: a boolean that determines whether the scroll-scene-progress event is triggered. Defaults to false.

These can be set on the <scroll-scene> element as an attribute or be passed in as a property.

<scroll-scene offset="0.75"></scroll-scene>
<scroll-scene progress></scroll-scene>
const scene = document.querySelector('scroll-scene');

// as properties
scene.offset = 0.75;
scene.progress = true;

// as attributes
scene.setAttribute('offset', '0.75');
scene.setAttribute('progress', '');

Events

Viewport events

scroll-scene-enter is emit when an element enters the viewport. scroll-scene-exit is emit when an element exits the viewport. Both events bubble up to document. They both have a detail property that contains the following:

  • bounds: the bounds (DOMRectReadOnly) of the triggering element as made available within the IntersectionObserver callback
  • element: the triggering element
  • isScrollingDown: whether the page was scrolling up or down when the event was triggered
  • offset: the offset used for this element

Progress event

scroll-scene-progress is emitted when the progress of an element changes. It bubbles up to document and has a detail property that contains the following:

  • bounds: the bounds (DOMRect) of the triggering element
  • element: the triggering element
  • offset: the offset used for this element
  • progress: the progress of the element from 0 to 1

License

MIT

You might also like...

JavaScript micro-library: pass in an element and a callback and this will trigger when you click anywhere other than the element

Add a click listener to fire a callback for everywhere on the window except your chosen element. Installation run npm install @lukeboyle/when-clicked-

May 13, 2021

Flight is a universal package manager for your needs, no matter what language you may want to write your code in.

Flight Swift, reliable, multi-language package manager. โšก Installation We don't have an official release of Flight yet, however, if you would like to

Dec 25, 2022

A quick and powerful plugin for your pull-to-refresh needs in your webapp.

A quick and powerful plugin for your pull-to-refresh needs in your webapp.

PulltoRefresh.js โ€ข Demos A small, but powerful Javascript library crafted to power your webapp's pull to refresh feature. No markup needed, highly cus

Jan 6, 2023

A tiny JavaScript library to easily toggle the state of any HTML element in any contexts, and create UI components in no time.

A tiny JavaScript library to easily toggle the state of any HTML element in any contexts, and create UI components in no time.

A tiny JavaScript library to easily toggle the state of any HTML element in any contexts, and create UI components in no time. Dropdown, navigation bu

Nov 25, 2022

A custom element for rendering stylable (light DOM) Markdown

Motivation There are many web components these days to render Markdown to HTML. Here are a few: zero-md marked-element โ€ฆand Iโ€™m sure many others H

Dec 20, 2022

A custom element that helps save alienated player API's to bring back their true inner HTMLMediaElement API

A custom element that helps save alienated player API's to bring back their true inner HTMLMediaElement API

Oct 14, 2022

little-planet Custom Element: interactive panorama viewer

little-planet This project is a Custom HTML Element (AKA Web Component) that renders an interactive view of a panoramic photo. Can be used with no J

Nov 25, 2022

A custom element that aims to make it easier to embed Spring '83 boards

spring-board element A custom element that makes it simple to embed Spring '83 boards! Usage If you are using spring-board in a client-side framew

Jan 1, 2023

An opinionated template for creating a custom element.

custom-element element An opinionated template for creating a custom element. Installation You can install custom-element with npm, Yarn or pnpm.

Jul 29, 2022
Comments
  • Progress values incorrect when changing offset

    Progress values incorrect when changing offset

    Using this as an example:

    https://rdmurphy.github.io/scroll-scene-element/progress

    If you change the offset from the default 0.5, for example to 0.75 the progress values are incorrect.

    opened by smellydelli 5
  • Add support for progress events

    Add support for progress events

    This should be an opt-in feature that is expected to be flagged on every instance of scroll-scene that wants them to activate.

    <scroll-scene progress></scroll-scene>
    <!-- may be better to use data attributes -->
    <scroll-scene data-progress></scroll-scene>
    

    This flag would cause an instance of scroll-scene to interface with the global observer and set up a scroll listener for calculating scroll depth for that element once it enters the view and removes the listener when it exits the view.

    The user would still be expected to set up their own listener for this event to react to it.

    document.addEventListener('scroll-scene-progress', ({ detail }) => {
      const element = detail.element;
     const progress = detail.progress;
      // do what you need to do
    });
    opened by rdmurphy 1
  • isScrollingDown in progress event listener

    isScrollingDown in progress event listener

    Good morning and thank you very much for this plugin. I was wondering if its possible to detect isScrollingDown boolean from inside the 'scroll-scene-progress' eventListener.

    scenesContainer.addEventListener('scroll-scene-progress', ({ detail }) => { const { progress } = detail; const percent=(progress * 100).toFixed(1) const index = scenes.indexOf(detail.element); const isScrollingDown = detail.isScrollingDown; // its undefined }

    Or in contrary, (which is much more suitable for my needs) is it possible to access the progress value in the'scroll-scene-enter' eventListener? scenesContainer.addEventListener('scroll-scene-enter', ({ detail }) => { const index = scenes.indexOf(detail.element); const indice=index + 1 const isScrollingDown = detail.isScrollingDown; const { progress } = detail; const percent=(progress * 100).toFixed(1) console.log(percent) // its undefined }

    Once again thank you very much, Sali

    opened by Salitehkat 0
  • Address setter compatibility with frameworks

    Address setter compatibility with frameworks

    I thought I did everything correctly with the custom element property setters but Svelte quickly informed me that I had in fact not.

    I think the issue here is that Svelte has special handling for custom elements โ€” if it sees that an attribute has a matching property it will pass it in as a property instead of an attribute. This is confusing because it does not do this for native DOM elements. ยฏ\(ใƒ„)/ยฏ

    But anyway, this is probably a real bug with how I implemented the setter for offset. It should be possible to "unset" it by doing scene.offset = null but that crashes and burns right now.

    enhancement 
    opened by rdmurphy 2
Releases(v0.1.1)
  • v0.1.1(Apr 29, 2022)

    What's Changed

    • Dynamically setting the offset property on a ScrollSceneElement will now correctly unset any observers created using the default offset value of 0.5.
    • Elements with progress and an offset that is not 0.5 will now report the correct progress value in relation to the offset trigger. Before this fix it was inverted.

    Full Changelog: https://github.com/rdmurphy/scroll-scene-element/compare/v0.1.0...v0.1.1

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Mar 22, 2022)

Owner
Ryan Murphy
Now @themarshallproject, previously @datadesk + @texastribune
Ryan Murphy
Hemsida fรถr personer i Sverige som kan och vill erbjuda boende till mรคnniskor pรฅ flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo fรถr kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
All the Fake Data for All Your Real Needs ๐Ÿ™‚

All the Fake Data for All Your Real Needs ?? Run it on Stackblitz Installation npm i @ngneat/falso yarn add @ngneat/falso Methods rand<T>(arr: T[]):

ngneat 2.8k Dec 29, 2022
This experimental library patches the global custom elements registry to allow re-defining or reload a custom element.

Redefine Custom Elements This experimental library patches the global custom elements registry to allow re-defining a custom element. Based on the spe

Caridy Patiรฑo 21 Dec 11, 2022
Kuldeep 2 Jun 21, 2022
Short JavaScript code snippets for all your development needs

30 seconds of code Short JavaScript code snippets for all your development needs Visit our website to view our snippet collection. Use the Search page

30 seconds 106.1k Dec 30, 2022
Short CSS code snippets for all your development needs

30 seconds of CSS Short CSS code snippets for all your development needs Visit our website to view our snippet collection. Use the Search page to find

30 seconds 15.9k Jan 3, 2023
P.S Its easy is a website to cater to all your PS allotment needs

P.S. It's Easy All-in-one Web App for all your Practice School Allotment needs! Note: Developers trying to fork and test. Please wait, we'll set up a

Tanya Prasad 33 Sep 26, 2022
Short JavaScript code snippets for all your development needs

30 seconds of code Short JavaScript code snippets for all your development needs Visit our website to view our snippet collection. Use the Search page

30 seconds of code 106.1k Dec 28, 2022
Tenzi is a dice game. The player needs to roll dice until they are all the same. Clicking on a dice, freezes it at its current value between rolls. Best scores are saved to local storage.

Roll until all dice are the same Try me! Technologies Used Description Tenzi is a dice game used to demonstrate the use of React Hooks (useState, useE

Michael Kolesidis 7 Nov 23, 2022