Search code examples
javascriptevent-listenerpostmessage

Performance Problems with JS postMessage to adjust parent iframe scrollHeight


I have a file browser application I want to integrate into an iframe of a main website. To adjust the scroll height of the iframte to the content I use following code:

iframecontent:

    window.top.postMessage({ height: document.body.scrollHeight }, "*");

main site html:

<iframe id="iframe1" src="https://mysubfilebrowser.com"  frameborder="0" scrolling="no" width="100%" height="10000">
    Your browser doesn't support iframes
</iframe>
<script>
    let iframe = document.getElementById("iframe1");

    window.addEventListener('message', function(e) {
        let message = e.data;

        iframe.style.height = message.height + 'px';
    } , false);
</script>

This works as it should but I have problems with performance. If I click in file/folder tree to fast, the post message event seems to be fired so much, that the iframe suddenly do not show the correct content or even empty areas.

Any hints what I can do, to solve this problem?

Thanks in advance!

Best regards Mini25


Solution

  • It sounds like a "single" change makes that call run many times (the scroll and resize events can be like that, amongst others). To handle that, you can "debounce" your call to postMessage so it only makes the call once when it gets a lot of triggers in a short period of time.

    It's quite simple to do: don't do the call right away, wait up to (say) 10, 50, or 100ms before doing it, and if you get another trigger to do it, reset that delay. That means you won't actually do it until things have settled down.

    Here's a simple ad hoc version just for that code, but a web search for "debounce" will turn up various general-purpose debouncing wrappers for functions:

    let updateWindowDebounceTimer = 0;
    function updateWindow() {
        // If there's a pending previous call, cancel it
        clearTimeout(updateWindowDebounceTimer);
        // Schedule a call for 10ms from now
        updateWindowDebounceTimer = setTimeout(sendUpdate, 100);
    }
    function sendUpdate() {
        updateWindowDebounceTimer = 0;
        window.top.postMessage({ height: document.body.scrollHeight }, "*");
    }
    

    Then use updateWindow(); where you're currently using window.top.postMessage(/*...*/).

    Here's an example using the scroll event without debouncing (notice how fast the counter goes up when you scroll):

    function updateWindow() {
        /*
        window.top.postMessage({ height: document.body.scrollHeight }, "*");
        */
        const display = document.getElementById("counter");
        display.textContent = String(parseInt(display.textContent, 10) + 1);
    }
    
    window.addEventListener("scroll", updateWindow);
    body {
        padding-top: 0;
        margin-top: 0;
    }
    #counter {
        position: sticky;
        top: 0;
    }
    
    .tall {
        margin-top: 1em;
        height: 10000px;
    }
    <div id="counter">0</div>
    <div class="tall">Scroll over this div</div>

    And here's an example with debouncing (notice how much more slowly the counter goes up, indicating fewer update calls):

    let updateWindowDebounceTimer = 0;
    function updateWindow() {
        // If there's a pending previous call, cancel it
        clearTimeout(updateWindowDebounceTimer);
        // Schedule a call for 10ms from now
        updateWindowDebounceTimer = setTimeout(sendUpdate, 50);
    }
    function sendUpdate() {
        updateWindowDebounceTimer = 0;
        /*
        window.top.postMessage({ height: document.body.scrollHeight }, "*");
        */
        const display = document.getElementById("counter");
        display.textContent = String(parseInt(display.textContent, 10) + 1);
    }
    
    window.addEventListener("scroll", updateWindow);
    body {
        padding-top: 0;
        margin-top: 0;
    }
    #counter {
        position: sticky;
        top: 0;
    }
    
    .tall {
        margin-top: 1em;
        height: 10000px;
    }
    <div id="counter">0</div>
    <div class="tall">Scroll over this div</div>

    That's an ad-hoc version, but a web search for "debounce" will turn up various general-purpose debouncing wrappers for functions.