Probl�matique

L'effet recherch� ressemble � �a :

L'effet recherch�

Ma premi�re id�e, pour placer les bo�tes est d'utiliser le positionnement float avec un hr et la propri�t� clear : both; comme expliqu� dans ce tr�s bon article sur OpenWeb.

Au niveau code �a donne :

<div class="galerie">
        <div class="bo�te">bo�te 1</div>
        <div class="bo�te">bo�te 2</div>
        <div class="bo�te">bo�te 3</div>
        <div class="bo�te">bo�te 4</div>
        <div class="bo�te">bo�te 5</div>
        
        <hr class="clear" />
</div>

Appliquons le style suivant :

.galerie {
        background : #ccc;
        width : 360px; /* (50 + (5 * 2) + 10) * 5 + 10 */
}
.bo�te {
        background : #69c;
        
        float : left;
        margin : 10px 0 10px 10px;
        padding : 5px;
        width : 50px;
        
        text-align : center;
}
.clear {
        clear : both;
        visibility : hidden;
}

Ça donne :

Exemple 1.1

Avec 3 bo�tes uniquement :

Exemple 1.2

Vous pouvez tester ce premier exemple.

Le positionnement est celui recherch� lorsque toutes les bo�tes sont pr�sentes, mais, malheureusement, d�s qu'il manque des bo�tes, celles restantes sont cal�es � gauche. C'est tout � fait logique puisqu'elles sont flottantes � gauche, mais ce n'est pas ce que nous voulons !

Un deuxi�me conteneur pour le centrage

Au d�part, j'ai simplement essay� quelques techniques de centrage css appliqu�es sur les fiches ou sur le conteneur. Mais rien de tout �a ne fonctionne.

Le probl�me est qu'on ne peut pas centrer un flottant, � cause de sa conception m�me. Comme �crit tr�s justement dans l'article d'Openweb : Une bo�te flottante est retir�e du flux normal, et plac�e le plus � droite (float: right) ou le plus � gauche (float: left) possible dans son conteneur. Il est donc logique que les techniques de centrage ne puisse pas s'appliquer (centrer par rapport � quoi, puisque la bo�te est retir�e du flux ?).

Par contre, un �l�ment non-flottant peut, lui, �tre centr� (par exemple avec la technique des marges auto). Imaginons que l'on cr�e un autre conteneur, qui soit centr� par rapport au premier (la galerie), et qui contienne les fiches...

L'effet recherch�

Au niveau code �a donne :

<div class="galerie">
        <div class="conteneur">
                <div class="bo�te">bo�te 1</div>
                <div class="bo�te">bo�te 2</div>
                <div class="bo�te">bo�te 3</div>
                <div class="bo�te">bo�te 4</div>
                <div class="bo�te">bo�te 5</div>
                
                <hr class="clear" />
        </div>
</div>

On applique le m�me style que pr�c�demment. Mais il faut rajouter de quoi centrer le conteneur :

.galerie {
        text-align : center; /* uniquement pour IE */
}
.conteneur {
        margin : 0 auto;
}

L� se pose un probl�me - LE probl�me devrais-je dire - : pour que �a fonctionne il faut que le conteneur enveloppe parfaitement les bo�tes, et donc soit exactement � la bonne taille. La formule donnant la taille est facile � trouver :

taille = nombre de bo�tes * ( width + padding + margin-left ) + margin-right

Mais je n'ai strictement aucune id�e de comment faire �a en CSS. C'est th�oriquement possible de le fixer dans un style inline (dans la page) appliqu� au moment o� le script qui g�n�re la page affiche les bo�tes. Une autre solution est d'utiliser DOM pour manipuler en Javascript les styles de la page.

Impl�mentation en Javascript/DOM

Le script doit se charger de calculer la taille en fonction du nombre d'�l�ments contenus. Pour �a il faut :

  1. Trouver le conteneur � agrandir ; on utilisera la m�thode getElementsById
  2. Compter le nombre de bo�tes contenues :
    • Trouver les �l�ments div avec la m�thode getElementsByTagName
    • Eliminer ceux qui ne sont pas directement fils du conteneur (si les bo�tes contiennent d'autres divs par exemple
  3. Calculer la taille et agrandir le conteneur

Mon impl�mentation (qui n'est s�rement pas optimale) :

<script type="text/javascript">
 // <![CDATA[
function setContainerSize(truc) {
        var navroot = document.getElementById(truc);
        if ( navroot ) {
                var lis = navroot.getElementsByTagName("div");
                /* XXX : y a-t-il un moyen plus simple de ne d�tecter que les divs de rang 1 ?
                 *       ou, d�tecter selon une classe ? */
                var ok = 0; var nok = 0;
                for ( i = 0; i < lis.length; i++ ) {
                        if ( lis[i].parentNode != navroot )
                                nok++;
                        else 
                                ok++;
                }
                navroot.style.width = ok * 70 + 10 + 'px';
        }
}
 // ]]>
</script>

Maintenant, pour utiliser cette fonction, on effectue les modifications suivantes :

<body onload="setContainerSize('c1');">
...
<div class="conteneur" id="c1">

Ça donne :

Exemple 2

Vous pouvez consulter cet exemple.

Limites et pistes de reflexions

Cette m�thode a �t� test�e avec succ�s sur IE 6, Firefox 1.x, Safari 1.3 et Camino 0.9.

Une limitation principale de cette m�thode est l'impossibilit� de centrer sur plusieurs lignes. Par exemple, si la premi�re ligne contient 5 bo�tes et la deuxi�me 2, les 2 seront cal�es � gauche. Je n'ai pas vraiment cherch� comment r�soudre ce probl�me, car �a n'�tait pas mon besoin.

L'autre am�lioration possible concerne l'impl�mentation du script, et notamment la fa�on de compter les �l�ments div qui n'est s�rement pas optimale. Il serait �galement int�ressant de pouvoir ne pas coder en dur les tailles des div dans le script, de mani�re � avoir du code r�utilisable. Mais l� �a d�passe mes connaissance en DOM :)

Enfin, �tant donn� que le centrage se fait par un script, les bo�tes ne sont pas centr�es tant que la page n'est pas compl�tement charg�e. Dans certains cas �a provoque un petit d�calage � l'�cran le temps que le chargement se termine...

Quelques liens suppl�mentaires

Merci � Scara, MrGecko et tout le chan #asw pour la relecture ;)