欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue實現(xiàn)搜索并高亮文字的兩種方式總結(jié)

 更新時間:2023年11月15日 14:38:49   作者:錢得樂  
在做文字處理的項目時經(jīng)常會遇到搜索文字并高亮的需求,常見的實現(xiàn)方式有插入標簽和貼標簽兩種,這兩種方式適用于不同的場景,各有優(yōu)劣,下面我們就來看看他們的具體實現(xiàn)吧

在做文字處理的項目時經(jīng)常會遇到搜索文字并高亮的需求,常見的實現(xiàn)方式有插入標簽和貼標簽兩種。這兩種方式適用于不同的場景,各有優(yōu)劣。為了方便操作,直接起一個Vue項目,在里面演示。

插入標簽的方式

簡單做一個布局,handleSearch 中放主要邏輯

<script setup>
import { ref } from 'vue'

const text = ref('豫章故郡,洪都新府。星分翼軫,地接衡廬。襟三江而帶五湖,控蠻荊而引甌越。物華天寶,龍光射牛斗之墟;人杰地靈,徐孺下陳蕃之榻。雄州霧列,俊采星馳。臺隍枕夷夏之交,賓主盡東南之美。都督閻公之雅望,棨戟遙臨;宇文新州之懿范,襜帷暫駐。十旬休假,勝友如云;千里逢迎,高朋滿座。騰蛟起鳳,孟學(xué)士之詞宗;紫電青霜,王將軍之武庫。家君作宰,路出名區(qū);童子何知,躬逢勝餞。')
const search = ref('')
const handleSearch = () => {
  console.log(search.value)
}
</script>
<template>
  <div class="editor">{{ text }}</div>
  <input type="text" v-model="search">
  <button @click="handleSearch">搜索</button>
</template>

<style scoped>
.editor {
  width: 200px;
  height: 200px;
  border: 1px solid #ddd;
  overflow: auto;
}
</style>

補充 handleSearch 的處理邏輯:

const handleSearch = () => {
  const regExp = new RegExp(search.value, 'g')
  text.value = text.value.replace(regExp, `<span style="background: yellow;">${search.value}</span>`)
}

用輸入框中的內(nèi)容創(chuàng)建一個正則,然后將內(nèi)容做替換,外面裹上 span 標簽并加背景顏色。

editor 稍作修改,否則標簽渲染不出來

<div class="editor" v-html="text"></div>

于是就實現(xiàn)了預(yù)期:

然而在有些業(yè)務(wù)場景中被搜索的區(qū)域會是 contenteditable 可編輯區(qū)域,如果再使用插入標簽的方式會污染原文,這時這種方式就行不通了。

貼標簽的方式

這種方式需要兩個前置的知識儲備,一個是 Document.createRange() ,該方法用以創(chuàng)建一個包含節(jié)點與文本節(jié)點的一部分的文檔片段。另一個是 Range.getBoundingClientRect() ,雖然是一個實驗中的方法,但是主流瀏覽器基本都支持,該方法會返回一個 DOMRect 對象,包含8個屬性,文檔中有詳細的介紹,在此就不贅述了。

對頁面稍作修改:

<script setup>
import { ref, watch, onMounted } from 'vue'

const text = ref('豫章故郡,洪都新府。星分翼軫,地接衡廬。襟三江而帶五湖,控蠻荊而引甌越。物華天寶,龍光射牛斗之墟;人杰地靈,徐孺下陳蕃之榻。雄州霧列,俊采星馳。臺隍枕夷夏之交,賓主盡東南之美。都督閻公之雅望,棨戟遙臨;宇文新州之懿范,襜帷暫駐。十旬休假,勝友如云;千里逢迎,高朋滿座。騰蛟起鳳,孟學(xué)士之詞宗;紫電青霜,王將軍之武庫。家君作宰,路出名區(qū);童子何知,躬逢勝餞。')
const search = ref('')
const highlight = ref([])
const editorRef = ref(null)
const wrapperRef = ref(null)
const handleSearch = () => {
  
}
</script>

<template>
  <div class="container">
    <div class="wrapper" ref="wrapperRef">
      <div class="editor" ref="editorRef" contenteditable>{{ text }}</div>
      <div class="highlight"></div>
    </div>
  </div>
  <input type="text" v-model="search">
  <button @click="handleSearch">搜索</button>
</template>

<style scoped>
.container {
  width: 200px;
  height: 200px;
  border: 1px solid #ddd;
  overflow: auto;
}

