HierMenus CENTRAL: HierMenus In Progress. HierMenus 5.1 Release Notes (4/4)
[previous] |
Speeding Up Menu Creation
DOM-capable browsers in previous HM versions created menus by first creating the menu DIV element using document.createElement and then immediately attaching the DIV to the document via document.body.appendChild, a standard procedure in DOM-document manipulation. However, as Andy King points out in his book, it is slightly more efficient to create the menu element, populate it with all of its menu items, and then attach it to the document body. Processing the menus in this way allows the browser to perform the expensive menu item attachments only to the menu element itself; instead of having to adjust the menu element while it's already attached to the document.
This small change required only the relocation of two lines of code; specifically the appendChild command was moved from the MakeElement function to the end of the MakeMenu function. However, the resulting performance improvement for some menu configurations was substantial. Specifically, very large and or heavily nested menu configurations in Internet Explorer v5 or later received the most benefit, building the menu sets in nearly half the time required in previous HM versions. Before you get too excited though, it's only fair to point out that this type of increase was noted only in the largest of menu sets, involving multiple hierarchical menu levels--not a typical menu set such as what actually appears on most sites. Also, no noticeable speed increase was noticed in Gecko-based browsers at all following this change, even on the largest menu sets. Still, any time reduction is a good one, and the amount of code changes required to implement the adjustment was so slight as to make the whole issue a no-brainer. Developers are encouraged to perform similar experiments in their own DOM-standards based applications.
An Official Welcome For Safari
Those who have looked at it are probably already aware that Safari for Mac OS X works great in HierMenus, and in fact has worked great in HM since our version 4.x days. As we pointed out in an earlier bulletin, this is because Safari identifies itself as a Gecko browser, and does an excellent job emulating Gecko-like behaviors and capabilities.
Since Safari has now reached the 1.0 milestone, we are pleased to announce "official" support for it in HierMenus. The two minor concerns noted in our earlier bulletin--support for overflow and page resizes--have been corrected in the 1.0 release of Safari, leaving no known outstanding issues specifically related to HierMenus use.
IE5 Windows and Too Many Evals
Early in our testing of the new optimized code set, we came across
a nasty--and random--"Invalid Page Fault" (an unrecoverable error) in Internet Explorer
5.0 that appears to be related to the use of specific variable names in
combination with multiple eval statements. While we still don't know exactly
why this error occurs, we found that renaming the variables and or reducing or removing
the eval calls removed the error. We suspect this is a problem which has
always been with us; the optimized code set uncovered it simply because it used two
additional eval calls (in quick succession) that are not used in the verbose version
of the code and it uses an entirely new set of variable names. To correct for this,
we simply replaced the eval calls in the AssignParameters function of the
optimized code set with associative array references. Recall that you can access
parameters of objects as an associative array in addition to direct property
queries. And since global parameters are assigned as properties of the window
object, we can therefore "locate" them like this:
function HW1(ae){ var af=ae[0]; var ag=ae[1]; var kt=ae[3]; var ah="H"+kt; if(typeof window["HM_PG_"+af]=="undefined"){ if(typeof window["HM_PG_"+kt]=="undefined"){ if(typeof window["HM_GL_"+af]=="undefined"){
etc. To further avoid this problem, we replaced the eval statement in the main parameter assignment loop with similar logic, to avoid the nesting of eval calls in both the main verbose scripts and the optimized scripts:
for (var i=0;i<HM_a_Parameters.length;i++) { HM_f_AssignParameters(HM_a_Parameters[i]); // 5.1 // eval(HM_a_Parameters[i][0] + "= // HM_f_EvalParameters("+ HM_a_Parameters[i][0] +", // null, // HM_a_Parameters[i][2])"); window[HM_a_Parameters[i][0]] = HM_f_EvalParameters(window[HM_a_Parameters[i][0]], null, HM_a_Parameters[i][2]); }
Gecko Event Firing on Unload
Gecko-based browsers can sometimes continue to fire events as a page is being unloaded; causing various errors that are more annoying than they are anything else (since they occur as a page is being unloaded they rarely have any noticeable effect on the pages themselves). Nonetheless, we've taken a few measures to (hopefully) reduce the number of errors that are triggered when this happens:
All specifically assigned menu event handlers are cleared when either the content or navigation page reloads. In the case of navigation page reloads, this step also serves a helpful--but less obvious--memory management function in Gecko browsers. Recall from our page reload discussion on the previous page that when a new navigation page is loaded, the menus that exist in the content page are confused since they utilized code that existed in the previous navigation page. Gecko browsers, in fact, hold on to that old code so that it can continue to be available to the handlers of the menus on the content page. By forcing these handlers to null, we reduce the risk of triggering errors by calling the handlers as the page unloads and simultaneously let the browser "clean up" the old code properly when the navigation page unloads. Similar steps are taken to remove the onload handlers of the frame itself in browsers that support it when the navigation page unloads, to prevent those handlers from continuing to point to previously loaded code.
The check for the HM_NavUnloaded sentinel--which is set to true whenever the navigation page is in the process of unloading--in the MainUnloadHandler (the onunload of the content frame) was made more sensitive to the fact that the navigation page may have already been unloaded just prior to the content page, and HM_NavUnloaded may therefore not even exist. The new check, then, is as follows:
// if(HM_NavUnloaded) return; if((typeof(window.HM_NavUnloaded)!="boolean")|| (HM_NavUnloaded)) {
Thus we first check to see that the variable's type is boolean (as opposed to the undefined we would see if the variable itself had been unloaded) and then check to see if it's true. This odd behavior appears to affect only older Gecko browsers (early N6) but the above fix is generic enough such that it should work fine as is in all browsers.
When unloading either the content page or the navigation page, all of the created menus--including permanent menus--are hidden. Be careful with this last step in Internet Explorer--we found that IE5 in particular does not respond well to the changing of the menu's visibility during page unload in certain situations. Rather than trying to force the issue we simply exclude IE from this action (since the particular errors we're trying to avoid weren't demonstrated in IE anyway).
Good Riddance to Bad Code
Our new HM_Initialized parameter (and the associated new function, HM_IsInitialized) provides a more elegant method of checking the content page for menu initialization, and therefore we use it instead of our HM_f_MenuExists logic as introduced in HM 5.0. As it turns out, the HM_f_MenuExists function was... well... buggy and didn't work as it was intended anyway, so this is a definite stability improvement from all vantage points.
"Other" Handler Calls Improved
Whenever HM takes over an event handler on a global level, it is careful to check for the existence of a previous handler as potentially assigned by another script, so as to save it and call it later as part of its own handler processing. Previous versions of HM 5.x weren't always careful enough to call those handlers properly, especially if the HM handler itself didn't execute through to completion. These calls were changed in several spots throughout the code to ensure that even if the HM handler is not called for some reason, the previous handler will still be called.
Opera 7 Initial Menu Creation and Display: Take 2
In HM release 5.0.1, we documented a behavior in Opera where the initial display of a menu would not occur properly. At the time, we concluded that this was the result of a display timing bug as we've often seen in both Gecko browsers and Mac IE5 in particular; specifically, the menus are created but sometimes when the visibility toggle is triggered, it isn't immediately "picked up" by the visible display. Therefore, we instituted a special timeout delay for Opera that would allow the menu display to be delayed slightly longer than other browsers.
Wrong!
The real culprit has nothing to do with video delays and timing, but rather Opera 7's handling of the document.readyState property. We check this property in two places: with the initialization of the menus on page load, and then at entry to each of our GUI event handlers. In both cases, the property must report a value of complete before processing is allowed to continue.
This works fine in Internet Explorer. In Opera, however, what we didn't account for is the fact that the readyState property is changed temporarily to interactive while images are downloaded to the browser--such as might happen normally in image rollovers, or even in the downloading of initial images for newly created menus (our own "more" image, for example). Thus, Opera would often trigger the readyState property to interactive immediately after the building of a new menu, and then the display of that menu would be stopped because the handler that triggers it first checks the readyState before allowing the display!
HM 5.1 corrects this problem--hopefully once and for all--by removing the readyState property check from the event handlers completely for Opera. Additionally, the initial menu build continues to check readyState, but includes special logic that allows it to repetitively check the property until it's complete before allowing Opera to continue with menu initialization (as opposed to the previous behavior, where Opera would simply fail to create menus entirely if the initial readyState check was anything other than complete).
Netscape 4 and Load Handler Checks
In Netscape 4 cross-frames implementations, we've previously checked onload and onunload events as being applicable to us based on the name of the element that triggered the event. I.E.:
function HM_f_CheckFrameLoad(e){ if (e.target.name == HM_FramesMainFrameName){ HM_f_FrameLoad(); routeEvent(e); } }
Recall, however, that when capturing events via Netscape 4's event model, the event we are examining can be triggered by documents, windows, and layers; thus if any one of them in the page happened to have the same name as our content page's name, we would call our HM_f_FrameLoad function twice (or more) with a single page load--a definite no-no in HM menu intialization. Therefore, we've adjusted these checks in NS4 to check not for the name of the target element, but the target element itself:
function HM_f_CheckFrameLoad(e){ // 5.1 // if (e.target.name == HM_FramesMainFrameName){ if (e.target == HM_MenusTarget){ HM_f_FrameLoad(); routeEvent(e); } }
This is safe; since the event capture is not set up until after HM_MenusTarget is properly assigned.
Conclusion
Onward and upward! We continue to improve and stabilize HierMenus, thanks in no small part to your continued feedback and suggestions. We're already hard at work on specific enhancements that we hope to incorporate in the next HM release. Check back often for further releases as well as HM tips and tutorials that will help you get the most out of your HierMenus implementation!
Files Changed in HM 5.1
- HM_ScriptDOM.js
- HM_ScriptIE4.js
- HM_ScriptNS4.js
- optimized/HM_ScriptDOM.js
- optimized/HM_ScriptIE4.js
- optimized/HM_ScriptNS4.js
- optimized/HM_Loader.js
Created: July 17, 2003
Revised: July 17, 2003
URL: https://www.webreference.com/dhtml/hiermenus/inprogress/5/4.html