การสร้าง iframe ด้วย HTML ที่กำหนดแบบไดนามิก


148

ฉันพยายามสร้าง iframe จาก JavaScript และเติมด้วย HTML โดยพลการเช่น:

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

ฉันคาดว่าiframeจะมีหน้าต่างและเอกสารที่ถูกต้อง อย่างไรก็ตามนี่ไม่ใช่กรณี:

> console.log (iframe.contentWindow);
โมฆะ

ลองด้วยตัวคุณเอง: http://jsfiddle.net/TrevorBurnham/9k9Pe/

ฉันกำลังมองเห็นอะไร


9
โปรดทราบว่า HTML5 นำพารามิเตอร์ใหม่มาใช้โดยอัตโนมัติ: w3schools.com/tags/att_iframe_srcdoc.asp ปัญหาเดียวคือความเข้ากันได้ของเบราว์เซอร์ ...
Vincent Audebert

คำตอบ:


121

การตั้งค่าsrcของที่สร้างขึ้นใหม่iframeในจาวาสคริปต์ไม่เรียกตัวแยกวิเคราะห์ HTML จนกว่าองค์ประกอบจะถูกแทรกลงในเอกสาร HTML จะได้รับการอัปเดตแล้วตัวแยกวิเคราะห์ HTML จะถูกเรียกใช้และประมวลผลแอตทริบิวต์ตามที่คาดไว้

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

อีกทั้งยังตอบคำถามของคุณเป็นสิ่งสำคัญที่จะต้องทราบว่าวิธีการนี้มีปัญหาความเข้ากันได้กับเบราว์เซอร์บางตัวโปรดดูคำตอบของ @mschr สำหรับโซลูชันข้ามเบราว์เซอร์


3
ไม่ได้ทำงานใน IE10 คำตอบของ @ mschr ทำงานได้ใน IE7 + แน่นอนอาจเป็นรุ่นที่เก่ากว่า
James M. Greene

6
คำถามของเขาคือ "ฉันมองอะไรดี" และนั่นคือความจริงที่ว่า iframe ไม่ได้ถูกผนวกเข้ากับเอกสาร ฉันไม่เคยอ้างว่าเป็นเบราว์เซอร์ข้ามและเพียงหนึ่งปีหลังจากที่ฉันตอบว่ามีคนบ่นจริงๆ ไม่ใช่มันไม่ได้ข้ามเบราว์เซอร์ แต่ขอให้เป็นคนซื่อสัตย์ถ้าคุณต้องการที่มีคุณภาพรหัสอาจมีวิธีการแก้ปัญหามากสะอาดกว่าการใช้ iframe ในสถานที่แรก :)
GillesC

1
โปรดทราบว่าencodeURI(...)จะไม่เข้ารหัสอักขระทั้งหมดดังนั้นหาก HTML ของคุณมีอักขระพิเศษเช่น#นี้จะทำให้แตก encodeURIComponent(...)เข้ารหัสอักขระเหล่านี้ ดูคำตอบนี้
Ryan Morlok

237

ถึงแม้ว่าคุณsrc = encodeURIควรจะทำงานฉันก็จะได้วิธีที่แตกต่าง:

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

เนื่องจากสิ่งนี้ไม่มีข้อ จำกัด โดเมน x และทำได้อย่างสมบูรณ์ผ่านจุดiframeจับคุณจึงสามารถเข้าถึงและจัดการเนื้อหาของเฟรมได้ในภายหลัง สิ่งที่คุณต้องทำให้แน่ใจคือเนื้อหานั้นถูกแสดงผลซึ่งจะ (ขึ้นอยู่กับประเภทของเบราว์เซอร์) เริ่มต้นในระหว่าง / หลังจากคำสั่ง. write ถูกใช้ - แต่จะไม่ทำอย่างที่ไม่จำเป็นเมื่อclose()ถูกเรียก

วิธีที่ใช้ร่วมกันได้ 100% ในการโทรกลับอาจเป็นวิธีนี้:

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

อย่างไรก็ตาม Iframes มีเหตุการณ์ onload นี่คือวิธีการเข้าถึง HTML ภายในเป็น DOM (js):

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

ที่น่าสนใจ; ฉันไม่เคยเห็นเทคนิคนี้มาก่อน ฉันรู้ว่าการเข้ารหัส URI / ถอดรหัสเพิ่มประสิทธิภาพตี แต่ผมเคยเห็นมันก็อธิบายว่าเป็นทางเลือก แต่เพียงผู้เดียวในสภาพแวดล้อมที่ไม่สนับสนุนคุณสมบัติsrcdoc มีข้อเสียของdocument.writeวิธีการหรือไม่
เทรเวอร์เบิร์นแฮม

1
@mschr วิธีนี้รองรับโค้ดเพจ HTML แบบเต็มที่มีการรวมสไตล์ชีตและโหลดไว้ด้วยหรือไม่? ดูstackoverflow.com/questions/19871886
1.21 gigawatts

