Javascript 글자 입력할 때 textarea 자동 높이 조절


글자 입력할 때 textarea 자동 높이 조절

[ Javascript 목차 보기 ]

textarea의 크기 자동조절은 크게 2가지 목적으로 이용할 수 있다. 하나는 위의 구현 결과처럼 글자를 textarea에 넣을 때 거기에 반응하여 textarea의 높이가 자동 조절되는 방식이고, 또 다른 하나는 인터넷 브라우저의 크기를 변동할 때마다 항상 내용물에 맞쳐서 높이를 조절하는 방식이다.[참고1. textarea 높이 자동 조절] 여기서는 첫 번째 경우를 어떻게 구현하는지에 대해서 알아보자.

[1] 기본 구현 (HTML, CSS)

  • 예제 목표
    여기서는 먼저 HTML과 CSS의 기본 구현을 한다. 자바스크립트는 다음 섹션에서 구현한다.
  • HTML
    <div id=”wrapper”></div> <!– 예제 코드를 둘러싼 래퍼에 해당한다.[참고2. 래퍼란?] –>
    <textarea id=”message-box” rows=”1″></textarea> <!– textarea를 생성하고, 높이는 1행으로 설정한다 –>
  • #message-box의 CSS[참고3.ID 선택자 기본 개념]
    resize: none; // 크기 변동을 못하게 한다.
    border: 1px solid rgb(42, 42, 42); // 테두리선의 모양과 색깔을 설정한다.
    width: 400px; // width를 400px로 설정한다.
    padding: 5px; // 내부 여백을 5px로 설정하여 내부 텍스트 주위에 5px 여백을 준다.
    overflow: hidden; // 스크롤바를 생성하지 않는다.
    box-sizing: border-box; // 박스 형태는 border-box로 설정한다. border-box와 content-box는 height를 서로 다르게 설정한다. 우리는 border-box 형태로 height를 설정할 것인데, 자세한 내용은 아래에 나온다.
  • #wrapper의 CSS[참고3.ID 선택자 기본 개념]
    display: flex; // 자식 요소 textarea#message-box를 정렬시키기 위해 flex로 설정한다.
    justify-content: center; // 자식 요소 textarea#message-box를 수평 가운데 정렬한다.
    나머지는 기본적인 래퍼 속성에 해당한다. 래퍼의 기본 설정에 대한 내용들은 참고1 포스트에 자세히 나와 있다.[참고1. 래퍼란?]
HTML
1
2
3
<div id=“wrapper”>
    <textarea id=“message-box” rows=“1”></textarea>
</div>
cs
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
    #message-box {
        resize: none;
        border: 1px solid rgb(42, 42, 42);
        width: 400px;
        padding: 5px;
        overflow: hidden;
        box-sizing: border-box;
    }
    /* 아래는 기본 래퍼 설정 */
    #wrapper {
        display: flex;
        justify-content: center;
        margin: 0 auto;
        padding: 20px;
        max-width: 1170px;
    }
</style>
cs
RESULT

  • 구현 결과
    textarea가 형성되어 있다. 현재로는 textarea 안에 글자를 적을지라도, textarea의 높이가 변하지 않는다. 다음 섹션에서는 글자를 입력할 때, 입력한 높이에 반응하여 textarea 높이를 변동시키는 것을 자바스크립트로 구현할 것이다.

[2] 동적 동작 구현 (Javascript)

  • 예제 목표
    textarea에 텍스트를 입력할 때 높이를 자동 조절할 수 있게 자바스크립트로 구현한다.
  • 구현 원리
    scrollHeight는 “내용물의 높이”를 포함하는 개념이다. 따라서 textarea의 height를 scrollHeight로 설정함으로써 내용물의 높이에 맞게 자동 조절할 수 있다. 하지만 조심해야할 점들이 몇 가지 있다. 예를 들어 textarea의 height는 border-box이냐 content-box이냐에 따라 다르고 (여기에서는 border-box로 설정했음), scrollHeight 또한 상황에 따라 비어있는 높이가 포함 될 수 있다. 아래의 scrollHeight의 원리를 정확히 먼저 이해하고, 자바스크립트를 작성해보자.

[2.1] scrollHeight의 원리

  • textarea의 박스 모델
    구현 핵심 원리는 scrollHeight를 이용하는 것이기 때문에, scrollHeight가 뭘 의미하는지 정확히 이해해야 한다. Figure 1을 보면, textarea는 기본적인 박스 모델처럼 margin (여기서는 표시 안함), border, padding, content 부분으로 나눠진다. 그리고 content 부분에 내용물을 표시하게 된다. 하지만 중요한 점은 기본 박스 모델 외에 “빈 공간”이라는 개념도 추가되어야 한다는 것이다. 자세한 내용은 아래에서 다루고, 여기서는 먼저 박스-사이징(box-sizing)에 따라 height가 어떻게 달라지는지에 대해서 알아보자.
    • box-sizing: border-box일 때
      height = content + padding + border
    • box-sizing: content-box일 때
      height = content
