Как исправить баги в css

Иногда поведение CSS может сбивать с толку. Узнайте о некоторых хитрых свойствах, которые могут вызывать наиболее распространенные проблемы, и как эти проблемы можно решить.

CSS – это беспорядок. CSS впервые был внедрен в 1995 году, и изначально предназначался не для стилизации веб-сайтов и веб приложений, а для оформления простых текстовых документов. С тех пор CSS прошел сложный путь. Возможно, слишком уж сложный.

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

С другой стороны, это делает CSS довольно забавным. И в некоторой степени – ему мы обязаны тем, что у нас есть работа. Я полностью убежден, что разработка абсолютно кроссбраузерного CSS пока невозможна, и вряд ли будет возможна в обозримом будущем.

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

Далее вы увидите мою подборку тех проблемных моментов, с которыми встретится каждый, кто работает с CSS:

Очистка потока (float clearing) – вопрос, старый, как мир

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

В основном, когда элемент содержит внутри себя плавающие (float) блоки, он “схлопывается” – принимает минимальную высоту. Это происходит оттого, что плавающие элементы выбиваются из общего потока элементов, так что блок, в который обернуты эти элементы, ведет себя так, как будто не содержит внутри себя никаких дочерних элементов.

Есть множество путей, позволяющих исправить подобное поведение. В прошлом приходилось добавлять пустой div со свойством clear: both после всех плавающих элементов, в конце контейнера. Также можно было этот div заменить тегом hr – тоже не самый лучший вариант.

Но Nicolas Gallagher предложил новый способ очистки потока в родительском элементе без загрязнения разметки дополнительными тегами. После бурных дискуссий и тестов был выявлен рабочий вариант с минимальным набором свойств, последняя версия которого приведена здесь:

.clearfix:after {
  content: "";
  display: table;
  clear: both;
}

Вообще-то я солгал говоря, что это версия – самая последняя, но это определенно самый оптимизированный вариант. Если вам нужна поддержка IE 6/7, то вам понадобиться это дополнительный код:

.clearfix {
  *zoom: 1;
}

/* Звездочка здесь для того, чтобы другие браузеры не воспринимали и не применяли это свойство . */

Что вам действительно потребуется – так это определить класс .clearfix в вашем проекте, который вы позже можете прописать элементам, в которых требуется произвести очистку потока. Это самый простой способ работы с плавающими элементами.

Как бороться с пространством между inline-block элементами?

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

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

