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

body-scroll-lock-enhanced

v0.0.1

Published

Enables body scroll locking (for iOS Mobile and Tablet, Android, desktop Safari/Chrome/Firefox) without breaking scrolling of a target element (eg. modal/lightbox/flyouts/nav-menus)

Downloads

75

Readme

body-scroll-lock-enhanced

Enhanced version of body-scroll-lock package, main changes:

  • fixes body-scroll-lock v4.0.0-beta.0 bug,
  • prevents unnecessary scrolling back to section on enableBodyScroll when property scroll-behavior is set to smooth,
  • for browsers that support scrollbar-gutter set it instead of padding-right to the body element

Why BSL?

Enables body scroll locking (for iOS Mobile and Tablet, Android, desktop Safari/Chrome/Firefox) without breaking scrolling of a target element (e.g. modal/lightbox/flyouts/nav-menus).

Features:

  • disables body scroll WITHOUT disabling scroll of a target element
  • works on iOS mobile/tablet
  • works on Android
  • works on Safari desktop
  • works on Chrome/Firefox
  • works with vanilla JS and frameworks such as React / Vue / Angular
  • supports nested target elements (e.g. a modal that appears on top of a flyout)
  • can reserve scrollbar width
  • -webkit-overflow-scrolling: touch still works

Aren't the alternative approaches sufficient?

  • the approach document.body.ontouchmove = (e) => { e.preventDefault(); return false; }; locks the body scroll, but ALSO locks the scroll of a target element (eg. modal).
  • the approach overflow: hidden on the body or html elements doesn't work for all browsers
  • the position: fixed approach causes the body scroll to reset
  • some approaches break inertia/momentum/rubber-band scrolling on iOS

LIGHT Package Size:

minzip size

Install

pnpm

pnpm add body-scroll-lock-enhanced

yarn

yarn add body-scroll-lock-enhanced

npm

npm install body-scroll-lock-enhanced

Usage examples

JS

// 1a. Import the functions (for CJS)
const bodyScrollLock = require('body-scroll-lock-enhanced');
const disableBodyScroll = bodyScrollLock.disableBodyScroll;
const enableBodyScroll = bodyScrollLock.enableBodyScroll;

// 1b. Import the functions (for ESM)
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock-enhanced';

// 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).
// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).
// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.
const targetElement = document.querySelector('#someElementId');

// 3. ...in some event handler after showing the target element...disable body scroll
disableBodyScroll(targetElement);

// 4. ...in some event handler after hiding the target element...
enableBodyScroll(targetElement);

React

import { useEffect, useRef } from 'react';

// 1. Import the functions
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock-enhanced';

const MyComponent = () => {
  // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).
  // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).
  // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.
  const targetElement = useRef(null);

  const showTargetElement = () => {
    // ... some logic to show target element

    // 3. Disable body scroll
    disableBodyScroll(targetElement.current);
  };

  const hideTargetElement = () => {
    // ... some logic to hide target element

    // 4. Re-enable body scroll
    enableBodyScroll(targetElement.current);
  };

  useEffect(() => {
    // 5. Useful if we have called disableBodyScroll for multiple target elements,
    // and we just want a kill-switch to undo all that.
    // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor
    // clicks a link which takes him/her to a different page within the app.
    return () => {
      clearAllBodyScrollLocks();
    };
  }, []);

  return (
    <div>
      <div ref={targetElement}>
        {/* target element content */}
      </div>
      <button onClick={showTargetElement}>Show</button>
      <button onClick={hideTargetElement}>Hide</button>
    </div>
  );
};

Vue

import { onUnmounted, ref } from 'vue';

// 1. Import the functions
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock-enhanced';

// 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).
// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).
// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.
const targetElement = ref(null)

const showTargetElement = () => {
  // ... some logic to show target element

  // 3. Disable body scroll
  disableBodyScroll(targetElement.value);
};

const hideTargetElement = () => {
  // ... some logic to hide target element

  // 4. Re-enable body scroll
  enableBodyScroll(targetElement.value);
};

