2011-12-10 1 views
12

와 iframe에 caret의 현재 위치로부터의 오프셋 (offset)하는 방법을 나는 사용자가 (자동 완성을 위해) 특정 키 조합을 입력하는 경우에, contentEditable와 함께 iframe에 떠있는 div 요소를 배치하고 싶습니다 . 픽셀이의 contentEditable

나는 캐럿 위치를 얻는 방법을 알고 document.getElementById('elm1_ifr').contentWindow.getSelection().anchorOffset

내가 사업부의 left 속성을 계산하기 위해 이것을 사용할 수 있습니다,하지만 난 top을 얻는 방법을 알아낼 수 없습니다.

내가 사용하던 생각 또 다른 가능성 : document.getElementById('elm1_ifr').contentWindow.getSelection().anchorNode.parentNode

을 그리고 오프셋을 얻기 위해 jQuery를 사용하지만 부모가 긴 텍스트 행이있는 경우, 내가 첫 번째 라인의 상단 위치를 추출 할 수있을 것입니다 .

아무도 도와 줄 수 있습니까?

답변

12

유일하게 신뢰할 수있는 방법은 캐럿에 임시 요소를 삽입하고 (너비가 제로인지 확인) 위치를 가져 와서 다시 제거하는 것입니다. DOM을 노드를 삽입하기 전에 그대로 유지하려면 텍스트 노드의 두 끝 (다시 말해서 캐럿이 포함 된 텍스트 노드 인 경우)을 다시 붙여야합니다. 그러나이 작업 (또는 편집 가능한 내용에 대한 다른 수동 DOM 조작)을 수행하면 브라우저의 내부 실행 취소 스택이 손상됩니다.

이 이유는 the spec for the getBoundingClientRect() method of Range을주의 깊게 읽으면 getBoundingClientRect()이 축소 된 범위의 Rect를 반환 할 의무가 없다는 것을 나타냅니다. 개념적으로, 문서의 모든 위치가 잘 정의 된 경계 사각형을 가지고있는 것은 아닙니다. 그러나 캐럿에는 화면 상에 물리적 인 위치가 있지만, 내 의견으로는 Selection API에서 제공해야하지만, 현재 브라우저에는 아무것도 제공하지 않습니다.

+2

당신이 후, 현재 캐럿 위치를 0으로 범위를 설정 왼쪽 + 폭 및 상단 + 높이 오프셋을 얻기 위해'getBoundingClientRect()'를 사용 할 수 없습니다, 다음 캐럿을 복원 환경? – Mottie

+3

낡은 스레드 예,하지만 [execCommand ('insertHTML')']을 사용하여 요소를 삽입하고이를 제거하면 실행 취소/다시 실행 스택이 망가질 수 있습니다. 실행 취소/다시 실행이 중요하지 않은 경우 중요하지 않습니다. – techfoobar

+0

@techfoobar : 좋은 지적입니다. 대답에 대한 쪽지를 추가했습니다. –

7

오늘이 문제가 발생했습니다. 몇 가지 테스트를 거친 후, 나는 temorary 요소를 사용하지 않고이 작업을했습니다.

IE에서는 TextRange 개체의 offsetLeft 및 offsetTop 속성을 사용하여 쉽게 해결할 수 있습니다. 웹킷에는 약간의 노력이 필요합니다.

여기 테스트 결과를 볼 수 있습니다. 그것은 이전의에 http://jsfiddle.net/gliheng/vbucs/12/

var getCaretPixelPos = function ($node, offsetx, offsety){ 
    offsetx = offsetx || 0; 
    offsety = offsety || 0; 

    var nodeLeft = 0, 
     nodeTop = 0; 
    if ($node){ 
     nodeLeft = $node.offsetLeft; 
     nodeTop = $node.offsetTop; 
    } 

    var pos = {left: 0, top: 0}; 

    if (document.selection){ 
     var range = document.selection.createRange(); 
     pos.left = range.offsetLeft + offsetx - nodeLeft + 'px'; 
     pos.top = range.offsetTop + offsety - nodeTop + 'px'; 
    }else if (window.getSelection){ 
     var sel = window.getSelection(); 
     var range = sel.getRangeAt(0).cloneRange(); 
     try{ 
      range.setStart(range.startContainer, range.startOffset-1); 
     }catch(e){} 
     var rect = range.getBoundingClientRect(); 
     if (range.endOffset == 0 || range.toString() === ''){ 
      // first char of line 
      if (range.startContainer == $node){ 
       // empty div 
       if (range.endOffset == 0){ 
        pos.top = '0px'; 
        pos.left = '0px'; 
       }else{ 
        // firefox need this 
        var range2 = range.cloneRange(); 
        range2.setStart(range2.startContainer, 0); 
        var rect2 = range2.getBoundingClientRect(); 
        pos.left = rect2.left + offsetx - nodeLeft + 'px'; 
        pos.top = rect2.top + rect2.height + offsety - nodeTop + 'px'; 
       } 
      }else{ 
       pos.top = range.startContainer.offsetTop+'px'; 
       pos.left = range.startContainer.offsetLeft+'px'; 
      } 
     }else{ 
      pos.left = rect.left + rect.width + offsetx - nodeLeft + 'px'; 
      pos.top = rect.top + offsety - nodeTop + 'px'; 
     } 
    } 
    return pos; 
}; 
+0

니스 대답 친구! –

+0

필자는 필자의 답변에서 언급 한 바와 같이 Firefox 및 다른 브라우저의 'range.getBoundingClientRect()'는 때때로 축소 된 범위의 모든 0을 포함하는 Rect를 반환한다는 점을 상기합니다. 한 가지 예를 보려면 http://jsfiddle.net/CK3e8/을 참조하십시오. –

+0

그건 사실이야, 팀. 내가 선택한 한 문자를 뒤로 이동하고 거기에서 캐럿 위치를 찾으려고했습니다. – osamu