您好,登錄后才能下訂單哦!
這篇文章主要講解了“elementui怎么仿寫el-collapse”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“elementui怎么仿寫el-collapse”吧!
el-collapse
即為折疊面板的意思,一般主要是用于:對復(fù)雜區(qū)域進行分組和隱藏,保持頁面的整潔,有分類整理的意思。
collapse
有折疊的意思,不過fold
也有折疊的意思。所以筆者這里封裝的組件就改名字了,不叫my-collapse
,叫做my-fold
我們先看一下下圖折疊組件的結(jié)構(gòu)圖
結(jié)合上圖已經(jīng)工作經(jīng)驗,大致分析組件的需求有以下:
點擊折疊頭部區(qū)域展開或關(guān)閉折疊內(nèi)容體區(qū)域
展開或折疊的時候,加上過渡效果
頭部區(qū)域的內(nèi)容文字參數(shù)定義
是否隱藏折疊的小箭頭
手風(fēng)琴模式的折疊面板(默認是都可以展開折疊的)
一般情況下父組件更改子組件數(shù)據(jù)狀態(tài)有以下方式:
父組件傳遞數(shù)據(jù),子組件props接收。更改父組件數(shù)據(jù),子組件也就自動更改更新了
使用this.$refs.child.xxx = yyy
,給子組件打一個ref
,直接更改對應(yīng)值即可
使用this.$children
可以訪問所有的子組件實例對象。所以,也可以直接更改,如下:
父組件代碼
// html <template> <div> <h3>下方為三個子組件</h3> <child1 /> <child2 /> <child3 /> <button @click="changeChildData">點擊按鈕更改所有子組件數(shù)據(jù)</button> </div> </template> // js changeChildData() { // this.$children拿到所有子組件實例對象的數(shù)組,遍歷訪問到數(shù)據(jù),更改之 this.$children.forEach((child) => { child.flag = !child.flag; }); },
其中一個子組件代碼,另外兩個也一樣
// html <template> <div>child1中的flag--> {{ flag }}</div> </template> // js <script> export default { data() { return { flag: false } }, }; </script>
效果圖
為什么要提到這個呢?因為手風(fēng)琴模式下的折疊面板會用到這個方式去更改別的面板,使別的面板關(guān)閉
高度的過渡,主要是從0到某個高度,以及從某個高度到0的變化,需要搭配transition
以及overflow
屬性去控制。我們先看一下簡單的寫法和效果圖,再看一下封裝的組件的代碼
1.簡單寫法
伸手黨福利,復(fù)制粘貼即可使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .target { width: 120px; height: 120px; line-height: 120px; text-align: center; background-color: #baf; /* 以下兩個是必要的樣式控制屬性 */ transition: height 0.2s linear; overflow: hidden; } </style> </head> <body> <button>點擊高度變化</button> <br> <br> <div class="target">過渡的dom</div> <script> let isOpen = true // 初始情況下,標識狀態(tài)為打開狀態(tài) let btn = document.querySelector('button') let targetDom = document.querySelector('.target') btn.onclick = () => { // 若為展開狀態(tài),就將其高度置為0,因為css有過渡代碼,所以高度過渡效果就出來了 if (isOpen) { targetDom.style.height = 0 + 'px' isOpen = false } // 若為關(guān)閉狀態(tài),就將其高度置為原來,因為css有過渡代碼,所以高度過渡效果就出來了 else { targetDom.style.height = 120 + 'px' isOpen = true } } </script> </body> </html>
2.簡單寫法效果圖
在我們封裝折疊面板的時候,這個高度變化的過渡組件是必須要有的,沒有的話,折疊面板展開關(guān)閉時,會有點突兀,加上一個組件,會絲滑不少。
3.折疊組件的封裝
理解了上述的簡單案例,再將其思路應(yīng)用到組件封裝中去即可
高度組件封裝代碼思路:
根據(jù)show
變量的標識,去更改dom.style.height
;
初始加載時,獲取初始高度`dom.offsetHeight更改一次、當show變量標識發(fā)生變化的時候,再更改一次。
同時搭配高度的transition樣式控制即可(即:監(jiān)聽props中show`標識的變化更改之)
封裝好的高度過渡組件代碼如下:
<template> <div class="transitionWrap" ref="transitionWrap"> <slot></slot> </div> </template> <script> export default { props: { // 布爾值show標識關(guān)閉還是展開 show: Boolean, }, data() { return { height: 0, }; }, mounted() { /* dom加載完畢,然后根據(jù)標識show去手動更新高度 */ this.$nextTick(() => { this.height = this.$refs.transitionWrap.offsetHeight; this.$refs.transitionWrap.style.height = this.show ? `${this.height}px` : 0; }); // this.$nextTick().then(() => { ... } }, watch: { /* 再監(jiān)聽標識的變化,從而更改高度,即關(guān)閉還是展開 */ show(newVal) { this.$refs.transitionWrap.style.height = newVal ? `${this.height}px` : 0; }, }, }; </script> <style scoped> /* 關(guān)鍵css樣式,高度線性勻速過渡 */ .transitionWrap { transition: height 0.2s linear; overflow: hidden; } </style>
另外餓了么UI也提供了el-collapse-transition
組件,也是一個不錯的選擇
封裝一套強大的開源組件其實要考慮的東西很多,比如需要適配屏幕閱讀器,我們看一下餓了么UI的el-collapse
組件使用到的兩個屏幕閱讀器屬性role
和aria-multiselectable
。如下圖:
role
屬性是html中語義化標簽的進一步補充(如 屏幕閱讀器,給盲人使用),另舉一個例子
<div role="checkbox" aria-checked="checked" />
高度屏幕閱讀器,此處有一個復(fù)選框,而且已經(jīng)被選中了
aria-multiselectable='true'
告知輔助設(shè)備,一次可以展開多個項,還是只能展開一個
詳情 css http://edu.jb51.net/jqueryui/jqueryui-intro.html
由此可以看出,一套開源的組件,的確是方方面面都要考慮到。
我們先看一下效果圖
<template> <div> <!-- 手風(fēng)琴模式 --> <my-fold v-model="openArr" accordion @change="changeFn"> <my-fold-item title="第一項" name="one">我是第一項的內(nèi)容</my-fold-item> <my-fold-item title="第二項" name="two"> <p>我是第二項的內(nèi)容</p> <p>我是第二項的內(nèi)容</p> </my-fold-item> <my-fold-item title="第三項" name="three"> <p>我是第三項的內(nèi)容</p> <p>我是第三項的內(nèi)容</p> <p>我是第三項的內(nèi)容</p> </my-fold-item> <my-fold-item title="第四項" name="four"> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> </my-fold-item> </my-fold> <br /> <!-- 可展開多個模式 --> <my-fold v-model="openArr2" @change="changeFn"> <my-fold-item title="第一項" name="one">我是第一項的內(nèi)容</my-fold-item> <my-fold-item title="第二項" name="two"> <p>我是第二項的內(nèi)容</p> <p>我是第二項的內(nèi)容</p> </my-fold-item> <my-fold-item title="第三項" name="three"> <p>我是第三項的內(nèi)容</p> <p>我是第三項的內(nèi)容</p> <p>我是第三項的內(nèi)容</p> </my-fold-item> <my-fold-item title="第四項" name="four"> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> <p>我是第四項的內(nèi)容</p> </my-fold-item> </my-fold> </div> </template> <script> export default { data() { return { // 手風(fēng)琴模式的數(shù)組項要么沒有項,要么只能有一個項 openArr: [], // 可展開多個的數(shù)組,可以有多個項 openArr2: ["one", "two"], }; }, methods: { changeFn(name, isOpen, vNode) { console.log(name, isOpen, vNode); }, }, }; </script>
<template> <div class="myFoldWrap"> <slot></slot> </div> </template> <script> export default { name: "myFold", props: { // 是否開啟手風(fēng)琴模式(每次只能展開一個面板) accordion: { type: Boolean, default: false, // 默認不開啟(可展開多個) }, // 父組件v-model傳參,子組件props中key為'value'接收,'input'事件更改 value: { type: Array, // 手風(fēng)琴模式的數(shù)組項只能有一個,反之可以有多個 default() { return []; }, }, }, data() { return { // 展開的項可一個,可多個(使用層v-model數(shù)組傳的有誰,就展開誰) openArr: this.value, // 收集誰需要展開 }; }, mounted() { // 手動加一個校驗 if (this.accordion & (this.value.length > 1)) { console.error("手風(fēng)琴模式下,綁定的數(shù)組最多一項"); } }, watch: { // 監(jiān)聽props中value的變化,及時更新 value(value) { this.openArr = value; }, }, methods: { updateVModel(name, isOpen, vNode) { // 若為手風(fēng)琴模式 if (this.accordion) { // 當某一項打開的時候,才去關(guān)閉其他項 isOpen ? this.closeOther(name) : null; this.openArr = [name]; // 手風(fēng)琴模式只保留(展開)一個 } // 若為可展開多項模式 else { let i = this.openArr.indexOf(name); // 包含就刪掉、不包含就追加 i > -1 ? this.openArr.splice(i, 1) : this.openArr.push(name); } // 無論那種模式,都需要更改并通知外層使用組件 this.$emit("input", this.openArr); // input事件控制v-model的數(shù)據(jù)更改 this.$emit("change", name, isOpen, vNode); // change事件拋出去,供用戶使用 }, closeOther(name) { this.$children.forEach((item) => { // 將除了自身以外的都置為false,故其他的就都折疊上了 if (item.name != name) { item.isOpen = false; } }); }, }, }; </script> <style lang="less" scoped> .myFoldWrap { border: 1px solid #e9e9e9; } </style>
<template> <div class="foldItem"> <!-- 頭部部分,主要是點擊時展開內(nèi)容,以及做小箭頭的旋轉(zhuǎn),和頭部的標題呈現(xiàn) --> <div class="foldItemHeader" @click="handleHeaderClick"> <i v-if="!hiddenArrow" class="el-icon-arrow-right" :class="{ rotate90deg: isOpen }" ></i> {{ title }} </div> <!-- 內(nèi)容體部分,主要是展開折疊時加上高度過渡效果,這里封裝了一個額外的工具組件 --> <transition-height class="transitionHeight" :show="isOpen"> <div class="foldItemBody"> <slot></slot> </div> </transition-height> </div> </template> <script> import transitionHeight from "@/components/myUtils/transitionHeight/index.vue"; export default { name: "myFoldItem", components: { transitionHeight, // 高度過渡組件 }, props: { title: String, // 折疊面板的標題 name: String, // 折疊面板的名字,即為唯一標識符(不可與其他重復(fù)?。? // 是否隱藏小箭頭,默認false,即展示小箭頭 hiddenArrow: { type: Boolean, default: false, }, }, data() { return { // true為展開即open,false為折疊 // 初始情況下取到父組件myFold組件的展開的數(shù)組,看看自身是否在其中 isOpen: this.$parent.openArr.includes(this.name), }; }, methods: { // 點擊展開或折疊 handleHeaderClick() { this.isOpen = !this.isOpen; // 內(nèi)容依托于變量isOpen直接更新即可 this.$parent.updateVModel(this.name, this.isOpen, this); // 于此同時也要通知父組件去更新 }, }, }; </script> <style lang="less" scoped> .foldItem { width: 100%; height: auto; // 高度由內(nèi)容區(qū)撐開 .foldItemHeader { box-sizing: border-box; padding-left: 8px; min-height: 50px; display: flex; align-items: center; background-color: #fafafa; cursor: pointer; border-bottom: 1px solid #e9e9e9; // 展開折疊項時,小圖標旋轉(zhuǎn)效果 i { transform: rotate(0deg); transition: all 0.24s; margin-right: 8px; } .rotate90deg { transform: rotate(90deg); transition: all 0.24s; } } .foldItemBody { width: 100%; height: auto; box-sizing: border-box; padding: 12px 12px 12px 8px; border-bottom: 1px solid #e9e9e9; } } // 去除和父組件的邊框重疊 .foldItem:last-child .foldItemHeader { border-bottom: none !important; } .foldItem:last-child .transitionHeight .foldItemBody { border-top: 1px solid #e9e9e9; border-bottom: none !important; } </style>
感謝各位的閱讀,以上就是“elementui怎么仿寫el-collapse”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對elementui怎么仿寫el-collapse這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。