วิธีการทำให้การตอบสนองของ iframe โดยไม่มีการสมมติอัตราส่วนกว้างยาว?


14

จะทำให้ iframe ตอบสนองได้อย่างไรโดยไม่ต้องสมมติอัตราส่วนกว้างยาว? ตัวอย่างเช่นเนื้อหาอาจมีความกว้างหรือความสูงซึ่งไม่เป็นที่รู้จักก่อนแสดงผล

หมายเหตุคุณสามารถใช้ Javascript

ตัวอย่าง:

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

ปรับขนาดนี้iframe-containerเพื่อให้เนื้อหาของมันพอดีกับภายในโดยไม่มีพื้นที่เพิ่มเติมกล่าวคือมีพื้นที่ว่างเพียงพอสำหรับเนื้อหาจึงสามารถแสดงได้โดยไม่ต้องเลื่อน แต่ไม่มีพื้นที่มากเกินไป คอนเทนเนอร์ล้อม iframe อย่างสมบูรณ์

สิ่งนี้แสดงวิธีการตอบสนอง iframe โดยสมมติว่าอัตราส่วนภาพของเนื้อหาคือ 16: 9 แต่ในคำถามนี้อัตราส่วนภาพแปรผัน


1
ให้ฉันอธิบายด้วยการแก้ไข @TravisJ
ferit

2
เป็นไปได้ที่จะคำนวณขนาดของเนื้อหาใน iframe แต่ควรทำอย่างไรขึ้นอยู่กับเนื้อหาของตัวเองและ CSS ที่ใช้เป็นจำนวนมาก (เนื้อหาที่วางตำแหน่งแน่นอนยากที่จะวัด) หากคุณใช้ไฟล์รีเซ็ต css ผลลัพธ์จะแตกต่างจากหน้าเว็บที่ไฟล์รีเซ็ตไม่ได้ใช้และแตกต่างกันระหว่างเบราว์เซอร์ การตั้งค่าขนาดของ iframe และพาเรนต์เป็นเรื่องเล็กน้อย ดังนั้นเราต้องการข้อมูลเพิ่มเติมเกี่ยวกับโครงสร้างของหน้าโดยเฉพาะชื่อของไฟล์รีเซ็ต CSS ที่ใช้แล้ว (หรือ CSS จริงถ้าคุณไม่ได้ใช้ไฟล์ทั่วไป)
Teemu

2
คุณมีการควบคุมเอกสารที่โหลดใน iframe หรือไม่? ฉันหมายความว่าถ้าคุณทำไม่ได้ไม่มีอะไรที่คุณสามารถทำได้ ทุกอย่างจะต้องทำภายในเอกสาร iframe (หรือโดยการเข้าถึงเอกสารนั้น) นอกจากนี้ยังเป็นไปไม่ได้เลย
Teemu

1
ไม่มีแล้วมันเป็นไปไม่ได้ดูdeveloper.mozilla.org/en-US/docs/Web/Security/... หน้าหลักไม่สามารถเข้าถึงเอกสารข้ามโดเมนใน iframe (และในทางกลับกัน) ด้วยเหตุผลด้านความปลอดภัยซึ่งสามารถหลีกเลี่ยงได้หากคุณสามารถควบคุมเอกสารที่แสดงใน iframe ได้
Teemu

1
... 15 ปีของอินเทอร์เน็ตบอกว่ามันเป็นไปไม่ได้ไม่พอหร เราต้องการคำถามใหม่เกี่ยวกับเรื่องนี้หรือไม่?
Kaiido

คำตอบ:


8

ไม่สามารถโต้ตอบกับ iFrame กำเนิดอื่นโดยใช้ Javascript เพื่อให้ได้ขนาด วิธีเดียวที่จะทำได้คือใช้window.postMessageกับtargetOriginชุดโดเมนของคุณหรือ wildchar *จากแหล่ง iFrame คุณสามารถพร็อกซีเนื้อหาของไซต์ต้นทางและการใช้งานต่างsrcdocๆ แต่ถือว่าเป็นการแฮกและมันจะไม่ทำงานกับ SPA และหน้าเว็บแบบไดนามิกอื่น ๆ อีกมากมาย

ขนาด iFrame กำเนิดเดียวกัน

สมมติว่าเรามี iFrames ต้นกำเนิดเดียวกันสองหนึ่งในความสูงสั้นและความกว้างคงที่:

<!-- 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>

และความสูงยาว 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>

