노션 클론 코딩을 진행하면서 textarea tag를 사용하지 않고
contenteditable="true"가 적용된 div tag를 사용하려고 했습니다
그러나 특정 상황에서 innerHTML로 text를 바꾸고,
focus해서 계속 입력을 진행하려는데 뜻대로 되지 않았습니다
이에 대해서 검색하며 찾아보던 중 window.getSelection()에 대해서 알게 되었습니다
해당 메소드로 무작정 코딩을 진행하다가 여러 무지성 헤딩 시행착오로 알게된 selection 객체에 대해서 정리하겠습니다
focus 버튼을 클릭해서 값을 바꾸는 상황이라고 하겠습니다
innerHTML로 text를 '초기화'로 바꾸고, 추가적으로 '완료'를 입력해서
'초기화 완료'로 바꾸고 싶습니다
하지만 focus 이후 커서가 div의 가장 앞쪽에 위치하게 됩니다
해당 메소드를 이용해서 이 문제 상황을 해결할 수 있습니다.
그 전에 Selection 객체에 대해 알고 가야 합니다
window.getSelection()
을 통해 얻을 수 있는 값이 Selection 객체
입니다
Selection 객체
는 현재 커서의 위치나 선택한 범위를 나타냅니다
왼 => 오 또는 오 => 왼 방향을 가질 수 있고, 시작과 끝을 속성으로 구분할 수 있습니다
Selection.anchorNode: 선택이 시작되는 위치
Selection.anchorOffset: anchor의 Offset.
anchorNode가 텍스트면 anchorNode 앞에 있는 문자 수 (index인 셈입니다)을 나타내고,
요소면 anchorNode의 자식 노드 수를 나타냅니다
Selection.focusNode: anchorNode와 반대로 선택이 끝나는 위치
Selection.focusOffset: focus의 Offset.
anchorOffset의 focus 버전으로 생각하시면 됩니다
Selection.isCollased: 시작 지점과 끝 지점이 같으면 true, 다르면 false 입니다
Selection.type: 범위를 지정했으면 "Range", 지정하지 않았으면 "Caret", 아예 클릭을 안했다면 "None"
=> 새로고침하고 클릭 안한 상태에서 isCollapsed는 true지만 type은 "None"입니다
window.getSelection().collapse(dom, number): 커서를 dom의 number로 이동합니다
collapse 버튼을 누르면 focus와 동일하게 값을 초기화하고
새로운 div를 만들어 추가하고, 해당 div dom으로 커서를 이동시킵니다
노션 클론코딩을 진행하면서 window.getSelection().collapse()를 이용해서 커서 이동을 해결했습니다
contenteditable="true"인 div tag는 엔터 입력 시 div 태그가 생성됩니다
=> 좀더 정확히는 해당 태그의 속성을 그대로 따라 가집니다
Shift Enter는 br tag가 추가될 뿐 div가 생성되지는 않습니다
첫 요소만 text고, 엔터 이후부터 (2번째 줄부터) div로 감싸진 text입니다
위 캡처는 contenteditable tag안에 아무내용도 없을 때
123 입력 후 엔터 후 456 입력한 모습입니다
첫 줄 123은 별도의 tag 없이 text고, 2번째 줄부터 div tag가 감싸고 있는것을 확인할 수 있습니다
=> 제 노션 클론에는 첫줄에 div를 강제로 씌웠습니다
위 gif는 Enter keydown event에 대해서
window.getSelection()과 window.getSelection().anchorNode를 출력하는 모습입니다
keydown 이벤트 시에는 anchorNode가 text지만,
다 완료된 시점에서 객체를 열어보면 anchorNode가 div tag로 바뀐것을 알 수 있습니다
이것으로 window.getSelection()이 반환하는 Selection 객체는 같은 주소값을 가지는 객체인것으로 추정할 수 있습니다
anchorNode나 anchorOffset의 값이 필요하면 하나하나 저장해서 써야한다는 뜻입니다
const selection = window.getSelection();
...
const { anchorNode, anchorOffset } = selection;
이 코드는 내가 원하는 anchorNode와 anchorOffset이 저장되지 않을 수 있으니 사용에 주의가 필요합니다
=> keyup일 때는 문제가 없는것으로 보이는데, 상황이나 로직에 따라 keydown이 필요할 수 있습니다
참고
mdn docs - Selection
mdn docs - window.getSelection()
호기심 많은 오리의 지식 저장소 - [javascript] Selection과 Range를 통해 내맘대로 커서 조작하기 - Selection편
Chairking's Dev Life - [Javascript] document.Selection 과 Range 정리