├── .editorconfig ├── .gitignore ├── .npmignore ├── README.md ├── bower.json ├── docs ├── assets │ └── online-demo.png ├── css │ └── milligram.min.css ├── index.html └── js │ └── body-scroll-freezer.min.js ├── gulpfile.js ├── package.json └── src └── body-scroll-freezer.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.sass-cache 4 | *.com 5 | *.class 6 | *.dll 7 | *.exe 8 | *.o 9 | *.so 10 | 11 | # Packages # 12 | ############ 13 | # it's better to unpack these files and commit the raw source 14 | # git has its own built in compression methods 15 | *.7z 16 | *.dmg 17 | *.gz 18 | *.iso 19 | *.jar 20 | *.rar 21 | *.tar 22 | *.zip 23 | 24 | # Logs and databases # 25 | ###################### 26 | *.log 27 | *.sql 28 | *.sqlite 29 | 30 | # OS generated files # 31 | ###################### 32 | .DS_Store 33 | .DS_Store? 34 | ._* 35 | .Spotlight-V100 36 | .Trashes 37 | # Icon? 38 | ehthumbs.db 39 | Thumbs.db 40 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.sass-cache 4 | *.com 5 | *.class 6 | *.dll 7 | *.exe 8 | *.o 9 | *.so 10 | 11 | # Packages # 12 | ############ 13 | # it's better to unpack these files and commit the raw source 14 | # git has its own built in compression methods 15 | *.7z 16 | *.dmg 17 | *.gz 18 | *.iso 19 | *.jar 20 | *.rar 21 | *.tar 22 | *.zip 23 | 24 | # Logs and databases # 25 | ###################### 26 | *.log 27 | *.sql 28 | *.sqlite 29 | 30 | # OS generated files # 31 | ###################### 32 | .DS_Store 33 | .DS_Store? 34 | ._* 35 | .Spotlight-V100 36 | .Trashes 37 | # Icon? 38 | ehthumbs.db 39 | Thumbs.db 40 | 41 | # folders # 42 | ###################### 43 | node_modules 44 | 45 | # dev files # 46 | ###################### 47 | gulpfile.js 48 | .editorconfig 49 | .gitignore -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # body-scroll-freezer 2 | 3 | [![npm version](https://badge.fury.io/js/body-scroll-freezer.svg)](https://badge.fury.io/js/body-scroll-freezer) 4 | 5 | Dependency-free JS module to freeze body scroll when opening modal box. 6 | 7 | Useful for modal, sliding-panel and lightbox interfaces. 8 | 9 | > <1kb [minified](https://raw.githubusercontent.com/ramonvictor/body-scroll-freezer/master/docs/js/body-scroll-freezer.min.js). 10 | 11 | body-scroll-freezer: online demo 12 | 13 | ## How to install? 14 | 15 | ``` 16 | $ npm i body-scroll-freezer 17 | ``` 18 | 19 | ## A note on performance 20 | 21 | Many other alternatives to this module listen to both `mousewheel` and `DOMMouseScroll` events in order to get some information from the DOM, which usually includes: `Element.scrollTop`, `event.deltaY`, `Element.scrollHeight` or `Element.clientHeight`. Check this [StackOverflow answer](http://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element#answer-16324762) as an example. 22 | 23 | The problem is that most of those DOM operations (`.scrollTop` and `.scrollHeight`, for example) are expensive because they [force layout/reflow](https://gist.github.com/paulirish/5d52fb081b3570c81e3a). For more info on scrolling performance [check out this article](https://www.html5rocks.com/en/tutorials/speed/scrolling/). 24 | 25 | So, to avoid all that, **body-scroll-freezer** just assigns `overflow: hidden;` and `padding-right: [scrollWidth]px;` to the ``. 26 | The `overflow` avoids vertical move on the background when users are scrolling within the modal box. The `padding-right` prevents horizontal jumps when hiding/showing the scrollbar. 27 | 28 | ## Usage 29 | 30 | ```js 31 | // If no AMD/CommonJS: window.bodyScrollFreezer; 32 | var bodyScroll = require('body-scroll-freezer'); 33 | ``` 34 | 35 | 1\. Init to calculate scroll bar width. 36 | 37 | ```js 38 | // Note: declaring variable to store init() return is optional. 39 | var scrollWidth = bodyScroll.init(); 40 | ``` 41 | 42 | 2\. Turn scroll freeze **ON** when closing modal. Example: 43 | 44 | ```js 45 | document.querySelector('.modal-open').addEventListener('click', function() { 46 | // Logic to show modal goes here 47 | bodyScroll.freeze(); 48 | }, false); 49 | ``` 50 | 51 | 3\. Turn scroll freeze **OFF** when closing modal. Example: 52 | 53 | ```js 54 | document.querySelector('.modal-close').addEventListener('click', function() { 55 | // Logic to hide modal goes here 56 | bodyScroll.unfreeze(); 57 | }, false); 58 | ``` 59 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "body-scroll-freezer", 3 | "description": "Dependency-free JS module to freeze body scroll when opening modal box", 4 | "main": "docs/js/body-scroll-freezer.min.js", 5 | "authors": [ 6 | "@ramonvictor" 7 | ], 8 | "license": "MIT", 9 | "keywords": [ 10 | "scroll", 11 | "freeze", 12 | "toggle", 13 | "mousewheel" 14 | ], 15 | "homepage": "https://github.com/ramonvictor/body-scroll-freezer", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests", 22 | "gulpfile.js" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /docs/assets/online-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramonvictor/body-scroll-freezer/750de60cddeb7501b34cdc8d8fa0408107d901ce/docs/assets/online-demo.png -------------------------------------------------------------------------------- /docs/css/milligram.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Milligram v1.2.4 3 | * http://milligram.github.io 4 | * 5 | * Copyright (c) 2016 CJ Patoilo 6 | * Licensed under the MIT license 7 | */ 8 | 9 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#9b4dca;border:0.1rem solid #9b4dca;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#9b4dca;border-color:#9b4dca}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#9b4dca}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#9b4dca}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#9b4dca}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#9b4dca}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #9b4dca;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#9b4dca;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#9b4dca;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.0rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:3.0rem;line-height:1.3}h4{font-size:2.4rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}@media (min-width: 40rem){h1{font-size:5.0rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3.0rem}h5{font-size:2.4rem}h6{font-size:1.5rem}}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} 10 | 11 | /*# sourceMappingURL=milligram.min.css.map */ -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | body-scroll-freezer: dependency-free module to freeze body scroll when opening modal components 7 | 8 | 9 | 10 | 11 | 12 | 68 | 69 | 70 | 75 |
76 |
77 |
78 |

