От автора: После публикации поста о том, как я создал отзывчивые блоки одинаковой высоты на сайте Readerrr, я получил несколько полезных отзывов от сообщества веб-разработчиков. Дэниел Стёрм (Daniel Sturm) предложил мне использовать модуль Flexbox из CSS3 вместо JavaScript, а Вирли Питерс (Veerle Pieters) оставил твит «… вы можете сделать это с помощью Flexbox, а JavaScript использовать для подстраховки». Точно! И почему я сам об этом не додумался?! Я читал до этого несколько статей про Flexbox, но сам никогда его не применял, поэтому он совершенно вылетел у меня из головы.
Почему Flexbox? Если вкратце, то модуль Flexbox Layout был создан для решения именно таких задач. Он является эффективным и гибким инструментом для управления, возможно, всеми разновидностями макетов. Благодаря ему практически не возникает задержки по времени между неправильной и правильной отрисовкой внешнего вида макета. При использовании решения на JavaScript тратится время на загрузку документа, затем на загрузку соответствующего JS-файла и, если таковые имеются, на загрузку изображений в блоках. Решение с Flexbox срабатывает мгновенно, а решению на JavaScript потребуются секунды. Но даже в этом случае, решение на JavaScript прекрасно подойдет для людей, использующих старые версии браузеров, которые не поддерживают Flexbox.
Проблема
Если вы не читали мой предыдущий пост, то вам это и не нужно делать. Вот собственно код и проблема (макет, который выглядит неисправным), которую нужно решить:
1 2 3 4 5 |
<ul class="list"> <li class="list__item"><!-- контент --></li> <li class="list__item"><!-- контент --></li> <!-- остальные элементы --> </ul> |
1 2 3 4 5 6 7 8 9 |
.list { overflow: hidden; /* отменяем обтекание */ } .list__item { width: 25%; /* 4 элемента на одной строке */ float: left; } |
Решение
Если вы никогда раньше не сталкивались с Flexbox, то вы будете удивлены, насколько он потрясающий. Свойство display: flex активирует сам Flexbox для контейнера и свойство flex-wrap: wrap говорит о том, что нужно обернуть дочерние элементы, а нет уместить их на одной строке. Повторное написание свойства display: flex для дочерних элементов гарантирует одинаковую высоту элементов в строках.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.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; } |
Это решение отлично работает в последних версиях браузеров Chrome, Android, 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;
Фактически делит список на строки в соответствии с этим числом;
Находит элемент с наибольшим значением высоты в каждой строке;
Устанавливает соответственно такое же значение высоты для всех остальных элементов в каждой строке.
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 |
;( 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 < j; i += perRow ) { var maxHeight = 0, $row = $items.slice( i, i + perRow ); $row.each( function() { var itemHeight = parseInt( $( this ).outerHeight() ); if ( itemHeight > 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.
В данном случае я решил представить блоки в виде строк, растянутых на всю ширину. Если в них будут вставлены какие-нибудь изображения, то на больших экранах они выровняются по правому краю.
1 2 3 4 5 6 7 |
<html class="no-js"> <head> <!-- ваш код --> <script>(function(e,t,n){var r=e.querySelectorAll("html")[0];r.className=r.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")})(document,window,0);</script><!-- удалите этот код, если вы используете Modernizr --> </head> <!-- ваш код --> </html> |
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 |
html.no-js .list__item { width: 100%; float: none; } html.no-js .list__item img { max-width: 9.375rem; /* 150 */ float: right; margin-left: 1.25rem; /* 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; } } |
Автор: Osvaldas Valutis
Источник: //osvaldas.info/
Редакция: Команда webformyself.
Комментарии (2)