2009-06-28 12 views
0

J'essaye de construire un accordéon horizontal avec Jquery. Il semble fonctionner "ok" dans Firefox. Mais dans Webkit (Safari 3 + 4 et Chrome), le sous-niveau UL clignote après la fonction Masquer. Toute aide serait grandement appréciée. Pour voir une démonstration de travail: http://ableobject.com/horaccordion1.htmlJquery Web Accordéon Webkit Bug

Voici ce que je travaille sur:

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

Répondre

1

Cela ressemble à elle est liée à une question que j'ai eu il y a un peu de temps avec WebKit. Il y a un bogue de webkit qui provoque parfois le retour à la taille d'origine d'un élément après une animation qui réduit la largeur des éléments. Après l'animation, le parent de l'élément revient à sa taille d'origine pour s'adapter à son contenu.

Modifier: commentaires supprimés à propos de jQueryUI. Je ne sais pas pourquoi je pensais que vous l'utilisiez.

Le bug a été discuté here, qui détaille une solution de contournement. J'ai également soumis un bug report à jQuery.

Fondamentalement, vous devez réduire simultanément la largeur de votre parent $sub de la même quantité que $sub est en cours de réduction. Donc, si la largeur de $sub est 100px, il y aurait un animate() séparé pour réduire le parent de 100px.

Je n'ai pas testé tout cela avec votre exemple, mais je pense que c'est probablement la clé.

Edit 2: Une nouvelle version en utilisant 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; 

}

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> 
+0

Je l'ai essayé sur http://ableobject.com/horaccordion2.html mais quand je vais partir Toplevel 4 d'un Toplevel élargi 3 il clignote toujours. Je vais essayer de le comprendre et de le republier ... mais pour le moment la solution m'échappe. Peut-être: $ sub.parent(). Css ({width: ''}); $ current = $ sub; ? – stapler

+0

BTW merci beaucoup patrick – stapler

+0

Quelle est l'importance de l'utilisation des éléments 'list'? Après les avoir finalement abandonnés et en utilisant divs, je suis arrivé avec un accordéon assez concis qui fonctionne dans webkit. – user113716

1

Je poste ceci comme une réponse séparée juste au cas où vous trouvez encore le précédent utile.

S'il vous plaît noter les éléments suivants:

  • Je n'ai pas testé dans IE.
  • Cela revient à une version «imbriquée», j'ai donc modifié un peu les noms de classes et de variables.
  • Un menu vide n'est plus nécessaire lorsqu'il n'y a pas de menu à afficher.
  • La largeur du 'conteneur' de chaque menu est maintenant réduite de la même valeur que le menu. C'est ce qui élimine le flash temporaire de webkit (qui était la stratégie originale).
  • Vous remarquerez que la synchronisation de l'animation du menu est légèrement différente de celle du conteneur du menu. Fondamentalement, vous voulez que le conteneur soit un peu en avance lors de l'expansion, et que le menu soit un peu en avance lors de la réduction. Si le minutage est égal, vous pouvez obtenir un clignotement du menu.
  • Comme expliqué dans les commentaires, au début, chaque menu "mémorise" sa largeur lorsqu'il est entièrement développé en définissant un attribut précédemment inexistant appelé "fullWidth". Il récupère ensuite la valeur de cet attribut en cas de besoin. Vous pouvez tout aussi bien utiliser des variables globales ou la fonction data() de jQuery pour stocker les informations. Le point est que les choses sont simplifiées si chaque menu est conscient de la largeur qu'il devrait être quand il est développé.

Alors voilà. J'espère que cela aide!

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> 

Edit: Ajouter la DTD suivante en haut de votre page-

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd"> 
+0

Cela fonctionne très bien, maintenant si je peux seulement comprendre comment le centrer dans IE :) – stapler

+0

Je rencontre un bug étrange dans Firefox ... J'ai changé la taille du lien à 16px, et maintenant le niveau inférieur A4 disparaît ? J'ai essayé d'ajouter plus (et moins) de pixels ... et de jouer avec le rembourrage ... Je n'arrive pas à le faire apparaître – stapler

+0

Pour ie au centre, ajoutez ce qui suit au tout en haut de la page. Je pense que je devrais être capable de comprendre le Firefox problème. Je reviendrai probablement à vous lundi. – user113716