微信小程序实现搜索功能以及效果(超详细)

我们先来看一下实现哪些功能:

1 搜索历史记录以及清空历史记录

2 热门搜索推荐以及更新推荐内容

3 根据输入框输入的内容来自动搜索相关查询,后台逻辑是模糊查询,后台就先不扯了

这里我用的是自己定义的虚拟数据,暂时没用后台接口,可能有点问题,希望大家指出,共同解决,一起进步

好了废话就不多说了,我们先看看代码吧

wxml

<!-- 自定义顶部导航 S -->
<view class="navbar custom-class" style='height:{{navHeight}}px;'>
  <view class="navbar-action-wrap navbar-action-group row item-center" style='top:{{navTop}}px;'>
    <image style="width: 1rem; height: 1.2rem; " src="{{leftIcon}}" catchtap="goBack"></image>
  </view>
  <view class='navbar-title' style='top:{{navTop}}px'>
    <view class="search-input">
      <span class="search-con">
        <view class="center-30f2b4d">
          <!-- 自定义区域 -->
          <image class="icon" src="{{searchIcon}}" catchtap="searchbegin" data-postname="{{inputValue}}"></image>
          <input class="search-size" confirm-type="search" value="{{inputValue}}" type="text" bindinput="getInputValue" placeholder="搜索品牌或商品" placeholder-class="phcolor"></input>
        </view>
      </span>
    </view>
  </view>
</view>
<!-- 自定义顶部导航 E -->
<view class="read-in">
  <!-- 最近搜索 S -->
    <view class="headline" wx:if="{{historyStorage.length!=0}}" hidden="{{historyStorageShow?false:true}}">
      <view class="head-headline justify">
        <view class="headline-size">最近搜索</view>
        <view class="right size" catchtap="remove">清除</view>
      </view>
      <!-- 最近搜索内容 S -->
      <view class="lately-main">
        <view class="chunk" wx:for="{{historyStorage}}" wx:key="index" bindtap='routeToSearchResPage' data-index='{{index}}'>
          <text>{{item}}</text>
        </view>
      </view>
      <!-- 最近搜索内容 E -->
    </view>
    <!-- 最近搜索 E -->
    <!-- 热门搜索 S -->
    <view class="headline">
      <view class="head-headline">
        <view class="headline-size">热门搜索</view> 
        <view class="liang">
          <view class="low-bo">
            <image class="icon-eye low-bottom" bindtap="reye" src="{{eyeIconOne}}" hidden="{{!eye}}"></image> 
            <image class="icon-eye low-bottom" bindtap="reye" src="{{eyeIcon}}" hidden="{{eye}}"></image> 
          </view>
          <view class="right size" bindtap="changeother">换一批</view>
        </view>
      </view>
      <!-- 热门推荐内容 S -->
      <view class="lately-main" hidden="{{eye?false:true}}">
        <view class="chunk" wx:for="{{falg ? hotsearch1 : hotsearch2}}" wx:key="index">
          <text>{{item.title}}</text>
        </view>
      </view>
      <!-- 热门推荐内容 E -->
    </view>
    <!-- 热门搜索 E -->
</view>


<!--搜索结果-->
<view class="searchresult" wx:if="{{inputValue != ''}}" hidden="{{searchresult?false:true}}">
  <view class="result" wx:for="{{searchResult}}" catchtap="searchbegin" wx:key="index" bindtap="getInputValue" data-postname="{{item.result}}">
    <view>{{item.result}}</view>
    <image src="{{upperLeftArrow}}"></image>
  </view>
</view>

大家应该都能看懂吧,可能有人就会问了 

<view class="result" wx:for="{{searchResult}}" catchtap="searchbegin" wx:key="index" bindtap="getInputValue" data-postname="{{item.result}}">
    <view>{{item.result}}</view>
    <image src="{{upperLeftArrow}}"></image>
  </view>

为什么这个wx:for用了两个点击事件,catchtap和bindtap,如果用两个catchtap或者bindtap可不可以,这里我就告诉你,不可以,亲测,会有冲突

我们先回顾一下catchtap和bindtap区别: 

  • DOM模型是一个树形结构,在DOM模型中,HTML元素是有层次的。当一个HTML元素上产生一个事件时,该事件会在DOM树中元素节点与根节点之间按特定的顺序传播,路径所经过的节点都会收到该事件,这个传播过程就是DOM事件流。
  • JS冒泡事件:当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的(所有祖先元素)中被触发。这 一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到DOM树的最上层

共同点:

在微信小程序的事件机制中,bindtap和catchtap都可以触发一个组件的点击事件

区别:

bindtap不能阻止事件冒泡 

catchtap可以阻止事件冒泡

wxss

page {
    background: white;
}
.navbar {
  width: 100%;
  overflow: hidden;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  flex-shrink: 0;
  background-color: white;
}

.navbar-title {
  width: 100%;
  box-sizing: border-box;
  padding-left: 40px;
  padding-right: 120px;
  height: 33px;
  line-height: 33px;
  position: fixed;
  left: 0;
  z-index: 10;
  color: #333;
  font-size: 16px;
  font-weight: bold;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
  display: flex;
}

