Изменение размера iframe на основе контента
Я работаю над iGoogle-подобным приложением. Содержимое из других приложений (в других доменах) показано с использованием iframes.
Как изменить размеры iframes, чтобы соответствовать высоте содержимого iframes?
Я пытался расшифровать использование javascript Google, но он запутался, и поиск в Интернете до сих пор был бесплодным.
Обновление: Обратите внимание, что контент загружается из других доменов, поэтому применяется политика одинакового происхождения.
Ответы
Ответ 1
У нас была такая проблема, но немного в обратном направлении к вашей ситуации - мы предоставляли iframed контент сайтам в других доменах, поэтому та же самая политика происхождения также была проблемой. После многих часов тралового ловля Google, мы в конце концов нашли (несколько..) работоспособное решение, которое вы, возможно, сможете адаптировать к вашим потребностям.
Существует один путь к одной и той же политике происхождения, но он требует изменений как для iframed-контента, так и для страницы кадрирования, поэтому, если у вас нет возможности запрашивать изменения с обеих сторон, этот метод не будет очень полезен вам, боюсь.
В браузере, который позволяет обойти одну и ту же политику происхождения - javascript может связываться со страницами в своем собственном домене или со страницами, на которых он имеет iframed, но никогда не страницы, в которые он обрамлен, например. если у вас есть:
www.foo.com/home.html, which iframes
|-> www.bar.net/framed.html, which iframes
|-> www.foo.com/helper.html
то home.html
может связываться с framed.html
(iframed) и helper.html
(тот же домен).
Communication options for each page:
+-------------------------+-----------+-------------+-------------+
| | home.html | framed.html | helper.html |
+-------------------------+-----------+-------------+-------------+
| www.foo.com/home.html | N/A | YES | YES |
| www.bar.net/framed.html | NO | N/A | YES |
| www.foo.com/helper.html | YES | YES | N/A |
+-------------------------+-----------+-------------+-------------+
framed.html
может отправлять сообщения на helper.html
(iframed), но не home.html
(дочерний объект не может связывать кросс-домен с родителем).
Ключевым моментом здесь является то, что helper.html
может получать сообщения от framed.html
, а может также связываться с home.html
.
Таким образом, при загрузке framed.html
он выдает собственную высоту, сообщает helper.html
, которая передает сообщение на home.html
, которое затем может изменить размер iframe, в котором сидит framed.html
.
Самый простой способ передачи сообщений от framed.html
до helper.html
был через URL-адрес. Для этого framed.html
имеет iframe с указанным src=''
. Когда его onload
срабатывает, он вычисляет собственную высоту и устанавливает для src iframe в этой точке значение helper.html?height=N
Вот объяснение здесь того, как facebook обрабатывает его, что может быть немного яснее моего выше!
код
В www.foo.com/home.html
требуется следующий код javascript (это может быть загружено из файла .js в любом домене, кстати..):
<script>
// Resize iframe to full height
function resizeIframe(height)
{
// "+60" is a general rule of thumb to allow for differences in
// IE & and FF height reporting, can be adjusted as required..
document.getElementById('frame_name_here').height = parseInt(height)+60;
}
</script>
<iframe id='frame_name_here' src='http://www.bar.net/framed.html'></iframe>
В www.bar.net/framed.html
:
<body onload="iframeResizePipe()">
<iframe id="helpframe" src='' height='0' width='0' frameborder='0'></iframe>
<script type="text/javascript">
function iframeResizePipe()
{
// What the page height?
var height = document.body.scrollHeight;
// Going to 'pipe' the data to the parent through the helpframe..
var pipe = document.getElementById('helpframe');
// Cachebuster a precaution here to stop browser caching interfering
pipe.src = 'http://www.foo.com/helper.html?height='+height+'&cacheb='+Math.random();
}
</script>
Содержание www.foo.com/helper.html
:
<html>
<!--
This page is on the same domain as the parent, so can
communicate with it to order the iframe window resizing
to fit the content
-->
<body onload="parentIframeResize()">
<script>
// Tell the parent iframe what height the iframe needs to be
function parentIframeResize()
{
var height = getParam('height');
// This works as our parent parent is on our domain..
parent.parent.resizeIframe(height);
}
// Helper function, parse param from request string
function getParam( name )
{
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( results == null )
return "";
else
return results[1];
}
</script>
</body>
</html>
Ответ 2
Если вам не нужно обрабатывать содержимое iframe из другого домена, попробуйте этот код, он полностью устранит проблему и прост:
<script language="JavaScript">
<!--
function autoResize(id){
var newheight;
var newwidth;
if(document.getElementById){
newheight=document.getElementById(id).contentWindow.document .body.scrollHeight;
newwidth=document.getElementById(id).contentWindow.document .body.scrollWidth;
}
document.getElementById(id).height= (newheight) + "px";
document.getElementById(id).width= (newwidth) + "px";
}
//-->
</script>
<iframe src="usagelogs/default.aspx" width="100%" height="200px" id="iframe1" marginheight="0" frameborder="0" onLoad="autoResize('iframe1');"></iframe>
Ответ 3
https://developer.mozilla.org/en/DOM/window.postMessage
window.postMessage()
window.postMessage - это метод безопасного межсетевого обмена. Обычно сценарии на разных страницах разрешены только для доступа друг к другу, если и только если страницы, которые их выполняли, находятся в местах с одним и тем же протоколом (как правило, оба http), номер порта (80 по умолчанию для http) и хост (по модулю document.domain устанавливается обоими страницами на одно и то же значение). window.postMessage обеспечивает контролируемый механизм, чтобы обойти это ограничение таким образом, который является безопасным при правильном использовании.
Резюме
window.postMessage при вызове вызывает отправку MessageEvent в целевом окне, когда все ожидающие выполнения script, которые должны быть выполнены, завершаются (например, оставшиеся обработчики событий, если window.postMessage вызывается из обработчика события, ранее установленного ожидания ожидания и т.д.). MessageEvent имеет сообщение типа, свойство данных, которое устанавливается в строковое значение первого аргумента, предоставленного window.postMessage, свойство origin, соответствующее началу основного документа в окне, вызывающем window.postMessage в окне времени. вызывается postMessage и свойство source, которое является окном, из которого вызывается window.postMessage. (Другие стандартные свойства событий присутствуют с их ожидаемыми значениями.)
В библиотеке iFrame-Resizer используется postMessage, чтобы сохранить размер iFrame для него, а также MutationObserver для обнаружения изменений содержимого и не зависит от jQuery.
https://github.com/davidjbradshaw/iframe-resizer
jQuery: добросовестность междоменных скриптов
http://benalman.com/projects/jquery-postmessage-plugin/
Имеет демонстрацию изменения размера окна iframe...
http://benalman.com/code/projects/jquery-postmessage/examples/iframe/
В этой статье показано, как удалить зависимость от jQuery... Плюс имеет много полезной информации и ссылок на другие решения.
http://www.onlineaspect.com/2010/01/15/backwards-compatible-postmessage/
Пример Barebones...
http://onlineaspect.com/uploads/postmessage/parent.html
HTML 5 рабочий проект на window.postMessage
http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
Джон Ресиг о перекрестном обмене сообщениями
http://ejohn.org/blog/cross-window-messaging/
Ответ 4
Самый простой способ использования jQuery:
$("iframe")
.attr({"scrolling": "no", "src":"http://www.someotherlink.com/"})
.load(function() {
$(this).css("height", $(this).contents().height() + "px");
});
Ответ 5
Решение http://www.phinesolutions.com/use-jquery-to-adjust-the-iframe-height.html отлично работает (использует jQuery):
<script type="text/javascript">
$(document).ready(function() {
var theFrame = $("#iFrameToAdjust", parent.document.body);
theFrame.height($(document.body).height() + 30);
});
</script>
Я не знаю, что вам нужно добавить 30 к длине... 1 работал у меня.
FYI. Если у вас уже есть атрибут "height" на вашем iFrame, это просто добавляет style = "height: xxx". Возможно, это не то, что вы хотите.
Ответ 6
может быть немного поздно, так как все остальные ответы старше:-), но... вот мое решение. Протестировано в реальных FF, Chrome и Safari 5.0.
CSS
iframe {border:0; overflow:hidden;}
JavaScript:
$(document).ready(function(){
$("iframe").load( function () {
var c = (this.contentWindow || this.contentDocument);
if (c.document) d = c.document;
var ih = $(d).outerHeight();
var iw = $(d).outerWidth();
$(this).css({
height: ih,
width: iw
});
});
});
Надеюсь, это поможет любому.
Ответ 7
Вот простое решение с использованием динамически созданной таблицы стилей, обслуживаемой тем же сервером, что и содержимое iframe. Совсем просто таблица стилей "знает", что находится в iframe, и знает размеры, которые нужно использовать для стилирования iframe. Это оборачивается теми же политическими ограничениями.
http://www.8degrees.co.nz/2010/06/09/dynamically-resize-an-iframe-depending-on-its-content/
Таким образом, в поставляемом коде iframe будет прилагаться таблица стилей, подобная этому...
<link href="http://your.site/path/to/css?contents_id=1234&dom_id=iframe_widget" rel="stylesheet" type="text/css" />
<iframe id="iframe_widget" src="http://your.site/path/to/content?content_id=1234" frameborder="0" width="100%" scrolling="no"></iframe>
Это требует, чтобы логика на стороне сервера могла вычислять размеры отображаемого содержимого iframe.
Ответ 8
Наконец, я нашел другое решение для отправки данных на родительский сайт из iframe с помощью window.postMessage(message, targetOrigin);
. Здесь я объясняю, как я это сделал.
Сайт A = http://foo.com
Сайт B = http://bar.com
SiteB загружается на веб-сайте сайта.
Сайт сайта SiteB имеет эту строку
window.parent.postMessage("Hello From IFrame", "*");
или
window.parent.postMessage("Hello From IFrame", "http://foo.com");
Затем siteA имеет следующий код
// Here "addEventListener" is for standards-compliant web browsers and "attachEvent" is for IE Browsers.
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child IFrame window
eventer(messageEvent, function (e) {
alert(e.data);
// Do whatever you want to do with the data got from IFrame in Parent form.
}, false);
Если вы хотите добавить соединение безопасности, вы можете использовать это условие if в eventer(messageEvent, function (e) {})
if (e.origin == 'http://iframe.example.com') {
alert(e.data);
// Do whatever you want to do with the data got from IFrame in Parent form.
}
Для IE
Внутри IFrame:
window.parent.postMessage('{"key":"value"}','*');
Внешний вид:
eventer(messageEvent, function (e) {
var data = jQuery.parseJSON(e.data);
doSomething(data.key);
}, false);
Ответ 9
Этот ответ применим только для сайтов, которые используют Bootstrap. Чувствительная функция вставки Bootstrap выполняет эту работу. Он основан на ширине (не высоте) содержимого.
<!-- 16:9 aspect ratio -->
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item" src="http://www.youtube.com/embed/WsFWhL4Y84Y"></iframe>
</div>
jsfiddle: http://jsfiddle.net/00qggsjj/2/
http://getbootstrap.com/components/#responsive-embed
Ответ 10
Я внедряю решение Frame-in-frame от ConroyP для замены решения на основе установки document.domain, но обнаружил, что он довольно жестко определяет высоту содержимого iframe правильно в разных браузерах (тестирование с помощью FF11, Ch17 и IE9 прямо сейчас).
ConroyP использует:
var height = document.body.scrollHeight;
Но это работает только при загрузке начальной страницы. Мой iframe имеет динамический контент, и мне нужно изменить размер iframe на определенные события.
В результате я использовал разные свойства JS для разных браузеров.
function getDim () {
var body = document.body,
html = document.documentElement;
var bc = body.clientHeight;
var bo = body.offsetHeight;
var bs = body.scrollHeight;
var hc = html.clientHeight;
var ho = html.offsetHeight;
var hs = html.scrollHeight;
var h = Math.max(bc, bo, bs, hc, hs, ho);
var bd = getBrowserData();
// Select height property to use depending on browser
if (bd.isGecko) {
// FF 11
h = hc;
} else if (bd.isChrome) {
// CH 17
h = hc;
} else if (bd.isIE) {
// IE 9
h = bs;
}
return h;
}
getBrowserData() - функция обнаружения браузера, "вдохновленная" Ext Core http://docs.sencha.com/core/source/Ext.html#method-Ext-apply
Это хорошо работало для FF и IE, но в Chrome появились проблемы. Одной из проблем было время, по-видимому, Chrome занимает некоторое время, чтобы установить/определить высоту iframe. И тогда Chrome также никогда не вернул высоту содержимого в iframe правильно, если iframe был выше, чем содержимое. Это не будет работать с динамическим контентом при уменьшении высоты.
Чтобы решить эту проблему, я всегда устанавливаю iframe на невысокую высоту, прежде чем обнаруживать высоту содержимого, а затем устанавливая правильную величину высоты iframe.
function resize () {
// Reset the iframes height to a low value.
// Otherwise Chrome won't detect the content height of the iframe.
setIframeHeight(150);
// Delay getting the dimensions because Chrome needs
// a few moments to get the correct height.
setTimeout("getDimAndResize()", 100);
}
Код не оптимизирован, он из моего теста разработки:)
Надеюсь, что кто-то найдет это полезным!
Ответ 11
<html>
<head>
<script>
function frameSize(id){
var frameHeight;
document.getElementById(id).height=0 + "px";
if(document.getElementById){
newheight=document.getElementById(id).contentWindow.document.body.scrollHeight;
}
document.getElementById(id).height= (frameHeight) + "px";
}
</script>
</head>
<body>
<iframe id="frame" src="startframe.html" frameborder="0" marginheight="0" hspace=20 width="100%"
onload="javascript:frameSize('frame');">
<p>This will work, but you need to host it on an http server, you can do it locally. </p>
</body>
</html>
Ответ 12
TinyMCE делает это, и он не запутывается.
Ответ 13
iGoogle-гаджеты должны активно реализовывать изменение размера, поэтому я предполагаю, что в междоменной модели вы не можете сделать это без участия удаленного контента. Если ваш контент может отправить сообщение с новым размером на страницу контейнера с использованием типичных методов междоменной связи, то остальное просто.
Ответ 14
Если вы хотите уменьшить масштаб веб-страницы, чтобы поместить ее в размер iframe:
- Вы должны изменить размер iframe, чтобы он соответствовал содержанию
- Затем вы должны уменьшить весь iframe с загруженным содержимым веб-страницы.
Вот пример:
<div id="wrap">
<IFRAME ID="frame" name="Main" src ="http://www.google.com" />
</div>
<style type="text/css">
#wrap { width: 130px; height: 130px; padding: 0; overflow: hidden; }
#frame { width: 900px; height: 600px; border: 1px solid black; }
#frame { zoom:0.15; -moz-transform:scale(0.15);-moz-transform-origin: 0 0; }
</style>
Ответ 15
Здесь используется jQuery-подход, который добавляет информацию в json через атрибут src iframe. Здесь демо, измените размер и прокрутите это окно.. результирующий URL-адрес с json выглядит так...
http://fiddle.jshell.net/zippyskippy/RJN3G/show/# {docHeight: 5124, windowHeight: 1019, scrollHeight: 571} #
Здесь сценарий исходного кода http://jsfiddle.net/zippyskippy/RJN3G/
function updateLocation(){
var loc = window.location.href;
window.location.href = loc.replace(/#{.*}#/,"")
+ "#{docHeight:"+$(document).height()
+ ",windowHeight:"+$(window).height()
+ ",scrollHeight:"+$(window).scrollTop()
+"}#";
};
//setInterval(updateLocation,500);
$(window).resize(updateLocation);
$(window).scroll(updateLocation);
Ответ 16
получить высоту содержимого iframe, а затем передать его этому iframe
var iframes = document.getElementsByTagName("iframe");
for(var i = 0, len = iframes.length; i<len; i++){
window.frames[i].onload = function(_i){
return function(){
iframes[_i].style.height = window.frames[_i].document.body.scrollHeight + "px";
}
}(i);
}
Ответ 17
Работа с jquery при загрузке (кросс-браузер):
<iframe src="your_url" marginwidth="0" marginheight="0" scrolling="No" frameborder="0" hspace="0" vspace="0" id="containiframe" onload="loaderIframe();" height="100%" width="100%"></iframe>
function loaderIframe(){
var heightIframe = $('#containiframe').contents().find('body').height();
$('#frame').css("height", heightFrame);
}
при изменении размера на отзывчивой странице:
$(window).resize(function(){
if($('#containiframe').length !== 0) {
var heightIframe = $('#containiframe').contents().find('body').height();
$('#frame').css("height", heightFrame);
}
});
Ответ 18
Использование jQuery:
parent.html
<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<style>
iframe {
width: 100%;
border: 1px solid black;
}
</style>
<script>
function foo(w, h) {
$("iframe").css({width: w, height: h});
return true; // for debug purposes
}
</script>
<iframe src="child.html"></iframe>
</body>
child.html
<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(function() {
var w = $("#container").css("width");
var h = $("#container").css("height");
var req = parent.foo(w, h);
console.log(req); // for debug purposes
});
</script>
<style>
body, html {
margin: 0;
}
#container {
width: 500px;
height: 500px;
background-color: red;
}
</style>
<div id="container"></div>
</body>
Ответ 19
Это немного сложно, поскольку вам нужно знать, когда загружена страница iframe, что затруднительно, когда вы не контролируете ее содержимое. Его можно добавить обработчик onload в iframe, но я уже пробовал это в прошлом, и он имеет совершенно другое поведение в браузерах (не угадайте, кто самый раздражающий...). Вам, вероятно, придется добавить функцию на страницу iframe, которая выполняет изменение размера и введет некоторый script в контент, который либо слушает, чтобы загружать события, либо изменять размер событий, а затем вызывает предыдущую функцию. Я думаю добавить функцию на страницу, так как вы хотите убедиться в ее безопасности, но я не знаю, как легко это будет делать.
Ответ 20
Что-то в строках этой веры должно работать.
parent.document.getElementById(iFrameID).style.height=framedPage.scrollHeight;
Загрузите это с загрузкой вашего тела на содержимое iframe.
Ответ 21
У меня есть простое решение и вам нужно определить ширину и высоту в ссылке, попробуйте (он работает с большинством браузеров):
<a href='#' onClick=" document.getElementById('myform').src='t2.htm';document.getElementById('myform').width='500px'; document.getElementById('myform').height='400px'; return false">500x400</a>