Заранее оговорюсь, что чистым CSS здесь, к сожалению, обойтись не получится: Firefox и IE8+ слишком точно (да-да, в данном случае это плохо) производят вычисления ширины блоков. Однако для этих браузеров дописывается нехитрый скриптик в пару строк, если таки надо достичь идеала, хоть это и портит всю прелесть.
Для тех, кому лень читать всё, ссылка на окончательный вариант: jsfiddle.net/XeFTr/11
Суть в том, что иногда необходимо отобразить на странице резиновый блок с несколькими дочерними элементами, причем потомок должен быть либо виден целиком, либо не виден вообще. Самый простой пример — лента каких-нибудь картинок с прокруткой и стрелочками по бокам. Очень некрасиво смотрится выглядывающее из-под «overflow: hidden» изображение, обрезаннное сбоку. Выход — заставить ширину обёртки всегда быть кратной скольки-то пикселям.
Разумеется, если ширина потомков разнится от элемента к элементу, то этот способ не имеет смысла.
Принцип работы заключается в том, что большинство браузеров вычисляют ширину потомка, если она задана в процентах, исходя из ширины родителя. Таким образом, если у элемента ширина 1000%, то при изменении ширины его родителя на пиксель сам элемент растянется или сожмётся на 10 пикселей. Собственно, всё.
Эту штуку описывал в лебедевском скринкасте Сергей Чикуёнок еще в 2008 году, в контексте исправления бага с «прыгающими» блоками в IE6. Однако я, к своему удивлению, так и не нашел описания этой презабавной технологии на Хабре.
Я буду рассматривать сферический элемент в вакууме, изначально состоящий исключительно из блока с «overflow: hidden» и очень широкого элемента с кучкой потомков, суммарная ширина которых будет превышать ширину обёртки:
Живой пример: jsfiddle.net/KKSCe/4
Итак, у нас есть список элементов, ширина каждого (включая margin) — 150px. Очевидно, что последний элемент списка будет виден целиком в одном случае из 150. Хорошо, в 11 случаях: можно оставить за пределами видимости пятипиксельный правый margin и принять, что левый маргин является частью предыдущего элемента. :-)
Из описания выше понятно, что надо добавить обёртку блоку «.wrapper», а самому «.wrapper» задать ширину в 150 раз большую, нежели ширина этой обёртки. Так же ясно, что если написать «width: 15000%» без дополнительных манипуляций, то результат будет несколько не тем, что требуется. Необходимо, чтобы у «.wrapper» сохранилась ширина в 60% от ширины экрана. Это компенсируется маленькой шириной обёртки, вычисляется по пропорции.
15000 / 100 = 60 / x
x = 0.4
Пишем так:
Живой пример (посмотрите в Chrome, что ли): jsfiddle.net/XeFTr/9
Можно порастягивать там область просмотра и посмотреть, как клёво всё работает. Блок действительно принимает значения ширины, кратные 150px.
Казалось бы, отлично!
Проблема заключается в том, что в части случаев, как и в данном примере, приходится задавать ширину обёртки менее одного процента — когда шаг должен быть более 100px. Большинство браузеров это проглатывают и считают всё правильно. Даже IE6. А вот с Оперой, в том числе последних версий, — беда. Опера не понимает значения меньше 1%.
Решение довольно простое, но требущее большего количества HTML. Вместо того, чтобы делать один блок с шириной 0.4%, сделаем два. Ширина первого — 1%, ширина второго, вложенного в первый, — 40%. Тут и Опера становится в ряды согласных.
Живой пример: jsfiddle.net/XeFTr/8
Вполне возможно, что использование значения менее 1% черевато боком и в других браузерах, однако я этого не встречал. Поправьте меня, если я ошибаюсь.
Впрочем, в любом случае, дробное процентное значение — это не особенно хорошо.
Итак, теперь всё работает везде, кроме Firefox и IE последних версий. Исправим и это.
Для простоты я буду использовать jQuery. Функция, как и разметка, тоже для лабораторных условий.
Окончательный вариант: jsfiddle.net/XeFTr/11
В более приближенных к жизни примерах присутствуют всевозможные стрелки, рамки и тому подобное. Их позиционирование (в частности, позиционирование элементов с той стороны, куда растягивается блок) делается точно тем же способом.
Проверял работоспособность в Firefox, Opera, Chrome, IE6-9 и Safari под Windows.
Было бы здорово посмотреть на это всё под разнообразными Safari под МакОСЬю, Konqueror и т. п.
Также буду рад замечаниям/исправлениям.
В основе написанного лежит упомянутый выше скринкаст и последующие самостоятельные изыскания и эксперименты на эту тему.
Для тех, кому лень читать всё, ссылка на окончательный вариант: jsfiddle.net/XeFTr/11
В чем суть?
Суть в том, что иногда необходимо отобразить на странице резиновый блок с несколькими дочерними элементами, причем потомок должен быть либо виден целиком, либо не виден вообще. Самый простой пример — лента каких-нибудь картинок с прокруткой и стрелочками по бокам. Очень некрасиво смотрится выглядывающее из-под «overflow: hidden» изображение, обрезаннное сбоку. Выход — заставить ширину обёртки всегда быть кратной скольки-то пикселям.
Разумеется, если ширина потомков разнится от элемента к элементу, то этот способ не имеет смысла.
Как это работает?
Принцип работы заключается в том, что большинство браузеров вычисляют ширину потомка, если она задана в процентах, исходя из ширины родителя. Таким образом, если у элемента ширина 1000%, то при изменении ширины его родителя на пиксель сам элемент растянется или сожмётся на 10 пикселей. Собственно, всё.
Эту штуку описывал в лебедевском скринкасте Сергей Чикуёнок еще в 2008 году, в контексте исправления бага с «прыгающими» блоками в IE6. Однако я, к своему удивлению, так и не нашел описания этой презабавной технологии на Хабре.
Как это написать?
Я буду рассматривать сферический элемент в вакууме, изначально состоящий исключительно из блока с «overflow: hidden» и очень широкого элемента с кучкой потомков, суммарная ширина которых будет превышать ширину обёртки:
<div class="wrapper">
<ul class="itemList">
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
<li class="item"></li>
</ul>
</div>
.wrapper {
border: 1px solid black;
margin: 10px;
overflow: hidden;
width: 60%; /* пусть лента будет чуть шире половины экрана */
}
.itemList {
height: 90px;
list-style: none;
margin: 0;
padding: 0;
width: 10000%; /* просто чтобы всё было в одну строку */
}
.item {
background: green; /* картинок не будет, будет цвет блоков */
display: inline; /* ну да, грязный хак для IE6 */
float: left;
height: 80px;
margin: 5px;
padding: 0;
width: 140px;
}
Живой пример: jsfiddle.net/KKSCe/4
Итак, у нас есть список элементов, ширина каждого (включая margin) — 150px. Очевидно, что последний элемент списка будет виден целиком в одном случае из 150. Хорошо, в 11 случаях: можно оставить за пределами видимости пятипиксельный правый margin и принять, что левый маргин является частью предыдущего элемента. :-)
Из описания выше понятно, что надо добавить обёртку блоку «.wrapper», а самому «.wrapper» задать ширину в 150 раз большую, нежели ширина этой обёртки. Так же ясно, что если написать «width: 15000%» без дополнительных манипуляций, то результат будет несколько не тем, что требуется. Необходимо, чтобы у «.wrapper» сохранилась ширина в 60% от ширины экрана. Это компенсируется маленькой шириной обёртки, вычисляется по пропорции.
15000 / 100 = 60 / x
x = 0.4
Пишем так:
<div class="pre-wrapper">
<div class="wrapper">......</div>
</div>
.pre-wrapper {
margin: 10px;
width: 0.4%;
}
.wrapper {
border: 1px solid black;
overflow: hidden;
width: 15000%;
}
Живой пример (посмотрите в Chrome, что ли): jsfiddle.net/XeFTr/9
Можно порастягивать там область просмотра и посмотреть, как клёво всё работает. Блок действительно принимает значения ширины, кратные 150px.
Казалось бы, отлично!
Но всё не так гладко
Проблема заключается в том, что в части случаев, как и в данном примере, приходится задавать ширину обёртки менее одного процента — когда шаг должен быть более 100px. Большинство браузеров это проглатывают и считают всё правильно. Даже IE6. А вот с Оперой, в том числе последних версий, — беда. Опера не понимает значения меньше 1%.
Решение довольно простое, но требущее большего количества HTML. Вместо того, чтобы делать один блок с шириной 0.4%, сделаем два. Ширина первого — 1%, ширина второго, вложенного в первый, — 40%. Тут и Опера становится в ряды согласных.
<div class="pre-wrapper">
<div class="opera-fix">
<div class="wrapper">......</div>
</div>
</div>
.pre-wrapper {
margin: 10px;
width: 1%;
}
.opera-fix {
width: 40%;
}
Живой пример: jsfiddle.net/XeFTr/8
Вполне возможно, что использование значения менее 1% черевато боком и в других браузерах, однако я этого не встречал. Поправьте меня, если я ошибаюсь.
Впрочем, в любом случае, дробное процентное значение — это не особенно хорошо.
Итак, теперь всё работает везде, кроме Firefox и IE последних версий. Исправим и это.
Скрипт для чрезмерно точных браузеров
Для простоты я буду использовать jQuery. Функция, как и разметка, тоже для лабораторных условий.
$(function() {
if ( $.browser.mozilla || ($.browser.msie && $.browser.version >= 8) ) {
resizeFix();
$(window).resize(resizeFix);
}
});
function resizeFix() {
var w,
h = $('.wrapper'),
i = $('.item').outerWidth() + parseInt($('.item').css('marginLeft')) * 2;
h.width('');
w = Math.floor(h.width() / i) * i;
h.width(w + 'px');
}
Окончательный вариант: jsfiddle.net/XeFTr/11
Резюмирую
В более приближенных к жизни примерах присутствуют всевозможные стрелки, рамки и тому подобное. Их позиционирование (в частности, позиционирование элементов с той стороны, куда растягивается блок) делается точно тем же способом.
Проверял работоспособность в Firefox, Opera, Chrome, IE6-9 и Safari под Windows.
Было бы здорово посмотреть на это всё под разнообразными Safari под МакОСЬю, Konqueror и т. п.
Также буду рад замечаниям/исправлениям.
В основе написанного лежит упомянутый выше скринкаст и последующие самостоятельные изыскания и эксперименты на эту тему.