Không thể tương tác với một iFrame gốc khác bằng Javascript để có được kích thước của nó; cách duy nhất để làm điều đó là bằng cách sử dụng window.postMessage
với targetOrigin
tập hợp vào miền của bạn hoặc ký tự đại diện *
từ nguồn iFrame. Bạn có thể ủy quyền nội dung của các trang web gốc khác nhau và sử dụng srcdoc
, nhưng đó được coi là hack và nó sẽ không hoạt động với các SPA và nhiều trang năng động khác.
Cùng kích thước iFrame gốc
Giả sử chúng ta có hai iFrames gốc, một chiều cao ngắn và chiều rộng cố định:
<!-- 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>
và một iFrame chiều cao dài:
<!-- 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>
Chúng tôi có thể nhận kích thước iFrame trên load
sự kiện bằng cách sử dụng iframe.contentWindow.document
mà chúng tôi sẽ gửi đến cửa sổ cha mẹ bằng cách sử dụng 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>
Chúng tôi sẽ có chiều rộng và chiều cao phù hợp cho cả iFrames; bạn có thể kiểm tra nó trực tuyến tại đây và xem ảnh chụp màn hình ở đây .
Hack kích thước iFrame nguồn gốc khác nhau ( không được đề xuất )
Phương pháp được mô tả ở đây là hack và nên được sử dụng nếu thực sự cần thiết và không còn cách nào khác; nó sẽ không hoạt động đối với hầu hết các trang và SPA được tạo động. Phương thức tìm nạp mã nguồn HTML của trang bằng proxy để bỏ qua chính sách CORS ( cors-anywhere
là một cách dễ dàng để tạo một máy chủ proxy CORS đơn giản và nó có bản demo trực tuyếnhttps://cors-anywhere.herokuapp.com
) sau đó đưa mã JS vào HTML đó để sử dụng postMessage
và gửi kích thước của iFrame vào tài liệu gốc. Nó thậm chí còn xử lý sự kiện iFrame resize
( kết hợp với iFramewidth: 100%
) và gửi kích thước iFrame trở lại cho phụ huynh.
patchIframeHtml
:
Một chức năng vá mã HTML iFrame và tiêm Javascript tùy chỉnh sẽ sử dụng postMessage
để gửi kích thước iFrame cho phụ huynh trên load
và trên resize
. Nếu có một giá trị cho origin
tham số, thì một <base/>
phần tử HTML sẽ được thêm vào phần đầu bằng cách sử dụng URL gốc đó, do đó, các URI HTML như /some/resource/file.ext
sẽ được tìm nạp chính xác bởi URL gốc bên trong 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
:
Một chức năng để có được một trang HTML bỏ qua CORS bằng proxy nếu useProxy
param được đặt. Có thể có các tham số bổ sung sẽ được chuyển đến postMessage
khi gửi dữ liệu kích thước.
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();
});
}
Hàm xử lý sự kiện thông báo hoàn toàn giống như trong "Kích thước iFrame gốc giống nhau" .
Bây giờ chúng tôi có thể tải một miền có nguồn gốc chéo bên trong iFrame với mã JS tùy chỉnh được chèn:
<!-- 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>
Và chúng ta sẽ nhận được iFrame để kích thước để nội dung của nó chiều cao đầy đủ mà không cần bất kỳ di chuyển dọc thậm chí sử dụng overflow-y: auto
cho cơ thể iFrame ( nó phải overflow-y: hidden
vì vậy chúng tôi không nhận thanh cuộn nhấp nháy đã đổi kích thước ).
Bạn có thể kiểm tra nó trực tuyến tại đây .
Một lần nữa để ý rằng đây là một hack và nó nên tránh ; chúng tôi không thể truy cập tài liệu iFrame chéo nguồn gốc hoặc tiêm bất kỳ loại điều nào.