Tidak mungkin berinteraksi dengan iFrame asal yang berbeda menggunakan Javascript sehingga untuk mendapatkan ukurannya; satu-satunya cara untuk melakukannya adalah dengan menggunakan window.postMessage
dengan targetOrigin
set ke domain Anda atau wildchar *
dari sumber iFrame. Anda dapat mem-proksi konten dari situs asal yang berbeda dan menggunakan srcdoc
, tetapi itu dianggap sebagai peretasan dan itu tidak akan berfungsi dengan SPA dan banyak halaman lain yang lebih dinamis.
Ukuran iFrame asal yang sama
Misalkan kita memiliki dua iFrames asal yang sama, satu dengan tinggi pendek dan lebar tetap:
<!-- 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>
dan iFrame tinggi panjang:
<!-- 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>
Kita bisa mendapatkan ukuran iFrame pada load
acara menggunakan iframe.contentWindow.document
yang akan kami kirim ke jendela induk menggunakan 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>
Kami akan mendapatkan lebar dan tinggi yang tepat untuk kedua iFrames; Anda dapat memeriksanya online di sini dan melihat tangkapan layar di sini .
Yang berbeda asal iFrame ukuran hack ( tidak direkomendasikan )
Metode yang dijelaskan di sini adalah peretasan dan harus digunakan jika benar-benar diperlukan dan tidak ada jalan lain; itu tidak akan berfungsi untuk sebagian besar halaman dan SPA yang dihasilkan dinamis. Metode ini mengambil halaman kode sumber HTML menggunakan proxy untuk memotong kebijakan CORS ( cors-anywhere
adalah cara mudah untuk membuat server proxy CORS sederhana dan memiliki demo onlinehttps://cors-anywhere.herokuapp.com
) lalu menyuntikkan kode JS ke HTML itu untuk menggunakan postMessage
dan mengirim ukuran file. iFrame ke dokumen induk. Ia bahkan menangani event iFrame resize
( dikombinasikan dengan iFramewidth: 100%
) dan memposting ukuran iFrame kembali ke induknya.
patchIframeHtml
:
Sebuah fungsi untuk menambal kode HTML iFrame dan menyuntikkan Javascript khusus yang akan digunakan postMessage
untuk mengirim ukuran iFrame ke orang tua terus load
dan terus resize
. Jika ada nilai untuk origin
parameter, maka <base/>
elemen HTML akan ditambahkan ke kepala menggunakan URL asal, dengan demikian, HTML URI seperti /some/resource/file.ext
akan diambil dengan benar oleh URL asal di dalam iFrame.
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
:
Sebuah fungsi untuk mendapatkan halaman HTML yang melewati CORS menggunakan proksi jika useProxy
param diatur. Mungkin ada parameter tambahan yang akan diteruskan ke postMessage
saat mengirim data ukuran.
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();
});
}
Fungsi pengendali acara pesan sama persis dengan "Ukuran asal yang sama iFrame" .
Kami sekarang dapat memuat domain asal silang di dalam iFrame dengan kode JS khusus kami yang disuntikkan:
<!-- 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>
Dan kita akan mendapatkan ukuran iFrame dengan isi penuh tanpa menggulir vertikal bahkan menggunakan overflow-y: auto
untuk tubuh iFrame ( seharusnya overflow-y: hidden
jadi kita tidak mendapatkan scrollbar berkedip pada ukuran ).
Anda dapat memeriksanya online di sini .
Sekali lagi untuk memperhatikan bahwa ini adalah peretasan dan harus dihindari ; kami tidak dapat mengakses dokumen iFrame Cross-Origin atau menyuntikkan segala hal.