在文本区域上调用焦点会中断 document.getSelection()
Calling focus on a textarea breaks document.getSelection()
我目前正在尝试为我的 Svelte 应用程序构建一个增强的文本输入组件,使用户可以轻松输入化学物质 formulas/equations,但是在尝试创建将光标移动到单击文本输入时的正确位置,我 运行 遇到了问题。
我当前的 HTML 布局类似于以下内容:
<div class="input-field" contenteditable>
<span class="left"> /* text left of the cursor goes here */ </span>
<span class="caret"></span>
<span class="right"> /* text right of the cursor goes here */ </span>
</div>
<textarea class="input-textarea"></textarea>
我正在使用文本区域监视 input
和 keydown
事件,并使用来自这些事件的信息将正确的 text/special 字符插入 input-field
div。 div 是 contenteditable,因为我希望能够看到用户试图将他们的插入符号放在何处,将真正的插入符号焦点转移到文本区域,并将我的模拟插入符号移动到适当的位置。
在将焦点转移到文本区域之前尝试从 input-field
div 检索光标位置时出现此问题。如果任何地方存在任何代码试图随时将焦点转移到文本区域,那么 getSelection()
返回的结果将被破坏。如果 textarea.focus()
是同步完成的,使用 promises 异步完成的,使用 setTimeout
异步完成的,甚至放置在完全不同时间运行的不同函数中(由来自用户的不同事件触发),就会发生这种情况。删除此 focus
调用会使 getSelection
结果再次正确。 textarea 和 input 元素都会出现此问题。
getSelection
返回的结果以两种方式之一损坏:
- anchor/focus/base/extent 元素都是
body
元素或 #svelte
元素(div 包裹页面中的所有内容)。这不是结果应该是什么,它应该是 .left
或 .right
。偏移量也不正确。
getSelection
直接返回的结果是正确的,但是访问结果的任何属性都没有给出正确的值。这个很奇怪,我无法想象为什么代码行之间的值会不同,或者取决于甚至还没有执行的代码行。下面是 selection
和 selection.anchorNode
相继记录时的输出图像。
这种行为很奇怪,我很难找到解决这些巨大不一致的方法。任何帮助解释正在发生的事情的帮助将不胜感激。下面是我为演示此错误而制作的测试页面。
<!DOCTYPE html>
<html>
<head>
<script>
function handleFocus() {
let textarea = document.getElementById('textarea')
console.dir(document.getSelection())
setTimeout(() => {
textarea.focus()
})
}
</script>
<style>
</style>
</head>
<body>
<div contenteditable tabindex="1" onfocus="handleFocus()">This is some sample text, click on me</div>
<textarea id="textarea" tabindex="1"></textarea>
</body>
</html>
由于 getSelection 仅在事件发生后才是正确的,因此您必须不在处理程序中而是在事后调用 getSelection。
您可以通过在 setTimeout 中调用它来从处理程序中执行此操作,因为这会将它放到事件循环的最后。
这将是 svelte 的有效解决方案:
<script>
import {tick} from 'svelte'
let textarea;
let div;
function handleFocus() {
setTimeout(() => {
const focusOffset = window.getSelection().focusOffset
console.log(focusOffset)
textarea.focus()
}, 0)
}
</script>
<div bind:this={div} contenteditable on:focus="{handleFocus}">This is some sample text, click on me</div>
<textarea bind:this={textarea} class="input-textarea"></textarea>
我目前正在尝试为我的 Svelte 应用程序构建一个增强的文本输入组件,使用户可以轻松输入化学物质 formulas/equations,但是在尝试创建将光标移动到单击文本输入时的正确位置,我 运行 遇到了问题。
我当前的 HTML 布局类似于以下内容:
<div class="input-field" contenteditable>
<span class="left"> /* text left of the cursor goes here */ </span>
<span class="caret"></span>
<span class="right"> /* text right of the cursor goes here */ </span>
</div>
<textarea class="input-textarea"></textarea>
我正在使用文本区域监视 input
和 keydown
事件,并使用来自这些事件的信息将正确的 text/special 字符插入 input-field
div。 div 是 contenteditable,因为我希望能够看到用户试图将他们的插入符号放在何处,将真正的插入符号焦点转移到文本区域,并将我的模拟插入符号移动到适当的位置。
在将焦点转移到文本区域之前尝试从 input-field
div 检索光标位置时出现此问题。如果任何地方存在任何代码试图随时将焦点转移到文本区域,那么 getSelection()
返回的结果将被破坏。如果 textarea.focus()
是同步完成的,使用 promises 异步完成的,使用 setTimeout
异步完成的,甚至放置在完全不同时间运行的不同函数中(由来自用户的不同事件触发),就会发生这种情况。删除此 focus
调用会使 getSelection
结果再次正确。 textarea 和 input 元素都会出现此问题。
getSelection
返回的结果以两种方式之一损坏:
- anchor/focus/base/extent 元素都是
body
元素或#svelte
元素(div 包裹页面中的所有内容)。这不是结果应该是什么,它应该是.left
或.right
。偏移量也不正确。 getSelection
直接返回的结果是正确的,但是访问结果的任何属性都没有给出正确的值。这个很奇怪,我无法想象为什么代码行之间的值会不同,或者取决于甚至还没有执行的代码行。下面是selection
和selection.anchorNode
相继记录时的输出图像。
这种行为很奇怪,我很难找到解决这些巨大不一致的方法。任何帮助解释正在发生的事情的帮助将不胜感激。下面是我为演示此错误而制作的测试页面。
<!DOCTYPE html>
<html>
<head>
<script>
function handleFocus() {
let textarea = document.getElementById('textarea')
console.dir(document.getSelection())
setTimeout(() => {
textarea.focus()
})
}
</script>
<style>
</style>
</head>
<body>
<div contenteditable tabindex="1" onfocus="handleFocus()">This is some sample text, click on me</div>
<textarea id="textarea" tabindex="1"></textarea>
</body>
</html>
由于 getSelection 仅在事件发生后才是正确的,因此您必须不在处理程序中而是在事后调用 getSelection。 您可以通过在 setTimeout 中调用它来从处理程序中执行此操作,因为这会将它放到事件循环的最后。 这将是 svelte 的有效解决方案:
<script>
import {tick} from 'svelte'
let textarea;
let div;
function handleFocus() {
setTimeout(() => {
const focusOffset = window.getSelection().focusOffset
console.log(focusOffset)
textarea.focus()
}, 0)
}
</script>
<div bind:this={div} contenteditable on:focus="{handleFocus}">This is some sample text, click on me</div>
<textarea bind:this={textarea} class="input-textarea"></textarea>