Created
July 4, 2024 22:20
-
-
Save latobibor/941627df030d1651b703ebafbc02b645 to your computer and use it in GitHub Desktop.
Scrolling in one div moves the other one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This is very primitive styling, I kept it like this as style is now not important. | |
If you change the size make sure the scrollbar is visible as without it nothing will happen. | |
*/ | |
.controller-div { | |
background-color: #ddffee; | |
width: 100px; | |
height: 200px; | |
max-height: 100px; | |
overflow-y: scroll; | |
border-radius: 15px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import styles from './controller-div.module.css'; | |
interface ControllerDivProps { | |
// source of UIEvent hack: https://freshman.tech/snippets/typescript/fix-value-not-exist-eventtarget/ | |
onScroll: (event: React.UIEvent<HTMLDivElement, UIEvent> & { target: HTMLElement }) => void; | |
} | |
export function ControllerDiv({ onScroll }: ControllerDivProps) { | |
return ( | |
<div className={styles['controller-div']} onScroll={onScroll}> | |
Scroll me! Scroll me! Scroll me! Scroll me! Scroll me! Scroll me! Scroll me! Scroll me! | |
</div> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { memo } from 'react'; | |
import { ControllerDiv } from './controller-div'; | |
import { MovableBlock } from './movable-block'; | |
import { useVerticalScrollPercentage } from './use-vertical-scroll-percentage'; | |
export function MainComponent() { | |
const { onScroll, verticalPercentage } = useVerticalScrollPercentage(); | |
return ( | |
<main> | |
<ControllerDiv onScroll={onScroll} /> | |
<MovableBlock yPosition={verticalPercentage} /> | |
<OtherThingsMemo /> | |
</main> | |
); | |
} | |
function OtherThings() { | |
console.log('Other things got rendered!'); | |
return ( | |
<> | |
<input name="input-1" /> | |
<input name="input-2" /> | |
</> | |
); | |
} | |
const OtherThingsMemo = memo(OtherThings); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.movable-block { | |
position: fixed; | |
/* I just stick it on the right, so it won't overlap with the controller div on the left */ | |
right: 0; | |
background-color: darkgoldenrod; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import styles from './movable-block.module.css'; | |
const HEIGHT = 50; | |
const HUNDRED_PERCENT = 100; | |
interface MovableBlockProps { | |
yPosition: number; | |
} | |
export function MovableBlock({ yPosition }: MovableBlockProps) { | |
const LOWER_BOUND = (yPosition * HEIGHT) / HUNDRED_PERCENT; | |
// There is a very neat trick you can use in CSS: 100vh equals the entire screen height (vh = view height). | |
// Therefore you can directly use percentage value to position on the screen. | |
// We also need to factor in the size of the block; if we do not then at 100vh it scrolls out of the screen. | |
// CSS calc can convert between percentage based values and pixel based values, how cool! | |
const top = `calc(${yPosition}vh - ${LOWER_BOUND}px)`; | |
return ( | |
<div | |
className={styles['movable-block']} | |
style={{ | |
top, | |
width: `${HEIGHT}px`, | |
height: `${HEIGHT}px`, | |
}}> | |
Hi! | |
</div> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState } from 'react'; | |
export function useVerticalScrollPercentage() { | |
const [verticalPercentage, setVerticalPercentage] = useState(0); | |
function onScroll(event: React.UIEvent<HTMLElement> & { target: HTMLElement }) { | |
const lowestPointForScrollTop = event.target.scrollHeight - event.target.clientHeight; | |
// For sake of mental ease the values returned should fall between 0 and 100 as they represent percentage | |
const percentageOfHeight = Math.floor((event.target.scrollTop / lowestPointForScrollTop) * 100); | |
setVerticalPercentage(percentageOfHeight); | |
} | |
return { | |
onScroll, | |
verticalPercentage, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment