Чуйні блоки однакової висоти на основі Flexbox

14

Від автора: Після публікації поста про те, як я створив чуйні блоки однакової висоти на сайті Readerrr, я отримав кілька корисних відгуків від спільноти веб-розробників. Деніел Стерм (Daniel Sturm) запропонував мені використовувати модуль Flexbox з CSS3 замість JavaScript, а Вирли Пітерс (Veerle Pieters) залишив твіт «… ви можете зробити це за допомогою Flexbox, а JavaScript використовувати для підстраховки». Точно! І чому я сам про це не додумався?! Я читав до цього кілька статей про Flexbox, але сам ніколи його не застосовував, тому він зовсім вилетів у мене з голови.

Чому Flexbox? Якщо коротко, то модуль Flexbox Layout був створений для вирішення саме таких завдань. Він є ефективним і гнучким інструментом для управління, можливо, усіма різновидами макетів. Завдяки йому практично не виникає затримки в часі між неправильною і правильним відображенням зовнішнього вигляду макета. При використанні рішення на JavaScript витрачається час на завантаження документа, потім на завантаження відповідного JS-файлу і, якщо такі є, на завантаження зображень в блоках. Рішення з Flexbox спрацьовує миттєво, а рішенням на JavaScript потрібні секунди. Але навіть у цьому випадку, рішення на JavaScript чудово підійде для людей, що використовують старі версії браузерів, які не підтримують Flexbox.

Проблема

Якщо ви не читали мій попередній пост, то вам це і не потрібно робити. Ось власне код і проблема (макет, який виглядає несправним), яку потрібно вирішити:

.list
{
overflow: hidden; /* скасовуємо обтікання */
}
.list__item
{
width: 25%; /* 4 елемента на одному рядку */
float: left;
}

Чуйні блоки однакової висоти на основі Flexbox

Рішення

Якщо ви ніколи раніше не стикалися з Flexbox, то ви будете здивовані, наскільки він приголомшливий. Властивість display: flex активує сам Flexbox для контейнера і властивість flex-wrap: wrap говорить про те, що потрібно обернути дочірні елементи, а немає вмістити їх в одному рядку. Повторне написання властивості display: flex для дочірніх елементів гарантує однакову висоту елементів в рядках.

.list
{
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.list__item
{
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}

Чуйні блоки однакової висоти на основі Flexbox

Це рішення відмінно працює в останніх версіях браузерів Chrome, Opera, Safari, Opera, Firefox і Internet Explorer 10+. Для всіх інших браузерів у мене є «ліки» на JavaScript.

Я не включив це в попередній CSS-код, але деякі старі версії браузерів на движку WebKit підтримують застарілий синтаксис для Flexbox (display: -webkit-box). Однак, властивість -webkit-box-lines: multiple просто не працює ні в браузері iOS Safari 6.1-, ні в Android 4.3-.

Запасний варіант на JavaScript

Тут я розглядаю альтернативне рішення для таких браузерів, Internet Explorer 9-, Android 4.3-, iOS Safari 6.1-, and Opera Mini. Я написав крихітний шматочок коду на jQuery, який:

Визначає браузер не підтримує Flexbox;

Обчислює число елементів на одному рядку, розділивши для цього значення ширини у елементів .list .list__item;

Фактично ділить список рядків у відповідності з цим числом;

Знаходить елемент з найбільшим значенням висоти в кожному рядку;

Встановлює відповідно таке ж значення висоти для всіх інших елементів у кожному рядку.

;( function( $, window, document, undefined )
{
‘use strict’;
var s = document.body || document.documentElement, s = s.style;
if( s.webkitFlexWrap == » || s.msFlexWrap == » || s.flexWrap == » ) return true;
var $list = $( ‘.list’ ),
$items = $list.find( ‘.list__item’ ),
setHeights = function()
{
$items.css( ‘height’, ‘auto’ );
var perRow = Math.floor( $list.width() / $items.width() );
if( perRow == null || perRow < 2 ) return true;
for( var i = 0, j = $items.length; i maxHeight ) maxHeight = itemHeight;
});
$row.css( ‘height’, maxHeight );
}
};
setHeights();
$( window ).on( ‘resize’, setHeights );
$list.find( ‘img’ ).on( ‘load’, setHeights );
})( jQuery, window, document );

А що якщо в браузері вимкнено JavaScript? Проблема полягає в тому, що вбудований механізм CSS для розпізнавання можливостей має більш слабку підтримку, ніж сам модуль Flexbox. Таким чином, використання CSS правила @support не підійде для визначення всіх браузерів, що підтримують Flexbox. Але це краще, ніж нічого.

Я пропоную міркувати таким чином: вимкнено JavaScript = немає підтримки Flexbox (я вважаю, що дана рівність є практично вірним), а для винятків будемо використовувати @support. Насправді вам потрібно додати клас .no-js для тега html і видалити його за допомогою JavaScript. Саме так ми дізнаємося, вимкнено JavaScript, або нема. Потім додайте відповідні стилі для елементів списку і, нарешті, «компенсуйте» дане оформлення з допомогою правила @supports.

В даному випадку я вирішив уявити блоки у вигляді рядків, розтягнутих на всю ширину. Якщо в них будуть вставлені які-небудь зображення, то на великих екранах вони вирівнюються по правому краю.

(function(e,t,n){var r=e.querySelectorAll(«html»)[0];r.className=r.className.replace(/(^|\s)no-js(\s|$)/,»$1$2″)})(document,window,0);
html.no-js .list__item
{
width: 100%;
float: none;
}
html.no-js .list__item img
{
max-width: 9.375 rem; /* 150 */
float: right;
margin-left: 1.25 rem; /* 20 */
}
@supports ( display: -webkit-flex ) or ( display: -ms-flex ) or ( display: flex )
{
html.no-js .list__item
{
width: 25%;
float: left;
}
html.no-js .list__item img
{
max-width: none;
float: none;
margin-left: 0;
}
}

Чуйні блоки однакової висоти на основі Flexbox

Демо-приклад