目录
个人小练习的小小体会,classList.toggle()在vue.js中的切换使用与CSS样式内联表的顺序问题
先说明一个学校里小练习的题目:
这需要初学者的一点DOM知识以及JS和vue的知识,才能理解
<!--
练习:
①有一个购物车功能,需要实时计算商品数量和总价,并在页面上显示。请完成以下任务:
②创建一个 Vue 组件,包含一个商品列表,每个商品包含名称、数量、价格等信息,以及一个显示商品数量和总价的区域。
③使用 computed 属性计算商品数量和总价,并在页面上显示。
④使用 watch 监听商品数量和价格的变化,并在变化时更新商品数量和总价。
⑤实现增加和删除商品的功能,包括修改数量和价格,并在更新商品信息后自动计算商品数量和总价。
⑥在组件的 created 生命周期钩子函数中添加 watch 监听器,而在 beforeUnmount 生命周期钩子函数中取消监听器
-->
而其中要使用的classList.toggle()属性就是为了解决其中的“增加商品”的功能,我想要实现的是在一个界面中,点击一个按钮,可以通过切换css中的display样式,来实现一个增加商品的表单的 出现和消失。
首先在js中定义一个数组用来存放需要增加的商品信息
然后再methods里定义一个方法addPageDisplay()来控制添加信息的出现
以及另一个方法addComdy()来提交信息
<script type="module">
let id='2500100';//id作用起始id,通过自增来依次赋值
const shopping=Vue.createApp({
data(){
return{
//定义一个新的对象模板来依次赋值
addNew_arr: {comdy_id:'',comdy_name:'',comdy_number:'',comdy_price:'',comdy_decr:''}
}
} ,
methods :{
addPageDisplay(){},
addComdy(){}
}
});
shopping.mount('#shopping')
</script>
然后需要在body里定义一个<div>
<div id="shopping">
<button type="button" @click="addPageDisplay">添加商品</button>
<div class="submitForm" >
<form action="">
<table>
<!--通过直接读取数组里的comdy_name属性,再通过v-model来动态绑定数组里的信息-->
<tr>商品名称: <input type="text" v-model="addNew_arr.comdy_name"></tr>
<tr>商品数量: <input type="text" v-model="addNew_arr.comdy_number"></tr>
<tr>商品价格: <input type="text" v-model="addNew_arr.comdy_price"></tr>
<tr>商品描述: <input type="text" v-model="addNew_arr.comdy_decr"></tr>
<tr><button type="button" @click="addComdy">提交</button></tr>
</table>
</form>
</div>
</div>
这时我们需要定义css的样式,其中一个样式为submiForm,另一个样式为hidden,
当我们点击“添加商品”的按钮时,submiForm的样式会被切换成hidden的样式,css格式为:
<!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>测试toggle的问题</title>
<script src="cdn.staticfile.org_vue_3.0.5_vue.global.js"></script>
<style>
.block{
display: block; /*设置为显示*/
}
.submitForm{
display:none; /*设置隐藏其显示*/
top: 10px;
width: 60%;
height: 60%;
justify-content: center;
align-items: center;
}
</style>
</head>
注意以上的顺序,其中.hidden样式设置为第一项,.submitForm为第二项,因为html是顺序解构的,会从上往下依次向下读取文件
这时候只要设置点击“添加商品的方法”,让.submitForm的display:none显示属性转变为display:block就可以了,因此:
addPageDisplay(){
//通过DOM的document.querySelector找寻上下文里所有的名为'.submitForm'的类,被将其赋值给新定义的常量const submitForm
const submitForm = document.querySelector('.submitForm');
//随后通过调用DOM里的classList属性里的toggle方法,实现属性的切换
//如果属性存在,则删除原本的属性,如果属性不存在,则添加属性
//即,原先submitForm中存在display:none的属性
submitForm.classList.toggle('block');
},
第一个大问题(CSS内联样式表的顺序问题)
那么第一个问题来了,实现了以上的方法后,是否能实现通过button的“添加商品”按钮实现form表单的显示和隐藏呢?
答案是错误,并不能实现,因为在刚定义时,CSS的内联样式表顺序反了。
在其解构时,html文件会先读取第一个.block再读取第二个.submitForm
也就是说后来的会覆盖前面的,第二项的css会覆盖第一项的css,第二项的优先级高于第一项
也就是说,第二项的display的优先级会覆盖第一项
这时候我们需要把第一项调为第二项,第二项调为第一项,让.submitForm的优先级大于.block的优先级,这个时候就可以用“添加按钮来切换了”
即:
调整位置后就可以实现了
第二个大问题(分离display属性):
虽然第一个问题解决了,但是第二个问题是,我想在提交表单这个动作完成的一瞬间,触发click事件,去关闭表单,让因为第一步的原因,表单的display:block重新变回display:none,让表格重新隐藏起来。可是如果再用一次第一次中的方法时,会发现,因为这个时候.block的优先级已经大于了.submitForm,此时再去切换的话已经切换不回去了。
对应的就是下面这个提交操作
所以需要指令,但因为优先级问题无法再读取回原来的样式
const submitForm = document.querySelector('.submitForm');
submitForm.classList.toggle('submitForm');
//无法再重新读取回原来的样式
所以这个时候需要再定义一个类样式,专门用来隐藏,即为 .hidden
但是即便如此,也不能实现目的,因为.hidden的优先级还是小于.block,只是相当于把display:none从.submitForm中单独拿了出来而已。
所以此时需要双重切换:
首先我们需要再div中将单独分离出来的display:none属性给加回class里,让<div>同时具备两种属性
即:
<div id="shopping">
<button type="button" @click="addPageDisplay">添加商品</button>
<!--注意这里的class里多了个hidden-->
<div class="submitForm hidden" >
<form action="">
<table>
<tr>商品名称: <input type="text" v-model="addNew_arr.comdy_name"></tr>
<tr>商品数量: <input type="text" v-model="addNew_arr.comdy_number"></tr>
<tr>商品价格: <input type="text" v-model="addNew_arr.comdy_price"></tr>
<tr>商品描述: <input type="text" v-model="addNew_arr.comdy_decr"></tr>
<tr><button type="button" @click="addComdy">提交</button></tr>
</table>
</form>
</div>
</div>
**同时注意,一个class里定义多个类名,不会有优先级的冲突,它们都同时作用于同一个div,所以无论是取名为class=“submitForm hidden”还是取名为class=“hidden submitForm”都不会有影响,取名顺序不会影响其优先级,但是CSS内联样式表的顺序还是会影响,就跟前面一样,这里的class虽不受取名顺序影响,但如果在样式表中submitForm在hidden前面,则submitForm会覆盖hidden里相同的属性,反之亦然**
同时需要更改一下原先methods方法里的代码:
当我们第一次点击时会发生以下事件:
addPageDisplay(){
//第一次点击时:第一步通过DOM的querySelector获取对应的类,如果是id的话就用getElementById,效果一样的
const submitForm = document.querySelector('.submitForm');
//第一次点击时:第二步,通过toggle切换css样式,根据原则“如果有则删除,如果没有则添加,如果不一样则修改”,原先的submitForm里有hidden的display:none,所以这里就先删除了submitForm里的display属性
submitForm.classList.toggle('hidden');
//第一次点击时:第三步,因为.block的优先级最大,且第二步的时候删除了display:none属性,所以第三步就会往原本的.submitForm里添加display:block属性,就会从隐藏变为显示了
submitForm.classList.toggle('block');
},
随后当我们第二次点击时又会发生以下事件:
addPageDisplay(){
//第一次点击时:第一步通过DOM的querySelector获取对应的类,如果是id的话就用getElementById,效果一样的
//当第二次点击时,同样第一步先获取DOM对象
const submitForm = document.querySelector('.submitForm');
//第一次点击时:第二步,通过toggle切换css样式,根据原则“如果有则删除,如果没有则添加,如果不一样则修改”,原先的submitForm里有hidden的display:none,所以这里就先删除了submitForm里的display属性
//当第二次点击时:因为第一次点击时最后一步的残留影响,效果为.block的display:block,所以此时如果要切换为hidden的话,根据内联样式表的顺序,.hidden的优先级小于.block,所以会添加上hidden但不能覆盖.blcok
submitForm.classList.toggle('hidden');
//第一次点击时:第三步,因为.block的优先级最大,且第二步的时候删除了display:none属性,所以第三步就会往原本的.submitForm里添加display:block属性,就会从隐藏变为显示了
//当第二次点击时:因为CSS样式为display:block,还是原本的样式,根据原则“如果有则删除,如果没有则添加,如果不一样则修改”,会删除掉.block的样式,只会留下.submitForm和.hidden的,所以这时候又会被隐藏起来了
submitForm.classList.toggle('block');
},
addComdy(){
this.addNew_arr.comdy_id=id++;
const submitForm = document.querySelector('.submitForm') ;
submitForm.classList.toggle('block');
submitForm.classList.toggle('hidden');
this.addNew_arr={comdy_id:id++,comdy_name:'',comdy_number:'',comdy_price:'',comdy_decr:''}
alert('提交成功!');
}
据此,个人剖析完毕。其中留了一个addComdy的方法,没有打上注释,可以给小白们自己分析一下,像一个练习一样吧~
全部完整的练习源码如下
有兴趣的可以看看:
<!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>测试toggle的问题</title>
<script src="cdn.staticfile.org_vue_3.0.5_vue.global.js"></script>
<style>
.submitForm{
/*
display:none ?
display:block ?
*/
top: 10px;
width: 60%;
height: 60%;
justify-content: center;
align-items: center;
}
.hidden{
display: none;
}
.block{
display: block;
}
</style>
</head>
<body>
<div id="shopping">
<button type="button" @click="addPageDisplay">添加商品</button>
<div class="submitForm hidden" >
<form action="">
<table>
<tr>商品名称: <input type="text" v-model="addNew_arr.comdy_name"></tr>
<tr>商品数量: <input type="text" v-model="addNew_arr.comdy_number"></tr>
<tr>商品价格: <input type="text" v-model="addNew_arr.comdy_price"></tr>
<tr>商品描述: <input type="text" v-model="addNew_arr.comdy_decr"></tr>
<tr><button type="button" @click="addComdy">提交</button></tr>
</table>
</form>
</div>
</div>
<script type="module">
let id='2500100';
const shopping=Vue.createApp({
data(){
return{
addNew_arr: {comdy_id:'',comdy_name:'',comdy_number:'',comdy_price:'',comdy_decr:''},
}
},
methods:{
addPageDisplay(){
//第一次点击时:第一步通过DOM的querySelector获取对应的类,如果是id的话就用getElementById,效果一样的
//当第二次点击时,同样第一步先获取DOM对象
const submitForm = document.querySelector('.submitForm');
//第一次点击时:第二步,通过toggle切换css样式,根据原则“如果有则删除,如果没有则添加,如果不一样则修改”,原先的submitForm里有hidden的display:none,所以这里就先删除了submitForm里的display属性
//当第二次点击时:因为第一次点击时最后一步的残留影响,效果为.block的display:block,所以此时如果要切换为hidden的话,根据内联样式表的顺序,.hidden的优先级小于.block,所以会添加上hidden但不能覆盖.blcok
submitForm.classList.toggle('hidden');
//第一次点击时:第三步,因为.block的优先级最大,且第二步的时候删除了display:none属性,所以第三步就会往原本的.submitForm里添加display:block属性,就会从隐藏变为显示了
//当第二次点击时:因为CSS样式为display:block,还是原本的样式,根据原则“如果有则删除,如果没有则添加,如果不一样则修改”,会删除掉.block的样式,只会留下.submitForm和.hidden的,所以这时候又会被隐藏起来了
submitForm.classList.toggle('block');
},
addComdy(){
this.addNew_arr.comdy_id=id++;
const submitForm = document.querySelector('.submitForm') ;
submitForm.classList.toggle('block');
submitForm.classList.toggle('hidden');
this.addNew_arr={comdy_id:id++,comdy_name:'',comdy_number:'',comdy_price:'',comdy_decr:''}
alert('提交成功!');
}
}
});
shopping.mount('#shopping')
</script>
<!--
结论:submitForm作为主类的话,其中的display会被覆盖,为最高优先级,无法被toggle通过其它类去改变修改,解决方法只能定义两个类
用其他类作为辅助属性,并在style中设定顺序,其顺序决定了它们的优先级问题
-->
</body>
</html>
测试toggle的问题