개발 기술/사소하지만 놓치기 쉬운 개발 지식

[CSS/JS] ellipsis 말줄임에서 발생할 수 있는 2가지 인터렉션 문제

by GicoMomg 2024. 9. 19.

1. 들어가며…

웹사이트를 탐색하다 보면, 일정 길이를 초과한 텍스트에 말줄임(…) 처리가 된 것을 볼 수 있다. 특히, 뉴스 피드나 검색 결과 페이지처럼 많은 정보가 한눈에 들어와야 하는 상황에서, 말줄임 기법은 중요한 역할을 한다.

이 방법은 글을 일정 부분에서 잘라내고 "…"을 추가하여 화면 공간을 보다 효율적으로 사용하게 해준다. 아래 이미지는 말줄임 기법이 적용된 예시로, 텍스트가 일정 길이를 넘을 경우 어떻게 처리되는지 보여준다.



그렇다면, 이러한 말줄임은 어떻게 구현할 수 있을까?
JavaScript를 사용해 글자를 특정 길이로 자른 후 '…'를 추가할 수 있으며,
또는 CSS의 text-overflow 속성을 활용할 수도 있다. 아래는 CSS를 이용한 말줄임 처리 예시이다.

See the Pen text one line ellipsis by KumJungMin (@kumjungmin) on CodePen.

.text {
  width: 250px;
  font-size: 50px;
  text-overflow: ellipsis;  /* 글자가 width 이상일 때 말줄임 처리 */
  white-space: nowrap;      /* 내부가 넘쳐도 일렬로 배치 */
  overflow: hidden;         /* width를 넘어가는 경우, 숨김 */
}

그런데, 말줄임 처리 영역에 글자가 아닌 다른 요소가 있어도 제대로 작동할까?

ellipsis는 텍스트뿐만 아니라 자식 요소가 공간을 초과할 때도 적용된다.
아래 예시를 보면, item 4가 말줄임 처리로 인해 숨겨진 것을 확인할 수 있다.

See the Pen ellipsis hidden issue - (1) text click issue by KumJungMin (@kumjungmin) on CodePen.



그렇다면, item 4는 숨겨졌지만 아무런 상호작용도 일어나지 않는 걸까? 놀랍게도 아니다!

이유는 간단하다. 말줄임 처리된 요소가 화면에 보이지 않더라도 여전히 DOM에 존재하기 때문이다.
그 결과, UI 상에서 보이지 않더라도 말줄임 처리된 영역이나 그 우측 영역을 클릭하면 클릭 이벤트가 발생할 수 있다.
문제가 발생하는 예시 코드는 다음과 같다.

See the Pen ellipsis hidden issue by KumJungMin (@kumjungmin) on CodePen.



  • 첫 번째 문제는 item 4가 위치한 부분(빨간 박스)을 클릭하면, item 4의 클릭 이벤트가 발생하고...


  • 두 번째 문제는 말줄임 영역(…)을 클릭하면, item 3의 클릭 이벤트가 발생한다는 것이다!



그렇다면, 말줄임 처리 후에도 숨겨진 요소에서 클릭 이벤트가 발생하는 문제는 어떻게 해결할 수 있을까요?
이번 시간에는 CSS ellipsis로 인해 발생할 수 있는 이러한 버그를 어떻게 수정할 수 있는지 알아보겠다!






2. css ellipsis(말줄임) 문제 해결법

문제 해결법 섹션의 코드는 아래 HTML 구조를 기준으로 설명한다. (예시 코드)

<div class="container">
    <div class="chip" data-id="1">Item 1</div>
    <div class="chip" data-id="2">Item 2</div>
    <div class="chip" data-id="3">Item 3</div>
    <div class="chip" data-id="4">Item 4</div>
</div>

1) 말줄임 영역의 상호작용 차단하기

(1) 현상

  • 말줄임표 영역(…)을 클릭하면, 왼쪽에 위치한 요소에서 클릭 이벤트가 발생하는 문제가 있다.
  • 이 문제는 말줄임표와 상호작용할 수 있는 요소가 인접해 있을 때 발생할 수 있다.
  • 그렇다면, 이 문제를 어떻게 해결할 수 있을까?


