Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Теряется связь с iframe при document.write. (https://javascript.ru/forum/misc/73365-teryaetsya-svyaz-s-iframe-pri-document-write.html)

ruslan_mart 09.04.2018 22:47

Теряется связь с 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 не срабатывает.

рони 09.04.2018 23:10

ruslan_mart,
страница формируется заново, если document.write используют после загрузки страницы.

ruslan_mart 09.04.2018 23:34

рони, ну окружение по идее не меняется, все переменные и функции остаются. Тут только непонятно, почему связь с contentWindow теряется.

brizing 09.04.2018 23:39

Эвент листенер курильщика:
https://ucalc.pro/help/src/b9/04-09-1pxjt.png

Эвент листенер здорового человека:
https://ucalc.pro/help/src/b9/04-09-xwc1n.png

j0hnik 09.04.2018 23:51

<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>

рони 09.04.2018 23:51

Цитата:

Сообщение от ruslan_mart
Тут только непонятно, почему связь с contentWindow теряется.

так нет же никакого прежнего
contentWindow

ruslan_mart 10.04.2018 00:25

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?

ruslan_mart 10.04.2018 00:33

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 10.04.2018 00:42

Всё, понял в чём подвох, спасибо.

j0hnik 10.04.2018 00:42

ruslan_mart,
?

ruslan_mart 10.04.2018 00:47

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

})();

brizing 10.04.2018 09:35

Цитата:

Сообщение от ruslan_mart
подвох в том, что при document.write - window как-то обнуляется, т.е. ссылка на него остаётся та же, но он рефрешится. А все глобальные переменные остаются на месте.

Документ перезаписывается.
Обработчик события висит на документе, так что он пропадает.
Можно заново назначать обработчик.

(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, время: 15:22.