[javascript] contentEditable 캐럿 인덱스 위치 가져 오기

contentEditable요소 에서 커서 또는 캐럿 인덱스 위치를 설정하는 방법에 대한 좋은 크로스 브라우저 답변을 많이 찾았 지만 인덱스를 가져 오거나 찾는 방법에 대해서는 없습니다.

내가하고 싶은 것은이 div 내에서 캐럿의 인덱스를 아는 것입니다 keyup.

따라서 사용자가 텍스트를 입력 할 때 contentEditable요소 내에서 커서의 인덱스를 언제든지 알 수 있습니다 .

편집 : 커서 좌표가 아닌 div 내용 (텍스트) 내 에서 INDEX를 찾고 있습니다.

<div id="contentBox" contentEditable="true"></div>

$('#contentbox').keyup(function() {
    // ... ? 
});



답변

다음 코드는 다음을 가정합니다.

  • 편집 가능한 노드에는 항상 단일 텍스트 노드가 있으며 <div>다른 노드는 없습니다.
  • 편집 가능한 div에 CSS white-space속성이pre

중첩 된 요소로 콘텐츠를 작동하는보다 일반적인 접근 방식이 필요한 경우 다음 답변을 시도하십시오.

https://stackoverflow.com/a/4812022/96100

암호:

function getCaretPosition(editableDiv) {
  var caretPos = 0,
    sel, range;
  if (window.getSelection) {
    sel = window.getSelection();
    if (sel.rangeCount) {
      range = sel.getRangeAt(0);
      if (range.commonAncestorContainer.parentNode == editableDiv) {
        caretPos = range.endOffset;
      }
    }
  } else if (document.selection && document.selection.createRange) {
    range = document.selection.createRange();
    if (range.parentElement() == editableDiv) {
      var tempEl = document.createElement("span");
      editableDiv.insertBefore(tempEl, editableDiv.firstChild);
      var tempRange = range.duplicate();
      tempRange.moveToElementText(tempEl);
      tempRange.setEndPoint("EndToEnd", range);
      caretPos = tempRange.text.length;
    }
  }
  return caretPos;
}
#caretposition {
  font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div>
<div id="caretposition">0</div>
<script>
  var update = function() {
    $('#caretposition').html(getCaretPosition(this));
  };
  $('#contentbox').on("mousedown mouseup keydown keyup", update);
</script>


답변

다른 답변에서 해결되지 않은 몇 가지 주름 :

  1. 요소는 여러 수준의 자식 노드를 포함 할 수 있습니다 (예 : 자식 노드가있는 자식 노드가있는 자식 노드 …)
  2. 선택은 다른 시작 및 끝 위치로 구성 될 수 있습니다 (예 : 여러 문자가 선택됨).
  3. 캐럿 시작 / 종료를 포함하는 노드는 요소 또는 직계 자식이 아닐 수 있습니다.

다음은 요소의 textContent 값에 대한 오프셋으로 시작 및 끝 위치를 가져 오는 방법입니다.

// node_walk: walk the element tree, stop when func(node) returns false
function node_walk(node, func) {
  var result = func(node);
  for(node = node.firstChild; result !== false && node; node = node.nextSibling)
    result = node_walk(node, func);
  return result;
};

// getCaretPosition: return [start, end] as offsets to elem.textContent that
//   correspond to the selected portion of text
//   (if start == end, caret is at given position and no text is selected)
function getCaretPosition(elem) {
  var sel = window.getSelection();
  var cum_length = [0, 0];

  if(sel.anchorNode == elem)
    cum_length = [sel.anchorOffset, sel.extentOffset];
  else {
    var nodes_to_find = [sel.anchorNode, sel.extentNode];
    if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
      return undefined;
    else {
      var found = [0,0];
      var i;
      node_walk(elem, function(node) {
        for(i = 0; i < 2; i++) {
          if(node == nodes_to_find[i]) {
            found[i] = true;
            if(found[i == 0 ? 1 : 0])
              return false; // all done
          }
        }

        if(node.textContent && !node.firstChild) {
          for(i = 0; i < 2; i++) {
            if(!found[i])
              cum_length[i] += node.textContent.length;
          }
        }
      });
      cum_length[0] += sel.anchorOffset;
      cum_length[1] += sel.extentOffset;
    }
  }
  if(cum_length[0] <= cum_length[1])
    return cum_length;
  return [cum_length[1], cum_length[0]];
}


