Вопрос

Я пытаюсь построить горизонтальный аккордеон с помощью Jquery.Кажется, в Firefox все работает нормально.Но в Webkit (Safari 3+4 и Chrome) после функции Hide мигает подуровень UL.Любая помощь будет принята с благодарностью.Чтобы увидеть рабочую демо-версию: http://ableobject.com/horaccordion1.html

Вот над чем я работаю:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>

    <title>untitled</title>
    <style type="text/css">
    #container {
        display: table; 
        margin: 0 auto; 
        text-align: center; /* for IE */
    }
            ul{
                    list-style: none;
                    background-color: yellow;
                    margin: 0;
                    padding: 0;
                    float: left;
                    height: 20px; /* For testing */     
            }
            ul li {
                   background-color: aqua;
                    float: left;
            }
            ul li ul {
                   background-color: blue;
                    display: none;
            }
            ul li ul li {
                   background-color: green;
            }
            a, a:link, a:hover, a:visited, a:active {
                    color: black;
                    text-decoration: none;
                    float: left;
            }
    </style>

   <script type="text/javascript">
/* Care of Hunter Daley */
    var $current = null;
        $(document).ready(function(){
         $("ul li ul").hide();  // hide submenus by default on load

           $("ul li a").click(function(){
              var $sub = $(this).next(); 
              if ($sub.css("display") == "none")
              {
                 if ($current != null)
                    $current.animate({ width: 'hide' }); // if you want to only show one sub at a time
                 $sub.animate({ width: 'show' }); 
                 $current = $sub;
              }
              else
              {
                 $sub.animate({ width: 'hide' });
                 $current = null;
              }
           });
        });
    </script>
</head>

<body>
    <div id="container">
    <ul>
            <li>
                    <a href="#">Top-level 1</a>
            </li>
            <li>
                    <a href="#">Top-level 2</a>

                    <ul>
                            <li><a href="#">Bottom Level A1</a></li>
                            <li><a href="#">Bottom Level A2</a></li>
                            <li><a href="#">Bottom Level A3</a></li>
                            <li><a href="#">Bottom Level A4</a></li>
                    </ul>
            </li>

            <li>
                    <a href="#">Top-level 3</a>
                    <ul>
                            <li><a href="#">Bottom Level B1</a></li>
                            <li><a href="#">Bottom Level B2</a></li>
                    </ul>
            </li>

            <li>
                    <a href="#">Top-level 4</a>
            </li>
    </ul>
</div>
</body>
Это было полезно?

Решение

Похоже, это связано с проблемой, с которой я недавно столкнулся с webkit.Существует ошибка в веб-ките, которая иногда приводит к тому, что родительский элемент элемента возвращается к исходному размеру после анимации, уменьшающей ширину элемента.После анимации родительский элемент возвращается к исходному размеру, чтобы разместить его содержимое.

Редактировать:Удалены комментарии о jQueryUI.Не знаю, почему я думал, что вы его используете.

Баг обсуждался здесь, в котором подробно описан обходной путь.

Я отправил отчет об ошибке также в jQuery.

По сути, вам нужно будет одновременно уменьшить ширину вашего $sub родительский элемент на ту же сумму, что и $sub сокращается.Итак, если ширина $sub составляет 100 пикселей, будет отдельный animate() чтобы уменьшить родительский элемент на 100 пикселей.

Я не проверял ничего из этого на вашем примере, но думаю, что это, вероятно, ключ.

Редактировать 2:Новая версия с использованием divs

CSS:

.title {
       list-style: none;
       margin: 0;
       padding: 0;
       float: left;
       height: 32px; /* For testing */
       font-family: helvetica;
       font-size: 18px;
       clip: auto; overflow: hidden;
}
.menu {
       height: 32px; /* For testing */
       clip: auto; overflow: hidden;
       float: left;
}
a, a:link, a:hover, a:visited, a:active {
       color: black;
       text-decoration: none;
       padding: 12px;
       font-weight: 700;
       float: left;
       color: #222;
}

 .menu a, .menu a:link, .menu a:hover, .menu a:visited, .menu a:active {
   color: black;
   text-decoration: none;
   padding: 12px;
   font-weight: normal;
   float: left;

}

яваскрипт:

    // Prevents us from having to check for null.
    var $current = $('#someFictionalElement');
    var $previous = null;
    $(document).ready(function(){
    $(".menu").css({width: 0});  // hide submenus by default on load

    $(".title").click(
        function() {
            $previous = $current;
            $current = $(this);
            var $currentMenu = $current.next();

            $previous.next().animate({ width: 0 }, {duration: 1000, queue: false} );

    // Make sure that if there's no menu text (like Top Level 1 and 4) that it does not animate.
    // This is because of the pixels added for Firefox (see comment below)
            if( $currentMenu.width() == 0 && $currentMenu.text() != ''  ) {

    // Expand the menu but keep it hidden so we can get its width
                $currentMenu.css({visibility: 'hidden', width: ''});

    // Store the width, and add a few pixels for Firefox
                var currentWidth = $currentMenu.width() + 3;

    // Make menu visible and set with to 0 in preparation for the animation
                $currentMenu.css({visibility: 'visible', width: 0})
                            .animate({ width: currentWidth }, 1000);
            }
    });

    $(".title a").hover(
        function(){$(this).animate ({ opacity: 0.7 }, 200);},
        function(){$(this).animate ({ opacity: 1 }, 600);}
    );
});

HTML:

