Создание 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 Единственная проблема - это совместимость с браузером ...
Винсент Одеберт,
возможный дубликат размещения html внутри iframe (с использованием javascript)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

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 для кросс-браузерного решения.

GillesC
источник
3
Даже не работает в IE10. Ответ @ mschr наверняка работает в IE7 +, может быть, даже в более старых версиях.
Джеймс М. Грин
6
Его вопрос был "Что я пропускаю?" и это был факт, что iframe не был добавлен к документу. Я никогда не утверждаю, что это были кросс-браузеры, и только через год после того, как я ответил, что кто-то жаловался. Нет, это не кросс-браузер. Но давайте будем честными, если вы хотите, чтобы качество кода было, вероятно, гораздо более чистым решением, чем в первую очередь использование iframe :)
GillesC
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 есть событие onload. Вот подход для доступа к внутреннему html как DOM (js):

iframe.onload = function() {
   var div=iframe.contentWindow.document.getElementById('mydiv');
};
mschr
источник
Интересный; Я не видел эту технику раньше. Я знаю, что кодирование / декодирование URI добавляет снижение производительности, но я также видел его описанным как единственную альтернативу в средах, которые не поддерживают это srcdocсвойство . Есть ли обратная сторона document.writeподхода?
Тревор Бернхэм
1
@mschr Поддерживает ли этот метод полный HTML-код страницы, где также загружаются таблицы включений и таблицы стилей? См. Stackoverflow.com/questions/19871886
1.21 гигаватт
2
В принципе, да, я считаю, что это должно быть, плохой комментарий там. Техника в основном открывает входной текстовый поток, в который вы можете писать через document.write. В свою очередь, обычная веб-страница загрузки получает это через поток websocket.
2013 г.
2
Это работает в Internet Explorer! Это удобно, поскольку URI данных не могут использоваться в качестве источников iframe ни в одной версии IE. caniuse.com/#feat=datauri
Джесси Халлетт
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 " от доступа к фрейму перекрестного происхождения.

kylewelsby
источник
12

Существует альтернатива для создания iframe, содержимое которого представляет собой строку HTML: атрибут srcdoc . Это не поддерживается в старых браузерах (главный из них: Internet Explorer и, возможно, Safari ?), Но для этого поведения есть поли-заливка , которую вы можете поместить в условные комментарии для IE или использовать что-то вроде has.js для условно ленивых загрузить его.

zedd45
источник
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);

frobinsonj
источник
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);
damoeb
источник
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;
}
Доминик Фортин
источник