微信小程序的搜索高亮、自定义导航条等踩坑记录

原文地址:https://oomabc.com/articledetail?atclid=7421fe13daad46389791463f51d3395d

前言
我在写这个博客的小程序过程中,遇到了很多的问题。之前断断续续也写过不少JavaScript和css,不过都是半吊子。所以在看了一会小程序demo代码,就开工了。虽然在一天之后就基本完成了大体功能框架,但依旧在细节上碰到了不少问题。本博客会将我遇到的一些问题和自己的解决方案贴在下面,纯粹记录一下。

图片

搜索高亮
不管是电商网站还是博客,亦或者是招聘网站,通常都少不了搜索功能。而搜索通常都会有一个基础应用,那就是关键字高亮,因为输入的关键字可能包含多个词语,而当多个词语命中对应结果时,这几个词语可能不是连续出现,所以这些高亮通常是后端接口通过搜索引擎框架实现。

在HTML中,后端在返回的结果数据中,用标签(例如:<span class="keyword">需要高亮的关键字</span>)将命中的关键字包起来,然后前端通过css样式将这些关键字进行特殊标记。

图片


按照这个思路,依旧是通过后端将命中关键字用小程序的<text class="keyword"></text>标签包起来,前端通过样式对其进行高亮。很遗憾,小程序直接将这段文字连同标签一起展示了,而且网上也没有查到让标签不展示的方法,所以这个思路行不通。

在小程序中,我们可以通过wx:for来实现标签的动态生成,然后根据具体参数来确定text标签是否需要添加高亮class。

后端依旧将高亮的关键字进行特殊标记,我这里使用HHtextHHS和HHtextHHE作为高亮的开始和结束标签,然后通过js进行字符串分割,最终返回的数据结构如下:

[
    {content : '重温', type : 0},   //content就是要显示的文本内容,type=0表示不高亮
    {content : 'Java', type : 1},   //type=1表示要高亮
    {content : '设计模式——', type : 0},
    {content : '工厂', type : 1},
    {content : '模式', type : 0}
]
实现带高亮标签的字符串转为数组的js方法如下:

//入参非空判断在上层已处理
splitTitle : function(s) {
    //保存最终返回的结果
    var rslist = [];

    //根据高亮开始标签进行字符串分割
    var arr1 = s.split('HHtextHHS');

    //遍历分割之后的每一个字符串,进行结束标签判断
    for (var i in arr1) {
        var word1 = arr1[i];

        //过滤空字符串
        if (word1 && word1.length > 0) {

            //如果有结束标签,则进行高亮字符串截取,并设置高亮标志位
            var indexEnd = word1.indexOf('HHtextHHE');
            if (indexEnd >= 0) {
                rslist.push({
                    content: word1.substring(0, indexEnd),
                    type: 1
                });

                //剩余不需要高亮
                //注意:这里的处理方式是基于没有标签嵌套的情况,如果有嵌套,这里需要另写逻辑
                var wordEnd = word1.substring(indexEnd + 9);
                if (wordEnd && wordEnd.length > 0) {
                    rslist.push({
                        content: wordEnd,
                        type: 0
                    });
                }
            } else {//没有结束标签,则表示当前字符串不是需要高亮的
                rslist.push({
                    content: word1,
                    type: 0
                });
            }

        }
    }

    return rslist;
}
对应在wxml中,循环显示这个数组对象的标签写法如下:

<!-- 这里的splitTitleList就是上面的js方法返回的数组对象 -->
<text class="articleTitle">
    <text wx:for="{{splitTitleList}}">
        <text class="{{item.type == 1 ? 'keywordHighLight' : ''}}">{{item.content}}</text>
    </text>
</text>
高亮样式就是很简单的wxss:

.keywordHighLight{
    color : red;
}
搜索框放大镜icon
图片

通常的搜索输入框都会有一个放大镜进行icon标记,既简介又功能清晰。所以,我也想在自己的小程序中也实现这个样式,之前写类似的前端代码很少,所以我打算通过background-image来引入放大镜的图标。 结果很遗憾:

template/topSearch.wxss 中的本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用<img>标签。
请参考文档:https://mp.weixin.qq.com/debug/wxadoc/dev/qa.html#%E6%9C%AC%E5%9C%B0%E8%B5%84%E6%BA%90%E6%97%A0%E6%B3%95%E9%80%9A%E8%BF%87-css-%E8%8E%B7%E5%8F%96
  39 |   margin-left: 15px;
  40 |