onUnmounted(() => {
  // 5. Useful if we have called disableBodyScroll for multiple target elements,
  // and we just want a kill-switch to undo all that.
  // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor
  // clicks a link which takes him/her to a different page within the app.
  clearAllBodyScrollLocks();
});

Angular

import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';

// 1. Import the functions
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock-enhanced';

@Component({
  selector: 'app-my-component',
  template: `
    <div #targetElement>
      <!-- target element content -->
    </div>
    <button (click)="showTargetElement()">Show</button>
    <button (click)="hideTargetElement()">Hide</button>
  `,
})
export class MyComponent implements OnDestroy {
  @ViewChild('targetElement', { static: true }) targetElement: ElementRef;

  showTargetElement() {
    // ... some logic to show target element

    // 3. Disable body scroll
    disableBodyScroll(this.targetElement.nativeElement);
  }

  hideTargetElement() {
    // ... some logic to hide target element

    // 4. Re-enable body scroll
    enableBodyScroll(this.targetElement.nativeElement);
  }

  ngOnDestroy() {
    // 5. Useful if we have called disableBodyScroll for multiple target elements,
    // and we just want a kill-switch to undo all that.
    // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor
    // clicks a link which takes him/her to a different page within the app.
    clearAllBodyScrollLocks();
  }
}

Functions

disableBodyScroll(targetElement: HTMLElement, options?: BodyScrollOptions): void
// Disables body scroll for the target element.

enableBodyScroll(targetElement: HTMLElement): void
// Enables body scroll for the target element.

clearAllBodyScrollLocks(): void
// Clears all body scroll locks.

toggleBodyScrollLock(targetElement: HTMLElement, options?: BodyScrollOptions & { toggleValue?: boolean }): void
// Toggles body scroll lock for the target element based on the value parameter in options (value is optional).

Options

reserveScrollBarGap reserveScrollbarGutter

optional, default: false

If the overflow property of the body is set to hidden, the body widens by the width of the scrollbar. This produces an unpleasant flickering effect, especially on websites with centered content. If the reserveScrollbarGutter option is set, this gap is filled by scrollbar-gutter: stable on the html element (or padding-right on the body element, if browser does not support this option). If disableBodyScroll is called for the last target element, or clearAllBodyScrollLocks is called, the scrollbar-gutter (padding-right) is automatically reset to the previous value.

import { disableBodyScroll } from 'body-scroll-lock-enhanced';

disableBodyScroll(targetElement, { reserveScrollbarGutter: true });

toggleValue

optional (only available in toggleBodyScrollLock), default: undefined

The toggleValue option is a boolean parameter used in the toggleBodyScrollLock function. This parameter determines whether to enable or disable body scroll based on its value.

  • If toggleValue is true, the toggleBodyScrollLock function will disable body scroll for the specified target element.
  • If toggleValue is false, the toggleBodyScrollLock function will enable body scroll for the specified target element.
  • If toggleValue is undefined, the toggleBodyScrollLock function will toggle the current state of body scroll for the specified target element. This means it will disable body scroll if it is currently enabled, and enable body scroll if it is currently disabled.

This option provides a convenient way to manage the body scroll lock state with a single function call.

import { toggleBodyScrollLock } from 'body-scroll-lock-enhanced';

toggleBodyScrollLock(targetElement, { toggleValue: isTargetElementVisible });

allowTouchMove

optional, default: undefined

To disable scrolling on iOS, disableBodyScroll prevents touchmove events. However, there are cases where you have called disableBodyScroll on an element, but its children still require touchmove events to function.

See below for 2 use cases:

Simple

disableBodyScroll(container, {
  allowTouchMove: el => el.tagName === 'TEXTAREA'
});

More Complex

JavaScript:

disableBodyScroll(container, {
  allowTouchMove: (el) => {
    while (el && el !== document.body) {
      if (el.getAttribute('body-scroll-lock-ignore') !== null) {
        return true;
      }

      el = el.parentElement;
    }
  }
});

HTML:

<div id="container">
  <div id="scrolling-map" body-scroll-lock-ignore>
    ...
  </div>
</div>

References

https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177 https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi

License

This project is licensed under the MIT License - see the LICENSE file for details.