diff --git a/src/helpers.js b/src/helpers.js index 48b69ff..7308c70 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -4,7 +4,23 @@ export const animateScroll = (function () { let timeoutId; let resolvePrevious; - return function animateScroll (id, animate) { + return function animateScroll (id, targetId, animate) { + let targetElement = document.getElementById(targetId); + if (!targetElement) { + targetElement = 'scrollTop' in document.documentElement + ? document.documentElement + : document.body; + } + + function getScrollTop () { + // like jQuery -> $('html, body').scrollTop + return targetElement.scrollTop; + } + + function setScrollTop (position) { + targetElement.scrollTop = position; + } + return new Promise((resolve, reject) => { const element = id ? document.getElementById(id) : document.body; @@ -12,6 +28,10 @@ export const animateScroll = (function () { return reject(`Cannot find element: #${id}`); } + function getOffsetTop () { + return element.getBoundingClientRect().top - targetElement.getBoundingClientRect().top + getScrollTop(); + } + const { offset, duration, easing } = animate; const start = getScrollTop(); const to = getOffsetTop(element) + offset; @@ -39,29 +59,14 @@ export const animateScroll = (function () { resolvePrevious = resolve; animateFn(); }); - } + }; })(); export function updateHistory (id) { id = '#' + id; if (history.pushState) { history.pushState(null, null, id); - } - else { + } else { location.hash = id; } } - -function getScrollTop () { - // like jQuery -> $('html, body').scrollTop - return document.documentElement.scrollTop || document.body.scrollTop; -} - -function setScrollTop (position) { - document.documentElement.scrollTop = document.body.scrollTop = position; -} - -function getOffsetTop (element) { - const { top } = element.getBoundingClientRect(); - return top + getScrollTop(); -} diff --git a/src/scrollchor.jsx b/src/scrollchor.jsx index c20c308..8f6a7c9 100644 --- a/src/scrollchor.jsx +++ b/src/scrollchor.jsx @@ -22,6 +22,10 @@ export default class Scrollchor extends React.Component { children: PropTypes.node } + static _normalizeId (id) { + return (id && id.replace(/^#/, '')) || '' + } + static _stateHelper (props) { const { // default animate object @@ -30,7 +34,8 @@ export default class Scrollchor extends React.Component { easing = easeOutQuad } = props.animate || {}; return { - to: (props.to && props.to.replace(/^#/, '')) || '', + to: Scrollchor._normalizeId(props.to), + target: Scrollchor._normalizeId(props.target), animate: { offset, duration, easing }, beforeAnimate: props.beforeAnimate || function () {}, afterAnimate: props.afterAnimate || function () {}, @@ -41,7 +46,7 @@ export default class Scrollchor extends React.Component { _handleClick = (event) => { this.state.beforeAnimate(event); event && event.preventDefault(); - animateScroll(this.state.to, this.state.animate) + animateScroll(this.state.to, this.state.target, this.state.animate) .then((id) => { if (id) { this.state.disableHistory || updateHistory(id); @@ -59,7 +64,7 @@ export default class Scrollchor extends React.Component { } render () { - const { to, animate, beforeAnimate, afterAnimate, disableHistory, ...props } = this.props; // eslint-disable-line no-unused-vars + const { to, target, animate, beforeAnimate, afterAnimate, disableHistory, ...props } = this.props; // eslint-disable-line no-unused-vars return !this.props.children ? null