> 41 |   background-image: url('/image/search.pn');
     |                    ^
  42 | }
  43 |
  44 | .searchIconImg{
微信小程序提醒我们,WXSS无法直接引入本地图片,只能使用网络图片或者以Base64格式,或者在WXML中用<img>引入。

所以,我就尝试在WXML中,直接用style的行内样式通过background-image引入图片。嘿嘿,很简单的通过view和input两个标签实现了图标显示,而且微信开发者工具中可以正常显示并调试(不过,input标签有个bug,会显示两个图标)。最终经过一番折腾,终于进入真机调试环节,结果依旧是很遗憾,在手机上无法显示这些图片,而且最终审核发布之后,手机端还是无法显示。

这下终于明白当初那些提示的含义了,接下来我就使用image试试。结果确实是可以的,不过要进行样式微调。大概的WXML和样式如下:

<view class="topSearchInputOuter">
    <img src="/image/search.png" class="searchIconImg">
    <input class="topSearchInput" value="{{queryWord}}" focus="true" auto-focus="true" name="topSearchInput" placeholder=" 热门关键字" bindconfirm="searchArticles">
</view>
.topSearchInput{
    font-size: 13px;
    border: 0px solid #EFEFEF;
    width: 220px;
    line-height: 33px;
    height : 33px;
    color : gray;
    padding-left : 5px;
}

.topSearchInputOuter{
    background-color: #EFEFEF;
    border-radius : 15px;
    padding-left : 8px;
    width : 240px;
    line-height : 33px;
    display: flex;
}
.searchIconImg{
    width : 15px;
    height : 15px;
    vertical-align: middle;
    justify-content: center;
    margin-top:9px;
}
自定义状态栏
在实现状态栏搜索功能的时候,还遇到的另一个问题就是,微信默认状态栏是不允许修改的。如果需要自定义状态栏,需要在app.json文件的window参数中,设置navigationStyle为custom。

"window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "欢迎访问OoM技术博客",
    "navigationBarTextStyle": "black",
    "backgroundColor": "#f1e8e8",
    "navigationStyle": "custom" //这里设置状态栏(导航栏)样式为自定义
}
这样一来,顶部除了微信的胶囊按钮之外,一片清爽。

不过这样一来,我们就需要自己实现顶部状态栏+导航条的设备自适应功能了。由于我也不是专业的前端,因此网上搜寻了针对全面屏和非全面屏两种设备的自适应方案。

通过判断设备是否是全面屏(iPhone X),进行状态栏高度和导航栏高度动态调整。js代码如下:

var vm = this;
wx.getSystemInfo({  //wx提供的获得设备系统信息的方法
    success: function (res) {
        let totalTopHeight = 68
        if (res.model.indexOf('iPhone X') !== -1) {
            totalTopHeight = 88
        } else if (res.model.indexOf('iPhone') !== -1) {
            totalTopHeight = 64
        }

        var statusBar = {};

        //设置状态栏相关参数
        statusBar.statusBarHeight = res.statusBarHeight;
        statusBar.titleBarHeight = totalTopHeight - res.statusBarHeight;
        statusBar.queryWord = vm.globalData.searchDefaultPlacehoder;

        //设定全局参数,其他页面通过 const app = getApp();app.globalData.statusBar 获得
        vm.globalData.statusBar = statusBar;
    },
    fail: function() {
        vm.globalData.statusBar = {
            statusBarHeight : 0,
            titleBarHeight : 0
        };
    }
});
对应的在WXML中的使用,下面给出模板化应用的例子:

<template name="topNavTmp">

<view class="topNavView" style="padding-top: 0px; height :0px">

        <view class="header" style="">
            <!-- 状态栏 --->
            <view class="status-bar" style="height:{{barStatus.statusBarHeight}}px"></view>
            <!-- 导航栏 --->
            <view class="title-bar" style="height:{{barStatus.titleBarHeight}}px; line-height : {{barStatus.titleBarHeight}}px;">
                <!-- 这里是顶部的返回和home按钮 -->
                <view class="topNavLeft">
                    <view class="topNav-one"><img bindtap="clickAndBack" class="topNavImg" src="/image/back-1.png"></view>
                    <view class="topNav-sep"></view>
                    <view class="topNav-two"><img bindtap="clickAndHome" class="topNavImg" src="/image/home-1.png"></view>
                </view>
                <!-- 标题 -->
                {{barStatus.topNavTitle}}
                <view class="tablet"></view>
            </view>
        </view>
    </view>

    <!-- 将topNavView的fixed样式,用同样高度顶下去,不然后面的样式会浮上来 -->
    <view style="height : {{barStatus.statusBarHeight+barStatus.titleBarHeight}}px; text-align : center;"></view>
</template>
在引用的页面使用:

<!-- 在其它页面引入模板的使用 -->
<import src="/template/topNav.wxml">
<!-- 这里使用模板,传入参数 -->
<template is="topNavTmp" data="{{barStatus}}"></template>

相关推荐