.wrapper {
  position: relative;
}

.highlight {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  z-index: -1;
}
</style>

增加了一個 highlight 框,用來存放高亮的塊, highlight 數(shù)組用來存放需要高亮的塊的位置信信息。

補充搜索函數(shù)中的邏輯

const len = search.value.length
const regExp = new RegExp(search.value, 'g')
const textNode = editorRef.value.firstChild
let result = null
while (result = regExp.exec(text.value)) {
  const { index } = result
  const range = document.createRange()
  range.setStart(textNode, index)
  range.setEnd(textNode, index + len)
  const rangeReact = range.getBoundingClientRect()
  highlight.value.push(rangeReact)
}

將要搜索的詞創(chuàng)建一個正則,并獲取文本框的文字節(jié)點。用 exec 來遍歷原文內(nèi)容,這樣可以實現(xiàn)全文搜索并得到搜索信息,拿到 index 屬性。此時就用到了前面提到的 createRange ,在文本結(jié)點根據(jù)起始位置和長度創(chuàng)建一個選中區(qū)域,并獲取選中區(qū)域的dom信息,將它們存放到一個數(shù)組中。此時可以拿到一個dom信息的數(shù)組:

可以用這個數(shù)組渲染高亮塊:

<div class="highlight">
  <span
    v-for="item in highlight"
    class="tag"
    :style="{
      left: item.left + 'px',
      top: item.top + 'px',
      width: item.width + 'px',
      height: item.height + 'px' }"></span>
</div>

增加對應(yīng)的樣式:

.tag {
  position: fixed;
  background: yellow;
}

這里使用 fixed 的原因是得到的距離信息時是相對于文檔,而不是父元素。但是這種方式是不可靠的,因為例子中可編輯區(qū)域是可以滾動的,一滾動高亮區(qū)域就錯位了:

所以還是要采用相對父元素定位,其實實現(xiàn)方式很簡單,先算出父元素相對于頁面的定位,再用剛才得出的距離詳見,最后得出高亮標簽相對于父元素的定位。畫個簡單的示意圖:

如圖所示,想得到距離3也就是高亮標簽相對于父元素的距離,就是距離2減去距離1。

const wrapperInfo = ref({})
onMounted(() => {
  wrapperInfo.value = wrapperRef.value.getBoundingClientRect()
})

在mounted狀態(tài)下獲取父元素的信息。

封裝一個計算位置信息的函數(shù),并修改搜索函數(shù),獲取 rangeReact 后增加和修改代碼:

const calRectInfo = (rangeReact) => {
  let rectInfo = {}
  rectInfo.width = rangeReact.width
  rectInfo.height = rangeReact.height
  rectInfo.left = rangeReact.left - wrapperInfo.value.left
  rectInfo.top = rangeReact.top - wrapperInfo.value.top
  return rectInfo
}
highlight.value.push(calRectInfo(rangeReact))

這時就可以把定位改為 position: absolute; 了。

此時再滾動樣式也不會錯亂:

但是當(dāng)被搜索詞在跨行時會出現(xiàn)bug:

搜索“星分翼軫“,然而兩行都被高亮了,通過調(diào)試可以看出它并不會很智能的分塊返回,所以這段邏輯就需要手動去實現(xiàn)。

首先是如何知道需要高亮的區(qū)域是多行。從 DOMReact 中得以得到需要高亮的行高,如果知道一行的高度,就可以知道是不是多行了。

const standardRange = document.createRange()
standardRange.setStart(textNode, 0)
standardRange.setEnd(textNode, 0)
const standardRangeReact = standardRange.getBoundingClientRect()
const lineHeight = standardRangeReact.height

在空白處創(chuàng)建一個 range ,就可以得到行高。然后根據(jù)行高判斷兩種情況:

if (rangeReact.height === lineHeight) {
  highlight.value.push(calRectInfo(rangeReact))
} else {
  // 多行的情況
}

多行的情況可以用雙指針來試,還以“星分翼軫”為例,設(shè)置 i = 0; j = 1; ,截取文字得到“星”,計算高度信息,然后 j++; 得到“星分”,當(dāng)文字為“星分翼”的時候,行高變?yōu)閮尚?,則應(yīng)高亮“星分”。然后將 i 設(shè)置為 j - 1 。繼續(xù)重復(fù)之前的操作。

