От переводчика
Статья большая решил разбить на две части.
Впервые css был представлен примерно в 1995 году, и был предназначен для стилизации простых текстовых документов. Не веб сайтов. Не приложений. А именно текстовых документов. С тех пор, css, прошел долгий путь. Возможно слишком долгий.
Для многих вещей, css, не был предназначен изначально, например для таких как: многоколоночность, отзывчивый веб дизайн и т.д. Вот почему он стал языком полным хаков и глюков, как какая-то древняя машина с кучей расширений.
В лучшем случае — работу с css можно назвать веселым занятием. И это то, благодаря чему мы имеем работу. Потому что, как я считаю, генерация эффективных и кроссбраузерных css стилей невозможна и не будет возможна в ближайшее время.
Перейдем к сути, ведь я здесь не для того, что бы высказать свое мнение о css. А для того, что бы рассмотреть ряд часто встречающихся в css проблем, и их возможных решений.
Я старался выбрать несколько действительно очень распространенных:
- Очистка float`ов.
- Как победить отступы между элементами с inline-block?
- Понимание абсолютного позиционирования.
- Когда использовать width / height равный 100% (часть 2)
- Как не облажаться с z-index. (часть 2)
- Что такое свертывание границ(margin collapsing)? (часть 2)
Очистка float`ов.
Мне кажется, что это самый распространенный вопрос в css. Он стар как мир, честно, я на тысячу процентов уверен, что каждый, кто хоть когда нибудь писал на css сталкивался с ним.
Простыми словами его можно описать так: когда внутри элемента содержатся только элементы с float, он схлопывается так, как будто в нем вообще нет дочерних элементов. То есть, по факту, элементы с float выпадают из общего потока.
Есть несколько способов для решения этой проблемы. Раньше мы использовали пустой div со стилем «clear: both» в самом низу контейнера. Затем, мы заменили его на тег hr, что не на много лучше.
И наконец Nicolas Gallagher предложил новый путь очистки float`ов без необходимости трогать разметку совсем. После продолжительных дискуссий и тестов, мы получили минимально необходимый, для его работы, набор стилей, вот его последняя версия:
.clearfix:after {
content: "";
display: table;
clear: both;
}
На самом деле я солгал, она не последняя, она самая короткая. Но если вам нужна поддержка IE 6/7, то вам нужно добавить еще вот это:
.clearfix {
*zoom: 1;
}
Для работы необходимо добавить класс .clearfix в свой проект, а затем применять его к элементам разметки. Это самый простой и чистый способ для работы с float`ами.
Как победить отступы между элементами с inline-block?
Продолжим с размещением элементов в строку, в этот раз не с помощью float, а с помощью inline-blocks. display: inline-block долгое время был недооценен, и все же мы разобрались, как он работает и почему это круто. Сегодня, все больше и больше front-end разработчиков предпочитают использовать inline-block взамен float`ов, когда у них есть такая возможность.
Главный плюс inline-block в том, что нам не приходится отчищать float`ы и мы не сталкиваемся с другими проблемами которые могут возникнуть из-за элементов спозиционированных с помощью float`ов. Просто установив свойство элемента display в значение inline-block получим гибрид строчного элемента и блока. Они могут иметь размер, отступы, но их ширина, по-умолчанию, зависит от контента, а не занимает всю ширину родителького элемента. Таким образом они размещаются горизонтально, а не вертикально.
Вы можете спросить: «В чем же тогда проблема?». А проблема в том, что они на половину строчные, а значит имеют отступ друг от друга размером равным пробелу. Со стандартным шрифтом размером 16px, он составляет 4px. В большинстве случаев размер отступа можно рассчитать, как 25% от размера шрифта. Так или иначе, это может помешать нормальному расположению элементов. Например, возьмем контейнер размером в 600px с тремя элементами внутри, размер которых 200px и задано свойством display: inline-block. Если не убрать отступ, то нам не удастся разместить их в одну линию (200 *3 + 4 * 2 = 608).
Есть несколько путей, как убрать ненужный нам пробел, каждый со своими плюсами и минусами. Если честно, то пока нет идеального решения. Давайте взглянем на все по очереди!
Уровень разметки: удаление пробелов
Для всех наших тестов воспользуемся следующей разметкой.
<div class="parent"> <!-- 600px -->
<div class="child">I'm a child!</div> <!-- inline-block 200px -->
<div class="child">I'm a child!</div> <!-- inline-block 200px -->
<div class="child">I'm a child!</div> <!-- inline-block 200px -->
</div>
Как я уже говорил ранее, дочерние элементы не станут в одну линию, потому что есть дополнительный символ пробела между каждым из них (в нашем случае перевод строки и 4 пробела). Первый способ решить проблему — полностью удалить пробелы.
<div class="parent">
<div class="child">I'm a child!</div><div class="child">I'm a child!</div><div class="child">I'm a child!</div>
</div>
Это действительно работает, но делает не удобным чтение нашего кода. Хотя, мы можем, немного реорганизовать наши элементы, что бы сохранить читаемость:
<div class="parent">
<div class="child">
I'm a child!</div><div class="child">
I'm a child!</div><div class="child">
I'm a child!</div>
</div>
И если быть уж совсем крутым, можно сделать и так:
<div class="parent">
<div class="child">I'm a child!</div
><div class="child">I'm a child!</div
><div class="child">I'm a child!</div>
</div>
Да — это работает! Конечно я не рекомендую такой подход, потому что он не интуитивен и делает код уродливым. Давайте, лучше, попробуем, что нибудь еще.
Уровень разметки: комментируем пробелы
Что если закомментировать пробелы вместо того, что бы удалить их?
<div class="parent"> <!-- 600px -->
<div class="child">I'm a child!</div><!--
--><div class="child">I'm a child!</div><!--
--><div class="child">I'm a child!</div>
</div>
Да, так гораздо лучше! Код читаем и работает. Да, способ выглядит не привычно на первый взгляд, но не так и сложно к нему привыкнуть. Я и сам использую такой способ, когда мне надо удалить пробелы между элементами с inline-block.
Конечно, кто-то скажет, что это не идеальное решение, так как оно работает на стороне разметки, а проблема должна быть решена на уровне css. Действительно. Большинство из нас на самом деле используют css решения.
Уровень css: расстояние между символами
Свойство letter-spacing используется для задания отступов между символами. Идея заключается в том, что бы установить отступ таким, что бы он нивелировал отступ между нашими элементами, затем, нам придется сбросить letter-spacing для дочерних элементов, что бы текст в них выглядил нормально.
.parent {
letter-spacing: -0.3em;
}
.child {
letter-spacing: normal;
}
Эта техика используется в Griddle — основанной на Sass системе сеток за авторством Nicolas Gallagher, так что, как вы видите, это достаточно серьезное решение. Хотя, честно говоря, мне не нравится тот факт, что мы полагаемся на магичиские числа в стилях. Плюс с некоторыми шрифтами, это число может меняться например на 0.31em и т.д. То есть, его необходимо подгонять под каждый конкретный случай.
Уровень css: отрицательный margin
Еще один подход к решению задачи, очень похож на предыдущий, но с использование отрицательного отступа. Главный его недостаток он не работает в IE 6/7. Плюс нам необходимо убрать отступ с первого элемента, что бы они ровно встали внутри нашего контейнера.
.child {
margin-left: -0.25em;
}
.child:first-of-type {
margin-left: 0;
}
Если вам не требуется поддержка IE 6/7, я считаю что это достаточно неплохе решение.
Уровень css: font-size
Ну и на последок, вы можете установить размер шрифта родительского блока в 0, что бы сделать пробел равным 0px, а затем восстановить размер шрифта для дочерних элементов.
.parent {
font-size: 0;
}
.child {
font-size: 16px;
}
У этого решения есть несколько своих проблем и ограничений:
- Вы не сможете восстановить шрифт для дочерних элементов используя em как размер шрифта
- Пробелы не удаляются на устройствах с Android до Jellybean
- Текст с использование @font-face может потерять сглаживание в Safari 5
- Некоторые браузеры игнорируют font-size: 0, например Китайская версия Chrome, в таком случае font-size сбрасывается до 12px
Так что, это не лучшее решение. Как я уже говорил ранее, скорее всего буду использовать путь с комментирование пробелов. Если для вас он выглядит неудобным, вы можете вернуться к float`ам или же вообще использовать flexbox.
Понимание абсолютного позиционирования.
Позиционирование элементов — каверзный процесс и всегда им был. Позиционирование, начинающим, дается с большим трудом. Они часто (не)используют свойство position. Это свойство определяет как элемент может перемещаться с помощью смещений (top, right, bottom and left). И принимает следующие значения:
- static — по-умолчанию, смещения не действуют
- relative — смещения двигают визуальный слой, но не сам элемент
- absolute — смещения двигают элемент внутри контекста (первый не static элемент)
- fixed — смещения позиционируют элемент внутри viewport`a и не важно где он расположен в документе
Проблемы появляются при использовании position: absolute. И наверняка вы с ними уже сталкивались: вы определили элемент с абсолютным позиционирование, потому что хотите, что бы он был в верхнем правом углу своего родителя (например кнопка закрыть у модального окна).
element {
position: absolute;
top: 0;
right: 0;
}
… а он оказывается в верхнем правом углу документа. У вас промелькает мысль «Какого черта?». На самом деле, это нормальное поведение браузера. Ключевое слово тут контекст.
Код выше просто говорит: «Я хочу что бы мой элемент был спозиционирован в верхнем правом углу контекста». Так что же такое контекст? Это первый элемент со свойством position не равным static. Это может быть непосредственно родительский элемент, или родитель родителя, или родитель родителя родителя. И так до первого элемента с position != static.
Эта концепция часто оказывается сложной для понимания новичками, но когда вы ее поняли она открывает обширные возможности по работе с элементами спозиционированными абсолютно.
Небольшая демка иллюстрирующая вышесказанное. Два родителя, в каждом по одному дочернему элементу спозиционированному абсолютно со смещение top: 0 и right: 0. Слева правильный родитель с position: relative, справа неправильный с position: static.
jsFiddle
Продолжение «Проблемы CSS. Часть 2».