有一种灵活的方法来修改可编辑元素的内容吗?

Luk*_*uke 7 html javascript text dom google-chrome-extension

学科:

我正在创建一个Google Chrome扩展程序,通过内容脚本和事件页面与网页进行交互.

我有当用户点击被归类为一个元素上出现的上下文菜单选项editablechrome.contextMenusAPI.

这些选项就像常用文本的快捷方式一样.当用户单击选项时,某些文本将放置在光标位置的元素内.如果用户突出显示文本,则会将其删除.


问题:

并非所有可编辑元素都可以以相同方式修改.

如果元素很简单textarea,则可以通过实现此解决方案来实现所需的结果:

但是,我不能假设我正常与正常人交往textarea.

可能的细微差别包括:

  • 文本区域隐藏在一个内部Iframe,这使得查找要与之交互的元素的过程复杂化(document.activeElement可能返回Iframe,而不是实际包含文本的元素).

  • <textarea>不会是一个<textarea>/ <input>所有,而是一个contentEditable <div>.该.value方法不能在这种情况下工作.

所以我正在寻找一种灵活的方法,可以优雅地处理所有边缘情况.


我试过的一些解决方案:

  • 选项1:
    我最初计划将值存储在系统剪贴板中.然后,我可以想象只是document.execCommand('paste')用来修改元素.然而,在尝试之后,这种方法似乎与我最初的方法具有相同的缺点.(请参阅此问题)

    此外,此方法将删除操作前剪贴板中的所有内容.这是不可取的,任何利用这种方法的解决方案都必须提供解决方法.

  • 选项2:
    我考虑的另一个选项是为字符串中的每个字符分派键盘事件.但是,使用此解决方案,您仍然遇到Iframe问题,并且它不允许您使用特殊的Unicode字符.┻━┻(ヽ(`Д')ノ(┻━┻

Rob*_*b W 7

您的问题包含两个子问题:

  1. 确定contextmenu操作的目标元素.
  2. 在插入符号处插入自定义文本片段(如果存在则删除任何选择).

子问题1:识别目标元素

  • 如果解决了crbug.com/39507,那么获取元素很容易.这个功能要求差不多5年没有任何进展,所以不要对这个有很高的期望.替代方法要求您在另外两个子问题中打破问题1:识别目标帧,然后选择目标DOM元素.

有几个API可以帮助识别框架(使用它们的组合,选择最适合您的情况的组合):

  • contextMenus.onClicked事件将tab的ID(tab.id)作为tabs.top实例中的属性,并将框架的URL(frameUrl)作为单独的对象提供.
  • chrome.tabs.executeScript方法可以直接在框架中运行脚本.
    (目前只有顶级框架或所有框架,针对特定框架正在进行中 - 计划用于Chrome 42的crbug.com/63979).
    在实现特定框架的目标之前,您可以在每个框架中插入内容脚本并将URL与之比较frameUrl(或使用下一个方法的组合).
  • 假设您已经chrome.runtime.onMessage使用侦听器插入了内容脚本,请使用chrome.tabs.sendMessage将消息发送到由frameId(自Chrome 41开始)标识的特定帧.
  • 使用该chrome.webNavigation.getAllFrames方法获取给定的选项卡中的所有帧的列表tabId,然后frameId通过已知的过滤帧列表来获取目标帧的列表frameUrl.
  • (将来(Chrome 42?),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;
Run Code Online (Sandbox Code Playgroud)

否则,您需要知道是否存在内容可编辑元素,如果存在,则删除任何选择(如果存在),最后将所需文本放在那里.

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);
}
Run Code Online (Sandbox Code Playgroud)

上一个示例中使用的Web平台API的相关文档: