首页 > 其他分享 >vue自定义组件——search-box

vue自定义组件——search-box

时间:2023-05-17 13:23:02浏览次数:49  
标签:box 检索 search node 自定义 dom currentIndex 文本 节点

github地址: https://github.com/lxmghct/my-vue-components

组件介绍

  • props:
    • value/v-model: 检索框的值, default: ''
    • boxStyle: 检索框的样式, default: 'position: fixed; top: 0px; right: 100px;'
    • highlightColor: 高亮颜色, default: 'rgb(246, 186, 130)'
    • currentColor: 当前高亮颜色, default: 'rgb(246, 137, 31)'
    • selectorList: 检索的选择器列表, default: []
    • iFrameId: 检索的iframe的id, default: null, 若需要搜索iframe标签中的内容, 则将该参数设为目标iframe的id
    • beforeJump: 跳转前的回调函数, default: () => {}
    • afterJump: 跳转后的回调函数, default: () => {}
    • (注: 上述两个回调函数参数为currentIndex, currentSelector, lastIndex, lastSelector)
  • events:
    • @search: 检索时触发, 参数为input和total
    • @goto: 跳转时触发, 参数为index
    • @close: 关闭时触发
  • methods:
    • clear() 清空检索框
    • search() 检索

效果展示

设计思路

完整代码见github: https://github.com/lxmghct/my-vue-components
在其中的src/components/SearchBox下。

1. 界面

界面上比较简单, 输入框、当前/总数、上一个、下一个、关闭按钮。

<div class="search-box" :style="boxStyle">
  <input
    v-model="input"
    placeholder="请输入检索内容"
    class="search-input"
    type="text"
    @input="search"
  >
  <!--当前/总数、上一个、下一个、关闭-->
  <span class="input-append">
    &nbsp;&nbsp;{{ current }}/{{ total }}&nbsp;&nbsp;
  </span>
  <span class="input-append" @click="searchPrevious">
    <div class="svg-container">
      <svg width="100px" height="100px">
        <path d="M 100 0 L 0 50 L 100 100" stroke="black" fill="transparent" stroke-linecap="round"/>
      </svg>
    </div>
  </span>
  <span class="input-append" @click="searchNext">
    <div class="svg-container">
      <svg width="100px" height="100px" transform="rotate(180)">
        <path d="M 100 0 L 0 50 L 100 100" stroke="black" fill="transparent" stroke-linecap="round"/>
      </svg>
    </div>
  </span>
  <span class="input-append" @click="searchClose">
    <div class="svg-container">
      <svg width="100%" height="100%">
        <line x1="0" y1="0" x2="100%" y2="100%" stroke="black" stroke-width="1" />
        <line x1="100%" y1="0" x2="0" y2="100%" stroke="black" stroke-width="1" />
      </svg>
    </div>
  </span>
</div>

2. 检索与跳转

