Làm cách nào để tạo một iframe đáp ứng mà không cần giả định tỷ lệ khung hình?


14

Làm cách nào để tạo iframe đáp ứng mà không giả sử tỷ lệ khung hình? Ví dụ: nội dung có thể có bất kỳ chiều rộng hoặc chiều cao nào, không xác định trước khi kết xuất.

Lưu ý, bạn có thể sử dụng Javascript.

Thí dụ:

<div id="iframe-container">
    <iframe/>
</div>

Kích thước này iframe-containersao cho nội dung của nó vừa khít bên trong mà không có thêm không gian, nói cách khác, có đủ không gian cho nội dung để nó có thể được hiển thị mà không cần cuộn, nhưng không có không gian thừa. Container kết thúc hoàn hảo iframe.

Điều này cho thấy cách làm cho iframe phản hồi, giả sử tỷ lệ khung hình của nội dung là 16: 9. Nhưng trong câu hỏi này, tỷ lệ khung hình là thay đổi.


1
Hãy để tôi làm rõ với một chỉnh sửa @TravisJ
lên

2
Có thể tính toán kích thước của nội dung trong iframe, nhưng cách chính xác nên thực hiện, phụ thuộc một chút vào chính nội dung đó và rất nhiều CSS được sử dụng (nội dung được định vị tuyệt đối rất khó đo). Nếu bạn đang sử dụng tệp đặt lại css, kết quả sẽ khác với trang mà tệp đặt lại không được sử dụng và cũng khác nhau giữa các trình duyệt. Đặt kích thước của iframe và cha của nó là không đáng kể. Vì vậy, chúng tôi cần thêm thông tin về cấu trúc của trang, cụ thể là tên của (các) tệp đặt lại CSS đã sử dụng (hoặc CSS thực tế, nếu bạn không sử dụng bất kỳ tệp chung nào).
Teemu

2
Bạn có quyền kiểm soát tài liệu được tải trong iframe không? Ý tôi là, nếu bạn không, thì bạn không thể làm gì. Mọi thứ phải được thực hiện bên trong tài liệu iframe (hoặc bằng cách truy cập tài liệu đó), khác với điều này là không thể.
Teemu

1
Không, sau đó không thể, hãy xem developer.mozilla.org/en-US/docs/Web/Security/ mẹo . Trang chính không thể truy cập tài liệu tên miền chéo trong iframe (và ngược lại) vì lý do bảo mật, điều này chỉ có thể bị phá vỡ nếu bạn có quyền kiểm soát tài liệu được hiển thị trong iframe.
Teemu

1
... 15 năm internet nói rằng điều đó là không thể? Chúng ta có thực sự cần một câu hỏi mới về điều này?
Kaiido

Câu trả lời:


8

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.postMessagevới targetOrigintậ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 loadsự kiện bằng cách sử dụng iframe.contentWindow.documentmà 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-anywherelà 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 postMessagevà 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 loadvà trên resize. Nếu có một giá trị cho origintham 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.extsẽ đượ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 useProxyparam được đặt. Có thể có các tham số bổ sung sẽ được chuyển đến postMessagekhi 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: autocho cơ thể iFrame ( nó phải overflow-y: hiddenvì 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.


1
Cảm ơn! Câu trả lời chính xác.
men

1
Cảm ơn bạn. Thật không may cors-anywherelà hiện tại không hoạt động nên bạn không thể xem trang demo . Tôi không muốn thêm ảnh chụp màn hình của trang bên ngoài vì lý do bản quyền. Tôi sẽ thêm một proxy CORS khác nếu nó duy trì lâu.
Christos Lytras

2

Chúng có rất nhiều điều phức tạp khi xử lý kích thước của nội dung trong iframe, chủ yếu là do CSS cho phép bạn làm những việc có thể phá vỡ cách bạn đo kích thước nội dung.

Tôi đã viết một thư viện chăm sóc tất cả những thứ này và cũng hoạt động trên nhiều miền, bạn có thể thấy nó hữu ích.

https://github.com/davidjbradshaw/iframe-resizer


1
Mát mẻ! Gonna kiểm tra xem!
men

0

Giải pháp của tôi là trên GitHubJSFiddle .

Trình bày một giải pháp cho iframe đáp ứng mà không cần giả định tỷ lệ khung hình.

Mục tiêu là thay đổi kích thước iframe khi cần thiết, nói cách khác khi cửa sổ được thay đổi kích thước. Điều này được thực hiện với JavaScript để có được kích thước cửa sổ mới và thay đổi kích thước iframe.

Lưu ý: Đừng quên gọi chức năng thay đổi kích thước sau khi trang được tải do cửa sổ không tự thay đổi kích thước sau khi tải trang.

Mật mã

index.html

<!DOCTYPE html>
<!-- Onyr for StackOverflow -->

<html>

<head> 
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <title>Responsive Iframe</title>
    <link rel="stylesheet" type="text/css" href="./style.css">
</head>

<body id="page_body">
    <h1>Responsive iframe</h1>

    <div id="video_wrapper">
        <iframe id="iframe" src="https://fr.wikipedia.org/wiki/Main_Page"></iframe>
    </div>

    <p> 
        Presenting a solution for responsive iframe without aspect
        ratio assumption.<br><br>
    </p>

    <script src="./main.js"></script>
</body>

</html>

style.css

html {
    height: 100%;
    max-height: 1000px;
}

body {
    background-color: #44474a;
    color: white;
    margin: 0;
    padding: 0;
}

#videoWrapper {
    position: relative;
    padding-top: 25px;
    padding-bottom: 100px;
    height: 0;
    margin: 10;
}
#iframe {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

