实现一个局部的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...

相关推荐
编程零零七2 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
北岛寒沫2 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
everyStudy3 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
(⊙o⊙)~哦4 小时前
JavaScript substring() 方法
前端
无心使然云中漫步4 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者4 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_5 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
麒麟而非淇淋5 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120535 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢6 小时前
【Vue】VueRouter路由
前端·javascript·vue.js