Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 20 additions & 24 deletions 3-frames-and-windows/03-cross-window-communication/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
- Получения ссылки на внутренний объект `window` из `iframe.contentWindow`
- Изменения `location`.

С другой стороны, если у ифрейма тот же источник, то с ним можно делать всё, что угодно:
С другой стороны, если у ифрейма тот же источник, то с ним можно делать всё что угодно:

```html run
<!-- ифрейм с того же сайта -->
Expand All @@ -96,9 +96,9 @@
```

```smart header="`iframe.onload` и `iframe.contentWindow.onload`"
Событие `iframe.onload` - по сути то же, что и `iframe.contentWindow.onload`. Оно сработает, когда встроенное окно полностью загрузится со всеми ресурсами.
Обработчик события `load` в свойстве `iframe.onload` (на элементе `<iframe>`) - по сути то же, что и `iframe.contentWindow.onload` (на объекте `window` внутри `<iframe>`). Оба сработают, когда встроенное окно полностью загрузится со всеми ресурсами.

...Но `iframe.onload` всегда доступно извне ифрейма, в то время как доступ к `iframe.contentWindow.onload` разрешён только из окна с тем же источником.
...Но свойство `iframe.onload` всегда доступно извне ифрейма, в то время как доступ к `iframe.contentWindow.onload` разрешён только из окна с тем же источником.
```

## Окна на поддоменах: document.domain
Expand Down Expand Up @@ -256,7 +256,7 @@ if (window == top) { // текущий window == window.top?

### postMessage

Окно, которое хочет отправить сообщение, должно вызвать метод [postMessage](mdn:api/Window/postMessage) окна получателя. Другими словами, если мы хотим отправить сообщение в окно `win`, тогда нам следует вызвать `win.postMessage(data, targetOrigin)`.
Окно, которое хочет отправить сообщение, должно вызвать метод [postMessage](mdn:api/Window/postMessage) окна-получателя. Другими словами, если мы хотим отправить сообщение в окно `targetWin`, тогда нам следует вызвать `targetWin.postMessage(data, targetOrigin)`.

Аргументы:

Expand All @@ -266,19 +266,19 @@ if (window == top) { // текущий window == window.top?
`targetOrigin`
: Определяет источник для окна-получателя, только окно с данного источника имеет право получить сообщение.

Указание `targetOrigin` является мерой безопасности. Как мы помним, если окно (получатель) происходит из другого источника, мы из окна-отправителя не можем прочитать его `location`. Таким образом, мы не можем быть уверены, какой сайт открыт в заданном окне прямо сейчас: пользователь мог перейти куда-то, окно-отправитель не может это знать.
Указание `targetOrigin` является мерой безопасности. Как мы помним, если окно-получатель происходит из другого источника, мы из окна-отправителя не можем прочитать его `location`. Таким образом, мы не можем быть уверены, какой сайт открыт в заданном окне прямо сейчас: пользователь мог перейти куда-то, окно-отправитель не может это знать.

Если указать `targetOrigin`, то мы можем быть уверены, что окно получит данные только в том случае, если в нём правильный сайт. Особенно это важно, если данные конфиденциальные.

Например, здесь `win` получит сообщения только в том случае, если в нём открыт документ из источника `http://example.com`:
Например, здесь `targetWin` получит сообщения только в том случае, если в нём открыт документ из источника `http://example.com`:

```html no-beautify
<iframe src="http://example.com" name="example">

<script>
let win = window.frames.example;
let targetWin = window.frames.example;

win.postMessage("message", "http://example.com");
targetWin.postMessage("message", "http://example.com");
</script>
```

Expand All @@ -288,18 +288,18 @@ if (window == top) { // текущий window == window.top?
<iframe src="http://example.com" name="example">

<script>
let win = window.frames.example;
let targetWin = window.frames.example;

*!*
win.postMessage("message", "*");
targetWin.postMessage("message", "*");
*/!*
</script>
```


### Событие message

Чтобы получать сообщения, окно-получатель должно иметь обработчик события `message` (сообщение). Оно срабатывает, когда был вызван метод `postMessage` (и проверка `targetOrigin` пройдена успешно).
Чтобы получать сообщения, окно-получатель должно иметь обработчик события `message` (сообщение). Оно происходит, когда был вызван метод `postMessage` (и проверка `targetOrigin` пройдена успешно).

Объект события имеет специфичные свойства:

Expand All @@ -312,7 +312,7 @@ if (window == top) { // текущий window == window.top?
`source`
: Ссылка на окно-отправитель. Можно сразу отправить что-то в ответ, вызвав `source.postMessage(...)`.

Чтобы добавить обработчик, следует использовать метод `addEventListener`, короткий синтаксис `window.onmessage` не работает.
Чтобы добавить обработчик, следует использовать метод `addEventListener` (или `window.onmessage`, если нужен только один обработчик).

Вот пример:

Expand All @@ -333,10 +333,6 @@ window.addEventListener("message", function(event) {

[codetabs src="postmessage" height=120]

```smart header="Без задержек"
Между `postMessage` и событием `message` не существует задержки. Событие происходит синхронно, быстрее, чем `setTimeout(...,0)`.
```

## Итого

Чтобы вызвать метод или получить содержимое из другого окна, нам, во-первых, необходимо иметь ссылку на него.
Expand All @@ -348,13 +344,13 @@ window.addEventListener("message", function(event) {
Для ифреймов мы можем иметь доступ к родителям/потомкам, используя:
- `window.frames` -- коллекция объектов `window` вложенных ифреймов,
- `window.parent`, `window.top` -- это ссылки на родительское окно и окно самого верхнего уровня,
- `iframe.contentWindow` -- это объект `window` внутри тега `<iframe>`.
- `iframe.contentWindow` -- это объект `window` внутри `<iframe>`.

Если окна имеют одинаковый источник (протокол, домен, порт), то они могут делать друг с другом всё, что угодно.
Если окна имеют одинаковый источник (протокол, домен, порт), то они могут делать друг с другом всё что угодно.

В противном случае возможны только следующие действия:
- Изменение свойства location другого окна (доступ только на запись).
- Отправить туда сообщение.
- Изменение свойства `location` другого окна (доступ только на запись).
- Отправка туда сообщения.

Исключения:
- Окна, которые имеют общий домен второго уровня: `a.site.com` и `b.site.com`. Установка свойства `document.domain='site.com'` в обоих окнах переведёт их в состояние "Одинакового источника".
Expand All @@ -363,10 +359,10 @@ window.addEventListener("message", function(event) {
Метод `postMessage` позволяет общаться двум окнам с любыми источниками:

1. Отправитель вызывает `targetWin.postMessage(data, targetOrigin)`.
2. Если `targetOrigin` не `'*'`, тогда браузер проверяет имеет ли `targetWin` источник `targetOrigin`.
2. Если `targetOrigin` не `'*'`, тогда браузер проверяет, имеет ли `targetWin` источник `targetOrigin`.
3. Если это так, тогда `targetWin` вызывает событие `message` со специальными свойствами:
- `origin` -- источник окна отправителя (например, `http://my.site.com`)
- `source` -- ссылка на окно отправитель.
- `data` -- данные, может быть объектом везде, кроме IE (в IE только строки).
- `origin` -- источник окна-отправителя (например, `http://my.site.com`)
- `source` -- ссылка на окно-отправитель.
- `data` -- данные, может быть объектом везде, кроме IE, поддерживающего только строки.

В окне-получателе следует добавить обработчик для этого события с помощью метода `addEventListener`.