实现一个局部的CTRL+F

需求来源

一天产品经理找到我,问我实现一个页面局部搜索能做吗?需要把所有相同的关键词高亮展示出来,并可以上下选中其他关键词。我一听好家伙这个不就是浏览器自带的Ctrl+F的全局搜索吗。于是我给他展示了一下:

产品经理一看好家伙还真是这样的功能,于是马上去给业务方教学了,但是很快又找到了我,说实现是实现了,但是用起来业务方感觉和实际需求还是有点偏颇:

  1. 这个是页面全局的搜索,但是实际需求页面上还会有其他的信息总数也会被统计进去,分析就不准确了。
  2. 希望每次进入页面都会给默认的关键词高亮,这样操作路径就短了。

这么一看就是要实现一个局部的搜索了,能不能做?当然能,毕竟存在即合理。

需求点拆分

实际体验过这个功能的话,主要功能可以拆分为以下几个点

  1. 高亮词匹配高亮。
  2. 选中单个高亮词。
  3. 选中的高亮词需要位于页面中。

实现

实现的效果

我们先来看下最终实现的效果:

可以看到我们的页面一共两个部分,一部分是在我们的搜索范围内的,一开始会有一个默认的关键词选中在我们的搜索框中,这里我是在请求中获得的,一开始是经常 ,后面我给改成了面试,同时在选中的高亮词超出可视区域的时候会自动滚动到可视区域中,至此整个的需求算是完整的实现了。

实现思路

关键词高亮:只需要匹配对应的关键词,然后进行替换成带样式的dom元素即可。

js 复制代码
const genContent = (text: string) => {
        try {
            return (
                <div dangerouslySetInnerHTML={{
                    __html: text.replace(new RegExp(inputValue, 'g'), `<span class=${hightLightWordClassName} style="background-color: yellow">${inputValue}</span>`)
                }} />
            )
        } catch (error) {

        }
    }

关键词选择改变:借助在关键词高亮的时候dom元素上的class属性,我们就可以获得所有的高亮dom,那么接下来我们只需要,维护一个当前位于第几个dom的状态即可。

js 复制代码
     /**
     * 从第几个到第几个dom
     */
    const changeDomBgc = (from: number, to: number) => {
        const doms = document.getElementsByClassName(hightLightWordClassName);
        if (!scrollRef.current || !doms) return;
        doms[to] && scrollToView(doms[to]);
        doms[to].setAttribute('style', 'background-color: orange');
        doms[from].setAttribute('style', 'background-color: yellow');
    }
    /**
     * 
     * 上一步或者下一步
     */
    const changeStep = (step: -1 | 1) => {
        if (step === 1) {
            if (curHeightLightDom < domListLength - 1) {
                changeDomBgc(curHeightLightDom, curHeightLightDom + 1);
                setCurHeightLightDom(curHeightLightDom + 1);
            }
        } else if (curHeightLightDom > 0) {
            changeDomBgc(curHeightLightDom, curHeightLightDom - 1);
            setCurHeightLightDom(curHeightLightDom - 1);
        }
    }

选词超出可视区域滚动到可视区域: 主要借助dom的scrollTo方法。

js 复制代码
    const scrollToView = (element: Element) => {
        if (!scrollRef.current) return;
        const { top: elementRectTop, bottom: elementRectBottom } = element.getBoundingClientRect();
        const { top: parentRectTop, bottom: parentRectBottom } = scrollRef.current.getBoundingClientRect();
        console.log(elementRectTop,elementRectBottom,parentRectTop,parentRectBottom)
        if (elementRectTop - parentRectTop > scrollContentHeight) {
            scrollRef.current.scrollTo({
                top: elementRectTop - parentRectTop + scrollRef.current.scrollTop + 20
            })
        }
        if (parentRectBottom - elementRectBottom > scrollContentHeight) {
            scrollRef.current.scrollTo({
                top: scrollRef.current.scrollTop - (parentRectTop  - elementRectBottom) - 20
            })
        }

    }
分为两种情况,一种是往下一个的时候,下一个在视图外,那么这个时候就要向上滚动, getBoundingClientRect() 可以获得元素的宽高和其相对于视口的上下左右,那么只要是父元素的底的高度减去子元素的底的差值已经大于父元素的高度的时候,说明元素元素已经滚到父元素的上面去了,这里可以看途中的1例子就是这种情况;如果子元素的顶减去父元素的顶已经大于父元素的顶了,那么说明此时已经滚动到父元素的下面去了,这里就是对应的第二种情况了,搞清楚了两种情况下面就简单了。
如果对应的第一种情况滚动到父元素的上面,那么我们的元素就要向下滚动一定的距离,滚动多少呢?我们只需要滚动子元素的底相对于父元素的顶的距离就行,这里需要注意的是要在页面的滚动基础上进行,因为我们的整个页面也是可以滚动的,同时还要让元素看的到所以要再往下滚动元素的高度这里我就写了20。同理如果是第二种我们只需要滚动父元素的顶到子元素的顶这样的距离,同样要在页面的滚动基础上,再加上元素的本身高度即可。

正文

至此我们的功能就已经实现了,有人就要问了,这么简单的功能为啥还要分享?没错正文才刚刚开始,细心的同学肯定发现了,在例子展示的时候我们请求改变响应体的时候用的是一个浏览器插件,对的没错这个就是我开发的一个浏览器mock插件,非常的实用,它可以帮助我们快速的获取浏览器的请求并进行更改,这样我们在开始的时候接口还没有好的阶段就可以用这个插件来进行请求的mock。详情可以看这个视频:www.bilibili.com/video/BV1Cw...

文章中案例的代码仓库:github.com/caixiaocai1...

相关推荐
Devil枫1 分钟前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦35 分钟前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子1 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山2 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享2 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
从兄3 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
清灵xmf4 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨4 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL4 小时前
npm入门教程1:npm简介
前端·npm·node.js
小白白一枚1115 小时前
css实现div被图片撑开
前端·css