npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@welt/sticky-observer

v1.1.0

Published

A simple and basic sticky observer (or watcher) on HTMLElement's in a given container

Downloads

14

Readme

sticky-observer CircleCI

npm codecov dep GitHub license

A simple and easy to use sticky observer (or watcher) on HTMLElement's in a designated container. When scrolling or resizing the window sticky-observer will tell you if an element is STICKY, STICKY_END_OF_CONTAINER or NORMAL. This library does NOT include any preconfigured styling or positioning options, what happens when and how is left up to the you to configure with the help of some included helper functions.

Bring-Your-Own-Styling (BYOS)

This library was heavily inspired by sticky-js, it uses the same calculations and exhibits the same internal behaviour but due to it being completely unstyled it fits a different use case, all styling is left up to the you (BYOS).

Features

  • Full control over styling/positioning/placeholder. NO magic BUT more work for you.
  • Observing still works correctly with dynamic (out of control) appended container (ads)
  • Written in TypeScript
  • No dependencies
  • Fully tested
  • Small (cjs: 6.78 KB / 1.75 KB gzip) (esm: 5.88 KB / 1.59 KB gzip)

Demo

This library is in production on welt.de for a few main features.

  1. Sticky Page-Header (desktop only): Demo
  2. Sticky Video-Player (desktop only): Demo
  3. Sticky Social-Bar (mobile and desktop): Demo

Install

# via NPM
npm install @welt/sticky-observer
# via yarn
yarn add @welt/sticky-observer

Usage example

<div class="container"><div class="sticky-element" data-sticky-class="sticky-element--is-sticky">example</div></div>
import { StickyObserver } from '@welt/sticky-observer';

const stickyContainer = document.querySelector('.container');
const stickyElement = document.querySelector('.sticky-element');

const stickyObserver = new StickyObserver([stickyElement], stickyContainer);
stickyObserver.init();

stickyObserver.onStateChange(event => {
  switch (event.nextState) {
    case 'STICKY':
      event.element.sticky.addStickyClass();
      event.element.sticky.addPlaceholder();
      break;
    case 'NORMAL':
      event.element.sticky.removeStickyClass();
      event.element.sticky.removePlaceholder();
      break;
    default:
      // ignore
      break;
  }
});

stickyObserver.observe();

Options

new StickyObserver([stickyElement], stickyContainer, { offsetTop: 20, offsetBottom: 20 });

Each sticky-element can be configured with a few options via HTML [data-*] attributes. All configuration options are optional.

<div class="container">
  <!--
    [data-sticky-class]
    Css class to add when calling `addStickyClass()`. See API section.

    [data-sticky-placeholder-class]
    Css class to add when calling `addPlaceholder()` and [data-sticky-placeholder-auto-height] is `false`. See API section.

    [data-sticky-placeholder-auto-height]
    When calling `addPlaceholder()` a `<div/>` is added to the DOM with the same height of the sticky element.
    With this option you can disable the auto height and add your own css class for example.

    [data-sticky-offset-top]
    The top offset takes part in the 'NORMAL' to 'STICKY' calculation.
    With the offset you have some more control over the 'sticky breakpoint'. You can 'move' it up and down.
    The value must be a number without any units.

    [data-sticky-offset-bottom]
    The bottom offset takes part in the 'STICKY' to 'STICKY_END_OF_CONTAINER' calculation.
    With the offset you have some more control over the 'sticky breakpoint'. You can 'move' it up and down.
    The value must be a number without any units.
  -->
  <div
    class="sticky-element"
    data-sticky-class="sticky-element--is-sticky"
    data-sticky-placeholder-class="sticky-element__placeholder"
    data-sticky-placeholder-auto-height="false"
    data-sticky-offset-top="-20"
    data-sticky-offset-bottom="-20"
  >
    example
  </div>
</div>

API

// StickyObserver API

// Creating a new instance
// [stickyElement]: Array of HTMLElement
// stickyContainer: HTMLElement
// options: optional options (see: Options section)
const stickyObserver = new StickyObserver([stickyElement], stickyContainer, options);

// Mandatory
// Lazy initialize function. Binds all internal listeners but does not start the observer.
stickyObserver.init();

// Mandatory
// Starts the observer and notifies the listeners of any updates/changes/resizes.
stickyObserver.observe();

