思路
1、先获取到搜索区域的dom和其html;
2、在html中通过正则匹配搜索的内容;
3、通过replace把匹配到的内容更换为带标签的高亮样式,比如<span style="color:red">key</span>;
4、通过scrollIntoView函数滚动到第一个高亮元素。
其他:本文通过vue3的hook方式实现。
实现代码
import { nextTick, onMounted, ref, watch } from 'vue';
export default ({ id }) => {
const search_key = ref('');// 搜索关键字
let contentDom = null;// 搜索区域dom
let contentOriginalHtml = '';// 全文初始html
const noReg = /\d|[a-zA-Z]|\<|\>|\/|\-/; // 不需要匹配的字符
// 监听搜索关键字变化-此处可添加防抖,或者导出为方法在外部调用
watch(() => search_key.value, (newVal) => {
reset();// 恢复原始html
newVal && search(newVal);// 搜索并高亮
})
// 获取dom和原始html
const getBase = () => {
nextTick(() => {
contentDom = document.getElementById(id);
contentOriginalHtml = contentDom.innerHTML;
})
}
// 恢复
const reset = () => {
contentDom.innerHTML = contentOriginalHtml;
}
// 搜索并高亮
const search = (keyword) => {
if (noReg.test(keyword)) {
return;
}
const searchRegExp = new RegExp(keyword, 'ig');
contentDom.innerHTML = contentOriginalHtml.replace(searchRegExp, (matched) => {
return `<span style="color:#F56C6C" class="search_span">${matched}</span>`
})
scrollToFirst('search_span');
}
// 滚动到第一个
const scrollToFirst = (className) => {
const doms = document.getElementsByClassName(className);// 获取高亮元素列表
if (!doms || doms.length === 0) return;
doms[0].scrollIntoView({ behavior: "smooth" });
}
onMounted(() => {
getBase();// 获取dom和原始html;getBase也可以导出到外部进行调用
})
return { search_key }
}
使用
<template>
<!-- 输入搜索关键字 -->
<input v-model="search_key" />
<!-- 搜索区域div -->
<div id="content">xxxxxxxxxxxx</div>
</template>
<script setup>
import usePageSearch from './usePageSearch';
// 页内搜索
const { search_key } = usePageSearch({ id: 'content' });// 传入搜索区域id
</script>