这部分是search-box的核心功能,一共有以下几个需要解决的问题:

  1. 获取待搜索的容器
    • 为提高组件的通用性,可以通过传入选择器列表来获取容器,如['.container', '#containerId'],使用document.querySelector()获取容器。
  2. 获取所有文本
    • 不能单独对某个dom节点获取文本, 因为某个待搜索词可能被分割在多个节点中, 例如<span>hello</span><span>world</span>,所以需要获取整个容器内的所有文本拼接起来, 然后再进行检索。
    • 使用innetText获取文本会受到样式影响, 具体见文章最后的其它问题。所以需要遍历所有节点将文本拼接起来。
    • 遍历文本节点时, 可以用node.nodeType === Node.TEXT_NODE判断是否为文本节点。
    if (node.nodeType === Node.TEXT_NODE) { // text node
        callback(node)
    } else if (node.nodeType === Node.ELEMENT_NODE) { // element node
        for (let i = 0; i < node.childNodes.length; i++) {
            traverseTextDom(node.childNodes[i], callback)
        }
    }
    
  3. 检索结果的保存
    • 由于查找完之后需要实现跳转, 所以为方便处理, 将检索到的结果所在的dom节点保存起来, 以便后续跳转时使用。每个结果对应一个domList。
  4. 高亮检索词
    • 使用span标签包裹检索词, 并设置样式, 实现高亮。
    • 为了避免检索词被html标签分割, 可以对检索词的每个字符都用span标签包裹, 例如检索词为hello,则可以将其替换为<span>h</span><span>e</span><span>l</span><span>l</span><span>o</span>
    • 样式设置可以给span设置background-color, 为了方便修改并减小整体html长度, 可以改为给span设置class, 注意这种情况下在style标签设置的样式未必有效, 可以采用动态添加样式的方式。
    function createCssStyle (css) {
        const style = myDocument.createElement('style')
        style.type = 'text/css'
        try {
            style.appendChild(myDocument.createTextNode(css))
        } catch (ex) {
            style.styleSheet.cssText = css
        }
        myDocument.getElementsByTagName('head')[0].appendChild(style)
    }
    
    • 将span标签插入到原先文本节点的位置, 若使用innerHtml直接进行替换, 处理起来略有些麻烦。可以考虑使用insertBefore和removeChild方法。
    const tempNode = myDocument.createElement('span')
    tempNode.innerHTML = textHtml
    const children = tempNode.children
    if (children) {
      for (let i = 0; i < children.length; i++) {
        domList.push(children[i])
      }
    }
    // 将节点插入到parent的指定位置
    // insertBofore会将节点从原来的位置移除,导致引错误,所以不能用forEach
    while (tempNode.firstChild) {
      parent.insertBefore(tempNode.firstChild, textNode)
    }
    parent.removeChild(textNode)
    
  5. 跳转
    由于结果对应的dom节点已保存,所以跳转起来比较容易。跳转时修改当前高亮的dom节点的类名, 然后将其滚动到可视区域。
    setCurrent (index) {
        const lastSelector = this.searchResult[this.currentIndex] ? this.searchResult[this.currentIndex].selector : null
        const currentSelector = this.searchResult[index] ? this.searchResult[index].selector : null
        if (this.currentIndex >= 0 && this.currentIndex < this.searchResult.length) {
            this.searchResult[this.currentIndex].domList.forEach((dom) => {
                dom.classList.remove(this.currentClass)
            })
            this.searchResult[this.currentIndex].domList[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
        }
        this.currentIndex = index
        if (this.currentIndex >= 0 && this.currentIndex < this.searchResult.length) {
            this.searchResult[this.currentIndex].domList.forEach((dom) => {
                dom.classList.add(this.currentClass)
            })
        }
    }
    
  6. 移除高亮效果
    • 由于高亮效果是通过给text节点添加span标签实现, 所以需要将span标签移除, 并替换为原先的文本节点。
    • 使用insertBeforeremoveChild方法。
    • 替换完节点后需要调用normalize()方法, 将相邻的文本节点合并为一个文本节点。
    function convertHighlightDomToTextNode (domList) {
        if (!domList || !domList.length) { return }
        domList.forEach(dom => {
            if (dom && dom.parentNode) {
                const parent = dom.parentNode
                const textNode = myDocument.createTextNode(dom.textContent)
                parent.insertBefore(textNode, dom)
                parent.removeChild(dom)
                parent.normalize() // 合并相邻的文本节点
            }
        })
    }
    

3. 添加对iframe的支持

有时候页面中可能会包含iframe标签, 如果需要检索iframe中的内容, 直接使用当前的document是无法获取到iframe中的内容的, 需要拿到iframe的document对象。

const myIframe = document.getElementById(this.iframeId)
if (myIframe) {
  myDocument = myIframe.contentDocument || myIframe.contentWindow.document
} else {
  myDocument = document
}
if (myIframe && this.lastIframeSrc !== myIframesrc) {
  const css = `.${this.highlightClass} { background-color: ${this.highlightColor}; } .${this.currentClass} { background-color: ${this.currentColor}; }`
  createCssStyle(css)
  this.lastIframeSrc = myIframe.src
}

同一个iframe, 如果src发生变化, 则需要重新给其生成样式, 否则样式会失效。

其他问题

  1. 使用svg画按钮图标时,双击svg按钮会自动触发全选
    • 解决方法: 在svg标签所在容器上添加user-select: none;样式
  2. 使用node.nodeType === Node.TEXT_NODE判断文本节点时,会遇到一些空节点,导致检索错误
    • 解决方法: 在判断文本节点时,加上node.textContent.trim() !== ''的判断, 获取所有元素的文本时。
    • 后续修改: 可以不单独处理这些空的文本节点, 只要保证所有使用到获取文本的地方都统一使用或不使用trim()即可。尽量都不使用trim(), 如果随意使用trim(),可能会导致部分空白字符被误删。

标签:box,检索,search,node,自定义,dom,currentIndex,文本,节点
From: https://www.cnblogs.com/lxm-cnblog/p/17408456.html

相关文章

  • 【Echarts】tooltip自定义提示框
    1组件可直接使用。2tooltip:{3show:true,4trigger:'axis',5confine:true,6padding:0,7borderWidth:0,8backgroundColor:'rgba(1,1,1,0)',9//axisPointer:{10......
  • 【Python】Centos7安装dirsearch
    一、升级Openssl1.1.1 1、官网下载源码:https://www.openssl.org/2、安装#./config--prefix=/datas/soft/openssl-1.1.1no-zlib#make#makeinstall3、新版配置#ln-s/datas/soft/openssl-1.1.1/include/openssl/usr/include/openssl#ln-s/datas/soft/openss......
  • Linux- 环境变量和自定义配置
    环境变量查看查看当前用户的全部环境变量:env,可以结合env|grep筛选信息查看当前系统的全部环境变量:export查看指定环境变量:printenv$env_name或者echo$env_name清除指定环境变量的值:unset$env_name常用环境变量PATH可执行程序的搜索目录,可执行程序包括Linux......
  • How to migrate mailboxes from O365 to O365?
    Office365isapowerfulsuiteofapplicationsthatoffersawiderangeoffeaturestohelpbusinessesrunefficiently.However,asbusinessesgrow,theyoftenneedtomergeoracquireothercompanies,leadingtotenant-to-tenantmigration.Thisarticlew......
  • AutoMagic设计思路简介及新增自定义关键字实例
    目录  简介  AutoMagic介绍  SeleniumKey介绍  自定义关键字简介AutoMagic是一个基于WebUI的自动化管理平台。为什么叫AutoMagic呢?因为自动化在执行起来的时候是一个很神奇的事情,它可以无人值守的模拟人的操作,就像魔术(Magic)一样。所以我给她取名叫AutoMagic。Aut......
  • 什么是 Angular 的 banana-in-a-box detection 机制
    "banana-in-a-boxdetection"是一个Angular表单绑定的术语。在Angular应用中,表单绑定通常采用“双向绑定”的方式,即使用[(ngModel)]或[(value)]等语法实现数据的双向绑定。其中,"banana-in-a-box"表示[()]符号的形状,即一个圆括号和一个方括号相连。这种绑定方式的优点......
  • filebeat日志收集到elasticsearch
    filebeat是轻量级日志收集框架,go语言开发。需要在每个日志收集的终端部署,配置日志文件路径。可以将日志收集到es,logstash,这里以收集到es为例。配置主要分为input和out两块。解压后有filebeat.yml配置文件,主要针对该文件进行配置。-type:log#日志文件位置paths:-/data......
  • GridSearchCV中的scoring
    说明scoring参数输入形式包括字符串、可调用对象或评分函数。以下是常用的评分规则示例:使用预定义的字符串指定评分规则:'accuracy':准确率(分类问题)'precision':精确率(分类问题)'recall':召回率(分类问题)'f1':F1分数(分类问题)'r2':R2分数(回归问题)'mean_squared_error':均方误差(......
  • AntDeisgn中checkbox group的使用
    <template><!--弹窗类型选择--><div><a-modal:visible="state.modalAttr.visible"title="规选类型"width="680px"@ok="showModal"@cancel="hideModal"><a-checkbox......
  • 多个 ComboBox 控件绑定同一数据源,数据会联动(其中一个选择项改变的时候,其余也会跟着变
    问题:在Winform开发中,两个ComboBox控件绑定了同一个数据源List<T>,但是在使用的时候发现,选择其中一个ComboBox的时候,另一个也会跟着变化。原因:内存中只有一份数据,改变任何一个ComboBox都会使得数据源有所变化,导致其他ComboBox的展示效果发生联动。解决:将数据源进行复制,相当于为每......