web-dev-qa-db-fra.com

Solution CSS pure pour fractionner des éléments en un nombre dynamique de colonnes

Existe-t-il un moyen d’aligner des éléments dans plusieurs colonnes, le nombre de colonnes dépendant de l’élément le plus large? La hauteur de l'élément et la largeur du conteneur sont fixes, mais la largeur de l'élément est dynamique.

Je recherche un moyen uniquement CSS d’obtenir le comportement suivant:

(Supposons que le conteneur parent mesure 300 pixels de large.)

  • Si l'élément le plus large est plus large que 150px, utilisez une seule colonne
  • Si l'élément le plus large se situe entre 100 et 150 pixels, utilisez deux colonnes
  • Si l'élément le plus large est inférieur à 100 px, utilisez trois colonnes
  • ...
  • Si l'élément le plus large est inférieur à la largeur du conteneur/N, utilisez N colonnes

Une façon possible d'avoir ce problème pourrait être d'utiliser display:inline-block et de définir la propriété width sur la largeur de l'élément le plus large du conteneur à l'aide de JavaScript.

Voir ce JSFiddle pour un exemple:

 Example

Cependant, je pense qu'il devrait également exister un moyen de le faire uniquement avec CSS. C'est possible?

Si ce n'est pas le cas, il existe peut-être un moyen élégant, basé uniquement sur CSS, de distribuer/capturer les éléments de taille dynamique dans les colonnes d'un conteneur de largeur fixe.

41
Costantino Rupert

De plus, ils n’ont pas de solution CSS. Je voulais donc vous laisser une solution intéressante en utilisant js, dans laquelle vous n’aurez même pas à parcourir le contenu le plus large, tout provient d’un ensemble de math.

Vous pouvez modifier le contenu des éléments et vous verrez comment le nombre de colonnes est ajusté. Si l'élément le plus large est inférieur à la largeur du conteneur/N, il utilisera automatiquement N colonnes.

Découvrez cette démonstration de JSFiddle

L'idée est très simple, vous set width: 'auto', display: 'inline-block' à container pour lui permettre de s'adapter à son contenu qui est défini par la largeur de l'élément le plus large, voyez-le dans le masque ci-dessous img:

 enter image description here

Maintenant, vous pouvez utiliser la largeur de container pour connaître la largeur de l'élément le plus large, ce qui vous évite d'avoir à itérer pour le rechercher. Pour le moment, vous pouvez utiliser cette largeur pour définir le reste des éléments, mais faisons-le un peu mieux. Nous pouvons faire en sorte que les éléments occupent toute la largeur de la container avec juste une portée calculée:

var div = Math.floor(initialWidth / widestItemWidth);     /*this is to get as many items fit with the width of the widest element*/
var itemWidth = initialWidth / div;      /*for the width of the item occupy the entire container*/

Donc, si l'élément le plus large est inférieur à la largeur du conteneur/N, il utilisera automatiquement N colonnes et leur taille correspondra à la largeur de container, c'est tout ce que vous avez à faire, définissez simplement une propriété css, faites un peu de calcul et vous avez ce que tu voulais réaliser.