เราสามารถรับขนาด iFrame ในloadเหตุการณ์โดยใช้iframe.contentWindow.documentสิ่งที่เราจะส่งไปที่หน้าต่างหลักโดยใช้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>

เราจะได้ความกว้างและความสูงที่เหมาะสมสำหรับทั้ง iFrames คุณสามารถตรวจสอบออนไลน์ได้ที่นี่และดูภาพที่นี่

การแฮ็กขนาด iFrame ต้นกำเนิดที่แตกต่างกัน( ไม่แนะนำให้ใช้ )

วิธีที่อธิบายไว้ที่นี่คือแฮ็คและควรใช้ถ้าจำเป็นจริงๆและไม่มีวิธีอื่น มันจะไม่ทำงานสำหรับหน้าและสปาที่สร้างขึ้นแบบไดนามิกส่วนใหญ่ วิธีการดึงหน้าซอร์สโค้ด HTML โดยใช้พร็อกซีเพื่อหลีกเลี่ยงนโยบาย CORS ( cors-anywhereเป็นวิธีที่ง่ายในการสร้างพร็อกซีเซิร์ฟเวอร์ CORS อย่างง่ายและมีการสาธิตออนไลน์https://cors-anywhere.herokuapp.com ) จากนั้นจะอัดรหัส JS ไปยัง HTML นั้นเพื่อใช้postMessageและส่งขนาด iFrame ไปที่เอกสารพาเรนต์ มันยังจัดการกับเหตุการณ์iFrame resize( รวมกับ iFramewidth: 100% ) และโพสต์ขนาด iFrame กลับไปที่ผู้ปกครอง

patchIframeHtml:

ฟังก์ชั่นการแก้ไขรหัส iFrame HTML และกำหนดเองฉีดจาวาสคริปต์ที่จะใช้postMessageในการส่งขนาด iFrame เพื่อแม่บนและload resizeหากมีค่าสำหรับoriginพารามิเตอร์<base/>องค์ประกอบHTML จะถูกนำไปใช้กับส่วนหัวโดยใช้ URL ต้นทางดังนั้น HTML URIs เช่น/some/resource/file.extนั้นจะถูกดึงข้อมูลอย่างถูกต้องโดย URL ต้นทางภายใน 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:

ฟังก์ชั่นที่จะได้รับเพจ HTML บายพาส CORS โดยใช้พร็อกซีหากuseProxyมีการตั้งค่าพารามิเตอร์ สามารถมีพารามิเตอร์เพิ่มเติมที่จะถูกส่งไปยังpostMessageเมื่อส่งข้อมูลขนาด

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();
  });
}

ฟังก์ชั่นเหตุการณ์จัดการข้อความตรงเช่นเดียวกับใน"ต้นกำเนิดเดียวกันขนาด iFrame"

ขณะนี้เราสามารถโหลดโดเมน cross origin ภายใน iFrame ด้วยการฉีดรหัส JS ที่กำหนดเองของเรา:

<!-- 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>

และเราจะทำให้ iFrame มีขนาดเท่ากับเนื้อหาเต็มความสูงโดยไม่มีการเลื่อนแนวตั้งแม้ใช้overflow-y: autoสำหรับเนื้อหาของ iFrame ( ควรเป็นoverflow-y: hiddenเช่นนั้นดังนั้นเราจึงไม่ได้รับการเลื่อนแถบเลื่อนเมื่อปรับขนาด )

คุณสามารถตรวจสอบออนไลน์ได้ที่นี่

อีกครั้งเพื่อแจ้งให้ทราบว่านี้คือการสับและมันควรจะหลีกเลี่ยง ; เราไม่สามารถเข้าถึงเอกสารCross-Origin iFrame หรือฉีดสิ่งใด ๆ


1
ขอบคุณ! คำตอบที่ดี
ferit

1
ขอบคุณ. น่าเสียดายcors-anywhereที่ตอนนี้คุณไม่สามารถดูหน้าตัวอย่างได้ ฉันไม่ต้องการเพิ่มภาพหน้าจอของไซต์ภายนอกด้วยเหตุผลด้านลิขสิทธิ์ ฉันจะเพิ่มพร็อกซี CORS อื่น ๆ ถ้ามันไม่ทำงานนาน
Christos Lytras

2

มีปัญหามากมายเกี่ยวกับการหาขนาดของเนื้อหาใน iframe ซึ่งส่วนใหญ่เป็นเพราะ CSS ทำให้คุณสามารถทำสิ่งต่าง ๆ ที่สามารถทำลายวิธีวัดขนาดของเนื้อหา

