我可以使用什么来使用多个文本节点可靠地获取 Javascript 中单击的字符偏移量?

当用户单击某些文本时,我使用Window.getSelection获取用户单击位置的字符偏移量。当只有一个文本节点时,这非常有效,并且关于如何做到这一点有很多很好的答案。

但是,当我有两个或多个文本节点彼此相邻呈现时,我遇到了问题。这是一个复制问题的小提琴,但我将在这里介绍它:

用例

我正在构建一个文本编辑器,并使用对用户按键做出反应的 Javascript 控制 DOM,而不是使用 contentEditable 容器。我想跟踪(并显示)用户的“光标”在文本中的位置(即,如果他们开始输入,将输入文本的位置),并让他们单击文本中的任意位置以手动设置光标到他们点击的地方。

HTML

<p class="sentence">

  <span class="word" index="0">There </span><span class="word" index="1">is </span><span class="word" index="2">a </span><span class="word" index="3">big, </span><span class="word" index="4">wonderful </span><span class="word" index="5">world.</span>

</p>

JavaScript:


$('.word').click(function() {

  let word_index = $(this).attr('index');

  

  let selection  = window.getSelection();

  let char_index = selection.focusOffset;


  console.log('Clicked to set a new cursor @');

  console.log('Word index = ' + word_index.toString() + 

             ' / char index = ' + char_index.toString() );

});

简而言之,此代码打印 aword index和 acharacter index表示光标在文本中的位置,就好像它是可编辑输入一样。如果你点击“There”中“T”的左半部分,它会打印


Clicked to set a new cursor @

Word index = 0

/ char index = 0

单击“T”的右侧(这样光标将放置在其之后但“h”之前)将打印相同的单词索引,但字符索引为 1,因为光标现在放置在一个字符上,等等。

这也很有效......除了单击任何单词(第一个除外)的第一个字符的左半部分。单击“is”中“i”的左半部分(在单词索引 1、字符索引 0 处设置插入符号)会打印单词索引 1(正确)和字符索引 6(前一个单词的长度)。

当存在多个彼此相邻的文本节点时Window.getSelection(或更具体地说, )不是计算这种字符偏移量的正确方法吗?selection.focusOffset我需要使用其他库或方法吗?

解决该问题的一个“解决方案”是在每个单词周围应用边距,以在它们之间放置不可点击的间隙。在这种情况下,单击“i”的左半部分会给出正确的单词/字符索引(1,0 而不是 1,6),但会产生空格的副作用,如果用户不小心单击那里,则不会发生任何情况(此应用程序中存在严重的副作用,因此它并不是真正可行的“修复”)。看起来至少 3px 的边距总是返回正确的值,而 2px 或更小的边距总是返回错误的值。

我正在 Chrome 中进行测试,因为我最终将构建一个 Electron 应用程序,所以我想我只需要它在 Blink 中工作,但如果解决方案对于 Web 版本也与浏览器无关,那就太好了。


GCT1015
浏览 112回答 2
2回答

潇潇雨雨

我能够通过实施以下两个额外的护栏来实现此目的:要使其跨浏览器,还有更多工作要做,但这里有一个更新的小提琴,它使用这些调整来获取 Chrome 中所需的值。

子衿沉夜

默认行为取决于浏览器,因此您需要进行一些检查才能给出一致的结果。这是一个简单的示例,它将event.target元素与parentElement返回的文本节点的进行比较selection.focusNode,如果不匹配,它会检查焦点节点的兄弟节点是否匹配并相应地调整偏移量。您需要使其更加稳健。您还需要处理方向性(从左到右选择会产生反向焦点,并且选择从右到左的锚节点)。您也可以看看Selection.containsNode()function handleSelection(e) {  const selection = window.getSelection();    const fNode = selection.focusNode;    const fElement = fNode.parentElement;  const tElement = e.target;    let word_index = tElement.dataset.index;  let char_index = selection.focusOffset;    if (!fElement.isEqualNode(tElement)     && fElement.nextElementSibling.isEqualNode(tElement)) {    char_index=0;  }    console.log(    'fNode: ', fNode.parentElement.innerText,    ' tNode: ', tElement.innerText,    ' char: ', char_index,    ' word: ', word_index  );}const words = document.querySelectorAll('.word');words.forEach((w, i) => (  w.dataset.index = i+1,   w.addEventListener('click', handleSelection)));.word {  border: 1px solid gray;  font-size: 2rem;}<p class="sentence">  <span class="word">Hello </span><span class="word">wonderful</span><span class="word"> world.</span></p>
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript