1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
// TODO(crbug.com/888443): It would be better to listen to the scrollend event
// instead of polling the scroll position.
function observeScrolling(elements, callback) {
if (!Array.isArray(elements))
elements = [elements];
var lastChangedFrame = 0;
var lastLeft = new Map();
var lastTop = new Map();
elements.forEach((element) => {
lastLeft.set(element, element.scrollLeft);
lastTop.set(element, element.scrollTop);
});
function tick(frames) {
// We requestAnimationFrame either for 5000 frames or until 20 frames with
// no change have been observed. (In Chromium, frames may run as frequently
// as once per millisecond when threaded compositing is disabled. The limit
// of 5000 frames is chosen to be high enough to reasonably ensure any
// scroll animation will run to completion.)
if (frames >= 5000 || frames - lastChangedFrame > 20) {
callback(true);
} else {
var scrollHappened = elements.some((element) => {
return element.scrollLeft != lastLeft.get(element) || element.scrollTop != lastTop.get(element);
});
if (scrollHappened) {
lastChangedFrame = frames;
elements.forEach((element) => {
lastLeft.set(element, element.scrollLeft);
lastTop.set(element, element.scrollTop);
});
callback(false);
}
requestAnimationFrame(tick.bind(null, frames + 1));
}
}
tick(0);
}
function waitForScrollEnd(elements) {
return new Promise((resolve) => {
observeScrolling(elements, (done) => {
if (done)
resolve();
});
});
}
function resetScroll(scrollingElement) {
// Try various methods to ensure the element position is reset immediately.
scrollingElement.scrollLeft = 0;
scrollingElement.scrollTop = 0;
scrollingElement.scroll({left: 0, top: 0, behavior: "instant"});
}
function resetScrollForWindow(scrollingWindow) {
// Try various methods to ensure the element position is reset immediately.
scrollingWindow.document.scrollingElement.scrollLeft = 0;
scrollingWindow.document.scrollingElement.scrollTop = 0;
scrollingWindow.scroll({left: 0, top: 0, behavior: "instant"});
}
function setScrollBehavior(styledElement, className) {
styledElement.classList.remove("autoBehavior", "smoothBehavior");
styledElement.classList.add(className);
}
function scrollNode(scrollingElement, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
var args = {};
if (behavior)
args.behavior = behavior;
switch (scrollFunction) {
case "scrollIntoView":
args.inline = "start";
args.block = "start";
elementToReveal.scrollIntoView(args);
break;
default:
args.left = elementToRevealLeft;
args.top = elementToRevealTop;
scrollingElement[scrollFunction](args);
break;
}
}
function scrollWindow(scrollingWindow, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
var args = {};
if (behavior)
args.behavior = behavior;
args.left = elementToRevealLeft;
args.top = elementToRevealTop;
scrollingWindow[scrollFunction](args);
}
|