Почему проценты полей / отступов в CSS всегда рассчитываются по ширине?

130

Если вы посмотрите на спецификацию блочной модели CSS , вы увидите следующее:

Процент [маржа] рассчитывается относительно ширины содержащего блока сгенерированного бокса. Обратите внимание, что это верно и для полей «верхний край» и «нижний край». Если ширина содержащего блока зависит от этого элемента, то результирующий макет не определен в CSS 2.1. (курсив мой)

Это действительно правда. Но почему ? Что, черт возьми, могло заставить кого-нибудь так спроектировать его? Легко придумать сценарии, в которых вы хотите, например, определенная вещь всегда должна быть на 25% ниже верхней части страницы, но трудно придумать какую-либо причину, по которой вы хотите, чтобы вертикальное заполнение было относительно горизонтального размера родитель.

Вот пример явления, о котором я говорю:

<div style="border: 1px solid red; margin: 0; padding: 0; width: 200px; height: 800px;">
  This div is 200x800.
  <div style="border: 1px solid blue; margin: 10% 0 0 10%;">
    This div has top-margin of 10% and left-margin of 10% with respect to its parent.
  </div>
</div>

http://jsfiddle.net/8JDYD/

MQP
источник
3
Моя первоначальная мысль заключается в том, что это вызовет двусмысленность относительно того, что на margin: 25%самом деле означает. Это не было бы равным запасом, даже если код предполагает, что это так. У меня нет доказательств, подтверждающих это, но это кажется разумным.
Райан Кинал,
4
jsСправка моих мыслей . Высота гораздо более изменчива, чем ширина, поэтому поля будут смещаться трудно предсказуемым образом при изменении содержимого.
RichardTowers
1
@Ryan: Это правда, это не будет равным запасом, но опять же, если вы скажете, что height: 10%; width: 10%вы тоже не получите квадратный элемент.
mqp
4
Согласно dev.w3.org/csswg/css3-box/#the-margin-properties, в нем говорится: « Note that in a horizontal flow, percentages on ‘margin-top’ and ‘margin-bottom’ are relative to the width of the containing block, not the height (and in vertical flow, ‘margin-left’ and ‘margin-right’ are relative to the height, not the width).Так что все идет в обе стороны»
Адам Суини,
1
@ Райан, я думаю, у нас есть победитель. Посмотрите здесь демо - jsFiddle
RichardTowers

Ответы:

60

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

Высота элемента определяется ростом детей. Если элемент имеет padding-top: 10% (относительно родительской высоты), это повлияет на высоту родительского элемента. Поскольку высота дочернего элемента зависит от высоты родителя, а высота родителя зависит от высоты дочернего элемента, у нас будет либо неточная высота, либо бесконечный цикл. Конечно, это влияет только на тот случай, когда смещение parent === parent, но все же. Это странный случай, который сложно разрешить.

Обновление: последние пару предложений могут быть не совсем точными. Высота листового элемента (дочерний элемент без дочерних элементов) влияет на высоту всех элементов над ним, поэтому это влияет на множество различных ситуаций.

Райан Кинал
источник
1
Обратите внимание, что есть исключения из этого правила - раздел 10.6 CSS2.1 охватывает почти все случаи вычисления высоты для различных типов ящиков. И действительно, случаи, когда есть взаимозависимость между родительским и дочерним по высоте, как правило, приводят к неопределенному поведению.
BoltClock
1
@sanjaypoyzer Я бы предположил, что в простейшем случае браузеры вычисляют ширину блоков извне (от корня до подсказок), а затем перенаправляют контент в эти блоки, чтобы определить их высоту (подсказки до корня).
sam
9
Почему W3C постоянно это делает? Очевидно, «семантический» для W3C эквивалентен необходимости обслуживать людей, которые не могут мыслить логически, что не имеет смысла. Это все равно, что жаловаться на то, насколько запутанные и глупые люди в мире, а затем распространять еще больше замешательства и глупости. Приведенное вами рассуждение не имеет смысла: тот же аргумент применим к ширине, но он работает нормально. Все, что потребуется, - это установить контекст и направление, в котором определяются высоты, если высота родительского элемента не установлена ​​в пикселях или что-то неизменное, тогда особой проблемы нет.
u353
3
Эта зависимость представляет собой алгебраическую формулу, которая имеет решение и не приведет к бесконечному циклу, если вы не решите ее способом, который не соблюдает это. Скажем, родительский рост h_p. Верхнее заполнение 10% будет составлять высоту дочернего элемента h_c = h_ci + 0.1h_p, где h_ci- внутренняя высота дочернего элемента и не зависит от высоты родительского элемента. Для одного ребенка вы просто найдете родительский рост из уравнения h_p = h_ci + 0.1h_p=> h_p = h_ci / 0.9. Вы всегда можете сделать это, независимо от того, сколько у вас детей, даже при разном заполнении. Это всего лишь одно неизвестное ( h_p) и одно уравнение.
jeteon
2
Я не думаю, что причина в бесконечном расчете. Причина проста: использование widthприводит к одной и той же проблеме по горизонтали.
Joy
30

