当前位置: 首页 > news >正文

前端实现类浏览器的 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.dtcms.com/a/293586.html

相关文章:

  • Oracle迁移到高斯,查询字段默认小写,解决办法
  • Qt 常用控件 - 1
  • 分布式风电分散式风电
  • Qt内存管理的核心点
  • 技术速递|使用 Semantic Kernel 与 A2A 协议构建多智能体解决方案
  • MCP:UVX的安装
  • 计算机组成原理(一)
  • 架构师--缓存场景
  • 利用DeepSeek编写一个使用lzav算法的文件压缩工具
  • 什么是LLMs.txt?如何在线生成?robots.txt一键转LLMs.txt
  • 第九讲:C++中的list与forward_list
  • OpenCV 零基础到项目实战 | DAY 2:图像预处理全解析
  • 基于STM32驱动ADS1118实现电压采样并输出到串口
  • PetaLinux 使用技巧与缓存配置
  • 从零搭建 OpenCV 项目(新手向)-- 第二天 OpenCV图像预处理(一)
  • 第四章 Freertos物联网实战DHT11温湿度模块
  • 嵌入式学习-(李宏毅)机器学习(1)-day28
  • 本地电脑映射端口到外网访问的开启方法和注意事项,内网服务提供跨网使用简单操作实现
  • 神经网络和机器学习的一些基本概念
  • 某种物联网SIM卡流量查询方法
  • BQ4050RSMR DIOTEC高精度锂电池保护与电量监测芯片 集成保护+计量+通信
  • 2025年Zigbee技术白皮书:全球物联网无线通信的关键创新
  • 【Linux | 网络】应用层(HTTPS)
  • 如何在 Ubuntu 20.04 Linux 上安装 TeamSpeak 客户端
  • SparkSQL 聚合函数 MAX 对 NULL 值的处理
  • AWS Lambda IoT数据处理异常深度分析:从告警到根因的完整排查之路
  • Notepad++工具操作技巧
  • BitDistiller:通过自蒸馏释放 Sub-4-Bit 大语言模型的潜力
  • React Native + Expo 入坑指南:从核心概念到实战演练
  • Android 测试全指南:单元测试与UI测试框架详解