Wie kann ein Iframe ohne Annahme eines Seitenverhältnisses reaktionsfähig gemacht werden?

14

Wie kann ein Iframe reagieren, ohne ein Seitenverhältnis anzunehmen? Beispielsweise kann der Inhalt eine beliebige Breite oder Höhe haben, die vor dem Rendern unbekannt ist.

Beachten Sie, dass Sie Javascript verwenden können.

Beispiel:

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

Größe dies iframe-containerPassen Sie die so an, dass der Inhalt ohne zusätzlichen Platz kaum hineinpasst. Mit anderen Worten, es ist genügend Platz für den Inhalt vorhanden, sodass er ohne Scrollen angezeigt werden kann, jedoch ohne zusätzlichen Platz. Der Behälter umhüllt den Iframe perfekt.

Dies zeigt, wie ein Iframe reagiert, wenn das Seitenverhältnis des Inhalts 16: 9 beträgt. In dieser Frage ist das Seitenverhältnis jedoch variabel.

ferit
quelle
1
Lassen Sie mich mit einer Bearbeitung @TravisJ
ferit
2
Es ist möglich, die Dimensionen des Inhalts im Iframe zu berechnen, aber wie genau dies erfolgen soll, hängt ein wenig vom Inhalt selbst und einem Großteil des verwendeten CSS ab (absolut positionierter Inhalt ist äußerst schwer zu messen). Wenn Sie eine CSS-Rücksetzdatei verwenden, unterscheidet sich das Ergebnis von der Seite, auf der die Rücksetzdatei nicht verwendet wird, und variiert auch zwischen den Browsern. Das Festlegen der Größe des Iframes und seines übergeordneten Elements ist trivial. Daher benötigen wir weitere Informationen zur Struktur der Seite, insbesondere den Namen der verwendeten CSS-Rücksetzdatei (en) (oder das tatsächliche CSS, wenn Sie keine allgemeinen Dateien verwenden).
Teemu
2
Haben Sie die Kontrolle über das im Iframe geladene Dokument? Ich meine, wenn Sie das nicht tun, können Sie nichts tun. Alles muss innerhalb des iframe-Dokuments (oder durch Zugriff auf dieses Dokument) erfolgen, andernfalls ist dies überhaupt nicht möglich.
Teemu
1
Nein, dann ist es nicht möglich, siehe developer.mozilla.org/en-US/docs/Web/Security/… . Die Hauptseite kann aus Sicherheitsgründen nicht auf ein domänenübergreifendes Dokument im Iframe zugreifen (und umgekehrt). Dies kann nur umgangen werden, wenn Sie die Kontrolle über das im Iframe angezeigte Dokument haben.
Teemu
1
... 15 Jahre Internet sagen, dass es unmöglich ist, ist nicht genug? Brauchen wir dazu wirklich eine neue Frage?
Kaiido

Antworten:

8

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.

Christos Lytras
quelle
1
Vielen Dank! Gute Antwort.
Ferit
1
Vielen Dank. Leider cors-anywhereist im Moment nach unten , so dass Sie nicht sehen können Demo - Seite. Ich möchte aus urheberrechtlichen Gründen keinen Screenshot der externen Website hinzufügen. Ich werde einen anderen CORS-Proxy hinzufügen, wenn dieser lange nicht funktioniert.
Christos Lytras
2

Es gibt viele Komplikationen bei der Ermittlung der Größe des Inhalts in einem Iframe, hauptsächlich aufgrund von CSS, das es Ihnen ermöglicht, Dinge zu tun, die die Messung der Inhaltsgröße beeinträchtigen können.

Ich habe eine Bibliothek geschrieben, die sich um all diese Dinge kümmert und auch domänenübergreifend arbeitet. Vielleicht finden Sie sie hilfreich.

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

David Bradshaw
quelle
1
Cool! Ich werde das überprüfen!
Ferit
0

Meine Lösung ist auf GitHub und JSFiddle .

Präsentation einer Lösung für reaktionsschnelle Iframes ohne Annahme eines Seitenverhältnisses.

Ziel ist es, die Größe des Iframes bei Bedarf zu ändern, dh wenn die Größe des Fensters geändert wird. Dies wird mit JavaScript durchgeführt, um die neue Fenstergröße zu erhalten und die Größe des Iframes zu ändern.

NB: Vergessen Sie nicht, die Größenänderungsfunktion nach dem Laden der Seite aufzurufen, da die Größe des Fensters nach dem Laden der Seite nicht von selbst geändert wird.

Der Code

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;

Ich habe einige Zeit damit verbracht. Ich hoffe es wird euch gefallen =)

Bearbeiten Dies ist wahrscheinlich die beste generische Lösung. Wenn der Iframe jedoch sehr klein gemacht wird, erhält er nicht die richtige Größe. Dies ist spezifisch für den von Ihnen verwendeten Iframe. Es ist nicht möglich, eine richtige Antwort für dieses Phänomen zu codieren, es sei denn, Sie haben den Code des Iframes, da Ihr Code nicht wissen kann, welche Größe vom Iframe bevorzugt wird, um richtig angezeigt zu werden.

Nur einige Hacks wie der von @Christos Lytras können den Trick machen, aber es wird nie für jeden Iframe funktionieren. Nur in einer bestimmten Situation.

Onyr
quelle
Danke für den Versuch! Es funktioniert jedoch nicht wie erwartet. Wenn der Inhalt von iframe klein ist, hat der Container noch zusätzlichen Speicherplatz. Siehe dies: jsfiddle.net/6p2suv07/3 Ich habe die src des iframe geändert.
Ferit
Ich bin mir nicht sicher, ob ich Ihr Problem mit "zusätzlichem Speicherplatz" verstehe. Die minimale Fenstergröße beträgt 100 px Breite. Der Iframe passt zum Container. Das Layout in Ihrem Iframe liegt nicht in meiner Hand. Ich habe deine Frage beantwortet. Bitte bearbeiten Sie Ihre, wenn Sie weitere Hilfe
benötigen
Setzen Sie ein Bild und beschreiben Sie es
Onyr
iframe nimmt den gesamten Speicherplatz ein, den der Container ihm zur Verfügung stellt. Wenn der iframe-Inhalt klein ist, benötigt er nicht den gesamten Speicherplatz, den er einnehmen kann. Der Container sollte also gerade genug Platz für den Iframe bieten, nicht weniger nicht mehr.
Ferit
Für die Höhe des Iframes liegt dies daran, dass die Höhe des Raums in meinem Beispiel durch das Fenster begrenzt ist. Das ist das? Ihr Beispiel ist spezifisch für den von Ihnen verwendeten Iframe. Ich habe eine allgemeine Antwort gegeben, die akzeptiert werden sollte. Natürlich ist es möglich, den Code so zu optimieren, dass er gut zu Ihrem
Iframe
-1

Machen Sie eine Sache, indem Sie dem Iframe-Container eine CSS-Eigenschaft für Breite und Höhe festlegen

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

.iframe-container iframe{
     width:100%;
     height:100%;
     object-fit:contain;
}
Aslam Khan
quelle
Was ist, wenn der Inhalt kleiner als 600 Pixel ist?
Ferit vor