Теряется связь с iframe при document.write.
Всем доброго времени суток!
Кто-нибудь может объяснить в чём тут подвох?
(function() {
var frame = document.createElement('iframe');
var write = function(content) {
frame.contentWindow.postMessage(content, '*');
};
frame.onload = function() {
write('<div>Line 1</div>');
write('<div>Line 2</div>');
};
frame.src = 'data:text/html,' + encodeURIComponent(`
<script>
window.addEventListener('message', function(event) {
console.log(event.data);
document.write(event.data);
});
<\/script>
`);
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(frame);
});
})();
После document.write внутри iframe - полностью теряется связь с ним. Как с этим бороться? На примере видим, что после первого "write", второй - совсем перестаёт работать, даже onmessage не срабатывает. |
ruslan_mart,
страница формируется заново, если document.write используют после загрузки страницы. |
рони, ну окружение по идее не меняется, все переменные и функции остаются. Тут только непонятно, почему связь с contentWindow теряется.
|
Эвент листенер курильщика:
https://ucalc.pro/help/src/b9/04-09-1pxjt.png Эвент листенер здорового человека: https://ucalc.pro/help/src/b9/04-09-xwc1n.png |
<script>
(function() {
var frame = document.createElement('iframe');
var write = function(content) {
frame.contentWindow.postMessage(content, '*');
};
frame.src = 'data:text/html,' + encodeURIComponent(`
<script>
window.addEventListener('message', function(event) {
console.log(event.data);
document.write(event.data);
});
<\/script>
`);
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(frame);
document.querySelector('iframe').contentWindow.document.write('<div>Line 1</div>');
document.querySelector('iframe').contentWindow.document.write('<div>Line 2</div>');
});
})();
</script>
|
Цитата:
contentWindow |
j0hnik, слишком очевидный вариант, не подойдёт, так как у меня iframe будет изолирован через sandbox.
рони,
(function() {
var frame = document.createElement('iframe');
frame.sandbox = 'allow-scripts';
var write = function(content) {
frame.contentWindow.postMessage(content, '*');
};
frame.onload = function() {
write(`
<script>
setInterval(function() {
document.write('<div>Line ' + index++ + '</div>');
}, 1000);
<\/script>
`);
setTimeout(function() {
write('<div><b>Bold line</b></div>');
}, 3000);
};
frame.src = 'data:text/html,' + encodeURIComponent(`
<script>
var index = 1;
window.addEventListener('message', function(event) {
console.log(event.data);
document.write(event.data);
});
<\/script>
`);
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(frame);
});
})();
Вот тут наглядно видно, что первый write срабатывает и начинает бесконечно отрабатывать интервал, переменная index прибавляется. А значит, окружение не меняется, физически остаётся всё то же самое. Непонятно только одно, почему contentWindow становится другой. index ведь остаётся, а куда тогда пропадает обработчик message? |
window.addEventListener('message', function() {
console.log('MESSAGE');
});
window.dispatchEvent(new Event('message')); //Работает
document.addEventListener('DOMContentLoaded', function() {
document.write('<div>Line 1</div>');
console.log('LOG'); //Работает
window.dispatchEvent(new Event('message')); //Не работает
});
Ну вот какого чёрта? Почему так? window ведь тот же, почему message перестаёт реагировать после document.write? |
Всё, понял в чём подвох, спасибо.
|
ruslan_mart,
? |
j0hnik, подвох в том, что при document.write - window как-то обнуляется, т.е. ссылка на него остаётся та же, но он рефрешится. А все глобальные переменные остаются на месте.
Решение:
(function() {
var frame = document.createElement('iframe');
frame.sandbox = 'allow-scripts';
var write = function(content) {
frame.contentWindow.postMessage(JSON.stringify({type: 'write', value: content}), '*');
};
var close = function() {
frame.contentWindow.postMessage(JSON.stringify({type: 'close'}), '*');
};
frame.onload = function() {
this.onload = null;
write('<div>Line 1</div>');
write('<div>Line 2</div>');
write('<b>Bold text</b>');
close();
write('<div>Line 3</div>');
write('<div>Line 4</div>');
write('<b>Bold text</b>');
close();
};
frame.src = 'data:text/html,' + encodeURIComponent(`
<script>
(function() {
var documentWrite = document.write.bind(document);
var onMessage = function(event) {
var data = JSON.parse(event.data);
if(data.type === 'write') {
document.write(data.value);
}
else if(data.type === 'close') {
document.close();
}
};
document.write = function(content) {
var refreshed = document.readyState !== 'loading';
documentWrite(content);
if(refreshed) {
window.onmessage = onMessage;
}
};
window.onmessage = onMessage;
})();
<\/script>
`);
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(frame);
});
})();
|
Цитата:
Обработчик события висит на документе, так что он пропадает. Можно заново назначать обработчик.
(function() {
var frame = document.createElement('iframe');
var write = function(content) {
frame.contentWindow.postMessage(content, '*');
};
frame.onload = function() {
write('<div>Line 1</div>');
write('<div>Line 2</div>');
};
frame.src = 'data:text/html,' + encodeURIComponent(`
<script>
var onMessage = function onMessageFn (event) {
console.log(event.data);
document.write(event.data);
window.onmessage = onMessageFn;
};
window.onmessage = onMessage;
<\/script>
`);
document.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(frame);
});
})();
|
| Часовой пояс GMT +3, время: 21:09. |