Giter Club home page Giter Club logo

Comments (16)

jpodpro avatar jpodpro commented on August 29, 2024 5

am i missing something? i can't find any working example for how to use this with an element ref.

from use-scroll-position.

joebochill avatar joebochill commented on August 29, 2024 5

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.

armordog avatar armordog commented on August 29, 2024 4

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.

sasweb avatar sasweb commented on August 29, 2024 2

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.

D-Thrane avatar D-Thrane commented on August 29, 2024 2

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.

sasweb avatar sasweb commented on August 29, 2024

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.

janhesters avatar janhesters commented on August 29, 2024

@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.

edriskus avatar edriskus commented on August 29, 2024

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.

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.

abhijithvijayan avatar abhijithvijayan commented on August 29, 2024

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.

jpodpro avatar jpodpro commented on August 29, 2024

@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.

joebochill avatar joebochill commented on August 29, 2024

@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 🤦 Time to do some more digging.

from use-scroll-position.

jpodpro avatar jpodpro commented on August 29, 2024

@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.

joebochill avatar joebochill commented on August 29, 2024

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.

hungvu193 avatar hungvu193 commented on August 29, 2024

any updates guys? I'm stuck :(

from use-scroll-position.

iksent avatar iksent commented on August 29, 2024

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.

joebochill avatar joebochill commented on August 29, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.