Wskazówka: Wyrównywanie wysokości kolumn na 100% strony


28 lipca 2014 / Michał Kortas


Wyrównywanie wysokości kolumn Jakiś czas temu projektowałem szablon zaplecza administracyjnego, składającego się z dwóch i trzech kolumn. Ze względów czysto wizualnych oraz zaleceń klienta, kolumny te miały być w każdym możliwym przypadku wyrównane względem siebie, wypełniając 100% wysokości witryny. Problem ten rozwiązałem w dość prosty sposób, wykorzystując pseudo-element  :before , który w tym krótkim przykładzie Wam zaprezentuję.

Struktura HTML

Przygotujmy sobie plik HTML witryny z trzema kolumnami. Po prawej i lewej stronie sidebary na menu i różnego rodzaju widżety, a po środku miejsce na treść.

Podstawowe style CSS

Kolumny należy teraz odpowiednio ostylować. Zrobimy to za pomocą poniższego kodu.

OK, efekt jest zbliżony do takiej wizualizacji:

Brak wyrównania

Kolumny dostosowują się do treści, co w przypadku, kiedy jest ona nierównomierna, wygląda mało estetycznie. Za moment jednak sobie z tym poradzimy, wykorzystując pseudo-element :before z CSS.

Pseudo-element :before

Dla każdej kolumny stworzymy osobną „wyimaginowaną” warstwę, która znajdować się będzie wizualnie pod spodem każdej z nich. Wykorzystując pozycjonowanie absolutne, umiejscowimy je w odpowiednich miejscach witryny, nadając im kolor i szerokość identyczne z kolumnami-matkami. Myślę, że następujący kod wszystko Wam wyjaśni.

Dzięki z-index: -1; osiągnęliśmy wspomniane już umiejscowienie dopełnienia poniżej warstw właściwych. Należy pamiętać, że jeśli pierwszy sidebar jest szeroki na 20%, to kolejna kolumna (w tym przypadku to .content) powinna być przesunięta w lewo właśnie o te 20%. Kolumna trzecia przesuwana jest natomiast o sumę szerokości kolumny pierwszej i drugiej. Obecny schemat kolumn powinien być od tej pory taki jak poniżej.

Kolumny wysokie na 100%

Podsumowanie

Nie jestem pewien, czy ten sposób jest najbardziej efektywny, ale na pewno rozwiązuję problem, z którym się borykałem. Jeżeli znacie inne rozwiązania, podzielcie się nimi w komentarzach pod wpisem.

Zobacz ten przykład online


Tagi:


54 odpowiedzi na “Wskazówka: Wyrównywanie wysokości kolumn na 100% strony”

  1. Comandeer pisze:

    Przekombinowane równo :D a wystarczyło użyć wyświetlania tabelkowego + odpowiednio ustawić height: http://bzdety.comandeer.pl/table.html

    • Jeden z najlepszych sposobów, ale prawdopodobnie nie zadziała z większością gridów.

      • Comandeer pisze:

        Jeśli grid nam przeszkadza, zamiast pomagać, to raczej coś jest nie tego ;) To raczej dopasowuje się narzędzie do rozwiązania a nie odwrotnie :P

      • Oczywiście tylko jest jedno ale. Przy złożonych realizacjach obok strony promocyjnej/informacyjnej, różnych lookbooków, katalogów etc. jest sklep i np. produkty muszą być wyrównane. Nie ma sensu zmieniać całego gridu (ja dla Gridle{scss} równie funkcjonalnej alternatywy nie widzę, ale to pomijam). Trochę się z tym już kopałem i finalnie najlepsze rozwiązania były w JS.

      • Comandeer pisze:

        Ok, ale jakie jest wgl szansa, że trzeba będzie łączyć grid z tego typu rozwiązaniem? Zwykle mamy do czynienia z dwiema, w porywach trzema kolumnami, które trzeba wyrównać w długości. W tym wypadku można se po prostu podarować grida ;) Można mieć też po prostu dodatkowy grid (tzn po prostu odziedziczony) – jeśli dla obsługi czegoś takiego należy zmienić cały grid, to IMO coś nie do końca jest z nim w porządku (z założenia takie komponenty powinny być elastyczne). Inna rzecz, że przy gridzie zwykle wystarczy samo height: 100%, bez tabelek (na moim działa bez problemu ;)).
        Jeśli jest to tylko estetyczny wybryk, to JS jak najbardziej. W innym wypadku szukałbym rozwiązania w CSS.

    • Hmmm…. Ale wydaje mi się, że zamiana elementu blokowego na display: table; jest nieelegancka. Po to mamy div, żeby był divem a nie zachowywał się jak tabela.

      • Comandeer pisze:

        Niby czemu? Zmieniasz mu semantykę? Nie. Zmieniasz mu jedynie sposób prezentacji a od tego jest przecież CSS. Czemu div wyświetlany jak tabelka Cię razi a tabelka wyświetlana jako div, żeby być responsive już nie? ;)

  2. Soanvig pisze:

    Należałoby zacząć od tego, że to wcale nie wyrównuje wysokości kolumn. Sama kolumna nadal ma taką wysokość, jaką miała. To tylko kładzie warstwę w takim samym kolorze pod tę kolumnę. A co jeśli kolumna ma tło obrazkowe repeat-y? Wtedy rozwiązanie do kosza.

    • To rozwiązanie ciągnie za sobą jednak problemy z wydajnością.

      • Soanvig pisze:

        overflow: hidden; nie jest renderowane, więc nie ma problemów z wydajnością. Tyle w temacie.

      • Comandeer pisze:

        Ermmm… jest. I w tym leży cały problem. To samo się zarzuca position: absolute i pozycjonowaniu poza ekran i dużym text-indent. Przeglądarki naprawdę tworzą te boxy (ah, te bezsensowne nieoptymalizacje).

        Inna rzecz, że to i tak jest to wydajniejsze od rozwiązania w JS, które wymaga zabawy ze zdarzeniami resize, orientationchange i przy każdej zmianie wysokości strony (a może się zmienić od wszystkiego ;)). Nie użyjemy debounce’a i już mamy problem.

      • Soanvig pisze:

        No jaha, okej, ale w założeniu przecież to, co jest poza widocznością nie zawiera żadnej treści. Pomijając, że to, co nie jest widoczne, nie jest wyrenderowane. Co najwyżej siedzi w pamięci i jeśli wykonuje jakieś akcje, to obciąża procesor, ale nie jest wyrenderowane (i przeglądarka nie musi tego renderować, mam na myśli oczywiście obraz sam w sobie, bo to jest rendering). No więc jeśli nie zawiera żadnej treści, to jedyne co może zawierać to puste tło. A dla przykładu podam pewnie znaną wam stronę:
        http://worlds-highest-website.com/pl/
        Ma 19 kilometrów, a nie 30k pikseli – a problemów z wydajnością nie ma.

      • Comandeer pisze:

        Otworzyłeś na jakimś mobilnym? ;)

      • Soanvig pisze:

        30k pikseli to nawet nie jest 1/1000 19 kilometrów, więc nie przesadzaj. Ja uważam, że jeśli to ma stwarzać problem (bo mówimy tu o problemie, a nie o samym obniżeniu wydajności), to większy problem będzie stwarzać transition na :hover.

      • Tak jakby ktoś chciał przeliczać px na km to niech nawet nie zaczyna… 1 px nie ma rozmiaru w mm. Można określić np. ppi, ale to już zależy od matrycy.

      • Soanvig pisze:

        To sobie przelicz, jaką byś musiał mieć matrycę, żeby 30k pikseli to było 19 metrów (1/1000*19km). Nawet najnowsza Retina nie będzie miała takiego wyniku. Tak czy siak, to wszystko to odbieganie od tematu, że 30k pikseli pustego obrazu (i to nie rastrowego obrazka, gdzie faktycznie każdy piksel coś waży w pamięci) nigdy w życiu nie wpłynie zauważalnie na wydajność. Tak jakby ktoś chciał pisać tutorial na wydajne rozwiązanie, używając JS albo N pseudoelementów, gdzie N to liczba kolumn.

      • > Nawet najnowsza Retina nie będzie miała takiego wyniku.
        Chyba nie rozumiesz. Im większe ppi tym mniej „kilometrów”. Retina będzie miała najmniej (tzn, że plamka jest mniejsza, a to jest na plus), a stare Nokie najwięcej.

      • Soanvig pisze:

        Czegoś się Pan tak przyczepił do tych kilometrów. Dobrze, pomyliłem się, pisząc z rozpędu. Zamiast podawać argumenty, że 30k pikseli spowoduje problem (powtarzam: problem) w postaci wydajności, to Ty właśnie debatujesz nad tym, czy 30k pikseli będzie, czy nie będzie miało 19 metrów.

  3. Tomasz Piątek pisze:

    We foundation jest data equalizer, w zwykłym kodzie height: 100% bez problemu zadziała :)

    • Comandeer pisze:

      To rozwiązanie ma 2 poważne wady:
      1) wymusza użycie Foundation albo co najmniej jego części
      2) jest zależne od JS – a podstawowy wygląd nigdy nie powinien być od niego zależny

      • Mr.Mr pisze:

        Pytanie czy takie coś jak wyrównanie kolumn w pionie (wyrównanie wysokości) to element 'podstawowego wygladu’? Jak zdefiniować to co jest podstawowym wyglądem a co nie? W przypadku zastosowania JS do rozwiązania tego problemu, problem może być tylko taki, że user wyłączył/nie ma JS i zobaczy kolumny w różnych wysokościach…

        Na marginesie – nie pochwalam wykorzystania całego Foundation tylko po to żeby wyrównać kolumny, chodzi mi o sam fakt stosowania JS tak jak tutaj np.: http://css-tricks.com/equal-height-blocks-in-rows/

      • Comandeer pisze:

        >Jak zdefiniować to co jest podstawowym wyglądem a co nie?
        Jeśli dana strona się rozpadnie albo będzie działać źle, to był to podstawowy wygląd ;) Jeśli bierzemy równe kolumny jedynie za myk estetyczny, to ok – można iść w JS. Pytanie tylko czy warto, skoro rozwiązania w CSS są proste i de facto działają wszędzie (bo nawet w IE8). No i takie wykorzystywanie JS jednak trochę mu po prostu urąga ;)

      • Tomasz Piątek pisze:

        Dla robiąc layout panelu administracyjnego warto opierać się na jakimś frameworku css więc na pewne wtedy nie wykorzystujemy tylko jednej rzeczy. Dodatkowo – można załadować tylko to co potrzeba :> Niemniej to ostateczność, zawsze najpierw staram się w css.

  4. Equalizer z Foundation daje radę pod warunkiem, że używamy go do prostych rzeczy (bez dynamicznej treści) i używamy Foundation. W moim przypadku ostatnio nie spełniają się oba warunki, więc napisałem własny, bardziej elastyczny kod jako dodatek do jQuery.

    Pisany był na szybko, więc jak ktoś znajdzie sposób na ulepszenie to niech da znać. Gwiazdki/stars miło widziane :)

    Demo na codepen.io: http://codepen.io/MichalRazorZalecki/pen/flHqw
    Gist: https://gist.github.com/MichalRazorZalecki/9b4a79fdba85edf14a53

    • Comandeer pisze:

      Plus za debounce (ale czemu tak dziwnie rozwiązany przez customowe zdarzenie?)
      Duży minus za niezłapanie wszystkich przypadków zmiany wielkości strony:
      1) user zmienia rozmiar ręcznie – jedyny przypadek złapany
      2) user zmienia orientację urządzenia i automatycznie następuje zmiana rozmiarów ekranu
      3) załadował się marudzący obrazek
      4) lazy load
      5) spoilers
      6) 800 innych przypadków, których nie mam teraz w głowie ;)
      Taki scrollspy od Bootstrapa przelicza offsety przy scrollu, ale tutaj i tak to jest niewystarczające (layout rozpadnie się i złoży dopiero po scrollu, co jest niedopuszczalne). Jedynym sensownym rozwiązaniem zostaje po prostu timer (a raczej rAF, żeby nie zarżnąć browsera). I tak wiąże się to z niesamowicie częstym repaintem, którego w CSS da się uniknąć bardzo prosto.

      btw this.resizeTimer brudzi global scope – po co?

      • > czemu tak dziwnie rozwiązany przez customowe zdarzenie?
        Performance! + tak mi się spodobało :)
        > user zmienia rozmiar ręcznie
        Tutaj to już popłynąłeś.
        > this.resizeTimer brudzi global scope
        Nie ma(?) idealnego rozwiązania. Taki boli mnie najmniej.
        > 800 innych przypadków
        I po to mam dostępną makeEqual. Badam psss! Ustawię sobie wszystko pod siebie, nie zawsze potrzeba RWD (+ nie wszyscy widzą potrzebę mieć RWD, albo strona pod mobilne to m.********), więc zmiana orientacji nie jest zawsze potrzebna, resize jest uniwersalne.
        > załadował się marudzący obrazek, lazy load, spoilers
        hm? Tak czy siak mam makeEqual().

      • Comandeer pisze:

        >Performance!
        Na pewno nie. Rzucenie dodatkowego zdarzenia zamiast czystego debounce’a na pewno nie jest szybsze (sama normalizacja zdarzenia przez jQuery zabija tę optymalizację).

        >Nie ma(?) idealnego rozwiązania.
        IIFE

        >hm?
        No przecież obrazki również zmieniają wysokość strony (zwłaszcza te RWD, nie mogące mieć wszak narzuconych z góry rozmiarów). Kiedy taki obrazek się wczyta, wielkość strony automatycznie się zmienia, ale resize event nie odpala się. To samo stanie się w przypadku lazy load, więc przy obrazkach Twój sposób zupełnie nie wypali. Rozwinięcie „natywnego spoilera” (details) nie odpala resize. W sumie mało co odpala resize, więc to tak naprawdę rozwiązanie pod bardzo konkretny przypadek ;) W tym wypadku naprawdę pozostaje timer a to z góry przekreśla dużą wydajność (rAF może ją zapewnić, ale w tym wypadku mamy bardzo ograniczone wsparcie: stare IE odpadają w przedbiegach)

      • > Rzucenie dodatkowego zdarzenia zamiast czystego debounce’a na pewno nie jest szybsze.
        Złoty środek pomiędzy wygodą. Obciążenie jest i tak na tyle małe, że nie ma sensu imo tracić na to czasu. To i tak jest mało znaczący punkt całego rozwiązania. Z założenia ma zadziałać:
        A) Po załadowaniu
        B) W innych przypadkach gdy tego chcę.
        Raki był „brief” i to zostało osiągnięte.

        Gdy się wczyta, zawsze można wywołać makeEqual i to załatwia sprawę.

      • Comandeer pisze:

        >Złoty środek pomiędzy wygodą.
        Czy ja wiem? W tym momencie masz rozbitą obsługę jednego zdarzenia na 2 różne miejsca w kodzie. Resizeend nie jest natywne i uwierz mi, że mało kto będzie jego definicji szukać w handlerze zdarzenia resize.

        >Gdy się wczyta, zawsze można wywołać makeEqual i to załatwia sprawę.
        Strona czy obrazek? Jeśli to drugie, to ustalenie czy obrazek na pewno się wczytał wcale nie jest takie proste (zwykłe load lubi oszukiwać).

      • Przede wszystkim to nie jest kompleksowa obsługa layout, a funkcja obliczenia i nadania odpowiednich, równych wysokości. Cały czas można coś poprawić, ale idąc tym tropem to całe życie bym refaktoryzował głupią funkcję, więc nie dajmy się zwariować :)

        Jeżeli znasz jakieś gotowe rozwiązania, które spełniają w/w zadania to chętnie poznam.

      • Comandeer pisze:

        >Jeżeli znasz jakieś gotowe rozwiązania to chętnie poznam.
        Tak, CSS ;) Nie używam JS do layoutu; jedynym wyjątkiem są aplikacje JS-only i to naprawdę heavy JS, gdzie cały layout i tak jest układany przy pomocy custom elements.
        Użycie height: 100%/ display: table-cell w 98% przypadków nie nastręcza większych trudności.

        Osobiście bym zrobił z tego IINFE i odpalał rekurencyjnie rAF, gdybym już musiał coś takiego zrobić (analogicznie do display systemu w grach/wysokowydajnościowych aplikacjach)

      • Od kiedy rekurencyjnie to wydajnie? :P

        btw. dla mnie RAF to lotnictwo UK, więc jakbyś mógł jaśniej to by było lepiej

      • Comandeer pisze:

        >Od kiedy odpalał rekurencyja jest wydajna? :P
        To zależy co odpalasz. Chyba nikt nie rzuca się o to, że setTimeout odpalający kolejny setTimeout jest niewydajne ;) Wszystko tak czy siak leci asyncem.

        rAF, nie RAF :P requestAnimationFrame, czyli podstawa Animation API, zarówno w CSS, jak i JS

      • Dobrze, tylko requestAnimationFrame zabije wydajność. U mnie obliczenia zostaną wykonane raz, a u Ciebie nawet do 60 razy na sek (przy monitorze 60Hz).

      • Comandeer pisze:

        No właśnie nie – rAF wywoływany jest wtedy, gdy przeglądarka uzna to za stosowne. To nie zabija wydajności, tylko pozwala na płynną animację. setTimeout zabije wydajność, rAF nie powinien.

        Poza tym Twoje rozwiązanie nie działa z obrazkami i wymaga dużych zmian, żeby zacząć… Moje, w wypadku gdy repaint ograniczy się tylko i wyłącznie do chwil, gdy zmieniła się wydajność, rozwiązywałoby ten problem i nie powodowało dużego narzutu.

        Poza tym – jeśli rAF pozwala na płynną animację zawartości canvas, to do określenia równych kolumn jest wystaraczające

    • Comandeer pisze:

      https://gist.github.com/Comandeer/f3d8dc03096b9eb450ab – całkowicie bezobsługowa wersja rAF-owa bez jQuery i z IINFE ;) Oczywiście rozwiązuje problem z artykułu, ale jego odpowiednie przerobienie tak, żeby zamiast dokumentu brał inny container, to kwestia 5 minut (ba, wystarczy podstawić inny element za 1. parametr!)

      • To rozwiązanie w ogóle nie spełnia moich założeń. Ma podobny efekt, ale dopasowuje się do rodzica. Nie zadziała gdy będę chciał mieć „kafelki” w kilku wierszach, a to podstawowa funkcjonalność przy budowie np. sklepów. Te, które podałem jest bardziej elastyczne.

      • Comandeer pisze:

        Problem w tym, że nigdzie nie opisałeś dokładnie swoich założeń… jak już pisałem, moje rozwiązanie obsługuje problem z tematu. Dorobienie do tego sprawdzania wysokości boxów a nie samego kontenera jest kwestią kilku minut a problem obrazków wciąż jest lepiej rozwiązany niż u Ciebie.
        Poza tym jeśli potrzebujesz kafelek w kilku rzędach, nie widzę sensu w rozwiązaniu opartym na JS. tutaj idealnie pasuje css grid/wyświetlanie tabelkowe i rozwiązanie w js zawsze będzie obskurnym, mniej wydajnym hackiem. To używanie js do tego, do czego nie został zaprojektowany. To nie czasy ie7, żeby stosować takie hacki…

      • > problem obrazków wciąż jest lepiej rozwiązany niż u Ciebie
        Nie rozumiemy się. Ja nie mam czegoś takiego jak problem obrazków. Ty stworzyłeś taki problem. Ja mam funkcję, która ustawia odpowiednia wysokość elementów. Jak chcę to zawsze mogę zrobić

        requestAnimationFrame(function() {
        $boxes.makeEqual();
        });

        I mam pozamiatane. To jest właśnie elastyczność.

      • Comandeer pisze:

        Eh, w sklepie nie masz obrazków? Poza tym elastyczne = przewidujące. Inaczej jest rozwiązaniem profilowanym.
        Nie widzę w tym „elastyczności”. To w dalszym ciągu hack w js. Dla mnie takie rozwiązanie jest gorsze niźli analogiczne w CSS. Zawsze będzie powodować więcej repaintów niż to konieczne. Nie rozumiem czemu tak bardzo unikasz rozwiązania w css.

      • [] [] [] []
        [] [] [] []
        [] [] []

        Pokaż jak to wyrównać w CSS.

      • Comandeer pisze:

        http://codepen.io/imohkay/pen/gpard – proszę bardzo ;) Pewnie jeszcze fajniej rozwiązuje to CSS Grids.
        I zanim zaczniesz marudzić, że nie wszystko obsługuje flexbox: a kto nie obsługuje, oprócz starych IE? Nie mam zamiaru dostosowywać strony do mniejszości. Jeśli mówimy o estetycznym smaczku można se go podarować… Albo zastosować komentarze warunkowe dla IE lub – jeszcze lepiej – feature detection (wystarczy stworzyć bogus div, dać mu display: flex i zobaczyć czy ma taką wartość) i wczytanie fallbacku (np. przez słynną funkcję loadCSS od Filament Group czy też has.js/yepnope). W tym momencie wszędzie tam, gdzie flexbox jest obsługiwany nie ma żadnych problemów z JS (odpowiedni moduł nawet się nie zassie) a stare browsery dostaną polyfilla. Sytuacja win-win, gdzie natywny mechanizm działa tam, gdzie to możliwe a reszta dostaje ekwiwalent w JS.

      • > Nie mam zamiaru dostosowywać strony do mniejszości.
        W praktyce działa to tak, że to nie dev podejmuje decyzje biznesowe. Niemniej jednak Karen Menezes się napracowała tworząc tego pena.

      • Comandeer pisze:

        >W praktyce działa to tak, że to nie dev podejmuje decyzje biznesowe.
        Jeśli decyzje biznesowe podejmuje „boss” bez udziału devów i każe mu robić shit, bo „działa”, to sorry, ale raczej nie tak powinna wyglądać współpraca. A takie działanie pomoże w przyszłości łatwiej utrzymać całą stronę (bo np zostanie 1% klientów nieobsługujących flexboxa i po prostu z biznesowego punktu widzenia nie opłaca się ich wspierać). Dialog to podstawa współpracy. Dlatego wolę być swoim własnym szefem, bo wówczas jeśli decyzja biznesowa była błędna, to mogę zwolnić jedynie siebie samego… i znaleźć kolejne ja do współpracy ;)

        > Niemniej jednak Karen Menezes się napracowała tworząc tego pena.
        Nie umniejszając Karen, nie jest to aż tak trudne. Flexbox to chyba najlepiej przemyślana rzecz z CSS3 i to po prostu widać ;) Osobiście gridy trzymam już tylko w nim, z małym fallbackiem dla IE.

  5. pikej pisze:

    a może padding-bottom: 10000px; margin-bottom: -10000px; dla każdej kolumny i overflow: hidden dla kontenera?

  6. hej może coś nie skumałem ale dlaczego nie chciałeś użyć atrybutu min-height i pobawić się wartościami np: {min-height:600px} ?

    ja tak na szybkiego zrobiłem …i jakoś ok ;)

    Kolumny

    #pierwszy{width:30%;

    min-height:600px;

    background-color:blue;

    float:left;}

    #drugi{width:30%;

    min-height:600px;

    background-color:red;

    float:left;}

    #trzeci{width:30%;

    min-height:600px;

    background-color:yellow;

    float:left;}

  7. Akinori pisze:

    Wiem, że temat przestarzały ale może komuś się przydać i jak tu wpadnie to najprościej użyć display:table-cell i po problemie, nie wiem czemu o tym nikt tutaj nie wspomniał, bo z tego co mi się wydaje opcja ta jest dostępna już od dawna :)

Skomentuj pikej Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany.