酷炫的手风琴效果
手风琴效果的特点:每次只展开一个元素,其他的兄弟元素都闭合
先从样式和布局开始,做一个原生JS的手风琴效果
html:
<div class="c"> <p class="red">1</p> <p class="blue">2</p> <p class="green">3</p> <p class="yellow">4</p></div>
css:
.c{ background-color: black; }.c::before,.c::after{ content: ''; display: table; }.c::after{ clear: both; }.red{ width: 100px; background-color:red; float: left; height: 300px; }.blue{ background-color:blue; }.green{ background-color: green; }.yellow{ background-color: yellow; }.yellow,.green,.blue{ width: 20px; float: left; height: 300px; }
效果如图:
写完结构和样式,对JS要完成的效果进行拆解
需求分析:当鼠标点击一个元素时,对应元素的宽度变大,兄弟元素中宽度最大的元素宽度变小
实践:
1.首先,手风琴的效果要动起来,在JS中能够动起来的只有计时器,因此这里会用到周期性计时器,这一步可以后写,先进行第二步
如果你回到这里,恭喜你,已经完成了一半
问题:何时停掉计时器,停计时器要写在计时器函数中,满足条件时清除计时器
+(function () { //查找元素 var c = document.querySelector('.c'); var ps = document.querySelectorAll('.c p'); //绑定事件:事件的对象是其中一个p元素,给每个p元素绑定事件 for (var i = 0; i < ps.length; i++) { //需求的第一个元素:被点击的元素 var p = ps[i]; p. = function () { //需求的第二个元素:兄弟中最大的元素 for (var j = 0; j < ps.length; j++) { if(getComputedStyle(ps[j]).width=='100px'){ var obj = ps[j]; break; } } if(parseInt(getComputedStyle(this).width)==20){ //添加计时器 var timer = setInterval(move,100); var step = 10; var that = this; function move() { if(parseInt(getComputedStyle(that).width)<100){ //点击的当前元素宽度变大 that.style.width = parseInt(getComputedStyle(that).width) + step + 'px'; obj.style.width = parseInt(getComputedStyle(obj).width) - step + 'px'; console.log(getComputedStyle(that).width); }else{ clearInterval(timer); } } } } } })();
问题:如果一次元素的宽度尚未到达20px,又去点击另一个元素,这时两个元素会同时动,
解决:让一个动作完成再执行另一个动作
分析:判断另一个动作是否要执行时在执行前判断的,所以在事件之外定义一个布尔全局变量
+(function () { //查找元素 var c = document.querySelector('.c'); var ps = document.querySelectorAll('.c p'); //绑定事件:事件的对象是其中一个p元素,给每个p元素绑定事件 for (var i = 0; i < ps.length; i++) { //需求的第一个元素:被点击的元素 var p = ps[i]; var flag = true; p. = function () { if(flag){ flag = false; //需求的第二个元素:兄弟中最大的元素 for (var j = 0; j < ps.length; j++) { if(getComputedStyle(ps[j]).width=='100px'){ var obj = ps[j]; break; } } if(parseInt(getComputedStyle(this).width)==20){ //添加计时器 var timer = setInterval(move,100); var step = 10; var that = this; function move() { if(parseInt(getComputedStyle(that).width)<100){ //点击的当前元素宽度变大 that.style.width = parseInt(getComputedStyle(that).width) + step + 'px'; obj.style.width = parseInt(getComputedStyle(obj).width) - step + 'px'; console.log(getComputedStyle(that).width); }else{ clearInterval(timer); flag = true; } } } } } } })();
2.从需求来看,这里需要一个事件,事件的三要素:元素、事件、函数
元素:被点击的元素和兄弟元素中最大的那个(两个)
事件:click
函数:匿名函数
函数的功能:①点击的当前元素:如果宽度为20px,变为100px;否则,不变
+(function () { //查找元素 var c = document.querySelector('.c'); var ps = document.querySelectorAll('.c p'); //绑定事件:事件的对象是其中一个p元素,给每个p元素绑定事件 for (var i = 0; i < ps.length; i++) { //需求的第一个元素:被点击的元素 var p = ps[i]; //需求的第二个元素:兄弟中最大的元素 for (var j = 0; j < ps.length; j++) { if(getComputedStyle(ps[j]).width=='100px'){ var obj = ps[j]; break; } } p. = function () { console.log("我执行了"); //点击的当前元素宽度变大 if(getComputedStyle(this).width=='20px'){ this.style.width = 100 + 'px'; obj.style.width = 20 + 'px'; } } } })();
代码到这里遇到一个问题:除了初始设置默认值100px的那个元素,以后的元素宽度都不会再变回20px
分析:兄弟中最大的元素除了默认值之外,都是点击事件发生后才产生的,上面的代码只在初始时获得一次,点击后产生的最大兄弟元素并没有获得,因此,查找兄弟中最大的元素应该放在事件处理函数中
更新代码:
+(function () { //查找元素 var c = document.querySelector('.c'); var ps = document.querySelectorAll('.c p'); //绑定事件:事件的对象是其中一个p元素,给每个p元素绑定事件 for (var i = 0; i < ps.length; i++) { //需求的第一个元素:被点击的元素 var p = ps[i]; p. = function () { //需求的第二个元素:兄弟中最大的元素 for (var j = 0; j < ps.length; j++) { if(getComputedStyle(ps[j]).width=='100px'){ var obj = ps[j]; break; } } //点击的当前元素宽度变大 if(getComputedStyle(this).width=='20px'){ this.style.width = 100 + 'px'; obj.style.width = 20 + 'px'; } } } })();
此时,测试没有问题,接下来给代码添加动画,回到第一步