fixes
This commit is contained in:
parent
b300836f00
commit
b1b66a3065
9 changed files with 61 additions and 85 deletions
|
@ -11,12 +11,12 @@ window.open('https://javascript.info/')
|
|||
|
||||
Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: we can load content dynamically with [fetch](info:fetch) and show it in a dynamically generated `<div>`. So, popups is not something we use everyday.
|
||||
|
||||
Also, popups are tricky on mobile devices.
|
||||
Also, popups are tricky on mobile devices, that don't show multiple windows simultaneously.
|
||||
|
||||
Still, there are situations when a popup works good, e.g. for OAuth authorization (login with Google/Facebook/...), because:
|
||||
Still, there are tasks where popups are still used, e.g. for OAuth authorization (login with Google/Facebook/...), because:
|
||||
|
||||
1. A popup is a separate window with its own independent JavaScript environment. So opening a popup with a third-party non-trusted site is safe.
|
||||
2. It's very easy to open a popup, little to no overhead.
|
||||
2. It's very easy to open a popup.
|
||||
3. A popup can navigate (change URL) and send messages to the opener window.
|
||||
|
||||
## Popup blocking
|
||||
|
@ -149,7 +149,7 @@ newWindow.onload = function() {
|
|||
Please note: immediately after `window.open`, the new window isn't loaded yet. That's demonstrated by `alert` in line `(*)`. So we wait for `onload` to modify it. We could also use `DOMContentLoaded` handler for `newWin.document`.
|
||||
|
||||
```warn header="Same origin policy"
|
||||
Windows may only freely modify each other if they come from the same origin (the same protocol://domain:port).
|
||||
Windows may freely access content of each other only if they come from the same origin (the same protocol://domain:port).
|
||||
|
||||
Otherwise, e.g. if the main window is from `site.com`, and the popup from `gmail.com`, that's impossible for user safety reasons. For the details, see chapter <info:cross-window-communication>.
|
||||
```
|
||||
|
@ -158,7 +158,7 @@ Otherwise, e.g. if the main window is from `site.com`, and the popup from `gmail
|
|||
|
||||
A popup may access the "opener" window as well using `window.opener` reference. It is `null` for all windows except popups.
|
||||
|
||||
If you run the code below, it replaces the opener window content with "Test":
|
||||
If you run the code below, it replaces the opener (current) window content with "Test":
|
||||
|
||||
```js run
|
||||
let newWin = window.open("about:blank", "hello", "width=200,height=200");
|
||||
|
@ -172,12 +172,13 @@ So the connection between the windows is bidirectional: the main window and the
|
|||
|
||||
## Closing a popup
|
||||
|
||||
- To close a window: `win.close()`.
|
||||
- To check if a window is closed: `win.close` property.
|
||||
To close a window: `win.close()`.
|
||||
|
||||
To check if a window is closed: `win.closed`.
|
||||
|
||||
Technically, the `close()` method is available for any `window`, but `window.close()` is ignored by most browsers if `window` is not created with `window.open()`. So it'll only work on a popup.
|
||||
|
||||
The `win.closed` property is `true` if the window is closed. That's useful to check if the popup (or the main window) is still open or not. A user can close it anytime, and our code should take that possibility into account.
|
||||
The `closed` property is `true` if the window is closed. That's useful to check if the popup (or the main window) is still open or not. A user can close it anytime, and our code should take that possibility into account.
|
||||
|
||||
This code loads and then closes the window:
|
||||
|
||||
|
@ -259,23 +260,18 @@ For instance:
|
|||
|
||||
## Summary
|
||||
|
||||
Всплывающие окна используются нечасто. Ведь загрузить новую информацию можно динамически, с помощью технологии AJAX, а показать -- в элементе `<div>`, расположенным над страницей (`z-index`). Ещё одна альтернатива -- тег `<iframe>`.
|
||||
Popup windows are used rarely, as there are alternatives: loading and displaying information in-page, or in iframe.
|
||||
|
||||
Но в некоторых случаях всплывающие окна бывают очень даже полезны. Например, отдельное окно сервиса онлайн-консультаций. Посетитель может ходить по сайту в основном окне, а общаться в чате -- во вспомогательном.
|
||||
|
||||
Если вы хотите использовать всплывающее окно, предупредите посетителя об этом, так же и при использовании `target="_blank"` в ссылках или формах. Иконка открывающегося окошка на ссылке поможет посетителю понять, что происходит и не потерять оба окна из поля зрения.
|
||||
If we're going to open a popup, a good practice is to inform the user about it. An "opening window" icon near a link or button would allow the visitor to survive the focus shift and keep both windows in mind.
|
||||
|
||||
- A popup can be opened by the `open(url, name, params)` call. It returns the reference to the newly opened window.
|
||||
- Browsers block `open` calls from the code outside of user actions. Usually a notification appears, so that a user may allow them.
|
||||
- Browsers open a new tab by default, but if sizes are provided, then it'll be a popup window.
|
||||
- The popup may access the opener window using the `window.opener` property.
|
||||
- The main window and the popup can freely read and modify each other if they havee the same origin. Otherwise, they can change location of each other and [exchange messages](cross-window-communication).
|
||||
- The main window and the popup can freely read and modify each other if they havee the same origin. Otherwise, they can change location of each other and [exchange messages.
|
||||
|
||||
Methods and properties:
|
||||
To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that.
|
||||
|
||||
- To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that.
|
||||
- Methods `focus()` and `blur()` allow to focus/unfocus a window. Sometimes.
|
||||
- Methods `focus()` and `blur()` allow to focus/unfocus a window. But they don't work all the time.
|
||||
- Events `focus` and `blur` allow to track switching in and out of the window. But please note that a window may still be visible even in the background state, after `blur`.
|
||||
- ...And a few scrolling and resizing methods.
|
||||
|
||||
If we're going to open a popup, a good practice is to inform the user about it. If there's a link that opens a popup, we could place an icon near it, so that visitor can survive the focus shift and keep both windows in mind.
|
||||
|
|
|
@ -28,12 +28,12 @@ The "Same Origin" policy states that:
|
|||
|
||||
### In action: iframe
|
||||
|
||||
An `<iframe>` tag hosts embbedded window, with its own separate `document` and `window` objects.
|
||||
An `<iframe>` tag hosts a separate embedded window, with its own separate `document` and `window` objects.
|
||||
|
||||
We can access them using properties:
|
||||
|
||||
- `iframe.contentWindow` to get the window inside the `<iframe>`.
|
||||
- `iframe.contentDocument` to get the document inside the `<iframe>`.
|
||||
- `iframe.contentDocument` to get the document inside the `<iframe>`, короткий аналог `iframe.contentWindoe.document`.
|
||||
|
||||
When we access something inside the embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (writing to `location` is an exception, it's still permitted).
|
||||
|
||||
|
@ -102,13 +102,13 @@ The `iframe.onload` event (on the `<iframe>` tag) is essentially the same as `if
|
|||
...But we can't access `iframe.contentWindow.onload` for an iframe from another origin, so using `iframe.onload`.
|
||||
```
|
||||
|
||||
## Iframes on subdomains: document.domain
|
||||
## Windows on subdomains: document.domain
|
||||
|
||||
By definition, two URLs with different domains have different origins.
|
||||
|
||||
But if windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com` (so that their common second-level domain is `site.com`), we can make the browser ignore that difference, so that they can be treated as coming from the "same origin" for the purposes of cross-window communication.
|
||||
|
||||
To make it work, each window (including the one from `site.com`) should run the code:
|
||||
To make it work, each such window should run the code:
|
||||
|
||||
```js
|
||||
document.domain = 'site.com';
|
||||
|
@ -144,22 +144,16 @@ Here, look:
|
|||
|
||||
We shouldn't work with the document of a not-yet-loaded iframe, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.
|
||||
|
||||
...The right document is definitely there when `iframe.onload` triggers. But it only triggers when the whole iframe with all resources is loaded.
|
||||
How to detect the moment when the document is there?
|
||||
|
||||
There's also `DOMContentLoaded` event, that triggers sooner than `onload`. As we assume that the iframe comes from the same origin, we can setup the event handler. But we should set it on the right document, so we need to detect when it's there.
|
||||
The right document is definitely at place when `iframe.onload` triggers. But it only triggers when the whole iframe with all resources is loaded.
|
||||
|
||||
Here's a small recipe for this.
|
||||
|
||||
We can try to catch the moment when a new document appears using checks in `setInterval`, and then setup necessary handlers:
|
||||
We can try to catch the moment earlier using checks in `setInterval`:
|
||||
|
||||
```html run
|
||||
<iframe src="/" id="iframe"></iframe>
|
||||
|
||||
<script>
|
||||
function onDocumentLoaded() {
|
||||
iframe.contentDocument.body.prepend('Hello, world!');
|
||||
}
|
||||
|
||||
let oldDoc = iframe.contentDocument;
|
||||
|
||||
// every 100 ms check if the document is the new one
|
||||
|
@ -167,14 +161,7 @@ We can try to catch the moment when a new document appears using checks in `setI
|
|||
let newDoc = iframe.contentDocument;
|
||||
if (newDoc == oldDoc) return;
|
||||
|
||||
// new document
|
||||
if (newDoc.readyState == 'loading') {
|
||||
// loading yet, wait for the event
|
||||
newDoc.addEventListener('DOMContentLoaded', onDocumentLoaded);
|
||||
} else {
|
||||
// DOM is ready!
|
||||
onDocumentLoaded();
|
||||
}
|
||||
alert("New document is here!");
|
||||
|
||||
clearInterval(timer); // cancel setInterval, don't need it any more
|
||||
}, 100);
|
||||
|
@ -227,11 +214,11 @@ if (window == top) { // current window == window.top?
|
|||
|
||||
The `sandbox` attribute allows for the exclusion of certain actions inside an `<iframe>` in order to prevent it executing untrusted code. It "sandboxes" the iframe by treating it as coming from another origin and/or applying other limitations.
|
||||
|
||||
There's a "default set" of restrictions applied for `<iframe sandbox src="...">`. But it can be relaxed if we provide a space-separated list of keywords for restrictions that should not be applied as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`.
|
||||
There's a "default set" of restrictions applied for `<iframe sandbox src="...">`. But it can be relaxed if we provide a space-separated list of restrictions that should not be applied as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`.
|
||||
|
||||
In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift.
|
||||
|
||||
Here's a list of limitations. By default, all are applied. We can disable each by specifying the corresponding keyword in the `sandbox` attribute:
|
||||
Here's a list of limitations:
|
||||
|
||||
`allow-same-origin`
|
||||
: By default `"sandbox"` forces the "different origin" policy for the iframe. In other words, it makes the browser to treat the `iframe` as coming from another origin, even if its `src` points to the same site. With all implied restrictions for scripts. This option removes that feature.
|
||||
|
@ -375,7 +362,7 @@ Exceptions are:
|
|||
- Windows that share the same second-level domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into the "same origin" state.
|
||||
- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site.
|
||||
|
||||
The `postMessage` interface allows two windows to talk with security checks:
|
||||
The `postMessage` interface allows two windows with any origins to talk:
|
||||
|
||||
1. The sender calls `targetWin.postMessage(data, targetOrigin)`.
|
||||
2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the origin `targetOrigin`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue