tua-body-scroll-lock
Advanced tools
Comparing version 0.1.0 to 0.2.0-0
{ | ||
"name": "tua-body-scroll-lock", | ||
"version": "0.1.0", | ||
"description": "Body scroll locking that just works with everything", | ||
"main": "lib/tua-bsl.umd.js", | ||
"module": "lib/tua-bsl.esm.js", | ||
"unpkg": "lib/tua-bsl.umd.js", | ||
"jsdelivr": "lib/tua-bsl.umd.js", | ||
"version": "0.2.0-0", | ||
"description": "🔐Body scroll locking that just works with everything", | ||
"main": "dist/tua-bsl.umd.js", | ||
"module": "dist/tua-bsl.esm.js", | ||
"unpkg": "dist/tua-bsl.umd.js", | ||
"jsdelivr": "dist/tua-bsl.umd.js", | ||
"typings": "src/index.d.ts", | ||
"scripts": { | ||
"lint": "eslint --fix ./", | ||
"start": "rollup -c -w", | ||
"build": "npm run lint && rollup -c && cp index.html lib/index.html", | ||
"lint": "eslint --fix ./", | ||
"deploy": "npm run build && gh-pages -d lib" | ||
"build": "npm run lint && rollup -c && cp index.html dist/index.html", | ||
"next": "npm --no-git-tag-version version prerelease", | ||
"next:m": "npm --no-git-tag-version version preminor", | ||
"pub": "npm run build && npm publish", | ||
"pub:n": "npm run build && npm publish --tag next", | ||
"deploy": "npm run build && gh-pages -d dist" | ||
}, | ||
"eslintIgnore": [ | ||
"lib/" | ||
"dist/" | ||
], | ||
@@ -38,2 +42,3 @@ "husky": { | ||
"@commitlint/config-conventional": "^7.5.0", | ||
"all-contributors-cli": "^6.3.0", | ||
"babel-eslint": "^10.0.1", | ||
@@ -50,6 +55,4 @@ "eslint-config-standard": "^12.0.0", | ||
"rollup-plugin-babel": "^4.3.2", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-eslint": "^5.0.0", | ||
"rollup-plugin-json": "^3.1.0", | ||
"rollup-plugin-node-resolve": "^4.0.0", | ||
"rollup-plugin-replace": "^2.1.0", | ||
@@ -85,4 +88,4 @@ "rollup-plugin-uglify": "^6.0.2" | ||
], | ||
"author": "evinma", | ||
"author": "Evinma, BuptStEve", | ||
"license": "MIT" | ||
} |
@@ -5,7 +5,26 @@ # tua-body-scroll-lock | ||
## 介绍 | ||
顾名思义 `tua-body-scroll-lock` 是用来锁住 `body` 滚动的包。并且针对`PC端`和移动端 `ios` 和 `android` 做了不同的处理,保证在各个端都可以完美使用。 | ||
<img src="https://img.shields.io/badge/dependencies-none-green.svg" alt="dependencies"> | ||
<a href="https://www.npmjs.com/package/tua-body-scroll-lock" target="_blank"> | ||
<img src="https://badgen.net/npm/dm/tua-body-scroll-lock" alt="Downloads per month"> | ||
<img src="https://img.shields.io/npm/v/tua-body-scroll-lock.svg" alt="Version"> | ||
<img src="https://img.shields.io/npm/v/tua-body-scroll-lock/next.svg" alt="Next Version"> | ||
<img src="https://img.shields.io/npm/l/tua-body-scroll-lock.svg" alt="License"> | ||
</a> | ||
## 安装 | ||
English | [简体中文](./README-zh_CN.md) | ||
## Introduction | ||
`tua-body-scroll-lock` enables body scroll locking for everything. | ||
### Why not [body-scroll-lock](https://github.com/willmcpo/body-scroll-lock)? | ||
* Doesn't work on Android webview | ||
* Doesn't work on PC with mouse wheel | ||
* Doesn't work on iOS, if you touch somewhere instead of targetElement | ||
* Must pass targetElement, even if it's not necessary | ||
[Try This](https://codepen.io/buptsteve/pen/EJoKQK) | ||
## Install | ||
### Node Package Manager(recommended) | ||
```bash | ||
@@ -17,18 +36,29 @@ $ npm i -S tua-body-scroll-lock | ||
## 使用 | ||
### CDN | ||
* UMD(`tua-bsl.umd.js`) | ||
* [unpkg](https://unpkg.com/tua-body-scroll-lock) | ||
* [jsdelivr](https://cdn.jsdelivr.net/npm/tua-body-scroll-lock) | ||
### 移动端 | ||
```html | ||
<!-- unpkg --> | ||
<script src="https://unpkg.com/tua-body-scroll-lock"></script> | ||
```js | ||
import { lock, unlock } from 'tua-body-scroll-lock' | ||
<!-- jsdelivr --> | ||
<script src="https://cdn.jsdelivr.net/npm/tua-body-scroll-lock"></script> | ||
``` | ||
// 禁止滑动后还需要内部可以滚动的元素(针对移动端ios处理) | ||
const targetElement = document.querySelector("#someElementId"); | ||
* Minified UMD(`tua-bsl.umd.min.js`) | ||
* [unpkg](https://unpkg.com/tua-body-scroll-lock/dist/tua-bsl.umd.min.js) | ||
* [jsdelivr](https://cdn.jsdelivr.net/npm/tua-body-scroll-lock/dist/tua-bsl.umd.min.js) | ||
lock(targetElement) | ||
unlock(targetElement) | ||
```html | ||
<!-- unpkg --> | ||
<script src="https://unpkg.com/tua-body-scroll-lock/dist/tua-bsl.umd.min.js"></script> | ||
<!-- jsdelivr --> | ||
<script src="https://cdn.jsdelivr.net/npm/tua-body-scroll-lock/dist/tua-bsl.umd.min.js"></script> | ||
``` | ||
### PC端 | ||
> tips: PC端不需要targetElement; 不传targetElement也不想要控制台提示可以传`null` | ||
## Usage | ||
### Normal | ||
@@ -42,5 +72,31 @@ ```js | ||
## 测试 | ||
[测试链接](https://tuateam.github.io/tua-body-scroll-lock) | ||
### TargetElement needs scrolling(iOS only) | ||
In some scenarios, when scrolling is prohibited, some elements still need to scroll, at this point, pass the targetElement. | ||
```js | ||
import { lock, unlock } from 'tua-body-scroll-lock' | ||
const targetElement = document.querySelector('#someElementId') | ||
lock(targetElement) | ||
unlock(targetElement) | ||
``` | ||
> The `targetElement` is not required on the PC and Android. | ||
## Test | ||
[testLink](https://tuateam.github.io/tua-body-scroll-lock) | ||
![bodyScrollLock](./tua-bsl.png) | ||
## Contributors | ||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): | ||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
<!-- prettier-ignore --> | ||
<table><tr><td align="center"><a href="https://github.com/evinma"><img src="https://avatars2.githubusercontent.com/u/16096567?v=4" width="100px;" alt="evinma"/><br /><sub><b>evinma</b></sub></a><br /><a href="https://github.com/tuateam/tua-body-scroll-lock/commits?author=evinma" title="Code">💻</a> <a href="https://github.com/tuateam/tua-body-scroll-lock/commits?author=evinma" title="Documentation">📖</a></td><td align="center"><a href="https://buptsteve.github.io"><img src="https://avatars2.githubusercontent.com/u/11501493?v=4" width="100px;" alt="StEve Young"/><br /><sub><b>StEve Young</b></sub></a><br /><a href="https://github.com/tuateam/tua-body-scroll-lock/commits?author=BuptStEve" title="Documentation">📖</a> <a href="#infra-BuptStEve" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td><td align="center"><a href="https://github.com/li2go"><img src="https://avatars2.githubusercontent.com/u/11485337?v=4" width="100px;" alt="li2go"/><br /><sub><b>li2go</b></sub></a><br /><a href="https://github.com/tuateam/tua-body-scroll-lock/issues?q=author%3Ali2go" title="Bug reports">🐛</a></td><td align="center"><a href="https://github.com/feitiange"><img src="https://avatars3.githubusercontent.com/u/7125157?v=4" width="100px;" alt="songyan,Wang"/><br /><sub><b>songyan,Wang</b></sub></a><br /><a href="https://github.com/tuateam/tua-body-scroll-lock/issues?q=author%3Afeitiange" title="Bug reports">🐛</a></td></tr></table> | ||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! |
@@ -1,64 +0,63 @@ | ||
// import json from 'rollup-plugin-json' | ||
import babel from 'rollup-plugin-babel' | ||
import replace from 'rollup-plugin-replace' | ||
import { eslint } from 'rollup-plugin-eslint' | ||
import commonjs from 'rollup-plugin-commonjs' | ||
import { uglify } from 'rollup-plugin-uglify' | ||
import nodeResolve from 'rollup-plugin-node-resolve' | ||
const pkg = require('./package.json') | ||
const plugins = [ | ||
// json(), | ||
eslint(), | ||
babel(), | ||
commonjs(), | ||
nodeResolve({ jsnext: true, main: true, browser: true }), | ||
] | ||
const banner = | ||
`/** | ||
* ${pkg.name} v${pkg.version} | ||
* (c) ${new Date().getFullYear()} ${pkg.author} | ||
* @license ${pkg.license} | ||
*/ | ||
` | ||
const plugins = [ eslint(), babel() ] | ||
export default [ | ||
{ | ||
input: 'src/index.js', | ||
output: [ | ||
{ | ||
file: pkg.module, | ||
// exports: 'named', | ||
format: 'es', | ||
}, | ||
], | ||
plugins, | ||
const configMap = { | ||
esm: { | ||
file: pkg.module, | ||
format: 'esm', | ||
}, | ||
{ | ||
input: 'src/index.js', | ||
output: [ | ||
{ | ||
file: pkg.main, | ||
format: 'umd', | ||
exports: 'named', | ||
name: 'bodyScrollLock', | ||
}, | ||
], | ||
plugins: [ | ||
...plugins, | ||
replace({ | ||
'process.env.NODE_ENV': true, | ||
}), | ||
], | ||
umdDev: { | ||
file: pkg.main, | ||
format: 'umd', | ||
env: 'development', | ||
}, | ||
{ | ||
umdProd: { | ||
file: `dist/tua-bsl.umd.min.js`, | ||
format: 'umd', | ||
env: 'production', | ||
}, | ||
} | ||
const genConfig = (opts) => { | ||
const isProd = /min\.js$/.test(opts.file) | ||
const config = { | ||
input: 'src/index.js', | ||
plugins: plugins.slice(), | ||
output: { | ||
file: `lib/tua-bsl.umd.min.js`, | ||
format: 'umd', | ||
exports: 'named', | ||
file: opts.file, | ||
format: opts.format, | ||
banner, | ||
name: 'bodyScrollLock', | ||
}, | ||
plugins: [ | ||
...plugins, | ||
replace({ | ||
'process.env.NODE_ENV': false, | ||
}), | ||
uglify(), | ||
], | ||
}, | ||
] | ||
} | ||
if (opts.env) { | ||
config.plugins.push(replace({ | ||
'process.env.NODE_ENV': JSON.stringify(opts.env), | ||
})) | ||
} | ||
if (isProd) { | ||
config.plugins.push(uglify()) | ||
} | ||
return config | ||
} | ||
export default Object.keys(configMap) | ||
.map(key => configMap[key]) | ||
.map(genConfig) |
162
src/index.js
@@ -1,17 +0,21 @@ | ||
let locksElement = [] | ||
let lockedNum = 0 | ||
let initialClientY | ||
let unLockCallback | ||
let documentListenerAdded | ||
let initialClientY = 0 | ||
let unLockCallback = null | ||
let documentListenerAdded = false | ||
let hasPassiveEvents = false | ||
if (typeof window !== 'undefined') { | ||
const isServer = typeof window === 'undefined' | ||
const lockedElements = [] | ||
const $ = !isServer && document.querySelector.bind(document) | ||
let eventListenerOptions | ||
if (!isServer) { | ||
const testEvent = '__TUA_BSL_TEST_PASSIVE__' | ||
const passiveTestOptions = { | ||
get passive () { | ||
hasPassiveEvents = true | ||
return undefined | ||
eventListenerOptions = { passive: false } | ||
}, | ||
} | ||
window.addEventListener('testPassive', null, passiveTestOptions) | ||
window.removeEventListener('testPassive', null, passiveTestOptions) | ||
window.addEventListener(testEvent, null, passiveTestOptions) | ||
window.removeEventListener(testEvent, null, passiveTestOptions) | ||
} | ||
@@ -21,29 +25,25 @@ | ||
const ua = navigator.userAgent | ||
const android = /(Android);?[\s/]+([\d.]+)?/.test(ua) | ||
const ipad = /(iPad).*OS\s([\d_]+)/.test(ua) | ||
const iphone = !ipad && /(iPhone\sOS)\s([\d_]+)/.test(ua) | ||
const android = /(Android);?[\s/]+([\d.]+)?/.test(ua) | ||
const os = android ? 'android' : 'ios' | ||
const ios = iphone || ipad | ||
return { | ||
os: android ? 'android' : 'ios', | ||
ios, | ||
ipad, | ||
iphone, | ||
android, | ||
} | ||
return { os, ios, ipad, iphone, android } | ||
} | ||
const setOverflowHiddenPc = () => { | ||
const $ = document::document.querySelector | ||
const $body = $('body') | ||
const bodyStyle = { ...$body.style } | ||
const scrollBarWidth = window.innerWidth - document.body.clientWidth | ||
$body.style.overflow = 'hidden' | ||
$body.style.paddingRight = scrollBarWidth + 'px' | ||
$body.style.boxSizing = 'border-box' | ||
$body.style.paddingRight = `${scrollBarWidth}px` | ||
return () => { | ||
$body.style.overflow = bodyStyle.overflow || '' | ||
$body.style.paddingRight = bodyStyle.paddingRight || '' | ||
$body.style.boxSizing = bodyStyle.boxSizing || '' | ||
;['overflow', 'boxSizing', 'paddingRight'].forEach((x) => { | ||
$body.style[x] = bodyStyle[x] || '' | ||
}) | ||
} | ||
@@ -53,28 +53,24 @@ } | ||
const setOverflowHiddenMobile = () => { | ||
const $ = document::document.querySelector | ||
const $html = $('html') | ||
const $body = $('body') | ||
const scrollTop = $html.scrollTop || $body.scrollTop | ||
const htmlStyle = { ...$html.style } | ||
const bodyStyle = { ...$body.style } | ||
const scrollTop = $html.scrollTop || $body.scrollTop | ||
$html.style.height = '100%' | ||
$html.style.overflow = 'hidden' | ||
$html.style.height = '100%' | ||
$body.style.overflow = 'hidden' | ||
$body.style.top = `-${scrollTop}px` | ||
$body.style.width = '100%' | ||
$body.style.position = 'fixed' | ||
$body.style.overflow = 'hidden' | ||
return () => { | ||
$html.style.height = htmlStyle.height || '' | ||
$html.style.overflow = htmlStyle.overflow || '' | ||
$html.style.height = htmlStyle.height || '' | ||
$body.style.overflow = bodyStyle.overflow || '' | ||
$body.style.height = bodyStyle.height || '' | ||
$body.style.width = bodyStyle.width || '' | ||
$body.style.position = '' | ||
$body.style.top = '' | ||
;['top', 'width', 'height', 'overflow', 'position'].forEach((x) => { | ||
$body.style[x] = bodyStyle[x] || '' | ||
}) | ||
window.scrollTo(0, scrollTop) | ||
@@ -84,6 +80,6 @@ } | ||
const preventDefault = event => { | ||
if (event.cancelable) { | ||
event.preventDefault() | ||
} | ||
const preventDefault = (event) => { | ||
if (!event.cancelable) return | ||
event.preventDefault() | ||
} | ||
@@ -94,8 +90,10 @@ | ||
if (targetElement && targetElement.scrollTop === 0 && clientY > 0) { | ||
return preventDefault(event) | ||
} | ||
if (targetElement) { | ||
const { scrollTop, scrollHeight, clientHeight } = targetElement | ||
const isOnTop = clientY > 0 && scrollTop === 0 | ||
const isOnBottom = clientY < 0 && scrollTop + clientHeight + 1 >= scrollHeight | ||
if (targetElement && (targetElement.scrollHeight - 1 - targetElement.scrollTop <= targetElement.clientHeight) && clientY < 0) { | ||
return preventDefault(event) | ||
if (isOnTop || isOnBottom) { | ||
return preventDefault(event) | ||
} | ||
} | ||
@@ -108,52 +106,70 @@ | ||
const checkTargetElement = (targetElement) => { | ||
if (!targetElement && targetElement !== null && process.env.NODE_ENV !== 'production') { | ||
console.warn('If scrolling is also required in the floating layer, the target element must be provided') | ||
} | ||
if (targetElement) return | ||
if (targetElement === null) return | ||
if (process.env.NODE_ENV === 'production') return | ||
console.warn( | ||
`If scrolling is also required in the floating layer, ` + | ||
`the target element must be provided.` | ||
) | ||
} | ||
export const lock = (targetElement) => { | ||
const lock = (targetElement) => { | ||
if (isServer) return | ||
checkTargetElement(targetElement) | ||
if (detectOS().ios) { | ||
if (targetElement && locksElement.indexOf(targetElement) < 0) { | ||
targetElement.ontouchstart = event => { | ||
// iOS | ||
if (targetElement && lockedElements.indexOf(targetElement) === -1) { | ||
targetElement.ontouchstart = (event) => { | ||
initialClientY = event.targetTouches[0].clientY | ||
} | ||
targetElement.ontouchmove = event => { | ||
if (event.targetTouches.length === 1) { | ||
handleScroll(event, targetElement) | ||
} | ||
targetElement.ontouchmove = (event) => { | ||
if (event.targetTouches.length !== 1) return | ||
handleScroll(event, targetElement) | ||
} | ||
locksElement.push(targetElement) | ||
lockedElements.push(targetElement) | ||
} | ||
if (!documentListenerAdded) { | ||
document.addEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined) | ||
document.addEventListener('touchmove', preventDefault, eventListenerOptions) | ||
documentListenerAdded = true | ||
} | ||
} else { | ||
} else if (lockedNum <= 0) { | ||
unLockCallback = detectOS().android ? setOverflowHiddenMobile() : setOverflowHiddenPc() | ||
} | ||
lockedNum += 1 | ||
} | ||
export const unlock = (targetElement) => { | ||
const unlock = (targetElement) => { | ||
if (isServer) return | ||
checkTargetElement(targetElement) | ||
lockedNum -= 1 | ||
if (lockedNum > 0 && !targetElement) return | ||
if (detectOS().ios) { | ||
const targetElementIndex = locksElement.indexOf(targetElement) | ||
if (targetElementIndex > -1) { | ||
targetElement.ontouchstart = null | ||
targetElement.ontouchmove = null | ||
locksElement.splice(targetElementIndex, 1) | ||
} | ||
if (documentListenerAdded && lockedNum <= 0) { | ||
document.removeEventListener('touchmove', preventDefault, hasPassiveEvents ? { passive: false } : undefined) | ||
documentListenerAdded = false | ||
} | ||
} else { | ||
lockedNum <= 0 && unLockCallback() | ||
if (lockedNum > 0) return | ||
if (!detectOS().ios) { | ||
lockedNum <= 0 && typeof unLockCallback === 'function' && unLockCallback() | ||
return | ||
} | ||
// iOS | ||
const index = lockedElements.indexOf(targetElement) | ||
if (index !== -1) { | ||
targetElement.ontouchmove = null | ||
targetElement.ontouchstart = null | ||
lockedElements.splice(index, 1) | ||
} | ||
if (documentListenerAdded) { | ||
document.removeEventListener('touchmove', preventDefault, eventListenerOptions) | ||
documentListenerAdded = false | ||
} | ||
} | ||
export { lock, unlock } |
{ | ||
"compilerOptions": { | ||
"experimentalDecorators": true, | ||
"allowJs": true, | ||
"baseUrl": ".", | ||
"experimentalDecorators": true, | ||
"paths":{ | ||
@@ -7,0 +7,0 @@ "@/*": ["src/*"], |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
45987
21
25
577
100
2