.navbar-action-wrap {
  display: -webkit-flex;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  -webkit-align-items: center;
  align-items: center;
  left: 10px;
  z-index: 11;
  line-height: 1;
  padding-top: 4px;
  padding-bottom: 4px;
  position: fixed;
}

.navbar-action-group {
  border-radius: 20px;
  overflow: hidden;
}

.navbar-action_item {
  padding: 3px 0;
  color: #333;
}

.navbar-action-group .navbar-action_item {
  border-right: 1px solid #f0f0f0;
  padding: 3px 14px;
}

.navbar-action-group .last {
  border-right: none;
}

.navbar-title-size {
  font-size: 10px;
  margin-right: 20rpx;
  align-self: center;
  margin: 0 auto;
}

.navbar-title-size.active {
  color: red;
  border-bottom: 2px solid red;
}

.scroll-box {
  position: absolute;
  height: 100%;
}

.search-input {
  width: 100%;
  height: 28px;
  line-height: 28px;
  background: #f6f6f6;
  border-radius: 30rpx;
  /* margin-top: 2px; */
  font-size: 25rpx;
}

.search-con {
  display: flex;
  align-items: center;
}
.search-con .center-30f2b4d {
  height: 28px;
  line-height: 28px;
  flex: 1;
  display: flex;
  align-items: center;
}
.search-con .center-30f2b4d .icon {
  width: 15px;
  height: 15px;
  align-self: center;
  margin: 0 10px;
}
.search-con .center-30f2b4d .search-size {
  width: 60%; 
  font-size: 12px;
  font-family: "微软雅黑";
}
.phcolor {
  color: #D3D3D3;
}
/* nav E */
/* 标题部分 */
.read-in {
  margin-top: 64px;
  padding: 0 40rpx;
}
.headline {
  padding-top: .5rem;
}

