Пользователь
0,0
рейтинг
7 августа 2013 в 14:05

Разработка → Проблемы CSS. Часть 2 перевод

CSS*
Продолжение перевода статьи «Проблемы CSS. Часть 1».

Когда использовать width / height равный 100%?


Height: 100%

Пожалуй, начнем с того, что попроще. Когда использовать height: 100%? На самом же деле, вопрос часто звучит немного по-другому: «Как мне сделать так, чтобы моя страница заняла всю высоту экрана?». Ведь правда?

Для ответа на него нужно понять, что height: 100% равен высоте родительского элемента. Это не магическое «высота всего окна». Так что, если вы захотите, чтобы ваш элемент занял все 100% от высоты окна, то установить height: 100% будет недостаточно.

Почему? А потому, что родителем вашего контейнера является элемент body, а у него свойство height установлено в auto по умолчанию; а значит — его высота равна высоте контента. Конечно, вы можете попробовать добавить height: 100% к body, но этого тоже будет недостаточно.

Почему? А все потому же, родителем элемента body является элемент html, у которого также свойство height равно auto и он также растягивается под размер контента. А вот теперь, если добавить height: 100% и к элементу html, то все заработает.

Стало понятнее? Корневой элемент html на самом деле не самый верхней уровень на странице — им является «viewport». Для простоты, будем считать, что это окно браузера. Так вот, если установить height: 100% элементу html, то это то же самое, что сказать — стань такой же высоты, как окно браузера.

Суммируем полученную информацию в небольшом кусочке кода:

html, body, .container {
    height: 100%;
}

Готово. Если вам интересно углубится в тему, как устроен viewport, я настоятельно рекомендую статью от PPK.

А что если у родительского элемента установлено свойство min-height, а не height?

Недавно, Роджер Йохансен (Roger Johansson) описал проблему с height: 100%, проявляющуюся, когда у родительского элемента не установлен height, но указан min-height. Я не хочу углубляться в сказанное в статье, а перейду сразу к выводам. Вам необходимо установить height: 1px для родителя, чтобы дочерний элемент смог занять всю высоту указанную в min-height.

.parent {
    min-height: 300px;
    height: 1px; /* Required to make the child 100% of the min-height */
}
 
.child {
    height: 100%;
}

Пример на jsFiddle.

Более подробно, с этим вопросом, вы можете ознакомится в статье Роджера Йохансена (Roger Johansson).

Width: 100%

Теперь давайте разберемся с width: 100%. Для начала, небольшое уточнение: устанавливая свойство width: 100%, мы хотим, чтобы наш элемент занял всю ширину родительского элемента. Все стандартно.

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

Если добавить padding и/или border к элементу с width: 100%, то он перестанет помещаться в родительский элемент. Потому что появились padding и border и вот почему width должен был называться content-width. А теперь, пожалуйста, посмотрите на пример демонстрирующий вышесказанное.

Пример на jsFiddle.

Допустим, ширина родителя 25em, а дочернего элемента — 100% (от ширины родителя) и он также имеет padding равный 1em (1em справа и1emслева, всумме2em по горизонтали) и border размером в 0.5em (0.5 em справа и 0.5 emслева, всумме1em по горизонтали), что в итоге нам дает 25em (100%) + 2em + 1em = 28em.

Есть 4 возможных пути решения этой проблемы. Первый и, наверное, лучший способ — избегать свойства width: 100%, тем более что в данном случае оно абсолютно бесполезно. Если дочерний элемент блочный, то он и так займет всю ширину родителя автоматически (без проблем с padding`ами и border`ами). Но если мы работаем с inline-block элементом, то нам не удастся так просто решить эту проблему.

Мы можем заменить width: 100% на статичный размер. В нашем случае 25 — (2 + 1) = 22em. Само собой — это плохое решение, потому что нам нужно вычислять ширину вручную. Пойдем другим путем!

