about
08/09/2024
WebDev MsgSndr
WebDev Code

WebDev MsgSndr

communication between parent page and iframe child using JavaScript messages

1.0 JavaScript Messaging

This page demonstrates JavaScript messaging by sending messages to another page embedded in the iframe below and receiving its replies. Clicking buttons just above the iframe result in messages sent to the child page. If the child replies that results in a text display in the area to the right of the two message buttons.

2.0 Messages

The demonstration uses these JavaScript messages:
Message Sender Receiver
"sections" Sent by WebDev_MsgSndr to request receiver display its sections menu Received by WebDev_MsgRcvr which calls its JavaScript method: bottomMenu.sections() to toggle display of its sections menu.
"request" Sent by WebDev_MsgSndr to request receiver to send back a reply message Received by WebDev_MsgRcvr which posts a reply message containing its url.
"url" Reply sent by WebDev_MsgRcvr back to WebDev_MsgSndr in response to its request Received by WebDev_MsgSndr which posts a "Received Reply" string to display block, and logs the url to the JavaScript console.

3.0 Comments

Browsers classify pages from a static site as each coming from its own domain. That means that a page from a static site is not allowed to directly run JavaScript in another page. That's cross-site scripting, a security vulnerability. However, one page can send a JavaScript message to another page requesting it run its own JavaScript. That is what Explorers from this site do when the "Page Sections" button is clicked. The button click sends a message to the hosted page to display its page sections for navigation. This demo just abstracts away all the other details to illustrate how that message-passing works. JavaScript, CSS, and page markup for both iframe host and child are shown in the code blocks below the iframe.
No Reply

4.0 Code Excerpts

The two panels below show messaging code for both WebDev_MsgSndr.html and WebDev_MsgRcvr.html.
MsgSndr
MsgRcvr
WebDev_MsgSndr JavaScript
<script>
  /*-- send request message to iframe child MsgRsvr --*/
  function postMsg(data) {

    /* msg should be 'sections' or 'reply' */
    let ifrm = document.getElementById("pgframe");
    ifrm.contentWindow.postMessage(data, '*');

    /*-- clear display --*/
    let rply = document.getElementById('rply');
    rply.innerHTML = "No Reply";
  }
  /*-----------------------------------------------------
    The onmessage(event) function is a message handler 
    installed by JavaScript runtime in a message event
    listener. It is invoked when a message is posted 
    by another window.

    The function supplies application specific processing
    for each type of message used by the application.
  -----------------------------------------------------*/
  window.onmessage = function (evnt) {
    if (evnt === null) {
      console.log('null msg');
      return;
    }
    if (evnt.data === 'sections') {

      /*------------------------------------------------- 
        message from Explorer parent so toggle display 
        of sectons list 
      -------------------------------------------------*/
      bottomMenu.sections();

    } else {

      /*-- message from MsgRcvr child --*/
      console.log('msg: ', evnt.data);
      let rply = document.getElementById('rply');
      rply.innerHTML = "Received Reply";

    }
  }
</script>








WebDev_MsgSndr HTML Markup
<div>
  <t-b>
    <div style="display:flex; flex-direction:row; gap:1em;">
      <button onclick="postMsg('sections')" class="msgButton">
        Send Message 'sections'
      </button>
      <button onclick="postMsg('request')" class="msgButton">
        Send Message 'request'
      </button>
      <div id="rply" class="replyDisplay">No Reply</div>
    </div>
  </t-b>
  <div style="height:0.75em;"></div>
  <div>
    <iframe 
      id="pgframe" 
      class="iFrame" 
      src="WebDev_MsgRcvr.html" 
      name="rtcnt">
    </iframe>
  </div>
</div>




WebDev_MsgSndr CSS
<style>
  #github .msgButton {
    cursor: pointer;
    padding: 0.25em 0.75em;
    font-size: 1em;
    border: 2px solid var(--dark);
  }

  #github .replyDisplay {
    padding: 0.25em 0.75em;
    font-size: 1em;
    border: 2px solid var(--dark);
  }

  #github .iFrame {
    height: 30em;
    width: 99%;
    border: 2px solid var(--dark);
  }

  #github .codeHeader {
    background-color: var(--light);
    padding: 0.15em 1.25em;
    font-weight: bold;
    font-size: 1em;
  }
</style>
WebDev_MsgRcvr JavaScript
<script>
  /*-- send reply message to parent MsgSndr --*/
  function postMsg(msg) {
    let parent = window.parent;
    parent.postMessage(msg, '*');
  }
  /*-----------------------------------------------------
    The onmessage(event) function is a message handler
    installed by JavaScript runtime in a message event
    listener. It is invoked when a message is posted
    by another window.

    The function supplies application specific processing
    for each type of message used by the application.
  -----------------------------------------------------*/
  /*-- receive message from parent MsgSndr --*/
  window.onmessage = function (evnt) {
    let msg = evnt.data;
    if (evnt === null | evnt.data === 'sections') {

      /*-- toggle display of sectons list --*/
      bottomMenu.sections();

    } else {

      if (evnt.data === 'request') {
        let url = window.location.href;
        /*-- post url back to parent MsgSndr --*/
        postMsg(url);
      }
    }
  }
</script>

<script>
/*----------------------------------------------- 
  used by onmessage(evnt) to toggle display 
  of page-sections list 
------------------------------------------------*/

bottomMenu.sections = function () {
  var menu = document.getElementById("sections");
  if (isDefined(menu)) {
    if (menu.style.display == "flex")
      menu.style.display = "none";
    else
      menu.style.display = "flex";
  }
}
</script>


WebDev_MsgRcvr HTML Markup
<page-sections id="sections" style="display:none;">
  <sec-elem style="width:0.0em"> </sec-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#bottom">bottom</a>
  </menu-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#actions">actions</a>
  </menu-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#request">request</a>
  </menu-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#sect">sections</a>
  </menu-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#demo">demo</a>
  </menu-elem>
  <menu-elem class="secElem">
    <a target="_self" href="#top">top</a>
  </menu-elem>
  <div class='darkItem popupHeader' 
    style="padding:0.25em 2.0em;" 
    onclick="this.parentElement.style.display = 'none'">
      Sections
  </div>
</page-sections>
WebDev_MsgRcvr CSS
<!--
  The markup above and this set of styles completely define
  the "sections" popup.
-->

<style>
  #github page-sections {
    position: fixed;
    right: 0em;
    bottom: calc(2.0em);
    max-height: calc(100% - 6em - 6px);
    padding: 0em 0.25em;
    display: flex;
    flex-direction: column-reverse;
    overflow-y: auto;
    z-index: 1000;
    background-color: var(--light);
    color: var(--dark);
    border: 5px double var(--dark);
  }

  #github page-sections a {
    color: var(--dark);
  }
</style>
Note: There is one peculiar thing about the code above. If you look closely, you will see that the page-sections markup elements are in the reverse order you might expect. Then, the page-sections styles reverse the order. That little bit of code strangeness is an artifact left from an earlier menu system. By the time the current system was installed, there were too many pages to go back and make the required changes, e.g., reorder the markup and remove the reverse from flex-direction.
  Next Prev Pages Sections About Keys