【问题标题】:Showing context menu buttons only when right-clicked on classes that start with "Story"仅当右键单击以“故事”开头的类时才显示上下文菜单按钮
【发布时间】:2014-09-10 10:51:42
【问题描述】:

只有当用户右键单击以“故事”开头的类时,有没有一种方法可以显示上下文菜单操作。

例如:如果用户在“story ....”类的页面中右击一个对象,上下文菜单按钮应该会出现,否则什么都不会发生。

这是我的代码(虽然它不起作用):

var divs = document.querySelectorAll("[class^=story]"); //get all classes that start with "Story"

    window.oncontextmenu = function() {

        for(var i=0; i < divs.length; i++)
        {
            divs[i].onclick = function() { 
            chrome.contextMenus.create
            (
                {"id": "butto1", "title": "1", "contexts":["all"], "onclick": genericOnClick}
            );
            chrome.contextMenus.create
            (
                {"id": "button2", "title": "2", "contexts":["all"], "onclick": genericOnClick}
            );
            chrome.contextMenus.create
            (
                {"id": "button3", "title": "3", "contexts":["all"], "onclick": genericOnClick}
            );

            };
        }

        return true; 
    };


function genericOnClick(info, tab) {
  //do some crap here
  chrome.contextMenus.removeAll();
}

