Programming

contenteditable element (div)에서 캐럿 (커서) 위치를 설정하는 방법은 무엇입니까?

procodes 2020. 5. 29. 23:15
반응형

contenteditable element (div)에서 캐럿 (커서) 위치를 설정하는 방법은 무엇입니까?


이 간단한 HTML을 예로 들어 보겠습니다.

<div id="editable" contenteditable="true">
  text text text<br>
  text text text<br>
  text text text<br>
</div>
<button id="button">focus</button>

간단한 것을 원합니다. 버튼을 클릭하면 캐럿 (커서)을 편집 가능한 div의 특정 위치에 배치하고 싶습니다. 웹 검색에서 버튼 클릭 에이 JS가 첨부되었지만 작동하지 않습니다 (FF, Chrome).

var range = document.createRange();
var myDiv = document.getElementById("editable");
range.setStart(myDiv, 5);
range.setEnd(myDiv, 5);

이와 같이 수동 캐럿 위치를 설정할 수 있습니까?


대부분의 브라우저에는 RangeSelection객체 가 필요 합니다. 각 선택 경계를 노드로 지정하고 해당 노드 내에서 오프셋을 지정합니다. 예를 들어, 캐럿을 두 번째 텍스트 행의 다섯 번째 문자로 설정하려면 다음을 수행하십시오.

var el = document.getElementById("editable");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[2], 5);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

IE <9는 완전히 다르게 작동합니다. 이러한 브라우저를 지원해야하는 경우 다른 코드가 필요합니다.

jsFiddle 예 : http://jsfiddle.net/timdown/vXnCM/


내용 편집 가능한 커서 위치에서 찾을 수있는 대부분의 답변은 일반 바닐라 텍스트가있는 입력에만 적합하다는 점에서 상당히 단순합니다. 컨테이너 내에서 html 요소를 사용하면 입력 한 텍스트가 노드로 분할되고 트리 구조에 자유롭게 배포됩니다.

커서 위치를 설정하려면 제공된 노드 내의 모든 자식 텍스트 노드를 순환하고 초기 노드의 시작에서 chars.count 문자 까지의 범위를 설정하는이 기능이 있습니다 .

function createRange(node, chars, range) {
    if (!range) {
        range = document.createRange()
        range.selectNode(node);
        range.setStart(node, 0);
    }

    if (chars.count === 0) {
        range.setEnd(node, chars.count);
    } else if (node && chars.count >0) {
        if (node.nodeType === Node.TEXT_NODE) {
            if (node.textContent.length < chars.count) {
                chars.count -= node.textContent.length;
            } else {
                range.setEnd(node, chars.count);
                chars.count = 0;
            }
        } else {
           for (var lp = 0; lp < node.childNodes.length; lp++) {
                range = createRange(node.childNodes[lp], chars, range);

                if (chars.count === 0) {
                    break;
                }
            }
        }
    } 

    return range;
};

그런 다음이 함수를 사용하여 루틴을 호출하십시오.

function setCurrentCursorPosition(chars) {
    if (chars >= 0) {
        var selection = window.getSelection();

        range = createRange(document.getElementById("test").parentNode, { count: chars });

        if (range) {
            range.collapse(false);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
};

range.collapse (false)는 커서를 범위의 끝으로 설정합니다. 최신 버전의 Chrome, IE, Mozilla 및 Opera에서 테스트했으며 모두 정상적으로 작동합니다.

추신. 누군가 관심이 있다면이 코드를 사용하여 현재 커서 위치를 얻습니다.

function isChildOf(node, parentId) {
    while (node !== null) {
        if (node.id === parentId) {
            return true;
        }
        node = node.parentNode;
    }

    return false;
};

function getCurrentCursorPosition(parentId) {
    var selection = window.getSelection(),
        charCount = -1,
        node;

    if (selection.focusNode) {
        if (isChildOf(selection.focusNode, parentId)) {
            node = selection.focusNode; 
            charCount = selection.focusOffset;

            while (node) {
                if (node.id === parentId) {
                    break;
                }

                if (node.previousSibling) {
                    node = node.previousSibling;
                    charCount += node.textContent.length;
                } else {
                     node = node.parentNode;
                     if (node === null) {
                         break
                     }
                }
           }
      }
   }

    return charCount;
};

이 코드는 set 함수의 반대 기능을 수행합니다. 현재 window.getSelection (). focusNode 및 focusOffset을 가져오고 containerId가 id 인 상위 노드에 도달 할 때까지 발생한 모든 텍스트 문자를 거꾸로 계산합니다. isChildOf 함수는 실행하기 전에 제공된 노드가 실제로 제공된 parentId 의 자식인지 확인합니다 .

이 코드는 변경없이 바로 작동해야하지만 난 그냥 몇 밖으로 해킹 그래서 개발 한 플러그인 JQuery와에서 촬영 한 이의를 - 아무것도 작동하지 않는 경우 알려주세요!


(p) (스팬) 등과 같은 고급 요소가있을 때 캐럿을 올바른 위치에 놓기가 매우 어렵습니다. 목표는 (객체 텍스트)를 얻는 것입니다.

<div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p>
    <p>dd</p>
    <p>text text text</p>
</div>
<p id='we'></p>
<button onclick="set_mouse()">focus</button>
<script>

    function set_mouse() {
        var as = document.getElementById("editable");
        el = as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text) because it work only in object text
        var range = document.createRange();
        var sel = window.getSelection();
        range.setStart(el, 1);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);

        document.getElementById("we").innerHTML = el;// see out put of we id
    }