textarea scrollHeight
Figure 1. textarea 박스 모델. scrollHeight = 내용물의 높이 + paddingTop + paddingBottom

  • scrollHeight란?
    scrollHeight는 “content+padding” 외에 “보여지지 않는 내용물”의 높이를 더한 값이 된다 – (Figure 1). 따라서 textare가 border-box의 경우에는 height를 scrollHeight + borderTop + borderBottom으로 설정해주면, 스크롤바 없이 모든 내용물을 보여 줄 수 있다. 여기서 borderTop과 borderBottom을 포함시켜야한다는 것에 유의하자. 인터넷에서 검색을 해보면 많은 사람들이 border 파트를 넣지 않는 경우가 많은데, 정확하지 않는 계산이다.
  • 주의할 점: “빈 공간“의 개념
    scrollHeight의 정의를 곱씹을 필요가 있다. 만약 Figure 1 오른쪽의 경우와 같이 scrollHeight를 단순히 “내용물의 높이 + padding”으로 이해한다면 다음과 같은 혼란이 발생할 수 있다. Figure 2와 같이 내용물이 다 보여지는 경우를 생각해보자. textarea의 height가 내용물의 height보다 크게 설정되어 있어서, “비어있는 높이”가 존재한다. 이러한 비어있는 높이는 내용물의 높이도 아니고, 그렇다고 padding에 속하는 것도 아니다. 그냥 content에 속하는 것이다. 이런 경우에는, scrollHeight = 내용물 높이 + padding이 아니라, scrollHeight = content (=내용물 높이 + 비어있는 높이) + padding가 된다. 비어있는 높이도 고려해야 한다는 것이다. 따라서 정리해보자면, scrollHeight의 정의를 내용물의 총 높이 + padding으로 이해하는 것이 아니라, scrollHeight = “content+padding” + “보여지지 않는 내용물”로 기억해야한다.
textarea scrollHeight
Figure 2. textarea의 높이가 내용물 높이보다 큰 경우: scrollHeight = content + padding에서 content는 내용물 높이 뿐만 아니라 “비어있는 높이”도 포함한다는 것에 유의하자.

  • 실제 문제 발생 시점
    Figure 2와 같은 상황이 발생할 수 있는 경우는, textarea에 글자를 여러 줄 입력한 뒤 글자를 지워서 행이 감소할 때 발생할 수 있다. 만약 textarea의 height를 단순히 scrollHeight + borderTop + borderBottom로 할당한다면, textarea의 높이가 내용물 높이에 맞게 조절되지 않는다. scrollHeight가 “비어있는 높이”도 포함하기 때문이다. 무슨말인지 글로만 봐서는 이해하기 어려울 수 있으니, 아래의 데모를 테스트해보자. textarea에 여러 텍스트를 여러행 입력하고, 글자들을 다시 지워보자. 아래의 비어있는 공간이 줄어들지 않음을 확인할 수 있다.

  • 해결 방법
    현재로선 “비어있는 높이”를 측정할 수 있는 방법이 따로 없기 때문에, 우리가 할 수 있는 방법은 textarea의 height를 0px로 만들어서 비어있는 높이를 없애 준다. 이렇게 함으로써 Figure 1 상태로 만들수 있고, 이 상태에서 height를 scrollHeight + borderTop + borderBottom으로 할당해주면 내용물 높이에 맞출 수 있다.

[2.2] Javascript

위의 원리를 바탕으로 자바스크립트 코드를 짜보자.

전체 구조

1
2
3
4
5
6
7
8
9
10
<script>
    window.onload = function() {
        function resize() {
            // 높이 조절을 실행
        }
 
        let textarea = document.getElementById(“message-box”);
        textarea.addEventListener(“input”, resize);
    }
</script>
cs

  • window.onload = function() { } – line 2
    페이지 모두 로딩되면, 우변에 정의된 함수가 실행된다. 코드를 window의 onload 이벤트 리스너에 넣어주는 이유는 <script></script>의 위치가 HTML 보다 앞에 위치하게 될 때를 위해서이다. 이런 경우 HTML에 앞서 script를 먼저 읽게 되고, script에서 해당 요소들을 불러올 수가 없게 된다. [참고4. 요소, 노드, 태그의 개념]
  • function resize() { } – line 3
    텍스트를 입력할 때마다 입력한 텍스트의 행수에 맞게 높이를 조절해줄 건데, 이 때 작용하는 함수이다. 즉, textarea의 input 이벤트 리스너로 지정된다.
  • let textarea = document.getElementById(“message-box”); – line 7
    id가 message-box인 요소(textarea 요소)를 선택하여 변수 textarea에 할당한다.
  • textarea.addEventListener(“input”, resize); – line 8
    textarea의 input 이벤트 리스너를 함수 resize로 설정한다. 이렇게 함으로써 textarea에 글자를 입력할 때마다, resize함수가 실행되면서 높이를 조절하게 된다.

