{"id":13630161,"url":"https://github.com/rdmurphy/scroller","last_synced_at":"2025-04-12T18:54:27.490Z","repository":{"id":34067557,"uuid":"166301886","full_name":"rdmurphy/scroller","owner":"rdmurphy","description":"📜 A super-tiny library for your scrollytelling needs.","archived":false,"fork":false,"pushed_at":"2025-03-10T21:07:41.000Z","size":2014,"stargazers_count":180,"open_issues_count":2,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-03T20:13:16.256Z","etag":null,"topics":["intersection-observer","scroller","scrollytelling"],"latest_commit_sha":null,"homepage":"https://rdmurphy.github.io/scroller/basic","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rdmurphy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-17T21:48:22.000Z","updated_at":"2025-03-10T21:07:45.000Z","dependencies_parsed_at":"2023-02-10T07:15:52.403Z","dependency_job_id":"538b98ad-fb30-4f2a-aa6d-7aa0dca196f7","html_url":"https://github.com/rdmurphy/scroller","commit_stats":{"total_commits":97,"total_committers":5,"mean_commits":19.4,"dds":0.6391752577319587,"last_synced_commit":"7d016d12ba783c918f555e9202f623500db35b64"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fscroller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fscroller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fscroller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fscroller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rdmurphy","download_url":"https://codeload.github.com/rdmurphy/scroller/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618262,"owners_count":21134200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["intersection-observer","scroller","scrollytelling"],"created_at":"2024-08-01T22:01:32.208Z","updated_at":"2025-04-12T18:54:27.449Z","avatar_url":"https://github.com/rdmurphy.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003e\n  @newswire/scroller\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.org/package/@newswire/scroller\"\u003e\u003cimg src=\"https://badgen.net/npm/v/@newswire/scroller\" alt=\"npm\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://unpkg.com/@newswire/scroller/dist/index.umd.js\"\u003e\u003cimg src=\"https://badgen.net/badgesize/gzip/https://unpkg.com/@newswire/scroller/dist/index.umd.js\" alt=\"gzip size\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://unpkg.com/@newswire/scroller/dist/index.umd.js\"\u003e\u003cimg src=\"https://badgen.net/badgesize/brotli/https://unpkg.com/@newswire/scroller/dist/index.umd.js\" alt=\"brotli size\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://packagephobia.now.sh/result?p=@newswire/scroller\"\u003e\u003cimg src=\"https://badgen.net/packagephobia/install/@newswire/scroller\" alt=\"install size\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n`@newswire/scroller` is a super-tiny library for your scrollytelling needs.\n\n## Heads up!\n\n`@newswire/scroller` will continue to work great, but I've written a semi-successor to it as a custom element called [`scroll-scene-element`](https://github.com/rdmurphy/scroll-scene-element) that I encourage you to check out! Both are great options for building out your scrollytelling projects.\n\n## Key features\n\n- 🐜 **Less than 600 bytes** gzipped\n- 👀 Uses a highly-performant **[Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)** to monitor scrolling changes\n- 🙅🏽‍ **No dependencies** (unless you need an [**Intersection Observer** polyfill](#intersection-observer-polyfill) - [check what browsers you support](https://caniuse.com/#feat=intersectionobserver)!)\n\n## Examples\n\n- [Basic](https://rdmurphy.github.io/scroller/basic)\n- [Sticky graphic](https://rdmurphy.github.io/scroller/sticky)\n\n## Installation\n\n`@newswire/scroller` is available via `npm`.\n\n```sh\nnpm install @newswire/scroller\n```\n\nYou can also use it directly via [unpkg.com](https://unpkg.com/).\n\n```html\n\u003cscript src=\"https://unpkg.com/@newswire/scroller/dist/index.umd.js\"\u003e\u003c/script\u003e\n\u003c!-- Now available at `window.Scroller` --\u003e\n```\n\nYou can also import it as a module via unpkg!\n\n```html\n\u003cscript type=\"module\"\u003e\n  import Scroller from 'https://unpkg.com/@newswire/scroller/dist/index.mjs';\n\n  const scroller = new Scroller({\n    scenes: document.querySelectorAll('.scene'),\n  });\n\u003c/script\u003e\n```\n\n## Usage\n\nAssume for the following examples that our HTML is as follows:\n\n```html\n\u003cdiv class=\"container\"\u003e\n  \u003cdiv class=\"scene\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"scene\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"scene\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"scene\"\u003e\u003c/div\u003e\n  \u003cdiv class=\"scene\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n```\n\nTo begin tracking the progression of the scenes, we need to set up our `Scroller`.\n\n```js\nimport Scroller from '@newswire/scroller';\n\n// sets up the scroller instance, pass in an array of all the scenes\nconst scroller = new Scroller({\n  scenes: document.querySelectorAll('.scene'),\n});\n\n// Scroller has a tiny event emitter embedded in it!\n\n// the `enter` event is triggered every time a scene crosses the threshold\nscroller.on('scene:enter', d =\u003e {\n  d.element.classList.add('active');\n});\n\n// the `exit` event is triggered every time a scene exits the threshold\nscroller.on('scene:exit', d =\u003e {\n  d.element.classList.remove('active');\n});\n\nscroller.on('init', () =\u003e {\n  console.log('Everything is ready to go!');\n});\n\n// starts up the IntersectionObserver\nscroller.init();\n```\n\nIf you need to additionally track the \"container\" of your scenes, `Scroller` can track and alert you about that too.\n\n```js\nimport Scroller from '@newswire/scroller';\n\n// sets up the scroller instance, pass in the container and an array of all the scenes\nconst scroller = new Scroller({\n  container: document.querySelector('.container'),\n  scenes: document.querySelectorAll('.scene'),\n});\n\n// the `enter` event works as you expect with scenes...\nscroller.on('scene:enter', d =\u003e {\n  d.element.classList.add('active');\n});\n\nscroller.on('scene:exit', d =\u003e {\n  d.element.classList.remove('active');\n});\n\n// ...but if a container is passed, events fire for it as well!\nscroller.on('container:enter', d =\u003e {\n  console.log(\"Let's go!\");\n});\n\nscroller.on('container:exit', d =\u003e {\n  console.log('We are done here.');\n});\n\nscroller.init();\n```\n\n## Intersection Observer's pre-check\n\nThis is a minor quirk of Intersection Observer to keep in mind. 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`) is going to fire for each element 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!\n\n## Intersection Observer polyfill?\n\nTo 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 [pretty good](https://caniuse.com/#feat=intersectionobserver)! There's no hard and fast rule here - check what browsers your users are using and make a call.\n\nThere are a few good ways to ensure the polyfill is in place.\n\n### Load it within your bundle\n\nThe spec-based Intersection Observer polyfill is [available on `npm`](https://www.npmjs.com/package/intersection-observer).\n\n```sh\nnpm install intersection-observer\n```\n\nOnce 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!\n\n```js\nimport 'intersection-observer';\n// or\nrequire('intersection-observer');\n\n// your awesome code here!\n```\n\n### Load it with [`polyfill.io`](https://polyfill.io/v3/)\n\nBefore loading your scripts, you can include a link to [`polyfill.io`](https://polyfill.io/v3/). 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.\n\n```html\n\u003c!-- \"nomodule\" is used to prevent this from loading in modern browers, which almost certainly\nsupport IntersectionObserver --\u003e\n\u003cscript nomodule src=\"https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver\"\u003e\u003c/script\u003e\n\u003cscript src=\"\u003cyour-code\u003e\"\u003e\u003c/script\u003e\n```\n\n### Load it with [`unpkg.com`](https://unpkg.com)\n\nThis is similar to the `polyfill.io` method, but without a service in-between determining whether it is necessary.\n\n```html\n\u003c!-- \"nomodule\" is used to prevent this from loading in modern browers, which almost certainly\nsupport IntersectionObserver --\u003e\n\u003cscript nomodule src=\"https://unpkg.com/intersection-observer/intersection-observer\"\u003e\u003c/script\u003e\n\u003cscript src=\"\u003cyour-code\u003e\"\u003e\u003c/script\u003e\n```\n\n## API\n\n\u003c!-- Generated by documentation.js. Update this documentation by updating the source code. --\u003e\n\n#### Table of Contents\n\n- [Scroller](#scroller)\n  - [Parameters](#parameters)\n  - [Properties](#properties)\n  - [Examples](#examples)\n  - [on](#on)\n    - [Parameters](#parameters-1)\n    - [Examples](#examples-1)\n  - [off](#off)\n    - [Parameters](#parameters-2)\n    - [Examples](#examples-2)\n  - [init](#init)\n    - [Examples](#examples-3)\n- [Scroller#container:enter](#scrollercontainerenter)\n  - [Properties](#properties-1)\n- [Scroller#scene:enter](#scrollersceneenter)\n  - [Properties](#properties-2)\n- [Scroller#container:exit](#scrollercontainerexit)\n  - [Properties](#properties-3)\n- [Scroller#scene:exit](#scrollersceneexit)\n  - [Properties](#properties-4)\n- [Scroller#init](#scrollerinit)\n\n### Scroller\n\nUses Intersection Observer to monitor the page location of a series of\nelements for scrollytelling.\n\n#### Parameters\n\n- `options` **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**\n  - `options.container` **[Element](https://developer.mozilla.org/docs/Web/API/Element)?** Optionally pass in what should be\n    considered the containing element of all the scenes - this gets added to the\n    Intersection Observer instance and additionally fires its own events\n  - `options.offset` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** How far from the top/bottom of the viewable\n    area to trigger enters/exits of scenes, represented as a value between\n    0 and 1 (optional, default `0.5`)\n  - `options.scenes` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\u0026lt;[Element](https://developer.mozilla.org/docs/Web/API/Element)\u003e** An array of all the Elements to be\n    considered scenes of this Scroller\n\n#### Properties\n\n- `observer` **(IntersectionObserver | null)** Once initialized, a reference\n  to the Scroller's instance of IntersectionObserver\n\n#### Examples\n\n```javascript\nimport Scroller from '@newswire/scroller';\n\nconst scroller = new Scroller({\n  scenes: document.querySelectorAll('.scenes'),\n});\n\nscroller.init();\n```\n\n#### on\n\nAdds a callback to the queue of a given event listener.\n\n##### Parameters\n\n- `type` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Name of the event\n- `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Callback function added to the listener\n\n##### Examples\n\n```javascript\nconst scroller = new Scroller({\n  scenes: document.querySelectorAll('.scenes')\n});\n\nconst fn = (...) =\u003e {...};\n\n// adds callback to listener\nscroller.on('scene:enter', fn);\n```\n\nReturns **void**\n\n#### off\n\nRemoves a callback from the queue of a given event listener.\n\n##### Parameters\n\n- `type` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Name of the event\n- `handler` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** Callback function removed from the listener\n\n##### Examples\n\n```javascript\nconst scroller = new Scroller({\n  scenes: document.querySelectorAll('.scenes')\n});\n\nconst fn = (...) =\u003e {...};\n\n// adds callback to listener\nscroller.on('scene:enter', fn);\n\n// removes callback from listener\nscroller.off('scene:enter', fn);\n```\n\nReturns **void**\n\n#### init\n\nInitializes a Scroller's IntersectionObserver on a page and begins sending\nany intersection events that occur.\n\n##### Examples\n\n```javascript\nconst scroller = new Scroller({\n  scenes: document.querySelectorAll('.scenes'),\n});\n\nscroller.init();\n```\n\nReturns **void**\n\n### Scroller#container:enter\n\nContainer enter event. Fires whenever the container begins intersecting.\n\nType: [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)\n\n#### Properties\n\n- `bounds` **DOMRectReadOnly** The bounds of the active element\n- `element` **[Element](https://developer.mozilla.org/docs/Web/API/Element)** The element that intersected\n- `index` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** This is always -1 on the container\n- `isScrollingDown` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Whether the user triggered this element\n  while scrolling down or not\n\n### Scroller#scene:enter\n\nScene enter event. Fires whenever a scene begins intersecting.\n\nType: [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)\n\n#### Properties\n\n- `bounds` **DOMRectReadOnly** The bounds of the active element\n- `element` **[Element](https://developer.mozilla.org/docs/Web/API/Element)** The element that intersected\n- `index` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** The index of the active element\n- `isScrollingDown` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Whether the user triggered this element\n  while scrolling down or not\n\n### Scroller#container:exit\n\nContainer exit event. Fires whenever the container has exited.\n\nType: [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)\n\n#### Properties\n\n- `bounds` **DOMRectReadOnly** The bounds of the exiting element\n- `element` **[Element](https://developer.mozilla.org/docs/Web/API/Element)** The element that exited\n- `index` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** This is always -1 on the container\n- `isScrollingDown` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Whether the user triggering the exit\n  while scrolling down or not\n\n### Scroller#scene:exit\n\nScene enter event. Fires whenever a scene has exited.\n\nType: [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)\n\n#### Properties\n\n- `bounds` **DOMRectReadOnly** The bounds of the exiting element\n- `element` **[Element](https://developer.mozilla.org/docs/Web/API/Element)** The element that exited\n- `index` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** The index of the exiting element\n- `isScrollingDown` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Whether the user triggering the exit\n  while scrolling down or not\n\n### Scroller#init\n\nInit event. Fires once Scroller has finished setting up.\n\n## License\n\nMIT\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdmurphy%2Fscroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frdmurphy%2Fscroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdmurphy%2Fscroller/lists"}