Firefox 关键词高亮插件的简单实现

目录

1、配置 manifest.json 文件

2、编写侧边栏结构

3、查找关键词并高亮的方法

3-1) 如果直接使用 innerHTML 进行替换

4、清除关键词高亮

5、页面脚本代码

6、参考


1、配置 manifest.json 文件

{"manifest_version": 2,"name": "key_word_plugin","version": "1.0","description": "find_key_word",// 添加权限"permissions":["*://*/*","activeTab"],"icons": {"48": "icons/flower.jpg"},"content_scripts": [{"matches": ["*://*/*"],"js": ["index.js"],"run_at":"document_idle"}],// 侧边栏"sidebar_action": {"default_title": "My tool","default_panel": "./sidebar/sidebar.html","default_icon": "./sidebar/sidebar_icon.png"},// 背景脚本"background": {"scripts": ["bg.js"],"persistent": false,"type": "module"}
}

 

2、编写侧边栏结构

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>/* 略 */</style><link rel="stylesheet" href="./top_area.css">
</head>
<body><div class="container"><!-- 关键词查找 --><div class="top-area"><section class="inp-area"><input class="inp" type="text" maxlength="10"><button class="find-btn">查找</button></section><section class="result-area"><p>共找到</p><p class="count"><!-- 将查找到的结果条目数量写入此处 --></p><p>处;</p></section><section class="btn-area"><input type="number" step="1" min="1" class="goto-keyword-inp usable"><button class="usable goto-btn">跳转</button></section><section class="btn-area"><button class="usable last-btn">上一个</button><button class="usable next-btn">下一个</button><button class="clear">清除所有标记</button></section></div></div>
</body>
<script src="keyword.js"></script>
</html>

        效果图 

 

3、查找关键词并高亮的方法

    // 获取当前激活的标签页面 使用 tabs 需要权限 activeTab,在 manifest 中配置browser.tabs.query({active: true, currentWindow: true}).then((logTabs,onError)=>{// 然后往当前页面中注入内容脚本,document将是当前页面的 documentbrowser.tabs.executeScript({code:`
