개발일지/Front-end

HTML Element Javascript를 이용한 Overflow 감지

cotnmin 2023. 1. 2. 22:02

HTML Object overflow 감지

HTML 내 요소에서 자식요소가 부모요소보다 클 때 css 상에서 overflow 속성을 정의해 처리를 할 수 있습니다.
하지만 이를 javascript에서 동적으로 처리를 하고 싶다면 다른 방법을 이용해야 합니다.
이때 2가지 방법을 이용할 수 있습니다.

1. scrollWidth 이용

위 이미지를 참고하면 clientWidthscrollWidth를 비교하여 scrollWidthclientWidth보다 크다면 overflow가 발생했음을 알 수 있습니다.


아래의 예제를 따라 해보면 문제없이 결과를 얻을 수 있습니다.

<div class="wrap">
    <!-- short는 200px 미만으로 overflow가 발생하지 않는다. -->
    <div id="short">Lorem ipsum dolor sit amet</div>
    <div id="long">consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>
<style>
    .wrap > div {
        width: 200px;
        white-space: nowrap;
    }
</style>
const short = document.getElementById('short');
const long = document.getElementById('long');
// └ getElementById를 이용해 Element 객체 가져오기

const isOverflow = element => {
    const {clientWidth, scrollWidth} = element;
    // └ 구조분해 할당으로 clientWidth, scrollWidth 값 가져오기
    return clientWidth < scrollWidth;
}

console.log(`is overflow? ${isOverflow(short)}`); 
// 출력: is overflow? false
console.log(`is overflow? ${isOverflow(long)}`);  
// 출력: is overflow? true

좀 더 응용해 보면

const short = document.getElementById('short');
const long = document.getElementById('long');

const getDiff = element => {
    const {clientWidth, scrollWidth} = element;
    const diff = scrollWidth - clientWidth;
    return diff ? `overflow ${diff}px` : 'not overflow';
}

console.log(getDiff(short)); 
// 출력: not overflow
console.log(getDiff(long));  
// 출력: overflow 439px

이렇게 얼마나 콘텐츠가 넘치는지 체크할 수 있습니다.

2. cloneNode 이용

두 번째 방법은 Node객체의 instance methods 중 하나인 cloneNode를 이용한 방법입니다.

Element객체는 Node객체를 상속받은 객체로 Node객체의 instance methods를 이용 가능합니다.
cloneNode는 한 Node객체를 그대로 복사해오는 method로 parameter로 boolean을 받아 자식요소들도 복제할지 선택할 수 있습니다.

 

복제해온 Node의 style에 길이를 max-content로 지정해 길이를 구하고 제거하는 방식으로 overflow를 감지할 수 있습니다.

<div class="wrap">
    <!-- short는 200px 미만으로 overflow가 발생하지 않는다. -->
    <div id="short">Lorem ipsum dolor sit amet</div>
    <div id="long">consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>
<style>
    .wrap > div {
        width: 200px;
        white-space: nowrap;
    }
</style>

바로 1번 방식의 응용 예제를 따라 해보면

const short = document.getElementById('short');
const long = document.getElementById('long');
// └ getElementById를 이용해 Element 객체 가져오기

const getCloneWidth = element => {
    const clone = element.cloneNode(true);
    // └ parameter로 true를 전달해 자식요소를 함께 복사
    clone.style.width = 'max-content';
    // └ 콘텐츠가 최대로 커질 수 있는 너비로 변경
    element.parentNode.appendChild(clone);
    // └ 원본 요소의 부모요소에 append해 상속받는 style도 적용하기
    const cloneWidth = clone.clientWidth;
    // └ 복제된 요소의 길이 구하기
    element.parentNode.removeChild(clone);
    // └ 복제된 요소 제거
    return cloneWidth;
}


const getDiff = element => {
    const width = element.clientWidth;
    const cloneWidth = getCloneWidth(element);
    const diff = cloneWidth - width;
    return diff ? `overflow ${diff}px` : 'not overflow';
}

console.log(getDiff(short)); 
// 출력: overflow -15px
console.log(getDiff(long));  
// 출력: overflow 439px

long의 결과는 1번 방식과 같은 결과를 보이나 short의 경우가 -15px로 나타남을 볼 수 있습니다.

 

차이는 콘텐츠의 최대 너비를 구하는 과정에서 발생하는데 scrollWidth는 요소가 아무리 작아도 clientWidth보다 작아지지 않습니다.

그러나 cloneNode방식을 이용했을 때는 복제된 Node의 너비 자체를 콘텐츠의 크기로 변경한 것으로 getCloneWidth에서 반환된 크기는 콘텐츠의 크기가 됩니다.

 

즉, Lorem ipsum dolor sit amet의 길이가 185px일 때 scrollWidth는 200px이 되는것이고 getCloneWidth의 반환 값은 185px이 되는 것입니다.


추가로 콘텐츠 크기를 구할 수 있는 cloneNode 방식을 Element에 바인딩할 수 있습니다.

Object.defineProperty(Element.prototype, 'contentWidth', {get() {
    const clone = this.cloneNode(true);
    clone.style.width = 'max-content';
    this.parentNode.appendChild(clone);
    const cloneWidth = clone.clientWidth;
    this.parentNode.removeChild(clone);
    return cloneWidth;
}});

위와 같이 작업해두면 아래처럼 간단하게 이용 가능합니다.

const element = document.getElementById('someId');
console.log(element.contentWidth);
const elements = document.querySelectorAll('someSelector');
elements.forEach(element => console.log(element.contentWidth));

마무리로 두가지 방법을 요약하자면

감지방식 scrollWidth cloneNode
콘텐츠 크기 < clientWidth clientWidth 콘텐츠 크기
성능 빠름(scrollWidth값을 읽기만 하면 됨) 느림(DOMcloneNode를 그려야함)
추천 상황 일반적으로 사용 정확한 콘텐츠 크기를 비교해야할 때

로 볼 수 있습니다.

위 방법들을 이용해 Javascript에서 HTML Element에 Overflow가 발생했는지 간단히 알아볼 수 있습니다.