Responsive iFrames

Responsive Retrofitting Teil 3

Ein Blogpost der nunmehr 3 Jahre auf Vollendung wartet, ist jetzt endlich fertig :)

Responsive iFrame? Das klingt schon falsch und ist auch ein schwieriges Pflaster. Grundsätzlich können iFrames responsive sein, Voraussetzung ist natürlich, dass sich deren Inhalt ebenfalls responsive verhält. Es ist ja quasi ein Browserfenster im Browserfenster. Doch die Anpassung der Breite ist nicht das Problem, die Anpassung der Höhe macht es kompliziert.

Prominentes Beispiel: YouTube Video responsive einbinden

Der YouTube-iFrame soll folgendermaßen eingebunden werden:

<iframe width="560" height="315" src="https://www.youtube.com/embed/9T7eSyo7DRU" frameborder="0" allowfullscreen></iframe>

Die Breite des iFrame überschreibt man durch CSS wie auch bei Responsive Images:

img,
iframe {
  max-width: 100%;
}

Die Breite passt sich immer dem Container an. Die Höhe wird leider nicht automatisch angepasst, im Beispiel bleibt die Höhe also bei 315px. Die Regel height: auto; hat hier leider keinen Einfluss auf unseren iFrame. Das Problem bleib die relationale Höhenanpassung des iFrames, wenn sich die Breite verändert. Für das reponsive iFrame Video behelfen wir uns mit einem bekannten Trick. Den iFrame umschließen wir mit einem Container-DIV und erweitern den Container mit ‚padding-bottom‘ durch die prozentuale Höhe des Seitenverhältnis vom iFrames, genauso wie bei Lazy Loading Responsive Images Blogpost.

Beispiel:

<div class="embed-container">
    <iframe width="560" height="315" src="https://www.youtube.com/embed/9T7eSyo7DRU" frameborder="0" allowfullscreen></iframe>
</div>
.embed-container {
  position: relative; 
  padding-bottom: 56.25%; /* ratio 16x9 */
  height: 0; 
  overflow: hidden; 
  width: 100%;
  height: auto;
}
.embed-container iframe {

  position: absolute; 
  top: 0; 
  left: 0; 
  width: 100%; 
  height: 100%; 
}
/* ratio 4x3 */
.embed-container.ratio4x3 {
  padding-bottom: 75%;
}

Jetzt wird die Video-Containerhöhe immer relational an die Breite angepasst. Je nach Seitenverhältnis kann man auch weitere Klassen im .embed-container verwenden, z.B. .ratio4x3 für ein Seitenverhälnis 4:3.

Bei einem externen Video mit festem Seitenverhältnis ist das recht einfach, schwieriger wird es mit interaktiven Inhalten, die eine dynamische Höhe haben.

iFrame Interaktion

Wenn man im iFrame weitere Aktionen durchführen kann (Bestellformular, etc.) und die iFrame-Inhaltsseiten unterschiedlich lang sind, wird unter Umständen ein Scrollbalken angezeigt oder man hat viel Weißraum. Dieses Verhalten ist verständlicherweise nicht gewollt. In einem Projekt sollte die Anwendung (Berechnung einer Versicherung) in Partnerwebseiten eingebunden werden, ohne dass auffällt, dass es eine externe Anwendung (ein externes Produkt) ist.

Das ist unlängst komplizierter, da iFrames insbesondere von verschiedenen Domains aus Sicherheitsgründen nicht so einfach miteinander kommunizieren dürfen (Same-Origin-Policy).

Same Origin iFrame

Wenn der iFrame von der gleichen Domain aus z.B. mit der ID „responsive-iframe“ eingebunden ist, kann man folgende jQuery-Funktion verwenden.

$(function(){
  $(window).on('load resize', adjustIframe);
});

function adjustIframe() {
  $(parent.document.getElementById("responsive-iframe")).css("height", $("html").css("height"));
}

Erfolgt die Einbindung unter einer anderen Domain, muss man eine andere Technik verwenden damit die Kommunikation zwischen den Frames gewährleistet ist.

Cross Origin iFrame

In HTML5 ist postMessage eingeführt worden, das eine Kommunikation zwischen Browserfenstern erlaubt. Hiermit ist es möglich die aktuelle Höhe des iFrame-Inhalt an die einbindende Seite zu übermitteln.

postMessage Definition:

otherWindow.postMessage(message, targetOrigin, [transfer]);

JavaScript Funktion für Cross-Origin responsive iFrames

iframe.html – Sender:

