前端实现高亮划词

2,472 阅读4分钟

前言

前端实现文字高亮划词。这个功能一般都是一些类似笔记产品上才会出现的一个功能,通过添加指定的关键字进行高亮显示。我这两天也做了一个出来,名字叫ohlight,实现的方式不是网上那些复杂的方式,很简单的一个方式,感兴趣的朋友可以看下方地址。

项目地址

// pnpm 
pnpm i ohlight

// npm
npm i ohlight

// yarn
yarn add ohlight

使用效果

我们先看看以下使用效果,高亮样式是全场支持自定义的哈,这里我使用的是默认的样式。

image.png

实现

采用web Components实现组件,这样就可以直接跨框架、无框架运行了,简直高效一套代码运行任何框架。感兴趣的朋友可以看MDN。 高亮划词的地方也很简单,不要看文件有点多,实际都是打辅助的,核心的就只有下面的代码。

for(let keyword of keywords){
    c = c.replace(new RegExp(keyword, 'g'), `<mark style="${s}">${keyword}</mark>`)
}

现在我们开始实现这个组件吧,本文将采用和ohlight组件一样的方式。

本篇文章是基于web Components进行编写,还不了解的朋友可以去看一下MDN

注:本文会简单实现ohlight组件的亮词功能,其他功能大家可以去giteegithub上看源码。👏欢迎大家star、issue等。

搭建基础框架

我们直接新建一个OHLight的类,然后注册,组件内部进行挂载。这里我们写OHLight类的时候需要继承HTMLElement

class OHLight extends HTMLElement {}

customElements.define('o-hlight', OHLight)

export default OHLight

然后在constructor中需要调用super(),这一步必不可少。

constructor(){
    super()
}

我们在类中添加一个renderer渲染函数。

renderer () {}

我们的基础框架就搭建好了,下面是目前步骤的完整代码:

class OHLight extends HTMLElement {
    constructor(){
        super()
    }

    renderer () {}
}

customElements.define('o-hlight', OHLight)

export default OHLight

渲染

这一步我们需要将我们的组件渲染在页面上。这一步的操作全程都在renderer方法中。

我们先让我们的组件可以根据dom操作而操作。这里当modeopen是可以操作的,为closed则该dom不可操作。

renderer () {
    this.attachShadow({
        mode: 'open'
    })
}

我们创建一个div盒子然后并挂载到我们的shadowRoot影子节点上,当然你也可以创建其他的标签;并给他插入内容;然后添加组件。

const node = document.createElement('div')

node.innerHTML = '桃小瑞'

this.shadowRoot?.appendChild(node)

可以在constructor中调用我们的renderer方法。

接下来就到了奇迹的时刻了,我们引入组件,并在页面上使用。

image.png

image.png

此时的所有的完整代码如下:


class OHLight extends HTMLElement {
    constructor(){
        super()

        this.renderer()
    }
    renderer () {
        this.attachShadow({
            mode: 'open'
        })
        const node = document.createElement('div')
        node.innerHTML = '桃小瑞'
        this.shadowRoot?.appendChild(node)
    }
}

customElements.define('o-hlight', OHLight)
export default OHLight

页面上就会出现我们的组件内容。

获取属性并设置高亮

这一步我们将获取我们传递的文本内容、关键词进行高亮显示。

我们以content属性来做我们的文本内容传参。

我们需要获取我们组件中的content属性。并通过innerHTML将内容插入到页面中去

let content = this.attributes.content
node.innerHTML = `${content.value}`

组件内容:

image.png

效果图:

image.png

这里我们需要将constructor中调用renderer的方法移动到connectedCallback生命钩子内,不然会拿不到内容。

connectedCallback(){
    this.renderer()
}

页面上已经渲染出来了我们的内容。现在我们进行关键词获取 + 渲染。

跟我们获取内容是一样的方法,我们获取keywords关键词列表。

let keywords = this.attributes.keywords

现在我们已经获取到了关键词列表,我们对内容进行检查和替换。

声明一个handle的方法,并将文本内容和关键字传进去,用来处理我们的高亮词汇。这个方法返回的内容就是我们已经处理好的文本内容,可以直接插入显示。

node.innerHTML = this.handle((content.value || ''), (keywords.value || ''))
handle (content, keywords) {
    let keywordList = []
    if(keywords.includes(',')){
        keywordList = keywords.split(',')
    }else{
        keywordList = [keywords]
    }

}

上面我们在handle方法中进行了关键词组处理,多个关键词使用(,)逗号隔开,然后进行判断、切割。

for(let keyword of keywordList){
            c = c.replace(new RegExp(keyword, 'g'), `<mark>${keyword}</mark>`)
}

这里我们对已经处理好的关键词进行循环,在循环的过程中我们使用new RegExp(keyword, 'g')正则进行替换,将获取到的关键字替换为用mark标签装起来的字符串<mark>${keyword}</mark>

看一下效果:

image.png

组件内容:

image.png

这样我们就实现好了。

完整代码:


class OHLight extends HTMLElement {
    constructor(){
        super()
    }

    renderer () {
        this.attachShadow({
            mode: 'open'
        })

        const node = document.createElement('div')
        
        let content = this.attributes.content
        let keywords = this.attributes.keywords

        node.innerHTML = this.handle((content.value || ''), (keywords.value || ''))

        this.shadowRoot?.appendChild(node)
    }

    handle (content, keywords) {
        let c = content
        let keywordList = []

        if(keywords.includes(',')){
            keywordList = keywords.split(',')
        }else{
            keywordList = [keywords]
        }
        
        for(let keyword of keywordList){
            c = c.replace(new RegExp(keyword, 'g'), `<mark>${keyword}</mark>`)
        }

        return c
    }

    // 当自定义元素第一次被连接到文档 DOM 时被调用
    connectedCallback(){
        this.renderer()
    }
}

customElements.define('o-hlight', OHLight)

export default OHLight

生命钩子说明补充

web components 有四个状态的生命钩子,分别如下:

  • connectedCallback:当自定义元素第一次被连接到文档 DOM 时被调用;
  • disconnectedCallback:当自定义元素与文档 DOM 断开连接时被调用;
  • adoptedCallback:当自定义元素被移动到新文档时被调用;
  • attributeChangedCallback:当自定义元素的一个属性被增加、移除或更改时被调用。

我们想实现自定义样式的话直接给mark标签添加样式就可以了。

关于响应式的问题,ohlight是支持响应式的修改内容/关键字的,这里我是使用的attributeChangedCallback进行监听。

最后

以上就是前端实现高亮划词的一个文章。

欢迎大家下载使用ohlight 高亮划词组件