// All global listeners are still active but does not notify the listeners of any updates/changes/resizes.
stickyObserver.pause();

// Removes all global and element listeners. Deletes the `sticky` property of each sticky element
stickyObserver.destroy();

// Register a callback function to listen for (unique) state changes only (one event per change/transition).
// A basic flow is: NORMAL (start) -> STICKY -> STICKY_END_OF_CONTAINER
stickyObserver.onStateChange(stickyEvent => {});

// Register a callback function to listen for all window scroll and resize events (not throttled)
stickyObserver.onUpdate(stickyEvent => {});

// Register a callback function to listen for all window resize events only (not throttled)
stickyObserver.onResize(stickyEvent => {});

// Is the sticky observer still active.
// (boolean)
const isActive = stickyObserver.isActive();
// StickyState types
stickyObserver.onStateChange(stickyEvent => {
  // element is in default/non-sticky state
  const isNormal = stickyEvent.nextState === 'NORMAL';

  // element is in sticky state
  const isSticky = stickyEvent.nextState === 'STICKY';

  // element is below the the sticky container
  const isStickyEndOfContainer = stickyEvent.nextState === 'STICKY_END_OF_CONTAINER';
});
// StickyEvent API
stickyObserver.onStateChange(stickyEvent => {
  // The previous state of the sticky element
  // (string)
  const prevState = stickyEvent.prevState;

  // The next or updated/current state of the sticky element
  // (string)
  const nextState = stickyEvent.nextState;

  // The native HTMLElement with a `sticky` property for more options.
  // (HTMLElement)
  const element = stickyEvent.element;

  // The current scroll position based on `window.pageYOffset`
  // (number)
  const scrollTop = stickyEvent.scrollTop;
});
// Sticky element API
stickyObserver.onStateChange(stickyEvent => {
  const sticky = stickyEvent.element.sticky;

  // The offset top value used for the sticky calculation.
  // (number)
  const offsetTop = sticky.offsetTop;

  // The offset bottom value used for the sticky calculation.
  // (number)
  const offsetBottom = sticky.offsetBottom;

  // The default height of the element in a non-sticky state.
  // This is useful for an additional placeholder somewhere.
  // (number)
  const nonStickyHeight = sticky.nonStickyHeight;

  // Containing the `width`, `height`, `top` and `left` of the sticky element.
  // This is important for styling.
  // (object)
  const rect = sticky.rect;

  // Reference to the sticky container element
  // (HTMLElement)
  const container = sticky.container;

  // Containing the `width`, `height`, `top` and `left` of the sticky container element.
  // (object)
  const containerRect = sticky.container.rect;

  // Active state of the sticky element.
  // (boolean)
  const active = sticky.active;

  // Current state of the sticky element.
  // (string)
  const state = sticky.state;

  // Configured sticky class
  // (string)
  const stickyClass = sticky.stickyClass;

  // Configured placeholder class
  // (string)
  const placeholderClass = sticky.placeholderClass;

  // Configured placeholder auto height
  // (boolean)
  const placeholderAutoHeight = sticky.placeholderAutoHeight;

  // Adding the css class to the sticky element.
  // (null safe)
  sticky.addClass('some-special-class');

  // Removes the css class from the sticky element.
  // It does not throw an error when a class is not present
  sticky.removeClass('some-special-class');

  // Adds a configured sticky class.
  // When no sticky class is configured it does nothing
  sticky.addStickyClass();

  // Removes the configured sticky class.
  // When no sticky class is configured it does nothing
  sticky.removeStickyClass();

  // Adds a configured placeholder class.
  // When no placeholder class is configured it does nothing.
  sticky.addPlaceholder();

  // Removes the configured placeholder class.
  // When no placeholder class is configured it does nothing.
  sticky.removePlaceholder();
});

Usage standalone Browser version

See Demo

Browser support

This library is transpiled to ES5 without any special / custom browser API. This means:

  • in order for it to work on IE11 you must include the classList polyfill

Sticky-element works on all other major browsers.

Build

yarn install
yarn build

Test + Coverage

# You need a locally installed Chrome
yarn test

Release

The npm and GitHub releases are triggered manually (via release-it)

# You need a valid GITHUB_TOKEN
# See: https://github.com/webpro/release-it#github-releases
yarn publish

License

MIT