Aleksandar • Vacić

iOS bits and pieces

WCH 2

After the last post, I knew that only IE/Win had any benefit from this script. This way I was able to streamline the script and completely remove the dependency on other scripts. It’s not even needed to do anything on the page load. I also removed the support for IE 5.0 and concentrated on iFrame technique. And it’s a lot smaller: 4.65kB, heavily commented.

Script now has only three functions. WCH_Hider() does all the browser checks and creates the hiding iFrame. WCH_HideWndCtrl() is placing the hider below the layer that needs to go over windowed controls. WCH_ShowWndCtrl is removing the hider.

For those of you wondering what this is all about…read this entry that will explain the problem at hand. Now, on to version 2. I will still explain every part of the script, as I think that this version should be used in the future - it’s much smaller, faster, independent…simply better.

How to use it

This script is useful if you have dynamic layers that pop-out after page load, like menu scripts, fancy widgets and similar stuff. To prevent drop-downs to show through your layer, just call the function that will hide the portion of drop-down where layer is located.

if ( typeof(WCH_HideWndCtrl) != "undefined" )
    WCH_HideWndCtrl(this.submenu, this);

This type of calling allows you to freely work, regardless if WCH is actually loaded or not. When your layer is hidden, just call the opposite function in the same way:

if ( typeof(WCH_ShowWndCtrl) != "undefined" )
    WCH_ShowWndCtrl(this.submenu, this);
Creating Hider

WCH_Hider still takes the same two parameters: layer under which hider should be placed and the container for both of them. If the first parameter is not passed (i.e. nothing is passed), it simply exits. Then we check for browser compliance. Only IE5+/Win will pass this check:

var bBrowserOk = (document.all && document.getElementById);
if ( !bBrowserOk ) return null;

And only IE5.5+ will pass this check:

bBrowserOk = (bBrowserOk && 
typeof(document.body.contentEditable) != "undefined");
if ( !bBrowserOk ) return null;

Object detection at its best (for the first time in my dev life, MSDN was very useful). No user-agent spoofing can trick this.

After this line, I try to access the hiding iFrame element. If it’s there, jolly good, work done. If not, create it. First, we need a container, where hider iFrame will be inserted. That pointer is passed in the function call. If it’s not, assume it’s body.

if ( typeof(vContainer) == "undefined" )
    oContainer = document.getElementsByTagName("BODY")[0];
else if ( typeof(vContainer) == "string" )
    oContainer = document.getElementById(vContainer);
else
    oContainer = vContainer;

There is one nice IE/Win’s proprietary style rule that allows the execution of various visual filters. Here, Alpha filter is very useful, as it is rendering the frame invisible (this is not the same as CSS' visibility). Unfortunately, my test showed that applying the filter in IE5.5, even with SP2 installed, crashes the browser. Hence another object detection (compatMode property exists only in IE6+):

var sFilter = (typeof(document.compatMode) != "undefined")
? "filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);" : "";

Next, we need to get the value of z-index for our layer (remember, we need to place the hider one z-index step below the layer). First try with DOM, then with runtime style (value from stylesheet files). If we can’t get the value, or it’s less than two, do nothing since there is no room for the hider.

var zIndex = oLayer.style.zIndex;
<strong>if ( zIndex == "" )</strong>
    zIndex = oLayer.currentStyle.zIndex;
zIndex = parseInt(zIndex);

if ( isNaN(zIndex) ) return null;
if (zIndex < 2) return null;

March 17: I have changed the highlighted line above from if ( zIndex == "" && document.compatMode == "CSS1Compat" ) to shown one. Second condition effectively excluded IE5.5 (as zIndex was always 0 then and it got out on the last line), causing the script not to have effect in it.

Finally, create the hider element and get its pointer (line breaks added for clarity).

zIndex--;
oContainer.insertAdjacentHTML("afterBegin", 
    '<iframe src="about:blank" id="hider' + oLayer.id + '"' +
    ' scroll="no" frameborder="0"' +
    ' style="position:absolute;visibility:hidden;' + 
    sFilter + 
    'border:0;top:0;left;0;width:0;height:0;' +
    'background-color:#ccc;z-index:' + zIndex + ';">' +
    '</iframe>'
);
oIframe = document.getElementById("hider" + oLayer.id);

Initially, hider is hidden. It first need to be given proper size and position.

Hide / show

Both hiding and revealing functions call the previous one. Work is done is valid object pointer is returned. In the function that hides everything below the layer, WCH_HideWndCtrl, we first get the layer size and apply it to the hider and then do the same for position, before we change its visibility.

oIframe.style.width = oLayer.offsetWidth + "px";
oIframe.style.height = oLayer.offsetHeight + "px";

oIframe.style.left = oLayer.offsetLeft + "px";
oIframe.style.top = oLayer.offsetTop + "px";

oIframe.style.visibility = "visible";

In the opposite function, we simply hide the hider, thus revealing everything below it.

Wait? Something missing…

Yup, there is no IE 5.0 support. This fancy iFrame hiding is not possible there. In the 1st gen of this script, I collected the pointers to windowed controls and then hide them with visibility:hidden. I didn’t like that too much, as it added 50% more code for 10% more users.

Should this be added? I’m still thinkin' about it…