微信小程序搜索关键字高亮和ctrl+f搜索定位实现

本文介绍了一种在小程序中实现高亮搜索的方法,通过将文本分割并动态渲染关键字,结合使用wx.createSelectorQuery()来获取匹配项的位置信息,实现了类似Ctrl+F的搜索功能,并能平滑滚动到指定的搜索结果。

原理

高亮关键字
  • 将文本存入数组,以text标签循环渲染,当有关键字keyword时,将文本的关键字替换成%%keyword%%再分割split成数组去渲染。
  • 当前节点文本等于关键字 class="{{ item == searchValue ? 'searchHigh' : ''}}"时,添加highLight样式。
    ———————————————————————————————————————————
    到这里高亮关键字就实现了。
类似于ctrl+f搜索定位
  • 小程序没有办法操作dom元素不会返回像web端一样的dom节点
  • 给所有循环渲染的text标签加上id
  • 利用 wx.createSelectorQuery().selectAll('.searchHigh').boundingClientRect获取所有添加了highLight样式的节点id数组
  • 数组的length就是找到关键字的总数,创建一个activeNodeIndex控制上一条和下一条。
  • text标签id和数组中取出的选中id相同时,添加选中selected样式。
  • 可以利用scrollviewscrollIntoView(selector)滚动到选中的节点,前提是scrollviewenhanced属性要为true
  • 也可以用wx.pageScrollTo。具体看你使用的场景。

效果在这里插入图片描述

源码(mpx)

<template>
  <view class="container">
    <van-search
      id="searchPanel"
      value="{{ searchValue }}"
      placeholder="请输入搜索关键词"
      bind:change="inpChange"
    />

    <view class="statistic-bar">
      <text>第{{ current }}条</text>
      <text>共{{ totalKeywordNodes }}条</text>
      <text style="color: #1d84f6" bindtap="handleScroll('pre')">上一条</text>
      <text style="color: #1d84f6" bindtap="handleScroll('next')">下一条</text>
    </view>

    <scroll-view scroll-y enhanced style="height: {{listHeight}}px" class="listPanel">
      <view class="news_item" wx:for="{{newList}}" wx:key="index">
        <view class="descBox">
          <view class="news_title textoverflow">{{ item.title }}</view>
          <view class="news_text">
            <text
              wx:for="{{ item.jj }}"
              wx:for-index="secondIndex"
              wx:key="secondIndex"
              id="text_{{index + '_'+ secondIndex}}"
              class="{{ item == searchValue ? 'searchHigh' : ''}}"
              wx:class="{{ {selected: 'text_'+index + '_'+ secondIndex === keywordNodes[activeNodeIndex]} }}"
              >{{ item }}</text
            >
          </view>
        </view>
      </view>
    </scroll-view>
  </view>
