Flicker-Free IFRAME refresh

By , May 5, 2016 3:05 pm

One of our projects consists of single parent page and “widgets” that display secondary (classic ASPX webform) pages. A recent feature request was to auto-refresh widget information at given intervals. On the surface it was pretty straghtforward:

<iframe id="xIfrWidget0"></iframe>
var ifr = document.getElementById('xIfrWidget');

setInterval(function () {
   ifr.src = 'widget.aspx';
}, 2000)

The problem with this approach – there’s an ugly flicker of white between page refresh and the goal was to keep displaying current IFRAME content up until refreshed content is ready. Yes, there’re various AJAX-ified methods (including ASP.NET UpdatePanel) – but they add unnecessary overhead and may cause other issues.

The solution was suggested by this Stack Overflow post. The idea is to have secondary, hidden IFRAME into which perform actual load. Once load complete – switch IFRAMES – currently visible with old content becomes hidden, and hidden one with new content becomes visible. It goes something like this:

<iframe id="xIfrWidget0" style="display:none"></iframe>
<iframe id="xIfrWidget1" style="display:none"></iframe>
var ifrNo = 0;
var ifrHidden;
var ifr;

function swap() {

   ifr = document.getElementById('xIfrWidget' + ifrNo);
   ifrNo = 1 - ifrNo;
   ifrHidden = document.getElementById('xIfrWidget' + ifrNo);

   ifr.onload = null;
   ifrHidden.onload = function() {

      ifr.style.display = 'none';
      ifHidden.style.display = 'block';

   }
   ifrHidden.src = "widget.aspx";

}
  
setInterval(function () {
   swap();
}, 2000)

Initially both iframes are hidden. Timer calls function that assigns source to secondary iframe (Line 18), but also assigns “onload” event (Line 12). Only when frame finishes loading it makes itself visible and primary iframe hidden. On the next timer call – frames change roles.

This is sufficient for most of the cases but if your page continues to build in client-side JavaScript after load is completed – this may not be enough. You may need to hook into that JavaScript to determine when it finished to do visible/hidden IFRAME swap.

Case in point: Fusion Charts. It’s a super-cool HTML5/JS charting library, but, depending on complexity of your chart it may take noticeable time to render the chart. Fortunately it provides “rendered” event so you know when chart is ready. With this in mind here is augmented solution. Note also that instead of "display:none" to hide the iframe I use "position: absolute;top: -1000px" to move it outside of viewport. This is due to Fusion Charts don’t render correctly if their container is hidden. But the effect is the same – iframe is invisible until it’s needed.

<iframe id="xIfrWidget0" style="position: absolute; top: -1000px"></iframe>
<iframe id="xIfrWidget1" style="position: absolute; top: -1000px"></iframe>
var ifrNo = 0
var ifrHidden
var ifr;

function swap() {

   ifr = document.getElementById('xIfrWidget' + ifrNo);
   ifrNo = 1 - ifrNo;
   ifrHidden = document.getElementById('xIfrWidget' + ifrNo);
   ifrHidden.src = "widget.aspx";

}
  
setInterval(function () {
   swap();
}, 2000)

This code is similar to previous one, the difference is (besides using absolute positioning to hide iframe) is we just assigning iframe source, we don’t use “onload” event to swap visibility of iframes. This is the job of JavaScript of “widget.aspx”.

Fusion Chart constructor function accepts an object specifying chart properties, data source and even actual data. One of the object properties is “events”. It specifies various events from chart life-cycle. We’re interested in “rendered” event, which signals chart is done:

"events": {
   "rendered": function (eventObj, dataObj) {
      parent.ifr.style.position = "absolute";
      parent.ifr.style.top = "-1000px";

      parent.ifrHidden.style.position = "static";
      parent.ifrHidden.style.top = "";
   }
}

Here widget code does what we did previously in “onload” event in the parent. When chart finishes rendering it hides first iframe (via absolute positioning) and makes second iframe visible. And on the next call roles are reversed. And the cycle continues.

3 Responses to “Flicker-Free IFRAME refresh”

  1. Alex Castner says:

    Hey there – Found an undeclared reference issue (‘ifHidden is not defined’) with our middle code block swap() ln:15
    ifrHidden.style.display = ‘block’;

    should be

    ifHidden.style.display = ‘block’;

    var ifrNo = 0;
    var ifrHidden;
    var ifr;

    function swap() {

    ifr = document.getElementById(‘xIfrWidget’ + ifrNo);
    ifrNo = 1 – ifrNo;
    ifrHidden = document.getElementById(‘xIfrWidget’ + ifrNo);

    ifr.onload = null;
    ifrHidden.onload = function() {

    ifr.style.display = ‘none’;
    ifHidden.style.display = ‘block’;

    }
    ifrHidden.src = “widget.aspx”;

    }

    setInterval(function () {
    swap();
    }, 2000)

  2. lazna says:

    Having ZERO experinces with javascript programming, trying to put the example into my web page but got following error each tmie script try to refresh content:

    Uncaught ReferenceError: ifHidden is not defined
    at HTMLIFrameElement.ifrHidden.onload

    What I only did, is to enclose main code with elements, rewrite widget.aspx by custom script name and put both elements into web page. Do I need some more work? My whole code here:

    var ifrNo = 0;
    var ifrHidden;
    var ifr;

    function swap() {

    ifr = document.getElementById(‘xIfrWidget’ + ifrNo);
    ifrNo = 1 – ifrNo;
    ifrHidden = document.getElementById(‘xIfrWidget’ + ifrNo);

    ifr.onload = null;
    ifrHidden.onload = function() {

    ifr.style.display = ‘none’;
    ifHidden.style.display = ‘block’;

    }
    ifrHidden.src = “user.cgi”;

    }

    setInterval(function () {
    swap();
    }, 2000)

    Thanks for your effort

  3. lazna says:

    just noticed the post of Alex Castner, it work than. PLease ignore my previous post.

    thanks, L.

Leave a Reply to Alex Castner

Panorama Theme by Themocracy

Bitnami