Вот тут вы скажете: “а какая тогда может быть проблема с ними?”. Проблема-то и заключается в том, что они – наполовину строки, и, как и строки, они отделяются друг от друга небольшим промежутком, равными ширине пустого символа. Для стандартного шрифта 16px размерность этого расстояние составляет 4px. В большинстве случаев, это расстояние составляет 25% от размера шрифта. В любом случае, это раздражает, когда ты пытаешься выстроить элементы. Давайте предположим, что у нас есть родительский элемент шириной в 600px с тремя дочерними строчно-блочными элементами шириной 200px. Если не избавиться от этих четырехпиксельных пробелов, получим, что результирующая ширина элементов будет больше ширины родительского элемента (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>

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

<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>

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

Однако, кое-кто скажет, что это не идеальный способ, так как это решение требует контроля со стороны разметки, а проблемы с позиционированием – это дело CSS и только CSS. И они будут абсолютно правы. Это приводит нас к решениям на базе CSS.

CSS: манипуляция пространством между символами. Свойство letter-spacing

Свойство letter-spacing используется для того, чтобы задать ширину пространства между символами. Идея нашего способа состоит в том, чтобы сделать пространство между символами меньше ширины пробела, а для дочерних элементов привести его к изначальному виду:

.parent {
  letter-spacing: -0.3em;
}

.child {
  letter-spacing: normal;
}

Эта техника используется в Griddle – сеточном движке, разработанном Nicolas Gallagher, так что можем считать этот вариант довольно надёжным. Однако, мне не очень нравится то, что мы опираемся на какое-то магическое число. Вдобавок, для некоторых шрифтов придется выставлять значение ниже -0.3em – например, -0.31em или -0.32em. Приходится подстраиваться под каждый конкретный случай.

CSS: отрицательный внешний отступ

Еще один способ, который похож на предыдущий – использование отрицательного отступа. Данный способ даёт сбои в IE 6/7, так как они не дружат с отрицательными отступами. К тому же, надо убирать отрицательный отступ у первого дочернего элемента, чтобы он не смещался влево, и чтобы дочерние элементы полностью влезали в контейнер:

.child {
  margin-left: -0.25em;
}

.child:first-of-type {
  margin-left: 0;
}

Если вам не надо поддерживать IE 6/7, или если у вас есть отдельный файл стилей для этих браузеров – думаю, это довольно-таки неплохое и безопасное решение.

CSS: размер шрифта

И последний, но далеко не худший способ заключается в том, что можно попробовать выставить размер шрифта родительского элемента в 0, чтобы пустые символы также стали нулевой ширины, а потом восстановить размер шрифта для дочерних элементов.

.parent {
  font-size: 0;
}

.child {
  font-size: 16px;
}

Этот способ работает замечательно, но у него тоже есть свои недостатки:

  • вы не можете восстановить размер шрифта, используя относительную величину em, так как размер шрифта родительского элемента составляет 0px
  • пробелы не убираются в стандартных браузерах систем Android до версии Jelly Bean
  • если менять в контексте @font-face, шрифт может не сглаживаться в Safari 5
  • в некоторых браузерах не разрешен шрифт нулевой высоты, например, китайский Chrome выставляет такой шрифт в стандартные 12px

Так что это определенно не самое лучшее решение. Как уже говорил ранее, я бы остановился на использовании способа с комментариями.

Разбираем абсолютное позиционирование

Позиционирование – довольно хитрая штука. Многие новички застревают на моментах, когда нужно спозиционировать элемент на странице. И они обычно слабо используют, или наоборот – злоупотребляют – свойством position. Это свойство определяет, как будет сдвинут элемент относительно какой-то стороны (top, right, bottom и left). Оно принимает четыре значения:

  • static – значение по умолчанию, смещения не работают
  • relative – сдвигается визуальный слой, но не сам элемент
  • absolute – сдвигается элемент в окружающем контексте (первого не-статичного предка)
  • fixed – элементы позиционируются в области просмотра, неважно, какова его позиция в DOM

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

.element {
  position: absolute;
  top: 0;
  left: 0;
}

… а он отображается в верхнем правом углу окна. А вы думаете: “какого фига?”. На самом деле, это предопределенное поведение браузера. Ключевое слово здесь – контекст.

Вышеприведенный код на самом деле означает: “я хочу, чтобы мой элемент размещался в правом верхнем углу относительно его контекста”. Так что такое “контекст”? Это первый не-статичный предок. Это может быть прямой родительский элемент. Или родитель родительского элемента. Или родитель родителя родителя… И так будет произведен поиск до первого элемента, чье позиционирование не задано как static.

Может быть несколько сложно для понимания, особенно для начинающих, но если вы однажды усвоили это – вы можете очень гибко управлять абсолютным позиционированием.

Вот простой пример, показывающий то, о чем мы только что говорили. Два родительских элемента, у каждого внутри дочерний элемент, который спозиционирован абсолютно значениями top: 0 и right: 0. Слева родительский элемент спозиционирован относительно (ожидаемое поведение). Справа родительский элемент оставлен статичным (неверно).

Когда применять высоту/ширину в 100%?

Высота в 100%

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

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

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

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

Почему? На самом деле потому, что корневой элемент (html) – это не самый последний родительский блок на странице – есть еще окно просмотра (viewport). Проще говоря – это окно браузера. Так что если задать height: 100% элементу html, вы сделаете его высоту, равной высоте браузера. Вот так-то просто.

Результат нашей истории отражен в следующем коде:

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

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

Roger Johansson недавно выявил проблему с height: 100% в случае когда у родительского элемента нет назначенной высоты, но для него задана минимальная высота. Я не буду углубляться в описание, но в итоге решение состоит в том, что родительскому элементу нужно задать высоту в 1px, так чтобы дочерний элемент мог растянуть его до min-height.

.parent {
  min-height: 300px;
  height: 1px; /* Необходим для того, чтобы дочерний элемент раскрылся на всю высоту min-height */
}

.child {
  height: 100%;
}

Ширина в 100%

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

А сейчас поделюсь небольшим секретом. Ширина – не совсем правильное название для этого свойства. Свойство width на самом деле не определяет всю ширину элемента, а только ширину его содержимого, что совершенно отличается от полной ширины элемента.

Если мы добавим внутреннее поле и границы к вашему элементу, которому выставлена ширина width: 100%, то этот элемент уже не будет вписываться в родительский – он будет выходить за его пределы. Все это из-за полей и границ. И именно поэтому свойство width должно было бы называться content-width (шириной контента). Пожалуйста, рассмотрите следующий пример, чтобы понять, что я имею в виду:

Ширина родительского элемента равна 25em. Ширина дочернего элемента равна 100% (от его родительского элемента), но также имеет поля размером 1em (1em слева, 1em справа. В сумме по горизонтали это будет 2em), а также границу в 0.5em (всего 1em по горизонтали), что приводит к тому, что результирующая ширина дочернего элемента состовит 25em(100%) + 2em + 1em = 28em. Хмм… Хьюстон, похоже, у нас проблема.

Есть четыре способа поправить положение дел. Первый, и определенно лучший – избегать установки width: 100%, особенно если в этом нет пользы, как в данном случае. Дочерний элемент является блочным элементом, что автоматически расширяет его до ширины родительского элемента (без той проблемы, что мы видим выше). К сожалению, если мы имеем дело со строчно-блочными элементами, то можно воспользоваться следующим фиксом.

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

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

Четвертый вариант – использовать свойство box-sizing: border-box. По существу, она меняет поведение модели контейнера так, что свойство width отражает суммарную ширину элемента, границы и поля включены. Хорошие новости заключаются в том, что поддержка браузерами этого свойства довольно хороша (все, кроме IE 7- и Opera 9).

Как не ошибиться с z-index?

Все элементы на странице позиционируются в трехмерном пространстве: в дополнение к вертикальному и горизонтальному позиционированию, элементы также располагаются вдоль оси Z. На первый взгляд, эта концепция кажется довольно простой: элементы с более высоким z-index отображаются поверх элементов с более низким z-index.

К сожалению, вещи могут быть более сложными, чем они кажутся на первый взгляд. Я вообще уверен, что это самое запутанное CSS-свойство. Уверен, что проблемы с z-index – самые распространенные и раздражающие, с которыми только можно встретиться во время работы с CSS. Но мы постараемся найти решение.

Начнем с начала: свойство z-index не работает со статичными элементами. Для того, чтобы сдвинуть элемент по оси Z, нужно определить позиционирование для элемента, как relative, absolute или fixed. Так что первая вещь, которую мы должны сделать – убедиться, что элемент правильно спозиционирован, прежде чем вообще задумываться о том, чтобы назначить ему z-index.

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

Меняя положение по оси Z, элементы можно заставить перекрывать друг друга в нужном порядке. Вот как, согласно CSS спецификации отображаются элементы в рамках одного группового контекста:

  1. фон и границы элемента, который формирует групповой контекст
  2. дочерние групповые контексты с отрицательными уровнями (первыми идут наименьшие)
  3. внутри-потоковые, не-строчные, неспозиционированные потомки
  4. не-спозиционированные плавающие элементы
  5. внутри-потоковые, строчные, неспозиционированные потомки, включая таблицы и большинство строчных блоков
  6. дочерние групповые контексты с уровнем 0 и спозиционированные потомки с уровнем 0
  7. дочерние групповые контексты с положительными уровнями (от меньшего к большему)

Когда результат ужасает

Хорошо, это была самая основная информация о свойстве z-index. Знания об этом могут сэкономить вам много времени, и уменьшить количество проблем, уж будьте уверены. К сожалению, этого недостаточно. Это были ещё цветочки!

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

Но подождите – на самом деле, все еще хуже. Элемент html формирует корневой групповой контекст. В таком случае, каждый спозиционированный (не-статичный) блок со значением z-index больше, чем auto создает новый групповой контекст. Ничего нового в этом нет. А вот здесь все становится довольно печально: некоторые CSS-свойства, совершенно не связанные с позиционировнием, создают новые групповые контексты. К примеру, opacity.

Да, да – свойство opacity создает новый групповой контекст. Так же действуют свойства transform и perspevtive. Но ведь это не имеет смысла, не так ли? Значит, что если у вас есть элемент с прозрачностью, отличающейся от 1, или с трансформацией, отличной от none – у вас есть потенциальная проблема.

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

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

Что такое объединение отступов?

Я думаю, что это один из “глюков” CSS, который отнял большую часть моего времени. Полагаю, что вы скажете, что это настолько же странно, как и поведение z-index. Короче говоря, объединение отступов это когда верхний и нижний отступ двух элементов сливаются в наибольший из двух этих отступов. Вообще говоря, вертикальный отступ между двумя блоками рассчитывается так:

margin = max(block1.marginBottom, block2.marginTop)

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

Смежные блоки

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

  • clear: left; float: left; для смежных блоков (right тоже работает)
  • display: inline-block on siblings (также срабатывает inline-table)

Следующий пример демонстрирует эти фиксы в действии:

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

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

  • overflow: hidden (или другое, отличное от auto)
  • padding: 1px (или любое другое значение, больше 0); некоторые браузеры даже поддерживают субпиксельные значения)
  • border: 1px solid transparent (или любая граница)
  • display: inline-block (также работают строчные таблицы – inline-table)
  • float: left (right также подходит)