$(document).ready(function() {
    var $container = $('.container');
    var $item = $('.item');

    var initialWidth = $container.outerWidth();

   $container.css({     /*to allow it to fit its contents which is defined by the width of the widest element*/
        width: 'auto',
        display: 'inline-block'
    });   

    var widestItemWidth = $container.outerWidth();     /*Now this is the width of the widest item*/

    var div = Math.floor(initialWidth / widestItemWidth);     /*this is to get as many items fit with the width of the widest element*/
    var itemWidth = initialWidth / div;      /*for the width of the item occupy the entire container*/
  
  /*if you don't want the items occupy the entire width of the container just use 'widestItemWidth' as width of $item*/

    $item.css({
        width: itemWidth,
        float: 'left'
    });
    $container.css({     /*restoring the initial styles*/
        width: initialWidth,
        display: 'block'
    });
})
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
}
*{
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
    <div class="item">Maudie Mcmanus</div>
    <div class="item">Remedios Arrington</div>
    <div class="item">Chaya B</div>
    <div class="item">Duncan</div>
    <div class="item">Lashonda Tatum Walls</div>
</div>

Si vous souhaitez ajouter une marge à vos éléments, vous devez simplement en tenir compte lorsque vous faites le calcul comme ceci:

$(document).ready(function() {
    var $container = $('.container');
    var $item = $('.item');

    var initialWidth = $container.outerWidth();

    $container.css({
            width: 'auto',
            display: 'inline-block'
        });

    var currentWidth = $container.outerWidth();
    var itemMargin= parseInt($item.css('margin-right'));

    var div = Math.floor(initialWidth / currentWidth);
    var itemWidth = initialWidth / div - itemMargin;   /*subtracting the item's margin from the compute width*/

    $item.css({
        width: itemWidth,
        float: 'left'
    });
    $container.css({
        width: initialWidth,
        display: 'block'
    });
})
.container {
  width: 400px;
  overflow: hidden;
}
.container .item {
  padding: 8px;
  border: 1px solid rgb(216, 213, 213);
  margin-right: 3px;
}
*{
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
    <div class="item">Maudie Mcmanus</div>
    <div class="item">Remedios Arrington</div>
    <div class="item">Chaya B</div>
    <div class="item">Duncan</div>
    <div class="item">Lashonda Tatum Walls</div>
</div>

Voici un exemple JSFiddle à jouer avec

10
Yandy_Viera

Le plus proche que vous pouvez probablement obtenir avec une solution CSS pure consiste à utiliser des colonnes CSS comme suggéré par d'autres (ou l'approche Grille CSS - Voir Edition). Toutefois, la clé consiste à utiliser des unités relatives, à bien connaître l'emplacement et le conteneur de colonnes dans votre présentation et à utiliser des requêtes de support pour contrôler le nombre de colonnes. Ceci est certainement PAS une solution à toute épreuve. Toutefois, si vous créez la mise en page et contrôlez le placement et la portée de l'élément conteneur de colonne, vous pouvez contrôler le nombre de colonnes de manière relativement fiable. S'il s'agit d'un widget hors de votre contrôle, vous devrez en effet implémenter une solution basée sur Javascript.

Liens de référence: Responsive CSS Columns , CSS Grid Layout

EDIT: Après avoir relu votre question et examiné votre exemple de capture d'écran, il semble que vous cherchiez réellement une solution de grille de grille CSS commune dans laquelle les éléments sont placés horizontalement et non verticalement. J'ai mis à jour ma réponse pour inclure une démonstration de l'extrait où les blocs sont redirigés horizontalement.

Démonstration d'extrait de code avec requêtes de média et largeurs de conteneur basées sur des colonnes (largeur de colonne minimale d'environ 10 em):

#main, #sideBar {
    box-sizing:border-box;
    display:block;
    padding:0;
    margin:0;
    width:100%;
}

.container {
    width: 100%;
    min-width:10em;
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
}
.container .item {
    display: block;
    padding: 8px;
    border: 1px solid rgb(216, 213, 213);
    box-sizing:border-box;
    -webkit-column-break-inside: avoid; /* Chrome, Safari, Opera */
          page-break-inside: avoid; /* Firefox */
               break-inside: avoid; /* IE 10+ */
}
@media only screen and (min-width:20em) {
    .container {
    -webkit-columns: 2;
    -moz-columns: 2;
      columns: 2;
    }
}

@media only screen and (min-width:32em) {
    #main {
        float:left;
        width:65%;
    }

    #sideBar {
        float:left;
        width:30%;
        margin-left:5%;
    }
    #sideBar .container {
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
    }

}

@media only screen and (min-width:48em) {
    
    #main .container {
    -webkit-columns: 3;
    -moz-columns: 3;
      columns: 3;
    }

    #sideBar .container {
    -webkit-columns: 1;
    -moz-columns: 1;
      columns: 1;
    }

}

@media only screen and (min-width:52em) {
    
    #sideBar .container {
    -webkit-columns: 2;
    -moz-columns: 2;
      columns: 2;
    }

}

@media only screen and (min-width:100em) {
    
    #main .container {
    -webkit-columns: 5;
    -moz-columns: 5;
      columns: 5;
    }

    #sideBar .container {
    -webkit-columns: 3;
    -moz-columns: 3;
      columns: 3;
    }

}
<div id="main"><h1>#main</h1>
    <div class="container">
        <div class="item">Maudie Mcmanus</div>
        <div class="item">Remedios Arrington</div>
        <div class="item">Chaya B</div>
        <div class="item">Duncan</div>
        <div class="item">Lashonda Tatum Walls</div>
    </div>
