HierMenus CENTRAL: HierMenus In Progress. HierMenus 5.1 Release Notes (3/4)
[previous] [next] |
Reloading the Navigation Page
A design limitation in the previous releases of HM 5.x prevented Webmasters from setting up cross-frames scenarios where the navigation page--the page with the links that spawn menus--could be reloaded independently from the rest of the frameset. If you attempted to do so, you would typically find that the menus that had been previously built in the content page would either not work at all, or would spawn errors when rolled over (in the case of permanently displayed menus, or menus that were displayed with the ClickKill parameter set to true).
This limitation affects a fairly small percentage of HM implementations, since it would only occur in cross-frames scenarios where the navigation page was reloaded or replaced individually. It doesn't have any effect on stand-alone implementations (non-frames) or traditional frames implementations where the navigation page does not change (only the content page changes). Additionally, it wouldn't be a problem if the entire frameset itself is reloaded. Nonetheless, in order to make the HM script as robust as possible we're attempting to correct this limitation with HM version 5.1.
To correct the problem it's necessary to understand the
fundamental reasons why the menus would refuse to work and/or spawn
errors. At the heart of the problem is our early design decision to not require
Webmasters to make changes to their content pages in order to deploy HM in their
frame-based sites. (See Bulletin 2
for further discussion on this point.) Because of this decision, though the
actual menus are created in the content page of the frameset, the code
that governs those menus exists only in the navigation page:
The above is a typical illustration of a cross-frames HM implementation following the load of the initial frameset. The menus are created and attached to the content page (otherwise they couldn't be displayed there) but the code that controls the display of the menus is loaded into the navigation page. Now assume that the user loads a new page--navigation page B--into the navigation frame:
The initially created Menu A1 now has a dilemma. In order to work properly, it has multiple handlers pointing into the code that was a part of navigation page A--however, that code is now gone. Note that even if navigation page B is in every way identical to page A the problem still exists; JavaScript knows--since the handlers are assigned to the menu object's methods as simple addresses--that the code in the new page is not the same instance of the code it relied on to control the menus when they were intially created. The result in this scenario is either menus that refuse to appear at all, or menus that spawn errors if you attempt to use them.
HM Changes to Accommodate Nav Page Reloads
The first thing we must change in HM to accommodate nav page replacements is the initial menu loading mechanism triggered when the navigation page loads. The previous mechanism was fine when used in traditional scenarios.
HM_LoadElement = (HM_FramesEnabled) ? (HM_NS6) ? parent : parent.document.body : window; HM_f_OtherOnLoad = (HM_LoadElement.onload) ? HM_LoadElement.onload : HM_f_Return; HM_LoadElement.onload = function(){setTimeout("HM_f_FrameLoad()",10)};
In a frames-enabled setting, we attach the initial onload handler not to the window--as we would in stand-alone settings--but to the frameset or frameset's document, depending on the browser. This onload will not fire until all the pages of the frameset have been loaded, making it the safest place to fire our first menu creation and initialization routines.
When the navigation page is replaced/reloaded, however, this parent onload handler will not refire (in the same way that it does not refire if the content page were to be replaced). Thus, if we're loading a new navigation page utilizing the same logic above, no new menus will ever be created, since the initial call to HM_f_FrameLoad will never happen!
To correct this problem, we check for a special variable which we attach to the parent element itself that will tell us if we've already loaded HM once in this frameset; and if so, we attach the onload handler not to the parent element but to the window of the navigation frame itself (similar to a stand alone implementation). This should be a safe move; since if the frameset were being replaced in its entirety then we would not be able to find our sentinel boolean variable:
HM_LoadElement = (HM_FramesEnabled) ? (HM_NS6) ? parent : parent.document.body : window; // 5.1 if(HM_LoadElement.HM_LoadedOnce) { HM_LoadElement = (HM_NS6) ? window : window.document.body; } else HM_LoadElement.HM_LoadedOnce=true; HM_f_OtherOnLoad = (HM_LoadElement.onload) ? HM_LoadElement.onload : HM_f_Return; HM_LoadElement.onload = function(){setTimeout("HM_f_FrameLoad()",10)};
Next, when attempting to initialize the menu system within the key HM_f_StartIt function, we must first check the content page to see if menus have already been created within the content page. To do this, we set an additional sentinel variable within the content page itself:
// 5.1 HM_MenusTarget.HM_Initialized=true;
at the tail end of the HM_f_InitIt routine. HM_f_StartIt calls HM_f_InitIt as part of its normal processing. Therefore, prior to calling HM_f_InitIt we make an additional check to see if this sentinel has already been set; and if so, we replace the content page that currently exists within the content frame, effectively removing the old "dead" menus that may have existed within the content frame, and simultaneously triggering a natural rebuild of the menus when the "new" content page is loaded. We then immediately exit the HM_f_StartIt function, since we know that the reload of the content page itself will trigger the rebuild of the menus (and we need to wait for the new document structure of the content page to be recreated, anyway):
if(HM_FramesEnabled){ var TargetFrame = parent.frames[HM_FramesMainFrameName]; if(typeof TargetFrame == "undefined"){ HM_FramesEnabled = false; // 5.1 var ReloadMain = HM_f_IsInitialized(); } else { HM_MenusTarget = TargetFrame; // 5.1 var ReloadMain = HM_f_IsInitialized(); if(!HM_LoadCheckDone) { // 5.1 if(HM_CanAssignFrameLoad) { var FrameEl= parent.document.getElementsByName(HM_FramesMainFrameName)[0]; FrameEl.addEventListener('load',HM_f_FrameLoad,false); HM_FrameHasLoadHandler = true; } else if(parent.HM_UseFrameLoad) { HM_FrameHasLoadHandler = true; parent.HM_f_LoadMenus = HM_f_FrameLoad; } HM_LoadCheckDone = true; // 5.1 if(ReloadMain) { if(HM_Opera) HM_f_HideAllPermanent(); else { var EventTarget = HM_IE ? HM_MenusTarget.document.body : HM_MenusTarget; EventTarget.onunload = HM_f_MainUnloadHandler; HM_f_MainOtherOnUnload=HM_f_Return; HM_MenusTarget.location.replace(HM_MenusTarget.location.href); return false; } } } } }
Why reload the page completely, instead of simply destroying and recreating the menus? Remember that we no longer have the original information used to create the menus in the first place. The navigation page--including the HM_Arrays.js used to create the initial menus--is now gone. Therefore, we cannot assume that we even know what the menu structure of the content page looks like. Further, the earlier browsers (IE4/NS4) do not provide graceful, bug-free ways of removing the menu structures from a page once they are created, anyways. By reloading the page, we insure that the menu sets are recreated according to the new parameters of the now loaded HM_Arrays file and navigation page parameters.
Note in the above that we first point the onunload handler of the content page to our newly loaded code set (remember that it currently points to the onunload in the previously loaded navigation page) before we perform the reload procedure. Finally, note that we chose location.replace over the history.go(0) trick that we learned in the early days of NS4 reloads because history.go(0) proved to be inconsistent in frames settings; sometimes reloading only the frame in question and other times reloading the entire frameset itself.
We'll touch briefly on some of the other minor changes to the script that this reloading process required later in this article, but before we move on notice one major work-around required by Opera 7. Recall from our earlier release bulletin that Opera 7 will not refire the onload event of a page when it is recalled from memory (nor will it call the onunload of the current page). Our reload process above, then, will not work for Opera, since the location.replace will not refire the onload of the document when it is replaced. With Opera, therefore, we have no choice but to allow the menu creation routines to continue as is and attempt to remove existing menus as they are created, which we do with this new, and previously unnecessary, code in the HM_f_MakeMenu function:
var NewMenu = HM_MenusTarget.document.getElementById(HM_MenuIDPrefix + menucount); // 5.1 if(NewMenu){ var MenuParent=NewMenu.parentNode; MenuParent.removeChild(NewMenu); NewMenu=null; }
This, unfortunately, leaves a potential problem with any existing menus on the content page that are not recreated. We attempt to rectify this by hiding the menus before attempting to recreate them; but keep in mind that this hiding is based on the new navigation page parameters and will still, therefore, be imperfect. An additional and related Opera 7 gotcha occurs when you attempt to reuse menus based on a navigation page that no longer exists in memory; for example, if you go backwards in history to a previous navigation page, go forward to an entirely new, non-HM related page, then go back in history to the frameset and attempt to use visible menus appearing within the content page. These menus, unfortunately, rely on code that only existed in the second navigation page--a page which has now been replaced. We have no graceful work-around for this situation, but fortunately it should be a rare enough occurrence that it won't be a major problem. As is the case with all such issues, if we discover or are notified of a fix for the issue we will incorporate it in a future HM release.
Let's now wrap up this release with a brief look at some of the other fixes and adjustments made to HM 5.1, including a small change that can have a significant impact on menu creation speed in Internet Explorer 5.0+.
Created: July 17, 2003
Revised: July 17, 2003
URL: https://www.webreference.com/dhtml/hiermenus/inprogress/5/3.html