编写 Chrome 扩展——contextMenus 的快捷创建
1 写在前面
最近使用 typescript 与 angular 编写 chrome 扩展, 对繁复的 contextMenus 创建步骤进行了提炼,并总结一个类
2 重构思路
2.1 一般方法
在编写 chrome 扩展中的 contextMenu 中,一般的思路是定义一个 JSON,并且遍历这个JSON数据并且以此创建 menu, 如:
const menu = {
"menus": [
{"id": "main", "visible": true, "title": "main"},
{"id": "sub1", "visible": true, "title": "sub1", "parentId": "main"},
{"id": "sub11", "visible": true, "title": "sub11", "parentId": "sub1"},
{"id": "sub12", "visible": true, "title": "sub12", "parentId": "sub1"},
{"id": "sub2", "visible": true, "title": "sub2", "parentId": "main"}
]
}; const createMenu = () => {
menu.menus.forEach(value => {
chrome.contextMenus.create(value);
})
};
createMenu();
2.2 重构
在 menu 的个数很少的情况下,上述的传统方式可能不会构成问题,但在选项多的场景下(如编写工具类扩展),contextMenu 的可读性就变得极差
2.2.1 编写 Menu 类
因此我们将用 typescript 来构建一个层次结构清晰的类以组合编排 父-子 menu 的层次结构, 类的结构如下:
interface Menu {
createProperties: CreateProperties;
children?: Menu[];
}
这个类的结构很简单,但是已经足以代表需要创建 ContextMenu 的数据
现在上边使用过的 json 数据现在可以重写为:
const defaultMenu: Menu = {
createProperties: {
id: 'main',
visible: true,
title: 'main'
},
children: [
{
createProperties: {
id: 'sub1',
visible: true,
title: 'sub1', },
children: [
{
createProperties: {
id: 'sub11',
visible: true,
title: 'sub11',
parentId: 'sub1'
}
},
{
createProperties: {
id: 'sub12',
visible: true,
title: 'sub12',
parentId: 'sub1'
}
}
]
},
{
createProperties: {
id: 'sub2',
visible: true,
title: 'sub2',
parentId: 'main'
}
}
]
};
结构似乎很清晰,但是不够完善,我们不想手动管理 menu 之间的父子关系,现在,删掉所有的 parentId 属性,我们将在接下来的节点来讲述如何动态维护关系
2.2.2 编写 collectMenuCreateProperties 方法
在上一节点中,我们创建了一个足以涵盖创建 contextMenu 的类 Menu,但是 chrome.contextMenus.create(property); 要求我们每次传入一个 CreateProperties 类,因此我们需要一个工具类从 Menu 类中抽取所有的 CreateProperties 信息,并且不要忘记了该方法必须能够动态维护 menu 间的父子关系(设置子menu 的 parentId 为上一层的 menu 的 id)
function collectMenuCreateProperties(parent: Menu): CreateProperties[] {
if (parent.createProperties.id === undefined) {
throw new Error('parent contextMenu must has id');
}
let result: CreateProperties[] = [];
result.push(parent.createProperties);
if (parent.children) {
parent.children.forEach(child => {
// 确保每一层的层级关系
child.createProperties.parentId = parent.createProperties.id;
result = result.concat(collectMenuCreateProperties(child));
});
}
return result;
}
2.2.3 编写 createMenu 方法
一切就绪,我们需要创建一个 contextMenu,这里有一些注意的点:
- 如果在文件manifest.json 中对 background.js 设置了属性 "persistent": false ,可能会出现多次创建同一个menu
- 我们的 menu 只需要创建一次,比如扩展初次安装的时候就很合适
- 当调用者忘记传入构建源 Menu 时,需要确保运行不出错,这一点我们将采用ts中的默认值
function createMenu(menu: Menu = defaultMenu): void {
chrome.runtime.onInstalled.addListener(details => {
const properties: CreateProperties[] = collectMenuCreateProperties(menu);
// alert(JSON.stringify(properties));
properties.forEach(property => {
chrome.contextMenus.create(property);
}); });
}
最后,别忘了在顶层导出
export {Menu, defaultMenu};
export {createMenu};
export {collectMenuCreateProperties};
附:
完整的代码
import CreateProperties = chrome.contextMenus.CreateProperties; export {Menu, defaultMenu};
export {createMenu};
export {collectMenuCreateProperties}; /**
* 一系列创建 {@link contextMenus} 需要的数据
* @see defaultMenu
*/
interface Menu {
createProperties: CreateProperties;
children?: Menu[];
} /**
* 默认的的数据 :
* [{"id":"main","visible":true,"title":"main"},
* {"id":"sub1","visible":true,"title":"sub1","parentId":"main"},
* {"id":"sub11","visible":true,"title":"sub11","parentId":"sub1"},
* {"id":"sub12","visible":true,"title":"sub12","parentId":"sub1"},
* {"id":"sub2","visible":true,"title":"sub2","parentId":"main"}]
*/
const defaultMenu: Menu = {
createProperties: {
id: 'main',
visible: true,
title: 'main'
},
children: [
{
createProperties: {
id: 'sub1',
visible: true,
title: 'sub1', },
children: [
{
createProperties: {
id: 'sub11',
visible: true,
title: 'sub11',
}
},
{
createProperties: {
id: 'sub12',
visible: true,
title: 'sub12',
}
}
]
},
{
createProperties: {
id: 'sub2',
visible: true,
title: 'sub2',
}
}
]
}; /**
* 主要方法
* 创建 contextMenus
* 1 监听 {@link chrome.runtime}事件,事件的实体可能是:("install", "update", "chrome_update", or "shared_module_update")
* 2 移除之前创建的所有 {@link chrome.contextMenus}
* 3 执行创建
*
* @param menu 执行创建的上下文信息
* @see Menu
* @see defaultMenu
*/
function createMenu(menu: Menu = defaultMenu): void {
chrome.runtime.onInstalled.addListener(details => {
const properties: CreateProperties[] = collectMenuCreateProperties(menu);
// alert(JSON.stringify(properties));
properties.forEach(property => {
chrome.contextMenus.create(property);
}); });
} /**
* 递归方式返回 parent 中包含的{@link CreateProperties} 对象,
* 每一层的 {@link CreateProperties.id} 必须不为空
* 并以编码的方式保证: 第二层开始, {@link CreateProperties.parentId} 被正确设置
*
* @param parent 顶层
*/
function collectMenuCreateProperties(parent: Menu): CreateProperties[] {
if (parent.createProperties.id === undefined) {
throw new Error('parent contextMenu must has id');
}
let result: CreateProperties[] = [];
result.push(parent.createProperties);
if (parent.children) {
parent.children.forEach(child => {
// 确保每一层的层级关系
child.createProperties.parentId = parent.createProperties.id;
result = result.concat(collectMenuCreateProperties(child));
});
}
return result;
}
用例:
import {Component, OnInit} from '@angular/core';
import {createMenu} from './contextMenus'; @Component({
selector: 'app-event-page',
templateUrl: './event-page.component.html',
styleUrls: ['./event-page.component.css']
}) /**
* @author siweipancc
* @version 1.0.0
*/
export class EventPageComponent implements OnInit {
ngOnInit() {
createMenu();
} }
编写 Chrome 扩展——contextMenus 的快捷创建的更多相关文章
- 编写Chrome扩展程序
Chrome的扩展程序很多,也很容易入门,可以来简单实现一下 看看,慢慢就能实现出一个扩展程序来 每个扩展程序应用一般会包含: 一个manifest清单文件 html文件 js文件 其他文件等 可以看 ...
- 【转】编写Chrome扩展程序
Chrome的扩展程序很多,也很容易入门,可以来简单实现一下 看看,慢慢就能实现出一个扩展程序来 每个扩展程序应用一般会包含: 一个manifest清单文件 html文件 js文件 其他文件等 可以看 ...
- BlazeMeter发布chrome扩展插件,支持JMeter脚本创建
BlazeMeter发布chrome扩展插件,支持JMeter脚本创建http://www.automationqa.com/forum.php?mod=viewthread&tid=3898 ...
- 了解Chrome扩展程序开发--摘抄
了解Chrome扩展程序开发 2018-01-11 边城到此莫若 鸡蛋君说前端 首先,我尝试来用简单几句话描述一下Chrome扩展程序: Chrome扩展主要用于对浏览器功能的增强,它强调与浏览器相结 ...
- 清除页面广告?身为前端,自己做一款简易的chrome扩展吧
大家肯定有这样的经历,浏览网页的时候,左右两端广告,诸如“屠龙宝刀,点击就送”,以及最近火的不行的林子聪37传奇霸业什么“霸业面具,霸业吊坠”的魔性广告总是充斥我们的眼球. 当然有现成的扩展程序或者插 ...
- Chrome扩展程序的二次开发:把它改得更适合自己使用
我当然知道未经作者允许修改别人程序是不道德的了,但作为学习研究之用还是无可厚非,这里仅供交流. 一切都是需求驱动的 话说某天我在网上猎奇的时候无意间发现这么一款神奇的谷歌浏览器插件:Extension ...
- Web 开发人员必备的12款 Chrome 扩展程序
之前已经分享过一些帮助 Web 开发人员和设计师的 Chrome 扩展,这次我们继续展示一组很有用的 Chrome 应用程序.这些免费的 Chrome 应用程序可以简化您的工作流程,为了加快您的工作流 ...
- chrome扩展程序----域名助手
音乐分享: Future Islands - <Aladdin> 中年大叔的抖腿新专辑<The Far Field> ————————————————————————————— ...
- 一起来做chrome扩展《页面右键菜单》
本文主要内容 contextMenus的设置 打开权限 创建菜单 点击菜单 background script向content script发送消息 1. contextMenus的设置 1.1 打开 ...
随机推荐
- hdu 1087 最大递增子序列和
#include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #defin ...
- 计蒜客 蓝桥模拟 I. 天上的星星
计算二维前缀和,节省时间.容斥定理. 代码: #include <cstdio> #include <cstdlib> #include <cstring> #in ...
- Stack Overflow 推荐编程书单
Stack Overflow 推荐编程书单 1 Working Effectively with Legacy Code Michael C. Feathers 修改代码是每一位软件开发人员的日常 ...
- [每日一学]apache camel|BDD方式开发apache camel|Groovy|Spock
开发apache camel应用,最好的方式就是tdd,因为camel的每个组件都是相互独立并可测试的. 现在有很多好的测试框架,用groovy的Spock框架的BDD(行为测试驱动)是比较优秀和好用 ...
- 微信 PHP - SDK 包
下载 个人公众号谢谢各位老铁支持
- 表单-angular
模板表单: <form #myform="ngForm" (ngSubmit)="onsubmit(myform.value)" > <div ...
- Python---常用的内置模块
#fsum() 对整个序列求和 返回浮点数 print(math.fsum([1,4.5,5,7])) #sum() python内置求和 print(sum([1,4,5,7])) print( ...
- R语言-变量命名规则
1.大原则:只有字母(区分大小写).数字.“_”(下划线).“.”(英文句号)可以出现. 2.数字.下划线不能开头. 3.英文句号开头不能紧接数字. 就这么简单!
- APP功能测试注意点
App功能测试的7大注意点 : APP测试 在日常工作的摸索中,我们将如何做好app测试的注意点简单归结为如下内容. 弱网测试,兼容性测试,UI测试.中断测试, 01 运行 1)App安装完成后 ...
- 获取第几个class
假如类名为a,如果是同级(兄弟元素)的,如:<ul><li class='a'></li><li class='a'></li></u ...