body-scroll-freezer

79 |

Dependency-free JS module to freeze body scroll when opening modal box.

80 |
81 |
82 | Star 83 |
84 |
85 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

86 |

87 | 88 |

89 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

90 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

91 | 95 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

96 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

97 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Esse libero, similique. Quo tempora maiores mollitia magni asperiores pariatur nobis amet, cupiditate quam impedit at libero laboriosam, deserunt ipsum, provident debitis.

98 |
99 | 110 | 111 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /docs/js/body-scroll-freezer.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * body-scroll-freezer - Dependency-free JS module to freeze body scroll when opening modal box 3 | * 4 | * @version v1.0.4 5 | * @link https://github.com/ramonvictor/body-scroll-freezer/ 6 | * @author @ramonvictor 7 | * @license MIT 8 | */ 9 | !function(){"use strict";function e(){return"undefined"!=typeof l?l:this.scrollWidth=l=i()}function t(){s.style.overflow="hidden",l&&(o()&&(s.style.paddingRight=l+"px"),c&&d(!0))}function n(){s.style.overflow="",l&&(s.style.paddingRight="",c&&d(!1))}function i(){var e,t=document.createElement("div");return t.setAttribute("style",["width: 100px","height: 100px","overflow: scroll","position: absolute","top: -9999px"].join(";")),s.appendChild(t),e=t.offsetWidth-t.clientWidth,s.removeChild(t),e}function o(){return s.scrollHeight>document.documentElement.clientHeight}function d(e){e?window.addEventListener("resize",r,!1):window.removeEventListener("resize",r,!1)}function r(){f||(o()?s.style.paddingRight=l+"px":s.style.paddingRight="",f=!0,window.setTimeout(function(){f=!1},150))}var l,u={},s=document.body,f=!1,c="addEventListener"in Element.prototype;u.init=e,u.freeze=t,u.unfreeze=n,"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=u:"undefined"!=typeof window&&(window.bodyScrollFreezer=u)}(); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var uglify = require('gulp-uglify'); 3 | var rename = require('gulp-rename'); 4 | var header = require('gulp-header'); 5 | var jshint = require('gulp-jshint'); 6 | var stylish = require('jshint-stylish'); 7 | var target = { 8 | js_concat_src: ['src/body-scroll-freezer.js'], 9 | js_dest: 'docs/js' 10 | }; 11 | 12 | gulp.task('js-uglify', function() { 13 | var pkg = require('./package.json'); 14 | var banner = [ 15 | '/**', 16 | ' * <%= pkg.name %> - <%= pkg.description %>', 17 | ' *', 18 | ' * @version v<%= pkg.version %>', 19 | ' * @link <%= pkg.homepage %>', 20 | ' * @author <%= pkg.author %>', 21 | ' * @license <%= pkg.license %>', 22 | ' */', 23 | ''].join('\n'); 24 | 25 | gulp.src(target.js_concat_src) 26 | .pipe(uglify()) 27 | .pipe(header(banner, {pkg: pkg})) 28 | .pipe(rename(function(dir, base, ext){ 29 | var trunc = base.split('.')[0]; 30 | return trunc + '.min' + ext; 31 | })) 32 | .pipe(gulp.dest(target.js_dest)); 33 | }); 34 | 35 | gulp.task('js-lint', function() { 36 | gulp.src(target.js_concat_src) 37 | .pipe(jshint()) 38 | .pipe(jshint.reporter(stylish)); 39 | }); 40 | 41 | gulp.task('default', ['js-lint', 'js-uglify']); 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "body-scroll-freezer", 3 | "author": "@ramonvictor", 4 | "description": "Dependency-free JS module to freeze body scroll when opening modal box", 5 | "license": "MIT", 6 | "version": "1.0.5", 7 | "main": "docs/js/body-scroll-freezer.min.js", 8 | "homepage": "https://github.com/ramonvictor/body-scroll-freezer/", 9 | "repository": "ramonvictor/body-scroll-freezer", 10 | "scripts": { 11 | "start": "gulp" 12 | }, 13 | "dependencies": {}, 14 | "engines": {}, 15 | "devDependencies": { 16 | "gulp": "^3.9.1", 17 | "gulp-header": "^1.8.8", 18 | "gulp-jshint": "^2.0.1", 19 | "gulp-rename": "~0.2.2", 20 | "gulp-uglify": "~0.2.0", 21 | "jshint": "^2.9.2", 22 | "jshint-stylish": "^2.2.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/body-scroll-freezer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | /* 4 | * Dependency-free module to freeze body scroll when opening modal components. 5 | * @module body-scroll-freezer 6 | */ 7 | var API = {}; 8 | var body = document.body; 9 | var scrollWidth; 10 | var resizeWait = false; 11 | var supportsEventListener = ('addEventListener' in Element.prototype); 12 | 13 | /** 14 | * Init module by getting browser scroll width. 15 | * @public 16 | * @return {Number} Browser scroll bar width 17 | */ 18 | function init() { 19 | /* jshint validthis:true */ 20 | if (typeof scrollWidth !== 'undefined') { 21 | return scrollWidth; 22 | } 23 | return (this.scrollWidth = scrollWidth = getScrollWidth()); 24 | } 25 | 26 | /** 27 | * Freeze body scroll. 28 | * @public 29 | */ 30 | function freeze() { 31 | body.style.overflow = 'hidden'; 32 | 33 | if (!scrollWidth) { 34 | return; 35 | } 36 | 37 | if (windowHasScroll()) { 38 | body.style.paddingRight = scrollWidth + 'px'; 39 | } 40 | 41 | if (supportsEventListener) { 42 | toggleResizeListener(true); 43 | } 44 | } 45 | 46 | /** 47 | * Unfreeze body scroll. 48 | * @public 49 | */ 50 | function unfreeze() { 51 | body.style.overflow = ''; 52 | 53 | if (!scrollWidth) { 54 | return; 55 | } 56 | 57 | body.style.paddingRight = ''; 58 | 59 | if (supportsEventListener) { 60 | toggleResizeListener(false); 61 | } 62 | } 63 | 64 | /** 65 | * Append/remove `
` to measure browser's scrollbar width. 66 | * Props to {@link https://davidwalsh.name/detect-scrollbar-width|@davidwalshblog}. 67 | * @private 68 | * @return {Number} Browser scroll bar width 69 | */ 70 | function getScrollWidth() { 71 | var div = document.createElement('div'); 72 | var scrollBarWidth; 73 | 74 | div.setAttribute('style', [ 75 | 'width: 100px', 76 | 'height: 100px', 77 | 'overflow: scroll', 78 | 'position: absolute', 79 | 'top: -9999px' 80 | ].join(';')); 81 | 82 | body.appendChild(div); 83 | scrollBarWidth = div.offsetWidth - div.clientWidth; 84 | body.removeChild(div); 85 | 86 | return scrollBarWidth; 87 | } 88 | 89 | /** 90 | * Check whether window scroll is visible or not. 91 | * @private 92 | * @return {Boolean} 93 | */ 94 | function windowHasScroll() { 95 | return ( 96 | body.scrollHeight > document.documentElement.clientHeight 97 | ); 98 | } 99 | 100 | /** 101 | * Switch resize listener on/off depending on freeze state. 102 | * @private 103 | */ 104 | function toggleResizeListener(isFrozen) { 105 | if (isFrozen) { 106 | window.addEventListener('resize', onWindowResize, false); 107 | } else { 108 | window.removeEventListener('resize', onWindowResize, false); 109 | } 110 | } 111 | 112 | /** 113 | * Throttled logic to update body padding based on window scroll visibility. 114 | * @private 115 | */ 116 | function onWindowResize() { 117 | if (resizeWait) { 118 | return; 119 | } 120 | 121 | if (windowHasScroll()) { 122 | body.style.paddingRight = scrollWidth + 'px'; 123 | } else { 124 | body.style.paddingRight = ''; 125 | } 126 | 127 | resizeWait = true; 128 | window.setTimeout(function() { resizeWait = false; }, 150); 129 | } 130 | 131 | /** 132 | * Public api. 133 | */ 134 | API.init = init; 135 | API.freeze = freeze; 136 | API.unfreeze = unfreeze; 137 | 138 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { 139 | module.exports = API; 140 | } else if (typeof define === "function" && define.amd) { 141 | define([], function () { 142 | return API; 143 | }); 144 | } else if (typeof window !== 'undefined') { 145 | window.bodyScrollFreezer = API; 146 | } 147 | })(); 148 | --------------------------------------------------------------------------------