jQuery 的 slideUp 和 slideDown 动画

jQuery 可以通过调用 animate 方法添加动画效果, 而且还提供了一套别名, 使用起来很是方便. 其中 slideDownslideUp 两方法的作用是纵向展开和卷起一个页面元素, 被使用的几率很高, 却一直存在一个小问题.

如果目标元素是被外部事件驱动, 当鼠标快速地连续触发外部元素事件, 动画会滞后的反复执行, 相当不美观 (演示页面). 演示页面中有一个按钮, 请用鼠标迅速地来回划过...

不知道本博客的老读者是否记得我写过的一个关于菜单导航的专题, 其中一节讨论的是滚动导航菜单的实现方法 (演示页面), 实现了类似 slideUp 和 slideDown 的效果, 但是不存在上述问题, 因为它的滚动高度是即时计算的, 而 jQuery 作为一个通用框架应用的面很广, 很难做到这一点.

如果用 jQuery 来实现这样的效果, 该如何处理呢?
其实很简单, 只需在触发元素上的事件设置为延迟处理, 即可避免滞后反复执行的问题. 例如: 当鼠标滑过按钮后 0.2 秒, 菜单才会展开, 如果鼠标离开按钮, 展开的处理将被终止. 也就是说, 想要展开菜单鼠标必须有 0.2 秒的事件停留在按钮上, 那么迅速地划过按钮是无法执行展开事件的. 卷起也是如此.

下面我们用代码来解决这个问题.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 线程 IDs
var mouseover_tid = [];
var mouseout_tid = [];
 
jQuery(document).ready(function(){
	jQuery('#menus > li').each(function(index){
		jQuery(this).hover(
 
			// 鼠标进入
			function(){
				var _self = this;
				// 停止卷起事件
				clearTimeout(mouseout_tid[index]);
				// 当鼠标进入超过 0.2 秒, 展开菜单, 并记录到线程 ID 中
				mouseover_tid[index] = setTimeout(function() {
					jQuery(_self).find('ul:eq(0)').slideDown(200);
				}, 400);
			},
 
			// 鼠标离开
			function(){
				var _self = this;
				// 停止展开事件
				clearTimeout(mouseover_tid[index]);
				// 当鼠标离开超过 0.2 秒, 卷起菜单, 并记录到线程 ID 中
				mouseout_tid[index] = setTimeout(function() {
					jQuery(_self).find('ul:eq(0)').slideUp(200);
				}, 400);
			}
 
		);
	});
});

看看处理过后的效果.
演示页面

注意: 当鼠标悬停时间小于动画时间时, 同样可能出现反复执行现象, 可以考虑将悬停时间设置比动画时间稍长.

嗯, 问题解决了, 但 jQuery 应该还存在其他类似的问题, 如 fadeIn 和 fadeOut (演示页面), 处理方法类似. 如果你选用 jQuery 发现了这类问题, 又不情愿为此重新写一大段代码, 那这应该算是一个不错的解决办法, 而且效果也还行. 现在本博客的 RSS feed 列表也是这样实现的.

最后, 拓展一下思维. 不知道你是否见过网页上有这样的效果: 当鼠标在某个元素上停顿一定的时候后页面上某些内容会被激活. 这样的效果处理方法和上面说到的其实是大同小异, 有兴趣的同学可自行尝试.

