origin

Recently, I happened to need a small function in a project. Briefly describe it

Open the new song window from page A to page B. After page B completes A series of operations, page B needs to be closed automatically, and then page A completes A refreshing action.

I thought this is cross-window communication, and then I searched online search engine, and found that there are not many related articles, the most beautiful is that most of them are copied and pasted, and they are written around iframe, not really two window interaction, so there is such a hole article.

There are a number of solutions, but I’ll just talk about postMessage. This article focuses on how to address this requirement, and nothing else.

postMessage

As a new feature of cross-source communication, messages can be sent as long as the window to be sent can be obtained, and some insecure messages can be filtered on the receiving side to prevent security problems.

It doesn’t seem to have any compatibility issues, just great.

implementation

First of all, there are several different terms used in the article, but they all mean the same thing

T1. HTML === Indicates the first page opened on page A

T2.html === Page B === window.open Opens the page

Useless version

Looking at the MDN documentation, I naively thought that all I needed was the following code.

// t1.html
<script>
  window.addEventListener('message'.function(event) {
    console.log('event :>> ', event);
  })
</script>
Copy the code
// t2.html
<script>
  window.postMessage('123'.'/')
</script>
Copy the code

Then I realized it didn’t work at all (still not reading the document carefully) until I got to this point in the document

The window object of another window needs to call postMessage in order for the message event to be triggered.

But let’s recall the initial requirement: page A needs to open A new window-page B, and then page B sends A message to page A. The idea at this time is like this.

The second edition

If you call postMessage and call otherWindow, that means you need page B and you need page A’s window object to send A message.

Manage the logic

1. When page A opens page B, page A can return the value from window.open and get the window of page B

2. Page A sends A message to page B

3. Page B receives the message from page A and obtains the window of page A

4. At this time, page A has the window of page B, and page B also has the window of page A. As long as both sides listen for message events, two-way communication can be established.

So here’s the code

// t1.html
<h1>This is T1</h1>
<button id="btn1">Open the t2</button>
<button id="btn2">Send a message to T2</button>
<pre id="text"></pre>

