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.postMessagedas targetOriginSet 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 srcdocwird 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 loadEreignis ermitteln, indem iframe.contentWindow.documentwir 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-anywhereist 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, postMessageum 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 postMessageauf die iFrame Größe an die Eltern zu senden loadund auf resize. Wenn für den originParameter ein Wert vorhanden ist , <base/>wird dem Kopf unter Verwendung dieser Ursprungs-URL ein HTML- Element vorangestellt, sodass HTML-URIs wie /some/resource/file.extvon 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 useProxyAbrufen eines Seiten-HTML- Codes , der das CORS mithilfe eines Proxys umgeht, wenn param festgelegt ist. Es können zusätzliche Parameter vorhanden sein, die postMessagebeim 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: autoerforderlich ist, selbst wenn der iFrame-Körper verwendet wird ( dies sollte overflow-y: hiddenso 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.