直接Windowを参照する
オリジン(プロトコル+ポート+ホスト)が同じ場合は、親はopen()した返り値で、子はwindow.openerで相手のwindowが取れて、直接参照したりDOMを操作したりすることもできる。
同じ/異なるオリジンのiframeの中からできること - sambaiz-net
$ cat index.html
<button id="btn">Open window</button>
<button id="btn2">Close window</button>
<div id="view"></div>
<script>
let win2;
const button = document.getElementById("btn");
button.addEventListener("click", () => {
window.foo = "bar from window1";
win2 = window.open("index2.html");
}, false);
const button2 = document.getElementById("btn2");
button2.addEventListener("click", () => {
if (win2) {
win2.close();
}
}, false);
</script>
$ cat index2.html
<button id="btn">Close window</button>
<div id="view"></div>
<script>
console.log(window.aaa);
const parentWindow = window.opener;
const view = document.getElementById("view");
view.textContent = parentWindow.foo; // window1 -> window2
const button = document.getElementById("btn");
button.addEventListener("click", () => {
if(!parentWindow) {
window.close();
}
const view = parentWindow.document.getElementById("view");
if (view) {
view.textContent = "Window2 has been closed by myself"; // window2 -> window1
}
window.close();
}, false);
</script>
確認する際はnode-staticなどでサーバーを立てる必要がある。
postMessage()を使う
postMessageで送り、messageのeventで受け取れる。オリジンが異なっていてもよいが、 どこからでも送れてしまうので、ハンドリングする際はevent.originが意図したものかチェックしなくてはならない。
$ cat index.html
<button id="btn">Open window</button>
<button id="btn2">Post to window</button>
<div id="view"></div>
<script>
const view = document.getElementById("view");
const receiveMessage = (event) => {
if (event.origin === "http://localhost:8081") {
view.textContent = event.data;
}
}
window.addEventListener("message", receiveMessage, false);
let win2;
const button = document.getElementById("btn");
button.addEventListener("click", () => {
win2 = window.open("http://localhost:8081/index2.html");
}, false);
const button2 = document.getElementById("btn2");
button2.addEventListener("click", () => {
win2.postMessage("bar from window1", "http://localhost:8081");
}, false);
</script>
$ cat index2.html
<script>
const receiveMessage = (event) => {
if (event.origin === "http://localhost:8080") {
event.source.postMessage(
`window2 received data: ${event.data}`,
event.origin
);
}
window.close();
}
window.addEventListener("message", receiveMessage, false);
</script>
別のポートでも値が渡されていることが確認できる。
ReactでpostMessageする
componentDidMount() でaddEventListenerして componentWillUnmount() でremoveする。
$ cat index.jsx
class App extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleMessage = this.handleMessage.bind(this);
this.openWindow = this.openWindow.bind(this);
}
componentDidMount() {
window.addEventListener('message', this.handleMessage);
}
componentWillUnmount() {
window.removeEventListener('message', this.handleMessage);
}
handleMessage(event) {
if (
(window.opener && event.origin === "http://localhost:8080") ||
(!window.opener && event.origin === "http://localhost:8081")
) {
this.setState({value: event.data});
}
}
handleChange(event) {
this.setState({value: event.target.value});
if (this.newWindow) {
this.newWindow.postMessage(this.state.value, "http://localhost:8081"); // window1 -> window2
} else if (window.opener) {
window.opener.postMessage(this.state.value, "http://localhost:8080"); // window2 -> window1
}
}
openWindow(event) {
this.newWindow = window.open('http://localhost:8081/index.html', '_blank', 'width=640, height=480');
}
render() {
return <div>
{ !window.opener ? <button onClick={this.openWindow}>Open Window</button> : <div></div>}
<input type="text" value={this.state.value} onChange={this.handleChange} />
</div>;
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
$ cat index.html
<dic id="root"></div>
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script src="index.js"></script>
$ yarn add --dev @babel/cli @babel/core @babel/preset-react
$ echo '{ "presets": ["@babel/preset-react"] }' > .babelrc
$ ./node_modules/.bin/babel index.jsx -o index.js