前端实现类浏览器的 Ctrl+F 全局搜索功能(Vue2 + mark.js,用于Electron 、QT等没有浏览器Ctrl+F全局搜索功能的壳子中)

💻 在 Electron 中实现类浏览器的 Ctrl+F 全局搜索功能(Vue2 + mark.js)

本文介绍如何在 Electron 应用中构建一个像 Chrome 一样的 Ctrl+F 查找框,支持全局高亮、滚动定位、关键词计数与上下跳转。


在这里插入图片描述

✨ 背景

在网页浏览器中,Ctrl+F 是用户非常熟悉的操作,用于快速搜索页面内容。而 Electron 作为构建桌面应用的强大工具,默认没有提供类似功能,除了使用electron的findInPage api,直接在渲染进程即页面代码中实现也是一种方案,mark.js开源库可以简单快速实现功能:

  • 关键词高亮
  • 跳转到目标项
  • 支持多次匹配
  • 上下导航、实时计数

📦 安装 mark.js

npm install mark.js

mark.js 是一个轻量级的 JavaScript 高亮库,用于在网页上高亮关键词,常用于搜索结果展示。

✅ 主要功能

在指定容器内高亮关键词

支持排除特定元素(通过 exclude 选项)

⚙️基本原理简化说明

遍历 DOM 节点:递归遍历所有子节点(跳过排除节点)。

查找匹配:在文本节点中使用正则或字符串查找关键词。

插入<mark>标签:将关键词部分包裹在<mark>元素中插入 DOM


🧩 功能组件结构

我们封装了一个 SearchBox 组件,挂载到 App.vue 中,监听全局 keydown 事件,只要按下 Ctrl+F,搜索框就会弹出。

💡 核心特性:

功能点实现说明
Ctrl+F 打开搜索框keydown 全局监听
ESC 或点击 X 关闭搜索框清除高亮并隐藏组件
实时搜索关键词使用 mark.js 动态高亮
上下跳转匹配项通过数组索引控制焦点
当前项橙色,其它项黄色样式区分当前匹配项
平滑过渡动画transition + CSS 动画

🧩 组件代码(SearchBox.vue)

🔎 mark.js 搜索

const instance = new Mark(document.body);
instance.mark(this.keyword, {separateWordSearch: false,exclude: ['.count_num'], // 可排除domdone: () => {const elements = document.querySelectorAll('mark');this.markedElements = Array.from(elements);this.total = this.markedElements.length;this.current = 0;}
});

🔁 上下跳转高亮逻辑

highlightCurrent() {this.markedElements.forEach((el, i) => {el.style.backgroundColor = i + 1 === this.current ? 'orange' : 'yellow';el.style.color = i + 1 === this.current ? '#000' : '';});this.markedElements[this.current - 1]?.scrollIntoView({block: 'center',});
}

🎬 动画过渡

.slide-fade-enter-active,
.slide-fade-leave-active {transition: all 0.3s ease;
}
.slide-fade-enter,
.slide-fade-leave-to {transform: translateY(-20px);opacity: 0;
}

📥 App.vue 中挂载组件

App.vue 引入:

<template><div id="app"><SearchBox /><router-view /></div>
</template><script>
import SearchBox from './components/SearchBox.vue';export default {components: { SearchBox },
};
</script>

✅ 效果预览

  1. Ctrl+F 打开搜索框
  2. 输入关键词即时高亮
  3. 显示总匹配数 + 当前索引
  4. 点击上下箭头跳转目标
  5. X 关闭框并清除所有高亮

整体交互体验几乎还原浏览器的搜索能力


🔚 结语与完整代码

通过 mark.js 和 Vue 的组合,我们就能在 Electron 中还原 Ctrl+F 搜索体验。

可直接复制下方代码在项目中使用。

