chevron-left chevron-right

[CSS] Jak stworzyć 2 kolumny elementów ułożonych jak w mozaice?

W dzisiejszym artykule mam zamiar zaprezentować ciekawy sposób na zbudowanie 2-kolumnowego layoutu w którym elementy będą zajmowały dostępne miejsce bez pozostawiania wolnych przestrzeni. Dzięki temu otrzymamy coś w rodzaju mozaiki w dwóch kolumnach, która będzie się dostosowywała do wielkości elementów wewnątrz.

Demo

Swego czasu już pisałem o tym czym jest flexbox, dlatego tym, którzy nie do końca się orientują w tym temacie proponuję tam zajrzeć.

Kod HTML - flexbox

W celu osiągnięcia wymaganego efektu musimy najpierw sobie przygotować kod HTML, który będzie odwzorowywał listę elementów. Nie będzie to nic skomplikowanego, będzie to po prostu kontener z elementami w środku - czytaj: divy w divie 😉 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<div class="container">
    <div class="box">
        <img src="http://placehold.it/400x300" alt="">
        <p>Minions ipsum me want bananaaa! Para tú hahaha tank yuuu! Ti aamoo! Hana dul sae. Underweaaar bee do bee do bee do aaaaaah hahaha me want bananaaa! Chasy daa daa chasy uuuhhh chasy. Butt me want bananaaa! Bananaaaa la bodaaa tulaliloo uuuhhh poulet tikka masala chasy. Potatoooo tulaliloo jeje bee do bee do bee do po kass butt hana dul sae hahaha. Bappleees poulet tikka masala hahaha poopayee bappleees tulaliloo hana dul sae poopayee. Tatata bala tu wiiiii aaaaaah para tú. Wiiiii pepete me want bananaaa! Baboiii tank yuuu! Uuuhhh gelatooo poulet tikka masala butt hana dul sae. Bappleees pepete poopayee hahaha jiji tank yuuu!</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x130" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x270" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x250" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x180" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x300" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x150" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x290" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x350" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x430" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x460" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
    <div class="box">
        <img src="http://placehold.it/400x256" alt="">
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolores laboriosam numquam optio tenetur delectus animi incidunt aperiam, tempora non totam rerum iste officia doloremque asperiores praesentium suscipit reprehenderit, repellendus, libero.</p>
    </div>
</div>

Powyższa struktura jest bardzo powtarzalna. W każdym elemencie z klasą .box zamieściłem obrazek o różnej wysokości oraz tekst typu lorem ipsum.

Kod CSS - flexbox

Pora na trochę kodu CSS. Aby osiągnąć zamierzony przez nas efekt musimy dodać następujące reguły:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
* {
    box-sizing: border-box;
}
 
.container {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
}
 
.box {
    width: calc(50% - 1rem);
    padding: 1rem;
    margin: 0 .5rem 1rem;
    background: #eee;
}
 
.box img {
    display: block;
    width: 100%;
    max-width: 100%;
}

Efekt został osiągnięty dzięku temu, że do elementu .container dodano regułę uruchamiającą tryb flexbox: display: flex;. Inne reguły takie jak: flex-direction: column; oraz flex-wrap: wrap; sprawiają, że elementy będą się układały w kolumnie oraz będą zajmowały dostępne miejsce w kontenerze (ich ułożenie będzie się zawijać).

W związku z tym, że elementy z klasą .box mają szerokość połowie szerokości kontenera, to zostawiają one miejsce z boku, które jest zajmowane przez inne elementy z klasą .box.

Lecz to nie wszystko! - Potrzebny jest kod JS

Niestety, aby kontener mógł dynamicznie reagować na zmienną liczbę elementów wewnątrz lub na zmianę szerokości ekranu, tak aby elementy .box zawsze układały się w dwóch kolumnach, należy dopisać kod JS, który będzie obliczał wysokość elementu z klasą .container

:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
(function () {
    'use strict';
 
    var container = document.querySelector('.container'),
        boxes = Array.prototype.slice.call(container.querySelectorAll('.box')),
        setFlexboxHeight = function () {
            // lepiej ustawić wartość ratio na trochę więcej niż 50%,
            // dzięki temu jest mniejsze prawdopodobieństwo, że elementy się nie zmieszczą
            // w kontenerze
            var ratio = 0.55,
                boxesHeight = 0,
                boxMaxHeight = 0;
 
            // aby wykonać pomiar, należy tymczasowo ustawić wysokość kontenera na `auto`
            container.style.height = 'auto';
 
            // szukamy najwyższego elementu w kontenerze
            // oraz budujemy sumę wysokości wszystkich elementów
            boxes.forEach(function (box) {
                boxMaxHeight = box.clientHeight > boxMaxHeight ? box.clientHeight : boxMaxHeight;
                boxesHeight += box.clientHeight;
            });
 
            container.style.height = (boxesHeight * ratio) + 'px';
 
            // jeśli elementy ułożyły się tak, że nie mieszczą się w kontenerze
            // powodując widoczny scrollbar to zwiększ wysokość kontenera poprzez
            // powiększenie wysokości kontenera elementów o wysokość najwyższego elementu.
            if (container.offsetWidth < container.scrollWidth) {
                container.style.height = (boxesHeight * ratio) + boxMaxHeight + 'px';
            }
        };
 
    // uruchamiamy pomiar wysokości w dwóch sytuacjach:
    // - po załadowaniu wszystkich elementów na stronie,
    // - po zmianie wielkości okna przeglądarki
    window.addEventListener('load', setFlexboxHeight);
    window.addEventListener('resize', setFlexboxHeight);
})();

Dokładny opis kodu został zawarty w komentarzach do kodu, dlatego warto sobie go przejrzeć.

Powyższy kod przelicza wysokość kontenera elementów w dwóch momentach:

  • Po załadowaniu wszystkich elementów na stronie,
  • Oraz po zmianie wielkości okna przeglądarki.

Wysokość kontenera jest sumą wysokości elementów w środku. Jeśli po podzieleniu wysokości na pół okaże się, że elementy w środku się nie mieszczą w kontenerze i wychodzą z niego, to do wysokości kontenera - .container, doliczam wysokość najwyższego elementu z klasą .box. Dzięki temu rozwiązanie działa prawie w 100% przypadków.

Podsumowanie

Mam nadzieję, że zaprezentowane rozwiązanie będzie przydatne w Twoim projekcie. Od siebie dodam, że mając taki układ 2-kolumnowy jesteśmy w stanie utworzyć bardzo ciekawą galerię zdjęć bądź projektów zrealizowanych przez nas. Zapraszam do korzystania i komentowania rozwiązania.

  • Patrząc na to ile kodu ma Isotope czy Masonry to aż się nie chce wierzyć, że tak prosta wersja działa poprawnie 😀

    BTW do zdarzeń dorzuciłbym jeszcze orientationchange, bo wówczas rozmiar okna też się zmienia a AFAIR nie odpala się zdarzenie resize.

  • To nie problem, aby dodać obsługę tego typu zdarzenia 🙂

  • SpeX

    A demo?

  • Demo już jest dostępne. Zapraszam do testowania 🙂

  • SpeX

    Z tego co widzę, to przydał by się pewien parametr min, by w pewnym momencie układ 2 kolumnowy przeistoczył się w 1.

  • To jest kwestia rozszerzenia funkcjonalności i to nie było celem tego artykułu.