Следующий пример демонстрирует эти фиксы в действии:

Пустые блоки

Когда у пустого блока нет ни границы, ни полей, ни высоты – его верхняя и нижняя граница объединяются в одну. В таком случае это подходит под первый или второй случай, которые описаны выше – объединение границ с родителем/смежным элементом. Тем не менее, пустые элементы – тоже, в общем, плохая идея, так что я надеюсь, что вам не придется сталкиваться с ними часто.

Итоги

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

В любом случае, надеюсь, что некоторые решения позволят вам сэкономить время.

Поиск первопричины ошибки в CSS

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

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

Работая над своей книгой «Отладка CSS», я много узнал об отладке и исправлении ошибок CSS. В этой статье я рассмотрю процесс исправления первопричины ошибки и исследую некоторые распространенные проблемы CSS, а также то, как их исправление извне может привести к большему количеству проблем.

Базовый пример

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

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

.card__img {

  width: 100%;

}

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

Поиск первопричины ошибки в CSS

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

Поиск первопричины ошибки в CSS

Мы действительно хотим получить это? Это похоже на макет Mansory, но цель этого макета — ограничить компоненты карточек определенным размером. Карточки не должны быть выше друг друга. Вот быстрое решение этой проблемы:

.card__img {

  width: 100%;

  height: 200px;

}

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

Поиск первопричины ошибки в CSS

Как видите, добавление height частично решило проблему и привело к тому, что высота компонентов была одинаковой. Однако это вызвало еще более серьезную проблему — сжатие вертикального изображения. Как фронт-энд разработчик, вы обязаны задавать множество вопросов, которые позволят вам понять, как именно должен работать и вести себя компонент.

Вот несколько вопросов, над которыми стоит задуматься:

Должны ли компоненты карточки быть одинаковой высоты?

Каков ожидаемый размер миниатюры карточки?

Нужно ли разрешать авторам контента загружать изображения большего или меньшего размера?

Ответы на эти вопросы могут меняться от проекта к проекту. Я хочу, чтобы это было похоже на настоящий проект, поэтому я отвечу на них за вас.

Да, они должны быть одинаковой высоты.

330 пикселей * 220 пикселей.

Нет, размер изображения должен быть одинаковым.

А теперь давайте посмотрим, как исправить проблему. Применяя object-fit: cover к изображению, мы можем гарантировать, что изображение не будет сжато.

.card__img {

  width: 100%;

  height: 220px;

  object-fit: cover;

}

Я знаю, что размеры изображений должны быть одинаковыми, но добавление object-fit может предотвратить нежелательные проблемы в будущем. И, наконец, вопрос, над которым я должен подумать: можно ли скрыть некоторые части изображения? Использование object-fit не сделает изображение сжатым, но, с другой стороны, оно изменит размер изображения, чтобы оно соответствовало указанной ширине и высоте, и это может скрыть некоторые его части.

Вот как будет выглядеть высокое изображение с object-fit: cover.

Поиск первопричины ошибки в CSS

Обратите внимание, что некоторые части второго изображения скрыты. Это ожидается, и это можно решить, изменив свойство object-position следующим образом:

.card__img {

  width: 100%;

  height: 220px;

  object-fit: cover;

  object-position: center 75%;

}

Хотя это решает проблему, это непрактично. Мы не можем добавить определенное object-position для каждой миниатюры в проекте.

Поиск первопричины ошибки в CSS

Некоторые могут возразить, что нам не следует скрывать части изображений, поскольку мы платим большие деньги за их съемку и редактирование. В таком случае единственное, что вы можете сделать — это объяснить, что это не ваша проблема и что размеры изображения должны быть согласованы.

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

Размещение раскрывающегося меню

Сколько раз вам нужно было добавить настраиваемое раскрывающееся меню в компонент заголовка? Я думаю, вы делали это раньше.

Поиск первопричины ошибки в CSS

Выпадающее меню расположено под его родительским элементом. Поскольку раскрывающийся список представляет собой элемент с абсолютно позиционированием, мы можем сделать это, изменив свойство top.

.nav__item {

  position: relative;

}

.nav__dropdown {

  position: absolute;

  left: 0;

  top: 40px;

}

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

Вы сделали именно это, но забыли проверить, работает ли раскрывающееся меню должным образом или нет. К сожалению, это не так.

Поиск первопричины ошибки в CSS

Это потому, что свойство top имеет фиксированное значение. Лучший способ сделать это — использовать процентное значение. Таким образом, раскрывающийся список останется на своем месте независимо от высоты навигации.

.nav__dropdown {

  position: absolute;

  left: 0;

  top: 100%;

}

Очень хорошее понимание основ CSS может помочь избежать такой ситуации. Использование фиксированного значения для свойства top является ошибкой и не является чем-то, на что можно положиться.

Заменяемые в HTML элементы

Согласно MDN: Заменяемые элементы — это элементы, на содержимое которых не влияют стили текущего документа. С помощью CSS можно изменить положение заменяемого элемента, но не содержимое самого заменяемого элемента.

Абсолютно позиционированное изображение

Примером заменяемого элемента является HTML img. Я хочу рассмотреть менее известные факты, касающиеся его. Предположим, что у нас есть изображение, которое абсолютно позиционировано относительно своего контейнера, но без установки width и height.

Если вам повезет и размер изображения такой же, как у оболочки, то вы не заметите проблемы, которую я вам покажу.

.card__image {

  position: absolute;

  left: 0;

  top: 0;

  right: 0;

  bottom: 0;

}

Вы можете догадаться, что изображение заполнит родительский элемент, потому что оно позиционируется со значением 0 с четырех сторон оболочки. Нет, это не так!

Поиск первопричины ошибки в CSS

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

.card__image {

  position: absolute;

  left: 0;

  top: 0;

  width: 100%;

  height: 100%;

}

Это не самая сложная проблема в мире, но она может легко сбить с толку разработчика, если он недостаточно сосредоточен.

Поиск первопричины ошибки в CSS

Изображение, как флекс-элемент

Поиск первопричины ошибки в CSS

Если вы хотите получить оболочку, содержащую несколько изображений, то с этим также может быть сложно справиться с помощью Flexbox. Предположим у нас есть следующие HTML и CSS:

<div class=“wrapper”>

  <img src=“image-1.jpg” alt=“” />

  <img src=“image-2.jpg” alt=“” />

</div>

.wrapper {

  display: flex;

}

.wrapper img {

  flex: 1;

}

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

По умолчанию флекс-элементы не сжимаются меньше минимального размера содержимого (длины самого длинного слова или элемента фиксированного размера). Чтобы изменить это, установите свойство min-width или min-height.

Это означает, что нам нужно заставить флекс-элемент сжиматься меньше минимального размера содержимого, используя min-width (потому что flex-direction в нашем случае равно row).

.wrapper {

  display: flex;

}

.wrapper img {

  flex: 1;

  min-width: 0;

}

Вот и все. Мы узнали основную причину проблемы, просто прочитав спецификацию CSS и узнав, как все работает под капотом.

Переполнение и Inline-Block элементы

Я объяснил эту концепцию в своей статье Переполнение в CSS.

Поиск первопричины ошибки в CSS

У нас есть три элемента inline-block, и один из них имеет overflow: hidden. Вы заметите, что кнопка с overflow: hidden не выровнена по базовой линии других элементов.

Согласно спецификации CSS: Базовая линия ‘inline-block’ — это базовая линия его последнего встроенного блока в нормальном потоке, если только он не имеет встроенных блоков в потоке или если его свойство ‘overflow’ имеет вычисленное значение, отличное от ‘visible’, в этом случае базовая линия — это край нижнего поля.

Мне повезло, что Гаэль Пупар упомянул меня в этой проблеме на github в репозитории Bootstrap. Я должен был увидеть вышеупомянутую проблему в реальной жизни!

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

.search-results span {

  display: inline-block;

  max-width: 150px;

  overflow: hidden;

  text-overflow: ellipsis;

  white-space: nowrap;

}

С этим у нас проблема. Строка запроса не выровнена со смежными элементами. См. рисунок ниже:

Поиск первопричины ошибки в CSS

Без хорошего понимания CSS и тщательного исследования вы можете подумать, что это проблема, связанная с браузером или что-то в этом роде. На самом деле, это естественно. Вот краткое сравнение трех основных браузеров:

Поиск первопричины ошибки в CSS

Как вы заметили, Chrome и Firefox дают одинаковый результат. С другой стороны, Safari показывает другой результат. Вы можете подумать вслух так: «О, это работает в Safari. Тогда проблема в Chrome и Firefox», и затем вы тратите часы на исследование, чтобы наконец обнаружить, что Safari реализует поведение неправильно.

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

.search-results span {

  position: relative;

  top: 3px;

  display: inline-block;

  max-width: 150px;

  overflow: hidden;

  text-overflow: ellipsis;

  white-space: nowrap;

}

Теперь, когда мы знаем первопричину, вот подходящее решение:

.search-results span {

  display: inline-block;

  max-width: 150px;

  overflow: hidden;

  text-overflow: ellipsis;

  white-space: nowrap;

  vertical-align: top;

}

Я только добавил vertical-align: top, и проблема решилась мгновенно.

Поиск первопричины ошибки в CSS

Расстояние между смежными элементами

Поиск первопричины ошибки в CSS

Вы работали над созданием веб-сайта и получили отзывы от ведущего дизайнера о том, что расстояние между кнопками непоследовательно. Это 22px вместо 16px. При проверке CSS получается следующее:

<div class=“wrapper”>

  <a class=“button” href=“”>Save changes</a>

  <a class=“button” href=“”>Cancel</a>

</div>