声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: jQuery 的 slideUp 和 slideDown 动画
  1. shy9000 | #1
    2009-12-13 15:20

    占上沙发...

  2. Showfom | #2
    2009-12-13 15:31

    板凳!

  3. accecc | #3
    2009-12-13 15:34

    这个Demo似曾相识,锋利的jQuery里?忘了

  4. bolo | #4
    2009-12-13 16:15

    我以前发给你那个jQuery插件也是实现mouseover事件延迟的,扩展性强些,如果页面上大量应用了这些动画效果的话可以挂上那个插件。

    另外你写的这段代码可以改写成两个函数,方便多次调用啊

  5. crossyou | #5
    2009-12-13 16:16

    做个 小 板凳 学习下。

  6. QiQiBoY | #6
    2009-12-13 17:07

    我做滑动展开feed订阅时就是这么做的,我设置了100毫秒(貌似太短了,回头加长点吧),想一块去了。。 :razz: 第一次感觉离大师如此近。。。

  7. Leo | #7
    2009-12-13 18:06

    click事件还是不好处理……

  8. keon | #8
    2009-12-13 18:57

    前排圍觀~~~

  9. Xu.hel | #9
    2009-12-13 19:13

    谢谢咯,呵呵.有机会来成都耍哈

  10. ZH CEXO | #10
    2009-12-13 19:14

    这个我一直有疑问来着,现在总算有解决方法了

  11. sky | #11
    2009-12-13 23:07

    没根本解决。移动鼠标如果能掌握那个0.2秒的规律还是存在这个问题. 不过一般客户不会这样。呵呵

  12. Rui | #12
    2009-12-14 02:34

    已经算很完美的解决方案了。

  13. Tao | #13
    2009-12-14 10:08

    这里聚集了这么多搞前台的?

  14. underone | #14
    2009-12-14 11:13

    我在6.3的按钮上玩了半天:)~

  15. 吓我一跳 | #15
    2009-12-14 11:42

    好东西 收藏一下

  16. Jutoy | #16
    2009-12-14 18:40

    围观技术贴…

  17. fatkun | #17
    2009-12-15 00:05

    围观一下~~·看的不是太懂。。

  18. Justice | #18
    2009-12-15 01:10

    好巧,今天看到 CSSTricks 上也写了篇关于这个问题的。他的解决方法好像相当简单啊。
    http://css-tricks.com/full-jquery-animations/

  19. bolo | #19
    2009-12-15 16:11

    新文章跳票了?

  20. Gaze | #20
    2009-12-15 16:38

    如18楼所说. 真的好巧. 偶也是刚好看到这篇. 只要加上 stop(true, false) 即可解决.
    http://css-tricks.com/examples/jQueryStop/
    但延迟执行的处理其它时候也可能会用到

  21. Kael | #21
    2009-12-15 16:55

    jQuery的hover事件,没有判断event的relatedtarget,所以在悬停的时候,在hover中定义的方法会不断地被执行。

    可以判断鼠标的来源(relatedtarget),已经触发过hover,就可以不再触发,这也是这个问题更好的解决方法。可以参考一下mootools的mouseenter的定义。

    延时仍然不是根本的解决方法,如果动画时间较长,这个问题仍然会重演。

  22. SATURN | #22
    2009-12-15 21:14

    细节的问题也要很讲究~~

  23. zwwooooo | #23
    2009-12-16 19:44

    我的RSS订阅是加一个判断 is(":animated") 来阻止再触发

  24. mg12 | #24
    2009-12-19 00:54

    @Xu.hel
    我明年一定要去成都玩一次. 你备钱请客吧, 哈~

    @sky
    如果把悬停时间设置比动画时间稍长就不会发生这个问题了.

    @underone
    好玩不?

    @Justice
    哦~ 这个很好, 我要细细得看一遍, 谢谢哈~

    @bolo
    我敲错日期了, 改了一下都被你看到了...

    @Gaze , @Kael
    嗯, 很好. 我又学到一招了.

  25. lin04com | #25
    2010-01-09 22:36

    这个解决方法不错,记得以前在cssrain的网站上面好像也看到了一个解决方法,原理都是延迟。

  26. d.c. | #26
    2010-04-30 01:05

    嗨 mg12 谢谢你写了这么详细的教程。我学到很多东西。:)

    我想修改一下滑动方向的时候,却不知道怎么下手。你能帮我一下吗?

    我想把下滑的动作slideDown,改成往上滑。因为我的导航bar是在页面最下面,我想滑出的菜单是往上的(类似windows的开始菜单一样)。不知道你有没有时间,告诉我应该修改哪个地方?

    tks again

  27. ZhouQi | #27
    2010-05-14 14:49

    “现在本博客的 RSS feed 列表也是这样实现的. ”
    最好加一个小箭头,提示有列表

  28. Joe | #28
    2010-05-22 16:20

    请使用.stop()

  1. 给 RSS 订阅换上 jQuery 滚动下拉菜单 | ③秋之流☆'s Blog
    2010-06-02 19:43
  2. Wordpress折腾手记 6 | A.shun's Blog
    2010-07-09 08:48

Twitter Email feed
RSS feed