{"id":13394055,"url":"https://github.com/willmcpo/body-scroll-lock","last_synced_at":"2025-04-23T20:51:44.138Z","repository":{"id":28320764,"uuid":"118079934","full_name":"willmcpo/body-scroll-lock","owner":"willmcpo","description":"Body scroll locking that just works with everything 😏","archived":false,"fork":false,"pushed_at":"2023-03-30T18:49:50.000Z","size":20324,"stargazers_count":4090,"open_issues_count":121,"forks_count":334,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-15T18:06:24.275Z","etag":null,"topics":["angular","body-scroll","body-scroll-lock","bsl","chrome","freeze","ios","lightbox","lock","mobile","modal","overflow","react","react-scroll-lock","react-scrolllock","safari","scroll-lock","toggle","vanilla-js","vuejs"],"latest_commit_sha":null,"homepage":"","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/willmcpo.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2018-01-19T05:09:01.000Z","updated_at":"2025-04-15T06:42:53.000Z","dependencies_parsed_at":"2024-09-25T00:30:20.773Z","dependency_job_id":null,"html_url":"https://github.com/willmcpo/body-scroll-lock","commit_stats":{"total_commits":241,"total_committers":33,"mean_commits":7.303030303030303,"dds":0.7219917012448133,"last_synced_commit":"79c4cf2c956eb7d5cf8d54a03d12751bc6ac8aa3"},"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willmcpo%2Fbody-scroll-lock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willmcpo%2Fbody-scroll-lock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willmcpo%2Fbody-scroll-lock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willmcpo%2Fbody-scroll-lock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willmcpo","download_url":"https://codeload.github.com/willmcpo/body-scroll-lock/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250514756,"owners_count":21443208,"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":["angular","body-scroll","body-scroll-lock","bsl","chrome","freeze","ios","lightbox","lock","mobile","modal","overflow","react","react-scroll-lock","react-scrolllock","safari","scroll-lock","toggle","vanilla-js","vuejs"],"created_at":"2024-07-30T17:01:07.434Z","updated_at":"2025-04-23T20:51:44.121Z","avatar_url":"https://github.com/willmcpo.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Libraries \u0026 Plugins","前端开发框架及项目","前端常用"],"sub_categories":["Helpers","多工具库支持或纯JS"],"readme":"\u003cp align=\"left\"\u003e \u003cimg width=\"675\" src=\"https://raw.githubusercontent.com/willmcpo/body-scroll-lock/master/images/logo.png\" alt=\"Body scroll lock...just works with everything ;-)\" /\u003e \u003c/p\u003e\n\n## Why BSL?\n\nEnables 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).\n\n_Features:_\n\n- disables body scroll WITHOUT disabling scroll of a target element\n- works on iOS mobile/tablet (!!)\n- works on Android\n- works on Safari desktop\n- works on Chrome/Firefox\n- works with vanilla JS and frameworks such as React / Angular / VueJS\n- supports nested target elements (eg. a modal that appears on top of a flyout)\n- can reserve scrollbar width\n- `-webkit-overflow-scrolling: touch` still works\n\n_Aren't the alternative approaches sufficient?_\n\n- the approach `document.body.ontouchmove = (e) =\u003e { e.preventDefault(); return false; };` locks the\n  body scroll, but ALSO locks the scroll of a target element (eg. modal).\n- the approach `overflow: hidden` on the body or html elements doesn't work for all browsers\n- the `position: fixed` approach causes the body scroll to reset\n- some approaches break inertia/momentum/rubber-band scrolling on iOS\n\n_LIGHT Package Size:_\n\n[![minzip size](https://badgen.net/bundlephobia/minzip/body-scroll-lock?color=orange)](https://badgen.net/bundlephobia/minzip/body-scroll-lock?color=orange)\n\n## Install\n\n    $ yarn add body-scroll-lock\n\n    or\n\n    $ npm install body-scroll-lock\n\nYou can also load via a `\u003cscript src=\"lib/bodyScrollLock.js\"\u003e\u003c/script\u003e` tag (refer to the lib folder).\n\n## Usage examples\n\n##### Common JS\n\n```javascript\n// 1. Import the functions\nconst bodyScrollLock = require('body-scroll-lock');\nconst disableBodyScroll = bodyScrollLock.disableBodyScroll;\nconst enableBodyScroll = bodyScrollLock.enableBodyScroll;\n\n// 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).\n// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).\n// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.\nconst targetElement = document.querySelector('#someElementId');\n\n// 3. ...in some event handler after showing the target element...disable body scroll\ndisableBodyScroll(targetElement);\n\n// 4. ...in some event handler after hiding the target element...\nenableBodyScroll(targetElement);\n```\n\n##### React/ES6\n\n```javascript\n// 1. Import the functions\nimport { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock';\n\nclass SomeComponent extends React.Component {\n  targetElement = null;\n\n  componentDidMount() {\n    // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).\n    // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).\n    // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.\n    this.targetElement = document.querySelector('#targetElementId');\n  }\n\n  showTargetElement = () =\u003e {\n    // ... some logic to show target element\n\n    // 3. Disable body scroll\n    disableBodyScroll(this.targetElement);\n  };\n\n  hideTargetElement = () =\u003e {\n    // ... some logic to hide target element\n\n    // 4. Re-enable body scroll\n    enableBodyScroll(this.targetElement);\n  };\n\n  componentWillUnmount() {\n    // 5. Useful if we have called disableBodyScroll for multiple target elements,\n    // and we just want a kill-switch to undo all that.\n    // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor\n    // clicks a link which takes him/her to a different page within the app.\n    clearAllBodyScrollLocks();\n  }\n\n  render() {\n    return \u003cdiv\u003esome JSX to go here\u003c/div\u003e;\n  }\n}\n```\n\n##### React/ES6 with Refs\n\n```javascript\n// 1. Import the functions\nimport { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock';\n\nclass SomeComponent extends React.Component {\n  // 2. Initialise your ref and targetElement here\n  targetRef = React.createRef();\n  targetElement = null;\n\n  componentDidMount() {\n    // 3. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).\n    // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).\n    // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.\n    this.targetElement = this.targetRef.current;\n  }\n\n  showTargetElement = () =\u003e {\n    // ... some logic to show target element\n\n    // 4. Disable body scroll\n    disableBodyScroll(this.targetElement);\n  };\n\n  hideTargetElement = () =\u003e {\n    // ... some logic to hide target element\n\n    // 5. Re-enable body scroll\n    enableBodyScroll(this.targetElement);\n  };\n\n  componentWillUnmount() {\n    // 5. Useful if we have called disableBodyScroll for multiple target elements,\n    // and we just want a kill-switch to undo all that.\n    // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor\n    // clicks a link which takes him/her to a different page within the app.\n    clearAllBodyScrollLocks();\n  }\n\n  render() {\n    return (\n      // 6. Pass your ref with the reference to the targetElement to SomeOtherComponent\n      \u003cSomeOtherComponent ref={this.targetRef}\u003esome JSX to go here\u003c/SomeOtherComponent\u003e\n    );\n  }\n}\n\n// 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).\nclass SomeOtherComponent extends React.Component {\n  componentDidMount() {\n    // Your logic on mount goes here\n  }\n\n  // 8. BSL will be applied to div below in SomeOtherComponent and persist scrolling for the container\n  render() {\n    return \u003cdiv\u003esome JSX to go here\u003c/div\u003e;\n  }\n}\n```\n\n\n##### Angular\n\n```javascript\nimport { Component, ElementRef, OnDestroy, ViewChild } from \"@angular/core\";\n\n// 1. Import the functions\nimport {\n  disableBodyScroll,\n  enableBodyScroll,\n  clearAllBodyScrollLocks\n} from \"body-scroll-lock\";\n\n@Component({\n  selector: \"app-scroll-block\",\n  templateUrl: \"./scroll-block.component.html\",\n  styleUrls: [\"./scroll-block.component.css\"]\n})\nexport class SomeComponent implements OnDestroy {\n  // 2. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).\n  // Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).\n  // This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.\n  @ViewChild(\"scrollTarget\") scrollTarget: ElementRef;\n\n  showTargetElement() {\n    // ... some logic to show target element\n\n    // 3. Disable body scroll\n    disableBodyScroll(this.scrollTarget.nativeElement);\n  }\n  \n  hideTargetElement() {\n    // ... some logic to hide target element\n\n    // 4. Re-enable body scroll\n    enableBodyScroll(this.scrollTarget.nativeElement);\n  }\n\n  ngOnDestroy() {\n    // 5. Useful if we have called disableBodyScroll for multiple target elements,\n    // and we just want a kill-switch to undo all that.\n    // OR useful for if the `hideTargetElement()` function got circumvented eg. visitor\n    // clicks a link which takes him/her to a different page within the app.\n    clearAllBodyScrollLocks();\n  }\n}\n\n```\n\n##### Vanilla JS\n\nIn the html:\n\n```html\n\u003chead\u003e\n  \u003cscript src=\"some-path-where-you-dump-the-javascript-libraries/lib/bodyScrollLock.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n```\n\nThen in the javascript:\n\n```javascript\n// 1. Get a target element that you want to persist scrolling for (such as a modal/lightbox/flyout/nav).\n// Specifically, the target element is the one we would like to allow scroll on (NOT a parent of that element).\n// This is also the element to apply the CSS '-webkit-overflow-scrolling: touch;' if desired.\nconst targetElement = document.querySelector('#someElementId');\n\n// 2. ...in some event handler after showing the target element...disable body scroll\nbodyScrollLock.disableBodyScroll(targetElement);\n\n// 3. ...in some event handler after hiding the target element...\nbodyScrollLock.enableBodyScroll(targetElement);\n\n// 4. Useful if we have called disableBodyScroll for multiple target elements,\n// and we just want a kill-switch to undo all that.\nbodyScrollLock.clearAllBodyScrollLocks();\n```\n\n## Demo\n\nCheck out the demo, powered by Vercel.\n\n* https://bodyscrolllock.vercel.app for a basic example \n* https://bodyscrolllock-modal.vercel.app for an example with a modal.\n\n## Functions\n\n| Function                  | Arguments                                                      | Return | Description                                                  |\n| :------------------------ | :------------------------------------------------------------- | :----: | :----------------------------------------------------------- |\n| `disableBodyScroll`       | `targetElement: HTMLElement` \u003cbr/\u003e`options: BodyScrollOptions` | `void` | Disables body scroll while enabling scroll on target element |\n| `enableBodyScroll`        | `targetElement: HTMLElement`                                   | `void` | Enables body scroll and removing listeners on target element |\n| `clearAllBodyScrollLocks` | `null`                                                         | `void` | Clears all scroll locks                                      |\n\n## Options\n\n### reserveScrollBarGap\n\n**optional, default:** false\n\nIf the overflow property of the body is set to hidden, the body widens by the width of the scrollbar. This produces an\nunpleasant flickering effect, especially on websites with centered content. If the `reserveScrollBarGap` option is set,\nthis gap is filled by a `padding-right` on the body element. If `disableBodyScroll` is called for the last target element,\nor `clearAllBodyScrollLocks` is called, the `padding-right` is automatically reset to the previous value.\n\n```js\nimport { disableBodyScroll } from 'body-scroll-lock';\nimport type { BodyScrollOptions } from 'body-scroll-lock';\n\nconst options: BodyScrollOptions = {\n  reserveScrollBarGap: true,\n};\n\ndisableBodyScroll(targetElement, options);\n```\n\n### allowTouchMove\n\n**optional, default:** undefined\n\nTo disable scrolling on iOS, `disableBodyScroll` prevents `touchmove` events.\nHowever, there are cases where you have called `disableBodyScroll` on an\nelement, but its children still require `touchmove` events to function.\n\nSee below for 2 use cases:\n\n##### Simple\n\n```javascript\ndisableBodyScroll(container, {\n  allowTouchMove: el =\u003e el.tagName === 'TEXTAREA',\n});\n```\n\n##### More Complex\n\nJavascript:\n\n```javascript\ndisableBodyScroll(container, {\n  allowTouchMove: el =\u003e {\n    while (el \u0026\u0026 el !== document.body) {\n      if (el.getAttribute('body-scroll-lock-ignore') !== null) {\n        return true;\n      }\n\n      el = el.parentElement;\n    }\n  },\n});\n```\n\nHtml:\n\n```html\n\u003cdiv id=\"container\"\u003e\n  \u003cdiv id=\"scrolling-map\" body-scroll-lock-ignore\u003e\n    ...\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n## References\n\nhttps://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177\nhttps://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi\n\n## Changelog\n\nRefer to the [releases](https://github.com/willmcpo/body-scroll-lock/releases) page.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillmcpo%2Fbody-scroll-lock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillmcpo%2Fbody-scroll-lock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillmcpo%2Fbody-scroll-lock/lists"}