这篇文章主要讲解了“elementui怎么仿写el-collapse”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“elementui怎么仿写el-collapse”吧!
el-collapse
即为折叠面板的意思,一般主要是用于:对复杂区域进行分组和隐藏,保持页面的整洁,有分类整理的意思。
collapse
有折叠的意思,不过fold
也有折叠的意思。所以笔者这里封装的组件就改名字了,不叫my-collapse
,叫做my-fold
我们先看一下下图折叠组件的结构图
结合上图已经工作经验,大致分析组件的需求有以下:
点击折叠头部区域展开或关闭折叠内容体区域
展开或折叠的时候,加上过渡效果
头部区域的内容文字参数定义
是否隐藏折叠的小箭头
手风琴模式的折叠面板(默认是都可以展开折叠的)
一般情况下父组件更改子组件数据状态有以下方式:
父组件传递数据,子组件props接收。更改父组件数据,子组件也就自动更改更新了
使用this.$refs.child.xxx = yyy
,给子组件打一个ref
,直接更改对应值即可
使用this.$children
可以访问所有的子组件实例对象。所以,也可以直接更改,如下:
父组件代码
// html <template> <div> <h3>下方为三个子组件</h3> <child1 /> <child2 /> <child3 /> <button @click="changeChildData">点击按钮更改所有子组件数据</button> </div> </template> // js changeChildData() { // this.$children拿到所有子组件实例对象的数组,遍历访问到数据,更改之 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>
效果图
为什么要提到这个呢?因为手风琴模式下的折叠面板会用到这个方式去更改别的面板,使别的面板关闭
高度的过渡,主要是从0到某个高度,以及从某个高度到0的变化,需要搭配transition
以及overflow
属性去控制。我们先看一下简单的写法和效果图,再看一下封装的组件的代码
1.简单写法
伸手党福利,复制粘贴即可使用
<!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 // 初始情况下,标识状态为打开状态 let btn = document.querySelector('button') let targetDom = document.querySelector('.target') btn.onclick = () => { // 若为展开状态,就将其高度置为0,因为css有过渡代码,所以高度过渡效果就出来了 if (isOpen) { targetDom.style.height = 0 + 'px' isOpen = false } // 若为关闭状态,就将其高度置为原来,因为css有过渡代码,所以高度过渡效果就出来了 else { targetDom.style.height = 120 + 'px' isOpen = true } } </script> </body> </html>
2.简单写法效果图
在我们封装折叠面板的时候,这个高度变化的过渡组件是必须要有的,没有的话,折叠面板展开关闭时,会有点突兀,加上一个组件,会丝滑不少。
3.折叠组件的封装
理解了上述的简单案例,再将其思路应用到组件封装中去即可
高度组件封装代码思路:
根据show
变量的标识,去更改dom.style.height
;
初始加载时,获取初始高度`dom.offsetHeight更改一次、当show变量标识发生变化的时候,再更改一次。
同时搭配高度的transition样式控制即可(即:监听props中show`标识的变化更改之)
封装好的高度过渡组件代码如下:
<template> <div class="transitionWrap" ref="transitionWrap"> <slot></slot> </div> </template> <script> export default { props: { // 布尔值show标识关闭还是展开 show: Boolean, }, data() { return { height: 0, }; }, mounted() { /* dom加载完毕,然后根据标识show去手动更新高度 */ this.$nextTick(() => { this.height = this.$refs.transitionWrap.offsetHeight; this.$refs.transitionWrap.style.height = this.show ? `${this.height}px` : 0; }); // this.$nextTick().then(() => { ... } }, watch: { /* 再监听标识的变化,从而更改高度,即关闭还是展开 */ show(newVal) { this.$refs.transitionWrap.style.height = newVal ? `${this.height}px` : 0; }, }, }; </script> <style scoped> /* 关键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" />
高度屏幕阅读器,此处有一个复选框,而且已经被选中了
aria-multiselectable='true'
告知辅助设备,一次可以展开多个项,还是只能展开一个
详情 css http://edu.jb51.net/jqueryui/jqueryui-intro.html
由此可以看出,一套开源的组件,的确是方方面面都要考虑到。
我们先看一下效果图
<template> <div> <!-- 手风琴模式 --> <my-fold v-model="openArr" accordion @change="changeFn"> <my-fold-item title="第一项" name="one">我是第一项的内容</my-fold-item> <my-fold-item title="第二项" name="two"> <p>我是第二项的内容</p> <p>我是第二项的内容</p> </my-fold-item> <my-fold-item title="第三项" name="three"> <p>我是第三项的内容</p> <p>我是第三项的内容</p> <p>我是第三项的内容</p> </my-fold-item> <my-fold-item title="第四项" name="four"> <p>我是第四项的内容</p> <p>我是第四项的内容</p> <p>我是第四项的内容</p> <p>我是第四项的内容</p> </my-fold-item> </my-fold> <br /> <!-- 可展开多个模式 --> <my-fold v-model="openArr2" @change="changeFn"> <my-fold-item title="第一项" name="one">我是第一项的内容</my-fold-item> <my-fold-item title="第二项" name="two"> <p>我是第二项的内容</p> <p>我是第二项的内容</p> </my-fold-item> <my-fold-item title="第三项" name="three"> <p>我是第三项的内容</p> <p>我是第三项的内容</p> <p>我是第三项的内容</p> </my-fold-item> <my-fold-item title="第四项" name="four"> <p>我是第四项的内容</p> <p>我是第四项的内容</p> <p>我是第四项的内容</p> <p>我是第四项的内容</p> </my-fold-item> </my-fold> </div> </template> <script> export default { data() { return { // 手风琴模式的数组项要么没有项,要么只能有一个项 openArr: [], // 可展开多个的数组,可以有多个项 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: { // 是否开启手风琴模式(每次只能展开一个面板) accordion: { type: Boolean, default: false, // 默认不开启(可展开多个) }, // 父组件v-model传参,子组件props中key为'value'接收,'input'事件更改 value: { type: Array, // 手风琴模式的数组项只能有一个,反之可以有多个 default() { return []; }, }, }, data() { return { // 展开的项可一个,可多个(使用层v-model数组传的有谁,就展开谁) openArr: this.value, // 收集谁需要展开 }; }, mounted() { // 手动加一个校验 if (this.accordion & (this.value.length > 1)) { console.error("手风琴模式下,绑定的数组最多一项"); } }, watch: { // 监听props中value的变化,及时更新 value(value) { this.openArr = value; }, }, methods: { updateVModel(name, isOpen, vNode) { // 若为手风琴模式 if (this.accordion) { // 当某一项打开的时候,才去关闭其他项 isOpen ? this.closeOther(name) : null; this.openArr = [name]; // 手风琴模式只保留(展开)一个 } // 若为可展开多项模式 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的数据更改 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"> <!-- 头部部分,主要是点击时展开内容,以及做小箭头的旋转,和头部的标题呈现 --> <div class="foldItemHeader" @click="handleHeaderClick"> <i v-if="!hiddenArrow" class="el-icon-arrow-right" :class="{ rotate90deg: isOpen }" ></i> {{ title }} </div> <!-- 内容体部分,主要是展开折叠时加上高度过渡效果,这里封装了一个额外的工具组件 --> <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, // 折叠面板的名字,即为唯一标识符(不可与其他重复!) // 是否隐藏小箭头,默认false,即展示小箭头 hiddenArrow: { type: Boolean, default: false, }, }, data() { return { // true为展开即open,false为折叠 // 初始情况下取到父组件myFold组件的展开的数组,看看自身是否在其中 isOpen: this.$parent.openArr.includes(this.name), }; }, methods: { // 点击展开或折叠 handleHeaderClick() { this.isOpen = !this.isOpen; // 内容依托于变量isOpen直接更新即可 this.$parent.updateVModel(this.name, this.isOpen, this); // 于此同时也要通知父组件去更新 }, }, }; </script> <style lang="less" scoped> .foldItem { width: 100%; height: auto; // 高度由内容区撑开 .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; // 展开折叠项时,小图标旋转效果 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”的内容了,经过本文的学习后,相信大家对elementui怎么仿写el-collapse这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。