Creazione dinamica di un iframe con HTML specificato


148

Sto cercando di creare un iframe da JavaScript e riempirlo di HTML arbitrario, in questo modo:

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

Mi aspetterei iframequindi di contenere una finestra e un documento validi. Tuttavia, questo non è il caso:

> console.log (iframe.contentWindow);
nullo

Provalo tu stesso: http://jsfiddle.net/TrevorBurnham/9k9Pe/

Cosa sto trascurando?


9
Si noti che HTML5 ha introdotto un nuovo parametro in questo modo automaticamente: w3schools.com/tags/att_iframe_srcdoc.asp L'unico problema è la compatibilità del browser ...
Vincent Audebert,

Risposte:


121

L'impostazione srcdi un nuovo creato iframein JavaScript non attiva il parser HTML fino a quando l'elemento non viene inserito nel documento. L'HTML viene quindi aggiornato e il parser HTML verrà richiamato ed elaborerà l'attributo come previsto.

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

Anche questa risposta alla tua domanda è importante notare che questo approccio ha problemi di compatibilità con alcuni browser, per favore vedi la risposta di @mschr per una soluzione cross-browser.


3
Non funziona nemmeno in IE10. La risposta di @ mschr funziona sicuramente in IE7 +, forse anche nelle versioni precedenti.
James M. Greene,

6
La sua domanda era "Cosa sto trascurando?" e questo era il fatto che iframe non era stato aggiunto al documento. Non ho mai sostenuto che fosse cross-browser ed è passato solo un anno dopo che ho risposto che qualcuno si è davvero lamentato. No, non è cross-browser. Ma siamo onesti se vuoi la qualità del codice probabilmente c'è una soluzione molto più pulita rispetto all'utilizzo di un iframe in primo luogo :)
GillesC

1
Nota che encodeURI(...)non codifica tutti i caratteri, quindi se il tuo HTML ha caratteri speciali come #, questo si interromperà. encodeURIComponent(...)codifica questi personaggi. Vedi questa risposta
Ryan Morlok,

237

Anche se src = encodeURIdovresti funzionare, avrei fatto diversamente:

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

Poiché questo non ha vincoli di dominio x ed è completamente eseguito tramite l' iframehandle, è possibile accedere e manipolare il contenuto del frame in un secondo momento. Tutto ciò di cui hai bisogno è assicurarti che i contenuti siano stati renderizzati, che (a seconda del tipo di browser) inizieranno durante / dopo che il comando .write è stato emesso - ma non necessario quando close()viene chiamato.

Un modo compatibile al 100% di effettuare una richiamata potrebbe essere questo approccio:

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

Iframes ha l'evento onload, tuttavia. Ecco un approccio per accedere al codice HTML interno come DOM (js):

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};

Interessante; Non avevo mai visto questa tecnica prima. So che la codifica / decodifica dell'URI aggiunge un impatto sulle prestazioni, ma ho anche visto che è descritta come l'unica alternativa in ambienti che non supportano la srcdocproprietà . C'è un aspetto negativo document.writenell'approccio?
Trevor Burnham,

1
@mschr Questo metodo supporta il codice pagina HTML completo in cui vengono caricati anche fogli di stile e di inclusione? Vedi stackoverflow.com/questions/19871886
1,21 gigawatt

2
Fondamentalmente, sì, credo che dovrebbe, commento negativo lì. La tecnica fondamentalmente apre un inputtextstream al quale puoi scrivere tramite document.write. A sua volta, una normale pagina Web di caricamento lo recupera attraverso un flusso WebSocket.
mschr,

2
Funziona in Internet Explorer! Ciò è utile poiché gli URI dei dati non possono essere utilizzati come origini iframe in nessuna versione di IE. caniuse.com/#feat=datauri
Jesse Hallett

2
Interessante che document.body.appendChild(iframe)sia necessario per far funzionare tutto questo.
Paolo,

14

Grazie per la tua grande domanda, questo mi ha sorpreso alcune volte. Quando utilizzo l'origine HTML di dataURI, trovo che devo definire un documento HTML completo.

Vedi sotto un esempio modificato.

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

prendi nota del contenuto html racchiuso tra <html>tag e iframe.srcstringa.

L'elemento iframe deve essere aggiunto all'albero DOM per essere analizzato.

document.body.appendChild(iframe);

Non sarai in grado di ispezionare il iframe.contentDocumentmeno che tu non sia disable-web-securitynel tuo browser. Riceverai un messaggio

DOMException: impossibile leggere la proprietà 'contentDocument' da 'HTMLIFrameElement': bloccato un frame con origine " http: // localhost: 7357 " dall'accesso a un frame di origine incrociata.


12

Esiste un'alternativa per la creazione di un iframe i cui contenuti sono una stringa di HTML: l'attributo srcdoc . Questo non è supportato nei browser più vecchi (il principale tra questi: Internet Explorer e forse Safari ?), Ma esiste un polyfill per questo comportamento, che potresti inserire nei commenti condizionali per IE o usare qualcosa come has.js per pigro condizionatamente caricalo.


2
il supporto per questo è piuttosto mainstream ora (salva IE). E questo è sicuramente preferibile per accedere direttamente a contentDocument, soprattutto perché se utilizzato insieme all'attributo sandbox, non è possibile accedere a contentDocument.
CambridgeMike,

1

So che questa è una vecchia domanda, ma ho pensato di fornire un esempio usando l' srcdocattributo in quanto ora è ampiamente supportato e questa domanda viene visualizzata spesso.

Utilizzando l' srcdocattributo, è possibile fornire HTML incorporato da incorporare. Sostituisce l' srcattributo se supportato. Il browser tornerà srcall'attributo se non supportato.

Vorrei anche raccomandare di utilizzare l' sandboxattributo per applicare ulteriori restrizioni al contenuto nel frame. Ciò è particolarmente importante se l'HTML non è il tuo.

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

Se è necessario supportare browser meno recenti, è possibile verificare la presenza di srcdocsupporto e fallback in uno degli altri metodi da altre risposte.

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


1

L'approccio URL funziona solo per piccoli fragement HTML. L'approccio più solido è generare un URL oggetto da un BLOB e utilizzarlo come fonte dell'iframe dinamico.

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

0

Fai questo

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

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow è definito qui

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.