(2) 해결 방법: CSS에서 pointer-events 설정하기

  • 해결 방법은 간단하다. 바로 pointer-events 속성을 사용해 말줄임표 영역에서 클릭 이벤트가 발생하는 걸 막는 것이다.
  • 여기서 pointer-events란 특정 요소가 마우스 클릭, 터치 등과 같은 이벤트를 받을 수 있는지 제어하는 CSS 속성이다.
pointer-events: auto;
pointer-events: none; 
pointer-events 값 설명
auto 기본값으로, 요소가 정상적으로 포인터 이벤트를 수신함
none 해당 요소는 모든 포인터 이벤트를 무시함 (클릭, 마우스 오버, 포커스 등의 이벤트가 발생하지 않음)

  • 말줄임 처리된 영역에서는 포인터 이벤트를 차단하고, 버튼과 같은 상호작용이 필요한 .chip 요소에서는 이벤트를 활성화해야 한다.
  • 이를 위해 먼저 .container에서 포인터 이벤트를 막는다.
  • 하지만 .container에 포인터 이벤트를 막으면, 하위 요소의 포인터 이벤트도 함께 차단된다.
.container {
  pointer-events: none;
}

  • 따라서, .container 하위 요소 중 .chip에서는 포인터 이벤트가 정상적으로 동작하도록, .chippointer-events: auto를 추가해야 한다.
.container .chip {
  pointer-events: auto;
}

(3) 결과 보기

  • 이와 같이 CSS를 설정하면, 말줄임 영역을 클릭했을 때 인접한 요소에서 클릭 이벤트가 발생하는 문제를 해결할 수 있다.
.container {
  pointer-events: none;
}

.container .chip {
  pointer-events: auto;
}

말줄임 영역을 클릭해도 Item3의 클릭 이벤트가 발생하지 않는 모습




2) 숨겨진 요소의 상호작용 차단하기

(1) 현상

  • ellipsis의 두 번째 문제는 숨겨진 요소에서도 상호작용이 발생한다는 점이다.
  • 아래 GIF를 보면, 말줄임 우측 여백을 클릭할 때 숨겨진 요소(.item 4)의 클릭 이벤트가 발생하는 걸 알 수 있다.


왜 이런 현상이 발생하는 걸까?

  • CSS의 text-overflow: ellipsis 속성은 요소의 콘텐츠가 지정된 영역을 넘어설 때 텍스트를 잘라내고 말줄임표(...)를 표시하는 방식이다.
  • 하지만 중요한 점은 브라우저가 요소의 전체 내용을 여전히 렌더링한다는 것이다. 즉, 시각적으로 말줄임표 처리가 되지만, 실제로는 DOM(Document Object Model)에 해당 요소가 그대로 남아 있다.
  • 개발자 도구에서 확인해보면, item 4가 여전히 존재하는 것을 알 수 있다. (아래 그림 참고)


(2) 해결 방법: 요소가 숨겨져 있는지 여부 체크하기

  • 이 문제를 해결하려면, 해당 요소가 숨겨져 있는지 확인하는 함수(isVisible)가 필요하다.
  • isVisible 함수의 전체 코드는 다음과 같다.
const isVisible = (element) => { 
  // (a)
  const isOverflow = container.clientWidth < container.scrollWidth; 
  if (!isOverflow) return true;

 // (b)
  const target = element.closest('.chip');
  const targetStyles = window.getComputedStyle(target);
  const targetRight = target.offsetLeft + target.offsetWidth + parseFloat(targetStyles.marginRight);

  // (c)
  const containerStyles = window.getComputedStyle(container);
  const containerWidth = container.clientWidth - parseFloat(containerStyles.paddingLeft) - parseFloat(containerStyles.paddingRight);

  // (d)
  return targetRight <= containerWidth;
};

(a) 오버플로우 여부 확인

