Search code examples
javascriptsapui5

How do i change the parent side iframe to the entire height of the SAPUI5 child side?


My goal is to implement my SAPUI5 website into an iframe that resizes itself to the complete height of the iframe content. The website that includes the iframe is on a different server.

How do i pass the entire height of the website to the parent page? The examples use a localhost site instead of the original. The used version of the SAPUI5 framework is 1.120.

My first attempts were entirely parent side calculations, which didn't work because of the cross-DOM policies. I cannot change these policies at the moment. The attempts shown below are later implementations since i lost the code for the first attempt.

-- This was the second attempt. It works with pages that are not created with the SAPUI5 framework. The received size for the SAPUI5 child is always the current iframe height.

Parent side:

<script>        
    window.addEventListener('message', receiveMessage, false);

    function receiveMessage(size){
        resizeIFrameToFitContent(size.data);
    }
                
    function resizeIFrameToFitContent(size){
        document.getElementById("exampleFrame").style.height = size;
    }
</script>

Child side:

function postPageSize(){
    size = window.document.body.scrollHeight + "px";
    window.parent.postMessage(size, '*');
}

-- There was also this attempt by using an interval and resizing the page more or less "dynamically". Here i get the error "Uncaught DOMException: Permission denied to access property "document" on cross-origin object" which is currently not solvable for me.

Parent side:

<script>
setInterval(function(){
    var pageHeight = document.getElementById("exampleFrame").contentWindow.document.body.offsetHeight;
}, 1000)
</script>

-- Last attempt was a bit more elaborate and uses some self written functions of our own. This also returns the current size of the iframe. It should be noted that this code works on a older webpage made with the SAP framework.

Parent side:

<script>
  window.addEventListener('message', e=>{if (e.origin=="http://localhost"){
        
        var iFr = document.getElementById("exampleFrame")
        if (iFr && iFr.style.height != e.data) iFr.style.height = e.data
    }})
</script>

<iframe src="http://localhost/path/to/page" id="exampleFrame" style="height:500px"></iframe>

Child side:

function ensureIFrameResizingFor(MainCont){
    setInterval(function(){
        var pgId = MainCont.getCurrentPage().getId() //MainCont is our usual App- or Navigation-Container (which holds "Pages")
        var hFrm = 0, hHdr = 0
        
        var hdr = document.querySelector("#" + pgId + " .sapMPageHeader") 
        if (hdr) hHdr = parseInt("0" + getComputedStyle(hdr).getPropertyValue("height"))
 
        var frm = document.querySelector("#" + pgId + " .sapUiSimpleForm") 
        if (!frm){
            hHdr = 3*hHdr
            frm = document.querySelector("#" + pgId + " .sapUiTableVSbContent") //fallback (second attempt, in case the Page is a Listing-Page)
            if (!frm) frm = document.querySelector("#" + pgId + " .sapMListTblCnt") 
        } 
        if (frm) hFrm = parseInt("0" + getComputedStyle(frm).getPropertyValue("height"))
        //console.log(hFrm, hHdr)

        if (hFrm) top.postMessage((hFrm + hHdr + 18) + "px", PostMsg_CallbackURL)
    }, 500)
}

EDIT: Got the solution. I needed to extract the "scrollHeight" by searching where the scrollbar is handled. The function is then controlled by an setInterval, since some elements change dynamically in the site.

This is the finished child side function:

function sendSizeToParent(){

    if(document.getElementsByClassName("sapMPageEnableScrolling")[0].scrollHeight != null){

        var height = document.getElementsByClassName("sapMPageEnableScrolling")[0].scrollHeight
        if(window.parent){
            window.parent.postMessage(height, '*');
        }
    }
}

Solution

  • Based on your comments so far, what you need is a reliable point in time at which the sap.m.Page has finished rendering, so that you can then inform the parent page about the height.

    By looking into the docs of SAP UI 5, the sap.m.Page summary shows that a Page has the ability to add a onAfterRendering method which it borrows from sap.ui.core.Control. According to the docs, this method is then automatically called after the rendering process finishes.

    Therefor I'd suggest:

    In your parent page, you basically do what you've done so far:

    window.addEventListener('message', (event) => {
      document.getElementById('exampleFrame').style.height = `${event.data}px`;
    }); 
    

    The code inside the iframe needs to use .postMessage inside the onAfterRendering method then:

    // please adjust accordingly
    onAfterRendering() {
      const height = document.body.scrollHeight;
      if (window.parent) { // only post if placed inside an iframe
        window.parent.postMessage(height, '*');
      }
    }
    

    Remember that you need to define onAfterRendering on your sap.m.Page instance. Maybe you have to adjust the shown example code according to the rest of your code, for example:

    page.onAfterRendering = function() {
      const height = document.body.scrollHeight;
      if (window.parent) { // only post if placed inside an iframe
        window.parent.postMessage(height, '*');
      }
    }