Третий способ — использовать calc() для расчета ширины: width: calc(100% — 3em). Но оно тоже не подходит. Во-первых, нам все еще нужно вычислять размеры padding + border. Во-вторых, calc() плохо поддерживается браузерами (не работает в IE 8, Safari 5, Opera 12, родном браузере Android).

Идея номер четыре — использовать свойство box-sizing: border-box. Оно изменяет алгоритм расчета ширины и высоты элемента так, чтобы в них учитывались свойства padding и border. Отличная новость, заключается в том, что у box-sizing хорошая поддержка браузерами (IE8+, Opera 7+). А для всех остальных браузеров можно использовать polyfill.

Вывод: не используйте width: 100% без box-sizing: border-box.

Как не облажаться с z-index.


Все элементы на страницы позиционируются в трех плоскостях: кроме вертикальной и горизонтальной оси, существует дополнительная ось Z (глубина). Поначалу все выглядит очень просто — элементы с большим z-index находятся выше элементов с меньшим z-index. К несчастью, все гораздо сложнее. Я уверен, что z-index самое сложное css свойство за всю его историю. А также уверен, что проблемы связанные с z-index встречаются чаще других при работе с css. Надеюсь, что мы просветим возможные пути их решения.

Для начала. Свойство z-index не имеет эффекта на статических элементах. Чтобы иметь возможность перемещать элемент по оси Z, нам нужно изменить его позиционирование на relative, absolute или fixed.

Важно понимать в z-index то, что не все элементы в DOM дереве размещены на одном уровне. Это значит, что изменение z-index у элемента до очень большого значения, не гарантирует того, что он будет помещен на передний план. Это называется контекстом наложения.

Простыми словами, контекст наложения является, своего рода, группой на основе одного html элемента, у которого все дочерние элементы получают ту же позицию в контексте и такой же z-index. Изменения z-index у элемента может привести к перекрыванию им других элементов, так как вам необходимо. Вот как располагаются элементы в одном контексте наложения (снизу вверх):

  1. Фон и границы элемента, формирующего контекст
  2. Дочерние контексты наложения с негативным z-index (самый маленький первый)
  3. Не позиционированные элементы
  4. Позиционированные элементы со значением z-index равным auto или 0
  5. Позиционированные элементы с положительным z-index (каждый следующий по порядку расположен выше предыдущего, при равенстве z-index)

Когда ситуация становится неприятной

Итак, мы рассмотрели основы z-index понимание которых сэкономит вам кучу времени, уж поверьте. К сожалению, их недостаточно. Тогда все было бы слишком просто.

Дело в том, что каждый контекст наложения имеет свою ось Z. Например, элемент A в контексте 1 и элемент B в контексте 2 не могут взаимодействовать через z-index. Это значит, что элемент A, как часть контекста наложения находящегося в самом низу общего контекста наложения, никогда не сможет перекрыть элемент B другого контекста, находящегося выше уровнем, даже с очень большим значением z-index.

Но, что еще хуже. Элемент html создает корневой контекст наложения. Затем, каждый не статично-спозиционированный элемент со свойством z-index не равным auto, также создает свой контекст наложения. Ничего нового. Но вот где все начинает рушиться: некоторые, никак не связанные с контекстом наложения css свойства, также создают новые контексты. Например, свойство opacity.



Все верно, свойство opacity создает новый контекст наложения. То же самое делают свойства transform и perspective. Хотя это не имеет никакого смысла, не так ли? Например, если у вас есть какой-нибудь элемент с opacity меньше 1 или с любой трансформацией, у вас потенциально может возникнуть проблема.

К сожалению, каждая проблема с z-index имеет свой контекст (не каламбур) делающий невозможным универсальное решение.

