有没有一种灵活的方法来修改可编辑元素的内容?
Is there a flexible way to modify the contents of an editable element?
主题:
我正在创建一个 Google Chrome 扩展,它通过内容脚本和事件页面与网页交互。
如果用户单击被 chrome.contextMenus
API 归类为 editable
的元素,我会显示上下文菜单选项。
这些选项就像常用输入文本的快捷方式。当用户单击一个选项时,一些文本将放置在光标位置的元素内。如果用户突出显示了文本,则会将其删除。
问题:
并非所有可编辑元素都可以用相同的方式修改。
如果元素是简单的textarea
,则可以通过实施此解决方案获得所需的结果:
- Replacing selected text in the textarea
但是,我不能假设我正在与正常人互动 textarea
。
可能的细微差别包括:
文本区域隐藏在 Iframe
中,这使得查找要与之交互的元素的过程变得复杂(document.activeElement
可能 return Iframe
, 而不是实际包含文本的元素).
<textarea>
根本不是 <textarea>
/<input>
,而是 contentEditable <div>
。 .value
方法在这种情况下不起作用。
所以我正在寻找一种可以优雅地处理所有边缘情况的灵活方法。
我尝试过的一些解决方案:
- 选项 1:
我最初计划将值存储在系统剪贴板中。然后,我可以想象只使用 document.execCommand('paste')
来修改元素。然而,在尝试之后,这种方法似乎与我最初的方法有同样的缺点。 (参见 this question)
此外,此方法会删除操作前剪贴板中的所有内容。这是不可取的,任何使用此方法的解决方案都必须提供变通方法。
- 选项 2:
我考虑过的另一种选择是为字符串中的每个字符分派键盘事件。但是,使用此解决方案,您仍然 运行 陷入 Iframe
问题,并且它不允许您使用特殊的 Unicode 字符。 ┻━┻︵ヽ(`Д´)ノ︵┻━┻
您的问题由两个子问题组成:
- 确定上下文菜单操作的目标元素。
- 在插入符号处插入自定义文本片段(删除任何 selection 如果存在)。
子问题 1:识别目标元素
- 如果crbug.com/39507解决了,那么获取元素就很容易了。这个功能请求已经将近 5 年了,没有任何进展,所以不要对这个抱有太大希望。
替代方法要求您在另外两个子问题中解决问题 1:识别目标框架,然后 select 目标 DOM 元素。
有几个 API 可以帮助识别框架(使用它们的组合,选择最适合您情况的组合):
contextMenus.onClicked
event provides the tab's ID (tab.id
) as a property in an instance of tabs.Tab and the frame's URL (frameUrl
) 在单独的对象中。
chrome.tabs.executeScript
方法可以直接运行一个框架中的脚本
(目前只有顶级框架或所有框架,针对特定框架的工作正在进行中 - crbug.com/63979,计划 Chrome 42)。
在实现针对特定帧之前,您可以在每个帧中插入内容脚本并将 URL 与 frameUrl
进行比较(或使用下一种方法的组合)。
- 假设您已经插入了带有
chrome.runtime.onMessage
listener, use chrome.tabs.sendMessage
的内容脚本以将消息发送到由 frameId
标识的特定框架(自 Chrome 41)。
- 使用
chrome.webNavigation.getAllFrames
方法获取给定tabId
选项卡中所有框架的列表,然后通过筛选框架列表获取目标框架的frameId
由一个已知的 frameUrl
.
- (以后(Chrome42?),
contextMenus.onClicked
会得到frameId
).
好的,假设您有正确的框架,您可以简单地使用 document.activeElement
来获取目标元素,因为输入元素会在点击时获得焦点。
子问题 2:在插入符处插入文本片段
如果目标元素是 <textarea>
或 <input>
,那么您可以简单地使用
// Assume: elem is an input or textarea element.
var YOURSTRING = 'whatever';
var start = elem.selectionStart;
var end = elem.selectionEnd;
elem.value = elem.value.slice(0, start) + YOURSTRING + elem.value.substr(end);
// Set cursor after selected text
elem.selectionStart = start + YOURSTRING.length;
elem.selectionEnd = elem.selectionStart;
否则,你需要知道是否有内容可编辑元素,如果有,删除任何 selection 如果存在,最后把你想要的文本放在那里。
var elem = document.activeElement;
if (elem && elem.isContentEditable) {
// YOURSTRING as defined before
var newNode = document.createTextNode(YOURSTRING);
var sel = window.getSelection();
// Remove previous selection, if any.
sel.deleteFromDocument();
// If there is no range in the selection, add a new one, with the
// caret set to the end of the input element to avoid the next error:
//"Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index."
if (sel.rangeCount === 0) {
sel.addRange(document.createRange());
sel.getRangeAt(0).collapse(elem, 1);
}
// Insert new text
var range = sel.getRangeAt(0);
range.insertNode(newNode);
// Now, set the cursor to the end of your text node
sel.collapse(newNode, 1);
}
上一个例子中使用的web平台API的相关文档:
主题:
我正在创建一个 Google Chrome 扩展,它通过内容脚本和事件页面与网页交互。
如果用户单击被 chrome.contextMenus
API 归类为 editable
的元素,我会显示上下文菜单选项。
这些选项就像常用输入文本的快捷方式。当用户单击一个选项时,一些文本将放置在光标位置的元素内。如果用户突出显示了文本,则会将其删除。
问题:
并非所有可编辑元素都可以用相同的方式修改。
如果元素是简单的textarea
,则可以通过实施此解决方案获得所需的结果:
- Replacing selected text in the textarea
但是,我不能假设我正在与正常人互动 textarea
。
可能的细微差别包括:
文本区域隐藏在
Iframe
中,这使得查找要与之交互的元素的过程变得复杂(document.activeElement
可能 returnIframe
, 而不是实际包含文本的元素).<textarea>
根本不是<textarea>
/<input>
,而是contentEditable <div>
。.value
方法在这种情况下不起作用。
所以我正在寻找一种可以优雅地处理所有边缘情况的灵活方法。
我尝试过的一些解决方案:
- 选项 1:
我最初计划将值存储在系统剪贴板中。然后,我可以想象只使用document.execCommand('paste')
来修改元素。然而,在尝试之后,这种方法似乎与我最初的方法有同样的缺点。 (参见 this question)
此外,此方法会删除操作前剪贴板中的所有内容。这是不可取的,任何使用此方法的解决方案都必须提供变通方法。 - 选项 2:
我考虑过的另一种选择是为字符串中的每个字符分派键盘事件。但是,使用此解决方案,您仍然 运行 陷入Iframe
问题,并且它不允许您使用特殊的 Unicode 字符。 ┻━┻︵ヽ(`Д´)ノ︵┻━┻
您的问题由两个子问题组成:
- 确定上下文菜单操作的目标元素。
- 在插入符号处插入自定义文本片段(删除任何 selection 如果存在)。
子问题 1:识别目标元素
- 如果crbug.com/39507解决了,那么获取元素就很容易了。这个功能请求已经将近 5 年了,没有任何进展,所以不要对这个抱有太大希望。 替代方法要求您在另外两个子问题中解决问题 1:识别目标框架,然后 select 目标 DOM 元素。
有几个 API 可以帮助识别框架(使用它们的组合,选择最适合您情况的组合):
contextMenus.onClicked
event provides the tab's ID (tab.id
) as a property in an instance of tabs.Tab and the frame's URL (frameUrl
) 在单独的对象中。chrome.tabs.executeScript
方法可以直接运行一个框架中的脚本
(目前只有顶级框架或所有框架,针对特定框架的工作正在进行中 - crbug.com/63979,计划 Chrome 42)。
在实现针对特定帧之前,您可以在每个帧中插入内容脚本并将 URL 与frameUrl
进行比较(或使用下一种方法的组合)。- 假设您已经插入了带有
chrome.runtime.onMessage
listener, usechrome.tabs.sendMessage
的内容脚本以将消息发送到由frameId
标识的特定框架(自 Chrome 41)。 - 使用
chrome.webNavigation.getAllFrames
方法获取给定tabId
选项卡中所有框架的列表,然后通过筛选框架列表获取目标框架的frameId
由一个已知的frameUrl
. - (以后(Chrome42?),
contextMenus.onClicked
会得到frameId
).
好的,假设您有正确的框架,您可以简单地使用 document.activeElement
来获取目标元素,因为输入元素会在点击时获得焦点。
子问题 2:在插入符处插入文本片段
如果目标元素是 <textarea>
或 <input>
,那么您可以简单地使用
// Assume: elem is an input or textarea element.
var YOURSTRING = 'whatever';
var start = elem.selectionStart;
var end = elem.selectionEnd;
elem.value = elem.value.slice(0, start) + YOURSTRING + elem.value.substr(end);
// Set cursor after selected text
elem.selectionStart = start + YOURSTRING.length;
elem.selectionEnd = elem.selectionStart;
否则,你需要知道是否有内容可编辑元素,如果有,删除任何 selection 如果存在,最后把你想要的文本放在那里。
var elem = document.activeElement;
if (elem && elem.isContentEditable) {
// YOURSTRING as defined before
var newNode = document.createTextNode(YOURSTRING);
var sel = window.getSelection();
// Remove previous selection, if any.
sel.deleteFromDocument();
// If there is no range in the selection, add a new one, with the
// caret set to the end of the input element to avoid the next error:
//"Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index."
if (sel.rangeCount === 0) {
sel.addRange(document.createRange());
sel.getRangeAt(0).collapse(elem, 1);
}
// Insert new text
var range = sel.getRangeAt(0);
range.insertNode(newNode);
// Now, set the cursor to the end of your text node
sel.collapse(newNode, 1);
}
上一个例子中使用的web平台API的相关文档: