Es ist nicht möglich, mit Javascript mit einem iFrame eines anderen Ursprungs zu interagieren, um dessen Größe zu ermitteln. Die einzige Möglichkeit, dies zu tun, besteht darin, window.postMessage
das targetOrigin
Set auf Ihre Domain oder den Wildchar *
aus der iFrame-Quelle zu verwenden. Sie können den Inhalt der verschiedenen Ursprungsseiten als Proxy verwenden und verwenden. Dies srcdoc
wird jedoch als Hack angesehen und funktioniert nicht mit SPAs und vielen anderen dynamischeren Seiten.
Gleiche iFrame-Größe
Angenommen, wir haben zwei iFrames gleichen Ursprungs, einen mit kurzer Höhe und fester Breite:
<!-- iframe-short.html -->
<head>
<style type="text/css">
html, body { margin: 0 }
body {
width: 300px;
}
</style>
</head>
<body>
<div>This is an iFrame</div>
<span id="val">(val)</span>
</body>
und ein langer iFrame:
<!-- iframe-long.html -->
<head>
<style type="text/css">
html, body { margin: 0 }
#expander {
height: 1200px;
}
</style>
</head>
<body>
<div>This is a long height iFrame Start</div>
<span id="val">(val)</span>
<div id="expander"></div>
<div>This is a long height iFrame End</div>
<span id="val">(val)</span>
</body>
Wir können die iFrame-Größe für ein load
Ereignis ermitteln, indem iframe.contentWindow.document
wir Folgendes an das übergeordnete Fenster senden postMessage
:
<div>
<iframe id="iframe-local" src="iframe-short.html"></iframe>
</div>
<div>
<iframe id="iframe-long" src="iframe-long.html"></iframe>
</div>
<script>
function iframeLoad() {
window.top.postMessage({
iframeWidth: this.contentWindow.document.body.scrollWidth,
iframeHeight: this.contentWindow.document.body.scrollHeight,
params: {
id: this.getAttribute('id')
}
});
}
window.addEventListener('message', ({
data: {
iframeWidth,
iframeHeight,
params: {
id
} = {}
}
}) => {
// We add 6 pixels because we have "border-width: 3px" for all the iframes
if (iframeWidth) {
document.getElementById(id).style.width = `${iframeWidth + 6}px`;
}
if (iframeHeight) {
document.getElementById(id).style.height = `${iframeHeight + 6}px`;
}
}, false);
document.getElementById('iframe-local').addEventListener('load', iframeLoad);
document.getElementById('iframe-long').addEventListener('load', iframeLoad);
</script>
Wir erhalten die richtige Breite und Höhe für beide iFrames. Sie können es hier online überprüfen und den Screenshot sehen hier sehen .
Hack mit iFrame-Größe unterschiedlicher Herkunft ( nicht empfohlen )
Die hier beschriebene Methode ist ein Hack und sollte verwendet werden, wenn dies absolut notwendig ist und es keinen anderen Weg gibt. Es funktioniert nicht für die meisten dynamisch generierten Seiten und SPAs. Die Methode ruft den HTML-Quellcode der Seite mithilfe eines Proxys ab, um die CORS-Richtlinie zu umgehen (dies cors-anywhere
ist eine einfache Möglichkeit, einen einfachen CORS-Proxyserver zu erstellen, und verfügt über eine Online-Demohttps://cors-anywhere.herokuapp.com
). Anschließend wird JS-Code in diesen HTML-Code eingefügt, postMessage
um die Größe des zu verwenden und zu senden iFrame zum übergeordneten Dokument. Es behandelt sogar das iFrame-Ereignis resize
( kombiniert mit iFramewidth: 100%
) und sendet die iFrame-Größe an das übergeordnete Element zurück.
patchIframeHtml
::
Eine Funktion , die iFrame HTML - Code und inject benutzerdefinierte Javascript flicken , die verwenden werden postMessage
auf die iFrame Größe an die Eltern zu senden load
und auf resize
. Wenn für den origin
Parameter ein Wert vorhanden ist , <base/>
wird dem Kopf unter Verwendung dieser Ursprungs-URL ein HTML- Element vorangestellt, sodass HTML-URIs wie /some/resource/file.ext
von der Ursprungs-URL im iFrame ordnungsgemäß abgerufen werden.
function patchIframeHtml(html, origin, params = {}) {
// Create a DOM parser
const parser = new DOMParser();
// Create a document parsing the HTML as "text/html"
const doc = parser.parseFromString(html, 'text/html');
// Create the script element that will be injected to the iFrame
const script = doc.createElement('script');
// Set the script code
script.textContent = `
window.addEventListener('load', () => {
// Set iFrame document "height: auto" and "overlow-y: auto",
// so to get auto height. We set "overlow-y: auto" for demontration
// and in usage it should be "overlow-y: hidden"
document.body.style.height = 'auto';
document.body.style.overflowY = 'auto';
poseResizeMessage();
});
window.addEventListener('resize', poseResizeMessage);
function poseResizeMessage() {
window.top.postMessage({
// iframeWidth: document.body.scrollWidth,
iframeHeight: document.body.scrollHeight,
// pass the params as encoded URI JSON string
// and decode them back inside iFrame
params: JSON.parse(decodeURIComponent('${encodeURIComponent(JSON.stringify(params))}'))
}, '*');
}
`;
// Append the custom script element to the iFrame body
doc.body.appendChild(script);
// If we have an origin URL,
// create a base tag using that origin
// and prepend it to the head
if (origin) {
const base = doc.createElement('base');
base.setAttribute('href', origin);
doc.head.prepend(base);
}
// Return the document altered HTML that contains the injected script
return doc.documentElement.outerHTML;
}
getIframeHtml
::
Eine Funktion zum useProxy
Abrufen eines Seiten-HTML- Codes , der das CORS mithilfe eines Proxys umgeht, wenn param festgelegt ist. Es können zusätzliche Parameter vorhanden sein, die postMessage
beim Senden von Größendaten an übergeben werden.
function getIframeHtml(url, useProxy = false, params = {}) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
// If we use a proxy,
// set the origin so it will be placed on a base tag inside iFrame head
let origin = useProxy && (new URL(url)).origin;
const patchedHtml = patchIframeHtml(xhr.responseText, origin, params);
resolve(patchedHtml);
}
}
// Use cors-anywhere proxy if useProxy is set
xhr.open('GET', useProxy ? `https://cors-anywhere.herokuapp.com/${url}` : url, true);
xhr.send();
});
}
Die Funktion zur Behandlung von Nachrichtenereignissen ist genau die gleiche wie unter "Gleiche Ursprungs-iFrame-Größe" .
Wir können jetzt eine Cross-Origin-Domain in einen iFrame laden, wobei unser benutzerdefinierter JS-Code eingefügt wird:
<!-- It's important that the iFrame must have a 100% width
for the resize event to work -->
<iframe id="iframe-cross" style="width: 100%"></iframe>
<script>
window.addEventListener('DOMContentLoaded', async () => {
const crossDomainHtml = await getIframeHtml(
'https://en.wikipedia.org/wiki/HTML', true /* useProxy */, { id: 'iframe-cross' }
);
// We use srcdoc attribute to set the iFrame HTML instead of a src URL
document.getElementById('iframe-cross').setAttribute('srcdoc', crossDomainHtml);
});
</script>
Und wir werden den iFrame auf die volle Höhe des Inhalts bringen, ohne dass ein vertikales Scrollen overflow-y: auto
erforderlich ist, selbst wenn der iFrame-Körper verwendet wird ( dies sollte overflow-y: hidden
so sein, dass beim Ändern der Größe keine Bildlaufleiste flackert ).
Sie können es hier online überprüfen .
Wieder zu bemerken, dass dies ein Hack ist und es vermieden werden sollte ; Wir können weder auf Cross-Origin iFrame-Dokumente zugreifen noch irgendwelche Dinge injizieren.