function resize() 코딩

textarea의 크기를 조정하는 함수 resize()를 만들 것이다. 구현은 크게 3가지 순서로 나눌 수 있다. (1) textarea의 높이를 0px로 바꾼다. (2) textarea의 scrollHeight, borderTop, borderBottom을 구한다. (3) textarea의 height를 scrollHeight + borderTop + borderBottom으로 할당한다.

1
2
3
4
5
6
7
8
9
10
function resize() {
    textarea.style.height = “0px”;
 
    let scrollHeight = textarea.scrollHeight;
    let style = window.getComputedStyle(textarea);
    let borderTop = parseInt(style.borderTop);
    let borderBottom = parseInt(style.borderBottom);
 
    textarea.style.height = (scrollHeight + borderTop + borderBottom) + “px”;
}
cs

  1. textarea의 높이를 0px로 바꾼다.
    • textarea.style.height = “0px”; // line 2 – textarea의 높이를 0으로 설정한다. 변수 textarea는 함수 외부에 선언되어 있어서 그대로 가져올 수 있다.
  2. textarea의 크기 정보를 가져온다.
    • let scrollHeight = textarea.scrollHeight; // line 4 – scrollHeight는 요소.scrollHeight 형태로 가져올 수 있다. 값은 단위(px)없이 정수로만 이뤄져 있다.
    • let style = window.getComputedStyle(textarea);  // line 5 – textarea의 계산된 스타일을 가져오기 위해서 window.getComputedStyle(요소)를 사용하여 변수 style에 저장한다.
    • let borderTop = parseInt(style.borderTop); // line 6 – borderTop은 style.borderTop으로 가져올 수 있고, 값은 정수가 아니라 단위 px이 붙은 문자열의 형태이다. (예: 47px) 따라서 뒤에 있는 단위를 때어주기 위해 parseInt함수를 사용한다.
    • let borderBottom = parseInt(style.borderBottom); // line 7 – borderBottom은 style.borderBottom으로 가져올 수 있고, 단위를 때어주기 위해 parseInt함수를 사용한다.
  3. textarea의 height를 할당해준다.
    • textarea.style.height = (scrollHeight + borderTop + borderBottom) + “px”; // line 9 – border-box 형태이기 때문에 hegiht를 scrollHeight + borderTop + borderBottom으로 할당해준다. 그리고 style에는 단위 px를 붙인 형태로 입력해야함을 명심하자.

  • 주의할 점
    요소.stylewindow.getComputedStyle(요소)에서 불러온 크기 속성들은 단위 px가 붙어 있다. 변수에 이 값들을 할당할 때는 단위를 없앤 숫자만 넣어주면, 변수들을 더하거나 빼주는 등의 계산이 가능하기 때문에 편리하다. 단위(문자열 부분)를 없애고 숫자만을 뽑아내기 위해 사용한 함수가 parseInt()이다 – (위 예제 line 6, 7). 그리고 요소.style에 값을 넣어줄 때도 다시 단위 px를 꼭 붙여줘야 하는 것을 잊지 말자 – (위 예제 line 2 & 9).

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
    window.onload = function() {
        function resize() {
            textarea.style.height = “0px”;
            
            let scrollHeight = textarea.scrollHeight;
            let style = window.getComputedStyle(textarea);
            let borderTop = parseInt(style.borderTop);
            let borderBottom = parseInt(style.borderBottom);
            
            textarea.style.height = (scrollHeight + borderTop + borderBottom) + “px”;
        }
        
        let textarea = document.getElementById(“message-box”);
        textarea.addEventListener(“input”, resize);
    }
</script>
 
cs

  • 최종결과
    아래에 textarea에 글자를 여러줄 입력해보면서 textarea의 높이 변화를 확인해보자.

조금 더 생각해보기

이 포스트에서 사용한 textarea의 width를 일정한 값 400px로 고정하였다. 경우에 따라서는 고정된 너비가 아니라, 인터넷 브라우저 창의 크기에 따라 너비가 변하게 만들기 위해 width를 %로 지정하는 경우가 있다. 이런 경우에는 위의 방식만을 사용하여 textarea를 만들면, 브라우저 창의 크기가 변동할 때 textarea의 높이가 자동 조절 되지 않는다. 무슨 말인지 아래 데모를 보자. 이를 해결하기 위해서는 브라우저 창의 크기에 따라 textarea 높이를 자동조절하는 것이 필요하다. 이를 구현하기 위해서는 [참고 1] 브라우저 창크기에 따라 textarea 높이 자동 조절에 나와 있는 방법을 결합하면 된다.

참고
[1] 브라우저 창크기에 따라 textarea 높이 자동 조절
[2] 래퍼란?
[3] ID 선택자
[4] 요소, 노드, 태그의 개념
[ Javascript 목차 보기 ]

Leave a Reply