1

我想我知道为什么会发生这种情况,但我不确定解决它的最佳方法。这是一个您可以参考的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);
            }
        }
    });
});​
4

3 回答 3

2

这是因为您在stop()方法上传递的属性, before animate()

方法中的第二个属性stop(true, false)指定动画是否应该跳转到最后一帧。在您的代码中,因为它是false,所以它卡在了click下一个在锚标记上注册的阶段。

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

于 2012-11-22T10:36:57.060 回答
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 legacyModetrue最佳效果)、Firefox 15、Chrome 23

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

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'});
            }
        }
    });           
}​

谢谢阅读。

于 2012-11-22T15:10:05.233 回答
0

我在使用 jQuery 1.8.3 时遇到了同样的问题,发现升级到 v1.12.2 解决了这个问题。对于遇到此问题的其他任何人,这可能是一个解决方案。如果这对你不起作用,请告诉我。

于 2016-05-13T14:45:05.177 回答