获取相对于窗口的插入符位置坐标(x,y)

3

摘要

我想获取插入符号(光标)的窗口位置(以像素为单位),用于当用户开始输入时。

完整故事

有一个多行的 contentEditable 元素,其中包含复杂的 HTML 结构。当用户在其中任何地方开始输入时,在第一次按键时,我想在插入符号下方放置一个absolute定位的元素。新添加的元素必须注入到<body>标签中,而不是作为contentEditable元素内的DOM节点。

因此,我需要插入符号的确切全局坐标。 我将明确表示没有文本选择,选择在这里无关紧要且不太可能出现。

我的代码

通常问题应该包括一些 OP 已尝试但需要帮助的代码,但在这种情况下,我已经无计可施了。 input DOM 事件不会公开坐标。 我至少提供了我所需的结果:

.info{
  border: 1px solid gold;
  padding: 8px;
  background: rgba(255,255,224, .8);
  position: absolute;
  z-index: 9999;
  max-width: 180px;
}
<h2>contentEditable:</h2>
<div contentEditable>
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
    <strong>Ut enim ad minim veniam</strong>, 
    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
  </p>

  <p>
    Imagine the caret is somewhere here and the info element is right under it... <div>reprehenderit <em>in</em> voluptate</div> 
    velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat 
    cupidatat non proident, sunt in culpa qui officia deserunt <small>mollit anim</small> 
    id est laborum.
  </p>
</div>
<div class='info' style='top:150px; left:60px;'>Extra info with a long list of some content goes here</div>


免责声明:

我已经进行了相当多的调查,但不幸的是没有找到答案。有些问题看起来很相似,但深入研究后发现它们根本不同,而有些则非常古老和过时。

谢谢!

1个回答

6

经过更深入的了解,我找到了一个Gist文件,它正好符合我的要求,所以这里是一个完美的工作方案,我稍微进行了一些调整:

/**
 * Get the caret position, relative to the window 
 * @returns {object} left, top distance in pixels
 */
function getCaretGlobalPosition(){
    const r = document.getSelection().getRangeAt(0)
    const node = r.startContainer
    const offset = r.startOffset
    const pageOffset = {x:window.pageXOffset, y:window.pageYOffset}
    let rect,  r2;

    if (offset > 0) {
        r2 = document.createRange()
        r2.setStart(node, (offset - 1))
        r2.setEnd(node, offset)
        rect = r2.getBoundingClientRect()
        return { left:rect.right + pageOffset.x, top:rect.bottom + pageOffset.y }
    }
}

/////////////////[ DEMO ]\\\\\\\\\\\\\\\\\\\\

const contenteditable = document.querySelector('[contenteditable]')
const infoElm = document.querySelector('.info')

contenteditable.addEventListener('input', onInput)

function onInput(){
  const caretGlobalPosition = getCaretGlobalPosition()

  infoElm.style.cssText = `top:${caretGlobalPosition.top}px;
                           left:${caretGlobalPosition.left}px;`
}
.info{
  border: 1px solid gold;
  padding: 8px;
  background: rgba(255,255,224, .8);
  position: absolute;
  z-index: 9999;
  max-width: 180px;
  display:none;
}

.info[style]{ display:block; }
<h2>Place caret somewhere and type:</h2>
<div contenteditable>
  <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
    <strong>Ut enim ad minim veniam</strong>, 
    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
  </p>

  <h2>
    Imagine the caret is somewhere here and the info element is right under it... <div>reprehenderit <em>in</em> voluptate</div> 
    velit esse cillum dolore eu fugiat f nulla pariatur. Excepteur sint occaecat 
    cupidatat non proident, sunt in culpa qui officia deserunt <small>mollit anim</small> 
    id est laborum.
  </h2>
</div>
<div class='info'>Extra info with a long list of some content goes here</div>

我发现另一种解决方案: https://github.com/component/textarea-caret-position

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接