Элемент iframe является обычным узлом DOM, как и любой другой. Существенное отличие – в том, что с ним связан объект window внутреннего окна. Он доступен по ссылке iframe.contentWindow.
Таким образом, iframe.contentWindow.document будет внутренним документом, iframe.contentWindow.document.body – его <body> и так далее.
В старых браузерах использовались другие свойства, такие как iframe.contentDocument и даже iframe.document, но они давно не нужны.
Переход внутрь ифрейма
В примере ниже JavaScript получает документ внутри ифрейма и модифицирует его:
<iframe src="javascript:'тест'" style="height:60px"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
var iframeDoc = iframe.contentWindow.document;
if (iframeDoc.readyState == 'complete') {
iframeDoc.body.style.backgroundColor = 'green';
}
iframe.onload = function() {
var iframeDoc2 = iframe.contentWindow.document;
iframeDoc2.body.style.backgroundColor = 'orange';
}
</script>
Атрибут src может использовать протокол javascript, как указано выше: src="javascript:код". При этом код выполняется и его результат будет содержимым ифрейма. Этот способ описан в стандарте и поддерживается всеми браузерами.
Атрибут src является обязательным, и его отсутствие может привести к проблемам, вплоть до игнорирования ифрейма браузером. Чтобы ничего не загружать в ифрейм, можно указать пустую строку: src="javascript:''" или специальную страницу: src="about:blank".
В некоторых браузерах (Chrome) пример выше покажет iframe зелёным. А в некоторых (Firefox) – оранжевым.
Дело в том, что, когда iframe только создан, документ в нём обычно ещё не загружен.
При обычных значениях iframe src="...", которые указывают на HTML-страницу (даже если она уже в кеше), это всегда так. Документ, который в iframe на момент срабатывания скрипта iframeDoc – временный, он будет заменён на новый очень скоро. И работать надо уже с новым документом iframeDoc2 – например, по событию iframe.onload.
В случае с javascript-протоколом, по идее, ифрейм уже загружен, и тогда onload у него уже не будет. Но здесь мнения браузеров расходятся, некоторые (Firefox) всё равно «подгрузят» документ позже. Поэтому факт «готовности» документа в скрипте проверяется через iframeDoc.readyState.
Ещё раз заметим, что при обычных URL в качестве src нужно работать не с начальным документом, а с тем, который появится позже.
Кросс-доменность: ограничение доступа к окну
Элемент <iframe> является «двуличным». С одной стороны, это обычный узел DOM, с другой – внутри находится окно, которое может иметь совершенно другой URL, содержать независимый документ из другого источника.
Внешний документ имеет полный доступ к <iframe> как к DOM-узлу. А вот к окну – если они с одного источника.
Это приводит к забавным последствиям. Например, чтобы узнать об окончании загрузки <iframe>, мы можем повесить обработчик iframe.onload. По сути, это то же самое что iframe.contentWindow.onload, но его мы можем поставить лишь в случае, если окно с того же источника.
<iframe src="https://example.com" style="height:100px"></iframe>
<script>
var iframe = document.getElementsByTagName('iframe')[0];
// сработает
iframe.onload = function() {
alert( "iframe onload" );
};
// не сработает
iframe.contentWindow.onload = function() {
alert( "contentWindow onload" );
};
</script>
Если бы в примере выше <iframe src> был с текущего сайта, то оба обработчика сработали бы.
Иерархия window.frames
Альтернативный способ доступа к окну ифрейма – это получить его из коллекции window.frames.
Есть два способа доступа:
window.frames[0]– доступ по номеру.window.frames.iframeName– доступ поnameифрейма.
Обратим внимание: в коллекции хранится именно окно (contentWindow), а не DOM-элемент.
Демонстрация всех способов доступа к окну:
<iframe src="javascript:''" style="height:80px" name="i"></iframe>
<script>
var iframeTag = document.body.children[0];
var iframeWindow = iframeTag.contentWindow; // окно из тега
alert( frames[0] === iframeWindow ); // true, окно из коллекции frames
alert( frames.i == iframeWindow ); // true, окно из frames по имени
</script>
Внутри ифрейма могут быть свои вложенные ифреймы. Всё это вместе образует иерархию.
Ссылки для навигации по ней:
-
window.frames– коллекция «детей» (вложенных ифреймов) -
window.parent– содержит ссылку на родительское окно, позволяет обратиться к нему из ифрейма.Всегда верно:
// (из окна со фреймом) window.frames[0].parent === window; // true -
window.top– содержит ссылку на самое верхнее окно (вершину иерархии).Всегда верно (в предположении, что вложенные фреймы существуют):
window.frames[0].frames[0].frames[0].top === window
Свойство top позволяет легко проверить, во фрейме ли находится текущий документ:
if (window == top) {
alert( 'Этот скрипт является окном верхнего уровня в браузере' );
} else {
alert( 'Этот скрипт исполняется во фрейме!' );
}
Песочница sandbox
Атрибут sandbox позволяет построить «песочницу» вокруг ифрейма, запретив ему выполнять ряд действий.
Наличие атрибута sandbox:
- Заставляет браузер считать ифрейм загруженным с другого источника, так что он и внешнее окно больше не могут обращаться к переменным друг друга.
- Отключает формы и скрипты в ифрейме.
- Запрещает менять
parent.locationиз ифрейма.
Пример ниже загружает в <iframe sandbox> документ с JavaScript и формой. Ни то ни другое не сработает:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe sandbox src="sandboxed.html"></iframe>
</body>
</html><!doctype html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
alert(1);
</script>
<form action="http://google.ru">
<input type="text">
<input type="submit" value="Отправить форму на http://google.ru">
</form>
</body>
</html>Если у атрибута sandbox нет значения, то браузер применяет максимум ограничений.
Атрибут sandbox может содержать через пробел список ограничений, которые не нужны:
- allow-same-origin
- Браузер будет считать документ в ифрейме пришедшим с другого домена и накладывать соответствующие ограничения на работу с ним. Если ифрейм и так с другого домена, то ничего не меняется.
- allow-top-navigation
- Разрешает ифрейму менять
parent.location. - allow-forms
- Разрешает отправлять формы из
iframe. - allow-scripts
- Разрешает выполнение скриптов из ифрейма. Но скриптам, всё же, будет запрещено открывать попапы.
Цель атрибута sandbox – наложить дополнительные ограничения. Он не может снять уже существующие, в частности, убрать ограничения безопасности, если ифрейм с другого источника.
Комментарии
<code>, для нескольких строк кода — тег<pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)