-
-
Notifications
You must be signed in to change notification settings - Fork 8
Closed
Labels
Description
Describe the problem 🧐
- Currently, scroll plugin only supports scrolling the
Window - The underlying library gmrchk/scrl has that limitation as well
- More complex scenarios like overlays or other overflowing divs are currently not supported
Describe the propsed solution 😎
- Fork gmrchk/scrl to
swup/scrlOR implement the logic directly in ScrollPlugin - Implement support for scrolling to anchor elements inside overflowing divs
- Pass the scroll root to
swup.scrollTo()
Example implementation
/**
* Find the closest overflowing parent
*/
export function getScrollableParent(
element: Element | null | undefined,
): HTMLElement | null {
if (!element) return null;
let parent: HTMLElement | null = element.parentElement;
while (parent) {
const { overflowY } = getComputedStyle(parent);
const isScrollable =
["auto", "scroll"].includes(overflowY) &&
parent.scrollHeight > parent.clientHeight;
if (isScrollable) {
return parent;
}
parent = parent.parentElement;
}
return document.documentElement;
}
/**
* Attempt to scroll to an anchor, inside an overflowing element or the Window
*/
export function maybeScrollToAnchor(
swup: Swup,
hash?: string,
animate: boolean = false,
): boolean {
if (!hash) {
return false;
}
const scrollPlugin = swup.findPlugin("SwupScrollPlugin") as SwupScrollPlugin;
const element = scrollPlugin.getAnchorElement(hash);
if (!element) {
console.warn(`Anchor target ${hash} not found`);
return false;
}
if (!(element instanceof Element)) {
console.warn(`Anchor target ${hash} is not a DOM node`);
return false;
}
const { top: elementTop } = element.getBoundingClientRect();
const scrollRoot = getScrollableParent(element)!;
const scrollTop = scrollRoot.scrollTop;
const y = elementTop + scrollTop - scrollPlugin.getOffset(element);
// swup.scrollTo(scrollRoot, y, animate);
scrollToAnchor(swup, scrollRoot, y, animate);
return true;
}
/**
* Scroll to an anchor inside an element
*/
export function scrollToAnchor(
swup: Swup,
target: HTMLElement,
y: number,
animate: boolean,
): void {
if (!animate) {
swup.hooks.callSync("scroll:start", undefined);
target.scrollTo(0, y);
swup.hooks.callSync("scroll:end", undefined);
return;
}
/**
* Use GSAP ScrollToPlugin for animated scrolling
* @see https://greensock.com/docs/v3/Plugins/ScrollToPlugin
*/
gsap.to(target, {
duration: 0.6,
scrollTo: {
y,
autoKill: !isTouch(),
},
ease: "scrl1",
onStart: () => {
swup.hooks.callSync("scroll:start", undefined);
},
onComplete: () => {
swup.hooks.callSync("scroll:end", undefined);
},
onAutoKill: () => {
swup.hooks.callSync("scroll:end", undefined);
},
});
}Alternatives considered 🤔
Keep hacking this in every new project.
Considerations
This would be a breaking change since swup.scrollTo() would now need to know what to scroll (The Window or an element).
How important is this feature to you? 🧭
Would make my life a lot easier
Checked all these? 📚
- I have read and searched the official docs
- I have checked discussions and existing issues for related problems
- I agree to follow this project's Code of Conduct
- I have provided all necessary information to the best of my knowledge