Отладка CSS
Перевод статьи 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? Если нет, кто из них понимает правильно? Если ошибка всплывает только в одном браузере, то стоит поискать среди известных багов для этого браузера.
Это известная проблема с браузером Х или конкретной версии браузера Х? Баг скоро исправят? Известны ли обходные пути, не влияющие на другие браузеры? В крайнем случае, можете добавить в код ветку с исправлением бага для нужного браузера?
Недавно я подробно описывал баги в браузерах, а Лиа Веру ещё в 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. Это тоже может быть интересно:
«ничто не сравнится с «удаленной» отладкой по кабелю»
На Windows можно через wi-fi отлаживать, если устройства в одной локальной сети, правда для Android-устройства надо получить root и будет чутка ощутимая задержка между устройством и десктопом в плане синхронизации, но зато удобней и не надо заморачиваться с драйверами.
В хроме работает, а вот в Лисе — нет.
В Хроме я нашёл во вкладочке «Computed» свойство min-width: min-content, в Лисе, во множестве «Скомпилированных стилей» нашёл min-width: 0, как я и поставил, по рекомендации в статье, но только почему-то Мозиле пофиг :)
Да, к сожалению, в Фоксе до сих пор не закрыт баг на эту тему.
А как понять ОТКУДА он берёт эти вычисленные стили? У меня на локальном компе в Лисе ширина абсолютно позиционированного блока 57 пикселей. Воспроизвести это в песочнице у меня не получается. В ней, вроде как, ширина считается по содержимому, что понятно. То есть самое длинное слово (Налейте,) плюс паддинги. Интересно, что если абс элемент не двигаешь, то ширина у него получается равной родителю. Можно, конечно, это и вручную задать, но интересно, как как она сама считает и как понять, как именно она считает…
Интересно, вот написал, а через некоторое время понял почему он (FF) так себя ведёт: он считает ширину по первому пункту списка (самое длинное слово + паддинги). Остальные пункты при этом, он, почему-то игнорит…