.button {

  display: inline-block;

  margin-right: 16px;

  /* Other button styles */

}

Вы определили, что интервал равен 16px, но если вы измеряете его в браузере, он больше. Если вы торопитесь, вы можете просто уменьшить поде и сделать его 10px в CSS, а в браузере оно станет 16px. Если вам повезет, дизайнер может не заметить, что вы здесь сделали, но это плохое решение.

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

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

.wrapper {

  display: flex;

  flex-wrap: wrap;

}

.button {

  margin-right: 16px;

  /* Other button styles */

}

Заключение

Для вас как для фронт-энд разработчика важно найти первопричину ошибки в CSS. Вот что вы получите, решив проблему с нуля:

Вы узнаете что-то новое.

Вы никогда не забудете решение.

Вы станете более опытным.

Это весело.

Автор: Ahmad Shadeed

Источник: ishadeed.com

Редакция: Команда webformyself.

Изучая веб-разработку, вы будете часто бороться с CSS. В нем есть много вещей, о которых не знаешь, пока не столкнешься с ними нос к носу.

Особенно много проблем доставляет разметка макета. Например, вы пытаетесь что-то спозиционировать, но внезапно исчезают отступы, элемент уменьшается или пропадает или просто позиционируется не там, где нужно.

В интернете очень много запросов подобных следующим:

  • как удалить вертикальный отступ между дивами
  • как убрать горизонтальный отступ между дивами
  • как исправить схлопывание маргинов css
  • почему маргины внутренних элементов не работают
  • почему нижний/левый/правый маргин не работает

Да, схлопывание маргинов – это довольно сложная концепция, которая не сразу поддается пониманию. Узнать об этом подробнее можно здесь.

Есть и другие популярные вопросы, которые мы разберем прямо сейчас.

text-align: center не работает

Мы постоянно используем text-align: center для центрирования текста. Но это правило также может быть использовано для выравнивания элементов внутри родительского контейнера.

У новичков это часто не получается: текст внутри блока центрируется, а сам блок – нет. Почему? Потому что элемент, который вы пытаетесь выровнять является блочным.

По умолчанию он занимает 100% ширины родителя, поэтому его невозможно выровнять по центру. Чтобы решить проблему, ему следует установить строчно-блочный тип отображения.

Рассмотрим на примере.

Есть вот такая разметка:

<div id="container">
  <div id="box">
    <h3>Some Content</h3>
  </div>
</div>

и вот такие стили:

body{
  background: #ccc;
}
#container{
  background: white;
  width: 500px;
  height: 500px;
  text-align: center;  /* отцентрируется только контент */
}
#box{
  color: white;
  background: #333;
  width: 250px;
  height: 250px;
}

В браузере это выглядит следующим образом:

Текст выровнялся по центру, но сам элемент – нет. Теперь вы знаете, что это происходит из-за того, что он имеет блочный тип отображения. Чтобы сработало свойство text-align: center, он должен быть строчным или строчно-блочным.

Добавим всего одну строчку в файл стилей:

#box{
  display: inline-block; 
  color: white;
  background: #333;
  width: 250px;
  height: 250px;
}

Теперь все правильно:

И это совсем не магия, ведь вы знаете, как все работает 😉

Расстояние между инлайн-блочными элементами

С этой проблемой сталкивался каждый CSS-новичок.

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

Предполагается, что каждому нужно назначить ширину в 50%, однако это не работает. Непонятное расстояние между ними заставляет второй элемент сползать на следующую строчку, так как итоговая сумма размеров превышает 100% родителя.

В этом моменте начинаются истерики и появляются костыли с волшебными цифрами вроде width: 49.76%.

Разберемся на примере: есть два элемента с одинаковой шириной и высотой, и требуется, чтобы они занимали 50/50 доступного пространства внутри своего родителя.

<div id="container">
  <div id="first"></div>
  <div id="second"></div>
</div>

Добавляем стили:

body{
  background: #ccc;
}
#container{
  background: white;
  width: 500px;
}
#first,#second{
  display: inline-block;
  color: white;
  background: #333;
  width: 50%;
  height: 250px;
}

и получаем вот это:

Есть 2 распространенных способа решения этой проблемы.

  1. использовать комментарии;
  2. удалить пространство между тегами в HTML-файле.
<!-- использование комментария -->

<div id="container">
   <div id="first"></div><!--
--><div id="second"></div>
</div>

<!-- удаление промежутка между тегами -->
<div id="container">
   <div id="first"></div><div id="second"></div>
</div>

Теперь все нормально:

Примечание переводчика:
Строчно-блочные элементы воспринимаются браузером как обычные слова в предложении, поэтому он разделяет их пробелом, ширина которого примерно равна 4px. Оба способа позволяют убрать этот пробел на физическом уровне.
Также можно воспользоваться CSS и установить для контейнера нулевой размер шрифта. При этом пробел сохранится, но будет иметь нулевую длину. Этот способ часто используется, но подходит не для всех ситуаций.

«Съезжание» строчно-блочных элементов

Эта проблема появляется, когда у вас есть два строчно-блочных элемента и вы добавляете в один из них больше контента.

Для примера возьмем вот такую разметку:

<div id="first">
  <h3>Some content</h3>
</div>
<div id="second"></div>

и вот такие стили:

body{
  background: #ccc;
}
#first,#second{
  display: inline-block;
  color: white;
  background: #333;
  width: 250px;
  height: 250px;
}

Вот результат:

Макет сломался из-за того, что вершина первого элемента сползла к низу второго – это проблема выравнивания. Решить ее очень легко, есть целых два пути:

  1. overflow: hidden;
  2. vertical-align: middle (или top, или bottom, если вы понимаете, как это работает).

overflow: hidden

Это свойство можно добавить всем строчно-блочным элементам в ряду или только тому, который вызвал проблему:

body{
  background: #ccc;
}
#first,#second{
  display: inline-block;
  color: white;
  background: #333;
  width: 250px;
  height: 250px;
  overflow: hidden; 
}