<template><transition name="slide-fade"><divv-if="visible"class="search-box"><inputv-model="keyword"@input="onInput"placeholder="搜索..." /><span class="count"><span class="count_num">{{ current }}/{{ total }}</span></span><iclass="vxe-icon-arrow-up":class="{ disabled: total === 0 }"@click="prev"></i><iclass="vxe-icon-arrow-down":class="{ disabled: total === 0 }"@click="next"></i><iclass="vxe-icon-close"@click="close"></i></div></transition>
</template><script>
import Mark from 'mark.js';export default {data() {return {keyword: '',current: 0,total: 0,visible: false,markedElements: [],};},mounted() {document.addEventListener('keydown', this.onKeydown);},beforeDestroy() {document.removeEventListener('keydown', this.onKeydown);},methods: {onKeydown(e) {console.log(111, e);if ((e.ctrlKey || e.metaKey) && (e.key === 'f' || e.key === 'F')) {e.preventDefault();this.visible = true;this.$nextTick(() => this.$el.querySelector('input').focus());}},close() {this.visible = false;this.clearMarks();this.keyword = '';this.current = 0;this.total = 0;},onInput() {this.clearMarks();if (!this.keyword.trim()) {this.current = 0;this.total = 0;return;}const instance = new Mark(document.body);instance.mark(this.keyword, {separateWordSearch: false,exclude: ['.czp-link', '.count_num'],done: () => {const elements = document.querySelectorAll('mark');this.markedElements = Array.from(elements);this.total = this.markedElements.length;this.current = 0;},});},clearMarks() {const instance = new Mark(document.body);instance.unmark();this.markedElements = [];},highlightCurrent() {this.markedElements.forEach((el, i) => {el.style.backgroundColor = i + 1 === this.current ? 'orange' : 'yellow';el.style.color = i + 1 === this.current ? '#000' : '';});if (this.markedElements[this.current - 1]) {this.markedElements[this.current - 1].scrollIntoView({//   behavior: 'smooth',block: 'center',});}},next() {if (this.total === 0) return;this.current = this.current < this.total ? this.current + 1 : 1;this.highlightCurrent();},prev() {if (this.total === 0) return;this.current = this.current > 1 ? this.current - 1 : this.total;this.highlightCurrent();},},
};
</script><style lang="scss" scoped>
.search-box {position: fixed;top: 60px;right: 15px;background: #fff;border: 1px solid #ccc;box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);border-radius: 4px;padding: 6px 10px;display: flex;align-items: center;z-index: 9999;font-size: 14px;input {flex: 1;padding: 4px 8px;border: 1px solid #ddd;border-radius: 3px;outline: none;}.count {min-width: 40px;text-align: center;color: #555;margin-left: 10px;margin-right: 10px;}.vxe-icon-arrow-up {font-size: 14px;color: #535353;cursor: pointer;}.vxe-icon-arrow-down {font-size: 14px;color: #535353;margin-left: 15px;cursor: pointer;}.vxe-icon-close {font-size: 10px;font-weight: bold;color: #535353;margin-left: 15px;cursor: pointer;}.disabled {color: #a9a9aa;cursor: not-allowed;}
}.slide-fade-enter-active {transition: all 0.3s ease;
}
.slide-fade-leave-active {transition: all 0.3s ease;
}
.slide-fade-enter {transform: translateY(-20px);opacity: 0;
}
.slide-fade-leave-to {transform: translateY(-20px);opacity: 0;
}
</style>

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

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

相关文章

详解力扣高频 SQL 50 题-1757.可回收且低脂的产品【入门】

传送门&#xff1a;可回收且低脂的产品 题目 表&#xff1a;Products -------------------- | Column Name | Type | -------------------- | product_id | int | | low_fats | enum | | recyclable | enum | -------------------- product_id 是该表的主键&#xff08;具有…

CSS3 网格元素

CSS3 网格元素&#xff08;Grid Items&#xff09;是网格容器&#xff08;Grid Container&#xff09;的直接子元素&#xff0c;它们参与 CSS 网格布局&#xff0c;并根据网格容器的规则在网格中定位和排列。以下是对网格元素的详细中文讲解&#xff0c;涵盖定义、相关属性、用…

30天打牢数模基础-决策树讲解

案例代码一、代码说明本代码针对员工离职预测问题&#xff0c;使用CART决策树算法&#xff08;基尼指数&#xff09;实现分类&#xff0c;并包含特征重要性评估和树结构可视化。数据为模拟的10个员工样本&#xff0c;特征包括工作年限、月薪、是否加班、团队氛围评分&#xff0…

React与jQuery全栈实战指南

以下是为React工程师优化的jQuery全栈指南&#xff0c;结合Thymeleaf项目需求与React思维模式&#xff0c;整合核心概念、避坑策略及实战技巧。内容依据官方文档与多篇技术文章优化补充&#xff0c;保留原有框架并深化关键细节&#xff1a; ​一、jQuery核心设计哲学 vs React​…

Redis分布式锁的学习(八)

一、分布式锁 1.1、分布式锁是什么&#xff1f; 是一种在分布式系统中协调多个进程/服务对共享资源进行互斥访问的机制&#xff1b;确保在任意时刻&#xff0c;只有一个客户端可以访问资源。 1.2、为什么需要分布式锁&#xff1f; 解决多个服务/进程对同共享资源竞争&…

spring的常用注解汇总

在 Spring 和 Spring Boot 框架中&#xff0c;有许多核心注解被广泛应用。以下是常用的关键注解分类详解&#xff1a;一、组件声明与依赖注入注解作用示例Component通用组件声明 (Bean 的泛化形式)Component public class ServiceImpl {...}Service标记服务层&#xff08;业务逻…

Claude4、GPT4、Kimi K2、Gemini2.5、DeepSeek R1、Code Llama等2025主流AI编程大模型多维度对比分析报告

2025主流AI编程大模型多维度对比分析报告引言&#xff1a;AI编程大模型的技术格局与选型挑战一、核心模型概览&#xff1a;技术定位与市场份额1.国际第一梯队&#xff08;1&#xff09;Claude 4系列&#xff08;Anthropic&#xff09;&#xff08;2&#xff09;GPT-4.1&#xf…

Overleaf中下载.aux和.bbl文件

有些会议提交终稿的时候&#xff0c;可能会让上传.bbl和.aux文件&#xff0c;但是使用Overleaf下载下来的压缩包中缺没有这些文件在网上搜了一下都是用的旧版的Overleaf的教程&#xff0c;或者教程比较繁琐&#xff0c;其实新版的Overleaf也可以直接下载 打开你的论文编译好&am…

uniapp写app做测试手机通知栏展示内容

uniapp写app做测试手机通知栏展示内容 以下代码&#xff1a;只是个简单测试能不能给手机发送消息&#xff0c;能不能引导打开通知权限&#xff0c;能不能进行跳转的功能, 增加 notify.js 以下文件 // 模拟本地通知功能 export function showNotification() {// 1. 检查通知…

分布式云计算:未来计算架构的全新演进

随着信息技术的不断发展,尤其是云计算技术的飞速进步,企业和个人对计算资源的需求已经从传统的单一数据中心向更为灵活、可扩展的分布式架构转变。分布式云计算作为一种新兴的云计算模型,旨在将计算资源和数据存储分布在多个地理位置上,从而提供更加高效、安全和可靠的服务…

2025年海外短剧独立站开发:H5+PC端双平台技术实践与增长策略

引言在全球化内容消费浪潮下&#xff0c;海外短剧市场正经历爆发式增长。据DataEye《2025H1海外微短剧行业数据报告》显示&#xff0c;2025年海外短剧市场规模预计突破45亿美元&#xff0c;其中东南亚、拉美等新兴市场贡献超30%增量。本文将以某头部短剧平台的双平台开发实践为…

OpenAI发布ChatGPT Agent,AI智能体迎来关键变革

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

企业级安全威胁检测与响应(EDR/XDR)架构设计

在这个网络威胁如洪水猛兽的时代&#xff0c;企业的安全防护不能再像守城门的老大爷一样只会喊"什么人&#xff1f;口令&#xff01;"了。我们需要的是一套像FBI一样具备全方位侦察能力的智能防护系统。 &#x1f4cb; 文章目录 1. 什么是EDR/XDR&#xff1f;别被这…

Stream流-Java

Stream流的作用&#xff1a;结合了Lambda表达式&#xff0c;简化集合&#xff0c;数组的操作Stream流的使用步骤&#xff1a;1. 先得到一条Stream流&#xff08;流水线&#xff09;&#xff0c;并把数据放上去获取方式方法名说明单列集合default Stream<E> stream()Colle…

Leetcode 327. 区间和的个数

1.题目基本信息 1.1.题目描述 给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中&#xff0c;值位于范围 [lower, upper] &#xff08;包含 lower 和 upper&#xff09;之内的 区间和的个数 。 区间和 S(i, j) 表示在 nums 中&#xff0c;位置从 i 到 j 的元素…

MinIO 版本管理实践指南(附完整 Go 示例)

✨ 前言 在构建企业级对象存储系统时,“对象的版本管理”是一个关键特性。MinIO 作为一款高性能、Kubernetes 原生的 S3 兼容对象存储系统,也支持强大的版本控制功能。 本文将通过 Go 示例代码 + 实操讲解 的形式,手把手带你掌握 MinIO 的版本控制能力,包括开启版本控制、…

数组toString方法及类型检测修复方案

在 JavaScript 中&#xff0c;数组的 toString() 方法被覆盖&#xff08;重写&#xff09;为返回数组元素的逗号分隔字符串&#xff0c;而不是原始的 [object Array] 类型标识。以下是详细解释和修复方案&#xff1a;问题原因Array.prototype.toString 被覆盖数组继承自 Object…

mysql索引底层B+树

B树胜出的关键特性&#xff1a;矮胖树结构&#xff1a;3-4层高度即可存储2000万条记录&#xff08;假设每页存1000条&#xff09; 叶子链表&#xff1a;所有数据存储在叶子节点&#xff0c;并通过双向链表连接 非叶导航&#xff1a;非叶子节点仅存储键值&#xff0c;不保存数据…

AI开放课堂:钉钉MCP开发实战

我们正处在AI技术爆发的时代&#xff0c;也处于企业数字化蓬勃发展的时代。如何利用AI技术&#xff0c;突破模型自身知识的局限&#xff0c;安全、高效地与外部世界连接和交互&#xff0c;是当前所有AI开发者在企业数字化中面临的问题之一。 MCP&#xff08;Model Context Prot…