</template>
<script>
import { createPage } from '@mpxjs/core'
createPage({
  data: {
    mainHeight: 0,
    seachPanelHeight: 0,
    searchValue: '',
    scrollView: null, // 滚动容器节点
    newList: [
      {
        title: '关于举行机器人竞赛活动的相所发生的方式胜多负少的',
        jj: '内容简介中央气象台气温气温UIuouoiuo水电费水电费公司的发生的对方水电费'
      },
      {
        title: '关偶奇偶我奇偶就hi呕吼我和奇偶以后后的',
        jj: '内kjlkjljk防守打法你是端放开那没事的离开父母了情况没考虑为全面方水电费'
      },
      {
        title: '关于额外日围殴日头诶让你偷Irene的',
        jj: '内容简介中央气象台发布寒潮蓝色预警计今天20时至8日20时水电费水电费水电费公司的发生的对方水电费'
      },
      {
        title: '关于尔特瑞特认同与一体uyiuyiuyiuyiiuoui 胜多负少的',
        jj: '内容简介我解耦我偶奇偶我我就挨打王企鹅卖家前往颇尔恶趣味驱蚊器翁群水电费'
      }
    ],
    keywordNodes: [], // 存放的关键字节点ID数组
    activeNodeIndex: 0 // 当前激活的节点索引
  },
  computed: {
    listHeight () {
      return this.mainHeight - this.seachPanelHeight
    },
    totalKeywordNodes () {
      return this.keywordNodes.length
    },
    current () {
      if (this.keywordNodes.length === 0) {
        return 0
      } else {
        return this.activeNodeIndex + 1
      }
    }
  },
  onReady () {
    this.getWindowHeight()
    this.getFields()
    this.mockData()
  },
  methods: {
    mockData () {
      let temp = []
      for (let i = 0; i < 4; i++) {
        temp = temp.concat(this.newList)
      }
      this.setData({
        newList: temp
      })
    },
    getWindowHeight () {
      const that = this
      wx.getSystemInfo({
        success (res) {
          that.setData({
            mainHeight: res.windowHeight
          })
        }
      })
    },
    getFields () {
      const that = this
      const query = wx.createSelectorQuery()
      query.select('#searchPanel').fields({
        dataset: true,
        size: true
      }, res => {
        // console.log(res)
        that.setData({
          seachPanelHeight: res.height
        })
      }).exec()
    },
    // 替换关键字并分割成数组
    getInf (str, key) {
      return str
        .replace(new RegExp(`${key}`, 'g'), `%%${key}%%`)
        .split('%%')
        .filter(item => {
          if (item) {
            return true
          }
          return false
        })
    },
    //  输入改变时触发
    inpChange (e) {
      let key = e.detail
      this.setData({
        activeNodeIndex: 0,
        keywordNodes: [],
        searchValue: e.detail
      })
      // 如果关键字有值遍历新闻列表
      if (key) {
        this.newList.forEach(element => {
          // 如果该字段已经是数组则重新变为字符串
          if (element.jj instanceof Array) {
            element.jj = element.jj.join('')
          }
          let newContent2 = this.getInf(element.jj, key)
          // 把分割的数组赋值给每一项的title
          element.jj = newContent2
        })
        // console.log(this.newList)

        this.$nextTick(() => {
          wx.createSelectorQuery()
            .selectAll('.searchHigh')
            .boundingClientRect(res => {
              let keywordNodes = res.map(item => item.id)
              this.setData({
                keywordNodes: keywordNodes
              })
            })
            .select('.listPanel')
            .node(res => {
              this.setData({
                scrollView: res.node
              })
              this.scrollToView(0)
            })
            .exec()
        })
      }
    },
    /**
     * @method 将节点滚动至可视窗口
     * @param {number} index
     */
    scrollToView (index) {
      // console.log("#" + this.keywordNodes[index]);
      this.$nextTick(() => {
        this.scrollView.scrollIntoView("#" + this.keywordNodes[index])
      })
    },
    /**
     * @method 控制上一条和下一条
     * @param {string} type
     */
    handleScroll (type) {
      switch (type) {
        case 'pre':
          if (this.activeNodeIndex === 0) {
            this.activeNodeIndex = this.keywordNodes.length - 1
          } else {
            this.activeNodeIndex--
          }
          break;

        default:
          if (this.activeNodeIndex === this.keywordNodes.length - 1) {
            this.activeNodeIndex = 0
          } else {
            this.activeNodeIndex++
          }
          break;
      }
      this.scrollToView(this.activeNodeIndex)
    }
  }
})
</script>
<style scoped>
  .container {
    width: 100%;
    overflow: scroll;
    --webkit-overflow-scrolling: touch-action;
  }

  .statistic-bar {
    position: fixed;
    left: 16px;
    bottom: 30px;
    right: 16px;
    border-radius: 12rpx;
    background-color: #e6f7ff;
    z-index: 999;
    display: flex;
    justify-content: space-around;
    width: calc(100% - 16px -16px);
    padding: 16rpx 32rpx;
    box-sizing: border-box;
    font-size: 26rpx;
    box-shadow: 6rpx 6rpx 15rpx rgba(0, 0, 0, 0.125), -6rpx 0rpx 15rpx rgba(0, 0, 0, 0.125);
    color: #323233;
  }

  .listPanel {
    box-sizing: border-box;
    background-color: #f5f5f5;
  }

  .news_item {
    width: 100%;
    height: 208rpx;
    padding: 0 32rpx;
    margin: 32rpx 0;
    box-sizing: border-box;
  }

  .descBox {
    width: 100%;
    height: 100%;
    background: #fff;
    border-radius: 12rpx;
    padding: 24rpx;
    box-sizing: border-box;
  }

  .news_title {
    width: 100%;
    font-size: 32rpx;
    font-weight: 500;
    text-align: left;
    color: black;
    margin-bottom: 10rpx;
  }

  .news_text {
    font-size: 26rpx;
    font-weight: 400;
    color: #909090;
    text-align: left;
    margin-bottom: 7rpx;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-box-orient: vertical;
    word-break: break-all;
  }

  .textoverflow {
    display: -webkit-box;
    -webkit-line-clamp: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-box-orient: vertical;
    word-break: break-all;
  }

  .searchHigh {
    font-size: 36rpx;
    color: #1d84f6;
    font-weight: bold;
  }

  .selected {
    background: #909090;
  }
