Cómo obtener el desplazamiento de píxeles desde la posición actual de intercalación en un iframe con contenido Editable

Me gustaría contentEditable un elemento div flotante en un iframe con contentEditable , en caso de que el usuario ingrese una determinada combinación de teclas (para fines de autocompletar).

Sé cómo obtener la posición de intercalación: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorOffset

Puedo usar esto para calcular la propiedad left del div, pero parece que no puedo encontrar la manera de llegar a la top .

Otra posibilidad en la que pensé fue usar: document.getElementById('elm1_ifr').contentWindow.getSelection().anchorNode.parentNode

Y usando jQuery para obtener el desplazamiento, pero si ese padre tiene una línea de texto larga, solo podría extraer la posición superior de la primera línea.

Puede alguien ayudarme con esto?

La única forma confiable de hacerlo es insertar un elemento temporal en el símbolo de intercalación (asegurándose de que sea de ancho cero), obtener su posición y eliminarlo de nuevo. También debe unir los dos extremos del nodo de texto (si se trataba de un nodo de texto que contenía el símbolo de intercalación) de nuevo para garantizar que DOM esté como estaba antes de insertar el nodo. Sin embargo, tenga en cuenta que al hacer esto (o cualquier otra manipulación DOM manual en el contenido editable) se rompe la stack de deshacer interna del navegador.

La razón para esto es que la lectura cuidadosa de la especificación para el método getBoundingClientRect() de Range muestra que getBoundingClientRect() no está obligado a devolver un Rect para un rango colapsado. Conceptualmente, no todas las posiciones del documento tienen un rectángulo delimitador bien definido. El cursor, sin embargo, tiene una ubicación física en la pantalla que, en mi opinión, debe ser proporcionada por la API de selección, pero actualmente no hay nada en los navegadores que brinde esto.

Llegué a este problema hoy. Después de algunas pruebas, obtuve esto funcionando, sin usar el elemento temorario.

En IE, es fácil resolverlo con las propiedades offsetLeft y offsetTop de un objeto TextRange. Sin embargo, se necesita un poco de esfuerzo para webkit.

Aquí hay una prueba, puedes ver el resultado. 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; };