1

なぜこれが起こっているのかはわかっていると思いますが、それに取り組む最善の方法はわかりません。参照できるjsFiddleは次のとおりです。

jsFiddle でサブメニューを開いて閉じようとし (リンクの横にある [+] アイコンをクリック)、完全に閉じる前に再度開くと、スタックします。ここでメニューを開き、その子サブメニューの 1 つを開こうとすると、その親がそれに対応するように拡張されていないことがわかります。

この問題は、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()メソッドに渡したプロパティが原因で発生していますanimate()

メソッドの2番目のプロパティは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 legacyModeを得るには に設定true)、Firefox 15、Chrome 23

それはどのように機能しますか?これらは十分な柔軟性がないため、アニメーション ターゲットshowと を使用しなくなりました。hideこのため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 に答える