【发布时间】:2022-02-27 00:05:06
【问题描述】:
我正在尝试编写一个 Chrome 扩展程序,该扩展程序需要能够在输入字段的光标位置插入字符。
当输入是实际的HTMLInputElement(insertAtCaretInput 从另一个堆栈答案借用)时,这很容易:
function insertAtCaretInput(text) {
text = text || '';
if (document.selection) {
// IE
this.focus();
var sel = document.selection.createRange();
sel.text = text;
} else if (this.selectionStart || this.selectionStart === 0) {
// Others
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
this.value = this.value.substring(0, startPos) + text + this.value.substring(endPos, this.value.length);
this.selectionStart = startPos + text.length;
this.selectionEnd = startPos + text.length;
} else {
this.value += text;
}
}
HTMLInputElement.prototype.insertAtCaret = insertAtCaretInput;
onKeyDown(e){
...
targetElement = e.target;
target.insertAtCaret(charToInsert);
...
}
但是当输入实际上在 HTML 结构中以不同的方式表示时(例如,Facebook 有一个 <div> 和 <span> 元素出现并在奇怪的时间合并)我无法弄清楚如何可靠地做到这一点。当我开始与输入交互时,新字符消失或改变位置或光标跳到不可预知的位置。
Facebook 的 HTML 结构示例(Chrome 桌面页面、新帖子或消息输入字段)可编辑 <div> 包含字符串 Test :
<div data-offset-key="87o4u-0-0" class="_1mf _1mj">
<span>
<span data-offset-key="87o4u-0-0">
<span data-text="true">Test
</span>
</span>
</span>
<span data-offset-key="87o4u-1-0">
<span data-text="true">
</span>
</span>
</div>
这是我迄今为止最成功的尝试。我像这样扩展 span 元素(insertTextAtCursor 也借用了另一个答案):
function insertTextAtCursor(text) {
let selection = window.getSelection();
let range = selection.getRangeAt(0);
range.deleteContents();
let node = document.createTextNode(text);
range.insertNode(node);
for (let position = 0; position != text.length; position++) {
selection.modify('move', 'right', 'character');
}
}
HTMLSpanElement.prototype.insertAtCaret = insertTextAtCursor;
并且由于触发按键事件的元素是<div>,然后保存<span>元素,然后保存带有实际输入的文本节点,我找到最深的<span>元素并在该元素上执行insertAtCaret:
function findDeepestChild(parent) {
var result = { depth: 0, element: parent };
[].slice.call(parent.childNodes).forEach(function (child) {
var childResult = findDeepestChild(child);
if (childResult.depth + 1 > result.depth) {
result = {
depth: 1 + childResult.depth,
element: childResult.element,
parent: childResult.element.parentNode,
};
}
});
return result;
}
onKeyDown(e){
...
targetElement = findDeepestChild(e.target).parent; // deepest child is a text node
target.insertAtCaret(charToInsert);
...
}
上面的代码可以成功插入字符,但是当 Facebook 的幕后框架尝试处理新值时会发生奇怪的事情。我尝试了各种技巧,重新定位光标并插入 <span> 元素,类似于 Facebook 在插入时操作 dom 时似乎发生的情况,但最终,所有这些都以某种方式失败。我想这是因为输入区域的状态被保存在某个地方并且与我的修改不同步。
您认为有可能可靠地做到这一点吗?如果可以,如何做到?理想情况下,答案不会特定于 Facebook,但也适用于使用其他元素而不是 HTMLInputElement 作为输入字段的其他页面,但我知道这可能是不可能的。
【问题讨论】:
-
你能添加一张图片你想改变什么输入字段吗?
-
@Anton 这是创建帖子输入和底部显示的小消息/聊天窗口输入。它们的工作方式似乎相似,因此您可以假设使其与 Create Post 一起使用就足够简单了
标签: javascript html facebook google-chrome-extension