Dinamik olarak verilen HTML ile bir iframe oluşturma


156

JavaScript'ten bir iframe oluşturmaya ve bunu rasgele HTML ile doldurmaya çalışıyorum, örneğin:

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

Daha iframesonra geçerli bir pencere ve belge içermeyi beklerdim . Ancak durum bu değil:

> console.log (iframe.contentWindow);
boş

Kendiniz deneyin: http://jsfiddle.net/TrevorBurnham/9k9Pe/

Neyi gözden kaçırıyorum?


9
HTML5'in bunu otomatik olarak yapan yeni bir parametre sunduğunu unutmayın: w3schools.com/tags/att_iframe_srcdoc.asp Tek sorun tarayıcı uyumluluğudur ...
Vincent Audebert

Yanıtlar:


130

Javascript'te srcyeni oluşturulmuş bir iframeöğenin ayarlanması, öğe belgeye eklenene kadar HTML ayrıştırıcısını tetiklemez. HTML daha sonra güncellenir ve HTML ayrıştırıcı çağrılır ve özniteliği beklendiği gibi işler.

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

Ayrıca bu sorunun cevabı, bu yaklaşımın bazı tarayıcılarla uyumluluk sorunları olduğunu unutmamak önemlidir, lütfen tarayıcılar arası bir çözüm için @mschr yanıtına bakın.


3
IE10'da bile çalışmıyor. @ mschr'nin cevabı kesinlikle IE7 + 'da çalışıyor, belki daha eski sürümlerde bile.
James M. Greene

6
Sorusu, "Neyi gözden kaçırıyorum?" ve bu, iframe'in belgeye eklenmemiş olmasıdır. Asla tarayıcılar arasında olduğunu iddia etmedim ve birisinin gerçekten şikayet ettiğini yanıtladıktan sonra sadece bir yıldan fazla zaman geçti. Hayır, tarayıcılar arası değil. Ama dürüst olalım eğer kod kalitesi istiyorsanız, muhtemelen ilk etapta iframe kullanmaktan çok daha temiz bir çözüm vardır :)
GillesC

1
Bunun encodeURI(...)tüm karakterleri kodlamadığını unutmayın , bu nedenle HTML'nizde gibi özel karakterler varsa #, bu bozulacaktır. encodeURIComponent(...)bu karakterleri kodlar. Bu yanıtı
Ryan Morlok

241

Senin src = encodeURIçalışman gerekse de , ben farklı bir yoldan giderdim:

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

Bunun x-alan sınırlaması olmadığından ve tamamen iframetutamaç aracılığıyla yapıldığından , çerçevenin içeriğine daha sonra erişebilir ve bunları değiştirebilirsiniz. Emin olmanız gereken tek şey, içeriğin oluşturulmuş olmasıdır, bu (tarayıcı türüne bağlı olarak) .write komutu verildiği sırada / sonrasında başlayacaktır - ancakclose() çağrıldığında zorunlu olarak yapılmayacaktır .

Geri arama yapmanın% 100 uyumlu bir yolu şu yaklaşım olabilir:

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

Ancak iframe'ler, onload olayına sahiptir. İç html'ye DOM (js) olarak erişmek için bir yaklaşım:

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

İlginç; Bu tekniği daha önce görmemiştim. URI kodlama / kod çözme işleminin bir performans vuruşu eklediğini biliyorum, ancak aynı zamanda srcdocözelliği desteklemeyen ortamlarda tek alternatif olarak tanımlandığını da gördüm . document.writeYaklaşımın bir dezavantajı var mı ?
Trevor Burnham

1
@mschr Bu yöntem, içerme ve stil sayfalarının da yüklendiği tam HTML sayfa kodunu destekliyor mu? Bkz stackoverflow.com/questions/19871886
1.21 gigavatı

2
Temel olarak, evet öyle olması gerektiğine inanıyorum, orada kötü yorum yapmalıyım. Teknik temelde document.write aracılığıyla yazabileceğiniz bir girdi metni akışı açar. Sırayla, normal bir yükleme web sayfası bunu bir websocket akışı aracılığıyla alır.
mschr

2
Bu, Internet Explorer'da çalışır! Veri URI'leri herhangi bir IE sürümünde iframe kaynağı olarak kullanılamayacağı için bu kullanışlıdır. caniuse.com/#feat=datauri
Jesse Hallett

2
document.body.appendChild(iframe)Bunun işe yaraması için gerekli olması ilginç .
Paul

15

Harika sorunuz için teşekkürler, bu beni birkaç kez yakaladı. DataURI HTML kaynağını kullanırken, tam bir HTML belgesi tanımlamam gerektiğini anladım.

Aşağıda değiştirilmiş bir örneğe bakın.

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

<html>etiketler ve iframe.srcdizeyle sarılmış html içeriğini not alın .

İframe öğesinin ayrıştırılması için DOM ağacına eklenmesi gerekir.

document.body.appendChild(iframe);

Tarayıcınızda iframe.contentDocumentolmadıkça inceleyemeyeceksiniz disable-web-security. Bir mesaj alacaksınız

DOMException: 'HTMLIFrameElement'ten' contentDocument 'özelliği okunamadı: " http: // localhost: 7357 " kaynaklı bir çerçevenin çapraz kaynak çerçeveye erişmesi engellendi .


13

İçeriği bir HTML dizesi olan bir iframe oluşturmak için bir alternatif vardır: srcdoc özelliği . : (Aralarında baş Bu eski tarayıcılarda desteklenmez Internet Explorer ve muhtemelen Safari , ama bir var mı?) Polyfill koşullu tembel için has.js gibi bir şey IE için koşullu yorumları bölümüne, ya da kullanabildiği bu davranışa göre yükle.


2
bunun için destek artık oldukça yaygın (IE hariç). Ve bu kesinlikle contentDocument'e doğrudan erişmek için tercih edilir - özellikle sandbox özelliği ile birlikte kullanıldığında, contentDocument'e erişemezsiniz.
CambridgeMike

5

Bunun eski bir soru olduğunu biliyorum, ancak bu srcdocözelliği kullanarak bir örnek vereceğimi düşündüm, çünkü bu artık yaygın olarak destekleniyor ve bu soru sıkça görüntüleniyor.

Özelliği kullanarak, srcdocgömmek için satır içi HTML sağlayabilirsiniz. srcDestekleniyorsa özelliği geçersiz kılar . Desteklenmiyorsa tarayıcı srcözniteliğe geri dönecektir .

sandboxÇerçevedeki içeriğe fazladan kısıtlamalar uygulamak için özniteliği kullanmanızı da öneririm . Bu, özellikle HTML size ait değilse önemlidir.

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

Daha eski tarayıcıları desteklemeniz gerekiyorsa, desteği kontrol edebilir srcdocve diğer yöntemlerden birine diğer yanıtlardan geri dönebilirsiniz.

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
Peki ya yeni bir unsurun yaratılması gerekmiyorsa? Bir iframe zaten varsa ve veriler yalnızca güncellenecekse?
NL23codes

2

URL yaklaşımı yalnızca küçük HTML parçaları için çalışacaktır. Daha sağlam yaklaşım, bir blob'dan bir nesne URL'si oluşturmak ve bunu dinamik iframe'in kaynağı olarak kullanmaktır.

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

Bunu yap

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

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindow burada tanımlanır

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.