반응형

들어가며

Selection? Range?

  • input이나 contenteditable 영역의 커서(caret)를 핸들링하는 javascript 객체를 말한다.

예제로 이해하기

기본 사용

<pre id="editor" contenteditable="true"></pre>
<script>
    const KEY_CODE = { ESC: 27 };
    const $editor = document.querySelector('#editor');

    $editor.addEventListener('keydown', (e) => {
        if (e.keyCode === KEY_CODE.ESC) {
            e.preventDefault();

            const selection = document.getSelection();
            const range = selection.getRangeAt(0);

            console.log({ selection, range });
        }
    });
</script>

커서가 위치한 노드 range 선택하기

const selection = document.getSelection();
const range = selection.getRangeAt(0);

range.selectNode(range.startContainer);
// range.setStart(range.startContainer, 0);
// range.setEnd(range.startContainer, range.startContainer.nodeValue.length);

range 선택 제거

const selection = document.getSelection();

selection.removeAllRanges();

range에 포함된 노드 제거

const selection = document.getSelection();
const range = selection.getRangeAt(0);

range.deleteContents();

현재 커서 위치에 노드 추가

const selection = document.getSelection();
const range = selection.getRangeAt(0);
const $span = document.createElement('span');
const $text = document.createTextNode("I'm text");

$span.innerText = "I'm span";
range.insertNode($span);
range.insertNode($text);

응용하기

tab, back tab 구현

<pre id="editor" contenteditable="true"></pre>
<script>
    const KEY_CODE = { TAB: 9 };
    const NODE_TYPE = { TEXT: 3 };
    const INDENT = '    ';
    const $editor = document.querySelector('#editor');

    $editor.addEventListener('keydown', (e) => {
        if (e.keyCode !== KEY_CODE.TAB) {
            return;
        }

        e.preventDefault();

        const selection = document.getSelection();
        const range = selection.getRangeAt(0);

        let start = range.startContainer;

        if (!start.parentNode.getAttribute('contenteditable')) {
            start = start.parentNode;
        }

        while (true) {
            const node = start.nodeType === NODE_TYPE.TEXT ? start : start.firstChild;

            if (e.shiftKey) {
                if (node.nodeValue.indexOf(INDENT) >= 0) {
                    node.nodeValue = node.nodeValue.substring(INDENT.length);
                }
            } else {
                node.nodeValue = INDENT + node.nodeValue;
            }

            if (!start.nextSibling || start === range.endContainer.parentNode) {
                break;
            }

            start = start.nextSibling;
        }

        range.setStart(range.startContainer, 0);
        range.setEnd(range.endContainer, range.endContainer.nodeValue.length);
    });
</script>

참고

반응형

'Development > JS' 카테고리의 다른 글

[BackboneJS] 예제  (0) 2021.06.26
[Javascript] remark, rehype  (1) 2021.01.07

+ Recent posts