</div>
<div id="sideBar"><h1>#sideBar</h1>
    <div class="container">
        <div class="item">Maudie Mcmanus</div>
        <div class="item">Remedios Arrington</div>
        <div class="item">Chaya B</div>
        <div class="item">Duncan</div>
        <div class="item">Lashonda Tatum Walls</div>
    </div>
</div>

EDIT: Ajout de cette démo d'extrait de code utilisant une approche CSS Grid qui redirige les blocs horizontalement au lieu de verticalement.

#main, #sideBar {
    box-sizing:border-box;
    display:block;
    padding:0;
    margin:0;
    width:100%;
    clear:left;
}

.container {
    width: 100%;
    min-width:10em;
}

.container .item {
    display: block;
    float:left;
    padding: .5em;
    border: 1px solid rgb(216, 213, 213);
    box-sizing:border-box;
    vertical-align: top;

}

@media only screen and (min-width:20em) {
    .container .item {
        margin: 0 1%;
        width:48%;
    }
}

@media only screen and (min-width:32em) {
    #main {
        float:left;
        width:65%;
    }

    #sideBar {
        float:left;
        clear:none;
        width:30%;
        margin-left:5%;
    }
    

    #sideBar .container .item {
        margin: 0;
        width:100%;
    }

}

@media only screen and (min-width:48em) {
    
    #main .container .item {
        width:31%;
    }

}

@media only screen and (min-width:52em) {
    
    #sideBar .container .item {
        margin: 0 1%;
        width:48%;
    }


}

@media only screen and (min-width:100em) {
    
    #main .container .item {
        width:18%;
    }

    #sideBar .container .item {
        width:31%;
    }

}
<div id="main"><h1>#main</h1>
    <div class="container">
        <div class="item">Maudie Mcmanus</div>
        <div class="item">Remedios Arrington</div>
        <div class="item">Chaya B</div>
        <div class="item">Duncan</div>
        <div class="item">Lashonda Tatum Walls</div>
    </div>
</div>
<div id="sideBar"><h1>#sideBar</h1>
    <div class="container">
        <div class="item">Maudie Mcmanus</div>
        <div class="item">Remedios Arrington</div>
        <div class="item">Chaya B</div>
        <div class="item">Duncan</div>
        <div class="item">Lashonda Tatum Walls</div>
    </div>
</div>

4
IMI

Comme beaucoup d’autres l’ont fait remarquer ici, il n’existe pas de solution CSS uniquement. Principalement parce que CSS a été conçu pour être chargé avant le contenu du dom afin que le contenu s’affiche correctement. Notre principal problème ici est que nous ne connaissons PAS la largeur de l’élément à l’avance, nous ne le saurons que lorsque la page aura été chargée. Presque toutes les solutions de la page ont la même idée, utilisez Javascript pour parcourir en boucle les éléments, trouver le plus large et définir tous les autres éléments (de même classe) à la largeur donnée. Pour améliorer le processus et en faire une solution à une ligne (avec jQuery, on peut aussi le faire avec Javascript, mais pour plus de simplicité, jQuery a été utilisé), je suggère le "hack/trick" suivant:

1) Ne définissez pas la largeur de votre conteneur et définissez l’affichage sur table plutôt que sur bloc;

2) Ne définissez aucune largeur pour les éléments à l'intérieur du div

Cela fera en sorte que les éléments aient la même largeur et soient affichés dans une colonne. Ainsi, lorsque nous utilisons jQuery pour obtenir la largeur maximale, il n'est pas nécessaire de faire une boucle entre les éléments.

Utilisez maintenant jQuery pour injecter des règles CSS supplémentaires dans l'en-tête du document pour le conteneur et pour les éléments. Nous devons changer le type d'affichage du conteneur en block au lieu de tableau, définir sa largeur à 400px (ou tout ce dont nous avons besoin) et rendre les éléments inline-block d'une largeur définie. 

Comme CSS applique toujours la dernière propriété, ce sera très simple:

$("<style> .container {display: block; width:400px;} .item-min {width:"+$(".item-min").width()+"px;display:inline-block;}</style>").appendTo("head");