var rIframe = {};
rIframe.requestAnimFrame = (function(){
        
    return  window.requestAnimationFrame       ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame    ||
            window.oRequestAnimationFrame      ||
            window.msRequestAnimationFrame     ||
            function( callback ){
                window.setTimeout(callback, 1000 / 60);
            };
})();    
rIframe.windowHeight = 0;
rIframe.htmlElement = document.getElementsByTagName('html')[0];
rIframe.targetDomain = 'domain.tld';
rIframe.resizeFrame = function (){
    var windowHeight = document.body ? Math.max(document.body.offsetHeight, rIframe.htmlElement.offsetHeight) : rIframe.htmlElement.offsetHeight ;
    if ( rIframe.windowHeight === windowHeight ) {
        rIframe.requestAnimFrame.call(window, rIframe.resizeFrame);
        return false;
    }
    rIframe.windowHeight = windowHeight;
    try {
        // Same Origin iFrame
        window.frameElement.style.height = windowHeight + 'px';
    }
    catch ( e ) {
        // Cross Origin iFrame
        window.parent.postMessage('resize:' + windowHeight , rIframe.targetDomain);
    }
    rIframe.requestAnimFrame.call(window, rIframe.resizeFrame);
};
rIframe.requestAnimFrame.call(window, rIframe.resizeFrame);

index.html – Empfänger:

function receiveMessage(event) {
    var message = event.data.split(':');
    var eventName = message[0];
    var iframes, len, i = 0;

    // only receive messages from this domain
    var senderDomains = ['domain.tld'];

    if ( senderDomains.indexOf(event.origin) !== -1 && eventName === 'resize' ) {
        iframes = document.getElementsByTagName('iframe');
        len = iframes.length;
        
        for (; i < len; i++) {
            if ( ( iframes[i].contentWindow || iframes[i].documentWindow ) == event.source) {
                iframes[i].style.height = message[1] + "px";
                return;
            }
        }
    }
}

if ( window.addEventListener ) {
    window.addEventListener('message', receiveMessage, false);
} 
else if (window.attachEvent)  {
    window.attachEvent('onmessage', receiveMessage);
}

Den kompletten Code findet man hier

JavaScript-Plugins

easyXMD

easyXDM ist eine Javascript Library, die es Entwicklern ermöglich mit den Limitierung der Same Origin Policy umzugehen und Anwendungen Cross-Domain kommunizieren zu lassen. Im Blogpost auf http://easyxdm.net/wp/2010/03/17/resize-iframe-based-on-content/ wird gut beschrieben, welche Schritte man unternehmen muss.

http://easyxdm.net/wp/

Wer im Projekt jQuery verwendet, kann das „responsiveiframe” Plugin von NPR verwenden.

http://npr.github.io/responsiveiframe

Eine umfangreiche API für iFrame Embeds bietet iframely. Die Basisvariante ist auch auf Github verfügbar. Der Online-Service bietet noch weitere Features, wenn man ein Bezahlmodell wählt.

https://iframely.com/

Performance Tweak

Ein letzter Hinweis, wenn der iFrame onload nicht im Viewport ist, sollte man überlegen, ob man den iFrame mit einer Lazy Loading Technik verzögert lädt. Am Beispiel der lazySizes Bibliothek von Alex Farkas, ist das sehr einfach zu integrieren.

<iframe data-src="//www.youtube.com/embed/ZfV-aYdU4uE" class="lazyload" frameborder="0" allowfullscreen></iframe>

Die eigentliche Soure (src) wird durch „data-src“ ersetzt, zusätzlich mit der Klasse „lazyload“ versehen. Das Script lädt dann den iFrame beim Scrollen nach, wenn der iFrame sich den Viewport nähert.

Das ist aber mehr oder weniger nur ein Zusatzfeature der Bibliothek - das Hauptaugenmerk liegt auf das Lazy Loading von Responsive Images (srcset/picture). Also durchaus eine Script, das man in vielerlei Hinsicht verwenden kann.

Flattr this!

7 Gedanken zu „Responsive iFrames“

  1. Schöner Artikel!
    Wir setzen auch in einem Projekt responsive iFrames ein, in denen unser content auf fremden Seiten läuft. Wir verwenden für des Übergeben der Höhe das gute alte postMessage plug in von Ben Alman, diese Lösung läuft stabil bis runter zu IE7. Dein Artikel bringt mich aber auf den Gedanken, in Zukunft html5 postMessage zu benutzen und das plug in als Fallback. Merci!

  2. Genau das, wonach ich gesucht habe! Die Beschreibung zum responsiven iFrame habe ich sofort ausprobiert und es funktioniert auf Anhieb. Meine Seiten sind erst kürzlich auf ein responsives Design umgestellt worden. Videos sind (noch) nicht so viele vorhanden, doch dem steht ja nun nichts mehr im Wege!

Kommentare sind geschlossen.