Xiper

Ресайз окна: потомок перерос родителя

Автор: Александр Головко Дата публикации:

потомок перерос родителя Взаимоотношения родителя и потомка не всегда складываются гладко. А уж если потомок внезапно перерос родителя, ситуация может выйти из под контроля. А если по-нашему — жди багов.

Рассмотрим случай, когда основной контентный блок оборачивается в дополнительный div, для того, чтобы реализовать фоновый рисунок (так иногда приходится делать, если фон сложный). Код при этом будет таким:

<body>
	<div class="wrap">
		<div class="main">
		
		</div>
	</div>
</body>
.wrap {
	background: url(images/grad.png) repeat-x;
}
.main {
	width: 980px;
	margin: 0 auto;
}

Проблема 1. Фон пропал!

Такой код несет в себе замаскированную мину! При беглом осмотре все хорошо и красиво — фон тянется на всю ширину, а контент уютно расположился по центру в отведенных ему 970 пикселях.

Бока вылазят, если попробовать уменьшить окно до появления горизонтальной полосы прокрутки, а потом взять да и прокрутить страничку. Оказывается в невидимой области окна (для доступа к которой нужно тянуть ползунок прокрутки) фон не отрисовывается! То есть фон распространяется на ширину окна браузера, а не всего содержимого.

часть фона обрезалась Упс! Куда же делся фон?

Кто не поверил скриншоту — можно посмотреть на живом примере.

Примечание: фон пропадает во всех популярных браузерах, за исключением IE6.

Теория

Получаем ситуацию, когда ширина потомка больше ширины родителя. Реальная ширина блока wrap равна ширине body, т.е. ширине окна браузера. Для main же ширина задана принудительно (970px). Как результат - main «торчит» из родителя. Соответственно фон wrap'а (как и сам wrap) визуально обрывается. В этом легко убедиться, написав wrap{overflow:hidden} — горизонтальная прокрутка исчезнет. Страница обрежется до ширины окна.

Это объясняет почему фон не пропадает в IE6. Ослик, согласно своим принципам, растянет родителя до ширины потомка. Другие браузеры так не делают.

Про соотношение ширины потомка и родителя можно подробнее почитать в статье Блочные элементы. Особые приметы.

Проблема 2. Отцентрированный фон уехал!

С первой проблемой тесно связанна вторая. Если фон не повторяется, как в предыдущем примере, а центрируется (вместо background: url(images/grad.png) repeat-x; пишем background:url(images/grad.png) center 0 no-repeat;), то при уменьшении размера окна он «съезжает» влево. То есть даже видимая часть фона не соответствует нашим ожиданиям!

Снова смотрим живой пример.

Но теперь тебе, конечно, понятно, почему так происходит — ведь фон центрируется относительно видимой части окна.

Проблема 3. Съехали абсолютно спозиционированные элементы!

Рассмотрим еще одно прямое следствие выпадения потомка из родителя. Если фон при ресайзе должен как-то особо хитро растягиваться, например какая-то его часть смещается, а остальная остается на месте, можно попасть на еще один неприятный эффект.

Допустим, реализуем такую схему: есть общий фон сайта, фон контентной части и какой-то подвижный элемент фона, который должен быть всегда у правого края страницы. При ширине страницы равной ширине контента, этот подвижный элемент должен прятаться за краем экрана.

схема

Для простоты на рисунке использован сплошной фон. В реальных условиях — это какие-то красивые изображения. В данном случае не важно, используем ли мы wrap, как в предыдущих примерах или цепляем внешний фон на body. Предлагаю такой код:

<body>
	<div class="letter"></div>
	<div class="main">
		
	</div>
</body>
body {
	position: relative;
	background: url(images/back.gif) center 0 no-repeat;
	/* внешний фон. Он шире, чем 980px. Поэтому, чтобы все было красиво на больших разрешениях мы его отцентрируем по горизонтали.*/
}
.main {
	width: 980px;
	margin: 0 auto;
	background: url(images/back-2.png) no-repeat; /* внутренний фон - ширина 980px */
}
.letter {
	background: url(images/letter.png) no-repeat;
	width: 50px;
	height: 50px;
	position: absolute;
	right: 0;
	top: 10px;
}

Подвижный элемент спозиционировали абсолютом — при больших разрешениях все красиво. Письмо будет под правым краем.

Но, опять же, стоит уменьшить окно браузера до определенной ширины, как письмо наедет на контент, что совсем не соответствует задумке дизайнера! И снова в таком поведении нет ничего удивительного — ведь позиционирование происходит относительно родителя контента, а он-то уже стал меньше потомка!

Cмотрим демку. Скучный пример с письмом я заменил необычным ДТП.

Решение. Убиваем трех зайцев.

Возможное решение всех трех проблем — запретить родителю становиться меньше потомка. В нашем случае можно задать минимальную ширину для body равную ширине контента:

body {
	min-width: 980px;
}

Update by SelenIT: еще один вариант решения — задать min-width не для body, а для html:

html {
	min-width: 980px;
}

Исправленные примеры:

  1. Фон больше не обрезается.
  2. Винтики на месте.
  3. ДТП предотвращено (кроме IE6).

Проверено в:

  • IE 6-8
  • Firefox 3.5
  • Opera 9.5-10.6
  • Safari 4
  • Chrome 7

Update. В вероломном IE6 троллейбусы все-таки столкнулись. Специально для него код пришлось немного модифицировать — добавить дополнительный контейнер: ДТП предотвращено и в IE6.