</style>
<script type="application/json">
  {
    "navigationBarTitleText": "搜索关键字高亮",
    "usingComponents": {
      "van-search": "@vant/weapp/dist/search/index"
    }
  }
</script>

微信小程序开发中,关键字绑定通常用于实现用户输入关键字后,根据关键字进行搜索、筛选等操作。以引用[3]中的代码为例,展示了一个简单的关键字绑定及搜索功能的实现。 在该示例中,通过 `bindinput` 事件将输入框的输入内容与页面的方法进行绑定。代码如下: ```html <view class="find"> <input type="text" placeholder="请输入关键字" bindinput="find" /> <button size="mini" type="default">搜索</button> </view> <view> <navigator url="/pages/goods_detail/goods_detail?goods_id={{item.goods_id}}" wx:for="{{findList}}" wx:key="goods_id" class="msg">{{item.goods_name}}</navigator> </view> ``` 在上述代码里,`<input>` 标签中的 `bindinput="find"` 表示当输入框内容发生变化时,会触发名为 `find` 的函数。在对应的 `js` 文件中,需要定义这个 `find` 函数来处理输入的关键字。以下是一个简单的 `js` 示例: ```javascript Page({ data: { findList: [] }, find: function (e) { var keyword = e.detail.value; // 这里可以根据关键字进行搜索操作,更新 findList 的数据 // 例如从后台获取数据并筛选 // 假设这里模拟筛选数据 var newList = []; // 假设原始数据存储在 allList 中 var allList = [ { goods_id: 1, goods_name: "商品1" }, { goods_id: 2, goods_name: "商品2" }, { goods_id: 3, goods_name: "商品3" } ]; for (var i = 0; i < allList.length; i++) { if (allList[i].goods_name.indexOf(keyword) > -1) { newList.push(allList[i]); } } this.setData({ findList: newList }); } }) ``` 在这个 `js` 代码中,`find` 函数接收输入框的输入事件 `e`,通过 `e.detail.value` 获取输入的关键字。然后根据关键字对数据进行筛选,将符合条件的数据存储在 `newList` 中,并使用 `this.setData` 方法更新页面的 `findList` 数据,从而实现根据关键字筛选展示数据的功能。 此外,在实际开发中,可能会涉及到与后端的交互,将关键字发送到后端服务器,由服务器进行数据库查询等操作,并返回符合条件的数据。结合引用[2],可以使用 Spring Boot 框架构建后端服务,为微信小程序提供稳定可扩展的支持,实现更复杂的关键字搜索功能。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值