Думаю, многие, кто сталкивался в своей работе с 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, но у кого-то, возможно, его не будет — тогда код все равно сохранит работоспособность.