20181111 计时器影响DOM点击事件的逻辑
今天在群里看见一个人在问"点击按钮使图片产生旋转为什么要使用计时器来实现",我自己操作了一遍她的代码才发现里面的逻辑实现很有意思,所以写出来分享一下。
她的代码是这样写的:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Page Title</title> 5 <style> 6 #myImg { 7 width: 60%; 8 } 9 .myShow { 10 animation: rotate 1s; 11 } 12 @keyframes rotate { 13 ftom {transform: rotate(0deg);} 14 to {transform: rotate(180deg);} 15 } 16 </style> 17 </head> 18 <body> 19 <div> 20 <img 21 id='myImg' 22 src='http://www.8090.com/uploads/allimg/141204/3-141204134GJ95.jpg' 23 /> 24 <p id='demo'></p> 25 <button onclick='myBtn()'>click</button> 26 </div> 27 <script> 28 function myBtn() { 29 var rotate = document.getElementById('myImg'); 30 var myAttr = function(){ 31 rotate.setAttribute('class','myShow'); 32 } 33 setTimeout(myAttr,30); 34 rotate.setAttribute('class',''); 35 } 36 37 </script> 38 </body> 39 </html>
在这里,通过点击按钮触发myBtn函数,先执行计时器setTimeout, 调用myAttr函数添加了class属性'myShow',再移除了这个class属性。和‘myShow'绑定了rotate这个CSS动画,从而实现图片旋转。
回到她的问题:"为什么要使用计时器来实现图片旋转?"
按照她的想法,不使用计时器,只要再次点击按钮,不一样会产生click事件么,那计时器有什么用?
下面去掉计时器,直接使用setAttribute属性看看是否可以达到同样效果。js代码如下:
1 <script> 2 function myBtn() { 3 var rotate = document.getElementById('myImg'); 4 // var myAttr = function(){ 5 // rotate.setAttribute('class','myShow'); 6 // console.log('hello') 7 // } 8 rotate.setAttribute('class','myShow'); 9 // setTimeout(myAttr,30); 10 rotate.setAttribute('class',''); 11 } 12 13 </script>
然而,点击按钮没有产生任何反应。原因是因为js的运行逻辑是至上而下的顺序,当我们点击按钮后,myBtn函数从上而下执行,执行完后class属性已被删除,就不会对HTML产生DOM效果。
那同样在前面的代码里,js函数的最后一行也是rotate.setAttribute('class',''),为什么可以正常产生DOM效果?
通过加入console.log后,我发现计时器的原理是它改变了函数的运行顺序。
通过添加计时器,函数的执行顺序变成了:’执行setTimeout方法' => ‘删除class属性' => (30毫秒后)'添加class属性'。因为函数的运行结果是添加class属性,所以保留了class='myShow',从而引用了CSS动画效果。
1 <script> 2 function myBtn() { 3 var rotate = document.getElementById('myImg'); 4 var myAttr = function(){ 5 rotate.setAttribute('class','myShow'); //设置class属性值为myShow 6 console.log(document.getElementById('myImg').className) 7 } 8 setTimeout(myAttr,30); 9 rotate.setAttribute('class','None'); //设置class属性值为None 10 console.log(document.getElementById('myImg').className) 11 } 12 13 </script>
执行页面后,发现console.log打印结果先是'None',再是'myShow',说明了每次点击按钮都是先"删除"class属性,在添加class属性。这样每次className都是被保留了下来,而再次点击按钮,则又先删掉了class属性,以此类推。