.head-headline {
  width: 100%;
  height: 45rpx;
  position: relative;
  display: flex;
}
.liang {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.justify {
  display: flex;
  justify-content: space-between;
}
.headline-size {
  width: 185rpx;
  font-size: 35rpx;
  float: left;
}

.low-bo {
  /* flex: 1; */
  height: 45rpx;
  position: relative;
  align-self: center;
}
.low-bottom {
  /* position:absolute; */
  bottom:0px;
  padding:0 10rpx  0;
  margin:0px;
}
.size {
  /* flex: 1; */
  float: right;
  font-size: 30rpx;
  color: #d4237a;
  /* position: absolute; */
  bottom:0px;
  align-self: center;
 
}

/* 内容部分 */
.lately-main {
  margin-top: 20rpx;
  overflow: hidden; 
}
.lately-main .chunk {
  display: inline-block;
  font-size: 25rpx;
  line-height: 20rpx;
  padding: 20rpx 20rpx;
  background: #f5f5f5;
  margin-right: 30rpx;
  border: 1px solid #DCDCDC;
  border-radius: 30rpx;
  margin-bottom: 30rpx;
  float: left;
}

.searchresult {
  margin-top: 20px; 
  position: absolute;
  top: 55px;
  left: 0;
  width: 100%;
  background: #fff;
}
.result {
  height: 50px;
  line-height: 50px;
  text-align: left;
  border-bottom: 1px solid #eee;
  padding: 0 30rpx;
  color: #333;
  font-family: "微软雅黑";
  font-size: 30rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.result image {
  width: 50rpx;
  height: 50rpx;
}

 css就不用说太多了吧,不过有个小bug,我在调试的时候发现的,不过很好解决,这里就不多说了

js

const App = getApp()
Page({

  /**
   * 页面的初始数据
   */
  data: {
    // 自定义顶部导航
    navHeight: App.globalData.navHeight,
    navTop: App.globalData.navTop,
    // 图标
    leftIcon: "../../../img/icon/icon-left.png",
    searchIcon: "../../../img/icon/icon-search.png",
    eyeIconOne: "../../../img/icon/icon-eye-one.png",
    eyeIcon: "../../../img/icon/icon-eye.png",
    upperLeftArrow: "../../../img/icon/icon-upper-left-arrow.png",
    recommend: [ //热门推荐
      {
        title: "冰箱"
      },
      {
        title: "红魔手机"
      },
      {
        title: "洗衣机"
      },
      {
        title: "电视机"
      },
      {
        title: "冰箱 双门"
      },
      {
        title: "海尔洗衣机 滚筒"
      },
      {
        title: "手机自营"
      },
      {
        title: "小天鹅洗衣机全自动"
      },
      {
        title: "手机"
      },
      {
        title: "笔记本"
      }
    ],
    historyStorage: [],        //历史搜索
    historyStorageShow: false,
    falg: true,         //换一批
    hotsearch1: [{ title: "短裤" }, { title: "背带裙" }, { title: "牛仔裤男" }, { title: "运动 休闲男鞋" }, { title: "蕾丝连衣裙" }, { title: "电视" }, { title: "长裙" }, { title: "oppo" }, { title: "蓝牙耳机" }, { title: "女包" }, { title: "格力空调" }, { title: "魅族" }],
    hotsearch2: [{ title: "平板电脑" }, { title: "耳机" }, { title: "男鞋" }, { title: "iPhone" }, { title: "蕾丝连衣裙" }, { title: "电视" }, { title: "长裙" }, { title: "oppo" }, { title: "蓝牙耳机" }, { title: "女包" }, { title: "格力空调" }, { title: "魅族" }],
    // searchresult: false,
    inputValue: "",        //输入框输入的值
    replaceValue: "",     //替换输入框的值
    eye: true,        //显示隐藏
    searchresult: false,
    searchResult: [{ result: "苹果手机" }, { result: "手机支架" }, { result: "手机自营" }, { result: "手机套" }, { result: "手机膜" }, { result: "手机卡" }, { result: "手机报" }, { result: "苹果手机壳" }, { result: "手机车载支架" }]//虚拟的查询结果
  },
  // 点击返回上一级
  goBack: function() {
    let pages = getCurrentPages();      //获取小程序页面栈
    let beforePage = pages[pages.length - 2];       //获取上个页面的实例对象
    beforePage.setData({
      txt: "修改数据了"
    })
    beforePage.goUpdate();           //触发上个页面自定义的go_update()方法
    wx.navigateBack({
      delta: 1
    })
  },
  /**
   * 获取顶部固定高度
   */
  attached: function() {
    this.setData({
      navHeight: App.globalData.navHeight,
      navTop: App.globalData.navTop,
    })
  },
  /**
   * 换一批操作
   */
  changeother: function () {
    this.setData({
      falg: !this.data.falg
    })
  },

  /**
   * 热门搜索显示隐藏
   */
  reye: function () {
    this.setData({
      eye: !this.data.eye
    })
  },

  /**
   * 清除
   */
  remove: function () {
    var _this = this
    wx: wx.showModal({
      content: '确认清除所有历史记录?',
      success: function (res) {
        if (res.confirm) {
          wx: wx.removeStorage({
            key: 'historyStorage',
            success: function (res) {
              _this.setData({
                historyStorage: []
              })
              wx.setStorageSync("historyStorage", [])
            },
          })
        } else {
          console.log("点击取消")
        }
      },
    })
  },


  /**
   * 获取input的值
   */
  getInputValue(e) {
    // console.log("获取value值",e.detail)   // {value: "ff", cursor: 2}
    this.setData({
      inputValue: e.detail.value
    })
    this.setData({
      searchresult: true,
    })
  },

  /**
   * 点击搜索提交跳转并存储历史记录
   */
  searchbegin: function (e) {
    let _this = this
    var data = e.currentTarget.dataset;
    _this.data.replaceValue = e.currentTarget.dataset.postname
    // _this.data.replaceValue = 
    wx: wx.setStorage({
      key: 'historyStorage',
      data: _this.data.historyStorage.concat(_this.data.inputValue),
      data: _this.data.historyStorage.concat(_this.data.replaceValue)
    })
    // console.log(_this.data.inputValue)
    // console.log(_this.data.historyStorage)
    wx.navigateTo({
      url: '../../commodity/commodity-search-list/index?postName=' + data['postname']
    })
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {
    // 历史搜索
    let that = this
    wx.getStorage({
      key: 'historyStorage',
      success: function (res) {
        console.log(res.data)
        that.setData({
          historyStorageShow: true,
          historyStorage: res.data
        })
      }
    })
  },
  //点击进入详情页
  goToList: function (e) {
    
  },
  goUpdate: function() {
    this.onLoad()
    console.log("我更新啦")
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function() {

  }
})

js基本上都有备注,这里就不说太多了,我就说一下这个函数吧

searchbegin: function (e) {
    let _this = this
    var data = e.currentTarget.dataset;
    _this.data.replaceValue = e.currentTarget.dataset.postname
    // _this.data.replaceValue = 
    wx: wx.setStorage({
      key: 'historyStorage',
      data: _this.data.historyStorage.concat(_this.data.inputValue),
      data: _this.data.historyStorage.concat(_this.data.replaceValue)
    })
    // console.log(_this.data.inputValue)
    // console.log(_this.data.historyStorage)
    wx.navigateTo({
      url: '../../commodity/commodity-search-list/index?postName=' + data['postname']
    })
  },

自定义的data在点击执行这个函数的时候获取input框输入的value值,这个应该都明白吧

_this.data.replaceValue这个是在data里面定义了一个变量名为replaceValue,然后将e.currentTarget.dataset.postname获取到的值赋值给replaceValue,下面的就不用多说了吧,将拿到的值存储到historyStorage空列表里面,下面的是点击搜索跳转到的结果列表

最后给大家看一下效果图吧

很简单,也很繁琐,不过还是看项目设计,这样用户体验很不错

如果对你有帮助希望关注一下,多多支持,有什么问题可以下方留言共同学习

发布了151 篇原创文章 · 获赞 28 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_42543264/article/details/105511603