JavaScript | Как дождаться загрузки iframe?

JavaScript | Как дождаться загрузки iframe?

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

Напомню, что атрибут принимает строку, которая должна являться частью синтаксиса HTML. Это может быть как полная разметка страницы, так и частичный код.

Смысл работы атрибута srcdoc в том, что мы получаем новый Документ для отображения в iframe. То есть строка из srcdoc будет преобразована в объектную модель документа.

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

<!DOCTYPE html>

<html lang=«ru»>

<head>

    <meta charset=«UTF-8»>

    <meta http-equiv=«X-UA-Compatible» content=«IE=edge»>

    <meta name=«viewport» content=«width=device-width, initial-scale=1.0»>

    <title>Страница с iframe</title>

</head>

<body>

    <script>

        let myIframe = document.createElement(‘iframe’);

        myIframe.srcdoc = `<p>Какой-то текст в предложении. Что-то ещё напишем</p>`;

        console.log(myIframe.contentDocument);

    </script>

</body>

</html>

Скриншот разметки:

HTML-разметка с элементом iframe
HTML-разметка с элементом iframe

 

Без встраивания элемента iframe на страницу текущего Документа, новый Документ не будет создаваться. А значит если мы сразу после присвоения строки для атрибута srcdoc попытаемся обратиться к свойству contentDocument, то нас постигнет неудача — мы получим null.

null - результат мгновенного обращения к свойству contentDocument после присваивания значения для srcdoc
null — результат мгновенного обращения к свойству contentDocument после присваивания значения для srcdoc

Если мы встроим элемент на страницу в браузере,

    <script>

        let myIframe = document.createElement(‘iframe’);

        myIframe.srcdoc = `<p>Какой-то текст в предложении. Что-то ещё напишем</p>`;

        document.body.append(myIframe);

        console.log(myIframe.contentDocument);

    </script>

то мы получим стандартную заглушку, без нашей разметки:

Документ не успел сформироваться. contentDocument возвращает пустую заглушку документа
Документ не успел сформироваться. contentDocument возвращает пустую заглушку документа

Как быть в этой ситуации?

 

Решение

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

Информация из справки:

Каждый документ Document имеет флаг прогресса загрузки iframe (iframe load in progress flag) и флаг отключения загрузки iframe (mute iframe load flag). При создании документа Document эти флаги должны быть сняты для этого документа Document.

Чтобы выполнить шаги события загрузки iframe (iframe load event steps), учитывая элемент элемента iframe:

1. Утверждено: вложенный контекст просмотра элемента element не равен null.
2. Пусть дочерний документ childDocument будет активным документом вложенного контекста просмотра элемента element.
3. Если для childDocument установлен флаг отключения загрузки iframe, возврат.
4. Установите флаг прогресса загрузки iframe для childDocument.
5. Запустите событие с именем load в элементе element.
6. Снять флаг прогресса загрузки iframe дочернего документа childDocument.
Каждый документ Document имеет флаг прогресса загрузки iframe (iframe load in progress flag) и флаг отключения загрузки iframe (mute iframe load flag)
Каждый документ Document имеет флаг прогресса загрузки iframe (iframe load in progress flag) и флаг отключения загрузки iframe (mute iframe load flag)

Из этого следует то, что у нас есть событие load, которое наступит сразу после загрузки нового Документа в содержимом в iframe. Нам останется просто перехватить его при помощи обработчика событий onload:

    <script>

        let myIframe = document.createElement(‘iframe’);

        myIframe.srcdoc = `<p>Какой-то текст в предложении. Что-то ещё напишем</p>`;

        document.body.append(myIframe);

        // Вешаем обработчик события onload на наш элемент iframe, который лежит в myIframe

        myIframe.onload = () => {

            // Вывод в консоль

            console.log(myIframe.contentDocument);

            // Или набор ваших действий с новым Документом

        }

    </script>

 

Скриншот из консоли:

Обработали событие загрузки нового Документа в iframe
Обработали событие загрузки нового Документа в iframe

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

В результате между работами функций появляются паузы, которые неизбежно замедляют работу всего сайта или приложения. Но скорости всё равно неподвластны мозгу обычного человека, поэтому для нас этот процесс по-прежнему похож на «мгновенный» (но это не так 🙂 ).

 

Информационные ссылки

HTML | Элемент iframe

Стандарт HTML — Раздел «4.8.5 The iframe element» — https://html.spec.whatwg.org/#the-iframe-element