├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmignore ├── README.md ├── demo ├── index.html ├── index.js └── webpack.config.js ├── package.json └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-object-rest-spread", 7 | "add-module-exports" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | demo/dist 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | src 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # isolated-scroll 2 | 3 | Prevent scroll events from bubbling up to parent elements — [View demo](https://markdalgleish.github.io/isolated-scroll). 4 | 5 | ```bash 6 | $ npm install --save isolated-scroll 7 | ``` 8 | 9 | ## Usage 10 | 11 | ```js 12 | const isolatedScroll = require('isolated-scroll'); 13 | 14 | // Isolate scrolling of selected element: 15 | const unbindHandlers = isolatedScroll(element); 16 | 17 | // Remove isolated scroll behaviour: 18 | unbindHandlers(); 19 | ``` 20 | 21 | ## Todo 22 | 23 | - Add tests. 24 | 25 | ## License 26 | 27 | [MIT](https://markdalgleish.mit-license.org/) 28 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | isolated-scroll demo 5 | 6 | 7 | 8 | 9 | 25 | 26 | 27 | 28 |
29 |

isolated-scroll demo

30 | 31 |

With isolated-scroll:

32 | 33 |
34 | 116 |
117 | 118 |

Without isolated-scroll:

119 | 120 |
121 | 203 |
204 |
205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | require('./index.html'); 2 | 3 | import isolatedScroll from '../src'; 4 | 5 | const el = document.getElementById('isolated-scroll'); 6 | isolatedScroll(el); 7 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports = { 4 | context: __dirname, 5 | 6 | entry: './index.js', 7 | 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | filename: 'script.js' 11 | }, 12 | 13 | module: { 14 | loaders: [ 15 | { 16 | test: /\.html$/, 17 | loader: 'file?name=[name].[ext]' 18 | }, 19 | { 20 | test: /\.js$/, 21 | loader: 'babel' 22 | } 23 | ] 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "isolated-scroll", 3 | "version": "0.1.0", 4 | "description": "Prevent scoll events from bubbling up to parent elements", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --config demo/webpack.config.js", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "rm -rf lib/ && babel -d lib/ src/", 10 | "prepublish": "npm run build", 11 | "clean": "rm -rf demo/dist", 12 | "build": "npm run clean && webpack --config demo/webpack.config.js", 13 | "deploy": "npm run build && gh-pages -d demo/dist" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/markdalgleish/isolated-scroll.git" 18 | }, 19 | "author": "Mark Dalgleish", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/markdalgleish/isolated-scroll/issues" 23 | }, 24 | "homepage": "https://github.com/markdalgleish/isolated-scroll#readme", 25 | "devDependencies": { 26 | "babel-cli": "^6.11.4", 27 | "babel-core": "^6.13.2", 28 | "babel-loader": "^6.2.4", 29 | "babel-plugin-add-module-exports": "^0.2.1", 30 | "babel-plugin-transform-object-rest-spread": "^6.8.0", 31 | "babel-preset-es2015": "^6.13.2", 32 | "babel-register": "^6.11.6", 33 | "file-loader": "^0.9.0", 34 | "gh-pages": "^0.11.0", 35 | "webpack": "^1.13.1", 36 | "webpack-dev-server": "^1.14.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const calculateHeight = el => { 2 | // Source adapted from: http://youmightnotneedjquery.com/#outer_height_with_margin 3 | let height = el.offsetHeight; 4 | const { marginTop, marginBottom } = getComputedStyle(el); 5 | 6 | height += parseInt(marginTop, 10) + parseInt(marginBottom, 10); 7 | return height; 8 | }; 9 | 10 | // Source adapted from: http://stackoverflow.com/a/16324762 11 | const makeHandler = el => event => { 12 | const { scrollTop, scrollHeight } = el; 13 | const { type, detail, wheelDelta } = event; 14 | const height = calculateHeight(el); 15 | const delta = (type === 'DOMMouseScroll' ? detail * -40 : wheelDelta); 16 | const up = delta > 0; 17 | 18 | const prevent = () => { 19 | event.stopPropagation(); 20 | event.preventDefault(); 21 | event.returnValue = false; 22 | 23 | return false; 24 | }; 25 | 26 | if (!up && -delta > scrollHeight - height - scrollTop) { 27 | el.scrollTop = scrollHeight; 28 | return prevent(); 29 | } else if (up && delta > scrollTop) { 30 | el.scrollTop = 0; 31 | return prevent(); 32 | } 33 | }; 34 | 35 | export default el => { 36 | const handler = makeHandler(el); 37 | 38 | const addEvent = (el.addEventListener || el.attachEvent).bind(el); 39 | const removeEvent = (el.removeEventListener || el.detachEvent).bind(el); 40 | 41 | addEvent('mousewheel', handler); 42 | addEvent('DOMMouseScroll', handler); 43 | 44 | return () => { 45 | removeEvent('mousewheel', handler); 46 | removeEvent('DOMMouseScroll', handler); 47 | }; 48 | }; 49 | --------------------------------------------------------------------------------