ฉันเขียนไลบรารีที่ดูแลสิ่งเหล่านี้ทั้งหมดและใช้งานข้ามโดเมนได้คุณอาจพบว่ามีประโยชน์

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


1
เย็น! จะตรวจสอบว่า!
ferit

0

วิธีการแก้ปัญหาของฉันอยู่บนGitHubและJSFiddle

นำเสนอโซลูชันสำหรับ iframe ที่ตอบสนองได้โดยไม่มีการสมมติอัตราส่วนกว้างยาว

เป้าหมายคือการปรับขนาด iframe เมื่อจำเป็นหรือกล่าวอีกนัยหนึ่งเมื่อปรับขนาดหน้าต่าง สิ่งนี้ถูกดำเนินการด้วย JavaScript เพื่อรับขนาดหน้าต่างใหม่และปรับขนาด iframe ตามลำดับ

หมายเหตุ: อย่าลืมเรียกฟังก์ชั่นปรับขนาดหลังจากโหลดหน้าเว็บเนื่องจากหน้าต่างไม่ได้ปรับขนาดด้วยตัวเองหลังจากโหลดหน้าเว็บ

รหัส

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;

ฉันใช้เวลานี้ ฉันหวังว่าคุณจะสนุกกับมัน =)

แก้ไข นี่น่าจะเป็นทางออกที่ดีที่สุด อย่างไรก็ตามเมื่อมีขนาดเล็กมาก iframe จะไม่ได้รับขนาดที่เหมาะสม นี่เป็นลักษณะเฉพาะของ iframe ที่คุณใช้ ไม่สามารถเขียนรหัสคำตอบที่เหมาะสมสำหรับปรากฏการณ์นี้ได้เว้นแต่คุณจะมีรหัสของ iframe เนื่องจากรหัสของคุณไม่มีทางรู้ว่าขนาดใดที่ iframe ต้องการให้แสดงอย่างเหมาะสม

แฮ็กบางอย่างเช่นที่นำเสนอโดย @Christos Lytras สามารถหลอกลวงได้ แต่จะไม่สามารถใช้ได้กับ iframe ทุกตัว ในสถานการณ์เฉพาะ


ขอบคุณที่ลอง! อย่างไรก็ตามมันไม่ทำงานตามที่คาดไว้ เมื่อเนื้อหาของ iframe มีขนาดเล็กคอนเทนเนอร์ยังคงมีพื้นที่เพิ่มเติม ดูสิ่งนี้: jsfiddle.net/6p2suv07/3ฉันเปลี่ยน src ของ iframe
ferit

ฉันไม่แน่ใจว่าจะเข้าใจปัญหาของคุณด้วย "พื้นที่พิเศษ" ขนาดต่ำสุดของหน้าต่างคือความกว้าง 100 px iframe เหมาะกับคอนเทนเนอร์ เค้าโครงภายใน iframe ของคุณไม่ได้อยู่ในการควบคุมของฉัน ฉันตอบคำถามของคุณ โปรดแก้ไขของคุณหากคุณต้องการความช่วยเหลือเพิ่มเติม
Onyr

วางภาพและอธิบายมัน
Onyr

iframe ใช้พื้นที่ทั้งหมดที่คอนเทนเนอร์มอบให้สิ่งนั้นคือเมื่อเนื้อหา iframe มีขนาดเล็กก็ไม่จำเป็นต้องใช้พื้นที่ทั้งหมดที่สามารถใช้ได้ ดังนั้นคอนเทนเนอร์ควรให้ที่ว่างเพียงพอสำหรับ iframe ไม่น้อยไปกว่านี้อีกแล้ว
ferit

สำหรับความสูงของ iframe นี่เป็นเพราะความสูงของพื้นที่ถูก จำกัด โดยหน้าต่างในตัวอย่างของฉัน นี่คืออะไร ตัวอย่างของคุณเฉพาะกับ iframe ที่คุณใช้ฉันให้คำตอบทั่วไปซึ่งควรเป็นที่ยอมรับ แน่นอนว่ามันเป็นไปได้ที่จะปรับแต่งโค้ดเพื่อให้เหมาะกับ iframe ของคุณ แต่สิ่งนี้จำเป็นต้องรู้รหัสที่อยู่เบื้องหลัง iframe
Onyr

-1

ทำสิ่งหนึ่งตั้งค่าภาชนะ iframe บางคุณสมบัติ CSS และความกว้างชุดความสูง

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

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

เกิดอะไรขึ้นถ้าเนื้อหามีขนาดเล็กกว่า 600px
ferit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.