Оперативное изменение Height в iFrame через postMessage.

Установка высоты iFrame в родителе в зависимости от изменения контента дочерней страницы.


В некоторых ситуациях необходимо поместить на основной веб странице дополнительную страницу со своей структурой документа. Так же это могут документы PDF, видео и интерактивные медиа файлы, отдельные приложения или ДЕМО страницы. Встроить в разметку основной страницы элемент iFrame и задать ему начальные параметры сложностей не вызывает. Можно задать параметры через стиль и управлять фреймом в зависимости от ситуации на главной странице. С шириной фрейма обычно проблем не возникает так как в большинстве случаев для него отводят всю ширину родительского блока, а внутренней контент фрейма адаптируется согласно текущей ширине его окна. Но что делать с высотой? Заданная изначально высота фрейма не изменяется в зависимости от структуры документа внутри него. Если он выходит за нижнюю границу фрейма, то появляется полоса прокрутки если не запрещена в установке его параметров, а если задать высоту с запасом, то остаётся довольно большой отступ до следующего блока. Особенно эта коллизия проявляется в мобильных версиях, когда экран меняет своё положение. К тому же контент внутренней страницы может быть динамичным и в процессе работы тоже менять свою высоту. Встаёт задача оперативно изменять высоту фрейма по событию изменения высоты внутреннего документа.

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

Для начала нужно после загрузки внутренней страницы фрейма определить стартовую высоту документа. Затем записать это значение в Cookie для передачи данных в родительский документ. Для этого используем такой код:

JS
$(document).ready(function () {
//Определяем полную высоту документа
var Hdok = $(document).outerHeight(true);

//записываем значение высоты в cookie
document.cookie = "Hdok="+Hdok;
});

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

Добавляем в код обработчик события изменения размера документа window.addEventListener('resize', start) , а для отправки сообщения родительскому документу о смене значений высоты используем метод window.postMessage().

Метод window.postMessage() безопасно обеспечивает связь между объектами из разных источников: например, между страницей и всплывающим окном, которое она создала, или между страницей и iframe, встроенным в нее. Подробнее.

JS
top.postMessage("Hdok" + "=" + Hdok, '/test/test.html');
//Функция отправки сообщений
//Первый параметр: строка сообщения
//Второй параметр:
//  домен - "*");
//  папка-"/demo/;
//  страница назначения: "index.html"

Перестраиваем код на дочерней странице:

JS
top.postMessage("Hdok" + "=" + Hdok, '/test/test.html');
//Функция отправки сообщений
//Первый параметр: строка сообщения
//Второй параметр:
//  домен - "*");
//  папка-"/demo/;
//  страница назначения: "index.html"

Ставим слушатель сообщений на основной странице:

JS
$(document).ready(function () {
//Получаем сообщение из фрейма
window.addEventListener("message", function(e){
    //Разбиваем по разделителю
    var dataE = e.data.split('=', 2);
            //Проверяем откуда пришёл
            if (e.origin !== 'https://test.com') { return false; }
            //Проверяем на соответствие
            if (dataE[0] == "demo") {
                //Задаем высоту фрейма
                $('iframe[name=demo]').height(dataE[1]);
            }
           },
          false);
});

Последний код вполне рабочий, но можно и поусложнять при желании angel.

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

  1. Дочерний документ. Получаем случайную цифровую последовательность и записываем в куке в качестве проверочного ключа.
  2. Дочерний документ. После загрузки страницы и в случае изменения размеров документа отправляем методом postMessage строку: ключ + разделитель + Height документа.
  3. Родительская страница. Получаем данные. Разбиваем по разделителю. Проверяем ключ на соответствие значений из сообщения и куки. Если все правильно, задаём высоту фрейма.

JS
$(document).ready(function () {
//Создаём ключ и записываем в Cookie
var demoKl = Math.random();
document.cookie = "demoKl="+demoKl;
//Посылаем данные на родитель после загрузки
start();
//Посылаем данные на родитель из слушателя событий
window.addEventListener('resize', start);
});

//Сообщение к родительской стороне
function start(){
  //Определяем полную высоту документа
  var Hdok = $(document).outerHeight(true);
  //Отправка данных в сообщении родителю
  top.postMessage(getCookie('demoKl')+"|"+Hdok, '*');
}

Функцию получения куки по имени ключа вынесу отдельно (не забываем подставлять в код):

function getCookie(name) { var matches = document.cookie.match(new RegExp("(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)")); return matches ? decodeURIComponent(matches[1]) : undefined; }

На родительской странице ставим слушатель сообщений от дочернего документа.

JS
$(document).ready(function () {
//Получаем сообщение из фрейма
window.addEventListener("message", function(e){
    //Разбиваем по разделителю
    var dataE = e.data.split('|', 2);
            //Проверяем на соответствие
            if (dataE[0] == getCookie('demoKl')) {
                //Задаем высоту фрейма
                $('iframe[name=demo]').height(dataE[1]);
            }
           },
          false);
});

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

JS
$(document).ready(function () {
//Вызов одноразовой функции
func_one();
//Посылаем данные на родитель после загрузки
start();
//Посылаем данные на родитель из обработчика события 'resize'
window.addEventListener('resize', start);
//Ещё один обработчик события для верности
var elem = $('body')[0];
let resizeObserver = new ResizeObserver(() => {
    start();
    });
    resizeObserver.observe(elem);
});

//Функция отправки сообщения к родительской стороне
function start(){
  //Определяем полную высоту документа
  var Hdok = $(document).outerHeight(true);
  //Отправка данных в сообщении родителю
  top.postMessage(getCookie('demoKl')+"|"+Hdok, '*');
}

//Одноразовая функция. Создаём ключ и записываем в Cookie
function func_one() {document.cookie = "demoKl="+Math.random(); func_one = () => {};}

Дата публикации: 

ТОП 10 случайных публикаций



Сайт разработан студией © WEB-VidST   


Яндекс.Метрика