delayed-scroll-restoration-polyfill
Advanced tools
Comparing version 0.1.0 to 0.1.1
## master (unreleased) | ||
- Initial release | ||
## 0.1.1 | ||
- Update packages to latest versions. | ||
- Take width of scrollbar into account (#1) | ||
## 0.1.0 | ||
- Initial release. |
@@ -30,2 +30,3 @@ if (window.history.pushState) { | ||
let timeoutHandle = null; | ||
let scrollBarWidth = null; | ||
@@ -41,2 +42,5 @@ // Try to scroll to the scrollTarget, but only if we can actually scroll | ||
const html = document.documentElement; | ||
if (!scrollBarWidth) { | ||
scrollBarWidth = getScrollbarWidth(); | ||
} | ||
@@ -49,4 +53,4 @@ // From http://stackoverflow.com/a/1147768 | ||
if (documentWidth - window.innerWidth >= scrollTarget.x && | ||
documentHeight - window.innerHeight >= scrollTarget.y || | ||
if (documentWidth + scrollBarWidth - window.innerWidth >= scrollTarget.x && | ||
documentHeight + scrollBarWidth - window.innerHeight >= scrollTarget.y || | ||
Date.now() > scrollTarget.latestTimeToTry) { | ||
@@ -75,3 +79,29 @@ window.scrollTo(scrollTarget.x, scrollTarget.y); | ||
// Calculating width of browser's scrollbar | ||
function getScrollbarWidth() { | ||
let outer = document.createElement("div"); | ||
outer.style.visibility = "hidden"; | ||
outer.style.width = "100px"; | ||
outer.style.msOverflowStyle = "scrollbar"; | ||
document.body.appendChild(outer); | ||
let widthNoScroll = outer.offsetWidth; | ||
// force scrollbars | ||
outer.style.overflow = "scroll"; | ||
// add innerdiv | ||
let inner = document.createElement("div"); | ||
inner.style.width = "100%"; | ||
outer.appendChild(inner); | ||
let widthWithScroll = inner.offsetWidth; | ||
// remove divs | ||
outer.parentNode.removeChild(outer); | ||
return widthNoScroll - widthWithScroll; | ||
} | ||
window.addEventListener('popstate', onPopState, true); | ||
} |
149
index.js
@@ -1,78 +0,107 @@ | ||
'use strict'; | ||
"use strict"; | ||
if (window.history.pushState) { | ||
(function () { | ||
var SCROLL_RESTORATION_TIMEOUT_MS = 3000; | ||
var TRY_TO_SCROLL_INTERVAL_MS = 50; | ||
var originalPushState = window.history.pushState; | ||
var originalReplaceState = window.history.replaceState; | ||
// Calculating width of browser's scrollbar | ||
var getScrollbarWidth = function getScrollbarWidth() { | ||
var outer = document.createElement("div"); | ||
outer.style.visibility = "hidden"; | ||
outer.style.width = "100px"; | ||
outer.style.msOverflowStyle = "scrollbar"; | ||
// Store current scroll position in current state when navigating away. | ||
window.history.pushState = function () { | ||
var newStateOfCurrentPage = Object.assign({}, window.history.state, { | ||
__scrollX: window.scrollX, | ||
__scrollY: window.scrollY | ||
}); | ||
originalReplaceState.call(window.history, newStateOfCurrentPage, ''); | ||
document.body.appendChild(outer); | ||
originalPushState.apply(window.history, arguments); | ||
}; | ||
var widthNoScroll = outer.offsetWidth; | ||
// force scrollbars | ||
outer.style.overflow = "scroll"; | ||
// Make sure we don't throw away scroll position when calling "replaceState". | ||
window.history.replaceState = function (state) { | ||
var newState = Object.assign({}, { | ||
__scrollX: window.history.state && window.history.state.__scrollX, | ||
__scrollY: window.history.state && window.history.state.__scrollY | ||
}, state); | ||
// add innerdiv | ||
var inner = document.createElement("div"); | ||
inner.style.width = "100%"; | ||
outer.appendChild(inner); | ||
for (var _len = arguments.length, otherArgs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
otherArgs[_key - 1] = arguments[_key]; | ||
} | ||
var widthWithScroll = inner.offsetWidth; | ||
originalReplaceState.apply(window.history, [newState].concat(otherArgs)); | ||
}; | ||
// remove divs | ||
outer.parentNode.removeChild(outer); | ||
var timeoutHandle = null; | ||
return widthNoScroll - widthWithScroll; | ||
}; | ||
// Try to scroll to the scrollTarget, but only if we can actually scroll | ||
// there. Otherwise keep trying until we time out, then scroll as far as | ||
// we can. | ||
var tryToScrollTo = function tryToScrollTo(scrollTarget) { | ||
// Stop any previous calls to "tryToScrollTo". | ||
clearTimeout(timeoutHandle); | ||
var SCROLL_RESTORATION_TIMEOUT_MS = 3000; | ||
var TRY_TO_SCROLL_INTERVAL_MS = 50; | ||
var body = document.body; | ||
var html = document.documentElement; | ||
var originalPushState = window.history.pushState; | ||
var originalReplaceState = window.history.replaceState; | ||
// From http://stackoverflow.com/a/1147768 | ||
var documentWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); | ||
var documentHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); | ||
// Store current scroll position in current state when navigating away. | ||
window.history.pushState = function () { | ||
var newStateOfCurrentPage = Object.assign({}, window.history.state, { | ||
__scrollX: window.scrollX, | ||
__scrollY: window.scrollY | ||
}); | ||
originalReplaceState.call(window.history, newStateOfCurrentPage, ''); | ||
if (documentWidth - window.innerWidth >= scrollTarget.x && documentHeight - window.innerHeight >= scrollTarget.y || Date.now() > scrollTarget.latestTimeToTry) { | ||
window.scrollTo(scrollTarget.x, scrollTarget.y); | ||
} else { | ||
timeoutHandle = setTimeout(function () { | ||
return tryToScrollTo(scrollTarget); | ||
}, TRY_TO_SCROLL_INTERVAL_MS); | ||
} | ||
}; | ||
originalPushState.apply(window.history, arguments); | ||
}; | ||
// Try scrolling to the previous scroll position on popstate | ||
var onPopState = function onPopState() { | ||
var state = window.history.state; | ||
// Make sure we don't throw away scroll position when calling "replaceState". | ||
window.history.replaceState = function (state) { | ||
var newState = Object.assign({}, { | ||
__scrollX: window.history.state && window.history.state.__scrollX, | ||
__scrollY: window.history.state && window.history.state.__scrollY | ||
}, state); | ||
if (state && Number.isFinite(state.__scrollX) && Number.isFinite(state.__scrollY)) { | ||
setTimeout(function () { | ||
return tryToScrollTo({ | ||
x: state.__scrollX, | ||
y: state.__scrollY, | ||
latestTimeToTry: Date.now() + SCROLL_RESTORATION_TIMEOUT_MS | ||
}); | ||
for (var _len = arguments.length, otherArgs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
otherArgs[_key - 1] = arguments[_key]; | ||
} | ||
originalReplaceState.apply(window.history, [newState].concat(otherArgs)); | ||
}; | ||
var timeoutHandle = null; | ||
var scrollBarWidth = null; | ||
// Try to scroll to the scrollTarget, but only if we can actually scroll | ||
// there. Otherwise keep trying until we time out, then scroll as far as | ||
// we can. | ||
var tryToScrollTo = function tryToScrollTo(scrollTarget) { | ||
// Stop any previous calls to "tryToScrollTo". | ||
clearTimeout(timeoutHandle); | ||
var body = document.body; | ||
var html = document.documentElement; | ||
if (!scrollBarWidth) { | ||
scrollBarWidth = getScrollbarWidth(); | ||
} | ||
// From http://stackoverflow.com/a/1147768 | ||
var documentWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); | ||
var documentHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight); | ||
if (documentWidth + scrollBarWidth - window.innerWidth >= scrollTarget.x && documentHeight + scrollBarWidth - window.innerHeight >= scrollTarget.y || Date.now() > scrollTarget.latestTimeToTry) { | ||
window.scrollTo(scrollTarget.x, scrollTarget.y); | ||
} else { | ||
timeoutHandle = setTimeout(function () { | ||
return tryToScrollTo(scrollTarget); | ||
}, TRY_TO_SCROLL_INTERVAL_MS); | ||
} | ||
}; | ||
// Try scrolling to the previous scroll position on popstate | ||
var onPopState = function onPopState() { | ||
var state = window.history.state; | ||
if (state && Number.isFinite(state.__scrollX) && Number.isFinite(state.__scrollY)) { | ||
setTimeout(function () { | ||
return tryToScrollTo({ | ||
x: state.__scrollX, | ||
y: state.__scrollY, | ||
latestTimeToTry: Date.now() + SCROLL_RESTORATION_TIMEOUT_MS | ||
}); | ||
} | ||
}; | ||
}); | ||
} | ||
}; | ||
window.addEventListener('popstate', onPopState, true); | ||
})(); | ||
window.addEventListener('popstate', onPopState, true); | ||
} |
{ | ||
"name": "delayed-scroll-restoration-polyfill", | ||
"version": "0.1.0", | ||
"description": "Limited polyfill for Chrome's delayed scroll restoration behavior.", | ||
"version": "0.1.1", | ||
"description": "Polyfill that mimics Chrome's scroll restoration behavior.", | ||
"main": "index.js", | ||
@@ -18,10 +18,13 @@ "repository": { | ||
"contributors": [ | ||
{ "name": "Jan Paul Posma", "email": "jp.posma@brigade.com" } | ||
{ | ||
"name": "Jan Paul Posma", | ||
"email": "jp.posma@brigade.com" | ||
} | ||
], | ||
"license": "MIT", | ||
"devDependencies": { | ||
"babel": "^5.1.9", | ||
"babel-core": "^5.1.9", | ||
"eslint": "^1.3.1", | ||
"eslint-config-brigade": "^1.6.0" | ||
"babel-cli": "^6.2.0", | ||
"babel-preset-es2015": "^6.1.18", | ||
"eslint": "^1.10.1", | ||
"eslint-config-brigade": "^1.9.0" | ||
}, | ||
@@ -28,0 +31,0 @@ "keywords": [ |
@@ -1,2 +0,2 @@ | ||
# Limited polyfill for Chrome's delayed scroll restoration behavior | ||
# Polyfill that mimics Chrome's scroll restoration behavior | ||
@@ -37,3 +37,3 @@ In single page apps that use the history API for navigation, the DOM is | ||
### In your HTML | ||
``` | ||
```html | ||
<script src='node_modules/delayed-scroll-restoration-polyfill/index.js'></script> | ||
@@ -53,2 +53,14 @@ ``` | ||
## Disabling native implementations | ||
While this polyfill is designed to work alongside native implementations, if you | ||
nevertheless experience compatibility problems, we suggest you disable native | ||
scroll restoration in browsers that support it, like this: | ||
```js | ||
if ('scrollRestoration' in window.history) { | ||
window.history.scrollRestoration = 'manual'; | ||
} | ||
``` | ||
## Differences from Chrome's native implementation | ||
@@ -55,0 +67,0 @@ |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
14476
8
167
123
1