Dynamisches Erstellen eines Iframes mit angegebenem HTML

148

Ich versuche, einen Iframe aus JavaScript zu erstellen und ihn mit beliebigem HTML zu füllen, wie folgt:

var html = '<body>Foo</body>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

Ich würde erwarten iframe, dann ein gültiges Fenster und Dokument zu enthalten. Dies ist jedoch nicht der Fall:

> console.log (iframe.contentWindow);
Null

Probieren Sie es aus: http://jsfiddle.net/TrevorBurnham/9k9Pe/

Was übersehe ich?

Trevor Burnham
quelle
9
Beachten Sie, dass HTML5 einen neuen Parameter eingeführt hat, der dies automatisch tut: w3schools.com/tags/att_iframe_srcdoc.asp Das einzige Problem ist die Browserkompatibilität ...
Vincent Audebert
Mögliches Duplikat des
Einfügens

Antworten:

121

Das Festlegen srceines neu iframein Javascript erstellten Elements löst den HTML-Parser erst aus, wenn das Element in das Dokument eingefügt wurde. Der HTML-Code wird dann aktualisiert und der HTML-Parser wird aufgerufen und verarbeitet das Attribut wie erwartet.

http://jsfiddle.net/9k9Pe/2/

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);
document.body.appendChild(iframe);
console.log('iframe.contentWindow =', iframe.contentWindow);

Auch bei der Beantwortung Ihrer Frage ist es wichtig zu beachten, dass dieser Ansatz Kompatibilitätsprobleme mit einigen Browsern aufweist. Eine browserübergreifende Lösung finden Sie in der Antwort von @mschr.

GillesC
quelle
3
Funktioniert nicht einmal in IE10. Die Antwort von @ mschr funktioniert mit Sicherheit in IE7 +, vielleicht sogar in älteren Versionen.
James M. Greene
6
Seine Frage war "Was übersehen ich?" und das war die Tatsache, dass iframe nicht an das Dokument angehängt wurde. Ich behaupte nie, dass es sich um Cross-Browser handelte, und erst über ein Jahr, nachdem ich geantwortet hatte, hat sich tatsächlich jemand beschwert. Nein, es ist kein Cross-Browser. Aber seien wir ehrlich, wenn Sie Codequalität wollen, gibt es wahrscheinlich eine viel sauberere Lösung als die Verwendung eines Iframes an erster Stelle :)
GillesC
1
Beachten Sie, dass encodeURI(...)nicht alle Zeichen codiert werden. Wenn Ihr HTML also Sonderzeichen wie enthält #, wird dies unterbrochen. encodeURIComponent(...)codiert diese Zeichen. Siehe diese Antwort
Ryan Morlok
237

Obwohl du arbeiten src = encodeURIsolltest, wäre ich einen anderen Weg gegangen:

var iframe = document.createElement('iframe');
var html = '<body>Foo</body>';
document.body.appendChild(iframe);
iframe.contentWindow.document.open();
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();

Da dies keine X-Domain-Einschränkungen aufweist und vollständig über das iframeHandle erfolgt, können Sie später auf den Inhalt des Frames zugreifen und ihn bearbeiten. Sie müssen lediglich sicherstellen, dass der Inhalt gerendert wurde, der (je nach Browsertyp) während / nach der Ausgabe des Befehls .write gestartet wird - jedoch nicht unbedingt, wenn er close()aufgerufen wird.

Eine 100% kompatible Art, einen Rückruf durchzuführen, könnte folgender Ansatz sein:

<html><body onload="parent.myCallbackFunc(this.window)"></body></html>

Iframes hat jedoch das Onload-Ereignis. Hier ist ein Ansatz, um auf das innere HTML als DOM (js) zuzugreifen:

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};
mschr
quelle
Interessant; Ich hatte diese Technik noch nie gesehen. Ich weiß, dass die URI-Codierung / -Decodierung einen Leistungseinbruch bewirkt, aber ich habe auch gesehen, dass sie als einzige Alternative in Umgebungen beschrieben wird, die die srcdocEigenschaft nicht unterstützen . Hat der document.writeAnsatz einen Nachteil ?
Trevor Burnham
1
@mschr Unterstützt diese Methode den vollständigen HTML-Seitencode, in den auch Includes und Stylesheets geladen werden? Siehe stackoverflow.com/questions/19871886
1,21 Gigawatt
2
Grundsätzlich ja ich glaube es sollte, dort schlecht kommentieren. Die Technik öffnet grundsätzlich einen Eingabetextstrom, in den Sie über document.write schreiben können. Eine normale Ladewebseite empfängt dies wiederum über einen Websocket-Stream.
Mschr
2
Dies funktioniert in Internet Explorer! Dies ist praktisch, da Daten-URIs in keiner IE-Version als Iframe-Quellen verwendet werden können. caniuse.com/#feat=datauri
Jesse Hallett
2
Interessant, dass das document.body.appendChild(iframe)erforderlich ist, damit dies funktioniert.
Paul
14

