主頁 > 企業開發 > 記錄--多行標簽超出展開折疊功能

記錄--多行標簽超出展開折疊功能

2023-07-04 08:33:09 企業開發

這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言

?記錄分享每一個日常開發專案中的實用小知識,不整那些虛頭巴腦的框架理論與原理,之前分享過抽獎功能、簽字功能等,有興趣的可以看看本人以前的分享, ?今天要分享的實用小知識是最近專案中遇到的標簽相關的功能,我不知道叫啥,姑且稱之為【多行標簽展開隱藏】功能吧,類似于多行文本展開折疊功能,如果超過最大行數則顯示展開隱藏按鈕,如果不超過則不顯示按鈕,多行文本展開與折疊功能在網上有相當多的文章了,也有許多開源的封裝組件,而多行標簽展開隱藏的文章卻比較少,剛好最近我也遇到了這個功能,所以就單獨拿出來與大家分享如何實作,

出處

?【多行標簽展開與隱藏】該功能我們平時可能沒注意一般在哪里會有,其實最常見的就是各種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>

決議:HTMLCSS同方法一同,不同點在于這里是通過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

上一篇:聊聊Excel決議:如何處理百萬行EXCEL檔案

下一篇:返回列表

標籤雲
其他(162041) Python(38266) JavaScript(25520) Java(18286) C(15238) 區塊鏈(8275) C#(7972) AI(7469) 爪哇(7425) MySQL(7281) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4609) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2438) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) HtmlCss(1986) .NET技术(1985) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1882) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 記錄--多行標簽超出展開折疊功能

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 記錄分享每一個日常開發專案中的實用小知識,不整那些虛頭巴腦的框架理論與原理,之前分享過抽獎功能、簽字功能等,有興趣的可以看看本人以前的分享。 今天要分享的實用小知識是最近專案中遇到的標簽相關的功能,我不知道叫啥,姑且稱之為【多行標簽 ......

    uj5u.com 2023-07-04 08:33:09 more
  • 聊聊Excel決議:如何處理百萬行EXCEL檔案

    如何恰當地處理資料量龐大的Excel檔案,避免記憶體溢位問題?本文將對比分析業界主流的Excel決議技術,并給出解決方案。 ......

    uj5u.com 2023-07-04 08:33:01 more
  • 前端Vue自定義可自由滾動精美tabs選項卡標簽欄標題欄 可設定背景

    #### 前端Vue自定義可自由滾動精美tabs選項卡標簽欄標題欄 可設定背景顏色, 下載完整代碼請訪問uni-app插件市場地址:https://ext.dcloud.net.cn/plugin?id=13313 #### 效果圖如下: #### ![](https://p3-juejin.byt ......

    uj5u.com 2023-07-04 08:32:58 more
  • 1.5 撰寫自定位ShellCode彈窗

    在筆者上一篇文章中簡單的介紹了如何運用匯編語言撰寫一段彈窗代碼,雖然簡易`ShellCode`可以被正常執行,但卻存在很多問題,由于采用了硬編址的方式來呼叫相應API函式的,那么就會存在一個很大的缺陷,如果作業系統的版本不統或系統重啟過,那么基址將會發生變化,此時如果再次呼叫基址引數則會呼叫失敗,本... ......

    uj5u.com 2023-07-04 08:32:06 more
  • 1.5 撰寫自定位ShellCode彈窗

    在筆者上一篇文章中簡單的介紹了如何運用匯編語言撰寫一段彈窗代碼,雖然簡易`ShellCode`可以被正常執行,但卻存在很多問題,由于采用了硬編址的方式來呼叫相應API函式的,那么就會存在一個很大的缺陷,如果作業系統的版本不統或系統重啟過,那么基址將會發生變化,此時如果再次呼叫基址引數則會呼叫失敗,本... ......

    uj5u.com 2023-07-04 08:30:25 more
  • 前端Vue基于騰訊地圖Api實作的選擇位置組件 回傳地址名稱詳細地

    #### 前端Vue基于騰訊地圖Api實作的選擇位置組件 回傳地址名稱詳細地址經緯度資訊, 下載完整代碼請訪問uni-app插件市場地址:https://ext.dcloud.net.cn/plugin?id=13310 #### 效果圖如下: ![](https://p3-juejin.bytei ......

    uj5u.com 2023-07-03 07:45:11 more
  • 1.4 撰寫簡易ShellCode彈窗

    在前面的章節中相信讀者已經學會了使用Metasploit工具生成自己的ShellCode代碼片段了,本章將繼續深入探索關于ShellCode的相關知識體系,ShellCode 通常是指一個原始的可執行代碼的有效載荷,攻擊者通常會使用這段代碼來獲得被攻陷系統上的互動Shell的訪問權限,而現在用于描述... ......

    uj5u.com 2023-07-03 07:44:13 more
  • 1.4 撰寫簡易ShellCode彈窗

    在前面的章節中相信讀者已經學會了使用Metasploit工具生成自己的ShellCode代碼片段了,本章將繼續深入探索關于ShellCode的相關知識體系,ShellCode 通常是指一個原始的可執行代碼的有效載荷,攻擊者通常會使用這段代碼來獲得被攻陷系統上的互動Shell的訪問權限,而現在用于描述... ......

    uj5u.com 2023-07-03 07:43:04 more
  • css學習(一)

    ### css引入 1. 行內樣式 ```css 我是div元素 ``` 2.內部樣式 ```css ``` 3. 外部樣式 ```css /* 可以通過@import引入其他的css資源 */ @import url(./style.css); @import url(./test.css); . ......

    uj5u.com 2023-07-02 08:01:55 more
  • 什么是 CSR、SSR、SSG、ISR - 渲染模式詳解

    本文以 `React`、`Vue` 為例,介紹下主流的渲染模式以及在主流框架中如何實作上述的渲染模式。 ## 前置知識介紹 看渲染模式之前我們先看下幾個主流框架所提供的相關能力,了解的可跳到下個章節。 ### 掛載組件到 DOM 節點 這是主流框架最基本的能力,就是將組件渲染到指定的 `DOM` 節 ......

    uj5u.com 2023-07-02 07:56:36 more