Чтобы поле «n%» (и отступы) было одинаковым для margin-top / margin-right / margin-bottom / margin-left, все четыре должны относиться к одной и той же базе. Если бы верх / низ использовали другое основание, чем левое / правое ', тогда поле «n%» (и отступы) не означало бы одно и то же на всех четырех сторонах.

(Также обратите внимание, что наличие верхнего / нижнего поля относительно ширины позволяет использовать странный CSS-хак, который позволяет вам указать поле с неизменным соотношением сторон ... даже если поле масштабируется.)

Чак Колларс
источник
... удивительно простой ответ. Хотя все вышеприведенные предположения заслуживают внимания, это довольно хороший аргумент. Кажется вероятным тот случай, когда несколько решений будут правильными, поэтому они просто выбрали наиболее вероятное для использования одно .... может быть?
dudewad
1
Я полагаю, что было бы неплохо иметь дополнительный элемент в синтаксисе, чтобы вы могли написать say padding: n [type]. Таким образом, у вас будет padding: 5% logical(что предлагает OP), а не padding: 5% widthсо вторым параметром, по умолчанию равным «ширине» для обратной совместимости.
jeteon
5
«n% margin (и padding) не будет означать одно и то же со всех четырех сторон» - Но почему это может быть проблемой?
Herbertusz
4

Я голосую за ответ от @ChuckKollars после игры с этим JSFiddle (в Chrome 46.0.2490.86) и ссылки на этот пост (написанный на китайском языке).


Основная причина против гипотезы о бесконечном вычислении заключается в том, что использование widthсталкивается с той же проблемой бесконечного вычисления .

Взгляните на этот JSFiddle , parentдисплей inline-block, который имеет право определять поля / отступы на нем. childИмеет значение рентабельности 20%. Если мы последуем гипотезе бесконечного вычисления :

  1. Ширина childзависит отparent
  2. Ширина parentзависит отchild

Но в результате Chrome где-то останавливает вычисление, в результате чего:

введите описание изображения здесь

Если вы попытаетесь развернуть панель «результатов» по ​​горизонтали в JSFiddle, вы обнаружите, что их ширина не изменится. Обратите внимание, что содержимое в childтеге разделено на две строки (а не, скажем, на одну строку), почему? Думаю, Chrome просто где-то жестко закодировал. Если вы отредактируете childконтент, чтобы сделать его больше ( JSFiddle ), вы обнаружите, что, пока есть дополнительное пространство по горизонтали, Chrome сохраняет содержимое двух строк.

Итак, мы видим: есть способ предотвратить бесконечный расчет .


Я согласен с предположением о том, что: этот дизайн предназначен для сохранения четырех значений полей / отступов, основанных на одной и той же мере.

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

Радость
источник
Это связано с тем, что веб-страницы прокручиваются вертикально в бесконечном направлении; поэтому ширину можно рассчитать исходя из ширины окна браузера. Единственный способ сделать элементы шире экрана - это указать, что их ширина больше. Типичные элементы блока имеют ширину 100% и автоматически расширяются в вертикальном направлении (неизвестно). Вот почему с шириной нет такой проблемы.
Lucent Fox,
3

Я понимаю, что OP спрашивает, почему спецификация CSS определяет процентное соотношение верхнего / нижнего поля в процентах от ширины (а не, как можно было бы предположить, высоты), но я подумал, что также может быть полезно опубликовать потенциальное решение.

Большинство современных браузеров теперь поддерживают vw и vh, что позволяет указывать номера полей в зависимости от ширины и высоты области просмотра.

100vw / 100vh равняется 100% ширины / 100% высоты (соответственно), если нет полосы прокрутки; если есть полоса прокрутки, номера окна просмотра не учитывают это (в то время как числа% учитывают). К счастью, почти все браузеры используют полосу прокрутки размером 17 пикселей ( см. Здесь ), поэтому вы можете использовать функцию css calc, чтобы учесть это. Если вы не знаете, появится ли полоса прокрутки или нет, это решение не сработает.

Например: при отсутствии горизонтальной полосы прокрутки верхнее поле в 50% от высоты можно определить как «margin-top: 50vh;». С горизонтальной полосой прокрутки это можно определить как «margin-top: calc (0.5 * (100vh - 17px));» (помните, что операторы «минус» и «плюс» в calc требуют пробелов с обеих сторон!).

ВКК
источник
1
Это полезный обходной путь во многих случаях, но есть множество примеров, когда он не работает (например, если вы пытаетесь подогнать контент пропорционально размеру контейнера flexbox, который увеличивается, чтобы заполнить доступное пространство, в то время как есть еще одно поле с переменное содержание).
Jules