3.3 右键菜单

当用户在网页中点击鼠标右键后,会唤出一个菜单,在上面有复制、粘贴和翻译等选项,为用户提供快捷便利的功能。Chrome也将这里开放给了开发者,也就是说我们可以把自己所编写的扩展功能放到右键菜单中。

要将扩展加入到右键菜单中,首先要在Manifest的permissions域中声明contextMenus权限。

"permissions": [
    "contextMenus"
]

同时还要在icons域声明16像素尺寸的图标,这样在右键菜单中才会显示出扩展的图标。

"icons": {
    "16": "icon16.png"
}

Chrome提供了三种方法操作右键菜单,分别是createupdateremove,对应于创建、更新和移除操作。

通常create方法由后台页面来调用,即通过后台页面创建自定义菜单。如果后台页面是Event Page,通常在onInstalled事件中调用create方法。

右键菜单提供了4种类型,分别是普通菜单、复选菜单、单选菜单和分割线,其中普通菜单还可以有下级菜单。连续相邻的单选菜单会被自动认为是对同一设置的选项,同时单选菜单会自动在两端生成分割线。下面的代码生成了一系列的菜单:

chrome.contextMenus.create({
    type: 'normal',
    title: 'Menu A',
    id: 'a'
});

chrome.contextMenus.create({
    type: 'radio',
    title: 'Menu B',
    id: 'b',
    checked: true
});

chrome.contextMenus.create({
    type: 'radio',
    title: 'Menu C',
    id: 'c'
});

chrome.contextMenus.create({
    type: 'checkbox',
    title: 'Menu D',
    id: 'd',
    checked: true
});

chrome.contextMenus.create({
    type: 'separator'
});

chrome.contextMenus.create({
    type: 'checkbox',
    title: 'Menu E',
    id: 'e'
});

chrome.contextMenus.create({
    type: 'normal',
    title: 'Menu F',
    id: 'f',
    parentId: 'a'
});

chrome.contextMenus.create({
    type: 'normal',
    title: 'Menu G',
    id: 'g',
    parentId: 'a'
});

上面的代码生成的菜单如下图所示。

enter image description here
自定义右键菜单

我们还可以定义自定义的右键菜单在何时显示,比如当用户选择文本时,或者在超级链接上单击右键时。下面的代码定义当用户在超级链接上点击右键时,在菜单中显示“My Menu”菜单:

chrome.contextMenus.create({
    type: 'normal',
    title: 'My Menu',
    contexts: ['link']
});

contexts域的值是数组型的,也就是说我们可以定义多种情况下显示自定义菜单,完整的选项包括allpageframeselectionlinkeditableimagevideoaudiolauncher,默认情况下为page,即在所有的页面唤出右键菜单时都显示自定义菜单。其中launcher只对Chrome应用有效,如果包含launcher选项,则当用户在chrome://apps/或者其他地方的应用图标点击右键,将显示相应的自定义菜单。需要注意的是,all选项不包括launcher

有时我们不仅想在特定的情况下显示自定义菜单,还希望限定URL,chrome同样提供了匹配URL的选项。documentUrlPatterns允许限定页面的URL,比如我们可以限定只在Google的网站上显示自定义菜单;targetUrlPatternsdocumentUrlPatterns差不多,但它所限定的不是标签的URL,而是诸如图片、视频和音频等资源的URL。

如果在创建菜单时,定义了onclick域,则菜单被点击后就会调用onclick指定的函数。调用的函数会接收到两个参数,分别是点击后的相关信息和当前标签信息。点击后的相关信息包括菜单id、上级菜单id、媒体类型(imagevideoaudio)、超级链接目标、媒体URL、页面URL、框架URL、选择的文字、是否可编辑(只针对text inputtextarea等控件)、用户点击前是否被选中和当前是否被选中(只针对checkboxradio)。完整的信息结构可以通过http://developer.chrome.com/extensions/contextMenus#type-OnClickData查看。

update方法可以动态更改菜单属性,指定需要更改菜单的id和所需要更改的属性即可。remove方法可以删除指定的菜单,removeAll方法可以删除所有的菜单。

下面我们来创建一个通过右键菜单使用Google翻译当前用户所选文本的扩展。我们希望只有当用户选择了文本才显示这个菜单,所以要将contexts的值设为selection

chrome.contextMenus.create({
    type: 'normal',
    title: '使用Google翻译……',
    id: 'cn',
    contexts: ['selection']
});

下面来编写调用的函数。Google翻译可以通过http://translate.google.com.hk/#auto/zh-CN/{翻译文本}调用,所以只需要获取用户所选择的文本,同时打开这个URL就可以了。

function translate(info, tab){
    var url = 'http://translate.google.com.hk/#auto/zh-CN/'+info.selectionText ;
    window.open(url, '_blank');
}

现在我们把create函数补充完整,把调用函数添加进去:

chrome.contextMenus.create({
    type: 'normal',
    title: '使用Google翻译……',
    contexts: ['selection'],
    id: 'cn',
    onclick: translate
});

最后把这段代码写进background.js中,让扩展在浏览器启动后自动执行就可以了。

enter image description here
Google翻译扩展

但我们发现这样无法在菜单中动态显示用户所选择的内容,那么如何动态显示诸如用Google翻译“XXX”这样的菜单呢?首先要获取用户所选择的文本,可以通过下面的代码来实现:

window.onmouseup = function(){
    var selection = window.getSelection();
    if(selection.anchorOffset != selection.extentOffset){
        //do something
    }
}

那么这段代码在background中执行会成功吗?显然不能,因为background和当前页面并不在一个空间中,所以我们需要用content_script来注入脚本,对content_script不了解的读者可以参考2.1节的内容。content_script获取到用户所选文字后,就可以通过2.5节所讲述的内容,传递给后台页面。

enter image description here
改进后的Google翻译扩展

由于改进的部分不是本章的重点,所以就不详细讲解了,大家可以参考前面的章节1。完整的代码可以通过https://github.com/sneezry/chrome_extensions_and_apps_programming/tree/master/google_translate下载得到。

1 创建菜单时也可以直接使用%s表示选定的文字。

Chrome还提供了onClicked事件,虽然在create方法中可以指定点击时调用的函数,但对于Event Page只能通过onClicked事件调用函数。Event Page与一般的background类似,但它只按需加载,并不像background那样一直驻守后台。