Skip to content

feat: add the ability to specify a scrollable target element for tracking scroll direction #311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import useDetectScroll, {

The `useDetectScroll` hook takes an options object with the following properties:

- `target`: The target scrollable element from which to detect scroll direction and position (default: `window`, must be an `HTMLDivElement`).
- `thr`: Threshold for scroll direction change detection (default: `0`, accepts only positive values).
- `axis`: Defines the scroll axis (`"y"` or `"x"`, default: `"y"`).
- `scrollUp`: Value returned when scrolling up (y-axis) or left (x-axis) (default: `"up"` for y-axis, `"left"` for x-axis).
Expand Down Expand Up @@ -74,6 +75,20 @@ const { scrollDir, scrollPosition } = useDetectScroll({ axis: Axis.X });
// scrollPosition: { top, bottom, left, right }
```

To use a custom scrollable element as a target rather than the default window:

```js
const customElementRef = useRef<HTMLDivElement>(null);
const [customElement, setCustomElement] = useState<HTMLDivElement>();
useEffect(() => {
if(customElementRef.current) {
setHomepageElement(customElementRef.current);
}
}, [customElementRef])
const scrollDir = useDetectScroll({target: customElement});
console.log(scrollDir);
```

## Contributing

Interested in making contributions to this project? Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines and details.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@
},
"type": "module",
"types": "./dist/index.d.ts",
"version": "4.1.0"
"version": "4.2.0"
}
62 changes: 49 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ type ScrollInfo = {

/** Type declaration for scroll properties */
type ScrollProps = {
/**
* The target represents the scrollable element to check for scroll detection.
*/
target?: HTMLDivElement | Window;
/**
* The thr represents the threshold value for scroll detection.
*/
Expand Down Expand Up @@ -125,6 +129,7 @@ type ScrollProps = {
*/
function useDetectScroll(props: ScrollProps = {}): ScrollInfo {
const {
target = window,
thr = 0,
axis = Axis.Y,
scrollUp = axis === Axis.Y ? Direction.Up : Direction.Left,
Expand All @@ -146,40 +151,70 @@ function useDetectScroll(props: ScrollProps = {}): ScrollInfo {

/** Function to update scroll direction */
const updateScrollDir = useCallback(() => {
const scroll = axis === Axis.Y ? window.scrollY : window.scrollX;
let scroll: number;
if (target instanceof Window) {
scroll = axis === Axis.Y ? target.scrollY : target.scrollX;
} else {
scroll =
axis === Axis.Y
? (target as HTMLDivElement).scrollTop
: (target as HTMLDivElement).scrollLeft;
}

if (Math.abs(scroll - lastScroll.current) >= threshold) {
setScrollDir(scroll > lastScroll.current ? scrollDown : scrollUp);
lastScroll.current = Math.max(0, scroll);
}
ticking.current = false;
}, [axis, threshold, scrollDown, scrollUp]);
}, [target, axis, threshold, scrollDown, scrollUp]);

useEffect(() => {
/** Function to update scroll position */
const updateScrollPosition = () => {
const top = window.scrollY;
const left = window.scrollX;
const top =
target instanceof Window
? target.scrollY
: (target as HTMLDivElement).scrollTop;
const left =
target instanceof Window
? target.scrollX
: (target as HTMLDivElement).scrollLeft;
const bottom =
document.documentElement.scrollHeight - window.innerHeight - top;
document.documentElement.scrollHeight -
(target instanceof Window
? target.innerHeight
: (target as HTMLDivElement).scrollHeight) -
top;
const right =
document.documentElement.scrollWidth - window.innerWidth - left;
document.documentElement.scrollWidth -
(target instanceof Window
? target.innerWidth
: (target as HTMLDivElement).scrollWidth) -
left;

setScrollPosition({ top, bottom, left, right });
};

/** Call the update function when the component mounts */
updateScrollPosition();

window.addEventListener('scroll', updateScrollPosition);
const targetElement = target as EventTarget;
targetElement.addEventListener('scroll', updateScrollPosition);

return () => {
window.removeEventListener('scroll', updateScrollPosition);
targetElement.removeEventListener('scroll', updateScrollPosition);
};
}, []);
}, [target]);

useEffect(() => {
lastScroll.current = axis === Axis.Y ? window.scrollY : window.scrollX;
if (target instanceof Window) {
lastScroll.current = axis === Axis.Y ? target.scrollY : target.scrollX;
} else {
lastScroll.current =
axis === Axis.Y
? (target as HTMLDivElement).scrollTop
: (target as HTMLDivElement).scrollLeft;
}

/** Function to handle onScroll event */
const onScroll = () => {
Expand All @@ -189,10 +224,11 @@ function useDetectScroll(props: ScrollProps = {}): ScrollInfo {
}
};

window.addEventListener('scroll', onScroll);
const targetElement = target as EventTarget;
targetElement.addEventListener('scroll', onScroll);

return () => window.removeEventListener('scroll', onScroll);
}, [axis, updateScrollDir]);
return () => targetElement.removeEventListener('scroll', onScroll);
}, [target, axis, updateScrollDir]);

return { scrollDir, scrollPosition };
}
Expand Down
Loading
Loading