Как получить ссылку на объект окна iframe внутри обработчика загрузки iframe, созданного из родительского окна
Перед тем, как вставить любой код, вот сценарий:
- У меня есть HTML-документ, который создает пустой iframe с использованием JavaScript
- JavaScript создает функцию и присоединяет ссылку на эту функцию к объекту документа iframe (используя
doc.open()
получить ссылку на документ) - Затем функция прикрепляется как
onload
обработчик для документа iframe (написав<body onload="...">
в iframe.
Что меня озадачило, так это то, что объекты global (window) и document внутри обработчика onload (пока он работает) отличаются от тех же объектов, которые выполняются через JavaScript, добавленный через узлы сценария.
Вот 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's not
console.log("this == document: %s", this == document);
console.log("this == doc: %s", this == doc);
// the next two lines added based on @Ian's 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
, но это не делается в этом примере кода.
1 ответ
Вы объявляете все на родительской странице. Так что ссылки на window
а также document
находятся на родительской странице. Если вы хотите сделать что-то для iframe
х, использовать iframe || iframe.contentWindow
чтобы получить доступ к его window
, а также iframe.contentDocument || iframe.contentWindow.document
чтобы получить доступ к его document
,
Есть слово для того, что происходит, возможно, "лексическая область": что такое лексическая область?
Единственный контекст области видимости - это. И в вашем примере, владелец метода doc
, какой iframe
"s 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>