Давайте подведем краткий итог вышесказанного:

  • Перед применением z-index убедитесь, что установили свойство position не равным static
  • Не используйте более 5 цифр для значения z-index, это абсолютно бессмысленно; в большинстве случаев, значение z-index в районе 10, будет более чем достаточно
  • Убедитесь, что элемент, который вы хотите перекрыть находится в том же контексте наложения.
  • Если у вас все еще что-то работает не так, как должно, убедитесь в отсутствии трансформаций и opacity выше у родительских элементов.


В тему, я так же рекомендую к прочтению What No One Told You About Z-index от Филипа Волтона (Philip Walton) и официальную спецификацию css.

Борьба со схлопыванием отступов


Как мне кажется — это один из глюков css, который крадет наибольшее количество времени, чтобы разобраться в чем же дело. Можно сказать, что он чем-то похож на баг с z-index. Как бы то ни было, схлопывание отступов — это когда верхний и нижний отступ двух элементов схлопываются в один (самый большой из двух).

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

Соседние элементы

Когда два соседних элемента имеют вертикальные отступы — они схлопываются до самого большого из них. Есть несколько способов предотвратить схлопывание:

  • clear: left; float: left; (right то же работает)
  • display: inline-block;


Пример на jsFiddle иллюстрирует работу фиксов.

Родитель и первый/последний дочерний элемент

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

  • overflow: hidden (или любой другой, но не visible)
  • padding: 1px (или другое значение больше 0)
  • border: 1px solid transparent (или любой другой border)
  • float: left (right то же работает)


Пример на jsFiddle иллюстрирует работу фиксов.

Пустые блоки

