scroll-into-view-if-needed
Version:
Element.scrollIntoViewIfNeeded ponyfill that can animate the scrolling
71 lines (70 loc) • 3.07 kB
JavaScript
import animate from 'amator';
export default function scrollIntoViewIfNeeded(elem, centerIfNeeded, options, finalElement, config) {
if (config === void 0) { config = {}; }
if (!elem || !(elem instanceof HTMLElement))
throw new Error('Element is required in scrollIntoViewIfNeeded');
function withinBounds(value, min, max, extent) {
if (false === centerIfNeeded ||
(max <= value + extent && value <= min + extent)) {
return Math.min(max, Math.max(min, value));
}
else {
return (min + max) / 2;
}
}
var offsetTop = config.offsetTop || 0;
var offsetLeft = config.offsetLeft || 0;
var offsetBottom = config.offsetBottom || 0;
var offsetRight = config.offsetRight || 0;
function makeArea(left, top, width, height) {
return {
left: left + offsetLeft,
top: top + offsetTop,
width: width,
height: height,
right: left + offsetLeft + width + offsetRight,
bottom: top + offsetTop + height + offsetBottom,
translate: function (x, y) {
return makeArea(x + left + offsetLeft, y + top + offsetTop, width, height);
},
relativeFromTo: function (lhs, rhs) {
var newLeft = left + offsetLeft, newTop = top + offsetTop;
lhs = lhs.offsetParent;
rhs = rhs.offsetParent;
if (lhs === rhs) {
return area;
}
for (; lhs; lhs = lhs.offsetParent) {
newLeft += lhs.offsetLeft + lhs.clientLeft;
newTop += lhs.offsetTop + lhs.clientTop;
}
for (; rhs; rhs = rhs.offsetParent) {
newLeft -= rhs.offsetLeft + rhs.clientLeft;
newTop -= rhs.offsetTop + rhs.clientTop;
}
return makeArea(newLeft, newTop, width, height);
},
};
}
var parent, area = makeArea(elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight);
while ((parent = elem.parentNode) instanceof HTMLElement &&
elem !== finalElement) {
var clientLeft = parent.offsetLeft + parent.clientLeft;
var clientTop = parent.offsetTop + parent.clientTop;
area = area.relativeFromTo(elem, parent).translate(-clientLeft, -clientTop);
var scrollLeft = withinBounds(parent.scrollLeft, area.right - parent.clientWidth, area.left, parent.clientWidth);
var scrollTop = withinBounds(parent.scrollTop, area.bottom - parent.clientHeight, area.top, parent.clientHeight);
if (options) {
animate(parent, {
scrollLeft: scrollLeft,
scrollTop: scrollTop,
}, options);
}
else {
parent.scrollLeft = scrollLeft;
parent.scrollTop = scrollTop;
}
area = area.translate(clientLeft - parent.scrollLeft, clientTop - parent.scrollTop);
elem = parent;
}
}