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 | `.
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 |
--------------------------------------------------------------------------------