ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ContentEditable에서 커서(Caret) 활용하기(2) - 자바스크립트
    백수의 개발/웹 2020. 3. 3. 21:16

    ContentEditable에서 커서(Caret) 활용하기(2) - 자바스크립트

    [ ContentEditable에서 커서(Caret) 활용하기(1) - 자바스크립트 ]에서 커서의 위치를 확인하는 방법을 알아봤다면, 이번에는 코드를 통해 커서의 위치를 설정하는 방법을 알아보자.

     

    Caret 위치 지정하기

    Caret의 위치를 지정하기 위해서 우리는 Selection과 Range만 알면 된다.

    아래와 같은 코드가 있다고 생각해보자.

    <div contenteditable='true'>
    	<span>가나다</span><span>라마바사</span><span>아자차</span>
    <div>

    여기서 우리는 Range를 먼저 설정해주고, 해당 Range를 Selection에 전달해주면 Caret의 위치가 지정될 것이다.

    "다" 부터 "아"까지 드래그를 한 형태로 Caret을 옮겨본다고 생각해보자.

     

    간단하게 설명하자면 다음과 같다.

    "다"의 위치를 Range의 Start로 설정하고, "아"의 위치를 Range의 End로 설정한다. 그 후 설정한 Range를 Selection에 전달해주면 간단하게 끝나는 것이다.

     

    그런데 중요한건 Start와 End에 어떤 데이터를 전달해줘야 하는 걸까? 조금 더 자세히 알아보자.

    Start와 End에는 기본적으로 원하는 텍스트가 있는 Node와 그 텍스트에서의 Offset을 통해 설정해주면 된다.

    코드와 그림을 통해 알아보자.

     

    // Range 설정
    const newRange = document.createRange();
    newRange.setStart(startContainer, startOffset);
    newRange.setEnd(endContainer, endOffset);
    
    // Selection에 전달
    const selection = document.getSelection();
    selection.removeAllRanges();
    selection.addRange(newRange);

    이처럼 간단하게 Caret을 설정해줄 수 있다.

    * 주의

    Caret을 설정하는건 간단하지만, 주의해야할 것이 있다. 바로 startContainer와 endContainer를 설정하는 것이다.

    위에서 보여준 것 처럼 Range에 넣을 Node를 span과 같이 태그를 넣는 것이 아닌, 해당 태그 안에 있는 text node를 넣어야한다는 것이다.

    만약, 위에서 span을 Container로 넣는다면 offset을 설정하는데 문제가 생긴다. 그림을 한번 보자.

    우리는 "라마바사"에서 offset을 설정하기 위해 가져왔다고 생각하고, offset이 각 글자마다 있어 0 ~ 4까지 있을 것이라고 생각하겠지만 전혀 그렇지 않다. span자체의 offset으로 span의 시작과 끝 두개의 offset밖에 존재하지 않는다.

     

    그렇기 때문에 항상 태그를 container로 가져가는 것이 아니라 text node를 container로 가져가야한다는 것을 잊지말자.

     

    마지막으로 위의 "가나다라마바사아자차"에 대해서 text node를 고려하면, 어떻게 Caret을 설정해야하는지 알아보자.

    <div contenteditable='true'>
    	<span>가나다</span><span>라마바사</span><span>아자차</span>
    <div>
    const spanList = document.querySelectorAll("span");
    
    // 각 span으로 부터 text node 가져오기
    const startContainer = spanList[0].childNodes[0];
    const startOffset = 2;
    const endContainer = spanList[2].childNodes[0];
    const endOffset = 1;
    
    // Range 설정
    const newRange = document.createRange();
    newRange.setStart(startContainer, startOffset);
    newRange.setEnd(endContainer, endOffset);
    
    // Selection에 전달
    const selection = document.getSelection();
    selection.removeAllRanges();
    selection.addRange(newRange);

    * childNodes로 부터 text node를 가지고 올 수 있다. textContent라는 속성도 있지만, 이는 node를 가지고 오는 것이 아니라 string값을 가져오기 때문에 올바르지 못한 방법이다.

    댓글

Designed by Tistory.