jQuery盒子显隐动画异常求助:快速反向点击按钮时卡顿
解决jQuery盒子平滑显示/隐藏的卡顿问题
问题描述
我用jQuery实现了盒子的平滑隐藏与显示功能,逻辑是:
- 点击按钮时,如果目标盒子处于隐藏状态,就先让所有盒子
fadeOut,400ms后移除目标盒子的hidden类,再让非hidden类的盒子fadeIn - 如果目标盒子处于显示状态,就先让所有盒子
fadeOut,给目标盒子添加hidden类,再让非hidden类的盒子fadeIn
之前因为出现过目标盒子在其他盒子未完成隐藏就提前显示的问题,所以加了和jQuery默认淡入淡出时长匹配的400mssetTimeout。现在隐藏动画在快速点击时表现正常,但快速反向点击让盒子重新显示时,动画卡顿严重。我推测是setTimeout导致的,有没有可行的解决办法?
相关代码
JavaScript
function showhide(num) { if ($("#box" + num).css('display') == 'none') { $(".box").fadeOut(); setTimeout(function() { $("#box" + num).removeClass("hidden"); $(".box:not(.hidden)").fadeIn(); }, 400); } else { $(".box").fadeOut(); $("#box" + num).addClass("hidden"); $(".box:not(.hidden)").fadeIn(); } }
CSS
html { width: 340px; border-style: solid; } .box { width: 100px; height: 100px; float: left; } #box1 { background-color: red; } #box2 { background-color: blue; } #box3 { background-color: green; } #box4 { background-color: yellow; } #box5 { background-color: orange; } #box6 { background-color: purple; }
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <b>Hide/Show</b><br> <button onclick="showhide(1)">Box 1</button> <button onclick="showhide(2)">Box 2</button> <button onclick="showhide(3)">Box 3</button> <button onclick="showhide(4)">Box 4</button> <button onclick="showhide(5)">Box 5</button> <button onclick="showhide(6)">Box 6</button><br><br> <div id="box1" class="box"></div> <div id="box2" class="box"></div> <div id="box3" class="box"></div> <div id="box4" class="box"></div> <div id="box5" class="box"></div> <div id="box6" class="box"></div>
问题根源
你猜的没错,问题确实出在setTimeout上。快速点击按钮时,多个setTimeout会被加入执行队列,它们不会等待前一个动画完成就触发,导致多个fadeIn/fadeOut动画叠加,最终出现卡顿。另外,用css('display') == 'none'判断元素状态也不靠谱——jQuery动画过程中,元素的display值可能和预期不符,应该用更可靠的方式判断可见性。
解决方案
核心思路是用jQuery动画的回调函数代替固定时长的setTimeout,同时在执行新动画前清除之前的动画队列,避免动画叠加冲突。
优化后的JavaScript
function showhide(num) { const $targetBox = $("#box" + num); const isTargetHidden = !$targetBox.is(':visible'); // 停止所有盒子的当前动画,清除队列,避免动画叠加 $(".box").stop(true, true); // 统一执行淡出动画,完成后处理后续逻辑 $(".box").fadeOut(400, function() { // 因为fadeOut会对每个.box元素触发回调,所以只在最后一个元素完成时执行后续操作 if ($(this).is('.box:last')) { if (isTargetHidden) { $targetBox.removeClass("hidden"); } else { $targetBox.addClass("hidden"); } $(".box:not(.hidden)").fadeIn(400); } }); }
关键改进点
stop(true, true)清除动画队列:第一个true清除未执行的动画队列,第二个true让当前动画立即完成,快速点击时不会有未完成的动画堆积。- 用动画回调代替
setTimeout:fadeOut的回调会在动画真正完成后触发,完全匹配动画时长,不会出现时间不匹配的问题。 is(':visible')判断状态:比读取css('display')更可靠,jQuery会正确处理动画过程中的元素可见性状态。- 判断最后一个元素的回调:确保所有盒子都完成淡出后,再执行显示逻辑,避免部分盒子还没隐藏就开始显示的冲突。
这样修改后,不管你怎么快速点击按钮,动画都会流畅执行,不会再出现卡顿的情况。
内容的提问来源于stack exchange,提问作者KarelRuzicka
相关产品推荐
相关产品推荐

