[javascript] 부모 컨테이너를 기준으로 범위의 시작 및 끝 오프셋 가져 오기

이 HTML 요소가 있다고 가정합니다.

<div id="parent">
 Hello everyone! <a>This is my home page</a>
 <p>Bye!</p>
</div>

그리고 사용자는 마우스로 “홈”을 선택합니다.

나는 #parent그의 선택 시작에 얼마나 많은 문자가 있는지 (그리고 #parent그의 선택 끝 에서 얼마나 많은 문자가 끝나는 지) 결정할 수 있기를 원합니다 . HTML 태그를 선택한 경우에도 작동합니다. (그리고 모든 브라우저에서 작동하려면 필요합니다)

range.startOffset 유망 해 보이지만 범위의 직접 컨테이너에만 상대적인 오프셋이며 컨테이너가 텍스트 노드 인 경우에만 문자 오프셋입니다.



답변

최신 정보

주석에서 지적했듯이 내 원래 답변 (아래)은 선택의 끝 또는 캐럿 위치 만 반환합니다. 시작 및 끝 오프셋을 반환하도록 코드를 조정하는 것은 매우 쉽습니다. 다음은 그렇게하는 예입니다.

다음은 지정된 요소 내에서 캐럿의 문자 오프셋을 가져 오는 함수입니다. 그러나 이것은 거의 확실하게 줄 바꿈과 불일치가 있고 CSS를 통해 숨겨진 텍스트를 처리하려고 시도하지 않는 순진한 구현입니다 (IE는 이러한 텍스트를 올바르게 무시하지만 다른 브라우저에서는 그렇지 않을 것입니다). 이 모든 것을 제대로 처리하는 것은 까다로울 것입니다. 이제 Rangy 라이브러리에 대해 시도했습니다 .

라이브 예 : http://jsfiddle.net/TjXEG/900/

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    var doc = element.ownerDocument || element.document;
    var win = doc.defaultView || doc.parentWindow;
    var sel;
    if (typeof win.getSelection != "undefined") {
        sel = win.getSelection();
        if (sel.rangeCount > 0) {
            var range = win.getSelection().getRangeAt(0);
            var preCaretRange = range.cloneRange();
            preCaretRange.selectNodeContents(element);
            preCaretRange.setEnd(range.endContainer, range.endOffset);
            caretOffset = preCaretRange.toString().length;
        }
    } else if ( (sel = doc.selection) && sel.type != "Control") {
        var textRange = sel.createRange();
        var preCaretTextRange = doc.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
    return caretOffset;
}


답변

나는 이것이 1 년 된 것을 알고 있지만,이 게시물은 캐럿 위치를 찾는 것에 대한 많은 질문에 대한 상위 검색 결과이며 이것이 유용하다는 것을 알았습니다.

콘텐츠 편집 가능한 div에서 요소를 한 위치에서 다른 위치로 끌어 놓은 후 위의 Tim의 훌륭한 스크립트를 사용하여 새 커서 위치를 찾으려고했습니다. FF 및 IE에서 완벽하게 작동했지만 Chrome에서는 드래그 동작이 드래그 시작과 끝 사이의 모든 콘텐츠를 강조 표시하여 반환 된 결과 caretOffset가 너무 크거나 작습니다 (선택 영역의 길이 기준).

첫 번째 if 문에 몇 줄을 추가하여 텍스트가 선택되었는지 확인하고 그에 따라 결과를 조정했습니다. 새로운 진술은 아래와 같습니다. OP가하려는 것이 아니기 때문에 여기에 추가하는 것이 부적절하다면 저를 용서하십시오.하지만 제가 말했듯이 Caret 위치와 관련된 정보에 대한 여러 검색이 저를이 게시물로 이끌었으므로 다른 사람을 도울 것입니다. .

줄이 추가 된 Tim의 첫 번째 if 문 (*) :

if (typeof window.getSelection != "undefined") {
  var range = window.getSelection().getRangeAt(0);
  var selected = range.toString().length; // *
  var preCaretRange = range.cloneRange();
  preCaretRange.selectNodeContents(element);
  preCaretRange.setEnd(range.endContainer, range.endOffset);

  if(selected){ // *
    caretOffset = preCaretRange.toString().length - selected; // *
  } else { // *
    caretOffset = preCaretRange.toString().length;
  } // *
}


답변

며칠 동안 실험 한 후 유망 해 보이는 접근 방식을 발견했습니다. 태그를 올바르게 selectNodeContents()처리하지 않기 때문에 <br>.NET node내부 의 각 텍스트 길이를 결정하는 사용자 지정 알고리즘을 작성 했습니다 contenteditable. 예를 들어 선택 시작을 계산하기 위해 모든 이전 노드의 텍스트 길이를 합산합니다. 이렇게하면 (다중) 줄 바꿈을 처리 할 수 있습니다.


답변

이 솔루션은 부모 컨테이너로 돌아가는 이전 형제의 텍스트 콘텐츠 길이를 계산하여 작동합니다. 깊이에 관계없이 중첩 된 태그를 처리하지만 모든 가장자리 케이스를 다루지는 않지만 비슷한 요구 사항이있는 경우 시작하기에 좋고 간단한 곳입니다.

  calculateTotalOffset(node, offset) {
    let total = offset
    let curNode = node

    while (curNode.id != 'parent') {
      if(curNode.previousSibling) {
        total += curNode.previousSibling.textContent.length

        curNode = curNode.previousSibling
      } else {
        curNode = curNode.parentElement
      }
    }

   return total
 }

 // after selection

let start = calculateTotalOffset(range.startContainer, range.startOffset)
let end = calculateTotalOffset(range.endContainer, range.endOffset)


답변