思考
当我们在事件处理程序中使用定时器时,没触发一次事件,就会启动新的定时器,如果新的定时器与旧的定时器同时执行,最终的结果并不是我们预想的那样,我们应该怎么去解决呢?总不可能让用户在一个时间段只能点击一次吧。本文给出了一个简单的解决方法,希望和大家共同探讨。
实现一个鼠标悬浮图片名切换到相应图片的效果
以下是未解决问题的代码:
html
<h1>图片预览</h1>
<ul>
<li><a href="#">图片1</a></li>
<li><a href="#">图片2</a></li>
<li><a href="#">图片3</a></li>
</ul>
<div id="container" style="border-color:red">
<div id="content">
<img src="image/1.jpg"><img src="image/2.jpg"><img src="image/3.jpg">
</div>
</div>
css
#container{
position:relative;
overflow:hidden;
width:200px;
height:150px;
border:1px solid #666;
}
img{ width:200px; }
#content{
position:absolute;
width:600px;
}
js
window.onload=function(){ move(); }
function move(){
var link=document.querySelectorAll('ul>li>a');
//因为通过style属性只能读取内部样式,所以通过script设置好top和left。
content.style.top="0";
content.style.left="0";
`//将参数传入moveElement方法中
link[0].onmouseover=function(){ moveElement('content',0,0,10); }
link[1].onmouseover=function(){ moveElement('content',-200,0,10); }
link[2].onmouseover=function(){ moveElement('content',-400,0,10); }
}
//移动目标元素的方法
function moveElement(elemID,fina_x,fina_y,interval){
var elem=document.getElementById(elemID);
//如果有正在执行的定时器,则清除
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if (xpos==fina_x&&ypos==fina_y) { return true; }
if (xpos<fina_x) { xpos+=2; }
if (xpos>fina_x) { xpos-=2; }
if (ypos<fina_y) { ypos+=2; }
if (ypos>fina_y) { ypos-=2; }
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
//将当前定时器函数的参数值组合好
var repeat="moveElement('"+elemID+"',"+fina_x+","+fina_y+","+interval+")";
//链式调用一次性定时器继续执行
setTimeout(repeat,interval);
}
页面效果
鼠标悬浮于li标签上,下方的图片就会移动位置直到到达目标图片的位置
问题提出
当图片在移动的过程中,鼠标又移动到另一个li上方,图片就会来回的抖动,看起来就像不再移动了一样,或者运动速度变得更快,到达后仍然在抖动。
问题原因
当第一鼠标悬浮到li元素上,启动moveElement方法,但在图片未到达目标位置上时,未到达定时器停止的条件,方法就会不断的执行一次性定时器调用。
而这时鼠标又移到了另一个li元素上,又启动了另一个moveElement方法,两个同时运行的方法对同一个元素执行不同的移动,就会造成图片来回的移动或者移动速度加快,与预设的效果不同。
解决办法
这种情况需要及时的中止前一个一次性定时器的链式调用就可以达到只执行最新的任务的目的
在moveElement方法中给目标元素添加一个movement属性,通过将定时器的ID存入这个属性中,每次执行定时器的移动代码之前先清除之前的定时器,再执行,就能避免目标元素上同时存在两个不同的定时器的问题。
function moveElement(elemID,fina_x,fina_y,interval){
var elem=document.getElementById(elemID);
//如果有正在执行的定时器,则清除
if (elem.movement) { clearTimeout(elem.movement); }
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if (xpos==fina_x&&ypos==fina_y) { return true; }
if (xpos<fina_x) { xpos+=2; }
if (xpos>fina_x) { xpos-=2; }
if (ypos<fina_y) { ypos+=2; }
if (ypos>fina_y) { ypos-=2; }
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
var repeat="moveElement('"+elemID+"',"+fina_x+","+fina_y+","+interval+")";
//将定时器ID存入elem的movement属性中
elem.movement=setTimeout(repeat,interval);
}
如果使用间隙性定时器也可以使用同样的方法清除多余的定时器,但建议使用一次性定时器的链式调用。