【问题讨论】:

    标签: javascript google-chrome google-chrome-extension contextmenu


    【解决方案1】:

    到目前为止 "all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher" 是仅支持的上下文。不能为 Object 的每个类进行自定义。

    解决方法

    如果您希望显示上下文菜单操作,只有当用户右键单击页面上的某个 div.class 时才使用鼠标悬停在此处显示的菜单

    【讨论】:

    • 您能否进一步详细说明解决方法?正如我所说,我是网络编程的新手 :)
    • @r3x:检查 this 以获得良好的开端
    【解决方案2】:

    在this related answer 中,我解释了上下文菜单项不能即时创建,因为contextmenu 事件和上下文菜单项出现之间的时间不足以在两者之间获得chrome.contextMenus.create 调用.

    另一个答案解释了如何确保上下文菜单条目显示选定的文本。这是通过收听selectionchange 事件来完成的。出于您的目的,我们希望使用具有所需时间的事件。

    我将使用mouseover 和mouseout 事件。根据鼠标事件,当您使用键盘时,上下文菜单将不起作用,例如通过使用 JavaScript 或 Tab 键聚焦元素,然后按上下文菜单键。我没有在下面的解决方案中实现它。

    演示包含三个文件(加上一个 HTML 页面来测试它)。我将所有文件放在一个 zip 文件中,地址为 https://robwu.nl/contextmenu-dom.zip。

    manifest.json

    每个 Chrome 扩展程序都需要此文件才能工作。对于此应用程序,使用了背景页面和内容脚本。另外,需要contextMenus 权限。

    {
        "name": "Contextmenu based on activated element",
        "description": "Demo for https://stackoverflow.com/q/14829677",
        "version": "1",
        "manifest_version": 2,
        "background": {
            "scripts": ["background.js"]
        },
        "content_scripts": [{
            "run_at": "document_idle",
            "js": ["contentscript.js"],
            "matches": ["<all_urls>"]
        }],
        "permissions": [
            "contextMenus"
        ]
    }
    

    background.js

    后台页面将代表内容脚本创建上下文菜单。这是通过将事件监听器绑定到chrome.runtime.onConnect 来实现的,它提供了一个简单的界面来替换上下文菜单条目。

    chrome.contextMenus.create 会在通过此端口(来自内容脚本)接收到消息时调用。除onclick 之外的所有属性都是JSON 可序列化的,因此只有“onclick”处理程序需要特殊处理。我使用字符串来识别字典中的预定义函数 (var clickHandlers)。

    var lastTabId;
    // Remove context menus for a given tab, if needed
    function removeContextMenus(tabId) {
        if (lastTabId === tabId) chrome.contextMenus.removeAll();
    }
    // chrome.contextMenus onclick handlers:
    var clickHandlers = {
        'example': function(info, tab) {
            // This event handler receives two arguments, as defined at
            // https://developer.chrome.com/extensions/contextMenus#property-onClicked-callback
    
            // Example: Notify the tab's content script of something
            // chrome.tabs.sendMessage(tab.id, ...some JSON-serializable data... );
    
            // Example: Remove contextmenus for context
            removeContextMenus(tab.id);
        }
    };
    
    chrome.runtime.onConnect.addListener(function(port) {
        if (!port.sender.tab || port.name != 'contextMenus') {
            // Unexpected / unknown port, do not interfere with it
            return;
        }
        var tabId = port.sender.tab.id;
        port.onDisconnect.addListener(function() {
            removeContextMenus(tabId);
        });
        // Whenever a message is posted, expect that it's identical to type
        // createProperties of chrome.contextMenus.create, except for onclick.
        // "onclick" should be a string which maps to a predefined function
        port.onMessage.addListener(function(newEntries) {
            chrome.contextMenus.removeAll(function() {
                for (var i=0; i<newEntries.length; i++) {
                    var createProperties = newEntries[i];
                    createProperties.onclick = clickHandlers[createProperties.onclick];
                    chrome.contextMenus.create(createProperties);
                }
            });
        });
    });
    
    // When a tab is removed, check if it added any context menu entries. If so, remove it
    chrome.tabs.onRemoved.addListener(removeContextMenus);
    

    contentscript.js

    此脚本的第一部分创建了用于创建上下文菜单的简单方法。
    在最后 5 行中,上下文菜单条目被定义并绑定到与给定选择器匹配的所有当前和未来元素。正如我在上一节中所说,参数类型与createProperties argument of chrome.contextMenus.create 相同,除了“onclick”,它是一个映射到后台页面中的函数的字符串。

    // Port management
    var _port;
    var getPort = function() {
        if (_port) return _port;
        _port = chrome.runtime.connect({name: 'contextMenus'});
        _port.onDisconnect.addListener(function() {
            _port = null;
        });
        return _port;   
    }
    
    // listOfCreateProperties is an array of createProperties, which is defined at
    // https://developer.chrome.com/extensions/contextMenus#method-create
    // with a single exception: "onclick" is a string which corresponds to a function
    // at the background page. (Functions are not JSON-serializable, hence this approach)
    function addContextMenuTo(selector, listOfCreateProperties) {
        // Selector used to match an element. Match if an element, or its child is hovered
        selector = selector + ', ' + selector + ' *';
        var matches;
        ['matches', 'webkitMatchesSelector', 'webkitMatches', 'matchesSelector'].some(function(m) {
            if (m in document.documentElement) {
                matches = m;
                return true;
            }
        });
        // Bind a single mouseover+mouseout event to catch hovers over all current and future elements.
        var isHovering = false;
        document.addEventListener('mouseover', function(event) {
            if (event.target && event.target[matches](selector)) {
                getPort().postMessage(listOfCreateProperties);
                isHovering = true;
            } else if(isHovering) {
                getPort().postMessage([]);
                isHovering = false;
            }
        });
        document.addEventListener('mouseout', function(event) {
            if (isHovering && (!event.target || !event.target[matches](selector))) {
                getPort().postMessage([]);
                isHovering = false;
            }
        });
    }
    
    // Example: Bind the context menus to the elements which contain a class attribute starts with "story"
    addContextMenuTo('[class^=story]', [
        {"id": "butto1", "title": "1", "contexts":["all"], "onclick": 'example'},
        {"id": "button2", "title": "2", "contexts":["all"], "onclick": 'example'},
        {"id": "button3", "title": "3", "contexts":["all"], "onclick": 'example'}
    ]);
    

    前面的代码假定所有上下文菜单点击都由后台页面处理。如果您想处理内容脚本中的逻辑,则需要在内容脚本中绑定消息事件。我在background.js 示例中展示了chrome.tabs.sendMessage 的(已注释的)实例,以显示应在何处触发此事件。

    如果您需要识别触发事件的元素,请不要使用我的示例中所示的预定义函数(在字典中),而是使用内联函数或工厂函数。要识别元素,消息需要与唯一标识符配对。我将把创建这个实现的任务留给读者(这并不难)。

    【讨论】:

    • 刚刚检查了测试 HTML(感谢您添加了那个顺便说一句,让我的事情变得更容易),并且扩展在您的测试页面上运行良好,但在我目前正在为其编写扩展的网站上运行不正常.向您发送了一封包含所有详细信息的电子邮件。一旦它在该站点上运行,赏金将属于您。提前致谢
    • @r3x 在您的电子邮件中,您显示了一个页面,其中在加载文档时该元素不存在。由于上一版本的代码在执行时将事件绑定到元素,因此后来添加的元素并没有获得所需的事件侦听器。我已经更新了使用事件委托的答案,这意味着事件绑定到一个公共父节点,在那里接收和过滤大量事件以找到正确的事件。
    • 干得好 :) 接受了你的回答,但有一个小错误,当你右键单击匹配的测试后,右键单击不匹配的测试时,上下文菜单仍会显示。一旦修好,一切都会完美。向您发送了一封包含问题描述和视频的电子邮件。
    • 现在一切正常。等待期结束后(8 小时内),赏金将归您所有。谢谢!
    猜你喜欢
    • 2019-05-08
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多