<body>
    <div id="container">
        <div class='title' id='level1'>
            <a href="#">Top-level 1</a>
        </div>
        <div class='menu'></div>
        <div class='title' id='level2'>
            <a href="#">Top-level 2</a>
        </div>
        <div class='menu'>                         
            <a href="#">Bottom Level A1</a>
            <a href="#">Bottom Level A2</a>
            <a href="#">Bottom Level A3</a>
            <a href="#">Bottom Level A4</a>
        </div>
        <div class='title' id='level3'>
            <a href="#">Top-level 3</a>
        </div>
        <div class='menu'>
            <a href="#">Bottom Level B1</a>
            <a href="#">Bottom Level B2</a>
        </div>
        <div class='title' id='level4'>
            <a href="#">Top-level 4</a>
        </div>      
        <div class='menu'></div>
    </div>
</body>

Другие советы

Я публикую это как отдельный ответ на тот случай, если предыдущий ответ все еще будет вам полезен.

Обратите внимание на следующее:

  • Я не проверял это, т.е.
  • Это восходит к «вложенной» версии, поэтому я немного изменил имена классов и переменных.
  • Пустое меню больше не требуется, если меню для отображения нет.
  • Ширина «контейнера» каждого меню теперь уменьшается на ту же величину, что и само меню.Это то, что исключает временную вспышку Webkit (которая была исходной стратегией).
  • Вы заметите, что время анимации меню немного отличается от времени анимации контейнера меню.По сути, вы хотите, чтобы контейнер был немного впереди при расширении, а меню — немного вперед при уменьшении.Если время установлено равным, вы можете получить некоторое мигание меню.
  • Как поясняется в комментариях, вначале каждое меню «запоминает» свою ширину при полном раскрытии, устанавливая ранее несуществующий атрибут под названием «fullWidth».Затем при необходимости он извлекает значение этого атрибута.Вы также можете легко использовать глобальные переменные или функцию jQuery data() для хранения информации.Дело в том, что все упрощается, если каждое меню знает, насколько широким оно должно быть при раскрытии.

Итак, вот оно.Надеюсь, поможет!

CSS

#container {
    margin: 0 auto 0 auto; 
    text-align: center;
    display: table;
}

.menuContainer {
       margin: 0;
       padding: 0;
       float: left;
       height: 32px; /* For testing */
       font-family: helvetica;
       font-size: 18px;
       clip: auto; overflow: hidden;
}
.menu {
       height: 32px; /* For testing */
       clip: auto; overflow: hidden;
       float: left;
}
a, a:link, a:hover, a:visited, a:active {
       color: black;
       text-decoration: none;
       padding: 12px;
       font-weight: 700;
       float: left;
       color: #222;
}

.menu a, .menu a:link, .menu a:hover, .menu a:visited, .menu a:active {
   color: black;
   text-decoration: none;
   padding: 12px;
   font-weight: normal;
   float: left;
}

javascript

var $currentMenuContainer = $('#someFictionalElement');
var $previousMenuContainer = null;

$(document).ready(function() {

// Iterate through each .menu element, setting the full width of each menu to a 'custom'
//        attribute called 'fullWidth'. Since the full width should never change, this
//        makes it easy to recall it quickly. You could use global variables instead.
// After setting 'fullWidth', it then collapses each menu and title.
$(".menu").each(function() {
    var $theMenu = $(this);
    var $theMenuContainer = $theMenu.parent();
    $theMenu.attr({fullWidth: ($theMenu.width() + 3)});   // Add a few pixels for firefox
    var menuContainerWidth = $theMenuContainer.width() - $theMenu.attr('fullWidth') + 6;  // Add DOUBLE the pixels here
    $theMenu.css({width: 0});
    $theMenuContainer.css({width: menuContainerWidth});
});

    $(".menuContainer a").click(
        function() {
// Set the current and previous elements properly
            $previousMenuContainer = $currentMenuContainer;
            $currentMenuContainer = $(this).parent();
            var $previousMenu = $previousMenuContainer.find('.menu');
            var $currentMenu = $currentMenuContainer.find('.menu');

// Collapse the previous menu
            $previousMenu.animate({ width: 0 }, {duration: 480, queue: false} );

// Subtract the width of the previous menuContainer's menu from the menuContainer (only if its menu is displayed)
            if($previousMenu.width() > 0) $previousMenuContainer.animate({width: ('-=' + $previousMenu.attr('fullWidth'))}, 500);

// Expand the current menu and its menuContainer if it's not showing
            if($currentMenu.width() == 0) {
                // Increase the menuContainer width by the full width of its menu
                $currentMenuContainer.animate({width: ('+=' + $currentMenu.attr('fullWidth'))}, 480);
                // Increase the menuContainer to its full width
                $currentMenu.animate({ width: $currentMenu.attr('fullWidth') }, 500);
            }
    });

    $(".menuContainer a").hover(
        function(){$(this).animate ({ opacity: 0.7 }, 200);},
        function(){$(this).animate ({ opacity: 1 }, 600);}
    );
});

HTML

<div id="container">
    <div class='menuContainer'>
        <a href="#">Top-level 1</a>
    </div>
    <div class='menuContainer'>
        <a href="#">Top-level 2</a>
        <div class='menu'>                         
            <a href="#">Bottom Level A1</a>
            <a href="#">Bottom Level A2</a>
            <a href="#">Bottom Level A3</a>
            <a href="#">Bottom Level A4</a>
        </div>
    </div>
    <div class='menuContainer'>
        <a href="#">Top-level 3</a>
        <div class='menu'>
            <a href="#">Bottom Level B1</a>
            <a href="#">Bottom Level B2</a>
        </div>
    </div>
    <div class='menuContainer'>
        <a href="#">Top-level 4</a>
    </div>
</div>

Редактировать: Добавьте следующий DTD в начало страницы:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top