<script>
  var t2
  var btn1 = document.getElementById('btn1')
  var btn2 = document.getElementById('btn2')
  var text = document.getElementById('text')

  btn1.addEventListener('click'.function() {
    t2 = window.open('/t2.html')
  })

  btn2.addEventListener('click'.function() {
    console.log('btn2 click');
    // Type is used to distinguish messages sent from other applications. The last parameter '/' indicates that it is valid only in the current domain
    t2.postMessage({type: 'popring'.message: 'Outgoing message from T1'}, '/')})window.addEventListener('message'.function(event) {
    // Filter messages that are not in the current domain
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    // Filter messages that are not sent by the app. For example, chrome plug-ins may also send messages (wappalyzer does).
    if(event.data? .type ! = ='popring') {
      return
    }
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code
// t2.html
<h1>This is a T2</h1>
<button id="btn">Send a message to T1</button>
<pre id="text"></pre>

<script>
  var btn = document.querySelector('#btn')
  var text = document.getElementById('text')
  var t1

  btn.addEventListener('click'.function() {
    t1.postMessage({type: 'popring'.message: 'Message sent from T2'}, '/')})window.addEventListener('message'.function(event) {
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    if(event.data? .type ! = ='popring') {
      return
    }
    t1 = event.source
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code

At this time, after opening page B from page A, you need to click the button on page A to send A message to page B, so that page B can get the window of page A after receiving the message. It is slightly troublesome, so it is better to automate this part.

The third edition

According to the above problems, the solution is actually very simple, since you can get the window of page B, you can trigger the event after rewriting page B is loaded. So here’s the solution, onLoad, DOMContentLoaded.

After several attempts, it is found that page A uses onload directly. If page B also uses onload event, the event of page A will be overwritten. You need to use addEventListener to listen for window load or DOMContentLoaded to work. The addEventListener features are as follows.

<h1>This is T1</h1>
<button id="btn1">Open the t2</button>
<button id="btn2">Send a message to T2</button>
<pre id="text"></pre>

<script>
  var t2
  var btn1 = document.getElementById('btn1')
  var btn2 = document.getElementById('btn2')
  var text = document.getElementById('text')

  btn1.addEventListener('click'.function() {
    t2 = window.open('/t2.html')
    // If onload is not overridden on t2, then it is ok to do so on t1, but if onload is overridden on t2, then the following methods do not take effect.
    t2.onload = () = > {
      btn2.click()
    }
    // It can be rewritten as follows
    t2.window.addEventListener('load'.function() {
        btn2.click()
    })
  })

  btn2.addEventListener('click'.function() {
    console.log('btn2 click');
    t2.postMessage({type: 'popring'.message: 'Outgoing message from T1'}, '/')})window.addEventListener('message'.function(event) {
    // Filter messages that are not in the current domain
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    // Filter messages that are not sent by the app. For example, chrome plug-ins may also send messages (wappalyzer does).
    if(event.data? .type ! = ='popring') {
      return
    }
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code
<h1>This is a T2</h1>
<button id="btn">Send a message to T1</button>
<pre id="text"></pre>

<script>
  var btn = document.querySelector('#btn')
  var text = document.getElementById('text')
  var t1
  
  // t2 defines the onload event
  window.onload = function() {
    console.log('t2 onload');
  }
  
  
  btn.addEventListener('click'.function() {
    t1.postMessage({type: 'popring'.message: 'Message sent from T2'}, '/')})window.addEventListener('message'.function(event) {
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    if(event.data? .type ! = ='popring') {
      return
    }
    t1 = event.source
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code

This time using DOMContentLoaded has the following effect.

The fourth edition

Now that you can communicate between the two Windows, that’s all you need to do. I’ve found some interesting apis.

Close the window

window.close()
Copy the code

Just note that this command can only close Windows opened with JS.

Focusing on the window

window.focus()
Copy the code

After testing, it can only focus from page A to page B. Page B cannot call this function to page A.

Check whether the window is closed

window.closed
Copy the code

The final completion of native HTML, JS code

<h1>This is T1</h1>
<button id="btn1">Open the t2</button>
<button id="btn2">Send a message to T2</button>
<pre id="text"></pre>

<script>
  var t2
  var btn1 = document.getElementById('btn1')
  var btn2 = document.getElementById('btn2')
  var text = document.getElementById('text')

  btn1.addEventListener('click'.function() {
    t2 = window.open('/t2.html')
    window.focus()
    t2.addEventListener('DOMContentLoaded'.function() {
      t2.console.log('DOMContentLoaded for T1 mounted on T2');
      btn2.click()
    })
  })

  btn2.addEventListener('click'.function() {
    console.log('btn2 click');
    // Check whether the T2 page is closed
    if(t2.closed) {
      return
    }
    // postMessage The third parameter is set to '/' to send messages under the current domain
    t2.postMessage({type: 'popring'.message: 'Outgoing message from T1'}, '/')
    t2.focus()
  })

  window.addEventListener('message'.function(event) {
    // Filter messages that are not in the current domain
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    // Filter messages that are not sent by the app. For example, chrome plug-ins may also send messages (wappalyzer does).
    if(event.data? .type ! = ='popring') {
      return
    }
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code
<h1>This is a T2</h1>
<button id="btn">Send a message to T1</button>
<pre id="text"></pre>

<script>
  window.addEventListener('DOMContentLoaded'.function() {
    console.log('t2 DOMContentLoaded');
  })

  var btn = document.querySelector('#btn')
  var text = document.getElementById('text')
  var t1

  btn.addEventListener('click'.function() {
    // Check whether the T1 page is closed
    if(t1.closed) {
      return
    }
    t1.postMessage({type: 'popring'.message: 'Message sent from T2'}, '/')
    t1.focus()
  })

  window.addEventListener('message'.function(event) {
    if(event.origin ! = ='http://127.0.0.1:5500'| |! event.data) {return
    }
    if(event.data? .type ! = ='popring') {
      return
    }
    t1 = event.source
    text.innerText += (JSON.stringify(event.data)+'\n')})</script>
Copy the code

Codesandbox. IO /s/modest-sh…

It is recommended to open a new window to experience.

result

Finally, summarize the results

PostMessage sends the window object that needs to fetch the receiver

2. When opening page B from page A, the event trigger can be monitored only after page B is finished loading, while page A can sense that page B is finished loading through the DOMContentLoaded event of page B

3. Other related apis

4. This is just a native JS implementation, but if combined with Vue and React frameworks, it will have a different feel, and they have a more detailed life cycle.

reference

MDN postMessage