Элемент 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
– наложить дополнительные ограничения. Он не может снять уже существующие, в частности, убрать ограничения безопасности, если ифрейм с другого источника.