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

相关推荐
Myli_ing10 分钟前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
dr李四维27 分钟前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
I_Am_Me_41 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
雯0609~1 小时前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ1 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z1 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁1 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4041 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue