Как получить смещение пикселя от текущей позиции каретки в iframe с contentEditable
Я хотел бы поместить плавающий элемент div
в iframe
с contentEditable
, если пользователь вводит определенную комбинацию клавиш (для целей автозаполнения).
Я знаю, как получить позицию каретки:
document.getElementById('elm1_ifr').contentWindow.getSelection().anchorOffset
Я могу использовать это для вычисления свойства left
для div, но я не могу понять, как получить top
.
Другая возможность, о которой я думал, - использование:
document.getElementById('elm1_ifr').contentWindow.getSelection().anchorNode.parentNode
И используя jQuery для получения смещения, но если у этого родителя есть длинная текстовая строка, я бы смог извлечь только верхнюю позицию первой строки.
Может ли кто-нибудь помочь мне с этим?
Ответы
Ответ 1
Единственный надежный способ сделать это - вставить временный элемент в каретку (убедитесь, что он равен нулю), получить его положение и удалить его снова. Вы должны также склеить два конца текста node (если это был текст node, содержащий каретку) назад, чтобы гарантировать, что DOM так же, как и перед вставкой node. Обратите внимание, однако, что выполнение этого (или любого другого ручного манипулирования DOM на редактируемом контенте) нарушает внутренний стек отладки браузера.
Причиной этого является то, что тщательное чтение спецификации для метода getBoundingClientRect()
Range
показывает, что getBoundingClientRect()
не обязан возвращать Rect для свернутого диапазона. Понятно, что не каждая позиция в документе имеет четко определенный ограничивающий прямоугольник. Однако каретка имеет физическое расположение на экране, которое, на мой взгляд, должно предоставляться API-интерфейсом выбора, но в настоящее время в браузерах нет ничего, чтобы обеспечить это.
Ответ 2
Сегодня я столкнулся с этой проблемой. После некоторого тестирования я получил эту работу, не используя темный элемент.
В IE легко обрабатывать его со свойствами offsetLeft и offsetTop объекта TextRange. Однако для webkit необходимы некоторые усилия.
В этом тесте вы можете увидеть результат. 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;
};