Створення 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в javascript не запускає 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 + напевно, можливо, навіть у старих версіях.
Джеймс М. Грін

6
Його питання було "Що я не помічаю?" і це був факт, що iframe не був доданий до документа. Я ніколи не стверджую, що це перехресні браузери, і це минуло лише через рік після того, як я відповів, що хтось насправді скаржився. Ні, це не крос-браузер. Але
будьмо

1
Зверніть увагу, що encodeURI(...)кодує не всі символи, тому якщо у вашому HTML є такі спеціальні символи #, це порушиться. encodeURIComponent(...)кодує цих символів. Дивіться цю відповідь
Райан Морлок

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 є подія завантаження. Ось підхід до доступу до внутрішнього 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 гігаватт

2
В основному, так, я вірю, що це повинно, погано коментувати там. Метод в основному відкриває вхідний текстовий потік, до якого ви можете написати через document.write. У свою чергу, звичайна веб-сторінка, що завантажує, відновлює це через потік websocket.
mschr

2
Це працює в Internet Explorer! Це зручно, оскільки URI даних не можна використовувати як джерела iframe в жодній версії IE. caniuse.com/#feat=datauri
Джессі Халлетт

2
Цікаво, що document.body.appendChild(iframe)для цього потрібно працювати.
Павло

14

Дякую за ваше чудове запитання, це мене кілька разів наздогнало. Під час використання джерела данихURUR HTML я вважаю, що мені потрібно визначити повний 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 ?), Але для такої поведінки є поліфайл , який ви можете поставити в умовних коментарях для IE або використовувати щось на зразок has.js, щоб умовно лінивий завантажте його.


2
підтримка цього зараз досить мейнстрім (крім IE). І це, безумовно, краще для доступу до contentDocument безпосередньо - тим більше, що якщо його використовувати разом з атрибутом пісочниці, ви не можете отримати доступ до 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-адресу з блобу та використовувати її як джерело динамічного 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.