-
ํ๋ฉด ๋๋น๋ฅผ ์ด๊ณผํ์ง ์๋ ํดํ ๋ง๋ค๊ธฐ w.ReactTECH 2024. 4. 28. 21:08
ํดํ์ ํด๋ฆญํ๊ฑฐ๋ ๋๋ ๋ง์ฐ์ค๋ฅผ ํน์ ๊ตฌ์ฑ ์์์ ๊ฐ์ ธ๊ฐ์ ๋(hover ํ์ ๋) ์ํฉ์ ๋ง๋ ๋์๋ง์ด๋ ์ ๋ณด๋ฅผ ํ์ํฉ๋๋ค.
ํดํ์ ์ง์ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ์ค, hover ์์๊ฐ ํ๋ฉด ์ ์ค๋ฅธ์ชฝ ๋์ ์์นํ ๋ ํดํ์ ์ปจํ ์ด๋๊ฐ ํ๋ฉด ๋๋น๋ฅผ ๋์ด๊ฐ๋ ํ์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
๋ค์๊ณผ ๊ฐ์ ํ์์ ์ฌ์ฉ์๊ฐ ์์ํ ๋๋ก ์ธํฐํ์ด์ค๊ฐ ๋์ํ์ง ์๊ณ , ํดํ์ ๋ด์ฉ์ ํ์ธํ๊ธฐ ์ํด ํ๋ฉด์ ์คํฌ๋กคํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ํดํฉ๋๋ค.
๋ฐ๋ผ์ ์์ ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ๊ฐ์ ํ์ฌ ํดํ์ ์ปจํ ์ด๋๊ฐ ํ๋ฉด์ ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๋์ด๊ฐ์ง ์๋๋ก ํดํ์ ์์น๋ฅผ ์กฐ์ ํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฐ์ ์์ ์ ํดํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import styles from './tooltip.module.css'; import IconQna from '../../assets/icon_qna.svg'; interface Props { title?: string; message: string[]; } export const Tooltip = ({ message, title }: Props) => { return ( <div className={styles.tooltip}> <img className={styles['icon']} src={IconQna} alt='tooltip icon' width={16} height={16} /> <div className={styles['tooltip']}> {title && <p className={styles['header']}>{title}</p>} {message.map((message, index) => ( <p className={styles['body']} key={index}> • {message} </p> ))} </div> </div> ); };
ํ๋ฉด ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๋์ด๊ฐ์ง ์๋ ํดํ์ ์ํด (1)
window.innerWidth
์ (2) MouseEvent์clientX
๋๋getBoundingClientRect
๋ฉ์๋, (3) ๊ทธ๋ฆฌ๊ณ ํดํ ์ปจํ ์ด๋์ ๋๋น ์์ฑ์ ํ์ฉํ ์ ์์ต๋๋ค.๋ค์ ๊ทธ๋ฆผ์ window์
outerHeight
์innerHeight
์์ฑ์, ๊ทธ๋ฆฌ๊ณouterWidth
์innerWidth
์์ฑ์ ๋น๊ตํ ๊ทธ๋ฆผ์ ๋๋ค.ํ๋ฉด์ ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๋์ด๊ฐ์ง ์๋ ํดํ์ ๋ง๋ค๊ธฐ ์ํด
window.innerWidth
๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.์ฐ์ MouseEvent์
clientX
์์ฑ์ ์ฌ์ฉํ์ฌ ๊ตฌํํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.์ฐธ๊ณ ๋ก
clientX
๋ ๋ธ๋ผ์ฐ์ ํ์ด์ง๋ฅผ ๊ธฐ์ค์ผ๋ก ํน์ ์ง์ ์ x ์ขํ๋ฅผ ํ์ํด ์ค๋๋ค.import styles from './tooltip.module.css'; import IconQna from '../../assets/icon_qna.svg'; import { useEffect, useRef, useState } from 'react'; interface Props { title?: string; message: string[]; } export const Tooltip = ({ message, title }: Props) => { const [screenWidth, setScreenWidth] = useState(0); const [clientX, setClientX] = useState(0); const [leftX, setLeftX] = useState(0); const tooltipRef = useRef<HTMLDivElement>(null); useEffect(() => { setScreenWidth(window.innerWidth); }, []); useEffect(() => { const tooltipWidth = tooltipRef.current?.clientWidth || 0; if (clientX + tooltipWidth > screenWidth) { setLeftX(clientX + tooltipWidth - screenWidth); } else { setLeftX(0); } }, [clientX, screenWidth]); return ( <div className={styles.tooltip} onMouseOver={(e) => setClientX(e.clientX)}> <img className={styles['icon']} src={IconQna} alt='qna' width={16} height={16} /> <div style={{ left: leftX > 0 ? `-${leftX}px` : '0' }} className={styles['tooltip']} ref={tooltipRef}> {title && <p className={styles['header']}>{title}</p>} {message.map((message, index) => ( <p className={styles['body']} key={index}> • {message} </p> ))} </div> </div> ); };
ํดํ์ ์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์ ๋๋น๋ฅผ ์ด๊ณผํ ๊ฒฝ์ฐ๋ฅผ ๋ํ๋ธ ๊ทธ๋ฆผ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
clientX
์tooltipWidth
์ ํฉ์์window.innerWidth
๋ฅผ ๋บleftX
๋งํผ ํดํ์ ์์น๋ฅผ ์ผ์ชฝ์ผ๋ก ์ด๋ํด ์ฃผ๋ฉด๋ค์๊ณผ ๊ฐ์ด ํดํ์ด ํ๋ฉด ๊ฐ๋ก ๊ธธ์ด๋ฅผ ๋์ด๊ฐ์ง ์๊ฒ ๋ฐฐ์นํ ์ ์์ต๋๋ค.
๊ทธ๋ฐ๋ฐ MouseEvent์
clientX
๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์๊น๋๋ค.๋ง์ฐ์ค๊ฐ ํดํ ์์๋ฅผ ํธ๋ฒํ์ ์ฐฐ๋์
clientX
๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ํดํ ์ปจํ ์ด๋๊ฐ ํ์๋ ๋๋ง๋คclientX
๊ฐ ์กฐ๊ธ์ฉ ๋ฌ๋ผ์ ธ ํดํ ์ปจํ ์ด๋์ ์์น๊ฐ ๋ณํ ์ ์๋ค๋ ์ ์ ๋๋ค.์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด
getBoundingClientRect
๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.getBoundingClientRect
๋ฉ์๋๋ ์์์ ํฌ๊ธฐ์ ๋ทฐํฌํธ์ ์๋์ ์ธ ์์น ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ DOMRect ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.offsetLeft
์์ฑ์ ์ฌ์ฉํ์ฌ ์์์ x ์ขํ๋ฅผ ๊ตฌํ ์๋ ์์ต๋๋ค.ํ์ง๋ง
offsetLeft
๋offsetParent
์ ์์กดํ๋ ๊ฐ์ ๋๋ค.offsetParent
๊ฐ body์ผ ๊ฒฝ์ฐ์๋ ์ํ๋ ๋๋ก ๋์ํ์ง๋ง,offsetParent
๋postion: static
์ด ์๋ ์์ ๋ ธ๋๋ฅผ ๊ฐ๋ฆฌํค๊ธฐ ๋๋ฌธ์offsetParent
๊ฐ body๊ฐ ์๋๋ผ๋ฉด ํดํ ์ปจํ ์ด๋๋ฅผ ๋ธ๋ผ์ฐ์ ์innerWidth
๋ด์ ์์น์ํค์ง ๋ชปํ ์๋ ์์ต๋๋ค.getBoundingClientRect
๋ฉ์๋๊ฐ ๋ฐํํ DOMRect ๊ฐ์ฒด์x
๋left
์์ฑ์ ์ฌ์ฉํ๋ฉด ๋ง์ฐ์ค์ ์์น์ ์๊ด์์ด ์์์ ๋ํ ๊ฐ์ ์ฌ์ฉํ๊ฒ ๋๋ฏ๋ก ํดํ ์ปจํ ์ด๋์ ์์น๋ฅผ ๊ณ ์ ์ํฌ ์ ์์ต๋๋ค.export const Tooltip = ({ message, title }: Props) => { // ์๋ต, ์ ์ฝ๋์ ๋์ผ return ( <div className={styles.tooltip} onMouseOver={(e) => setOffsetX(e.currentTarget.getBoundingClientRect().left)}> // ์๋ต </div> ); };
์์ ํ ์ฝ๋๋ ๊ฐ๋จํฉ๋๋ค.
setOffsetX(e.clientX)
๋์setOffsetX(e.currentTarget.getBoundingClientRect().left)
๋ก ํธ๋ฒํ๋ ์์์ ์๋ ์์น๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.๋ค์๊ณผ ๊ฐ์ด ํ๋ฉด์ ๋๋น๋ฅผ ๋ฒ์ด๋์ง ์๋ ํดํ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ต๋๋ค.
์ ์ฒด ์ฝ๋๋ ๋ค์ ๋งํฌ์์ ํ์ธํ ์ ์์ต๋๋ค.
https://github.com/ttaerrim/screen-limit-tooltip
'TECH' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฆฌ์กํธ์์ requestAnimationFrame ์ฌ์ฉ ์ ์ฃผ์ํ ์ (0) 2024.03.17 React Hook Form์ ์๋ก ๋น์ทํ ๊ธฐ๋ฅ๋ค์ ๋น๊ตํด ๋ณด์ (0) 2024.02.03 useState, ํด๋ก์ ๋ฅผ ํ์ฉํ์ฌ ๋์ํ๋ค๊ณ ? (0) 2024.01.27 React์์ Event Handler๋ ์ด๋ป๊ฒ ๋์ํ ๊น์? (0) 2024.01.11 Zero Runtime CSS-in-JS์ ๋ํด ์์๋ณด์ (1) 2024.01.06