【问题标题】:Jquery Horizontal Accordion Webkit BugJquery 水平手风琴 Webkit 错误
【发布时间】:2010-11-06 10:40:37
【问题描述】:

我正在尝试用 Jquery 构建一个水平手风琴。它似乎在 Firefox 中“正常”。但在 Webkit(Safari 3 + 4 和 Chrome)中,子级 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>

【问题讨论】:

    标签: javascript jquery webkit accordion


    【解决方案1】:

    这听起来与我不久前使用 webkit 时遇到的一个问题有关。有一个 webkit 错误,有时会导致元素的父级在减少元素宽度的动画后恢复到其原始大小。动画结束后,元素的父元素会跳回其原始大小以适应其内容。

    编辑:删除了关于 jQueryUI 的 cmets。不知道为什么我认为你在使用它。

    该错误已在here 进行了讨论,其中详细介绍了解决方法。

    我也向 jQuery 提交了bug report

    基本上,您需要同时减少 $sub 元素的父元素的宽度,其幅度与减少 $sub 的数量相同。所以如果$sub的宽度是100px,就会有一个单独的animate()将父级缩小100px。

    我没有用你的例子测试过这些,但我认为这可能是关键。

    编辑 2: 使用 div 的新版本

    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;
    

    }

    javascript:

        // 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>
    

    【讨论】:

    • 我已经尝试过了ableobject.com/horaccordion2.html,但是当我从扩展的 Toplevel 3 从 Toplevel 4 开始时,它仍然闪烁。我会尝试弄清楚并重新发布......但目前解决方案让我无法理解。也许: $sub.parent().css({ width: '' }); $当前 = $sub; ?
    • 使用“列表”元素对您来说有多重要?在最终放弃它们并使用 div 之后,我想出了一个在 webkit 中工作的相当简洁的手风琴。
    • 这不是那么重要,我只是不想为列表项(或 div)定义宽度。
    • 向左浮动时无需在 div 上设置宽度。不确定这是否是最好的方法,但无论如何您都没有 webkit 闪烁问题。这是我能找到的最干净、最简洁的方式。唯一奇怪的是需要 Firefox 修复(参见代码 cmets)。希望对您有所帮助。
    • 太好了,谢谢帕特里克。 webkit 似乎还有另一个关于居中对齐菜单的问题。它在 Firefox 中使用 display: table;边距:0 自动;即 IE 使用 text-align: center;在 #container 但 webkit 不会不起作用!感谢您的所有帮助!
    【解决方案2】:

    我将其作为单独的答案发布,以防您仍然觉得上一个有用。

    请注意以下几点:

    • 我没有在 ie 中测试过这个。
    • 这又回到了“嵌套”版本,所以我稍微更改了类和变量名。
    • 当没有要显示的菜单时,不再需要空菜单。
    • 每个菜单的“容器”的宽度现在减少了与菜单相同的量。这就是消除 webkit 的临时闪存(这是最初的策略)的原因。
    • 您会注意到菜单动画的时间与菜单容器的时间略有不同。基本上,您希望容器在扩展时稍微领先,而菜单在缩小时稍微领先。如果时间设置为相等,您可以获得一些菜单闪烁。
    • 正如 cmets 中所解释的,在开始时,每个菜单在完全展开时都会通过设置一个以前不存在的名为“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">
    

    【讨论】:

    • 这很好用,如果我现在只能弄清楚如何在 IE 中居中 :)
    • 我在 Firefox 中遇到了一个奇怪的错误...我已将链接大小更改为 16px,现在底层 A4 消失了?我已经尝试添加更多(和更少)像素......并使用填充......我似乎无法让它显示出来
    • 为了居中,将以下内容添加到页面的最顶部。 ttp://www.w3.org/TR/html4/strict.dtd" rel="nofollow" target="_blank">w3.org/TR/html4/strict.dtd"> 我想我应该能够找出 Firefox 的问题。可能会在星期一回复您。
    • 好的,其实我很快就搞定了。我已经更正了 .each() javascript 中的方法。向菜单添加多少像素,需要向菜单容器添加两倍的像素。
    • 一切运行良好,我也将其转换为与 Ems 一起使用。 ttp://www.w3.org/TR/html4/strict.dtd" rel="nofollow" target="_blank">w3.org/TR/html4/strict.dtd"> 似乎没有将其居中,也没有将其包含在
      中。我在ableobject.com/horaccordion.html进行了测试
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 2012-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多