转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 需求
在城市选择页的搜索框中,无论输入的是“拼音”还是“文字”,搜索框下方都会显示筛选出的包含对应“关键字”的城市。当没有对应城市时,则显示“没有找到匹配数据”:
2 “搜索框”布局
需求分析:
当在搜索框输入内容后,搜索框下方显示“搜索”出的对应内容,城市列表的内容不见了:
但,城市列表的内容并不是“消失”了,它其实只是被“搜索内容”给遮挡住了。当进行搜索时,则显示对应搜索内容;当没有进行搜索时,就显示城市列表内容。
而进行搜索,却没有对应的搜索城市时,显示的“没有找到匹配数据”,其实也算是另一种对应的“搜索内容”。只要没有对应的城市,就显示“没有找到匹配数据”这一结果:
OK,理清思路,我们就可以进行代码的编写了。
1️⃣打开 city 下 components 中的 Search.vue :
<template>
<div> <!-- 1️⃣-④:添加一层 div 包裹所有的内容; -->
<div class="search">
<input class="search-input" type="text" placeholder="输入城市名或拼音">
</div>
<div class="search-content"> <!-- 1️⃣-①:添加一个类名为 search-content 的 div,
它表示搜索内容区域,里边的 ul 标签中有两个类名为 search-item
的 li 标签;-->
<ul>
<li class="search-item border-bottom"></li> <!-- 1️⃣-②:一个 li 标签用来循环搜索
到的对应数据; -->
<!-- 1️⃣-⑦:两个 li 标签都增加
border-bottom 类名,添加一像素下边
框。-->
<li class="search-item border-bottom">
没有找到匹配数据
</li> <!--1️⃣-③:一个 li 标签的内容为“没有找到匹配数据”,作为没有对应搜索数据时
显示的“搜索结果”; -->
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'CitySearch'
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.search
height: .72rem
padding: 0 .1rem
background: $bgColor
.search-input
box-sizing: border-box
width: 100%
padding: 0 .1rem
height: .62rem
line-height: .62rem
color: #666
text-align: center
border-radius: .06rem
.search-content /*
1️⃣-⑤:.sear-content 绝对定位,距离顶部 1.58rem,空出头部和搜索框的距离;
z-index 设为 1,添加背景色 #eee;
❗️注意:.search-content 与 .search 平级。
*/
overflow: hidden
position: absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
z-index: 1
background: #eee
.search-item /*
1️⃣-⑥:.search-item 的 padding-left 设为 0.2rem,line-height 0.62rem,
背景色设为白色,字体颜色为 #666;
*/
padding-left: .2rem
line-height: .62rem
background: #fff
color: #666
</style>
保存后,返回页面查看,搜索内容区域显示在页面上,控制台无报错:
3 “搜索框”逻辑
🔗前置知识:
《JavaScript 初识——④ 流程控制语句》
《JavaScript 基础——JS 数组:② ES5 数组方法》
《Vue 初识——② 简易 TodoList》
《Vue 入门——⑦ 条件渲染》
2️⃣ Search.vue 要显示搜索的城市,就需要接收外部传来的城市数据。打开 city 下的 City.vue :
<template>
<div>
<city-header></city-header>
<city-search :cities="cities"></city-search> <!-- 2️⃣-①:通过属性 :cities 传递数据
cities 给 Search.vue; -->
<city-list :hot="hotCities" :cities="cities" :letter="letter"></city-list>
<city-alphabet :cities="cities" @change="handleLetterChange"></city-alphabet>
</div>
</template>
<script>
import axios from 'axios'
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
export default {
name: 'City',
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet
},
data () {
return {
hotCities: [],
cities: {},
letter: ''
}
},
methods: {
getCityInfo () {
axios.get('/api/city.json')
.then(this.getCityInfoSucc)
},
getCityInfoSucc (res) {
res = res.data
if (res.ret && res.data) {
const data = res.data
this.hotCities = data.hotCities
this.cities = data.cities
}
},
handleLetterChange (letter) {
this.letter = letter
}
},
mounted () {
this.getCityInfo()
}
}
</script>
<style></style>
2️⃣-②:打开 city 下 components 中的 Search.vue ;
<template>
<div>
<div class="search">
<input
class="search-input"
type="text"
placeholder="输入城市名或拼音"
v-model="keyword"
> <!-- 2️⃣-⑤:input 框上使用 v-model 指令与数据 keyword 进行双向绑定; -->
</div>
<div class="search-content">
<ul>
<li
class="search-item border-bottom"
v-for="item of list"
:key="item.id"
> <!-- 2️⃣-⑯:循环 list,绑定 key 值为每个循环项的 id; -->
{{item.name}} <!-- 2️⃣-⑰:li 标签的内容为每一个循环项的 name(即包含 keyword 的
城市名)。 -->
</li>
<li class="search-item border-bottom">
没有找到匹配数据
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'CitySearch',
props: { // 2️⃣-③:在 props 中接收 cities,它的类型为 Object;
cities: Object
},
data () {
return {
keyword: '', /*
2️⃣-④:在 data 中定义一个变量 keyword,表示 input 框中输入的关键字,
默认为空;
*/
list: [], // 2️⃣-⑥:定义一个变量 list,用来放最终的搜索结果,它的值为空数组;
timer: null // 2️⃣-⑦:定义一个变量 timer 默认为空,方便用节流函数提高代码执行效率;
}
},
watch: {
keyword () {
if (this.timer) { // 2️⃣-⑧:如果 this.timer 已经存在,就清除 this.timer;
clearTimeout(this.timer)
}
this.timer = setTimeout(() => { /*
2️⃣-⑨:否则就创建一个 timer,让它延迟 100 毫秒执行
箭头函数中的内容(即当 keyword 发生改变时,再让它延
迟 100 毫秒执行);
*/
const result = [] // 2️⃣-⑩:定义一个变量 result 为空数组;
for (let i in this.cities) { // 2️⃣-⑪:使用 for...in 循环对象 cities;
this.cities[i].forEach((value) => { /*
2️⃣-⑫:遍历 cities 下的每一个数组,并接收
一个参数 value(即对数据 cities 中的 A/B/C
所对应的内容进行遍历,value 就是 A/B/C 中的
每个城市的数据,包含 id、spell、name);
*/
// 2️⃣-⑬:如果 value.spell 或 value.name 中能找到 keyword 对应的关键字;
if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
result.push(value) // 2️⃣-⑭:就把这一项内容,添加到 result 中;
}
})
}
this.list = result /*
2️⃣-⑮:循环完成后,把结果 result 赋值给 list(即 list 中存储
了包含 keyword 的城市数据);
*/
}, 100)
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.search
height: .72rem
padding: 0 .1rem
background: $bgColor
.search-input
box-sizing: border-box
width: 100%
padding: 0 .1rem
height: .62rem
line-height: .62rem
color: #666
text-align: center
border-radius: .06rem
.search-content
overflow: hidden
position: absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
z-index: 1
background: #eee
.search-item
padding-left: .2rem
line-height: .62rem
background: #fff
color: #666
</style>
保存后,返回页面查看。当输入拼音或文字时,都能显示出对应的城市名。
但存在几个问题:
- “没有找到匹配数据”一直在页面显示;
- 搜索结束清空输入框后,页面没有清空“搜索内容”,且一直没有显示出城市列表的内容;
- 搜索出的结果过多时,不能滚动查看。
❓如何解决这些问题呢?
答:控制页面内容“显示”与“隐藏”,可以使用 v-show 指令来解决。不能滚动的问题,我们依然可以通过 BetterScroll 来解决。
3️⃣返回 city 下 components 中的 Search.vue :
<template>
<div>
<div class="search">
<input
class="search-input"
type="text"
placeholder="输入城市名或拼音"
v-model="keyword"
>
</div>
<div
class="search-content"
ref="search"
v-show="keyword"
> <!-- 3️⃣-②:给 .search-content 添加 ref 名为 search; -->
<!-- 3️⃣-⑦:添加 v-show 指令,值为 keyword(即当 keyword 存在时,
才显示搜索内容 .search-content)。 -->
<ul>
<li
class="search-item border-bottom"
v-for="item of list"
:key="item.id"
>
{{item.name}}
</li>
<li
class="search-item border-bottom"
v-show="hasNoData"
> <!-- 3️⃣-⑥:在第二个 li 标签上,添加 v-show 指令,hasNoData 为 true 时,
显示“没有找到匹配数据”; -->
没有找到匹配数据
</li>
</ul>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll' // 3️⃣-①:引入 BetterScroll;
export default {
name: 'CitySearch',
props: {
cities: Object
},
data () {
return {
keyword: '',
list: [],
timer: null
}
},
computed: { /*
3️⃣-⑤:在 computed 中添加一个计算属性 hasNoData(即 list 中没有内容、
搜索结果不存在时,hasNoData 为 true);
*/
hasNoData () {
return !this.list.length
}
},
watch: {
keyword () {
if (this.timer) {
clearTimeout(this.timer)
}
if (!this.keyword) { /*
3️⃣-④:侦听 keyword 时,如果没有关键字,就让 list 变为
空数组后返回(即清空搜索的内容);
*/
this.list = []
return
}
this.timer = setTimeout(() => {
const result = []
for (let i in this.cities) {
this.cities[i].forEach((value) => {
if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
result.push(value)
}
})
}
this.list = result
}, 100)
}
},
mounted () { // 3️⃣-③:在 mounted 中,创建一个 scroll,传入 search;
this.scroll = new BScroll(this.$refs.search)
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.search
height: .72rem
padding: 0 .1rem
background: $bgColor
.search-input
box-sizing: border-box
width: 100%
padding: 0 .1rem
height: .62rem
line-height: .62rem
color: #666
text-align: center
border-radius: .06rem
.search-content
overflow: hidden
position: absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
z-index: 1
background: #eee
.search-item
padding-left: .2rem
line-height: .62rem
background: #fff
color: #666
</style>
保存后,返回页面查看,刚才的问题都解决了,功能一切正常:
以上,我们完成了城市选择页“搜索”部分的功能。
祝好,qdywxs ♥ you!