vertical-align: middle

В этом решении вместо overflow используется vertical-align. Тип выравнивания может быть любым: top, middle, bottom.

Если использовать top для элементов с разной высотой, то результат будет следующий:

С другими значениями, вы можете поэкспериментировать самостоятельно.

Если вы не понимаете, как работает вертикальное выравнивание, просто используйте вместо него overflow: hidden.

А мы попробуем выровнять блоки в примере:

body{
  background: #ccc;
}
#first,#second{
  display: inline-block;
  color: white;
  background: #333;
  width: 250px;
  height: 250px;
  vertical-align: middle;
}

Получилось:

Плавающие элементы не имеют высоты?

Это одна из самых раздражающих проблем CSS.

Допустим, у вас есть 2 заголовка, вы устанавливаете одному float: left, и внезапно второй прилипает к верху сайта перед ним, как будто в разметке он идет первым.

<h2 id="left">Left Text</h2>
<h2 id="right">Right Text</h2>
body{
  background: #ccc;
}
#left{
 float: left;
}

Или например, вы устанавливаете float обоим заголовкам, одному – влево, а другому &ndahs; вправо. Тогда элемент, идущий после них, поднимется вверху.

<h2 id="left">Left Text</h2>
<h2 id="right">Right Text</h2>
<img src="https://bit.ly/2LsgNeT">
body{
  background: #ccc;
}
#left{
 float: left;
}
#right{
  float: right;
}
img{
  width: 50%;
}

Существует 3 возможных решения для этой проблемы:

  • overflow: hidden;
  • display: inline-block.

oveflow: hidden

Нужно обернуть плавающие элементы в родительский контейнер и уже для него установить overflow: hidden.

<div id="wrapper">
  <h2 id="left">Left Text</h2>
  <h2 id="right">Right Text</h2>
</div>
body{
  background: #ccc;
}
#wrapper{
  overflow: hidden;
}
#left{
 float: left;
}

display: inline-block

Решение похоже на предыдущее, только вместо overflow: hidden родительскому элементу добавляется display: inline-block.

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

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

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

Сложно, непонятно и сбивает с толку? Да, есть немного. На самом деле, вам не следует использовать это решение, так как оно совершенно немасштабируемо. Если размеры блоков могут меняться (например, при увеличении шрифта), оно не подходит.

Поэтому пойдем более простым путем и сделаем второй заголовок строчно-блочным либо также плавающим.

Не забудьте использовать вертикальное выравнивание.

<div id="wrapper">
  <h2 id="left">Left Text</h2>
  <h2 id="right">Right Text</h2>
</div>
<img src="https://bit.ly/2LsgNeT">

Стили с вертикальным выравниванием:

body{
  background: #ccc;
}
#wrapper{
  background: white;
  display: inline-block;
  vertical-align: top;       
}
#left{
 float: left;
}
#right{
  float: right;
}
img{
  width: 50%;
}

А этот пример без выравнивания:

Margin-top не работает?

Еще одна из самых распространенных проблем, которую часто решают с помощью паддингов, вместо того, чтобы разобраться, в чем дело.

Эта проблема известна как схлопывание полей.

Представим, что один элемент расположен внутри другого. Если назначить margin-top внутреннему блоку, то он появится у внешнего, а отступ потомка от родителя не изменится.

<div id="container">
  <div id="box"></div>
</div>
body{
  background: #ccc;
}
#container{
  background: white;
  width: 500px;
  height: 500px;
}
#box{
  background: #333;
  width: 250px;
  height: 250px;
  margin-top: 100px;    /* добавляем margin-top */
}

Есть целых 6 решений этой проблемы (но не все из них хороши).

  • display: inline-block для дочернего элемента;
  • display: inline-block для родительского элемента;
  • overflow: hidden для родительского элемента;
  • position: absolute для родительского элемента;
  • установка прозрачной рамки для родителя;
  • установка верхнего паддинга для родителя (можно использовать очень маленькие значения, десятые доли пикселя).

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

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

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

После применения любого из этих способов результат будет такой:

Вот с такими проблемами часто сталкиваются начинающие веб-разработчики. Надеюсь, статья была полезной.

Перевод статьи Debugging CSS с сайта benfrain.com, опубликовано на css-live.ru с разрешения автора — Бена Фрейна.

Опыта в отладке CSS у меня хоть отбавляй. Чужой код и мой собственный. Мобильные плотформы и десктопные браузеры. Всё от старых версий Internet Explorer до последних ночных сборок WebKit. Работая с людьми, я понял, что далеко не у всех налажен процесс отладки CSS.

Я понял, что с системным подходом к решению проблемы борьба с багами занимает меньше времени.

А вот и мой собственный подход.

Я не утверждаю, что это единственный способ отладки CSS. Но он крайне эффективен для меня. Если CSS для вас не основной язык, то его отладка может показаться чем-то таинственным; со следующим руководством можно намного эффективнее бороться и избавляться от багов.

В целом, я делю процесс отладки на три этапа:

  • Оценка и быстрые решения
  • Локализация и воспроизведение
  • Причины и исправление

Давайте рассмотрим каждый этап, а после быстренько разберём одну CSS-проблему для примера.

Оценка и быстрые решения

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

Вот некоторые из распространённых подводных камней, которые наверняка известны любому опытному CSS-разработчику:

  • странный пробел вокруг изображения? Задайте ему display: block (изображение по умолчанию строчное,  поэтому учитываются пробелы).
  • элемент неправильно ведёт себя в потоке? Вероятно, где-то установлено обтекание (float).
  • абсолютно позиционированный элемент не появляется или находится в неправильном месте или за другим элементом? Скорее всего, вы забыли установить position родительскому элементу, или случайно создали контекст наложения z-index с помощью transform или opacity.
  • псевдоэлемент не отображается? Скорее всего нужно добавить любое значение свойству content.

