최근 개발을 진행하다 내용에 따라 높이가 가변적으로 변하거나 화면 높이보다 긴 <textarea>
를 사용할 때 sticky 헤더나 푸터가 있으면 커서 영역이 헤더나 푸터에 가려지는 현상을 겪었다.
이를 해결하기 위해 TypeScript로 작성한 코드를 소개할까 한다.
코드에 앞서 전체적인 흐름을 보면 다음과 같다.
<textarea>
요소의 keydown 이벤트를 감지한다.selectionStart
를 이용해 현재 커서의 위치를 체크한다.<textarea>
의 모든 스타일을 상속 받은 임시 <pre>
요소를 만들어, 커서 이전의 모든 문자열을 넣어준다. <pre>
요소를 만드는 이유는 <textarea>
에 작성 된 내용의 height 를 알기 위함이다.이제 코드를 알아보자.
재사용성을 위해 클래스를 이용했다. 이름은 TextAreaCursorLens 라고 지어봤다. 이 클래스는 <textarea>
요소를 파라미터로 받는다.
class TextAreaCursorLens {
private _textarea: HTMLTextAreaElement;
constructor(textarea: HTMLTextAreaElement) {
this.focusToCursor = this.focusToCursor.bind(this);
this._textarea = textarea;
this._textarea.addEventListener('keydown', this.focusToCursor);
}
focusToCursor(): void {
const cursorPosition = this._textarea.selectionStart;
const textBeforeCursor = this._textarea.value.substring(0, cursorPosition);
const pre = document.createElement('pre');
const textareaDomRect = this._textarea.getBoundingClientRect();
// textarea의 모든 CSS 복사
pre.style.cssText = window.getComputedStyle(this._textarea, null).cssText;
// pre의 역할은 textarea에 작성된 글의 길이, 즉, 커서의 위치를 계산하기 위함이므로
// 그에 맞게 적절한 스타일 추가
pre.style.height = 'auto';
pre.style.width = textareaDomRect .width+ 'px';
pre.style.position = 'fixed';
pre.style.visibility = 'hidden';
pre.style.textWrap = 'wrap';
// 커서 이전의 모든 문자열 추가
pre.textContent = textBeforeCursor;
document.body.appendChild(pre);
const cursorBottom = pre.offsetHeight;
const textareaTop = textareaDomRect .top;
const windowScrollY = window.scrollY;
document.body.removeChild(pre);
const targetScrollTop =
windowScrollY + textareaTop + cursorBottom // 현재 스크롤 위치 기반 커서의 위치
- window.innerHeight / 2; // 화면 높이의 중앙
window.scrollTo({
top: targetScrollTop,
behavior: 'instant', // 'smooth' 사용 시 글 입력 시 마다 스크롤 위치가 재조정 되므로 'instant' 사용
});
}
destroy(): void {
this._textarea.removeEventListener('keydown', this.focusToCursor);
}
}
스크롤 위치를 조정하는 모든 과정은 keydown 이벤트에서 발생하기 때문에 구조는 간단하다.
#textarea #커서 #중앙 #위치 #scroll #window #스크롤 #조정 #타입스크립트 #커서 #위치 #계산 #Range #객체 #DOM #조작 #사용자 #경험 #개선 #부드러운 #스크롤 #이벤트 #처리 #웹 #개발 #기술 #커서 #포커스 #관리 #입력 #요소 #편집 #기능