main.js

let videoWrapper = document.getElementById("video_wrapper");

let w;
let h;
let bodyWidth;
let bodyHeight;

// get window size and resize the iframe
function resizeIframeWrapper() {
    w = window.innerWidth;
    h = window.innerHeight;

    videoWrapper.style["width"] = `${w}px`;
    videoWrapper.style["height"] = `${h - 200}px`;
}

// call the resize function when windows is resized and after load
window.onload = resizeIframeWrapper;
window.onresize = resizeIframeWrapper;

Tôi đã dành một chút thời gian cho việc này. Tôi hy vọng bạn sẽ thích nó =)

Chỉnh sửa Đây có lẽ là giải pháp chung tốt nhất. Tuy nhiên, khi được thực hiện rất nhỏ, iframe không được cung cấp kích thước phù hợp. Điều này là cụ thể cho iframe bạn đang sử dụng. Không thể mã một câu trả lời thích hợp cho hiện tượng này trừ khi bạn có mã của iframe, vì mã của bạn không có cách nào để biết kích thước nào được ưa thích bởi iframe được hiển thị đúng.

Chỉ một số hack như được trình bày bởi @Christos Lytras có thể tạo ra mánh khóe nhưng nó sẽ không bao giờ hoạt động cho mọi iframe. Chỉ trong một tình huống cụ thể.


Cảm ơn bạn đã cố gắng! Tuy nhiên, nó không hoạt động như mong đợi. Khi nội dung của iframe nhỏ, container vẫn có thêm dung lượng. Xem điều này: jsfiddle.net/6p2suv07/3 Tôi đã thay đổi src của iframe.
men

Tôi không chắc chắn để hiểu vấn đề của bạn với "không gian thêm". Kích thước tối thiểu của cửa sổ là chiều rộng 100 px. Iframe phù hợp với container. Bố cục bên trong iframe của bạn không nằm trong tầm kiểm soát của tôi. Tôi đã trả lời câu hỏi của bạn. Vui lòng chỉnh sửa nếu bạn muốn được trợ giúp thêm
Onyr

Đặt một hình ảnh và mô tả nó
Onyr

iframe lấy tất cả không gian mà container cung cấp cho nó, điều này là, khi nội dung iframe nhỏ, nó không yêu cầu tất cả không gian mà nó có thể chiếm. Vì vậy, container nên cung cấp đủ không gian cho iframe, không hơn không kém.
men

Đối với chiều cao của iframe, điều này là do chiều cao của không gian bị giới hạn bởi cửa sổ trong ví dụ của tôi. Đây là cái này à? Ví dụ của bạn là cụ thể cho iframe bạn sử dụng, tôi đã đưa ra một câu trả lời chung cần được chấp nhận. Tất nhiên, có thể điều chỉnh mã sao cho phù hợp với iframe của bạn nhưng điều này đòi hỏi phải biết mã đằng sau iframe
Onyr

-1

Do một điều đặt bộ chứa Iframe một số thuộc tính CSS chiều rộng và chiều cao

.iframe-container{
     width:100%;
     min-height:600px;
     max-height:100vh;
}

.iframe-container iframe{
     width:100%;
     height:100%;
     object-fit:contain;
}

Nếu nội dung nhỏ hơn 600px thì sao?
men
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.