Vielen Dank für Ihre tolle Frage, das hat mich ein paar Mal erwischt. Wenn ich eine dataURI-HTML-Quelle verwende, muss ich ein vollständiges HTML-Dokument definieren.

Siehe unten ein modifiziertes Beispiel.

var html = '<html><head></head><body>Foo</body></html>';
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);

Beachten Sie den HTML-Inhalt, der mit <html>Tags und der iframe.srcZeichenfolge umschlossen ist.

Das iframe-Element muss zum zu analysierenden DOM-Baum hinzugefügt werden.

document.body.appendChild(iframe);

Sie können das nur überprüfen, iframe.contentDocumentwenn Sie sich disable-web-securityin Ihrem Browser befinden. Sie erhalten eine Nachricht

DOMException: Fehler beim Lesen der Eigenschaft 'contentDocument' aus 'HTMLIFrameElement': Blockiert einen Frame mit dem Ursprung " http: // localhost: 7357 " für den Zugriff auf einen Cross-Origin-Frame.

Kylewelsby
quelle
12

Es gibt eine Alternative zum Erstellen eines Iframes, dessen Inhalt eine HTML-Zeichenfolge ist: das srcdoc-Attribut . Dies wird in älteren Browsern nicht unterstützt (hauptsächlich unter ihnen: Internet Explorer und möglicherweise Safari ?), Aber es gibt eine Polyfüllung für dieses Verhalten, die Sie in bedingte Kommentare für den IE einfügen oder so etwas wie has.js verwenden können, um bedingt faul zu sein lade es.

zedd45
quelle
2
Unterstützung dafür ist jetzt ziemlich Mainstream (außer IE speichern). Dies ist definitiv dem direkten Zugriff auf contentDocument vorzuziehen - insbesondere, da Sie in Verbindung mit dem Sandbox-Attribut nicht auf contentDocument zugreifen können.
CambridgeMike
1

Ich weiß, dass dies eine alte Frage ist, aber ich dachte, ich würde ein Beispiel mit dem srcdocAttribut liefern , da dies jetzt weitgehend unterstützt wird und diese Frage häufig gesehen wird.

Mit dem srcdocAttribut können Sie Inline-HTML zum Einbetten bereitstellen. Es überschreibt das srcAttribut, wenn es unterstützt wird. Der Browser greift auf die zurücksrc Attribut zurück, wenn es nicht unterstützt wird.

Ich würde auch empfehlen, das sandboxAttribut zu verwenden, um zusätzliche Einschränkungen auf den Inhalt im Frame anzuwenden. Dies ist besonders wichtig, wenn der HTML-Code nicht Ihr eigener ist.

const iframe = document.createElement('iframe');
const html = '<body>Foo</body>';
iframe.srcdoc = html;
iframe.sandbox = '';
document.body.appendChild(iframe);

Wenn Sie ältere Browser unterstützen müssen, können Sie anhand srcdocanderer Antworten nach Unterstützung suchen und auf eine der anderen Methoden zurückgreifen.

function setIframeHTML(iframe, html) {
  if (typeof iframe.srcdoc !== 'undefined') {
    iframe.srcdoc = html;
  } else {
    iframe.sandbox = 'allow-same-origin';
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write(html);
    iframe.contentWindow.document.close();
  }
}

var iframe = document.createElement('iframe');
iframe.sandbox = '';
var html = '<body>Foo</body>';

document.body.appendChild(iframe);
setIframeHTML(iframe, html);

frobinsonj
quelle
1

Der URL-Ansatz funktioniert nur für kleine HTML-Fragmente. Der solidere Ansatz besteht darin, eine Objekt-URL aus einem Blob zu generieren und diese als Quelle für den dynamischen Iframe zu verwenden.

const html = '<html>...</html>';
const iframe = document.createElement('iframe');
const blob = new Blob([html], {type: 'text/html'});
iframe.src = window.URL.createObjectURL(blob);
document.body.appendChild(iframe);
Damoeb
quelle
0

Mach das

...
var el = document.getElementById('targetFrame');

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow wird hier definiert

function getIframeWindow(iframe_object) {
  var doc;

  if (iframe_object.contentWindow) {
    return iframe_object.contentWindow;
  }

  if (iframe_object.window) {
    return iframe_object.window;
  } 

  if (!doc && iframe_object.contentDocument) {
    doc = iframe_object.contentDocument;
  } 

  if (!doc && iframe_object.document) {
    doc = iframe_object.document;
  }

  if (doc && doc.defaultView) {
   return doc.defaultView;
  }

  if (doc && doc.parentWindow) {
    return doc.parentWindow;
  }

  return undefined;
}
Dominique Fortin
quelle