jQuery动画高度显示/隐藏卡住

时间:2012-11-22 10:27:55

标签: javascript jquery html css

我想我知道为什么会这样,但我不确定解决问题的最佳方法。这是您可以参考的jsFiddle

如果您尝试打开和关闭jsFiddle中的子菜单(单击任何链接旁边的+图标),然后在完全关闭之前再次打开它将会卡住。现在打开菜单并尝试打开其中一个子子菜单,您将看到它的父级不会扩展以适应它。

我认为这个问题是由于在hide过程jQuery期间对元素应用内联高度,如果在完成动画之前尝试打开它,则假定它是最终高度元素。

我考虑在最开始时存储每个元素的高度并使用它来设置动画,但是这种方法的问题是具有子菜单高度的菜单一直在变化,这取决于它的子菜单是否打开和这个值永远不会是常数。

有没有告诉jQuery忽略元素的内联高度并计算它的真实高度应该是什么?

以下是使用的jQuery,对于HTML和CSS,请参阅jsFiddle,因为它们相当冗长:

jQuery(document).ready(function($){
    var legacyMode = $('html').hasClass('oldie');
    var titles = {normal: "Show sub-topics", active: "Hide sub-topics"};

    var sub_sections = $('ul#map li:has(ul.child)');

    sub_sections.each(function() {
        if (!$(this).find('li.active').length && !$(this).hasClass('active')) {
            var toggle = $('<a class="toggle" href="#"></a>').attr('title', titles.normal).insertBefore($(this).children('ul.child'));
            var child = $(this).children('ul.child').hide();
            toggle.data('container', this);
        }
    });

    $('a.toggle').click(function (event) {
        event.preventDefault();
        var target = $(this).siblings('ul.child');
        if($(this).hasClass('active')) {
            toggleDisplay(target, false);
            $(this).removeClass('active').attr('title', titles.normal);
        } else {
            toggleDisplay(target, true);
            $(this).addClass('active').attr('title', titles.active);
        }
        function toggleDisplay(target, on) {
            var mode = (on) ? "show" : "hide";
            if (!legacyMode) {
                target.stop(true, false).animate({height: mode, opacity: mode}, 500);
            } else {
                // Omits opacity to avoid using proprietary filters in legacy browsers
                target.stop(true, false).animate({height: mode}, 500);
            }
        }
    });
});​

3 个答案:

答案 0 :(得分:2)

这是因为您在animate()之前传递stop()方法的属性。

方法stop(true, false)中的第二个属性指定动画是否应跳转到最后一帧。在您的代码中,由于它是false,因此它会在锚标记旁边注册click的阶段停滞不前。

将其更改为.stop(true, true),它将按预期工作!

答案 1 :(得分:0)

好的,所以我提出了一个有效的解决方案...它不像我希望的那样直截了当,但无论如何这里是工作代码:

jQuery(document).ready(function($){
    var legacyMode = $('html').hasClass('oldie');
    var titles = {normal: "Show sub-topics", active: "Hide sub-topics"};

    var sub_sections = $('ul#map li:has(ul.child)');

    sub_sections.each(function() {
        if (!$(this).find('li.active').length && !$(this).hasClass('active')) {
            var child = $(this).children('ul.child');
            if (!legacyMode) {
                child.css({height : '0px', opacity : 0, display: 'none'});
            } else {
                // Omits opacity to avoid using proprietary filters in legacy browsers
                child.css({height : '0px', display: 'none'});
            }
            var toggle = $('<a class="toggle" href="#"></a>').attr('title', titles.normal).insertBefore(child);
            toggle.data('container', this);
        }
    });

    $('a.toggle').click(function (event) {
        event.preventDefault();
        var target = $(this).siblings('ul.child');
        if($(this).hasClass('active')) {
            toggleDisplay(target, false);
            $(this).removeClass('active').attr('title', titles.normal);
        } else {
            toggleDisplay(target, true);
            $(this).addClass('active').attr('title', titles.active);
        }
        function toggleDisplay(target, on) {
            var targetOpacity = 0;
            var targetHeight = 0;
            if (on) { 
                // Get height of element once expanded by creating invisible clone
                var clone = target.clone().attr("id", false).css({visibility:"hidden", display:"block", position:"absolute", height:""}).appendTo(target.parent());
                targetHeight = clone.height();
                targetOpacity = 1;
                console.log(clone.height());
                clone.remove();
                target.css({display : 'block'});
            }
            if (!legacyMode) {
                target.stop(true, false).animate({height: targetHeight + "px" , opacity: targetOpacity}, {
                    duration: 500,
                    complete: function() {
                        if (on) {
                            $(this).css({height : '', opacity : ''});
                        } else {
                            $(this).css({display : 'none'});
                        }
                    }
                });
            } else {
                // Omits opacity to avoid using proprietary filters in legacy browsers
                target.stop(true, false).animate({height: targetHeight + "px"}, {
                    duration: 500,
                    complete: function() {
                        if (on) {
                            $(this).css({height : ''});
                        } else {
                            $(this).css({display : 'none'});
                        }
                    }
                });           
            }
        }
    });
});​

在行动中看到它:http://jsfiddle.net/xetCd/24/(任意顺序点击切换的任意组合,它应保持平稳)。

经过测试:IE7,IE8(将var legacyMode设置为true以获得最佳效果),Firefox 15,Chrome 23

它是如何工作的?好吧,我不再使用showhide动画目标,因为这些目标不够灵活。出于这个原因,我必须在初始化时隐藏ul一点点:

var child = $(this).children('ul.child');
if (!legacyMode) {
    child.css({height : '0px', opacity : 0, display: 'none'});
} else {
    // Omits opacity to avoid using proprietary filters in legacy browsers
    child.css({height : '0px', display: 'none'});
}
var toggle = $('<a class="toggle" href="#"></a>').attr('title', titles.normal).insertBefore(child);​

现在谈到多汁的位,如何在动画的任何阶段计算元素的目标高度,并且打开或关闭任何数量的子菜单。我通过创建元素的visibility : hidden副本并强制它占用它的常规高度来解决这个问题,我还给它position : absolute,这样它就不会占用文档中的任何空格。我抓住它的高度并删除它:

var targetOpacity = 0;
var targetHeight = 0;
if (on) { 
    // Get height of element once expanded by creating invisible clone
    var clone = target.clone().attr("id", false).css({visibility:"hidden", display:"block", position:"absolute", height:""}).appendTo(target.parent());
    targetHeight = clone.height();
    targetOpacity = 1;
    console.log(clone.height());
    clone.remove();
    target.css({display : 'block'});
}​

然后我为其设置动画并确保重置display并删除height(以便子菜单可以扩展其父级)属性:

if (!legacyMode) {
    target.stop(true, false).animate({height: targetHeight + "px" , opacity: targetOpacity}, {
        duration: 500,
        complete: function() {
            if (on) {
                $(this).css({height : '', opacity : ''});
            } else {
                $(this).css({display : 'none'});
            }
        }
    });
} else {
    // Omits opacity to avoid using proprietary filters in legacy browsers
    target.stop(true, false).animate({height: targetHeight + "px"}, {
        duration: 500,
        complete: function() {
            if (on) {
                $(this).css({height : ''});
            } else {
                $(this).css({display : 'none'});
            }
        }
    });           
}​

感谢阅读。

答案 2 :(得分:0)

我遇到了与jQuery 1.8.3相同的问题,发现升级到v1.12.2修复了它。对于其他遇到此问题的人来说,这可能是一个解决方案。如果这对您不起作用,请告诉我。