<textarea>
는 <input type="text">
와 달리 여러 줄을 입력할 수 있지만, 줄바꿈에 따라 입력 칸 높이가 자동으로 조절되지는 않는다. 기본적으로는 내용을 두 줄 이상 입력하면 한 줄 높이의 칸에 스크롤이 생긴다. 하지만 내가 원하는 것은 두 줄 이상 입력하면 두 줄을 입력하면 스크롤 없이 칸이 두 줄 높이로 늘어나는 것이었다.
textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";
이렇게 높이를 scrollHeight
로 지정해주면 줄 수에 따라 칸이 늘어나지만, 백스페이스를 눌러서 내용을 지웠을 때 줄이 줄어들어도 칸은 그에 맞춰 줄어들지 않는다는 문제점이 있었다. 한 번 늘어난 scrollHeight
는 글자 수가 줄어들어도 자동으로 줄어들지 않나 보다. 그래서 내용을 지웠을 때 칸 높이가 줄어들게 하는 로직도 필요했다.
또, styled-components
에서 state를 높이로 지정할 수 있기 때문에 굳이 ref를 쓰지 않아도 되었다.
const [textareaHeight, setTextareaHeight] = useState({
row: 1,
lineBreak: {},
});
row
는 텍스트가 몇 줄인지 나타내는 값이고,
lineBreak
는 텍스트에서 줄바꿈이 일어나는 지점을 담는 객체이다.
줄바꿈이 일어나면 row
에 줄 수를 하나 추가하고, lineBreak
에 줄바꿈 일어난 지점 값을 true로 기록한다.
텍스트를 지워서 줄바꿈이 취소되면 row
에서 줄 수를 하나 빼고, lineBreak
에서 줄바꿈 일어난 지점을 false로 바꾼다.
onInput 이벤트 시 실행한다.
const resizeTextarea = e => {
const { scrollHeight, clientHeight, value } = e.target;
// 줄바꿈이 일어날 때
if (scrollHeight > clientHeight) {
setTextareaHeight(prev => ({
row: prev.row + 1,
lineBreak: { ...prev.lineBreak, [value.length - 1]: true },
}));
}
// 텍스트 지워서 줄바꿈 지점에 도달했을 때
if (textareaHeight.lineBreak[value.length]) {
setTextareaHeight(prev => ({
row: prev.row - 1,
lineBreak: { ...prev.lineBreak, [value.length]: false },
}));
}
};
엔터 쳤을 때 row
에 한 줄 더해 주고, 엔터 친 지점을 lineBreak
에 추가한다.
onKeyDown 이벤트 시 실행한다.
const onKeyEnter = e => {
if (e.code === 'Enter') {
setTextareaHeight(prev => ({
row: prev.row + 1,
lineBreak: { ...prev.lineBreak, [e.target.value.length]: true },
}));
}
};
return (
<InputText
autoComplete="off"
onInput={onInput}
onKeyDown={onKeyEnter}
row={textareaHeight.row}
/>
);
const InputText = styled.textarea`
all: unset;
display: block;
width: 100%;
height: ${({ row, theme }) => +theme.listSize * row + 4}px;
overflow-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
resize: none;
`;
overflow-wrap
: break-word
보통 안 바꿔지는 단어들을 한 줄에서 대신 줄을 바꿀 만한 지점이 없을 시 임의의 지점에서 줄바꿈.
word-break
: break-all
오버플로우될 때 문자 단위로 줄바꿈. (keep-all
은 한글 단어 단위로 줄바꿈)
white-space
: pre-wrap
연속 공백 유지. 한 줄이 너무 길어서 넘칠 경우 자동으로 줄바꿈.
잘 작동하지만, 긴 텍스트를 복사/붙여넣기하거나 여러 줄을 한꺼번에 선택해서 지웠을 때는 자동 높이 조절이 안 된다는 한계가 있다.
react-textarea-autosize 라이브러리를 사용하면 간편하게 해결할 수 있다.