Membuat iframe dengan HTML yang diberikan secara dinamis

148

Saya mencoba membuat iframe dari JavaScript dan mengisinya dengan HTML sewenang-wenang, seperti:

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

Saya berharap iframeuntuk kemudian berisi jendela dan dokumen yang valid. Namun, ini bukan masalahnya:

> console.log (iframe.contentWindow);
batal

Cobalah sendiri: http://jsfiddle.net/TrevorBurnham/9k9Pe/

Apa yang saya abaikan?

Trevor Burnham
sumber
9
Perhatikan bahwa HTML5 memperkenalkan parameter baru yang melakukan ini secara otomatis: w3schools.com/tags/att_iframe_srcdoc.asp Satu-satunya masalah adalah kompatibilitas browser ...
Vincent Audebert
kemungkinan duplikat meletakkan html di dalam iframe (menggunakan javascript)
Ciro Santilli 郝海东 冠状 病 六四 六四 事件

Jawaban:

121

Pengaturan yang srcbaru dibuat iframedalam javascript tidak memicu pengurai HTML sampai elemen dimasukkan ke dalam dokumen. HTML kemudian diperbarui dan parser HTML akan dipanggil dan memproses atribut seperti yang diharapkan.

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

Juga ini menjawab pertanyaan Anda, penting untuk dicatat bahwa pendekatan ini memiliki masalah kompatibilitas dengan beberapa browser, silakan lihat jawaban @mschr untuk solusi lintas-browser.

GillesC
sumber
3
Bahkan tidak berfungsi di IE10. Jawaban @ mschr bekerja di IE7 + pasti, mungkin versi yang lebih lama.
James M. Greene
6
Pertanyaannya adalah "Apa yang saya hadapi?" dan itu adalah fakta bahwa iframe tidak ditambahkan ke dokumen. Saya tidak pernah mengklaim itu adalah peramban silang dan baru setahun setelah saya menjawab bahwa seseorang benar-benar mengeluh. Tidak, ini bukan peramban silang. Tapi mari kita jujur ​​jika Anda menginginkan kualitas kode mungkin ada solusi yang jauh lebih bersih daripada menggunakan iframe di tempat pertama :)
GillesC
1
Catatan yang encodeURI(...)tidak menyandikan semua karakter, jadi jika HTML Anda memiliki karakter khusus seperti #, ini akan rusak. encodeURIComponent(...)mengkodekan karakter ini. Lihat jawaban ini
Ryan Morlok
237

Meskipun demikian src = encodeURI harus bekerja, saya akan pergi dengan cara yang berbeda:

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

Karena ini tidak memiliki pembatasan x-domain dan sepenuhnya dilakukan melalui iframepegangan, Anda dapat mengakses dan memanipulasi konten frame nanti. Yang perlu Anda pastikan adalah, bahwa konten telah dirender, yang akan (tergantung pada jenis browser) mulai selama / setelah perintah .write dikeluarkan - tetapi tidak dilakukan saat close()dipanggil.

Cara yang kompatibel 100% dalam melakukan panggilan balik bisa menjadi pendekatan ini:

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

Namun, iframes memiliki acara onload. Berikut adalah pendekatan untuk mengakses html dalam sebagai DOM (js):

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};
mschr
sumber
Menarik; Saya belum pernah melihat teknik ini sebelumnya. Saya tahu bahwa pengkodean / penguraian URI menambah hit kinerja, namun saya juga melihatnya digambarkan sebagai satu-satunya alternatif di lingkungan yang tidak mendukung srcdocproperti . Apakah ada kelemahan dari document.writependekatan ini?
Trevor Burnham
1
@mschr Apakah metode ini mendukung kode halaman HTML lengkap di mana termasuk dan stylesheet dimuat? Lihat stackoverflow.com/questions/19871886
1,21 gigawatts
2
Pada dasarnya, ya saya percaya seharusnya, komentar buruk di sana. Teknik ini pada dasarnya membuka inputtextstream yang dapat Anda tulis melalui document.write. Pada gilirannya, laman web pemuatan normal akan menerima ini melalui aliran websocket.
mschr
2
Ini berfungsi di Internet Explorer! Itu berguna karena data URI tidak dapat digunakan sebagai sumber iframe dalam versi IE mana pun. caniuse.com/#feat=datauri
Jesse Hallett
2
Menarik bahwa document.body.appendChild(iframe)diperlukan untuk ini berfungsi.
Paul
14

Terima kasih atas pertanyaan Anda yang hebat, ini telah membuat saya keluar beberapa kali. Saat menggunakan sumber dataURUR HTML, saya menemukan bahwa saya harus mendefinisikan dokumen HTML lengkap.

Lihat di bawah ini contoh yang dimodifikasi.

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

perhatikan konten html yang dibungkus dengan <html>tag dan iframe.srcstring.

Elemen iframe perlu ditambahkan ke pohon DOM untuk diuraikan.

document.body.appendChild(iframe);

Anda tidak akan dapat memeriksa iframe.contentDocumentkecuali Anda berada disable-web-securitydi browser Anda. Anda akan mendapat pesan

DOMException: Gagal membaca properti 'contentDocument' dari 'HTMLIFrameElement': Memblokir bingkai dengan asal " http: // localhost: 7357 " dari mengakses bingkai asal-silang.

Kylewelsby
sumber
12

Ada alternatif untuk membuat iframe yang isinya adalah string HTML: atribut srcdoc . Ini tidak didukung di browser lama (kepala di antaranya: Internet Explorer, dan mungkin Safari ?), Tetapi ada polyfill untuk perilaku ini, yang dapat Anda masukkan dalam komentar bersyarat untuk IE, atau gunakan sesuatu seperti has.js untuk bersyarat malas muat

zedd45
sumber
2
dukungan untuk ini cukup umum sekarang (simpan IE). Dan ini jelas lebih baik daripada mengakses contentDocument secara langsung - terutama karena jika digunakan bersama dengan atribut sandbox, Anda tidak dapat mengakses contentDocument.
CambridgeMike
1

Saya tahu ini adalah pertanyaan lama tapi saya pikir saya akan memberikan contoh menggunakan srcdocatribut karena sekarang ini banyak didukung dan pertanyaan ini sering dilihat.

Menggunakan srcdocatribut, Anda dapat memberikan HTML inline untuk disematkan. Itu menimpa srcatribut jika didukung. Browser akan kembali ke srcatribut jika tidak didukung.

Saya juga akan merekomendasikan menggunakan sandboxatribut untuk menerapkan batasan ekstra pada konten dalam bingkai. Ini sangat penting jika HTML itu bukan milik Anda.

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

Jika Anda perlu mendukung browser lama, Anda dapat memeriksa srcdocdukungan dan mundur ke salah satu metode lain dari jawaban lain.

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
sumber
1

Pendekatan URL hanya akan berfungsi untuk fragmen HTML kecil. Pendekatan yang lebih solid adalah menghasilkan URL objek dari gumpalan dan menggunakannya sebagai sumber iframe dinamis.

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
sumber
0

Melakukan hal ini

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

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow didefinisikan di sini

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
sumber