Javascript/React listen to dom move on screen?

Viewed 39

I'm looking to a solution to get if Dom move on screen,

I have an absolut div (a download overlay) appears when click on documents on my website, but I want her to disappear when the document move (like scroll, touchmove, etc...) but actually my documents can be on differents container that scroll vertically and horizontally, and I try to find a way to avoid put listener on each container to scroll..

My overlay is triggered by custom hook in React, disappear on click outside, disappear on window scroll, but I'm in trouble with different containers's scroll..

Can someone have an idea to help me please ? Have a nice day !

1 Answers

So if I understood the issue correctly, you have already implemented the "disappear on window scroll" functionality, but you actually need it to be "disappear on container scroll".

As this functionality is already encapsulated into the Overlay component, there are two non-exclusive approaches:

  • Pass a ref to the scrolling container as a prop and add the scroll event listener there.
  • Assuming the overlay is located within the container in the DOM, get a ref to the overlay itself and start going up until you find an element that scrolls (element.scrollHeight > element.offsetHeight).

Working example:

const Overlay = ({ open, onClose }) => {
  const rootRef = React.useRef(null);

  const onMount = React.useCallback((element) => {
    element.addEventListener("scroll", onClose);
  }, [onClose]);

  const onUnmount = React.useCallback((element) => {
    element.removeEventListener("scroll", onClose);
  }, [onClose]);

  const setRef = React.useCallback((element) => {
    if (rootRef.current) {      
      onUnmount(rootRef.current);
    }

    rootRef.current = element;

    if (element) {
      let scrollableElement = element;
    
      while (scrollableElement.scrollHeight <= scrollableElement.offsetHeight && scrollableElement !== document.body) {
        scrollableElement = scrollableElement.parentElement;
      }
      
      onMount(scrollableElement);
    }
  }, [onMount, onUnmount]);
  
  if (!open) return null;
  
  return (
    <div className="overlay" ref={ setRef }>
      <div className="overlayModal">
          <button onClick={ onClose }>
            Close
          </button>
          
          <div className="overlayContent"></div>
          
          <button onClick={ onClose }>
            Close
          </button>
      </div>
    </div>
  )
};

const App = () => {
  const [open, setOpen] = React.useState(0);

  return (
    <div className="app">
    
      <div className="col1">
        <button onClick={ () => setOpen(1) }>
          Open Overlay
        </button>
        
        <div className="content">
          <Overlay
            open={ open === 1 }
            onClose={ () => setOpen(0) } />        
        </div>
        
        <button onClick={ () => setOpen(1) }>
          Open Overlay
        </button>
      </div>
      
      <div className="col2">
        <button onClick={ () => setOpen(2) }>
          Open Overlay
        </button>
        
        <div className="content">
          <Overlay
            open={ open === 2 }
            onClose={ () => setOpen(0) } />
        </div>
        
        <button onClick={ () => setOpen(2) }>
          Open Overlay
        </button>        
      </div>
      
    </div>
  );
}

const root = ReactDOM.createRoot(document.querySelector('#app'));

root.render(<App />);
body {
  font-family: monospace;
  margin: 0;
}

button {
  font-family: monospace;
  padding: 8px 16px;
  border: 2px solid black;
  border-radius: 4px;
  background: white;
}

.app {
  display: flex;
  height: 100vh;
}

.col1,
.col2 {
  flex: 1 0 50%;
  overflow-y: scroll;
  padding: 16px;
  box-sizing: border-box;
}

.col1 {
  border-right: 2px solid black;
}

.content {  
  position: relative;
  height: 480px;
  margin: 8px 0;
  background: #F0F0F0;
  border-radius: 4px;
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, .95);
}

.overlayModal {
  position: relative;
  height: 100%;
  background: white;
  border-radius: 4px;
  box-shadow: 0 0 32px rgba(0, 0, 0, .125);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  padding: 16px;
  box-sizing: border-box;
}

.overlayContent {
  flex: 1 0 auto;
}
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>

<div id="app"></div>

Related