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

时间:2015-01-20 21:57:22

标签: javascript html dom text google-chrome-extension

主题:

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

如果用户点击chrome.contextMenus API归类为editable的元素,则会显示上下文菜单选项。

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


问题:

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

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

但是,我不能假设我正在与正常的textarea进行交互。

可能的细微差别包括:

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

  • <textarea>根本不是<textarea> / <input>,而是contentEditable <div>。在这种情况下,.value方法不起作用。

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


我尝试了一些解决方案:

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

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

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

1 个答案:

答案 0 :(得分:7)

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

  1. 确定contextmenu操作的目标元素。
  2. 在插入符号处插入自定义文本片段(如果存在,则删除所有选择)。
  3. 子问题1:识别目标元素

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

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

    • contextMenus.onClicked事件在单独的对象中将标签的ID(tab.id)作为tabs.Tab实例中的属性和框架的网址(frameUrl)提供。 / LI>
    • chrome.tabs.executeScript方法可以直接在框架中运行脚本 (目前只有顶级框架或所有框架,针对特定框架正在进行中 - crbug.com/63979,计划用于Chrome 42)。
      在实现特定框架的目标之前,您可以在每个框架中插入内容脚本,并将URL与frameUrl进行比较(或使用下一种方法的组合)。
    • 假设您已经使用chrome.runtime.onMessage侦听器插入了内容脚本,请使用chrome.tabs.sendMessageframeId标识的特定帧发送消息(自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;
    

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

    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的相关文档:

相关问题