Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 中文字幕在线播,精品国免费一区二区三区,国产男女猛烈无遮档免费视频网站

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

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

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

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

          出處

          ?【多行標簽展開與隱藏】該功能我們平時可能沒注意一般在哪里會有,其實最常見的就是各種APP的搜索頁面的歷史記錄這里,下面是我從拼多多(左)和騰訊學堂小程序(右)截下來的功能樣式:

          其它APP一般搜索的歷史記錄這里都有這個小功能,比如京東、支付寶、淘寶、抖音、快手等,可能稍有點兒不一樣,有的是按鈕樣式,有的是只有展開沒有收起功能,可能我們用過了很多年平時都沒有注意到這個小功能,有想了解的可以去看一看哈。如果有一天你們需要開發一個搜索頁面的話產品就很有可能出這樣的一個功能,接下來我們就來看看這種功能我們該如何實現。

          功能實現

          我們先看實現的效果圖,然后再分析如何實現,效果圖如下:

          【樣式一】:標簽容器和展開隱藏按鈕分開(效果圖樣式一)

          ?標簽容器和按鈕分開的這種樣式功能實現起來的話我個人覺得難度稍微簡單一些,下面我們看看如何實現這種分開的功能。

          第一種方法:通過與第一個標簽左偏移值對比實現

          原理:遍歷每個標簽然后通過與第一個標簽左偏移值對比,如果有幾個相同偏移值則說明有幾個換行

          具體實現上代碼:

          <div class="list-con list-con-1">
            <div class="label">人工智能</div>
            <div class="label">人工智能與應用</div>
            <div class="label">行業分析與市場數據</div>
            <div class="label">標簽標簽標簽標簽標簽標簽標簽標簽</div>
            <div class="label">標簽</div>
            <div class="label">啊啊啊</div>
            <div class="label">寶寶貝貝</div>
            <div class="label">微信</div>
            <div class="label">吧啊啊</div>
            <div class="label">哦哦哦哦哦哦哦哦</div>
          </div>
          <div class="expand expand-1">展開 ∨</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 class="list-con list-con-3" :class="{'list-expand': isExpand}">
              <div class="label" v-for="item in labelArr.slice(0, labelLength)">{{ item }}</div>
              <div class="label expand-btn" 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>
          

          這里也無需多做解釋了,直接看代碼即可。



          原文鏈接:https://juejin.cn/post/7251394142683742269

          SS圓形展開菜單。

          ·1.設置開關狀態為checked時子菜單的展開收縮動畫。

          ·2.設置開關狀態為checked時子菜單的展開收縮動畫。

          ·3.設置開關狀態為checked時子菜單的展開收縮動畫。

          ·4.重置開關細節樣式。

          ·5.添加子菜單的詳細樣式。

          ·6.設置開關狀態為checked時子菜單的展開收縮動畫。

          ·7.設置開關狀態為checked時子菜單的展開收縮動畫。

          ue可以通過v-if和v-show指令來實現收縮展開的效果。

          v-if是根據條件來決定是否渲染元素,當條件為真時,元素會被渲染;當條件為假時,元素會被從DOM中移除。因此,可以通過控制條件的真假來實現收縮展開的效果。

          v-show是根據條件來決定元素的顯示與隱藏,當條件為真時,元素會顯示;當條件為假時,元素會隱藏。因此,可以通過控制條件的真假來實現收縮展開的效果。

          具體實現可以參考以下代碼:

          ```html

          <template>

          <div>

          <button @click="toggle">收縮/展開</button>

          <div v-if="isExpanded">

          <!-- 展開內容 -->

          </div>

          <div v-show="isExpanded">

          <!-- 展開內容 -->

          </div>

          </div>

          </template>

          <script>

          export default {

          data() {

          return {

          isExpanded: false

          };

          },

          methods: {

          toggle() {

          this.isExpanded=!this.isExpanded;

          }

          }

          };

          </script>

          ``在上述代碼中,我們定義了一個isExpanded的data屬性,用于控制展開與收縮的狀態。初始狀態為false,表示收縮狀態。

          在按鈕的點擊事件中,通過toggle方法來切換isExpanded的值,從而實現收縮和展開的效果。

          在v-if指令中,根據isExpanded的值來決定是否渲染元素。

          在v-show指令中,根據isExpanded的值來決定元素的顯示與隱藏。

          v-if是惰性的,即在條件為假時,元素不會被渲染到DOM中。而v-show是通過CSS的display屬性來控制元素的顯示與隱藏,因此即使條件為假,元素仍然會存在于DOM中,只是不可見而已。

          根據實際需求,可以選擇使用v-if還是v-show來實現收縮展開的效果。如果需要頻繁切換展開與收縮,且展開內容較多,建議使用v-show;如果展開內容較少,且切換頻率較低,建議使用v-if。


          主站蜘蛛池模板: 午夜天堂一区人妻| 国产91精品一区| 波多野结衣一区二区三区高清av| 国产精品免费视频一区| 国产suv精品一区二区33| 国产亚洲福利精品一区二区| 色国产精品一区在线观看| 国产亚洲情侣一区二区无| 国产日韩精品一区二区在线观看| 久久久久人妻精品一区二区三区 | 亚洲第一区香蕉_国产a| 琪琪see色原网一区二区| 日本一道一区二区免费看| 国产成人亚洲综合一区| 亚洲一区精品视频在线| 91精品一区二区三区在线观看| 国产午夜精品一区理论片| 红杏亚洲影院一区二区三区| 成人国产精品一区二区网站| 精品国产日韩一区三区| 国产色欲AV一区二区三区| 在线视频一区二区三区四区| 成人精品一区二区电影| 日韩在线视频一区二区三区 | 日韩一区二区三区电影在线观看| 成人免费观看一区二区| 亚洲一区二区三区高清在线观看| 亚洲国产精品一区二区久| tom影院亚洲国产一区二区| 亚洲日韩国产一区二区三区在线| 交换国产精品视频一区| 濑亚美莉在线视频一区| 精彩视频一区二区| 国产精品 一区 在线| 亚洲AV无码一区二区乱孑伦AS | 黑巨人与欧美精品一区| 国产av一区二区精品久久凹凸 | 人妻激情偷乱视频一区二区三区 | 伊人久久精品无码麻豆一区| 国产精品无码一区二区三区不卡| 在线视频亚洲一区|