Дайджесты новостей
Современный векторный плакат демонстрирующий адаптацию высоты встраиваемого фрейма по высоте контента

Фреймы без костылей: нативная подгонка высоты iframe через свойство frame-sizing в Chromium

Каждый фронтенд-разработчик хоть раз в жизни проклинал технологию <iframe>. Представь типичную задачу: тебе нужно встроить на сайт форму оплаты от стороннего банка, виджет авторизации или блок опросов. Все эти сервисы живут на чужих доменах, а значит, в силу вступает фундаментальное правило браузерной безопасности — Same-Origin Policy.

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

Годами мы городили сложнейшие архитектурные мосты: внутри фрейма вешали ResizeObserver, слали замеры через window.parent.postMessage, на родительской странице парсили событие message и меняли iframe.style.height. Как мы отмечали, когда обсуждали Адаптивные iframe в Chrome 154: нативное свойство frame-sizing взамен костылей с postMessage, такой подход порождал вечные гонки рендеринга и прыжки макета.

Эксперт Ахмад Альфи (Ahmad Alfy) подробно исследовал, как новая спецификация CSS Box Sizing Module Level 4 кардинально меняет правила игры в браузерах на базе Chromium 154+.

На стороне родителя: одно свойство frame-sizing

Больше никакого JavaScript-кода для отслеживания габаритов на главной странице. Достаточно объявить для фрейма нативное свойство:

<iframe class="payment-widget" src="https://pay.example.com/checkout" title="Оплата"></iframe>

<style>
  .payment-widget {
    width: 100%;
    border: none;
    /* Нативная автоматическая подгонка высоты под поток документа */
    frame-sizing: content-height;
  }

  /* Безопасный фолбек для старых браузеров */
  @supports not (frame-sizing: content-height) {
    .payment-widget {
      min-height: 520px;
    }
  }
</style>

Браузерный движок сам связывает расчет геометрии дочернего документа с общим деревом компоновки (layout) страницы. При изменении ширины окна и переносе текста высота пересчитывается мгновенно, без задержек на межконтекстные сообщения.

На стороне встраиваемого фрейма: явное согласие безопасности

Но позволить любому сайту в интернете считывать габариты чужой страницы было бы дырой безопасности (риск атак timing-attack и утечки приватных интерфейсов). Поэтому встраиваемый документ должен явно дать согласие через новый метатег:

<!DOCTYPE html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <!-- Разрешаем доверенному домену родителя измерять высоту документа -->
  <meta name="responsive-embedded-sizing" content="allow-origins=https://shop.example.com">
  <title>Форма оплаты</title>
</head>
<body>
  <div class="checkout-form">
    <!-- Интерактивное содержимое -->
    <button onclick="expandAccordion()">Показать детали чека</button>
  </div>

  <script>
    function expandAccordion() {
      // При динамическом изменении DOM уведомляем движок о пересчете
      if ('requestResize' in window) {
        window.requestResize();
      }
    }
  </script>
</body>
</html>

Если твой виджет предназначен для публичной встройки на любые сайты, значение атрибута можно задать как allow-origins=*.

А если пользователь нажимает внутри фрейма на спойлер или валидатор формы выводит динамический список ошибок, скрипт вызывает легкий метод window.requestResize(), и движок браузера плавно адаптирует размер контейнера.

Критическое правило размещения метатега

Метатег <meta name="responsive-embedded-sizing"> обязан присутствовать в статическом исходном HTML-документе, который отдал сервер. Если попытаться создать и вставить его через JavaScript после загрузки DOM, движок безопасности Chromium проигнорирует директиву.

Пока Safari и Firefox дорабатывают реализацию в своих движках, прогрессивное улучшение через @supports (frame-sizing: content-height) позволяет уже сегодня избавить миллионы пользователей Chrome от скроллбаров внутри платежных виджетов.