|
home / bulletins / 5 / page 3
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+.
   
[previous] [next]
|