Comments (16)
am i missing something? i can't find any working example for how to use this with an element ref.
from use-scroll-position.
I am in the same boat...can't seem to get anything to work when the scrolling element is anything other than the window.
from use-scroll-position.
Is it me, or does this hook not work at all for target elements that aren't the window?
It doesn't fire at all unless the main document element is scrolling, regardless of the ref passed in.
from use-scroll-position.
Is it me, or does this hook not work at all for target elements that aren't the window?
It doesn't fire at all unless the main document element is scrolling, regardless of the ref passed in.
Yep, this is due to getBoundingClientRect()
. I think scrollTop
should be used instead.
from use-scroll-position.
Did anyone get this to work? I'm passing ref from a parent component and wont to detect scroll. But I get nothing when I do this:
useScrollPosition(({ prevPos, currPos }) => {
console.log("scrolling");
}, [visibleFilterResultButton], props.forwardedRef);
from use-scroll-position.
I have the same problem.
After some testing I have found out that you cannot use fixed heights. Neither on the scrollable element nor on parent elements. In your codesandbox change line 28 to height: "100%"
and it will work.
Of course, this is not a sufficient solution but at least one step towards a fix.
from use-scroll-position.
@pawelsas Thanks for the help! My actual use case doesn't work with heigh: '100%'
, because the element is more nested.
I now used something like this:
const mainRef = useRef();
const [shouldShowHeader, setShouldShowHeader] = useState(false);
function handleScroll() {
if (typeof window !== 'undefined' && mainRef.current.scrollTop) {
const showHeader = mainRef.current.scrollTop > OFFSET;
if (showHeader !== shouldShowHeader) {
setShouldShowHeader(showHeader);
}
}
}
Would still love to get this hook working!
from use-scroll-position.
Is it me, or does this hook not work at all for target elements that aren't the window?
It doesn't fire at all unless the main document element is scrolling, regardless of the ref passed in.Yep, this is due to
getBoundingClientRect()
. I thinkscrollTop
should be used instead.
I've run into the same issue. Looks like scrollTop
needs to be used, as well as addEventListener
for "scroll"
needs to be called on the element instead of window
from use-scroll-position.
Still no luck. I had this terms n conditions page which is set under overflow: auto
The purpose is to detect scroll till end of that component and enable accept button.
Since this is a normal use-case, @n8tb1t Any take on this?
from use-scroll-position.
@joebochill here's my modified version that works for containers. usage example at the bottom.
/**
* useScrollPosition.js
*/
import React from 'react';
const isBrowser = typeof window !== `undefined`;
const zeroPosition = { x: 0, y: 0 };
const useIsomorphicLayoutEffect = typeof window !== 'undefined'
? React.useLayoutEffect
: React.useEffect;
const getScrollPosition = (element) => {
if (!isBrowser || element === null || element === void 0) {
return zeroPosition;
}
return {
x: element.current.scrollLeft,
y: element.current.scrollTop
};
};
const getScrollProps = (element) => {
if (!isBrowser || element === null || element === void 0) {
return {
top: false,
bottom: false,
scrollable: false
};
}
return {
top: element.current.scrollTop === 0,
bottom: (element.current.clientHeight + element.current.scrollTop) === element.current.scrollHeight,
scrollable: element.current.scrollHeight > element.current.clientHeight
};
};
const resizeSensor = (element, callback) =>
{
let zIndex = parseInt(getComputedStyle(element));
if(isNaN(zIndex)) { zIndex = 0; };
zIndex--;
let expand = document.createElement('div');
expand.style.position = "absolute";
expand.style.left = "0px";
expand.style.top = "0px";
expand.style.right = "0px";
expand.style.bottom = "0px";
expand.style.overflow = "hidden";
expand.style.zIndex = zIndex;
expand.style.visibility = "hidden";
let expandChild = document.createElement('div');
expandChild.style.position = "absolute";
expandChild.style.left = "0px";
expandChild.style.top = "0px";
expandChild.style.width = "10000000px";
expandChild.style.height = "10000000px";
expand.appendChild(expandChild);
let shrink = document.createElement('div');
shrink.style.position = "absolute";
shrink.style.left = "0px";
shrink.style.top = "0px";
shrink.style.right = "0px";
shrink.style.bottom = "0px";
shrink.style.overflow = "hidden";
shrink.style.zIndex = zIndex;
shrink.style.visibility = "hidden";
let shrinkChild = document.createElement('div');
shrinkChild.style.position = "absolute";
shrinkChild.style.left = "0px";
shrinkChild.style.top = "0px";
shrinkChild.style.width = "200%";
shrinkChild.style.height = "200%";
shrink.appendChild(shrinkChild);
element.appendChild(expand);
element.appendChild(shrink);
const setScroll = () =>
{
expand.scrollLeft = 10000000;
expand.scrollTop = 10000000;
shrink.scrollLeft = 10000000;
shrink.scrollTop = 10000000;
};
setScroll();
let size = element.getBoundingClientRect();
let currentWidth = size.width;
let currentHeight = size.height;
const onScroll = () =>
{
let size = element.getBoundingClientRect();
let newWidth = size.width;
let newHeight = size.height;
if(newWidth !== currentWidth || newHeight !== currentHeight)
{
currentWidth = newWidth;
currentHeight = newHeight;
callback();
}
setScroll();
};
const cleanup = () => {
expand.removeEventListener('scroll', onScroll);
shrink.removeEventListener('scroll', onScroll);
}
expand.addEventListener('scroll', onScroll);
shrink.addEventListener('scroll', onScroll);
return cleanup;
};
export default function useScrollPosition(callback, deps, element, wait) {
const resizeCleanup = React.useRef();
const position = React.useRef(zeroPosition);
const throttleTimeout = React.useRef(null);
const [callbackState] = React.useState(() => callback);
const localCallback = React.useCallback(() => {
const currPos = getScrollPosition(element);
const scrollProps = getScrollProps(element);
callbackState({
prevPos: position.current,
currPos,
isTop: scrollProps.top,
isBottom: scrollProps.bottom,
isScrollable: scrollProps.scrollable
});
position.current = currPos;
throttleTimeout.current = null;
}, [element, callbackState]);
React.useLayoutEffect(() => {
localCallback();
}, [localCallback]);
useIsomorphicLayoutEffect(() => {
if (!isBrowser) {
return undefined;
}
const handleScroll = () => {
if (wait) {
if (throttleTimeout.current === null) {
throttleTimeout.current = setTimeout(localCallback, wait);
}
}
else {
localCallback();
}
};
if (element && element.current !== null && element.current !== void 0) {
element.current.addEventListener('scroll', handleScroll, { passive: true });
resizeCleanup.current = new resizeSensor(element.current, handleScroll);
}
return () => {
if (element && element.current !== null && element.current !== void 0) {
element.current.removeEventListener('scroll', handleScroll, { passive: true });
}
if (resizeCleanup.current) {
resizeCleanup.current();
}
if (throttleTimeout.current) {
clearTimeout(throttleTimeout.current);
}
};
}, deps);
};
usage - you need a ref to the container:
useScrollPosition(
({ currPos, prevPos, isTop, isBottom, isScrollable }) => {
console.log('scroll info', currPos, prevPos, isTop, isBottom, isScrollable);
},
[],
scrollRef,
);
from use-scroll-position.
@jpodpro thanks for the reply. Your version works great in a minimum reproduction (https://codesandbox.io/s/little-snow-0kush), but it's not working in my application...which makes me think I'm probably doing something else wrong unrelated
from use-scroll-position.
@joebochill interesting. i currently use this in production with no problems and i didn't think there was much involved in making it work. perhaps look into the dependencies array if there's something that is being updated in order to fully layout your container.
from use-scroll-position.
In my case, I'm not using it within the same component where the ref is defined (I have another component 'B' that responds to the scroll position of component 'A'): https://codesandbox.io/s/sharp-heyrovsky-iycwi?file=/src/ComponentB.js
I'm running into the issue where the ref is null on the first render and when ref.current gets updated, it doesn't trigger a redraw on component B, so it stays null forever or until something else triggers a re-render.
from use-scroll-position.
any updates guys? I'm stuck :(
from use-scroll-position.
I am stuck on this issue too. Can't get it work with a custom auto-scrolling element
Tried with 1.0.43
and with the latest 2.0.3
from use-scroll-position.
Sorry I can't be much help. We ended up going in a different direction and writing our own scroll listeners based on the guide here https://www.benmarshall.me/attaching-javascript-handlers-to-scroll-events/.
Here's an excerpt of what we did in our component 'B' (the one responding to the scroll events):
const scrollElement = props.scrollContainerId ? document.getElementById(props.scrollContainerId) : null;
const scrollTop = scrollElement ? scrollElement.scrollTop : window.scrollY;
// offset is where we store the current scroll position of the scrollable component
const [offset, setOffset] = useState(0);
const [scrolling, setScrolling] = useState(false);
const [animating, setAnimating] = useState(false);
const [endScrollHandled, setEndScrollHandled] = useState(false);
// This handler checks if the scrolling variable is true and adjusts the offset accordingly
// We do not do this directly in the scroll event callback for performance reasons
const handleScroll = useCallback(() => {
if (scrolling && !animating) {
setScrolling(false);
setEndScrollHandled(false);
if (scrollTop !== offset) {
setOffset(scrollTop);
}
}
// once the scroll ends, do one more update so we don't miss the latest value due to the debounce
else if (!endScrollHandled) {
setOffset(scrollTop);
setEndScrollHandled(true);
}
}, [scrolling, animating, offset, endScrollHandled]);
// This function listens for scroll events on the scrollable element and sets the scrolling variable to true
useEffect(() => {
(scrollElement || window).addEventListener('scroll', () => {
setScrolling(true);
});
const scrollCheck = setInterval(handleScroll, 50);
return (): void => {
clearInterval(scrollCheck);
(scrollElement || window).removeEventListener('scroll', () => setScrolling(true));
};
}, [handleScroll]);
from use-scroll-position.
Related Issues (20)
- Can't get it to work HOT 3
- Doesn't account for overscroll HOT 1
- Scroll bounce on iphone HOT 1
- `getBoundingClientRect` is called even when `useWindow: true` HOT 1
- Scroll position as soon as the page loads HOT 1
- Scroll on bottom HOT 3
- Make listener passive HOT 2
- Build error with Next.js when using this library HOT 4
- IE 11 Syntax error HOT 1
- Type 'undefined' is not assignable to type 'HTMLElement' HOT 1
- useScrollPosition.defaultProps does nothing HOT 1
- How to write tests in Jest+Enzyme for a Component that uses useScrollPosition? HOT 1
- NPM package doesn't install typings
- Old eventlisteners should be removed when the dependencies change.
- Hooks not called in overflowY: 'scroll' components HOT 1
- useScrollPosition does not work
- detect scroll stop
- any examples with the bouding element property being used?
- Argument of type 'MutableRefObject<HTMLElement | null>' is not assignable to parameter of type 'MutableRefObject<HTMLElement | undefined>'. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from use-scroll-position.