├── cypress.json ├── .eslintignore ├── fixtures └── demo.gif ├── .gitignore ├── .travis.yml ├── cypress ├── plugins │ └── index.js ├── support │ ├── index.js │ └── commands.js └── integration │ └── test.js ├── .eslintrc.js ├── LICENSE ├── rollup.config.js ├── CHANGELOG.md ├── package.json ├── dist ├── overflow-color.umd.min.js ├── overflow-color.esm.js ├── overflow-color.cjs.js └── overflow-color.umd.js ├── index.html ├── README.md └── src └── index.js /cypress.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*/dist/**/* 2 | **/*/node_modules/**/* 3 | **/*/vendor/**/* 4 | **/*/cypress/**/* 5 | -------------------------------------------------------------------------------- /fixtures/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimitrinicolas/overflow-color/HEAD/fixtures/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | tmp 3 | 4 | node_modules 5 | .npm-debug.log 6 | logs 7 | *.log* 8 | 9 | cypress/fixtures 10 | cypress/videos 11 | cypress/screenshots 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | - "8" 6 | 7 | install: 8 | - npm install 9 | 10 | # Telegram bot 11 | notifications: 12 | webhooks: https://fathomless-fjord-24024.herokuapp.com/notify 13 | -------------------------------------------------------------------------------- /cypress/plugins/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example plugins/index.js can be used to load plugins 3 | // 4 | // You can change the location of this file or turn off loading 5 | // the plugins file with the 'pluginsFile' configuration option. 6 | // 7 | // You can read more here: 8 | // https://on.cypress.io/plugins-guide 9 | // *********************************************************** 10 | 11 | // This function is called when a project is opened or re-opened (e.g. due to 12 | // the project's config changing) 13 | 14 | module.exports = (on, config) => { 15 | // `on` is used to hook into various events Cypress emits 16 | // `config` is the resolved Cypress config 17 | } 18 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true 4 | }, 5 | extends: 'airbnb', 6 | parser: 'babel-eslint', 7 | rules : { 8 | 'max-len': ['error', { 9 | 'code': 150 10 | }], 11 | 'comma-dangle': 0, 12 | 'no-plusplus': 0, 13 | 'arrow-parens': ['error', 'as-needed'], 14 | 'no-multi-assign': 0, 15 | 'strict': 0, 16 | 'no-console': 0, 17 | 'prefer-destructuring': 0, 18 | 'function-paren-newline': 0, 19 | 'global-require': 0, 20 | 'prefer-spread': 0, 21 | 'prefer-rest-params': 0, 22 | 'prefer-arrow-callback': 0, 23 | 'arrow-body-style': 0, 24 | 'no-restricted-globals': 0, 25 | 'no-restricted-syntax': 0, 26 | 'consistent-return': 0, 27 | 'no-param-reassign': 0, 28 | 'no-underscore-dangle': 0, 29 | 'import/no-unresolved': 0, 30 | 'import/no-dynamic-require': 0, 31 | 'import/no-extraneous-dependencies': 0, 32 | 'import/extensions': ['warn', 'ignorePackages'] 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Dimitri Nicolas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import commonjs from 'rollup-plugin-commonjs'; 4 | import pkg from './package.json'; 5 | 6 | export default [ 7 | { 8 | input: 'src/index.js', 9 | output: { 10 | name: 'overflowColor', 11 | file: pkg.browser, 12 | format: 'umd' 13 | }, 14 | plugins: [ 15 | babel({ 16 | presets: [ 17 | [ 18 | 'env', 19 | { modules: false } 20 | ] 21 | ], 22 | plugins: [ 23 | 'external-helpers' 24 | ], 25 | externalHelpers: true 26 | }), 27 | resolve(), 28 | commonjs() 29 | ] 30 | }, 31 | { 32 | input: 'src/index.js', 33 | external: ['ms'], 34 | output: [ 35 | { file: pkg.main, format: 'cjs' }, 36 | { file: pkg.module, format: 'es' } 37 | ], 38 | plugins: [ 39 | babel({ 40 | presets: [ 41 | [ 42 | 'env', 43 | { modules: false } 44 | ] 45 | ], 46 | plugins: [ 47 | 'external-helpers' 48 | ], 49 | externalHelpers: true 50 | }) 51 | ] 52 | } 53 | ]; 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 3 | 4 | ## 2.3.0 - 2018-11-22 5 | ### Added 6 | - A `data-oc-outside` attribute to exclude a body's immediate children to be put inside `data-oc-wrap` element 7 | 8 | ## 2.2.0 - 2018-11-22 9 | ### Added 10 | - The default exported function is now `update` and check for attribute and scroll change 11 | 12 | ## 2.1.5 - 2018-10-25 13 | ### Changed 14 | - Improved performance by passively listening to scroll event 15 | 16 | ## 2.1.4 - 2018-08-03 17 | ### Changed 18 | - Improved performance by throttling the scroll event listener through requestAnimationFrame. 19 | - Now default exporting a force `checkScroll` function 20 | 21 | ## 2.1.3 - 2018-05-14 22 | ### Changed 23 | - Background color switch process for better performances. 24 | 25 | ## 2.1.2 - 2018-05-11 26 | ### Changed 27 | - Now bundling the library with Rollup and exporting 3 versions: `main`, `module` and `browser` respectively as follows: `overflow-color.cjs.js`, `overflow-color.esm.js` and `overflow-color.umd.js` (plus `overflow-color.umd.min.js`). 28 | 29 | ## 2.1.1 - 2018-05-10 30 | ### Changed 31 | - The test for page loaded or now, so the script can now be loaded `async`. 32 | 33 | ## 2.1.0 - 2018-03-10 34 | ### Added 35 | - A shortcut `data-oc` for `data-oc-top` and `data-oc-bottom` combined. 36 | 37 | ## 2.0.1 - 2018-02-21 38 | ### Fixed 39 | - A bug on Firefox. 40 | 41 | ## 2.0.0 - 2018-02-06 42 | - Complete rework. 43 | 44 | ## 1.0.0 - 2017-03-02 45 | - Initial release. -------------------------------------------------------------------------------- /cypress/integration/test.js: -------------------------------------------------------------------------------- 1 | // / 2 | 3 | context('Test', () => { 4 | beforeEach(() => { 5 | cy.viewport(300, 600).visit('http://localhost:8000'); 6 | }); 7 | 8 | it('page contain github link', () => { 9 | cy.get('.header a') 10 | .should('have.attr', 'href') 11 | .and('include', 'github'); 12 | }); 13 | 14 | it('overflow color wrap background is white', () => { 15 | cy.get('[data-oc-wrap]').should('have.css', 'background-color', 'rgb(255, 255, 255)'); 16 | }); 17 | 18 | it('html first background is red', () => { 19 | cy.get('html').should('have.css', 'background-color', 'rgb(255, 0, 0)'); 20 | }); 21 | 22 | it('html page end background is blue', () => { 23 | cy.get('p:last-of-type').scrollIntoView(); 24 | cy.get('html').should('have.css', 'background-color', 'rgb(0, 0, 255)'); 25 | }); 26 | 27 | it('html page top background is blue', () => { 28 | cy.get('p:last-of-type').scrollIntoView(); 29 | cy.scrollTo(0); 30 | cy.get('html').should('have.css', 'background-color', 'rgb(255, 0, 0)'); 31 | }); 32 | 33 | it('update body attribute', () => { 34 | cy.get('body').invoke('attr', 'data-oc', 'lime,yellow'); 35 | cy.scrollTo(0); 36 | cy.window() 37 | .then( 38 | win => new Cypress.Promise(resolve => { 39 | win.updateOverflowColor(); 40 | resolve(); 41 | }) 42 | ) 43 | .then(() => { 44 | cy.get('html').should('have.css', 'background-color', 'rgb(0, 255, 0)'); 45 | cy.get('p:last-of-type').scrollIntoView(); 46 | cy.get('html').should('have.css', 'background-color', 'rgb(255, 255, 0)'); 47 | }); 48 | }); 49 | 50 | it('outside element', () => { 51 | cy.get('body') 52 | .find('> [data-oc-outside]') 53 | .should('exist'); 54 | cy.get('[data-oc-wrap]') 55 | .find('[data-oc-outside]') 56 | .should('not.exist'); 57 | cy.get('[data-oc-wrap]') 58 | .find('+ [data-oc-outside]') 59 | .should('exist'); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overflow-color", 3 | "version": "2.3.0", 4 | "description": "Automatically switch css html background color", 5 | "main": "dist/overflow-color.cjs.js", 6 | "module": "dist/overflow-color.esm.js", 7 | "browser": "dist/overflow-color.umd.js", 8 | "scripts": { 9 | "publish": "clean-publish --files cypress fixtures cypress.json", 10 | "start:ci": "http-server ./ -a localhost -p 8000 -c-1", 11 | "cypress:run": "cypress run", 12 | "cypress:open": "cypress open", 13 | "cypress": "npm run build && run-p --race start:ci cypress:open", 14 | "test": "npm run build && run-p --race start:ci cypress:run", 15 | "build": "rollup -c && uglifyjs dist/overflow-color.umd.js -o dist/overflow-color.umd.min.js -m", 16 | "lint": "eslint src/**/**/*.js" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/dimitrinicolas/overflow-color.git" 21 | }, 22 | "keywords": [ 23 | "overscroll", 24 | "overflow", 25 | "color", 26 | "css", 27 | "front-end", 28 | "javascript" 29 | ], 30 | "author": "Dimitri NICOLAS ", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/dimitrinicolas/overflow-color/issues" 34 | }, 35 | "homepage": "https://github.com/dimitrinicolas/overflow-color", 36 | "devDependencies": { 37 | "babel-core": "^6.26.3", 38 | "babel-eslint": "^8.2.6", 39 | "babel-plugin-external-helpers": "^6.22.0", 40 | "babel-preset-env": "^1.7.0", 41 | "clean-publish": "^1.0.9", 42 | "cypress": "^3.1.0", 43 | "eslint": "^5.4.0", 44 | "eslint-config-airbnb": "^17.1.0", 45 | "eslint-plugin-import": "^2.14.0", 46 | "eslint-plugin-jsx-a11y": "^6.1.1", 47 | "eslint-plugin-react": "^7.11.1", 48 | "http-server": "^0.11.1", 49 | "install": "^0.12.1", 50 | "npm": "^6.4.0", 51 | "npm-run-all": "^4.1.3", 52 | "rollup": "^0.64.1", 53 | "rollup-plugin-babel": "^3.0.7", 54 | "rollup-plugin-commonjs": "^9.1.5", 55 | "rollup-plugin-node-resolve": "^3.3.0", 56 | "uglify-js": "^3.4.7" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /dist/overflow-color.umd.min.js: -------------------------------------------------------------------------------- 1 | (function(e,t){typeof exports==="object"&&typeof module!=="undefined"?module.exports=t():typeof define==="function"&&define.amd?define(t):e.overflowColor=t()})(this,function(){"use strict";var a="data-oc";var u=void 0;var l=void 0;var d=void 0;var r=void 0;var o=void 0;var n=false;var t=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)}}();var i=function e(t){if(d!==t){d=t;var o="html { background: "+d+"; }";if(!r){r=document.createElement("style");var n=document.head||document.getElementsByTagName("head")[0];n.appendChild(r)}if(r.styleSheet){r.styleSheet.cssText=o}else{r.innerHTML=o}}};var c=function e(){o=window.scrollY;if(!n&&(u||l)){t(function(){var e=document.body.scrollHeight;var t=window.innerHeight;if(e===t){i(l)}else{i(t-e+2*o<0?u:l)}n=false});n=true}};var s=function e(){u=null;l=null;var t=document.querySelector("["+a+"]");if(t){var o=t.getAttribute(a).split(",");if(o.length>1){u=o[0];l=o[1]}else if(o.length===1){u=l=o[0]}}else{var n=document.querySelector("["+a+"-top]");var d=document.querySelector("["+a+"-bottom]");if(n){u=n.getAttribute(a+"-top")}if(d){l=d.getAttribute(a+"-bottom")}}if(!u&&l){u=l}else if(u&&!l){l=u}var r=window.getComputedStyle(document.body,null);var i=r.getPropertyValue("background");if(i===""||r.getPropertyValue("background-color")==="rgba(0, 0, 0, 0)"&&i.substring(21,17)==="none"){i="white"}document.body.style.background="transparent";c()};var e=function e(){var t=window.getComputedStyle(document.body,null);var o=t.getPropertyValue("background");if(o===""||t.getPropertyValue("background-color")==="rgba(0, 0, 0, 0)"&&o.substring(21,17)==="none"){o="white"}document.body.style.background="transparent";var n=document.createElement("div");n.setAttribute(a+"-wrap","");n.style.background=o;for(var d=document.body.childNodes.length-1;d>0;d--){var r=document.body.childNodes[d];if(typeof r.getAttribute!=="function"||r.getAttribute(a+"-outside")===null){n.insertBefore(r,n.childNodes[0])}}if(document.body.childNodes.length){document.body.insertBefore(n,document.body.childNodes[0])}else{document.body.appendChild(n)}s();if(typeof window.addEventListener!=="undefined"){window.addEventListener("scroll",c,{passive:true});window.addEventListener("resize",c,{passive:true})}else{window.attachEvent("scroll",c);window.attachEvent("resize",c)}};if(["interactive","complete","loaded"].indexOf(document.readyState)!==-1){e()}else if(typeof document.addEventListener!=="undefined"){document.addEventListener("DOMContentLoaded",e,false)}else{document.attachEvent("onreadystatechange",e)}window.updateOverflowColor=s;return s}); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overflow color demo 7 | 35 | 36 | 37 | 38 | 41 | 42 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero nam fugiat architecto, rem dignissimos, autem, eius blanditiis recusandae ut voluptatibus, earum atque similique eligendi obcaecati laborum doloremque. Consequatur, ad laborum.

