编写 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 打开 ...
随机推荐
- 001-supervisor
supervisor 使用教程(转) 原文地址:https://word.gw1770df.cc/2016-08-04/linux/supervisor-%E4%BD%BF%E7%94%A8%E6%9 ...
- ARP详解和ARP攻击
1.ARP简介 地址解析协议(Address Resolution Protocol),其基本功能为透过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行.它是IPv4中网络层必不可少 ...
- tf中的meta和pb
https://www.jianshu.com/p/af2b694eae61 简单来说,pb文件是个推理的图,meta更复杂,不能用来部署
- if_else
//if.......else if......else //object IF_ELSE {// def main(args:Array[String]){// var x=30// if (x== ...
- JavaSE---显式锁
1.概述 1.1.jdk5之前,用于 调节共享对象访问机制 只有 synchronized.volatile: jdk5之后,提供了 显示锁:Lock.ReentrantLock...: ...
- LeetCode--152--乘积最大子序列(python)
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4]输出: 6解释: 子数组 [2,3] 有最大乘积 6.示例 2: 输 ...
- js 遍历数组取出字符串用逗号拼接
var arr = [{"name":"hhh"},{"name":"dddd"}] //用js function ge ...
- 事物Spring boot @Transactional
事物:dr @Override @UDS(value="fq") @Transactional public BaseResultMessage testTransactional ...
- Bugku 杂项 又一张图片,还单纯吗
又一张图片,还单纯吗 下载后,用binwalk打开图片 使用foremost 2.png进行分离 得到图片 关于foremost foremost [-v|-V|-h|-T|-Q|-q|-a|-w-d ...
- codevs 1098 均分纸牌 2002年NOIP全国联赛提高组 x
时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,但纸牌总数必 ...