J'ai mis à jour votre exemple Fiddler ICI

Je l'ai testé dans IE 12, Edge, Chorme et Firefox et cela fonctionne dans tous les navigateurs.

EDIT 1

Si vous ne voulez pas utiliser jQuery pour garder la page aussi légère que possible, vous pouvez également utiliser le liner Javascript suivant:

document.head.innerHTML += "<style> .container {display: block; width:400px;} .item-min {width:"+(document.getElementsByClassName("container")[0].clientWidth-16-2)+"px;display:inline-block;}</style>";

Explication rapide: Container clientWidth inclura le remplissage et la bordure des éléments, puisque le remplissage de l'exemple d'origine est 8px et que la bordure est 1px, nous devons le déduire deux fois de la largeur du conteneur.

Voir la mise à jour Fiddle

EDIT 2

Un document HTML complet:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <style type='text/css'>
    .container {

    display:table;
    table-layout:fixed;
}

.container .item-min {
    padding: 8px;
    border: 1px solid rgb(216, 213, 213);

}
  </style>


<script type='text/javascript'>
window.onload=function(){
document.head.innerHTML += "<style> .container {display: block; width:400px;} .item-min {width:"+(document.getElementsByClassName("container")[0].clientWidth-16-2)+"px;display:inline-block;}</style>";
}
</script>

</head>
<body>
      <div class="container">
        <div class="item-min">Maudie Mcmanus</div>
        <div class="item-min">Remedios Arrington</div>
        <div class="item-min">Chaya B</div>
        <div class="item-min">Duncan</div>
        <div class="item-min">Lashonda Tatum Walls</div>
    </div>


</body>

</html>
2
Emil Borconi

Zuallererst - Découvrez le Fiddle Demo (Spiel mit der Breite des .container ...)

Cliquez ici pour afficher le code CSS, cliquez ici pour en savoir plus sur le style, les éléments graphiques, les éléments graphiques, les éléments graphiques, les éléments graphiques, les éléments graphiques, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les filtres, les masques, les filtres, les filtres, les masques, les filtres, les filtres, les masques, les filtres, les masques, les filtres, les masques, les filtres, les masques, les filtres, les masques, les filtres, les masques, les filtres, les masques, les filtres, les masques, les masques, les filtres, les masques, leur comportement.

HTML

<p>Without width:
    <div class="container">
        <div class="item">Maudie Mcmanus</div>
        <div class="item">Remedios Arrington</div>
        <div class="item">Chaya B</div>
        <div class="item">Duncan</div>
        <div class="item">Lashonda Tatum Walls</div>
    </div>
</p>

Javascript

$(function(){
    var $items = $('.item');

    // detect the widest column's width
    var widestItemWidth = 0;
    $items.each(function(){
        widestItemWidth = Math.max(widestItemWidth, $(this).width());
    });

    // calculate the number of columns
    var $container = $('.container');
    var containerWidth = $container.width();
    var numOfColumns = parseInt(containerWidth / widestItemWidth);

    // create the columns
    for(var i = 0; i < numOfColumns; i++){
        $container.prepend('<div class="column"></div>');
    }
    var $columns = $('.column');
    $columns.css('width', (100/numOfColumns)+'%');

    // spread the items between the columns
    $items.each(function(i){
        var columnIndex = i % numOfColumns;
        $(this).appendTo($columns.eq(columnIndex));
    });
});

CSS

.container {
    width: 400px;
}
.container .column {
    display: inline-block;
    box-sizing: border-box;
    vertical-align: text-top;
}
.container .item {
    display: inline-block;
    padding: 8px;
    border: 1px solid rgb(216, 213, 213);
}
1
Ronen Cypis

Essayez cet exemple

@media only screen and (device-width : 300px) {#container {width:300px; display:block;}}
@media only screen and (device-width : 150px) {#container {width:150px; display:block; float:left}}
@media only screen and (device-width : 100px) {#container {width:150px; display:block; float:left}}
0
Stalin Rex

Le plus simple semblerait définir la largeur à 32% plus clear: aucune pour toutes les colonnes et une option distincte: les deux classes pour chaque troisième (dernière) colonne. Ajoutez des requêtes multimédia pour traiter différentes tailles d'écran et définissez la largeur en conséquence, par ex. 24%, 48%, etc.

0
user1263254