Таких «багов» уйма. Но это не настоящие баги, просто разработчики не до конца понимают, что делает браузер. Точнее: что ваш CSS-код приказывает браузеру делать.

Более опытные разработчики уже знают, каких проблем можно ждать от этих особенностей CSS, и поэтому для них это «баги», с которыми можно быстро справиться. Они моментально распознают баг, в отличие от разработчиков с куда меньшим объёмом CSS-знаний. Важно понимать, что одним разработчикам быстрее понадобится переключиться на отладку по «методике», чем другим.

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

Вместо этого, в сложных ситуациях стоит определить проблемную область и буквально скопировать разметку из DOM, а после переходить к следующему этапу отладки: локализация и воспроизведение.

Полезный совет: инструменты разработчика в большинстве браузеров позволяют выбрать оборачивающий элемент и скопировать этот блок HTML-кода. В инструментах разработчика Chrome с выбранным в DOM элементом это выглядит так: «Copy > Copy OuterHTML».

Локализация и воспроизведение

На этом этапе отладки CSS очень помогают сервисы вроде Codepen. По сути нам нужно локализовать проблему — получить конкретный код, участвующий в баге. Это позволит быстро изолировать баг и сосредоточиться на первопричине.  

Для чистоты эксперимента, в этот минимальный пример должен попасть только тот HTML и CSS, который важен для воспроизведения проблемы. Можете набрать стили для разметки заново или скопировать минимальную часть реального CSS, относящуюся к делу. По возможности, постарайтесь не вываливать в тестовый пример весь CSS; для воспроизведения нужно только самое необходимое. Зачастую постепенного добавления CSS вполне хватает, чтобы выявить проблемную зону.

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

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

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

Разметка?

Допустим, мы сделали тестовый пример с минимальным CSS, но он ведет себя так же, как исходный проблемный код. Это также полезно. Теперь пришло время разметки.

Первое, что нужно сделать — и не пропускайте этот шаг — это проверить валидность разметки. Даже если валидатор укажет проблемы, которые нас не заботят (например, мета-теги), мы убедимся, что разметка не нарушена каким-либо образом. Задача найти пропущенные закрывающие теги, атрибуты без кавычек, и всё то, что может помешать браузеру парсить контент. Используйте для этого валидатор W3C (а лучше новую проверку разметки Майка Смита — прим. перев.).

Как только валидность разметки проверена, полезно «избавиться» от лишних браузерных стилей. Вот так:

Прежде всего, замените все элементы в разметке на div-ы (для блочных элементов) и span-ы (для строчных), а после проверьте, что элементы выбираются в CSS только по классу. Возможно, ещё потребуется поменять все перегруженные селекторы типа a.link на просто .link.

Благодаря нейтральной разметке мы удаляем любые браузерные стили/поведение по умолчанию для определённых заковыристых HTML-элементов, представляющих проблему. Особенно это касается элементов формы (как мы увидим в нашем примере).

Если после всех замен тестовый пример выглядит, как и предполагалось, значит виновный найден: в браузерные стили добавлены лишние значения по умолчанию. Теперь можно «избавиться» от всех этих лишних стилей, глядя в панель вычисленных стилей. Подробнее о вычисленных стилях чуть позже.

Причины и исправления

Допустим, упрощение разметки ничего не изменило. Тем не менее проблема локализована и стабильно воспроизводима Теперь стоит проверить локализованный участок в других браузерах. Та же проблема всплывает в Chrome, Internet Explorer, Safari и Firefox? Если нет, кто из них понимает правильно? Если ошибка всплывает только в одном браузере, то стоит поискать среди известных багов для этого браузера.

  • Chrome bugs
  • Firefox bugs
  • Safari bugs
  • Microsoft bugs

Это известная проблема с браузером Х или конкретной версии браузера Х? Баг скоро исправят? Известны ли обходные пути, не влияющие на другие браузеры? В крайнем случае, можете добавить в код ветку с исправлением бага для нужного браузера?

Недавно я подробно описывал баги в браузерах, а Лиа Веру ещё в 2011 написала замечательную статью о процессе.

Еще бывает, что требуется «безвредный» хак. Например, недавно у меня был случай, где абсолютно позиционированный блок визуально должен был размещаться в конце другого блока. left: 100% помогло везде кроме Internet Explorer (и его мобильных эквивалентов для Windows Phone 8, 8.1 и 10). В этом браузере между блоками образовался промежуток. Это походило на проблему субпиксельного рендеринга, поэтому изменение значения на 99.99% побороло Internet Explorer и и не помешало другим браузерам. Это хак. Но мы понимаем, как он работает (один браузер округляет субпиксели, а другой — нет), и поэтому этот код, если мы еще и поясним его комментарием, никому не повредит.

Грег Витворг из Microsoft также поделился некоторыми деталями округления субпикселей в браузерах. WebKit/Blink округляет до 1/64, Gecko — до 1/60 и Edge — до 1/100 (с подачи  ‘smfr, разработчика WebKit)

Отладка мобильных платформ

Для мобильных устройств, если у вас физически есть требуемое устройство, а не только сообщение о баге от пользователя, и это устройство поддерживает такую функцию, ничто не сравнится с «удаленной» отладкой по кабелю. В этом плане великолепен Safari с подключенным iPhone (только для Mac), впрочем, как и Chrome на Android в паре с десктопным Chrome (Windows и Mac). Для iOS, если у вас нет под рукой конкретного устройства, весьма неплох и отладчик Safari в паре с симулятором iOS, но вы будете ограничены теми версиями iOS, которые поддерживает ваша версия Xcode. В теории, можно завести несколько версий Xcode одновременно, например, одну, которая поддерживает iOS6 и iOS7, и другую с поддержкой iOS8 и 9, но у меня это никогда не толком не срабатывало.