Когда пустой блок не имеет границ, padding`ов, высоты, его верхние и нижние отступы схлопываются в один. Так или иначе, использовать пустые блоки плохая практика, так что такое встречается не часто.

Заключение


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

Также я бы рекомендовал к ознакомлению следующие статьи и сайты:



Надеюсь, статья помогла понять некоторые вещи, которые смогут уберечь вас от проблем в будущем.
Перевод: Hugo Giraudel
Станислав @d4rkr00t
карма
33,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (28)

  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      спасибо, добавил в статью
    • +2
      Я бы оставил как есть — спецификации мало, нужно еще умение ее читать, которое приходит только с опытом верстки, который, в свою очередь требует умения верстать. Я к спецификации добрался далеко не в первый год работы с сайтами.
  • +1
    Интересненькое, спасибо.
  • +4
    Про height:100% html и body — jsfiddle.net/PGtPK/1/ (вместо height надо min-height для html и body указывать)

    Про то, что height:100% применяется только для тех элементов, высота родителей которых известна (задана, пусть и в процентах) автор намеренно упустил? ( jsfiddle.net/PmJCD/ смотреть в ie (я в 9 тестировал), если что, режим совместимости надо выключить )

    > overflow: hidden (или любой другой но не auto)
    Любой другой кроме visible, а не auto — www.w3.org/TR/CSS21/visuren.html#block-formatting

    Вы когда переводили, сами проверяли что переводите?
    • +2
      Да вы правы, как раз с auto работает, а с visible нет, поправлю в переводе.
  • –9
    Редко пишу, дык карма не позволяет частить, но читай такой бред, понимаешь — учиться и читать сейчас ни кто не хочет.

    Ситуация с height:100% верная, но тут есть над чем подумать. Но об этом не хочу сейчас.

    С width:100% тут все гораздо проще, чем может быть. Есть такое замечательное свойство display:block, оно по умолчанию делает блок со свойством box-sizing: border-box. И при этом некоторые стандартные теги, например DIV уже имеют его. Пример: если размещаете один див в другой, то второй будет иметь ширину + отступ наружний + бордер + отсуп внутренний по умолчанию.

    Стоило написать, что проблема касается, например сделать поле для ввода шириной 100%. В этом случае да, уже без box-sizing: border-box не обойтись, это проблема касается только полей форм. Для остальных есть решение display:block

    C z-index все еще проще. Комментарий не совсем верные. Еще очень важен порядок элементов, в браузерах следующий элемент расположен выше, чем предыдущий. В одном из проектов столкнулись с актой проблемой. Важный блок поставили в начале HTML, задали absolute, z-index. А он оказывался ниже блока по Z, который был в самом конце HTML с тем жe z-index… Правильно будет так написать

    Перед применением z-inedx убедитесь, что установили свойство position не равным static
    Не используйте значения большем чем 5 цифр для z-index, это абсолютно бессмысленно; в большинстве случаев, значение z-index в районе 10, будет более чем достаточно.
    Убедитесь, что элемент который вы хотите перекрыть находится в том же контексте наложения или дальше по коду.
    Если у вас все еще что-то работает не так, как должно, убедитесь в отсутствии трансформаций, opacity и position не равный static выше у родительских элементов.

    Проблемы будут у автора, по любому

    • +1
      > С width:100% тут все гораздо проще, чем может быть. Есть такое замечательное свойство display:block, оно по

      display: block не делает элемент box-sizing: border-box. И этот случай в статье описан. Так работает только если не задавать свойство width: 100%;

      jsfiddle.net/UP6Cm/

      > C z-index все еще проще. Комментарий не совсем верные

      Добавлю уточнение в статью, хотя там и рассматривается немного другой случай. Когда есть два блока с z-index или position не static и нужно, что бы дочерний элемент одного перекрывал дочерний элемент другого блока. Или когда свойства типа opacity и transform создают свой контекст там где это не задумывалось.
  • +2
    Про opacity не знал, спасибо.
    Думаю, новичкам можно много из статьи почерпнуть. В своё время многое из вышеописанного приходилось узнавать методом научного тыка.
  • +1
    > Недавно, Роджер Йохансен (Roger Johansson) описал проблему с height: 100%

    Процентную высоту потомку можно задать тогда и только тогда, когда известна высота предка.
    В описанном случае браузер её и вычисляет из min-height и height.
    Забавно другое, из равных min-height и max-height хром не смог посчитать height
    jsfiddle.net/aUDnn/31/

    • 0
      Это да, лечится так же height: 1px;
      • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    интересно, если body по умолчанию имеет height:auto, то почему

    body style=«background:red;»

    зальёт всю страницу?
    • +4
      Потому что, если не указан фон для html, то на нём работает фон, указанный для body.
      Так говорит спецификация
      For documents whose root element is an HTML «HTML» element or an XHTML «html» element that has computed values of 'transparent' for 'background-color' and 'none' for 'background-image', user agents must instead use the computed value of the background properties from that element's first HTML «BODY» element or XHTML «body» element child when painting backgrounds for the canvas, and must not paint a background for that child element. Such backgrounds must also be anchored at the same point as they would be if they were painted only for the root element.

      www.w3.org/TR/CSS2/colors.html#background
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        На мгновение опередил ;-).
      • +2
        CSS 2.1 — вообще загадочная спецификация, чем больше ее читаешь, тем больше сюрпризов открываешь. Например, простейший тест на внимательность: почему в списке приложений после G сразу идет I? Что от нас скрывает W3C? ;)
        Наверное дело в том, что буква соответствующего приложения соответствует первой букве его заголовка.
        • НЛО прилетело и опубликовало эту надпись здесь
          • +2
            Верстальщик должен быть внимательным!
  • –1
    Эх, опять одни и те же баяны…
    Первая часть поста могла бы быть гораздо короче:
    — блок на всю высоту страницы:
    html{
    height:100%;
    }
    body{
    position:relative;
    height:auto !important;
    height:100%;
    min-height:100%;
    }
    #div{
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:100%;
    }
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Позиционирование можно отключить, но вот порядок следования свойств для — критичен, ибо
      • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Позиционирование можно отключить, но вот порядок следования свойств для — критичен, ибо
  • +1
    что бы → чтобы.
  • +7
    image
  • +2
    image
  • 0
    Благодарю, вы только что решили мне проблему.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.