43 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium culpa aspernatur reprehenderit itaque id voluptatibus. Ea repellat quae non molestiae numquam minus voluptates, beatae. Sunt enim earum eum repellat. Ab?

44 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero nam fugiat architecto, rem dignissimos, autem, eius blanditiis recusandae ut voluptatibus, earum atque similique eligendi obcaecati laborum doloremque. Consequatur, ad laborum.

45 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium culpa aspernatur reprehenderit itaque id voluptatibus. Ea repellat quae non molestiae numquam minus voluptates, beatae. Sunt enim earum eum repellat. Ab?

46 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero nam fugiat architecto, rem dignissimos, autem, eius blanditiis recusandae ut voluptatibus, earum atque similique eligendi obcaecati laborum doloremque. Consequatur, ad laborum.

47 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium culpa aspernatur reprehenderit itaque id voluptatibus. Ea repellat quae non molestiae numquam minus voluptates, beatae. Sunt enim earum eum repellat. Ab?

48 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero nam fugiat architecto, rem dignissimos, autem, eius blanditiis recusandae ut voluptatibus, earum atque similique eligendi obcaecati laborum doloremque. Consequatur, ad laborum.

49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # overflow-color [![Build Status][travis badge]][travis link] ![0 dependency][dependency badge] ![npm bundle size (minified + gzip)][size badge] 2 | 3 | [![Demo](fixtures/demo.gif)][demo] 4 | 5 | Try it on your smartphone or with a trackpad on MacOS: [git.io/overflow][demo] 6 | ([https://dimitrinicolas.github.io/overflow-color/][demo]) 7 | 8 | A simple script that automatically switches CSS `html` `background-color` 9 | according to scroll position. 10 | 11 | This package is on npm 12 | 13 | ```console 14 | npm i overflow-color 15 | ``` 16 | 17 | ## Usage 18 | 19 | You simply must add the browser minified script 20 | [dist/overflow-color.umd.min.js](dist/overflow-color.umd.min.js) and the two 21 | attributes `data-oc-top` and `data-oc-bottom` to your body tag. 22 | 23 | ```html 24 | 25 | 26 | 27 | 28 | ``` 29 | 30 | You can use the shortcut `data-oc` by separating the two values with a comma. 31 | 32 | ```html 33 | 34 | ``` 35 | 36 | ### With a module bundler 37 | 38 | You can import this package with a simple require or import. 39 | 40 | ```javascript 41 | require('overflow-color'); 42 | // with ES6+ 43 | import 'overflow-color'; 44 | ``` 45 | 46 | You don't have anything else to do, the script is automatically launched when 47 | the DOM content is loaded. 48 | 49 | #### Manually re-check scroll position 50 | 51 | When you update body's overflow color attribute or make a huge change to the DOM 52 | (e.g. changing page with Angular) and that the vertical scroll position stayed 53 | at the top, the new document height may be smaller than the previous one, and 54 | `overflow-color` should set the overflow color to the bottom one, but it has not 55 | be called by scroll event. In this specific case, you can use the default 56 | exported function `updateOverflowColor`: 57 | 58 | ```javascript 59 | import updateOverflowColor from 'overflow-color'; 60 | 61 | /** Change DOM or body's overflow color attribute */ 62 | 63 | updateOverflowColor(); 64 | ``` 65 | 66 | ## CSS tricks, Behind the Scenes 67 | 68 | This library will wrap all the body content inside a `
`. 69 | Then it set to the wrapper the same background as the `body`, and set `body`'s 70 | background to `transparent`. 71 | 72 | Add the `data-oc-outside` attribute to any body's immediate children that you 73 | don't want to be included into this wrap element at DOM content load. 74 | 75 | when the document is loaded: 76 | 77 | ```html 78 | 79 | 80 | 81 | 87 | 91 | 92 | 93 | 94 |
95 | 96 | 97 |
98 | 99 | ``` 100 | 101 | You can read the [Designer News discussion][dn] about the different tried 102 | tricks. 103 | 104 | ## Browsers compatibility 105 | 106 | I successfully tested this library, on: 107 | **Mac OS Mojave**: Safari v11, Google Chrome v70 and Opera v51 108 | **iOS 11**: Safari, Google Chrome v68, Firefox 12 and Microsoft Edge v42 109 | 110 | Unfortunately, Firefox on MacOS and Opera Mini on iOS doesn't show the 111 | over-scroll color even without this library. 112 | 113 | This library pass an E2E test through Chrome with Cypress. 114 | 115 | ## Build 116 | 117 | Compile with Rollup: 118 | 119 | ```console 120 | npm run build 121 | ``` 122 | 123 | Build and test with Cypress: 124 | 125 | ```console 126 | npm test 127 | ``` 128 | 129 | ## License 130 | 131 | This project is licensed under the [MIT license](LICENSE). 132 | 133 | [travis badge]: https://travis-ci.org/dimitrinicolas/overflow-color.svg?branch=master 134 | [travis link]: https://travis-ci.org/dimitrinicolas/overflow-color 135 | [dependency badge]: https://img.shields.io/badge/dependencies-0-brightgreen.svg 136 | [size badge]: https://img.shields.io/bundlephobia/minzip/overflow-color.svg 137 | 138 | [demo]: https://dimitrinicolas.github.io/overflow-color/ 139 | [dn]: https://www.designernews.co/stories/91663-switch-html-background-color-in-order-to-get-two-overflow-scrolling-colors-on-smartphones 140 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const ATTRIBUTE_PREFIX = 'data-oc'; 2 | 3 | let topColor; 4 | let bottomColor; 5 | 6 | let currentBgColor; 7 | let styleTag; 8 | 9 | let lastScrollY; 10 | let ticking = false; 11 | 12 | /** 13 | * Request animation frame polyfill 14 | * @param {function} callback 15 | */ 16 | const requestAnimFrame = (() => { 17 | return ( 18 | window.requestAnimationFrame 19 | || window.webkitRequestAnimationFrame 20 | || window.mozRequestAnimationFrame 21 | || (callback => { 22 | window.setTimeout(callback, 1000 / 60); 23 | }) 24 | ); 25 | })(); 26 | 27 | /** 28 | * If needed, set the new new color as 29 | * html background 30 | * @param {string} color 31 | */ 32 | const setBgColor = color => { 33 | if (currentBgColor !== color) { 34 | currentBgColor = color; 35 | const css = `html { background: ${currentBgColor}; }`; 36 | 37 | if (!styleTag) { 38 | styleTag = document.createElement('style'); 39 | const head = document.head || document.getElementsByTagName('head')[0]; 40 | head.appendChild(styleTag); 41 | } 42 | 43 | if (styleTag.styleSheet) { 44 | styleTag.styleSheet.cssText = css; 45 | } else { 46 | styleTag.innerHTML = css; 47 | } 48 | } 49 | }; 50 | 51 | /** 52 | * Checks the scroll position and determines 53 | * the overflow color to set between the 54 | * topColor and the bottomColor 55 | */ 56 | const checkScroll = () => { 57 | lastScrollY = window.scrollY; 58 | if (!ticking && (topColor || bottomColor)) { 59 | requestAnimFrame(() => { 60 | const scrollHeight = document.body.scrollHeight; 61 | const innerHeight = window.innerHeight; 62 | if (scrollHeight === innerHeight) { 63 | setBgColor(bottomColor); 64 | } else { 65 | setBgColor(innerHeight - scrollHeight + 2 * lastScrollY < 0 ? topColor : bottomColor); 66 | } 67 | ticking = false; 68 | }); 69 | ticking = true; 70 | } 71 | }; 72 | 73 | /** 74 | * Update colors and check scroll 75 | */ 76 | const updateOverflowColor = () => { 77 | topColor = null; 78 | bottomColor = null; 79 | 80 | const shortcutAttributeEl = document.querySelector(`[${ATTRIBUTE_PREFIX}]`); 81 | if (shortcutAttributeEl) { 82 | const split = shortcutAttributeEl.getAttribute(ATTRIBUTE_PREFIX).split(','); 83 | if (split.length > 1) { 84 | topColor = split[0]; 85 | bottomColor = split[1]; 86 | } else if (split.length === 1) { 87 | topColor = bottomColor = split[0]; 88 | } 89 | } else { 90 | const topAttributeEl = document.querySelector(`[${ATTRIBUTE_PREFIX}-top]`); 91 | const bottomAttributeEl = document.querySelector(`[${ATTRIBUTE_PREFIX}-bottom]`); 92 | if (topAttributeEl) { 93 | topColor = topAttributeEl.getAttribute(`${ATTRIBUTE_PREFIX}-top`); 94 | } 95 | if (bottomAttributeEl) { 96 | bottomColor = bottomAttributeEl.getAttribute(`${ATTRIBUTE_PREFIX}-bottom`); 97 | } 98 | } 99 | 100 | if (!topColor && bottomColor) { 101 | topColor = bottomColor; 102 | } else if (topColor && !bottomColor) { 103 | bottomColor = topColor; 104 | } 105 | 106 | const bodyComputedStyle = window.getComputedStyle(document.body, null); 107 | let bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 108 | if ( 109 | bodyComputedBackground === '' 110 | || (bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') 111 | ) { 112 | bodyComputedBackground = 'white'; 113 | } 114 | document.body.style.background = 'transparent'; 115 | 116 | checkScroll(); 117 | }; 118 | 119 | /** 120 | * Gets the two overflow colors and init the 121 | * window scroll and resize event listeners 122 | */ 123 | const initOverflowColor = () => { 124 | const bodyComputedStyle = window.getComputedStyle(document.body, null); 125 | let bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 126 | if ( 127 | bodyComputedBackground === '' 128 | || (bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') 129 | ) { 130 | bodyComputedBackground = 'white'; 131 | } 132 | document.body.style.background = 'transparent'; 133 | 134 | const bodyWrapperEl = document.createElement('div'); 135 | bodyWrapperEl.setAttribute(`${ATTRIBUTE_PREFIX}-wrap`, ''); 136 | bodyWrapperEl.style.background = bodyComputedBackground; 137 | for (let i = document.body.childNodes.length - 1; i > 0; i--) { 138 | const child = document.body.childNodes[i]; 139 | if (typeof child.getAttribute !== 'function' || child.getAttribute(`${ATTRIBUTE_PREFIX}-outside`) === null) { 140 | bodyWrapperEl.insertBefore(child, bodyWrapperEl.childNodes[0]); 141 | } 142 | } 143 | if (document.body.childNodes.length) { 144 | document.body.insertBefore(bodyWrapperEl, document.body.childNodes[0]); 145 | } else { 146 | document.body.appendChild(bodyWrapperEl); 147 | } 148 | 149 | updateOverflowColor(); 150 | 151 | if (typeof window.addEventListener !== 'undefined') { 152 | window.addEventListener('scroll', checkScroll, { passive: true }); 153 | window.addEventListener('resize', checkScroll, { passive: true }); 154 | } else { 155 | window.attachEvent('scroll', checkScroll); 156 | window.attachEvent('resize', checkScroll); 157 | } 158 | }; 159 | 160 | if (['interactive', 'complete', 'loaded'].indexOf(document.readyState) !== -1) { 161 | initOverflowColor(); 162 | } else if (typeof document.addEventListener !== 'undefined') { 163 | document.addEventListener('DOMContentLoaded', initOverflowColor, false); 164 | } else { 165 | document.attachEvent('onreadystatechange', initOverflowColor); 166 | } 167 | 168 | window.updateOverflowColor = updateOverflowColor; 169 | 170 | export default updateOverflowColor; 171 | -------------------------------------------------------------------------------- /dist/overflow-color.esm.js: -------------------------------------------------------------------------------- 1 | var ATTRIBUTE_PREFIX = 'data-oc'; 2 | 3 | var topColor = void 0; 4 | var bottomColor = void 0; 5 | 6 | var currentBgColor = void 0; 7 | var styleTag = void 0; 8 | 9 | var lastScrollY = void 0; 10 | var ticking = false; 11 | 12 | /** 13 | * Request animation frame polyfill 14 | * @param {function} callback 15 | */ 16 | var requestAnimFrame = function () { 17 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { 18 | window.setTimeout(callback, 1000 / 60); 19 | }; 20 | }(); 21 | 22 | /** 23 | * If needed, set the new new color as 24 | * html background 25 | * @param {string} color 26 | */ 27 | var setBgColor = function setBgColor(color) { 28 | if (currentBgColor !== color) { 29 | currentBgColor = color; 30 | var css = 'html { background: ' + currentBgColor + '; }'; 31 | 32 | if (!styleTag) { 33 | styleTag = document.createElement('style'); 34 | var head = document.head || document.getElementsByTagName('head')[0]; 35 | head.appendChild(styleTag); 36 | } 37 | 38 | if (styleTag.styleSheet) { 39 | styleTag.styleSheet.cssText = css; 40 | } else { 41 | styleTag.innerHTML = css; 42 | } 43 | } 44 | }; 45 | 46 | /** 47 | * Checks the scroll position and determines 48 | * the overflow color to set between the 49 | * topColor and the bottomColor 50 | */ 51 | var checkScroll = function checkScroll() { 52 | lastScrollY = window.scrollY; 53 | if (!ticking && (topColor || bottomColor)) { 54 | requestAnimFrame(function () { 55 | var scrollHeight = document.body.scrollHeight; 56 | var innerHeight = window.innerHeight; 57 | if (scrollHeight === innerHeight) { 58 | setBgColor(bottomColor); 59 | } else { 60 | setBgColor(innerHeight - scrollHeight + 2 * lastScrollY < 0 ? topColor : bottomColor); 61 | } 62 | ticking = false; 63 | }); 64 | ticking = true; 65 | } 66 | }; 67 | 68 | /** 69 | * Update colors and check scroll 70 | */ 71 | var updateOverflowColor = function updateOverflowColor() { 72 | topColor = null; 73 | bottomColor = null; 74 | 75 | var shortcutAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + ']'); 76 | if (shortcutAttributeEl) { 77 | var split = shortcutAttributeEl.getAttribute(ATTRIBUTE_PREFIX).split(','); 78 | if (split.length > 1) { 79 | topColor = split[0]; 80 | bottomColor = split[1]; 81 | } else if (split.length === 1) { 82 | topColor = bottomColor = split[0]; 83 | } 84 | } else { 85 | var topAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-top]'); 86 | var bottomAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-bottom]'); 87 | if (topAttributeEl) { 88 | topColor = topAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-top'); 89 | } 90 | if (bottomAttributeEl) { 91 | bottomColor = bottomAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-bottom'); 92 | } 93 | } 94 | 95 | if (!topColor && bottomColor) { 96 | topColor = bottomColor; 97 | } else if (topColor && !bottomColor) { 98 | bottomColor = topColor; 99 | } 100 | 101 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 102 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 103 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 104 | bodyComputedBackground = 'white'; 105 | } 106 | document.body.style.background = 'transparent'; 107 | 108 | checkScroll(); 109 | }; 110 | 111 | /** 112 | * Gets the two overflow colors and init the 113 | * window scroll and resize event listeners 114 | */ 115 | var initOverflowColor = function initOverflowColor() { 116 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 117 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 118 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 119 | bodyComputedBackground = 'white'; 120 | } 121 | document.body.style.background = 'transparent'; 122 | 123 | var bodyWrapperEl = document.createElement('div'); 124 | bodyWrapperEl.setAttribute(ATTRIBUTE_PREFIX + '-wrap', ''); 125 | bodyWrapperEl.style.background = bodyComputedBackground; 126 | for (var i = document.body.childNodes.length - 1; i > 0; i--) { 127 | var child = document.body.childNodes[i]; 128 | if (typeof child.getAttribute !== 'function' || child.getAttribute(ATTRIBUTE_PREFIX + '-outside') === null) { 129 | bodyWrapperEl.insertBefore(child, bodyWrapperEl.childNodes[0]); 130 | } 131 | } 132 | if (document.body.childNodes.length) { 133 | document.body.insertBefore(bodyWrapperEl, document.body.childNodes[0]); 134 | } else { 135 | document.body.appendChild(bodyWrapperEl); 136 | } 137 | 138 | updateOverflowColor(); 139 | 140 | if (typeof window.addEventListener !== 'undefined') { 141 | window.addEventListener('scroll', checkScroll, { passive: true }); 142 | window.addEventListener('resize', checkScroll, { passive: true }); 143 | } else { 144 | window.attachEvent('scroll', checkScroll); 145 | window.attachEvent('resize', checkScroll); 146 | } 147 | }; 148 | 149 | if (['interactive', 'complete', 'loaded'].indexOf(document.readyState) !== -1) { 150 | initOverflowColor(); 151 | } else if (typeof document.addEventListener !== 'undefined') { 152 | document.addEventListener('DOMContentLoaded', initOverflowColor, false); 153 | } else { 154 | document.attachEvent('onreadystatechange', initOverflowColor); 155 | } 156 | 157 | window.updateOverflowColor = updateOverflowColor; 158 | 159 | export default updateOverflowColor; 160 | -------------------------------------------------------------------------------- /dist/overflow-color.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ATTRIBUTE_PREFIX = 'data-oc'; 4 | 5 | var topColor = void 0; 6 | var bottomColor = void 0; 7 | 8 | var currentBgColor = void 0; 9 | var styleTag = void 0; 10 | 11 | var lastScrollY = void 0; 12 | var ticking = false; 13 | 14 | /** 15 | * Request animation frame polyfill 16 | * @param {function} callback 17 | */ 18 | var requestAnimFrame = function () { 19 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { 20 | window.setTimeout(callback, 1000 / 60); 21 | }; 22 | }(); 23 | 24 | /** 25 | * If needed, set the new new color as 26 | * html background 27 | * @param {string} color 28 | */ 29 | var setBgColor = function setBgColor(color) { 30 | if (currentBgColor !== color) { 31 | currentBgColor = color; 32 | var css = 'html { background: ' + currentBgColor + '; }'; 33 | 34 | if (!styleTag) { 35 | styleTag = document.createElement('style'); 36 | var head = document.head || document.getElementsByTagName('head')[0]; 37 | head.appendChild(styleTag); 38 | } 39 | 40 | if (styleTag.styleSheet) { 41 | styleTag.styleSheet.cssText = css; 42 | } else { 43 | styleTag.innerHTML = css; 44 | } 45 | } 46 | }; 47 | 48 | /** 49 | * Checks the scroll position and determines 50 | * the overflow color to set between the 51 | * topColor and the bottomColor 52 | */ 53 | var checkScroll = function checkScroll() { 54 | lastScrollY = window.scrollY; 55 | if (!ticking && (topColor || bottomColor)) { 56 | requestAnimFrame(function () { 57 | var scrollHeight = document.body.scrollHeight; 58 | var innerHeight = window.innerHeight; 59 | if (scrollHeight === innerHeight) { 60 | setBgColor(bottomColor); 61 | } else { 62 | setBgColor(innerHeight - scrollHeight + 2 * lastScrollY < 0 ? topColor : bottomColor); 63 | } 64 | ticking = false; 65 | }); 66 | ticking = true; 67 | } 68 | }; 69 | 70 | /** 71 | * Update colors and check scroll 72 | */ 73 | var updateOverflowColor = function updateOverflowColor() { 74 | topColor = null; 75 | bottomColor = null; 76 | 77 | var shortcutAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + ']'); 78 | if (shortcutAttributeEl) { 79 | var split = shortcutAttributeEl.getAttribute(ATTRIBUTE_PREFIX).split(','); 80 | if (split.length > 1) { 81 | topColor = split[0]; 82 | bottomColor = split[1]; 83 | } else if (split.length === 1) { 84 | topColor = bottomColor = split[0]; 85 | } 86 | } else { 87 | var topAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-top]'); 88 | var bottomAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-bottom]'); 89 | if (topAttributeEl) { 90 | topColor = topAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-top'); 91 | } 92 | if (bottomAttributeEl) { 93 | bottomColor = bottomAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-bottom'); 94 | } 95 | } 96 | 97 | if (!topColor && bottomColor) { 98 | topColor = bottomColor; 99 | } else if (topColor && !bottomColor) { 100 | bottomColor = topColor; 101 | } 102 | 103 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 104 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 105 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 106 | bodyComputedBackground = 'white'; 107 | } 108 | document.body.style.background = 'transparent'; 109 | 110 | checkScroll(); 111 | }; 112 | 113 | /** 114 | * Gets the two overflow colors and init the 115 | * window scroll and resize event listeners 116 | */ 117 | var initOverflowColor = function initOverflowColor() { 118 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 119 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 120 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 121 | bodyComputedBackground = 'white'; 122 | } 123 | document.body.style.background = 'transparent'; 124 | 125 | var bodyWrapperEl = document.createElement('div'); 126 | bodyWrapperEl.setAttribute(ATTRIBUTE_PREFIX + '-wrap', ''); 127 | bodyWrapperEl.style.background = bodyComputedBackground; 128 | for (var i = document.body.childNodes.length - 1; i > 0; i--) { 129 | var child = document.body.childNodes[i]; 130 | if (typeof child.getAttribute !== 'function' || child.getAttribute(ATTRIBUTE_PREFIX + '-outside') === null) { 131 | bodyWrapperEl.insertBefore(child, bodyWrapperEl.childNodes[0]); 132 | } 133 | } 134 | if (document.body.childNodes.length) { 135 | document.body.insertBefore(bodyWrapperEl, document.body.childNodes[0]); 136 | } else { 137 | document.body.appendChild(bodyWrapperEl); 138 | } 139 | 140 | updateOverflowColor(); 141 | 142 | if (typeof window.addEventListener !== 'undefined') { 143 | window.addEventListener('scroll', checkScroll, { passive: true }); 144 | window.addEventListener('resize', checkScroll, { passive: true }); 145 | } else { 146 | window.attachEvent('scroll', checkScroll); 147 | window.attachEvent('resize', checkScroll); 148 | } 149 | }; 150 | 151 | if (['interactive', 'complete', 'loaded'].indexOf(document.readyState) !== -1) { 152 | initOverflowColor(); 153 | } else if (typeof document.addEventListener !== 'undefined') { 154 | document.addEventListener('DOMContentLoaded', initOverflowColor, false); 155 | } else { 156 | document.attachEvent('onreadystatechange', initOverflowColor); 157 | } 158 | 159 | window.updateOverflowColor = updateOverflowColor; 160 | 161 | module.exports = updateOverflowColor; 162 | -------------------------------------------------------------------------------- /dist/overflow-color.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global.overflowColor = factory()); 5 | }(this, (function () { 'use strict'; 6 | 7 | var ATTRIBUTE_PREFIX = 'data-oc'; 8 | 9 | var topColor = void 0; 10 | var bottomColor = void 0; 11 | 12 | var currentBgColor = void 0; 13 | var styleTag = void 0; 14 | 15 | var lastScrollY = void 0; 16 | var ticking = false; 17 | 18 | /** 19 | * Request animation frame polyfill 20 | * @param {function} callback 21 | */ 22 | var requestAnimFrame = function () { 23 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { 24 | window.setTimeout(callback, 1000 / 60); 25 | }; 26 | }(); 27 | 28 | /** 29 | * If needed, set the new new color as 30 | * html background 31 | * @param {string} color 32 | */ 33 | var setBgColor = function setBgColor(color) { 34 | if (currentBgColor !== color) { 35 | currentBgColor = color; 36 | var css = 'html { background: ' + currentBgColor + '; }'; 37 | 38 | if (!styleTag) { 39 | styleTag = document.createElement('style'); 40 | var head = document.head || document.getElementsByTagName('head')[0]; 41 | head.appendChild(styleTag); 42 | } 43 | 44 | if (styleTag.styleSheet) { 45 | styleTag.styleSheet.cssText = css; 46 | } else { 47 | styleTag.innerHTML = css; 48 | } 49 | } 50 | }; 51 | 52 | /** 53 | * Checks the scroll position and determines 54 | * the overflow color to set between the 55 | * topColor and the bottomColor 56 | */ 57 | var checkScroll = function checkScroll() { 58 | lastScrollY = window.scrollY; 59 | if (!ticking && (topColor || bottomColor)) { 60 | requestAnimFrame(function () { 61 | var scrollHeight = document.body.scrollHeight; 62 | var innerHeight = window.innerHeight; 63 | if (scrollHeight === innerHeight) { 64 | setBgColor(bottomColor); 65 | } else { 66 | setBgColor(innerHeight - scrollHeight + 2 * lastScrollY < 0 ? topColor : bottomColor); 67 | } 68 | ticking = false; 69 | }); 70 | ticking = true; 71 | } 72 | }; 73 | 74 | /** 75 | * Update colors and check scroll 76 | */ 77 | var updateOverflowColor = function updateOverflowColor() { 78 | topColor = null; 79 | bottomColor = null; 80 | 81 | var shortcutAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + ']'); 82 | if (shortcutAttributeEl) { 83 | var split = shortcutAttributeEl.getAttribute(ATTRIBUTE_PREFIX).split(','); 84 | if (split.length > 1) { 85 | topColor = split[0]; 86 | bottomColor = split[1]; 87 | } else if (split.length === 1) { 88 | topColor = bottomColor = split[0]; 89 | } 90 | } else { 91 | var topAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-top]'); 92 | var bottomAttributeEl = document.querySelector('[' + ATTRIBUTE_PREFIX + '-bottom]'); 93 | if (topAttributeEl) { 94 | topColor = topAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-top'); 95 | } 96 | if (bottomAttributeEl) { 97 | bottomColor = bottomAttributeEl.getAttribute(ATTRIBUTE_PREFIX + '-bottom'); 98 | } 99 | } 100 | 101 | if (!topColor && bottomColor) { 102 | topColor = bottomColor; 103 | } else if (topColor && !bottomColor) { 104 | bottomColor = topColor; 105 | } 106 | 107 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 108 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 109 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 110 | bodyComputedBackground = 'white'; 111 | } 112 | document.body.style.background = 'transparent'; 113 | 114 | checkScroll(); 115 | }; 116 | 117 | /** 118 | * Gets the two overflow colors and init the 119 | * window scroll and resize event listeners 120 | */ 121 | var initOverflowColor = function initOverflowColor() { 122 | var bodyComputedStyle = window.getComputedStyle(document.body, null); 123 | var bodyComputedBackground = bodyComputedStyle.getPropertyValue('background'); 124 | if (bodyComputedBackground === '' || bodyComputedStyle.getPropertyValue('background-color') === 'rgba(0, 0, 0, 0)' && bodyComputedBackground.substring(21, 17) === 'none') { 125 | bodyComputedBackground = 'white'; 126 | } 127 | document.body.style.background = 'transparent'; 128 | 129 | var bodyWrapperEl = document.createElement('div'); 130 | bodyWrapperEl.setAttribute(ATTRIBUTE_PREFIX + '-wrap', ''); 131 | bodyWrapperEl.style.background = bodyComputedBackground; 132 | for (var i = document.body.childNodes.length - 1; i > 0; i--) { 133 | var child = document.body.childNodes[i]; 134 | if (typeof child.getAttribute !== 'function' || child.getAttribute(ATTRIBUTE_PREFIX + '-outside') === null) { 135 | bodyWrapperEl.insertBefore(child, bodyWrapperEl.childNodes[0]); 136 | } 137 | } 138 | if (document.body.childNodes.length) { 139 | document.body.insertBefore(bodyWrapperEl, document.body.childNodes[0]); 140 | } else { 141 | document.body.appendChild(bodyWrapperEl); 142 | } 143 | 144 | updateOverflowColor(); 145 | 146 | if (typeof window.addEventListener !== 'undefined') { 147 | window.addEventListener('scroll', checkScroll, { passive: true }); 148 | window.addEventListener('resize', checkScroll, { passive: true }); 149 | } else { 150 | window.attachEvent('scroll', checkScroll); 151 | window.attachEvent('resize', checkScroll); 152 | } 153 | }; 154 | 155 | if (['interactive', 'complete', 'loaded'].indexOf(document.readyState) !== -1) { 156 | initOverflowColor(); 157 | } else if (typeof document.addEventListener !== 'undefined') { 158 | document.addEventListener('DOMContentLoaded', initOverflowColor, false); 159 | } else { 160 | document.attachEvent('onreadystatechange', initOverflowColor); 161 | } 162 | 163 | window.updateOverflowColor = updateOverflowColor; 164 | 165 | return updateOverflowColor; 166 | 167 | }))); 168 | --------------------------------------------------------------------------------