А как быть, если нужно разобраться с более старым мобильным устройством? Удаленная отладка тут часто не вариант. Для таких случаев полезно выработать «чутьё», какие инструменты разработчика могут помочь в решении проблем отображения какой платформы.

Например, если вы рассматриваете проблему в старой версии Safari на iOS (напр. iOS5-6) или во встроенном браузере из Android < 4.2, полезно знать, что у них общая основа WebKit. На свежих версиях OS X, как отмечено выше, довольно трудно добиться, чтобы симулятор iOS точно воспроизвел эти устройства (симуляторы iOS склонны поддерживать лишь пару последних версий). Более плодотворным подходом может оказаться вовсе даже взять и скачать последнюю версию Safari для Windows (5.x, если память меня не подводит).

Отображение на этой старой версии Safari для Windows на самом деле очень похоже на те мобильные платформы, так что отладчик в нем наверняка покажет больше, чем в новейших Chrome/Firefox или имеющемся IE. Еще один вариант — если операционная система это поддерживает, скачать и установить древнюю ночную сборку WebKit.

Аналогично, если у вас Mac, а проблемы возникают на Windows Mobile (8/8.1), возьмите и скачайте виртуальную машину с IE10. Затем воспользуйтесь тамошним отладчиком, поскольку в отображении на IE10 и мобильных IE8 и 8.1 очень много общего.

В некоторых браузерах, особенно старых мобильных, если что-то кажется багом — оно вполне может им и оказаться, так что найти обходное решение будет намного проще, если исследовать проблему там, где ее можно «поковырять» в отладчике.

Вычисленные стили

Разработчики часто недооценивают такую функцию отладчика, как панель вычисленных стилей. Если вычисленные стили для вас новинка, то их название само себя поясняет — это стили, которые применяются к элементу после вычисления в браузере. Это важно, потому что фактически действующие стили могут отличаться от тех, что написали вы. Более того, среди действующих стилей могут быть те, которых вы вовсе не писали. Сейчас я поясню это на примере. Возьмём такую разметку.

<fieldset class="outer">
    <div class="inner">
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
        <label for="" class="item"><span>hello</span></label>
    </div>
</fieldset>

И CSS:

.outer {
    max-width: 400px;
}

.inner {
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    -ms-overflow-style: -ms-autohiding-scrollbar;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
}

.item {
    display: inline-block;
    width: 100px;
}

Какая ширина у outer по вашему? Если считаете, что 400px, как указано в max-width, то я бы вас понял. Но это не так. Взгляните на это:?

See the Pen When Computed Styles is useful by Ben Frain (@benfrain) on CodePen.

Что происходит? Почему max-width не учитывается? Дам подсказку. Откройте отладчик и взгляните на панель «Вычисленные стили» («Computed Styles»).

Найдёте виновного?

Избавлю вас от неопределенности; по умолчания у fieldset есть вычисленная ширина, которая подстраивается под его содержимое. В Chrome это отображается в «Computed Styles» в качестве нового значения длины min-content в min-width.

Чтобы это исправить, можно добавить новое значение в свойство min-width. С min-width: 0 наше свойство max-width заработает, как надо.

Вот насколько полезной может оказаться панель «Computed Styles» в отладчике. Помните, что всё то, что вы написали, не обязательно должно вычисляться в браузере.

Заключение

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

  • Оценить баг и применить любое очевидное быстрое решение;
  • Локализовать проблему до важного кода, необходимого для воспроизведения;
  • Докопайтесь до причины, анализируя код отладчиком и просматривая известные баги данного браузера;
  • Исправить проблему с помощью более гибкого кода, или применить хак, снабдив его хорошим комментарием, или добавить в код ветку с исправлением бага для нужного браузера при необходимости.

P.S. Это тоже может быть интересно:


9 Jul, 2013 @ 1:51am

Помощь по css,баги,лаги,ошибки и т.д

ВАЖНОЕ:
Кароче говоря ахтунг сюда,1 способ который я написал,работает 100%,недавно было обновление и меня сново коснулась проблема с языком (английский в игре) и тем что игра выдает что то про “beta” вообщем заходите:steam-настройки-сразу снизу видите (Бета-тестирование) выбираете его,ждете обнову и вуаля все починилось,если хотите верни все обратно,а если нет играйте так,удачи.
———————————————————————————–
У КОГО НЕ РАБОТАЕТ КСС: Ставим в бета тест (это в настройках),далее играем минут 20-30 и меняем все обратно.то есть убираем обновлени и возвращаем обратно (если в бета тесте не будет показывать,что вы не в сети или не подключены,не паникуйте все так и должно быть),после этого должно все заработать.
У КОГО АНГЛ.ЯЗ. В КСС: 1)Если у вас есть кс nosteam (v34-v80 и так далее) то сразу же удаляйте ее,она причина всех ваших бед( можно даже 1 пункт пропустить,бывает даже помогает) 2)Провести обновление кеша (кстати после 1 пункта тоже можно ее сделать,чтобы файлы встали нормально) 3) удалите все моды скачанные на пабликах,миксах,трикзах,зомбимодах и т.д. и запустите кс
КАСАЕМО БАГОВ,ЛАГОВ,ТЕКСТУР,ВЫЛЕТОВ САМОЙ КСС: Самой большой проблемой являются-моды,которые вы сами ставите,это касается серверов,вылетов,лагов,не меняйте модели,ведь некоторые встают криво и все настройки сбиваются в cfg файлах и вас выкидывает на некоторых серверах.Что делать если не ищет сервера,зайдите в панель управления компьютером,находим брандмауэер,и есть 2 путя-легкий и сложный,легкий-выключить брандмауэр,тяжелый-нужно поставить исключение в нем и указать до файла css.exe или как назван ваш запускатор в папке.
Скоро найду новые решения проблем.Удачи.

Last edited by HANdSOLO;
10 Apr, 2014 @ 12:57pm

Date Posted: 9 Jul, 2013 @ 1:51am

Posts: 901

Добавить комментарий