(function action(keyword, nodes){Array.from(nodes).forEach(node =>{let {nodeType, data : content} = node;if(nodeType === 3 && content.includes(keyword)){let parentNode = node.parentNode;let split_arr = node.data.trim().replaceAll(keyword,'-' + keyword + '-').split('-').filter(e => e);for(let item of split_arr){if(item === keyword){let strong = document.createElement('strong')strong.innerText = keyword;strong.classList.add('${KEYWORD_CLASS_NAME}')strong.style = '${__style}'parentNode.insertBefore(strong, node)}else{let text = document.createTextNode(item);parentNode.insertBefore(text, node)}}parentNode.removeChild(node)}else if(nodeType === 1 && node.textContent.includes(keyword)){action(keyword, node.childNodes)}})
})('${keyword}', Array.from(document.body.childNodes).filter((e)=>{return (e.textContent.includes('${keyword}') &&e.tagName !== 'SCRIPT')
}))
document.querySelectorAll('.__keyword_word__').length`}).then((onExecuted, onError)=>{
// onExecuted[0] 的内容就是document.querySelectorAll('.__keyword_word__').length的结果total = onExecuted[0]})})

        点击查找关键词后,页面脚本向当前的页面注入一段JavaScript代码。该代码包含一个立即执行的函数 和 一个关键词数量的获取。

        该立即执行的函数 action,接收一个 要匹配的关键词 keyword 和 当前搜索节点数组 nodes 作为参数。

       遍历每一个节点,取出节点的类型-->nodeType 和节点的文本内容 -->content。

        如果是纯文本节点,则该节点的 nodeType 为3,如果是元素节点,则为 1。

        如果有纯文本节点,并且该纯文本节点中的内容包含了关键词,那么构造出一个数组,使用该数组来区分非关键词内容和关键词内容,以及他们之间的位置关系。

let split_arr = content.trim().replaceAll(keyword, '-' + keyword + '-').split('-').filter(e => e);

        如关键词为 我们 ,纯文本节点的内容为:

我们的征途是星辰大海,请和我们一起,永远相信美好的事情即将发生

        那么构造的数组为:

[ "我们",  "的征途是星辰大海,请和",  "我们",  "一起,永远相信美好的事情即将发生"

    if(nodeType === 3 && content.includes(keyword)){let parentNode = node.parentNode;let split_arr = content.trim().replaceAll(keyword,'-' + keyword + '-').split('-        ').filter(e => e);for(let item of split_arr){if(item === keyword){let strong = document.createElement('strong')strong.innerText = keyword;strong.classList.add('${KEYWORD_CLASS_NAME}')strong.style = '${__style}'parentNode.insertBefore(strong, node)}else{let text = document.createTextNode(item);parentNode.insertBefore(text, node)}}parentNode.removeChild(node)}

          遍历构造的数组中的内容,如果当前值等于关键词,那么构造一个强调标签 Strong 将关键词作为 innerText,并添加指定的样式和样式类名,然后加入到当前所遍历的节点之前;如果该当前值与关键词不相等,则直接构造一个文本节点,将其添加到当前所遍历的节点之前......

        当遍历完构造的数组后,将当前遍历的节点从其父节点中删除。这样就将纯文本节点中的内容全部高亮处理了。

        没有包含关键词的纯文本节点直接跳过。 

        如果该节点不是纯文本结点,那么判断其 textContent 中是否包含关键词,如果是,那么让其所有子节点再参与 action 处理。否则就不用继续递归。

3-1) 如果直接使用 innerHTML 进行替换

如果标签中的属性出现了关键词,则会出现标签结构混乱的问题:

原代码: 

<body><div class="my_name"><img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.duz6S7Fvygrqd6Yj_DcXAQHaF7?rs=1&pid=ImgDetMain" alt="我的图片"><p>我的图片</p><div>你的图片<p>我们的图片</p><span>都是</span>图片</div></div><script>document.body.innerHTML = document.body.innerHTML.replaceAll('图片','<strong style="color:red">图片</strong>')</script>
</body>

 

4、清除关键词高亮

browser.tabs.query({active: true, currentWindow: true}).then(()=>{browser.tabs.executeScript({code:`
(function action(keyword){document.querySelectorAll('.${KEYWORD_CLASS_NAME}').forEach(e =>{let parent = e.parentNode;let textNode = document.createTextNode(keyword);parent.replaceChild(textNode, e)})
})('${keyword}')`})})

        获取到所有 strong 强调标签(根据自定义的 class 名称),然后进行遍历,获取到每一个strong 的父元素。使用 createTextNode 创建一个纯文本节点,其内容就是关键词。然后将该文本节点替换掉 strong 标签即可。

5、页面脚本代码

// 简单封装document.querySelector
const getFirstEle = sign => document.querySelector(sign);// 关键词
var KEYWORD = '';// 总共找到多少处
var total = 0;  
const count_ele = getFirstEle('.count')
count_ele.innerText = '____'const KEYWORD_CLASS_NAME = '__keyword_word__'
const __style = `color: #b60404; background-color: #f9f906; text-decoration: underline; text-decoration-style: double;`
var INDEX = null;               // 当前记录的关键词索引,用于跳转 [1 ~ total]const find_btn = getFirstEle('.find-btn');
const clear_btn = getFirstEle('.clear');
const last_btn = getFirstEle('.last-btn');
const next_btn = getFirstEle('.next-btn');
const goto_keyword_inp = getFirstEle('.goto-keyword-inp')
const goto_btn = getFirstEle('.goto-btn')// 控制关键词跳转是否可用
const usables = document.querySelectorAll('.usable');
const set_usable = (res)=>{ usables.forEach(e => { e.disabled = !res; }) }// 默认不可用
set_usable(false);// 点击查找关键词
find_btn.addEventListener('click', (e)=>{// 获取用户的输入let keyword = document.querySelector('.inp').value.trim()if(!keyword) return;// 获取上次的关键词let last_keyword = sessionStorage.getItem('_keyword_');// 如果上次查找的关键词存在并且与当前的关键词相等if(last_keyword && last_keyword === keyword){ return; }// 如果上次的关键词与当前的关键词不相等,那么页面的高亮没有被清理// 因为上次的关键词session中没有被清除。先清理页面残留else if(last_keyword && last_keyword !== keyword){clear_action(last_keyword, false, false, false)}// 更新关键词sessionStorage.setItem('_keyword_', keyword)KEYWORD = keyword;// 获取当前激活的标签页面 使用 tabs 需要权限 activeTab,在 manifest 中配置browser.tabs.query({active: true, currentWindow: true}).then((logTabs,onError)=>{// 然后往当前页面中注入内容脚本,document将是当前页面的 documentbrowser.tabs.executeScript({code:`
(function action(keyword, nodes){Array.from(nodes).forEach(node =>{let {nodeType, textContent : content} = node;if(nodeType === 3 && content.includes(keyword)){let parentNode = node.parentNode;let split_arr = content.trim().replaceAll(keyword,'-' + keyword + '-').split('-').filter(e => e);for(let item of split_arr){if(item === keyword){let strong = document.createElement('strong')strong.innerText = keyword;strong.classList.add('${KEYWORD_CLASS_NAME}')strong.style = '${__style}'parentNode.insertBefore(strong, node)}else{let text = document.createTextNode(item);parentNode.insertBefore(text, node)}}parentNode.removeChild(node)}else if(nodeType === 1 && content.includes(keyword)){action(keyword, node.childNodes)}})
})('${keyword}', Array.from(document.body.childNodes).filter((e)=>{return (e.textContent.includes('${keyword}') &&e.tagName !== 'SCRIPT')
}))
document.querySelectorAll('.__keyword_word__').length`}).then((onExecuted, onError)=>{total = onExecuted[0]count_ele.innerText = total;// 开启跳转功能if(total > 0) set_usable(true);})})
})// 点击清除按钮 回归页面原始的状态
clear_btn.addEventListener('click', ()=>{let keyword = sessionStorage.getItem('_keyword_');clear_action(keyword)
})// 清除关键词标记
const clear_action = (keyword, clear_inp=true, clear_keyword_session=true, clear_count=true)=>{browser.tabs.query({active: true, currentWindow: true}).then(()=>{browser.tabs.executeScript({code:`
(function action(keyword){document.querySelectorAll('.${KEYWORD_CLASS_NAME}').forEach(e =>{let parent = e.parentNode;let textNode = document.createTextNode(keyword);parent.replaceChild(textNode, e)})
})('${keyword}')`})})if(clear_inp) document.querySelector('.inp').value = '';if(clear_keyword_session) sessionStorage.setItem('_keyword_', '');if(clear_count) count_ele.innerText = '_____';set_usable(false)KEYWORD = ''goto_keyword_inp.value = ''
}// 跳转到上一个关键词位置
last_btn.addEventListener('click', ()=>{if(!INDEX) INDEX = 1;else if(INDEX <= 1 ) INDEX = total;else if(INDEX >= total) INDEX = total - 1;else INDEX --;goto_keyword_site(INDEX - 1, KEYWORD);
})// 跳转到下一个关键词位置
next_btn.addEventListener('click', ()=>{if(!INDEX) INDEX = 1;else if(INDEX <= 1) INDEX = 2;else if(INDEX >= total) INDEX = 1;else INDEX ++;goto_keyword_site(INDEX - 1, KEYWORD);
})// 跳转到指定的位置
goto_btn.addEventListener('click', ()=>{let index = parseInt(goto_keyword_inp.value)if(!index) return;if(index > total) index = total;else if(index < 1) index = 1;goto_keyword_site(index - 1)INDEX = index;
})// 跳转到具体的关键词位置
const goto_keyword_site = (index) =>{goto_keyword_inp.value = index + 1;browser.tabs.query({active: true, currentWindow: true}).then((logTabs,onError)=>{// 然后往当前页面中注入内容脚本,document将是当前页面的 documentbrowser.tabs.executeScript({code:`
document.querySelectorAll(".${KEYWORD_CLASS_NAME}")[${index}].scrollIntoView({behavior:'smooth'
})            `})})
}

6、参考

[1]: 扩展是什么? - Mozilla | MDN

[2]: Firefox插件(拓展)开发_火狐浏览器插件开发-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/296096.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【芯片验证】通关寄存器与ral_model —— 寄存器生成流程中加入backdoor后门配置

前言 【芯片验证】通关寄存器与ral_model —— backdoor后门访问实操测试-CSDN博客 上一篇文章中,我们通过在环境中配置后门路径的方式来实现了寄存器的后门访问,但是在实际应用中,无论寄存器RTL文件、例化还是寄存器模型大概率都是工具生成的,比如在本专栏中实现的gen_r…

Day57:WEB攻防-SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

目录 SSRF-原理&挖掘&利用&修复 SSRF无回显解决办法 SSRF漏洞挖掘 SSRF协议利用 http:// &#xff08;常用&#xff09; file:/// &#xff08;常用&#xff09; dict:// &#xff08;常用&#xff09; sftp:// ldap:// tftp:// gopher:// &#xff08;…

vue 内嵌第三方网页

需要将另一个系统嵌套到当前网页中 一、frame 方法一就是通过html的标签 iframe 实现网页中嵌入其他网站 标签属性 属性含义src嵌套的网页地址width设置嵌套网页的宽度&#xff0c;单位为像素height设置嵌套网页的高度&#xff0c;单位为像素frameborder控制嵌套的网页是否…

高性价比的挂耳式耳机哪个好用?五大高口碑品牌深度测评严选!

入耳式耳机虽然普及度极高&#xff0c;但其缺点也不容忽视。首先&#xff0c;长时间佩戴可能导致耳朵不适&#xff0c;甚至影响听力健康。其次&#xff0c;入耳式耳机往往因为隔音效果过好&#xff0c;导致用户与周围环境脱节&#xff0c;失去了一定的生活便利性。相比之下&…

医学图像处理 利用pytorch实现的可用于反传的Radon变换和逆变换

医学图像处理 利用pytorch实现的可用于反传的Radon变换和逆变换 前言代码实现思路实验结果 前言 Computed Tomography&#xff08;CT&#xff0c;计算机断层成像&#xff09;技术作为如今医学中重要的辅助诊断手段&#xff0c;也是医学图像研究的重要主题。如今&#xff0c;随…

2024年第三期丨全国高校大数据与人工智能师资研修班邀请函

2024年第三期 杭州线下班 数据采集与机器学习实战&#xff08;Python&#xff09; 线上班 八大专题 大模型技术与应用实战 数据采集与处理实战&#xff08;Python&八爪鱼&#xff09; 大数据分析与机器学习实战&#xff08;Python&#xff09; 商务数据分析实战&…

GridLayoutManager 中的一些坑

前言 如果GridLayoutManager使用item的布局都是wrap_cotent 那么会在布局更改时会出现一些出人意料的情况。&#xff08;本文完全不具备可读性和说教性&#xff0c;仅为博主方便查找问题&#xff09; 布局item: <!--layout_item.xml--> <?xml version"1.0&qu…

arm交叉编译器工具

下载地址&#xff1a; Builds & Downloads | Linaro 进入首页后&#xff0c;点击" GNU Toolchain Integration Builds" 有以下版本&#xff1a; 根据自己的选择下载对应的版本&#xff0c;本例选择14.0-2023.06-1 根据板端对应的版本选择相应的下载 比如下载3…

来个自定义的电子木鱼吧

<!DOCTYPE html> <html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>自定义木鱼</title> </head> <body style"background-…

R语言中的常用数据结构

目录 R对象的基本类型 R对象的属性 R的数据结构 向量 矩阵 数组 列表 因子 缺失值NA 数据框 R的数据结构总结 R语言可以进行探索性数据分析&#xff0c;统计推断&#xff0c;回归分析&#xff0c;机器学习&#xff0c;数据产品开发 R对象的基本类型 R语言对象有五…

运筹学基础(三):求解整数规划的切平面法(cutting plane method)

文章目录 算法思想一个例子参考文档 算法思想 先将整数规划问题松弛为线性规划问题&#xff0c;然后割掉线性规划问题可行域的一部分&#xff08;只包含非整数解&#xff09;&#xff0c;使得线性规划问题的最优解在原整数规划问题的可行域某顶点上取得。 因此&#xff0c;割平…

内存管理是如何影响系统的性能的

大家好&#xff0c;今天给大家介绍内存管理是如何影响系统的性能的&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 内存管理对系统性能的影响至关重要&#xff0c;主要体现在以下…

揭开AI编程语言Mojo比Pyhon快6.8万倍的5个秘密!

最近&#xff08;2024年3月29日&#xff09;&#xff0c;号称比Python快6.8万倍的Mojo编程语言开源啦&#xff01;6.8万倍&#xff1f;你敢相信这个数字是真的吗&#xff1f;不过&#xff0c;就连Mojo官网都把这个结果贴了出来&#xff08;见下图&#xff09;&#xff0c;这就很…

瀚海贫者福,铜子恣意游

上学时打饭追求性价比的习惯一直不改&#xff0c;半个大鱼头三块钱&#xff0c;一份豆腐一块钱&#xff0c;还有一个红烧茄子2块5&#xff0c;再加三毛钱的饭&#xff0c;共6块8毛钱&#xff0c;早晚餐也会有这类性价比高又营养的选择&#xff0c;科大食堂现在越来越人性化&…

计算机视觉的应用26-关于Fast-R-CNN模型的应用场景,Fast-R-CNN模型结构介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用26-关于Fast-R-CNN模型的应用场景&#xff0c;Fast-R-CNN模型结构介绍。Fast R-CNN是一种深度学习模型&#xff0c;主要用于目标检测任务&#xff0c;尤其适用于图像中物体的识别与定位。该模型在基…

go包下载时报proxyconnect tcp: dial tcp 127.0.0.1:80: connectex错误的解决方案

一大早的GoLand就开始抽风了&#xff0c;好几个文件import都红了&#xff0c;于是我正常操作点击提示的sync&#xff0c;但是却报了一堆错&#xff1a; go: downloading google.golang.org/grpc v1.61.1 go: downloading google.golang.org/genproto v0.0.0-20240228224816-df9…

阿里云数据库服务器价格表查询,2024年最新

阿里云数据库服务器价格表&#xff0c;优惠99元一年起&#xff0c;ECS云服务器2核2G、3M固定带宽、40G ESSD Entry云盘&#xff0c;优惠价格99元一年&#xff1b;阿里云数据库MySQL版2核2G基础系列经济版99元1年、2核4GB 227.99元1年&#xff0c;云数据库PostgreSQL、SQL Serve…

Java中线程详解

文章目录 相关概念多线程概念实现方式继承Thread类实现Runnable接口比较 常用方法线程安全产生的原因解决思想同步同步代码块同步方法Lock锁机制 死锁概念避免 状态线程间的通讯介绍方法 相关概念 并行&#xff1a;在同一时刻&#xff0c;有多个任务在多个CPU上同时执行并发&a…

Docker 笔记

1.Ubuntu安装Docker 安装Docker看这篇文章 http://t.csdnimg.cn/IsSsJ 2.在docker中运行python代码 2.1搭建python环境 docker部署python环境看这篇文章 http://t.csdnimg.cn/TYz0G 2.2在python shell中运行python代码 2.2.1查看镜像 2.2.1启动python&#xff0c;厦门这个…

MHA高可用-解决MySQL主从复制的单点问题

目录 一、MHA的介绍 1&#xff0e;什么是 MHA 2&#xff0e;MHA 的组成 2.1 MHA Node&#xff08;数据节点&#xff09; 2.2 MHA Manager&#xff08;管理节点&#xff09; 3&#xff0e;MHA 的特点 4. MHA工作原理总结如下&#xff1a; 二、搭建 MySQL MHA 实验环境 …