const isOverflow = container.clientWidth < container.scrollWidth; 

  • container.clientWidth컨테이너 요소의 가시적인 너비이고,
  • container.scrollWidth컨테이너의 전체 콘텐츠 너비를 나타낸다.
  • 만약 clientWidthscrollWidth보다 작다면, 콘텐츠가 넘쳐서 스크롤이 발생하고 있음을 의미한다.

(b) 타겟 요소의 끝 위치 계산

const target = element.closest('.chip');
const targetStyles = window.getComputedStyle(target);
const targetRight = target.offsetLeft + target.offsetWidth + parseFloat(targetStyles.marginRight);

  • 타겟의 우측 위치를 알면, 타겟이 화면을 벗어났는지 알 수 있다.
  • 타겟의 우측 위치(targetRight)는 "타겟의 좌측 위치 + 타겟의 전체 너비(border 포함) + 우측 margin"로 계산한다.

(c) 컨테이너의 콘텐츠 너비 계산

const containerStyles = window.getComputedStyle(container);
const containerWidth = container.clientWidth - parseFloat(containerStyles.paddingLeft) - parseFloat(containerStyles.paddingRight);
  • 마지막으로 clientWidth를 사용해, 컨테이너의 컨텐츠 너비를 구한다.
  • 이때 clientWidth은 패딩이 포함된 값이므로, clientWidth에서 좌우 패딩을 빼야 한다.

(d) 요소가 가시 영역 내에 있는지 확인

targetRight <= containerWidth
  • 타겟 요소의 우측 좌표(targetRight)가 컨테이너의 가시 영역(containerWidth) 내에 있는지 확인한다.
  • 만약 targetRightcontainerWidth보다 작거나 같다면, 타겟 요소는 가시 영역 내에 있으므로 보이는 상태이다.

+) 클릭 이벤트에서 호출하기

  • 앞서 설명한 isVisible 함수를 사용하면, 숨겨진 요소에서 클릭 이벤트가 발생하지 않도록 처리할 수 있다.
  • 이를 클릭 이벤트 핸들러에서 다음과 같이 활용할 수 있다.
  • 아래 handleClick().container 내부의 .chip 요소에서 클릭 이벤트가 발생할 때 실행되는 핸들러이다.
  • 만약 isVisible(item)false를 반환하면, 해당 요소는 숨겨진 상태이므로 early return으로 처리하여 이벤트를 중단한다.
const container = document.querySelector('.container');
container.addEventListener('click', handleClick);

function handleClick(e) {
  const item = e.target.closest('.chip')
  if (!item) return;
  if (!isVisible(item)) return; // 추가!
  ...
}
  • 이렇게 구현하면 숨겨진 요소에 클릭 이벤트가 발생하지 않도록 방지할 수 있다.

(3) 결과 보기

item4가 숨겨진 영역(빨간 박스)을 클릭해도 Item4의 클릭 이벤트가 발생하지 않는 모습


그럼 앞선 2가지 문제를 수정한 모습은 어떨까? 아래 playground가 바로 수정 결과이다.

See the Pen ellipsis hidden issue - resolved-1 by KumJungMin (@kumjungmin) on CodePen.



3. 마치며…

이번 시간에는 CSS ellipsis를 사용하면서 발생할 수 있는 두 가지 주요 문제에 대해 다루었다.
첫 번째는 말줄임표 영역에서 클릭 이벤트가 발생하는 문제였고, 두 번째는 말줄임으로 가려진 요소에서 클릭 이벤트가 발생하는 현상이었다.
이 문제들을 해결하기 위해 pointer-events 속성을 활용하거나, 숨겨진 요소가 가시적인지 여부를 체크하는 함수를 사용할 수 있었다.

말줄임 영역에 버튼이 들어가는 경우가 흔치는 않겠지만, 만약 이와 유사한 구조를 취해야 한다면 말줄임표와 숨겨진 요소에서 의도하지 않은 상호작용이 발생하지 않는지 확인하는 게 중요할 듯 하다.
해당 포스팅에서 사용한 예시 코드를 보고 싶다면 아래 링크를 참고해도 좋다 :)


반응형

댓글