Skip to content

Instantly share code, notes, and snippets.

@kristersd
Last active October 27, 2022 00:42
Show Gist options
  • Save kristersd/f37745431f1859b624e8794627e3564c to your computer and use it in GitHub Desktop.
Save kristersd/f37745431f1859b624e8794627e3564c to your computer and use it in GitHub Desktop.
Handles mouse and scroll event position relative to container
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import "./styles.css";
const clamp = (number: number, min: number, max: number) => {
return Math.min(Math.max(number, min), max);
};
export default function App() {
const [{ x, y }, setMousePosition] = useState({ x: 0, y: 0 });
const containerRef = useRef<HTMLDivElement>(null);
const scrollTopPositionRef = useRef<number>(0);
const [sidebarHeight, setSidebarHeight] = useState(0);
useLayoutEffect(() => {
const sidebarHeight =
document.querySelector(".sidebar__header")?.getBoundingClientRect()
.height || 0;
setSidebarHeight(sidebarHeight);
setMousePosition({ x: 0, y: sidebarHeight });
}, []);
useEffect(() => {
const element = containerRef.current;
if (!element) {
return;
}
const initialTop = element.getBoundingClientRect().top;
const initialLeft = element.getBoundingClientRect().left;
const handler = (e: MouseEvent | Event) => {
if (!e) {
return;
}
if (e.type === "scroll") {
setMousePosition((prevState) => ({
x: prevState.x,
y: clamp(
prevState.y +
(scrollTopPositionRef.current - element.scrollTop) * -1,
sidebarHeight,
Infinity
)
}));
scrollTopPositionRef.current = element.scrollTop;
return;
}
setMousePosition({
x: (e as MouseEvent).pageX - initialLeft,
y: clamp(
(e as MouseEvent).pageY - initialTop + element.scrollTop,
sidebarHeight,
Infinity
)
});
};
const eventTypes = ["scroll", "mousemove"];
for (const eventType of eventTypes) {
element.addEventListener(eventType, handler);
}
return () => {
for (const eventType of eventTypes) {
element.removeEventListener(eventType, handler);
}
};
}, [sidebarHeight]);
return (
<div className="App">
<div ref={containerRef} className="container">
<div className="sidebar">
<div
className="sidebar__header"
style={{ backgroundColor: "purple" }}
>
Sidebar Sidebar Sidebar Sidebar Sidebar Sidebar Sidebar Sidebar
Sidebar Sidebar
</div>
<div className="sidebar__position" style={{ top: y }}>
X: {x}, Y: {y - sidebarHeight}
</div>
</div>
<div className="content">
<div className="content__main">Main content</div>
</div>
</div>
</div>
);
}
// Play around - https://codesandbox.io/s/react-typescript-forked-wszqm8?file=/src/App.tsx:0-2587
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment