Думаю, многие, кто сталкивался в своей работе с iframe, сталкивались и с задачей установки высоты этого самого айфрейма.
Это может быть необходимо, например, когда ты хочешь дать возможность пользователям ставить виджеты с вашего сайта на их сайт, и хочется, чтобы размер контейнера (iframe) виджета соответствовал размерам содержимого этого виджета.
В яндексе можно найти множество решений этой проблемы, но большинство из них обладают одной проблемой: они не поддерживают возможность менять размеры окна когда содержимое iframe и родительский элемент находятся на разных доменах.
Есть одно неплохое кросс-доменное решение, но оно было написано в 2007 году, а с тех пор многое изменилось. Поэтому пришлось разрабатывать решение этой проблемы самостоятельно, основываясь на приведенном решении.
Собственно, основное, что поменялось — это появился инструмент window.postMessage, который позволяет окнам браузера обмениваться сообщениями. Благодаря этому мы сможем сделать аналог приведенного решения, обладающий следующими преимуществами:
Наш код не должен зависеть от каких-то внешних факторов, и для его использования должно быть достаточно на стороне содержимого фрейма подключить 2 js файла (один для postMessage, а ворой — наш), а на стороне клиента — подключить также 2 js файла и добавить айфрейму два аттрибута — id и name (должны быть идентичны), а также на onload повесить регистрацию iframe.
Для работы с postmessage мы будем использовать следующую библиотеку:
postmessage.freebaseapps.com
Эта библиотека является очень удобной оберткой для window.postMessage, которая, кроме прочего, также позволяет пользоваться передачей данных через hash в браузерах, которые не поддерживают window.postMessage.
Логика работы скрипта предельно проста — на клиентской странице мы «регистрируем» iframe — отправляем дочерней странице (которая в iframe) идентификатор этого iframe и подписываемся на сообщения от дочерней страницы. При получении сообщения — изменяем размер.
На стороне дочерней страницы (которая в iframe) мы в свою очередь подписываемся на сообщения о регистрации, когда оно приходит — запоминаем идентификатор и включаем таймер, который при изменении размера содержимого фрейма отправляет родительской странице информацию о том, что нужно поменять размер iframe.
Собственно вот и все. Сам код скриптов:
FrameManager.js («клиентский» скрипт)
Frame.js (скрипт для содержимого фрейма)
И напишем 2 тестовых html-страницы:
test.html (родительская)
test2.html (дочерняя)
В дочерней странице также можно расскомментировать участок кода с подключением jquery — тогда определение высоты будет более точным. Буду благодарен за нормальные кросс-браузерные функции для определения размеров страницы и окна, я использовал первые, которые нашел — они работают, но пикселей на 10 в вебките допускают погрешность.
Для меня это некритично, потому что у меня на стороне клиента (т.е. в дочерней странице) есть jquery, но у кого-то, возможно, его не будет — тогда код все равно сохранит работоспособность.
Это может быть необходимо, например, когда ты хочешь дать возможность пользователям ставить виджеты с вашего сайта на их сайт, и хочется, чтобы размер контейнера (iframe) виджета соответствовал размерам содержимого этого виджета.
В яндексе можно найти множество решений этой проблемы, но большинство из них обладают одной проблемой: они не поддерживают возможность менять размеры окна когда содержимое iframe и родительский элемент находятся на разных доменах.
Есть одно неплохое кросс-доменное решение, но оно было написано в 2007 году, а с тех пор многое изменилось. Поэтому пришлось разрабатывать решение этой проблемы самостоятельно, основываясь на приведенном решении.
Собственно, основное, что поменялось — это появился инструмент window.postMessage, который позволяет окнам браузера обмениваться сообщениями. Благодаря этому мы сможем сделать аналог приведенного решения, обладающий следующими преимуществами:
- В новых браузерах не будет спама в hash (и заодно будет корректно работать хеш-навигация на клиентской странице)
- корректная работа в webkit
- более простой и понятный код
Наш код не должен зависеть от каких-то внешних факторов, и для его использования должно быть достаточно на стороне содержимого фрейма подключить 2 js файла (один для postMessage, а ворой — наш), а на стороне клиента — подключить также 2 js файла и добавить айфрейму два аттрибута — id и name (должны быть идентичны), а также на onload повесить регистрацию iframe.
Для работы с postmessage мы будем использовать следующую библиотеку:
postmessage.freebaseapps.com
Эта библиотека является очень удобной оберткой для window.postMessage, которая, кроме прочего, также позволяет пользоваться передачей данных через hash в браузерах, которые не поддерживают window.postMessage.
Логика работы скрипта предельно проста — на клиентской странице мы «регистрируем» iframe — отправляем дочерней странице (которая в iframe) идентификатор этого iframe и подписываемся на сообщения от дочерней страницы. При получении сообщения — изменяем размер.
На стороне дочерней страницы (которая в iframe) мы в свою очередь подписываемся на сообщения о регистрации, когда оно приходит — запоминаем идентификатор и включаем таймер, который при изменении размера содержимого фрейма отправляет родительской странице информацию о том, что нужно поменять размер iframe.
Собственно вот и все. Сам код скриптов:
FrameManager.js («клиентский» скрипт)
var FrameManager = { registerFrame : function(frame) { pm({ target: window.frames[frame.id], type: "register", data: {id:frame.id}, url: frame.contentWindow.location }); pm.bind(frame.id, function(data) { var iframe = document.getElementById(data.id); if (iframe == null) return; iframe.style.height = (data.height+12).toString() + "px"; }); } };
Frame.js (скрипт для содержимого фрейма)
var FrameHeightManager = { FrameId: '', getCurrentHeight : function() { myHeight = 0; if( typeof( window.innerWidth ) == 'number' ) { myHeight = window.innerHeight; } else if( document.documentElement && document.documentElement.clientHeight ) { myHeight = document.documentElement.clientHeight; } else if( document.body && document.body.clientHeight ) { myHeight = document.body.clientHeight; } return myHeight; }, publishHeight : function() { if (this.FrameId == '') return; // если нет jQuery - воспользуемся решениями для определения размеров из яндекса if(typeof jQuery === "undefined") { var actualHeight = (document.body.scrollHeight > document.body.offsetHeight)?document.body.scrollHeight:document.body.offsetHeight; var currentHeight = this.getCurrentHeight(); } else { var actualHeight = $("body").height(); var currentHeight = $(window).height(); } if(Math.abs(actualHeight - currentHeight) > 20) { pm({ target: window.parent, type: this.FrameId, data: {height:actualHeight, id:this.FrameId} }); } } }; pm.bind("register", function(data) { FrameHeightManager.FrameId = data.id; // не забываем передать правильный this window.setInterval(function() {FrameHeightManager.publishHeight.call(FrameHeightManager)}, 300); });
И напишем 2 тестовых html-страницы:
test.html (родительская)
<!DOCTYPE html> <html> <body> <script src="postmessage.js"></script> <script src="FrameManager.js"></script> <iframe height="10" id="frame1" name="frame1" src="test2.html" onload="FrameManager.registerFrame(this)" scrolling="no" frameborder="0" marginheight="0" marginwidth="0" ></iframe> </body> </html>
test2.html (дочерняя)
<!DOCTYPE html> <html> <body> <!--<script src="http://yandex.st/jquery/1.7.1/jquery.min.js"></script>--> <script src="postmessage.js"></script> <script src="Frame.js"></script> <div style="border:1px solid red;margin:0; height:200px;"> test </div> </body> </html>
В дочерней странице также можно расскомментировать участок кода с подключением jquery — тогда определение высоты будет более точным. Буду благодарен за нормальные кросс-браузерные функции для определения размеров страницы и окна, я использовал первые, которые нашел — они работают, но пикселей на 10 в вебките допускают погрешность.
Для меня это некритично, потому что у меня на стороне клиента (т.е. в дочерней странице) есть jquery, но у кого-то, возможно, его не будет — тогда код все равно сохранит работоспособность.