답변

$("#editable").on('keydown keyup mousedown mouseup',function(e){

       if($(window.getSelection().anchorNode).is($(this))){
    	  $('#position').html('0')
       }else{
         $('#position').html(window.getSelection().anchorOffset);
       }
 });
body{
  padding:40px;
}
#editable{
  height:50px;
  width:400px;
  border:1px solid #000;
}
#editable p{
  margin:0;
  padding:0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div contenteditable="true" id="editable">move the cursor to see position</div>
<div>
position : <span id="position"></span>
</div>


답변

이 시도:

Caret.js 텍스트 필드에서 캐럿 위치 및 오프셋 가져 오기

https://github.com/ichord/Caret.js

데모 :
http://ichord.github.com/Caret.js


답변

파티에 늦었지만 다른 사람이 고군분투하는 경우를 대비하십시오. 지난 이틀 동안 내가 찾은 Google 검색 중 어느 것도 작동하는 것을 찾지 못했지만 중첩 태그 수에 관계없이 항상 작동하는 간결하고 우아한 솔루션을 찾았습니다.

function cursor_position() {
    var sel = document.getSelection();
    sel.modify("extend", "backward", "paragraphboundary");
    var pos = sel.toString().length;
    if(sel.anchorNode != undefined) sel.collapseToEnd();

    return pos;
}

// Demo:
var elm = document.querySelector('[contenteditable]');
elm.addEventListener('click', printCaretPosition)
elm.addEventListener('keydown', printCaretPosition)

function printCaretPosition(){
  console.log( cursor_position(), 'length:', this.textContent.trim().length )
}
<div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

단락의 시작 부분까지 다시 선택하고 문자열의 길이를 계산하여 현재 위치를 가져온 다음 선택을 취소하여 커서를 현재 위치로 되돌립니다. 당신은 전체 문서 (하나 이상의 단락)이 작업을 수행 할 경우, 변경 paragraphboundarydocumentboundary사건 또는 어떤 단위. 자세한 내용 은 API를 확인하십시오 . 건배! 🙂


답변

function getCaretPosition() {
    var x = 0;
    var y = 0;
    var sel = window.getSelection();
    if(sel.rangeCount) {
        var range = sel.getRangeAt(0).cloneRange();
        if(range.getClientRects()) {
        range.collapse(true);
        var rect = range.getClientRects()[0];
        if(rect) {
            y = rect.top;
            x = rect.left;
        }
        }
    }
    return {
        x: x,
        y: y
    };
}


답변

window.getSelection-대-document.selection

이것은 나를 위해 작동합니다.

function getCaretCharOffset(element) {
  var caretOffset = 0;

  if (window.getSelection) {
    var range = window.getSelection().getRangeAt(0);
    var preCaretRange = range.cloneRange();
    preCaretRange.selectNodeContents(element);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    caretOffset = preCaretRange.toString().length;
  }

  else if (document.selection && document.selection.type != "Control") {
    var textRange = document.selection.createRange();
    var preCaretTextRange = document.body.createTextRange();
    preCaretTextRange.moveToElementText(element);
    preCaretTextRange.setEndPoint("EndToEnd", textRange);
    caretOffset = preCaretTextRange.text.length;
  }

  return caretOffset;
}


// Demo:
var elm = document.querySelector('[contenteditable]');
elm.addEventListener('click', printCaretPosition)
elm.addEventListener('keydown', printCaretPosition)

function printCaretPosition(){
  console.log( getCaretCharOffset(elm), 'length:', this.textContent.trim().length )
}
<div contenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>

호출 라인은 이벤트 유형에 따라 다르며 키 이벤트의 경우 다음을 사용하십시오.

getCaretCharOffsetInDiv(e.target) + ($(window.getSelection().getRangeAt(0).startContainer.parentNode).index());

마우스 이벤트의 경우 다음을 사용하십시오.

getCaretCharOffsetInDiv(e.target.parentElement) + ($(e.target).index())

이 두 가지 경우에는 대상 인덱스를 추가하여 구분선을 처리합니다.