Body scroll locking that just works with everything 😏
Installations
npm install body-scroll-lock
Developer Guide
Typescript
No
Module System
CommonJS
Score
99.4
Supply Chain
99.1
Quality
76.1
Maintenance
100
Vulnerability
100
License
Releases
Change to how BSL works for IOS
Published on 23 Jun 2021
Minor Updates
Published on 31 Aug 2020
Reinstated the adding of 'module' field in package.json via a major version
Published on 18 Mar 2020
IOS Bug / Added NPM module field
Published on 16 Mar 2020
Documentation updates
Published on 13 Jul 2019
Fixed enableScroll not working in some conditions
Published on 27 Jun 2019
Contributors
Unable to fetch Contributors
Languages
JavaScript (100%)
Developer
willmcpo
Download Statistics
Total Downloads
160,954,723
Last Day
149,800
Last Week
648,574
Last Month
2,907,797
Last Year
41,957,533
GitHub Statistics
4,070 Stars
293 Commits
337 Forks
30 Watching
22 Branches
24 Contributors
Bundle Size
3.45 kB
Minified
1.29 kB
Minified + Gzipped
Package Meta Information
Latest Version
4.0.0-beta.0
Package Id
body-scroll-lock@4.0.0-beta.0
Size
9.26 kB
Publised On
23 Jun 2021
Total Downloads
Cumulative downloads
Total Downloads
160,954,723
Last day
-1.7%
149,800
Compared to previous day
Last week
-13.8%
648,574
Compared to previous week
Last month
-3.5%
2,907,797
Compared to previous month
Last year
7.9%
41,957,533
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Why BSL?
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).
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 / Angular / VueJS
- supports nested target elements (eg. 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:
Install
$ yarn add body-scroll-lock
or
$ npm install body-scroll-lock
You can also load via a <script src="lib/bodyScrollLock.js"></script>
tag (refer to the lib folder).
Usage examples
Common JS
1// 1. Import the functions 2const bodyScrollLock = require('body-scroll-lock'); 3const disableBodyScroll = bodyScrollLock.disableBodyScroll; 4const enableBodyScroll = bodyScrollLock.enableBodyScroll; 5 6// 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 7// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 8// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 9const targetElement = document.querySelector('#someElementId'); 10 11// 3. ...in some event handler after showing the target element...disable body scroll 12disableBodyScroll(targetElement); 13 14// 4. ...in some event handler after hiding the target element... 15enableBodyScroll(targetElement);
React/ES6
1// 1. Import the functions 2import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'; 3 4class SomeComponent extends React.Component { 5 targetElement = null; 6 7 componentDidMount() { 8 // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 9 // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 10 // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 11 this.targetElement = document.querySelector('#targetElementId'); 12 } 13 14 showTargetElement = () => { 15 // ... some logic to show target element 16 17 // 3. Disable body scroll 18 disableBodyScroll(this.targetElement); 19 }; 20 21 hideTargetElement = () => { 22 // ... some logic to hide target element 23 24 // 4. Re-enable body scroll 25 enableBodyScroll(this.targetElement); 26 }; 27 28 componentWillUnmount() { 29 // 5. Useful if we have called disableBodyScroll for multiple target elements, 30 // and we just want a kill-switch to undo all that. 31 // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 32 // clicks a link which takes him/her to a different page within the app. 33 clearAllBodyScrollLocks(); 34 } 35 36 render() { 37 return <div>some JSX to go here</div>; 38 } 39}
React/ES6 with Refs
1// 1. Import the functions 2import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'; 3 4class SomeComponent extends React.Component { 5 // 2. Initialise your ref and targetElement here 6 targetRef = React.createRef(); 7 targetElement = null; 8 9 componentDidMount() { 10 // 3. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 11 // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 12 // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 13 this.targetElement = this.targetRef.current; 14 } 15 16 showTargetElement = () => { 17 // ... some logic to show target element 18 19 // 4. Disable body scroll 20 disableBodyScroll(this.targetElement); 21 }; 22 23 hideTargetElement = () => { 24 // ... some logic to hide target element 25 26 // 5. Re-enable body scroll 27 enableBodyScroll(this.targetElement); 28 }; 29 30 componentWillUnmount() { 31 // 5. Useful if we have called disableBodyScroll for multiple target elements, 32 // and we just want a kill-switch to undo all that. 33 // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 34 // clicks a link which takes him/her to a different page within the app. 35 clearAllBodyScrollLocks(); 36 } 37 38 render() { 39 return ( 40 // 6. Pass your ref with the reference to the targetElement to SomeOtherComponent 41 <SomeOtherComponent ref={this.targetRef}>some JSX to go here</SomeOtherComponent> 42 ); 43 } 44} 45 46// 7. SomeOtherComponent needs to be a Class component to receive the ref (unless Hooks - https://reactjs.org/docs/hooks-faq.html#can-i-make-a-ref-to-a-function-component - are used). 47class SomeOtherComponent extends React.Component { 48 componentDidMount() { 49 // Your logic on mount goes here 50 } 51 52 // 8. BSL will be applied to div below in SomeOtherComponent and persist scrolling for the container 53 render() { 54 return <div>some JSX to go here</div>; 55 } 56}
Angular
1import { Component, ElementRef, OnDestroy, ViewChild } from "@angular/core"; 2 3// 1. Import the functions 4import { 5 disableBodyScroll, 6 enableBodyScroll, 7 clearAllBodyScrollLocks 8} from "body-scroll-lock"; 9 10@Component({ 11 selector: "app-scroll-block", 12 templateUrl: "./scroll-block.component.html", 13 styleUrls: ["./scroll-block.component.css"] 14}) 15export class SomeComponent implements OnDestroy { 16 // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 17 // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 18 // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 19 @ViewChild("scrollTarget") scrollTarget: ElementRef; 20 21 showTargetElement() { 22 // ... some logic to show target element 23 24 // 3. Disable body scroll 25 disableBodyScroll(this.scrollTarget.nativeElement); 26 } 27 28 hideTargetElement() { 29 // ... some logic to hide target element 30 31 // 4. Re-enable body scroll 32 enableBodyScroll(this.scrollTarget.nativeElement); 33 } 34 35 ngOnDestroy() { 36 // 5. Useful if we have called disableBodyScroll for multiple target elements, 37 // and we just want a kill-switch to undo all that. 38 // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor 39 // clicks a link which takes him/her to a different page within the app. 40 clearAllBodyScrollLocks(); 41 } 42} 43
Vanilla JS
In the html:
1<head> 2 <script src="some-path-where-you-dump-the-javascript-libraries/lib/bodyScrollLock.js"></script> 3</head>
Then in the javascript:
1// 1. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav). 2// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element). 3// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired. 4const targetElement = document.querySelector('#someElementId'); 5 6// 2. ...in some event handler after showing the target element...disable body scroll 7bodyScrollLock.disableBodyScroll(targetElement); 8 9// 3. ...in some event handler after hiding the target element... 10bodyScrollLock.enableBodyScroll(targetElement); 11 12// 4. Useful if we have called disableBodyScroll for multiple target elements, 13// and we just want a kill-switch to undo all that. 14bodyScrollLock.clearAllBodyScrollLocks();
Demo
Check out the demo, powered by Vercel.
- https://bodyscrolllock.vercel.app for a basic example
- https://bodyscrolllock-modal.vercel.app for an example with a modal.
Functions
Function | Arguments | Return | Description |
---|---|---|---|
disableBodyScroll | targetElement: HTMLElement options: BodyScrollOptions | void | Disables body scroll while enabling scroll on target element |
enableBodyScroll | targetElement: HTMLElement | void | Enables body scroll and removing listeners on target element |
clearAllBodyScrollLocks | null | void | Clears all scroll locks |
Options
reserveScrollBarGap
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 reserveScrollBarGap
option is set,
this gap is filled by a padding-right
on the body element. If disableBodyScroll
is called for the last target element,
or clearAllBodyScrollLocks
is called, the padding-right
is automatically reset to the previous value.
1import { disableBodyScroll } from 'body-scroll-lock'; 2import type { BodyScrollOptions } from 'body-scroll-lock'; 3 4const options: BodyScrollOptions = { 5 reserveScrollBarGap: true, 6}; 7 8disableBodyScroll(targetElement, options);
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
1disableBodyScroll(container, { 2 allowTouchMove: el => el.tagName === 'TEXTAREA', 3});
More Complex
Javascript:
1disableBodyScroll(container, { 2 allowTouchMove: el => { 3 while (el && el !== document.body) { 4 if (el.getAttribute('body-scroll-lock-ignore') !== null) { 5 return true; 6 } 7 8 el = el.parentElement; 9 } 10 }, 11});
Html:
1<div id="container"> 2 <div id="scrolling-map" body-scroll-lock-ignore> 3 ... 4 </div> 5</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
Changelog
Refer to the releases page.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 8/16 approved changesets -- score normalized to 5
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 22 are checked with a SAST tool
Reason
43 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw
- Warn: Project is vulnerable to: GHSA-wg6g-ppvx-927h
- Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2
- Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w
- Warn: Project is vulnerable to: GHSA-434g-2637-qmqr
- Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m
- Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw
- Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p
- Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
- Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw
- Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
- Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx
- Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
- Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq
- Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
- Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj
- Warn: Project is vulnerable to: GHSA-896r-f27r-55mw
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p
- Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp
- Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx
- Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch
- Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v
- Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
- Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
- Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp
Score
2.8
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More