Создание iframe с заданным HTML динамически

Я пытаюсь создать 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/

Что я пропускаю?

Ответ 1

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

Ответ 2

Пока ваш 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');
};

Ответ 3

Спасибо за ваш замечательный вопрос, это несколько раз застало меня. При использовании источника dataURI 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" от доступа к кадру скрещивания.

Ответ 4

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

Ответ 5

Сделай это

...
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;
}