Подтвердить что ты не робот

Как получить ссылку на объект окна iframe внутри обработчика onload iframe, созданного из родительского окна

Прежде чем вставить какой-либо код, вот сценарий:

  • У меня есть HTML-документ, который создает пустой iframe с использованием JavaScript
  • JavaScript создает функцию и связывает эту функцию с объектом документа iframe (используя doc.open() для получения ссылки на документ)
  • Затем функция привязывается как обработчик onload для документа iframe (путем записи <body onload="..."> в iframe.

Теперь то, что меня насторожило, состоит в том, что глобальные объекты (window) и document внутри обработчика onload (при его запуске) отличаются от тех же объектов, которые запускаются через JavaScript, добавленные через узлы script.

Здесь HTML:

<!doctype html>
<html>
<head>
<script>
(function(){
  var dom,doc,where,iframe;

  iframe = document.createElement('iframe');
  iframe.src="javascript:false";

  where = document.getElementsByTagName('script')[0];
  where.parentNode.insertBefore(iframe, where);

  doc = iframe.contentWindow.document;

  var _doc = document;

  doc.open()._l=function() {
    // the window object should be the one that doc is inside
    window.vanishing_global=new Date().getTime();

    var js = this.createElement("script");
    js.src = 'test-vanishing-global.js?' + window.vanishing_global;

    window.name="foobar";
    this.foobar="foobar:" + Math.random();
    document.foobar="barfoo:" + Math.random();

    // `this` should be the document object, but it not
    console.log("this == document: %s", this == document);
    console.log("this == doc:      %s", this == doc);

    // the next two lines added based on @Ian comment below
    console.log("_doc == document: %s", _doc == document);
    console.log("_doc == doc:      %s", _doc == doc);

    console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar);
    this.body.appendChild(js);
  };
  doc.write('<body onload="document._l();"></body>');
  doc.close();
})();
</script>
</head>
<body>
</body>
</html>

И здесь test-vanishing-global.js:

console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar);

Инструкция:

Поместите эти два файла в каталог и откройте HTML в браузере (протестированы в последних версиях Chrome и Firefox, в обоих случаях).

Это результат, который я получаю:

this == document: false
this == doc:      true
_doc == document: true
_doc == doc:      false

name: foobar
window.vanishing_global: 1366037771608
typeof window.vanishing_global: number
document.foobar: barfoo:0.9013048021588475

name: 
window.vanishing_global: undefined
typeof window.vanishing_global: undefined
document.foobar: foobar:0.5015988759696484

Объект this внутри обработчика должен быть либо объектом документа. Это объект документа, но не тот же объект документа, что и документ, который он запускает внутри (он также не совпадает с родительским документом). Объект окна внутри обработчика также не совпадает с объектом окна, который выполняется в JavaScript, загруженном на страницу.

Итак, наконец, мой вопрос:

Кто-нибудь знает, что происходит, и как я могу либо получить ссылку на фактический объект окна, либо, по крайней мере, объявить и ссылаться на глобальную переменную из одного и того же глобального контекста?

Сноски:

В этом iframe нет междоменных проблем, поскольку они находятся в одном домене. Существует проблема, если кто-то устанавливает document.domain, но это не выполняется в этом примере кода.

4b9b3361

Ответ 1

Вы объявляете все на родительской странице. Таким образом, ссылки на window и document относятся к родительской странице. Если вы хотите сделать материал для iframe, используйте iframe || iframe.contentWindow для доступа к его window и iframe.contentDocument || iframe.contentWindow.document для доступа к его document.

Есть слово для того, что происходит, возможно, "лексический охват": Что такое лексическая область?

Единственным контекстом области является это. И в вашем примере владелец метода doc, который является iframe document. Помимо этого, все, что доступно в этой функции, которая использует известные объекты, является родительским (если не объявлено в функции). Было бы иначе, если бы функция была объявлена ​​в другом месте, но она была объявлена ​​на родительской странице.

Вот как я его написал:

(function () {
  var dom, win, doc, where, iframe;

  iframe = document.createElement('iframe');
  iframe.src = "javascript:false";

  where = document.getElementsByTagName('script')[0];
  where.parentNode.insertBefore(iframe, where);

  win = iframe.contentWindow || iframe;
  doc = iframe.contentDocument || iframe.contentWindow.document;

  doc.open();
  doc._l = (function (w, d) {
    return function () {
      w.vanishing_global = new Date().getTime();

      var js = d.createElement("script");
      js.src = 'test-vanishing-global.js?' + w.vanishing_global;

      w.name = "foobar";
      d.foobar = "foobar:" + Math.random();
      d.foobar = "barfoo:" + Math.random();
      d.body.appendChild(js);
    };
  })(win, doc);
  doc.write('<body onload="document._l();"></body>');
  doc.close();
})();

Сглаживание win и doc как w и d необязательно, это просто может сделать его менее запутанным из-за непонимания областей. Таким образом, они являются параметрами, и вам нужно ссылаться на них, чтобы получить доступ к материалам iframe. Если вы хотите получить доступ к родительскому, вы все равно используете window и document.

Я не уверен, что подразумевается при добавлении методов к document (doc в этом случае), но может быть более целесообразным установить метод _l на win. Таким образом, все может выполняться без префикса... например, <body onload="_l();"></body>