let i = 0
let j = 1
while (j <= len) {
  const subRange = document.createRange()
  subRange.setStart(textNode, result.index + i)
  subRange.setEnd(textNode, result.index + j)
  const subRangeReact = subRange.getBoundingClientRect()
  if (subRangeReact.height === lineHeight) {
    if (j !== 1) highlight.value.pop()
    j++
  } else {
    i = j - 1
  }
  highlight.value.push(calRectInfo(subRangeReact))
}

每次不管是否是最終的結(jié)果都要把計算結(jié)果 pushhighlight 中, 當(dāng)后面的結(jié)果可以覆蓋前面的時候則再 pop 出來。 if (j !== 1) 的判斷是因為第一次截取時不應(yīng)該把以前的結(jié)果也刪除掉。

此時再進行搜索,可以折行顯示了。

但其實還有不盡如人意的地方,因為這個框是可編輯區(qū)域,當(dāng)插入新的文字時高亮框不會實時改變,留在了原地。

此時我們要監(jiān)聽文本框文字的改變

<div class="editor" ref="editorRef" contenteditable @input="handleChange">{{ text }}</div>

當(dāng)文字改變時重新計算高亮區(qū)域。

const handleChange = () => {
  text.value = editorRef.value.innerText
}

watch(text, () => {
  highlight.value = []
  handleSearch()
})

這樣當(dāng)輸入文字時,高亮區(qū)域可以實時計算:

以上就是vue實現(xiàn)搜索并高亮文字的兩種方式總結(jié)的詳細內(nèi)容,更多關(guān)于vue高亮文字的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中插槽slot的用法詳解

    Vue中插槽slot的用法詳解

    插槽(Slot)是Vue.js中一個非常重要的概念,它極大地提高了組件的復(fù)用性和靈活性,通過插槽,我們可以自定義組件的內(nèi)容,使其能夠適應(yīng)不同的場景,本文將結(jié)合實際案例,詳細介紹Vue中插槽的基本用法、類型以及高級技巧,需要的朋友可以參考下
    2024-11-11
  • Vue中Quill富文本編輯器的使用教程

    Vue中Quill富文本編輯器的使用教程

    這篇文章主要介紹了Vue中Quill富文本編輯器的使用教程,包括自定義工具欄、自定義字體選項、圖片拖拽上傳、圖片改變大小等使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • 在vue中使用echarts的方法以及可能遇到的問題

    在vue中使用echarts的方法以及可能遇到的問題

    Echarts是一個與框架無關(guān)的JS圖表庫,但是它基于Js,這樣很多框架都能使用它,下面這篇文章主要給大家介紹了關(guān)于在vue中使用echarts的方法以及可能遇到的問題的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Vue實現(xiàn)簡易記事本功能

    Vue實現(xiàn)簡易記事本功能

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)簡易記事本功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • vue @click與@click.native,及vue事件機制的使用分析

    vue @click與@click.native,及vue事件機制的使用分析

    這篇文章主要介紹了vue @click與@click.native,及vue事件機制的使用分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • VUE實現(xiàn)token登錄驗證

    VUE實現(xiàn)token登錄驗證

    這篇文章主要為大家介紹了VUE實現(xiàn)token登錄驗證,詳細記錄實現(xiàn)token登錄驗證的步驟,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • vue實現(xiàn)盒子內(nèi)拖動方塊移動的示例代碼

    vue實現(xiàn)盒子內(nèi)拖動方塊移動的示例代碼

    這篇文章主要給大家介紹了如何通過vue實現(xiàn)盒子內(nèi)拖動方塊移動,文章通過代碼示例講解的非常詳細,具有一定的參考價值,感興趣的小伙伴可以參考閱讀本文
    2023-08-08
  • 解決vue create 創(chuàng)建項目只有兩個文件問題

    解決vue create 創(chuàng)建項目只有兩個文件問題

    這篇文章主要介紹了解決vue create 創(chuàng)建項目只有兩個文件問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Vue裝飾器中的vue-property-decorator?和?vux-class使用詳解

    Vue裝飾器中的vue-property-decorator?和?vux-class使用詳解

    這篇文章主要介紹了Vue裝飾器中的vue-property-decorator?和?vux-class使用詳解,通過示例代碼給大家介紹的非常詳細,對vue-property-decorator?和?vux-class的使用感興趣的朋友一起看看吧
    2022-08-08
  • vue-cli 為項目設(shè)置別名的方法

    vue-cli 為項目設(shè)置別名的方法

    這篇文章主要介紹了vue-cli 為項目設(shè)置別名的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10

最新評論