</script>

jQuery를 사용하지 않으려면 다음 방법을 시도하십시오.

public setCaretPosition() {
    const editableDiv = document.getElementById('contenteditablediv');
    const lastLine = this.input.nativeElement.innerHTML.replace(/.*?(<br>)/g, '');
    const selection = window.getSelection();
    selection.collapse(editableDiv.childNodes[editableDiv.childNodes.length - 1], lastLine.length);
}

editableDiv you editable element, don't forget to set an id for it. Then you need to get your innerHTML from the element and cut all brake lines. And just set collapse with next arguments.


  const el = document.getElementById("editable");
  el.focus()
  let char = 1, sel; // character at which to place caret

  if (document.selection) {
    sel = document.selection.createRange();
    sel.moveStart('character', char);
    sel.select();
  }
  else {
    sel = window.getSelection();
    sel.collapse(el.lastChild, char);
  }

I'm writting a syntax highlighter (and basic code editor), and I needed to know how to auto-type a single quote char and move the caret back (like a lot of code editors nowadays).

Heres a snippet of my solution, thanks to much help from this thread, the MDN docs, and a lot of moz console watching..

//onKeyPress event

if (evt.key === "\"") {
    let sel = window.getSelection();
    let offset = sel.focusOffset;
    let focus = sel.focusNode;

    focus.textContent += "\""; //setting div's innerText directly creates new
    //nodes, which invalidate our selections, so we modify the focusNode directly

    let range = document.createRange();
    range.selectNode(focus);
    range.setStart(focus, offset);

    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
}

//end onKeyPress event

This is in a contenteditable div element

I leave this here as a thanks, realizing there is already an accepted answer.


I think it's not simple to set caret to some position in contenteditable element. I wrote my own code for this. It bypasses the node tree calcing how many characters left and sets caret in needed element. I didn't test this code much.

//Set offset in current contenteditable field (for start by default or for with forEnd=true)
function setCurSelectionOffset(offset, forEnd = false) {
    const sel = window.getSelection();
    if (sel.rangeCount !== 1 || !document.activeElement) return;

    const firstRange = sel.getRangeAt(0);

    if (offset > 0) {
        bypassChildNodes(document.activeElement, offset);
    }else{
        if (forEnd)
            firstRange.setEnd(document.activeElement, 0);
        else
            firstRange.setStart(document.activeElement, 0);
    }



    //Bypass in depth
    function bypassChildNodes(el, leftOffset) {
        const childNodes = el.childNodes;

        for (let i = 0; i < childNodes.length && leftOffset; i++) {
            const childNode = childNodes[i];

            if (childNode.nodeType === 3) {
                const curLen = childNode.textContent.length;

                if (curLen >= leftOffset) {
                    if (forEnd)
                        firstRange.setEnd(childNode, leftOffset);
                    else
                        firstRange.setStart(childNode, leftOffset);
                    return 0;
                }else{
                    leftOffset -= curLen;
                }
            }else
            if (childNode.nodeType === 1) {
                leftOffset = bypassChildNodes(childNode, leftOffset);
            }
        }

        return leftOffset;
    }
}

I also wrote code to get current caret position (didn't test):

//Get offset in current contenteditable field (start offset by default or end offset with calcEnd=true)
function getCurSelectionOffset(calcEnd = false) {
    const sel = window.getSelection();
    if (sel.rangeCount !== 1 || !document.activeElement) return 0;

    const firstRange     = sel.getRangeAt(0),
          startContainer = calcEnd ? firstRange.endContainer : firstRange.startContainer,
          startOffset    = calcEnd ? firstRange.endOffset    : firstRange.startOffset;
    let needStop = false;

    return bypassChildNodes(document.activeElement);



    //Bypass in depth
    function bypassChildNodes(el) {
        const childNodes = el.childNodes;
        let ans = 0;

        if (el === startContainer) {
            if (startContainer.nodeType === 3) {
                ans = startOffset;
            }else
            if (startContainer.nodeType === 1) {
                for (let i = 0; i < startOffset; i++) {
                    const childNode = childNodes[i];

                    ans += childNode.nodeType === 3 ? childNode.textContent.length :
                           childNode.nodeType === 1 ? childNode.innerText.length :
                           0;
                }
            }

            needStop = true;
        }else{
            for (let i = 0; i < childNodes.length && !needStop; i++) {
                const childNode = childNodes[i];
                ans += bypassChildNodes(childNode);
            }
        }

        return ans;
    }
}

You also need to be aware of range.startOffset and range.endOffset contain character offset for text nodes (nodeType === 3) and child node offset for element nodes (nodeType === 1). range.startContainer and range.endContainer may refer to any element node of any level in the tree (of course they also can refer to text nodes).

참고URL : https://stackoverflow.com/questions/6249095/how-to-set-caretcursor-position-in-contenteditable-element-div

반응형