Схлопывание внешних отступов
Отступы margin-top и margin-bottom иногда объединяются в один, с размером равным наибольшему из них (или размеру одного, если они равны). Это поведение известно как схлопывание внешних отступов (margin collapsing). Обратите внимание, что отступы плавающих и абсолютно позиционированных элементов никогда не схлопываются.
Схлопывание внешних отступов происходит в трёх случаях:
Соседние элементы (siblings)
Схлопываются отступы соседних элементов (за исключением случая, когда к последнему элементу применено свойство clear ).
Родительский и первый/последний дочерние элементы
Если отсутствуют границы (border), внутренние отступы (padding), строчное содержимое (inline/inline-block) или промежуток для отделения margin-top родительского элемента, от margin-top одного или нескольких его дочерних элементов/блоков или отсутствуют границы (border), внутренние отступы (padding), строчное содержимое (inline/inline-block), height , min-height или max-height для отделения отступов margin-bottom родительского блока от margin-bottom отступов одного или нескольких его дочерних элементов/блоков, то внешние отступы схлопываются. Схлопнутые отступы заканчиваются за пределами родительского элемента.
Если отсутствуют границы (border), внутренние отступы (padding), строчное содержимое (inline/inline-block), height или min-height для отделения margin-top верхнего отступа этого блока от его margin-bottom нижнего отступа, то верхние и нижние внешние отступы этого блока схлопываются.
- Более сложное схлопывание отступов (более, чем двух) происходит, когда описанные случаи сочетаются.
- Эти правила применяются даже к отступам, равным 0, поэтому отступ первого/последнего дочернего элемента заканчивается за пределами его родителя (согласно правилу выше) независимо от того, равен ли отступ родителя нулю.
- При использовании отрицательных отступов, размер схлопнутого отступа вычисляется, как сумма наибольшего положительного и наименьшего отрицательного (наибольшего по модулю) отступа.
- Если все отступы отрицательные, размер схлопнутого отступа равен наименьшему (наибольшему по модулю) отступу. Это относится как к вложенным элементам, так и к соседним.
- Внешние отступы плавающих и абсолютно позиционируемых элементов никогда не схлопываются.
Примеры
HTML
p>Нижний отступ этого абзаца схлопнулся …p> p>… с верхним отступом этого абзаца, объеденив отступы между ними в один, равный code>1.2remcode>.p> div>Этот родительский элемент содержит два абзаца! p>Этот абзац имеет отступ code>.4remcode> между ним и текстом выше.p> p>Нижний отступ этого абзаца схлопывается с отступом родителя, принимая значение code>2remcode>.p> div> p>Этот абзац имеет отступ code>2remcode> от элемента выше.p>
CSS
div margin: 2rem 0; background: lavender; > p margin: .4rem 0 1.2rem 0; background: yellow; >
Схлопывающиеся margin
Вертикальные margin у соседних элементов объединяются между собой, что известно под термином «схлопывающиеся margin». Само схлопывание действует на два соседних блока или родительский и дочерний блоки, при этом примыкающие отступы не суммируются, а комбинируются в один.
Для margin слева и справа схлопывание никогда не применяется.
Схлопывание margin у соседних элементов
Схлопывание задумывалось в первую очередь для корректного отображения текста в абзацах. Расстояние между абзацами
без схлопывания увеличится в два раза, тогда как верхний margin у первого абзаца и нижний margin у последнего абзаца останутся неизменными. Схлопывание гарантирует, что расстояние в абзацах везде будет одинаковым.
В примере 1 два блока располагаются один под другим, при этом у первого блока отступ снизу равен 20px, а у второго блока отступ сверху равен 10px.
Пример 1. Схлопывание margin
Результат данного примера показан на рис. 1. Расстояние между двумя блоками будет равно не 30 пикселей, как многие ожидают, а 20 пикселей, потому что выбирается большее значение из двух margin .
Рис. 1. Вертикальное расстояние между блоков
Если оба значения margin положительные, то из них выбирается наибольшее значение и оно задаётся как расстояние между блоков.
На рис. 2 приведено схематическое изображение результата схлопывания margin .
Если один из margin отрицательный, то в этом случае происходит их складывание по правилам математики:
Если один из margin отрицательный, тогда margin вычитаются.
На рис. 3 схематично приведено поведение блоков, когда верхний margin у нижнего блока отрицательный.
Рис. 3. Один margin отрицательный
Если полученное значение в результате суммирования окажется отрицательным, то оно будет действовать на нижний блок, соответственно, он сдвинется вверх на указанное значение. При этом возможно наложение одного блока на другой. В примере 2 показан блок с верхним отрицательным margin .
Пример 2. Отрицательный margin
Результат данного примера показан на рис. 4. Расстояние между двумя блоками считается как отступ снизу у верхнего блока минус отступ сверху у нижнего блока (20px-10px). В итоге расстояние будет равно 10 пикселей.
Рис. 4. Вертикальное расстояние между блоков
Если оба margin отрицательные, то из двух значений выбирается наибольшее по модулю, оно же и выступает в качестве отрицательного отступа между элементами.
Так, если отступы равны -10px и -20px, то итоговое значение будет -20px. При этом элементы будут частично перекрываться (рис. 5).
Рис. 5. Отрицательные margin
Пустые элементы
Для пустых элементов, внутри которых нет никакого содержимого, margin-top и margin-bottom также комбинируются в один по тем же правилам, что и для соседних блоков. При этом должен ещё соблюдаться ряд условий:
- для элемента не должен быть задан padding сверху или снизу;
- для элемента не должен быть задан border сверху или снизу;
- высота элемента не должна быть указана через свойство height или min-height.
Одновременное сочетание всех этих условий (нет содержимого, не указана высота, padding и border ) встречается довольно редко, так что многие веб-разработчики никогда и не сталкиваются с подобным поведением. В примере 3 пустой блок применяется в качестве разделителя между цветными блоками.
Результат данного примера показан на рис. 6. Расстояние между двумя цветными блоками определяется пустым блоком с классом hollow . У этого блока верхний и нижний margin объединяются в один, общая высота которого будет 20 пикселей.
Рис. 6. Схлопывание margin у пустого блока
Вложенные элементы
Также схлопываются margin самого блока с margin у его первого и последнего дочерними элементами. Более точно комбинируется так:
- margin-top родителя с margin-top его первого дочернего элемента;
- margin-bottom родителя с margin-bottom его последнего дочернего элемента.
На рис. 7 схематично показано что margin у дочернего элемента располагается не внутри родителя, а выходит за его пределы.
Рис. 7. margin у дочернего элемента
Такое поведение часто можно встретить у заголовков, вроде или , которые идут первыми в блоке. У этих заголовков уже содержится margin-top и margin-bottom по умолчанию и он, объединяясь с родительским margin , влияет на отступы всего блока (пример 4).
Пример 4. margin у заголовка
Текст снизу
Результат данного примера показан на рис. 8. margin у выходит за пределы блока и задаёт внешние отступы до и после блока.
Рис. 8. margin у дочернего элемента
Отмена схлопывания margin
Схлопывание не всегда требуется при вёрстке страницы, а в некоторых случаях вообще «ломает» дизайн. Поэтому следует знать, в каких случаях схлопывание не работает.
Схлопывание margin не срабатывает:
- для элементов с абсолютным позиционированием, т. е. таких, у которых position установлено как absolute или fixed ;
- для обтекаемых элементов (для них свойство float задано как left или right );
- для строчных или строчно-блочных элементов (для них свойство display задано как inline или inline-block );
- для флекс-элементов (у родителя которых свойство display задано как flex или inline-flex );
- для элемента .
Схлопывание не действует на дочерние элементы:
- если у родителя значение overflow задано как auto , hidden или scroll ;
- если у родителя на стороне схлопывания задано свойство padding;
- если у родителя на стороне схлопывания задано свойство border.
Учтите, что свойства padding и border должны иметь размер больше нуля, к примеру, 1px.
Возьмём пример 4 и доработаем его, чтобы margin у заголовка работал внутри блока. Для этого к родительскому блоку добавляем свойство padding со значением 0.1px. На экране такая величина будет незаметна, но браузеры её понимают и схлопывание margin отменяют (пример 5).
Пример 5. Отмена схлопывания margin
Текст снизу
Результат данного примера показан на рис. 9.
Рис. 9. margin у дочернего элемента
Для отмены схлопывания у дочерних элементов можно использовать один из вариантов.
Для соседних элементов схлопывание margin отменять, как правило, нет необходимости, поскольку результат достаточно предсказуем — надо только помнить о поведении margin или включать margin-top или margin-bottom лишь для одного элемента. В примере 6 расстояние между блоков регулируется значением margin-bottom у block1 . Здесь этот margin единственный, поэтому никакого объединения не происходит и вертикальный промежуток между блоков равен значению margin-bottom .
Пример 6. Расстояние между блоков
Для полноты картины приведём несколько вариантов, когда схлопывание margin у соседних блоков не работает. Данный стиль можно добавить к каждому блоку или только к одному. Ширина элемента с float определяется содержимым элемента, поэтому надо явно задать ширину блока, равную 100%. За счёт этого не ломается вёрстка, потому что нет места для обтекания элемента.
/* 1. Использование float */ .block < float: left; /* Обтекаемый элемент */ width: 100%; /* Занимает всю доступную ширину */ box-sizing: border-box; /* Ширина не учитывает padding и border */ >
Ширина строчно-блочных элементов также определяется их содержимым, поэтому данный вариант похож на предыдущий.
/* 2. Использование строчно-блочных элементов */ .block < display: inline-block; /* Строчно-блочный элемент */ width: 100%; /* Занимает всю доступную ширину */ box-sizing: border-box; /* Ширина не учитывает padding и border */ >
Класс parent надо добавить к родителю наших блоков.
/* 3. Использование флексбоксов */ .parent < display: flex; /* Работаем с флексами */ flex-direction: column; /* Элементы выводятся друг под другом */ >