是否有灵活的方法来修改可编辑元素的内容? [英] Is there a flexible way to modify the contents of an editable element?

查看:106
本文介绍了是否有灵活的方法来修改可编辑元素的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

主题:



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



如果用户点击被分类为可编辑的元素,我会显示上下文菜单选项,一个href =https://developer.chrome.com/extensions/contextMenus =nofollow noreferrer> chrome.contextMenus API 。



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






问题:



不能修改所有可编辑元素一样的方法。



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





但是,我不能假设我正在与正常的 textarea的



可能的细微差别包括:




  • 正在隐藏的文本区域一个 Iframe ,这使找到要交互的元素的过程变得复杂( document.activeElement 可能会返回 Iframe ,而不是实际包含文本的元素。


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




所以我正在寻找一种灵活的方式来做到这一点,可以优雅地处理所有的边缘案例。






我尝试过的一些解决方案:




  • 选项1:

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

    此外,这种方法将删除操作前的剪贴板中的任何内容。这是不可取的,使用这种方法的任何解决方案都必须提供一个工作。



  • 选项2:

    我考虑过的另一个选项是为每个字符串中的字符。但是,使用此解决方案,您仍然遇到 Iframe 问题,它不允许您使用特殊的Unicode字符。 ┻━ノノノノ┻┻┻┻┻┻$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $

    您的问题由两个子问题组成:


    1. 识别上下文菜单操作的目标元素。

    2. 在插入符处插入自定义文本片段(如果存在,请删除任何选择)。



    子问题1:识别目标元素




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



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





    好的,假设你有正确的框架,你可以简单使用 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的相关文档:




    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 the chrome.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 the Iframe, rather than the element that actually contains the text).

    • The <textarea> not being a <textarea>/<input> at all, but rather a contentEditable <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 use document.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 the Iframe problem and it doesn't allow you do use special Unicode characters. ┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻

    解决方案

    Your problem consists of two subproblems:

    1. Identify the target element of the contextmenu action.
    2. 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 with frameUrl (or use a combination of the next methods).
    • Assuming that you have already inserted a content script with a chrome.runtime.onMessage listener, use chrome.tabs.sendMessage to send a message to a specific frame identified by frameId (since Chrome 41).
    • Use the chrome.webNavigation.getAllFrames method to get a list of all frames in a tab for a given tabId, then get the frameId of the target frame by filtering the list of frames by a known frameUrl.
    • (in the future (Chrome 42?), contextMenus.onClicked will get the frameId).

    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:

    这篇关于是否有灵活的方法来修改可编辑元素的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