Совместимость с браузерами
Узнайте, как браузеры по-разному отображают HTML, CSS и JavaScript, и как писать кросс-браузерный код с помощью определения возможностей, CSS.supports, полифилов и прогрессивного улучшения.
Обеспечение совместимости с браузерами — критически важный аспект веб-разработки. Разные браузеры могут по-разному интерпретировать и отображать HTML, CSS и JavaScript, что приводит к несоответствиям в отображении и работе веб-страниц. В этом руководстве объясняется, почему возникают такие различия, как браузеры строят DOM, и описываются конкретные техники для написания кода, работающего везде: определение возможностей (в JavaScript и CSS), полифилы, прогрессивное улучшение и постепенное ухудшение.
Золотое правило, проходящее через всё это: определяйте возможности, а не браузеры. Проверять, что умеет браузер, — надёжно; проверять, каким браузером он представляется (через строку navigator.userAgent), — нет: строки user-agent легко подделываются, постоянно меняются и перестают работать при выходе новой версии браузера.
Понимание проблем совместимости
Почему возникают проблемы совместимости
Проблемы совместимости возникают потому, что разные браузеры используют разные движки рендеринга и реализуют возможности в разное время. Например, Chrome использует движок Blink, Firefox — Gecko, а Safari — WebKit. Каждый движок реализует одни и те же веб-стандарты, но новый API может появиться в одном движке на несколько месяцев раньше другого, а в старых или ограниченных браузерах (корпоративный IE, встроенные веб-просмотрщики, киоски) он может не появиться вообще. Результатом становятся различия как в рендеринге, так и в поведении JavaScript.
Типичные проблемы совместимости
- Различия в CSS-разметке: Разное толкование CSS браузерами приводит к различиям в макете и стилях.
- Функциональность JavaScript: Некоторые возможности JavaScript могут поддерживаться одним браузером, но не поддерживаться другими.
- Поддержка HTML5 и CSS3: Новые возможности HTML5 и CSS3 могут не поддерживаться одинаково во всех браузерах.
- Пробелы в DOM API: Метод узла DOM (например,
element.closest()илиelement.replaceChildren()) может отсутствовать в старых движках и вызыватьTypeErrorво время выполнения.
Как разные браузеры работают с DOM
Движки рендеринга браузеров
Каждый браузер использует собственный движок рендеринга для интерпретации и отображения веб-контента:
- Chrome и Edge: Blink
- Firefox: Gecko
- Safari: WebKit
Эти движки разбирают HTML, применяют CSS и выполняют JavaScript для построения и отображения DOM. Поскольку DOM — это живое представление в памяти, с которым работает JavaScript, любое различие в том, какие методы DOM предоставляет движок, напрямую влияет на то, какой код выполнится. Именно поэтому скрипты, работающие в одном браузере, могут выдавать ошибки в другом — проблема редко в «синтаксисе», чаще в том, что «этот метод здесь не существует».
Пример: работа с CSS Grid Layout
<!DOCTYPE html>
<html>
<head>
<title>CSS Grid Example</title>
<style>
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.item {
padding: 20px;
background-color: lightblue;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
</div>
</body>
</html>Этот пример демонстрирует базовый CSS Grid Layout. Хотя современные браузеры полностью поддерживают CSS Grid, Internet Explorer 11 поддерживает только более старую нестандартную версию спецификации, что может вызывать проблемы с макетом, если не использовать префиксы или полифилы.
Инструменты и техники для обеспечения кросс-браузерной совместимости
Инструменты тестирования
- Инструменты разработчика в браузере: Встроенные инструменты в браузерах, такие как Chrome DevTools, Firefox Developer Tools и Safari Web Inspector, помогают отлаживать и тестировать проблемы совместимости.
- Сервисы кросс-браузерного тестирования: Инструменты вроде BrowserStack и Sauce Labs позволяют тестировать сайт в разных браузерах и на разных устройствах.
Техники
- Используйте CSS-сбросы: Нормализуйте или сбрасывайте CSS для обеспечения единообразных стилей в разных браузерах.
- Полифилы и шимы: Используйте JavaScript-библиотеки, добавляющие недостающие возможности в старые браузеры.
- Прогрессивное улучшение: Сначала реализуйте основную функциональность, затем улучшайте её для более современных браузеров.
- Autoprefixer: Автоматически добавляйте вендорные префиксы к CSS-правилам для обеспечения совместимости с разными браузерами.
Использование определения возможностей
Введение в определение возможностей
Определение возможностей проверяет, поддерживает ли браузер ту или иную функцию перед её использованием, чтобы код мог перейти к запасному варианту вместо того, чтобы выдавать ошибку. Это современная альтернатива анализу user-agent.
Паттерн всегда одинаков: проверьте существование API, затем выберите путь выполнения.
// Detect a DOM/Web API: is the property or method actually there?
if ('clipboard' in navigator && navigator.clipboard.writeText) {
navigator.clipboard.writeText('copied!');
} else {
// Fallback for browsers without the async Clipboard API
console.log('Clipboard API not available — use a manual fallback');
}
// Detect a method on an element before calling it
const el = document.querySelector('.item');
if (el && typeof el.closest === 'function') {
el.closest('.container');
}К числу распространённых вещей, которые стоит проверять таким же образом, относятся: 'fetch' in window, 'IntersectionObserver' in window, 'localStorage' in window (завёрнутый в try/catch, так как приватный режим может выдавать ошибку) и 'serviceWorker' in navigator.
Определение поддержки CSS из JavaScript
Для CSS-возможностей браузеры предоставляют метод CSS.supports(). Он возвращает boolean, что позволяет принимать решения по стилизации в JavaScript:
if (window.CSS && CSS.supports('display', 'grid')) {
document.body.classList.add('has-grid');
} else {
document.body.classList.add('no-grid'); // ship a flexbox/float fallback
}
// You can also pass a full condition string:
CSS.supports('(gap: 1rem) and (display: flex)'); // true / falseИспользование Modernizr для определения возможностей
Modernizr — популярная JavaScript-библиотека, которая определяет поддержку функций HTML5 и CSS3 в браузере пользователя.
<!DOCTYPE html>
<html>
<head>
<title>Modernizr Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/3.12.1/modernizr.min.js"></script>
</head>
<body>
<div id="feature-check"></div>
<script>
if (Modernizr.canvas) {
document.getElementById('feature-check').textContent = 'Canvas is supported!';
} else {
document.getElementById('feature-check').textContent = 'Canvas is not supported.';
}
</script>
</body>
</html>В этом примере Modernizr используется для проверки поддержки браузером элемента HTML5 <canvas> и вывода соответствующего сообщения.
Для CSS можно использовать правило @supports, чтобы применять стили только при поддержке возможности:
.container {
display: flex;
}
@supports (display: grid) {
.container {
display: grid;
}
}Правило @supports — это CSS-аналог CSS.supports(): базовое объявление (display: flex) является запасным вариантом, а улучшенный макет применяется только там, где браузер понимает grid.
Полифилы и прогрессивное улучшение
Когда возможность отсутствует, существуют две стратегии, решающие разные проблемы:
- Полифил — загрузка небольшого скрипта, который добавляет недостающий API, чтобы обычный код работал без изменений. Используйте это, когда возможность необходима (например, старый браузер не поддерживает
fetch, поэтому вы загружаете полифил дляfetch). Недостаток — дополнительный вес, загружаемый каждым посетителем, если не загружать его условно. - Прогрессивное улучшение / постепенное ухудшение — создание работающей базовой версии, не требующей продвинутых возможностей, с последующим добавлением улучшений там, где они поддерживаются. Страница функционирует и без них.
Условная загрузка полифила снимает нагрузку с современных браузеров:
// Only fetch the polyfill if the browser actually needs it
if (!('IntersectionObserver' in window)) {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/intersection-observer.js';
document.head.appendChild(script);
}
// ... otherwise modern browsers pay nothing extraПоскольку определение возможностей и fetch API часто используются вместе, защищайте сетевой код таким же образом перед тем, как полагаться на него.
Лучшие практики
- Тестируйте рано и часто: Регулярно тестируйте веб-страницы в разных браузерах и на разных устройствах в процессе разработки.
- Используйте определение возможностей: Применяйте определение возможностей, чтобы сайт корректно работал во всех браузерах.
- Применяйте адаптивный дизайн: Используйте техники адаптивного дизайна, чтобы сайт хорошо выглядел на всех размерах экрана и при любой ориентации.
- Следите за изменениями в браузерах: Отслеживайте последние разработки и изменения в технологиях браузеров и веб-стандартах.
- Сначала проверяйте таблицу поддержки: Прежде чем принять API, найдите его на caniuse.com или MDN, чтобы узнать, какие браузеры его поддерживают и существует ли полифил.
Избегайте анализа user-agent (navigator.userAgent) для выбора пути выполнения кода. Строки UA поддаются подделке и постоянно меняются, поэтому определение незаметно ломается при выходе следующей версии браузера. Вместо этого определяйте саму возможность с помощью in, typeof, CSS.supports() или @supports.
Используйте определение возможностей (и библиотеки вроде Modernizr там, где они экономят время), чтобы сайт корректно обрабатывал неподдерживаемые возможности, предоставляя запасные варианты и единообразный опыт во всех браузерах.
Заключение
Обеспечение кросс-браузерной совместимости необходимо для предоставления единообразного пользовательского опыта. Понимая, почему браузеры различаются, тестируя рано в разных движках и определяя возможности вместо анализа браузеров — а затем подкрепляя определение полифилами или прогрессивным улучшением, — вы сможете создавать надёжные веб-приложения, работающие для всех.
Чтобы глубже изучить DOM, с которым вы обеспечиваете совместимость, ознакомьтесь со статьями Работа с DOM, Поиск: getElement, querySelector и Введение в события браузера.