问题描述
主题:
我正在创建一个Google Chrome扩展程序,通过内容脚本和事件页面与网页进行交互。
如果用户点击被分类为可编辑
的元素,我会显示上下文菜单选项,一个href =https://developer.chrome.com/extensions/contextMenus =nofollow noreferrer> chrome.contextMenus
API 。
选项的行为就像通常输入的文本的快捷方式一样。当用户单击选项时,元素中的某些文本将放置在光标的位置。如果用户突出显示文本,则会被删除。
问题:
不能修改所有可编辑元素一样的方法。
如果元素是一个简单的 textarea
,可以通过实现此解决方案实现所需的结果:
但是,我不能假设我正在与正常的 textarea的
。
可能的细微差别包括:
-
正在隐藏的文本区域一个
Iframe
,这使找到要交互的元素的过程变得复杂(document.activeElement
可能会返回Iframe
,而不是实际包含文本的元素。 -
textarea>
不是一个< textarea>
/< input>
而是。在这种情况下,.value
方法将不起作用。
所以我正在寻找一种灵活的方式来做到这一点,可以优雅地处理所有的边缘案例。
我尝试过的一些解决方案:
- 选项1:
我原来计划将该值存储在系统剪贴板中。然后,我可以想象只是使用document.execCommand('paste')
修改元素。然而,在尝试之后,这种方法似乎与我的初始方法有同样的缺点。 (请参阅 )
此外,这种方法将删除操作前的剪贴板中的任何内容。这是不可取的,使用这种方法的任何解决方案都必须提供一个工作。
- 选项2:
我考虑过的另一个选项是为每个字符串中的字符。但是,使用此解决方案,您仍然遇到Iframe
问题,它不允许您使用特殊的Unicode字符。 ┻━ノノノノ┻┻┻┻┻┻$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
您的问题由两个子问题组成:
- 识别上下文菜单操作的目标元素。
- 在插入符处插入自定义文本片段(如果存在,请删除任何选择)。
子问题1:识别目标元素
- 如果已解决,然后获取元素很容易。这个功能要求差不多5年了,没有任何进展,所以不要让你的希望高。
替代方法要求您在两个子问题中打破问题1:识别目标框架,然后选择目标DOM元素。
有几个API可以帮助识别框架(使用它们的组合,选择最适合您的情况的组合):
- 事件在和框架的URL()。
- 甲基od可以直接在一个框架中运行一个脚本。
(目前只有顶级框架或所有框架,定位一个特定的框架正在进行中 - ,计划用于Chrome 42)。
在定位特定框架之前实现时,您可以在每个框架中插入内容脚本,并将URL与frameUrl
(或使用下一个方法的组合)进行比较。 - 假设您已经使用侦听器,请使用将消息发送到由
frameId
标识的特定框架(由于Chrome 41)。 li>
- 使用方法获取给定
tabId
,然后通过已知的frameUrl过滤帧列表来获取目标帧的
。frameId
- (将来(Chrome 42?),
contextMenus.onClicked
将获得frameId
)。
好的,假设你有正确的框架,你可以简单使用
document.activeElement
获取目标元素,因为输入元素集中在点击。
子问题2:插入如果目标元素是
< textarea>
或
>< input> ,那么你可以简单地使用
/假设:elem是一个输入或textarea元素。
var YOURSTRING ='whatever';
var start = elem.selectionStart;
var end = elem.selectionEnd;
elem.value = elem.value.slice(0,start)+ YOURSTRING + elem.value.substr(end);
//设置选定文本后的光标
elem.selectionStart = start + YOURSTRING.length;
elem.selectionEnd = elem.selectionStart;
否则,您需要知道是否有内容可编辑元素,如果是,请删除任何选择如果存在,最后把你想要的文本放在那里。
var elem = document.activeElement;
if(elem&& elem.isContentEditable){
// YOURSTRING as defined before
var newNode = document.createTextNode(YOURSTRING);
var sel = window.getSelection();
//删除以前的选择,如果有的话。
sel.deleteFromDocument();
//如果选择中没有范围,则添加一个新的值,将
//插入符设置为输入元素的末尾,以避免下一个错误:
//无法在选择上执行getRangeAt:0不是有效的索引。
if(sel.rangeCount === 0){
sel.addRange(document.createRange());
sel.getRangeAt(0).collapse(elem,1);
}
//插入新文本
var range = sel.getRangeAt(0);
range.insertNode(newNode);
//现在,将光标设置为文本节点的结尾
sel.collapse(newNode,1);
}
上一个示例中使用的Web平台API的相关文档:
- a href =https://developer.mozilla.org/en-US/docs/Web/API/Selection.collapse =nofollow>
Selection.collapse
Subject:
I am creating a Google Chrome extension that interacts with web pages via a content script and an event page.
I have context menu options that appear if a user clicks on an element that is categorized as
editable
by thechrome.contextMenus
API.The options act like shortcuts for commonly entered text. When the user clicks an option, some text is placed inside the element at the position of the cursor. If the user has text highlighted, it is deleted.
Problem:
Not all editable elements can be modified the same way.
If the element is a simple
textarea
the desired result can be achieved by implementing this solution:However, I can not assume that I am interacting with a normal
textarea
.Possible nuances include:
The text area being hidden inside an
Iframe
, which complicates the process of finding the element to interact with (document.activeElement
may return theIframe
, rather than the element that actually contains the text).The
<textarea>
not being a<textarea>
/<input>
at all, but rather acontentEditable <div>
. The.value
approach will not work in this case.
So I am searching for a flexible way to do this that can handle all edge cases elegantly.
Some solutions I have tried:
- option 1 :
I originally planned on storing the value in the system clipboard. Then, I could conceivably just usedocument.execCommand('paste')
to modify the element. However, after trying it, this approach seems to have the same drawbacks as my initial approach. (See this question)
Additionally, this approach would delete whatever was in the clipboard before the operation. This is undesirable and any solution that utilizes this approach must provide a work around. - option 2 :
Another option that I have considered is dispatching keyboard events for each character in the string. However, with this solution, you still run into theIframe
problem and it doesn't allow you do use special Unicode characters. ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻
解决方案Your problem consists of two subproblems:
- Identify the target element of the contextmenu action.
- Insert a custom text fragment at the caret (remove any selection if it is present).
Subproblem 1: Identifying the target element
- If crbug.com/39507 is resolved, then getting the element is easy. This feature request is almost 5 year old without any progress, so don't get your hopes high on this one.Alternative methods require you to break problem 1 in two more subproblems: Identifying the target frame, and then select the target DOM element.
There are several APIs that help with identifying the frame (use a combination of them, choose whichever combination fits best in your situation):
- The
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
) in a separate object. - The
chrome.tabs.executeScript
method can directly run a script in a frame.
(currently only the top-level frame or all frames, targetting a specific frame is work in progress - crbug.com/63979, planned for Chrome 42).
Until targetting a specific frame is implemented, you could insert the content script in every frame and compare the URL withframeUrl
(or use a combination of the next methods). - Assuming that you have already inserted a content script with a
chrome.runtime.onMessage
listener, usechrome.tabs.sendMessage
to send a message to a specific frame identified byframeId
(since Chrome 41). - Use the
chrome.webNavigation.getAllFrames
method to get a list of all frames in a tab for a giventabId
, then get theframeId
of the target frame by filtering the list of frames by a knownframeUrl
. - (in the future (Chrome 42?),
contextMenus.onClicked
will get theframeId
).
Okay, assuming that you have the correct frame, you can simply use
document.activeElement
to get the target element, because input elements get focused upon click.Subproblem 2: Inserting a text fragment at the caret
If the target element is a
<textarea>
or<input>
, then you can simply use// 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;
Otherwise, you need to know whether there is a content editable element, and if so, remove any selection if existent, and finally put your desired text over there.
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); }
Relevant documentation for the web platform APIs used in the last example:
这篇关于是否有灵活的方法来修改可编辑元素的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!