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.