2
โดยทั่วไปใช่ฉันเชื่อว่ามันควรจะไม่แสดงความคิดเห็นที่นั่น เทคนิคโดยทั่วไปจะเปิดอินพุตสตรีมซึ่งคุณสามารถเขียนผ่านทาง document.write ในทางกลับกันการโหลดหน้าเว็บปกติจะทำการเรียกคืนผ่าน websocket-stream
mschr

2
ใช้งานได้ใน Internet Explorer! มีประโยชน์เนื่องจาก URI ของข้อมูลไม่สามารถใช้เป็นแหล่ง iframe ใน IE เวอร์ชันใด ๆ caniuse.com/#feat=datauri
Jesse Hallett

2
ที่น่าสนใจว่าdocument.body.appendChild(iframe)เป็นสิ่งจำเป็นสำหรับสิ่งนี้ในการทำงาน
พอล

14

ขอบคุณสำหรับคำถามที่ยอดเยี่ยมของคุณสิ่งนี้ทำให้ฉันไม่กี่ครั้ง เมื่อใช้แหล่งข้อมูล HTML datauri ฉันพบว่าฉันต้องกำหนดเอกสาร HTML ที่สมบูรณ์

ดูตัวอย่างด้านล่างที่ได้รับการแก้ไข

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 ที่ห่อด้วย<html>แท็กและiframe.srcสตริง

จำเป็นต้องเพิ่มองค์ประกอบ iframe ในทรี DOM เพื่อแยกวิเคราะห์

document.body.appendChild(iframe);

คุณจะไม่สามารถตรวจสอบiframe.contentDocumentยกเว้นว่าคุณdisable-web-securityอยู่ในเบราว์เซอร์ของคุณ คุณจะได้รับข้อความ

DOMException: ล้มเหลวในการอ่านคุณสมบัติ 'contentDocument' จาก 'HTMLIFrameElement': บล็อกเฟรมที่มีต้นกำเนิด " http: // localhost: 7357 " ไม่ให้เข้าถึงเฟรมข้ามที่มา


12

: มีทางเลือกสำหรับการสร้าง iframe ที่มีเนื้อหาเป็นสตริงของ HTML เป็นแอตทริบิวต์ srcdoc นี้ไม่ได้รับการสนับสนุนในเบราว์เซอร์รุ่นเก่า (หัวหน้าของพวกเขา: Internet Explorer และอาจ Safari ?) แต่มีpolyfillสำหรับพฤติกรรมนี้ซึ่งคุณสามารถใส่ในความคิดเห็นเงื่อนไขสำหรับ IE หรือการใช้งานบางอย่างเช่น has.js ขี้เกียจตามเงื่อนไข โหลดมัน


2
การสนับสนุนสำหรับเรื่องนี้ค่อนข้างสำคัญในขณะนี้ (บันทึก IE) และนี่เป็นวิธีที่ดีกว่าในการเข้าถึง contentDocument โดยตรง - โดยเฉพาะเมื่อใช้ร่วมกับแอตทริบิวต์ sandbox คุณจะไม่สามารถเข้าถึง contentDocument
CambridgeMike

1

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันคิดว่าฉันจะให้ตัวอย่างโดยใช้srcdocคุณลักษณะเนื่องจากตอนนี้ได้รับการสนับสนุนอย่างกว้างขวางและเป็นคำถามที่มีผู้ดูบ่อยครั้ง

การใช้srcdocแอตทริบิวต์ที่คุณสามารถให้ HTML แบบอินไลน์ฝัง มันจะแทนที่srcคุณลักษณะหากได้รับการสนับสนุน เบราว์เซอร์จะถอยกลับไปที่srcแอตทริบิวต์หากไม่ได้รับการสนับสนุน

ฉันขอแนะนำให้ใช้sandboxคุณลักษณะเพื่อใช้ข้อ จำกัด พิเศษกับเนื้อหาในเฟรม นี่เป็นสิ่งสำคัญอย่างยิ่งหาก HTML ไม่ใช่ของคุณเอง

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

หากคุณต้องการสนับสนุนเบราว์เซอร์รุ่นเก่าคุณสามารถตรวจสอบการsrcdocสนับสนุนและสำรองทางเลือกหนึ่งในวิธีอื่น ๆ จากคำตอบอื่น ๆ

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

วิธีการ URL จะใช้งานได้กับค่าจ้าง HTML ขนาดเล็กเท่านั้น แนวทางที่มั่นคงมากขึ้นคือการสร้าง URL วัตถุจาก Blob และใช้เป็นแหล่งที่มาของ iframe แบบไดนามิก

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

ทำเช่นนี้

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

var frame_win = getIframeWindow(el);

console.log(frame_win);
...

getIframeWindowถูกกำหนดไว้ที่นี่

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.