這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
?記錄分享每一個日常開發專案中的實用小知識,不整那些虛頭巴腦的框架理論與原理,之前分享過抽獎功能、簽字功能等,有興趣的可以看看本人以前的分享, ?今天要分享的實用小知識是最近專案中遇到的標簽相關的功能,我不知道叫啥,姑且稱之為【多行標簽展開隱藏】功能吧,類似于多行文本展開折疊功能,如果超過最大行數則顯示展開隱藏按鈕,如果不超過則不顯示按鈕,多行文本展開與折疊功能在網上有相當多的文章了,也有許多開源的封裝組件,而多行標簽展開隱藏的文章卻比較少,剛好最近我也遇到了這個功能,所以就單獨拿出來與大家分享如何實作,
出處
?【多行標簽展開與隱藏】該功能我們平時可能沒注意一般在哪里會有,其實最常見的就是各種APP的搜索頁面的歷史記錄這里,下面是我從拼多多(左)和騰訊學堂小程式(右)截下來的功能樣式:

其它APP一般搜索的歷史記錄這里都有這個小功能,比如京東、支付寶、淘寶、抖音、快手等,可能稍有點兒不一樣,有的是按鈕樣式,有的是只有展開沒有收起功能,可能我們用過了很多年平時都沒有注意到這個小功能,有想了解的可以去看一看哈,如果有一天你們需要開發一個搜索頁面的話產品就很有可能出這樣的一個功能,接下來我們就來看看這種功能我們該如何實作,
功能實作
我們先看實作的效果圖,然后再分析如何實作,效果圖如下:
【樣式一】:標簽容器和展開隱藏按鈕分開(效果圖樣式一)
?標簽容器和按鈕分開的這種樣式功能實作起來的話我個人覺得難度稍微簡單一些,下面我們看看如何實作這種分開的功能,
第一種方法:通過與第一個標簽左偏移值對比實作
原理:遍歷每個標簽然后通過與第一個標簽左偏移值對比,如果有幾個相同偏移值則說明有幾個換行
具體實作上代碼:
<div >
<div >人工智能</div>
<div >人工智能與應用</div>
<div >行業分析與市場資料</div>
<div >標簽標簽標簽標簽標簽標簽標簽標簽</div>
<div >標簽</div>
<div >啊啊啊</div>
<div >寶寶貝貝</div>
<div >微信</div>
<div >吧啊啊</div>
<div >哦哦哦哦哦哦哦哦</div>
</div>
<div >展開 ∨</div>
<script>
const listCon = document.querySelector('.list-con-1')
const expandBtn = document.querySelector('.expand-1')
console.log(listCon.children)
// HTMLCollection物件 item()、namedItem()方法 length屬性
let firstLabelOffsetLeft = 0 // 第一個標簽左側偏移
let line = 1 // 記錄行
const len = listCon.children.length
for(let i = 0; i < len; i++) {
const _offsetLeft = listCon.children.item(i).offsetLeft
if (i === 0) {
firstLabelOffsetLeft = _offsetLeft
} else if (firstLabelOffsetLeft === _offsetLeft) {
line++
console.log(line + '行')
}
}
// 如果大于一行則隱藏
if (line > 2) {
expandBtn.style = 'display: show'
} else {
expandBtn.style = 'display: none'
}
expandBtn.addEventListener('click', () => {
const _classList = listCon.classList
if (_classList.contains('list-expand')) {
expandBtn.innerHTML = '展開 ∨'
} else {
expandBtn.innerHTML = '收起 ∧'
}
_classList.toggle('list-expand') // 這個更簡潔
})
</script>
決議:HTML布局就不用多說了,是個前端都知道該怎么搞,如果不知道趁早送外賣去吧,多說無益,把機會留給其他人,其次CSS應該也是比較簡單的,注意的是有個前提需要先規定容器的最大高度,然后使用overflow超出隱藏,這樣展開就直接去掉該屬性,讓標簽自己撐開即可,JavaScript部分我這里沒有使用啥框架,因為這塊實作就是個簡單的Demo所以就用純原生寫比較方便,這里我們先獲取容器,然后獲取容器的孩子節點(這里我們也可以直接通過className查詢出所有標簽元素),回傳的是一個可遍歷的變簽物件,然后我們記錄第一個標簽的offsetLeft左偏移值,接下來遍歷所有的標簽元素,如果有與第一個標簽相同的值則累加,最終line表示有幾行,如果超過我們最大行數(demo超出2行隱藏)則顯示展開隱藏按鈕,
第二種方法:通過計算容器高度對比
原理:通過容器底部與標簽top比較,如果有top值大于容器底部bottom則表示超出容器隱藏,
具體上代碼:
<script>
const listCon2 = document.querySelector('.list-con-2')
const expandBtn2 = document.querySelector('.expand-2')
const listCon2Height = listCon2.getBoundingClientRect().bottom
const len2 = listCon2.children.length
for(let i = 0; i < len2; i++) {
const _top = listCon2.children.item(i).getBoundingClientRect().top
// 通過top判斷如果有標簽大于容器bottom則隱藏
if (_top >= listCon2Height) {
expandBtn2.style = 'display: show'
break
} else {
expandBtn2.style = 'display: none'
}
}
expandBtn2.addEventListener('click', () => {
const _classList = listCon2.classList
// console.log(_classList)
if (_classList.contains('list-expand')) {
expandBtn2.innerHTML = '展開 ∨'
} else {
expandBtn2.innerHTML = '收起 ∧'
}
_classList.toggle('list-expand')
})
</script>
決議:HTML和CSS同方法一同,不同點在于這里是通過getBoundingClientRect()方法來判斷,還是遍歷所有標簽,不同的是如果有標簽的top值大于等于了容器的bottom值,則說明了標簽已超出容器,則要顯示展開隱藏按鈕,展開隱藏還是通過容器overflow屬性來實作比較簡單,
【樣式二】:展開隱藏按鈕和標簽同級(效果圖樣式二)
?這種樣式也是絕大部分APP產品使用的風格,不信你可以打開抖音商城或汽車之家的搜索歷史,十個產品九個是這樣設計的,不是這樣的我倒立洗頭, ?這種放在同級的就相對稍微難一點,因為要把展開隱藏按鈕塞到標簽的最后,如果是隱藏的話就要切割標簽展示數量,那下面我就帶大家看看我是是如何實作的,
方法一:通過遍歷高度判斷
原理:同樣式一的高度判斷一樣,通過容器底部bottom與標簽top比較,如果有top值大于容器頂部bottom則表示超出容器隱藏,不同的是如何計算標簽展示的長度,有個前提是按鈕和標簽的的寬度要做限制,最好是一行能放一個標簽和按鈕,
具體實作上代碼:
<div id="app3">
<div :>
<div v-for="item in labelArr.slice(0, labelLength)">{{ item }}</div>
<div v-if="showExpandBtn" @click="changeExpand">{{ !isExpand ? '展開 ▼' : '隱藏 ▲' }}</div>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp, nextTick } = Vue
createApp({
props: {
maxLine: {
type: Number,
default: 2
}
},
data () {
return {
labelArr: [],
isExpand: false,
showExpandBtn: false,
labelLength: 0,
hideLength: 0
}
},
mounted () {
const labels = ['人工智能', '人工智能與應用', '行業分析與市場資料', '標簽標簽標簽標簽標簽標簽標簽', '標簽A', '啊啊啊', '寶寶貝貝', '微信', '吧啊啊', '哦哦哦哦哦哦哦哦', '人工智能', '人工智能與應用']
this.labelArr = labels
this.labelLength = labels.length
nextTick(() => {
this.init()
})
},
methods: {
init () {
const listCon = document.querySelector('.list-con-3')
const labels = listCon.querySelectorAll('.label:not(.expand-btn)')
const expandBtn = listCon.querySelector('.expand-btn')
let labelIndex = 0 // 渲染到第幾個
const listConBottom = listCon.getBoundingClientRect().bottom // 容器底部距視口頂部距離
for(let i = 0; i < labels.length; i++) {
const _top = labels[i].getBoundingClientRect().top
if (_top >= listConBottom ) { // 如果有標簽頂部距離超過容器底部則表示超出容器隱藏
this.showExpandBtn = true
console.log('第幾個索引標簽停止', i)
labelIndex = i
break
} else {
this.showExpandBtn = false
}
}
if (!this.showExpandBtn) {
return
}
nextTick(() => {
const listConRect = listCon.getBoundingClientRect()
const expandBtn = listCon.querySelector('.expand-btn')
const expandBtnWidth = expandBtn.getBoundingClientRect().width
const labelMaringRight = parseInt(window.getComputedStyle(labels[0]).marginRight)
for (let i = labelIndex -1; i >= 0; i--) {
const labelRight = labels[i].getBoundingClientRect().right - listConRect.left
if (labelRight + labelMaringRight + expandBtnWidth <= listConRect.width) {
this.hideLength = i + 1
this.labelLength = this.hideLength
break
}
}
})
},
changeExpand () {
this.isExpand = !this.isExpand
console.log(this.labelLength)
if (this.isExpand) {
this.labelLength = this.labelArr.length
} else {
this.labelLength = this.hideLength
}
}
}
}).mount('#app3')
</script>
決議:同級樣式Demo我們使用vue來實作,HTML布局和CSS樣式沒有啥可說的,還是那就話,不行真就送外賣去比較合適,這里我們主要分析一下Javascript部分,還是先通過getBoundingClientRect()方法來獲取容器的bottom和標簽的top,通過遍歷每個標簽來對比是否超出容器,然后我們拿到第一個超出容器的標簽序號,就是我們要截斷的長度,這里是通過陣列的slice()方法來截取標簽長度,接下來最關建的如何把按鈕拼接上去,因為標簽的寬度是不定的,我們要把按鈕顯示在最后,我們并不確定按鈕拼接到最后是不是會導致寬度不夠超出,所以我們倒敘遍歷標簽,如果(最后一個標簽的右邊到容器的距離right值+標簽的margin值+按鈕的width)和小于容器寬度,則說明展示隱藏按鈕可以直接拼接在后面,否則標簽陣列長度就要再減一位來判斷是否滿足,然后展開隱藏功能就通過切換原標簽長度和截取的標簽長度來完成即可,
方法二:通過與第一個標簽左偏移值對比實作
原理:同樣式一的方法原理,遍歷每個標簽然后通過與第一個標簽左偏移值對比判斷是否超出行數,然后長度截取同方法一一致,
直接上代碼:
<script>
const { createApp, nextTick } = Vue
createApp({
data () {
return {
labelList: [],
isExpand: false,
showExpandBtn: false,
labelLength: 0,
hideLength: 0
}
},
mounted () {
const labels = ['人工智能', '人工智能與應用', '行業分析與市場資料報告行業分析與市場資料報告', '標簽標簽標簽標簽標簽標簽標簽', '標簽A', '啊啊啊', '寶寶貝貝', '微信', '吧啊啊', '哦哦哦哦哦哦哦哦', '人工智能', '人工智能與應用']
this.labelList = labels
this.labelLength = labels.length
nextTick(() => {
this.init()
})
},
methods: {
init () {
const listCon = document.querySelector('.list-con-4')
const labels = listCon.querySelectorAll('.label:not(.expand-btn)')
const firstLabelOffsetLeft = labels[0].getBoundingClientRect().left // 第一個標簽左側偏移量
const labelMaringRight = parseInt(window.getComputedStyle(labels[0]).marginRight)
let line = 0 // 幾行
let labelIndex = 0 // 渲染第幾個
for(let i = 0; i < labels.length; i++) {
const _offsetLeft = labels[i].getBoundingClientRect().left
if (firstLabelOffsetLeft === _offsetLeft) {
line += 1
}
console.log(line, i)
if (line > 2) {
this.showExpandBtn = true
labelIndex = i
// this.labelLength = this.hideLength
break
} else {
this.showExpandBtn = false
}
}
if (!this.showExpandBtn) {
return
}
nextTick(() => {
const listConRect = listCon.getBoundingClientRect()
const expandBtn = listCon.querySelector('.expand-btn')
console.log(listConRect, expandBtn.getBoundingClientRect())
const expandBtnWidth = expandBtn.getBoundingClientRect().width
for (let i = labelIndex -1; i >= 0; i--) {
console.log(labels[i])
const labelRight = labels[i].getBoundingClientRect().right - listConRect.left
console.log(labelRight, expandBtnWidth, labelMaringRight)
if (labelRight + labelMaringRight + expandBtnWidth <= listConRect.width) {
this.hideLength = i + 1
this.labelLength = this.hideLength
break
}
}
})
},
changeExpand () {
this.isExpand = !this.isExpand
if (this.isExpand) {
this.labelLength = this.labelList.length
} else {
this.labelLength = this.hideLength
}
}
}
}).mount('#app4')
</script>
這里也無需多做解釋了,直接看代碼即可,
結尾
上面就是【多行標簽展開隱藏】功能的基本實作原理,網上相關實作比較少,我也是只用了Javascript來實作,如果可以純靠CSS實作,有更簡單或更好的方法實作可以留言相互交流學,代碼沒有封裝成組件,但是具有一些參考意義,用于生產可以自己去封裝成組件使用,完整的代碼在我的GitHub倉庫,
本文轉載于:
https://juejin.cn/post/7251394142683742269
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556551.html
標籤:Html/Css
下一篇:返回列表

