ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ํ™”๋ฉด ๋„ˆ๋น„๋ฅผ ์ดˆ๊ณผํ•˜์ง€ ์•Š๋Š” ํˆดํŒ ๋งŒ๋“ค๊ธฐ w.React
    TECH 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 ๋‚ด์— ์œ„์น˜์‹œํ‚ค์ง€ ๋ชปํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๋นจ๊ฐ„ ํ…Œ๋‘๋ฆฌ๋กœ ํ‘œ์‹œํ•œ ์š”์†Œ๊ฐ€ offsetParent๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ, ์ƒ์œ„ ์š”์†Œ์˜ position ์†์„ฑ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.

     

     

     

     

    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

     

    GitHub - ttaerrim/screen-limit-tooltip: ํ™”๋ฉด์„ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๋Š” ํˆดํŒ ๋งŒ๋“ค๊ธฐ

    ํ™”๋ฉด์„ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๋Š” ํˆดํŒ ๋งŒ๋“ค๊ธฐ. Contribute to ttaerrim/screen-limit-tooltip development by creating an account on GitHub.

    github.com

     

    ๋Œ“๊ธ€

Designed by Tistory.