본문 바로가기
JavaScript/React

[React.JS]/[리액트] 특정라인수 이상 text-overflow: ellipsis 처리와 ellipsis 더보기/접기

by 봉이로그 2024. 1. 1.

1. 특정 라인 수 이상일 경우 ellipsis 처리하기

일반적으로 아래의 CSS 속성들을 이용해 다중 줄수 ...(ellipsis) 처리를 한다.

overflow: hidden;
word-break: normal;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;

 

overflow: hidden; 

요소의 내용이 자신의 상자를 벗어날 경우, 초과된 부분을 숨김으로 처리

 

word-break: normal;

단어의 줄 바꿈을 일반적인 방식으로 처리.

긴 단어가 요소의 너비를 벗어날 경우, 단어의 일부를 다음 줄로 옮기지 않고, 단어 전체를 다음 줄로 이동

 

text-overflow: ellipsis;

요소의 텍스트가 요소의 너비를 초과하는 경우, 초과된 텍스트를 생략 부호(…)로 표시

 

display: -webkit-box;

요소를 블록 레벨 상자로 표시하며, 내부의 자식 요소들을 유연하게 배치하기 위해 사용

 

-webkit- 접두어

과거 웹킷(WebKit) 기반 브라우저에서의 사용을 위한 것

 

-webkit-line-clamp: 5;

브라우저의 웹킷 엔진에서 사용되며, 표시하고자 하는 텍스트의 줄 수를 지정

이 예시에서는 최대 5줄까지 표시하고 그 이상의 내용은 생략 부호로 표시

 

-webkit-box-orient: vertical;

브라우저의 웹킷 엔진에서 사용되며, 요소 내의 블록 레벨 상자들의 배치 방향을 수직(세로)으로 설정

 

Ellipsis 처리하려는 태그의 CSS 속성값은 위와같이 지정한다.

 

import styled from 'styled-components'

...

return (
    <Box>
        ...
        <EllipsisText>
            텍스트를 매우 많이 넣어주세요.
        </EllipsisText>
        ...
    </Box>
)

...

const EllipsisText = styled.div`
  overflow: hidden;
  word-break: normal;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
`;

 

2. ellipsis 처리된 text 더보기/접기 버튼을 이용해보기

1) ellipsis를 제어할 상태를 만든다. 총 3가지의 상태를 만들어 구현했다. (방법은 다양함)

   1. textRef: 텍스트의 길이변화에 따라 높이를 감지하는 역활

   2. hasEllipsis: Eliipisis의 유무 상태 핸들링

   3. isExpanded: "더보기" / "접기" 상태 핸들링

  - hasEllipsis는 텍스트가 ellipsis되는 경우 나타나는 "더 보기" or "접기" 버튼이 활성화되어지는 상태이다.

 

2) text-overflow가 ellipsis가 되었을 경우를 탐지하는 방법

scrollHeight: 텍스트영역(안보이는 부분을 포함) 이 차지하고 있는 높이 

offsetHeight: 현재 보이는 텍스트영역이 차지하고 있는 높이

 

scrollHeight(전체 텍스트 영역높이) > offsetHeight(보이는 텍스트영역높이) = 텍스트오버플로우 발생

 

결과

 

전체코드

import React, { useState, useEffect, useRef } from 'react';
import styled from 'styled-components';

const TEXT = `텍스트를 매우 많이 넣어주세요.텍스트를 매우 많이 넣어주세요.텍스트를
          매우 많이 넣어주세요.텍스트를 매우 많이 넣어주세요.텍스트를 매우 많이
          넣어주세요.텍스트를 매우 많이 넣어주세요.텍스트를 매우 많이
          넣어주세요.텍스트를 매우 많이 넣어주세요.텍스트를 매우 많이
          넣어주세요.`

export function App(props) {
  const textRef = useRef(null);
  const [isExpanded, setIsExpanded] = useState(false);
  const [hasEllipsis, setHasEllipsis] = useState(false);

  const checkEllipsis = () => {
    if (textRef.current) {
      const { scrollHeight, offsetHeight } = textRef.current || {};

      // scrollHeight: 실제 컨텐츠 영역의 높이
      // offsetHeight: 엘리먼트의 전체크기(패딩, 보더 스크롤바 사이즈포함)
      const _hasEllipsis = scrollHeight > offsetHeight;
      
      if (_hasEllipsis) { // ellipsis 일경우
      	setHasEllipsis(true);
      }
      else { // ellipsis 아닐경우
        setIsExpanded(false); // "더보기" 버튼상태 초기화
        setHasEllipsis(false); // ellipsis 상태 초기화
      }
    }
  };

  const onClickReadMore = () => {
    setIsExpanded(prev => !prev);
  };

  useEffect(() => {
    checkEllipsis();

    window.addEventListener('resize', checkEllipsis);
    return () => {
      window.removeEventListener('resize', checkEllipsis);
    };
  }, [textRef]);

  return (
    <>
      {isExpanded ? ( // "더보기 클릭 했을 경우"
        <p ref={textRef}>
        {TEXT}
        </p>
      ) : ( // "기본 텍스트"
        <EllipsisText ref={textRef}>
          {TEXT}
        </EllipsisText>
      )}

      {hasEllipsis && (
          <button type='button' onClick={onClickReadMore}>
            {isExpanded ? '접기' : '더 보기'}
          </button>
      )}
    </>
  );
}

const EllipsisText = styled.p`
  overflow: hidden;
  word-break: normal;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
`;