HierMenus CENTRAL: HierMenus In Progress. HierMenus 5.2 Release Notes (5/6)
[previous] [next] |
Re-Synchronizing Menus in Opera 7
In previous release bulletins, we've documented issues relating to Opera 7's ability to reload a previously loaded page from history without calling the load of the retrieved document or the unload of the current document. We initially noted this behavior in our release bulletin for HM 5.0, and later revisited it by discussing its impact on HM 5.1's ability to reload the navigation page of a frameset. Feel free to refresh your memory by referring to those bulletins, but in summary, menus can become "out-of-sync" in Opera 7 cross-frames scenarios; the handler code that is being used by menus that are displayed in the content frame can, in some contexts, point to code that no longer exists within the navigation frame.
While experimenting with potential workarounds for the event-timing issue described on the previous page, we discovered this tidbit of information that appears to allow us to conclusively know when the menus have become out-of-sync. With this information in hand, we can do something with those menus--other than allow them to spawn errors or attempt to continue accessing code that was loaded from a different, no longer visible page.
Specifically, in Opera, referring to a variable via the self or window objects always retrieves the value of that variable from within the currently displayed window within the frame; as opposed to the instance of the variable as it existed in the previously loaded page, where the handler was originally assigned (this, of course, is assuming that the original handler function was defined within the same window for which you wish to interrogate the current variable setting). Accessing a function directly by name, however, (and not using an explicit self or window reference) refers not to the function in the currently displayed page, but the function as it was defined in the original page that defined the handler code in the first place. This is a confusing point, so let's look at an example to see if we can sort it out. Again, this example will open in a new window and is designed to be meaningful only in Opera 7.
The initial top frame of the example page contains JavaScript
code as follows:
function theHandler() { alert("localFunction==window.localFunction: "+ (localFunction==window.localFunction)+ "\ntypeof(localFunction): "+ (typeof(localFunction))+ "\ntypeof(window.localFunction): "+ (typeof(window.localFunction))); alert("localFunction==self.localFunction: "+ (localFunction==self.localFunction)+ "\ntypeof(localFunction): "+ (typeof(localFunction))+ "\ntypeof(self.localFunction): "+ (typeof(self.localFunction))); alert("localFunction() = "+ localFunction()); alert("self.localFunction() = "+ self.localFunction()); alert("window.localFunction() = "+ window.localFunction()); } function localFunction() { return "Hello from Nav Page 1"; }
And in the lower page is code that sets the mouseover of a link defined within that page to the function theHandler from the top navigation page. When you intially roll over the link, all the responses are as you expect them to be: localFunction is declared to be equal to both window.localFunction and self.localFunction, and calling the function with or without the window or self prefix results in the string "Hello from Nav Page 1".
Rolling over the link after clicking through to the second nav page, however, produces differing results in each of the major browsers. The code on the second navigation page looks like this:
function theHandler() { alert("theHandler in Nav 2"); } function localFunction() { return "Hello from Nav Page 2"; }
None of the browsers attempt to execute the new theHandler code from the second navigation page (i.e., none of the browsers displays the "theHandler in Nav 2" message), and this is to be expected. As we explained in Bulletin 5, since the assignment of the handler is based on a simple address, the browsers all know that the address of the new theHandler code differs from that of the originally assigned handler from the first navigation page.
What happens next, however, depends on the browser. IE doesn't do anything; presumably the handler was removed when the page that defined it was removed as part of the navigation page swap. Gecko browsers execute the handler code as it existed in the initial page, using the currently displayed window to resolve function and variable references. In other words, in Gecko browsers the second rollover states that all of the localFunction references, whether prefixed with self/window or not, resolve to the localFunction as defined in the second navigation page, and not the first. Thus, when rolling over the main content page link after loading nav page 2, the "Hello from Nav Page 2" message is displayed and not the "Hello from Nav Page 1" message.
Opera 7 produces the most unique result of all. In Opera 7, the localFunction comparison in the second nav page is false; whether it is compared to window.localFunction or self.localFunction. And calling localFunction by itself results in the original "Hello from Nav Page 1" message; while calling the function through the self or window prefixes results in the "Hello from Nav Page 2" message in the second nav page.
Beginning with HM 5.2, we intend to use this unique behavior to our advantage. Since we have a way of knowing when a called menu is out-of-sync, we can check for this fact and then destroy or hide the menus in the content page when they are no longer pointing to the code being used in the main navigation frame. To accomplish this, we first set a marker in the content page each time it is initialized:
function HM_f_InitIt(reloadmain){ ... HM_MenusTarget.HM_SyncMarker = HM_f_Return; HM_MenusTarget.HM_Initialized=true; return true; }
And then we strategically check that marker each time any of our handlers fire to ensure that it is still what we think it should be:
function HM_f_CheckMenuSync() { try{var CheckWindow=self;} catch(e){return false;} if(HM_MenusTarget.HM_SyncMarker!=self.HM_f_Return) { // "Un" initialize the content page here } else return true; }
We'll discuss the first try..catch block on the next page, but for now note the key line:
if(HM_MenusTarget.HM_SyncMarker!=self.HM_f_Return) {
As described above, this expression will be true only when the HM_SyncMarker dropped as part of the initialization routine is no longer identical to the HM_f_Return function that it was set to originally. If the menus are determined to be out-of-sync, then four things happen:
All visible menus on the page are hidden. To know which menus have been created within a page, the HM_a_TopMenus array has been moved into the content page for Opera. In other words, each content page will have its own array of top level menu trees that have been successfully created within that page, and HM can use it to know which menus it can (or should) work with within the page.
The topmost menu in each tree is destroyed, effectively making direct access to any menu tree impossible.
The generic handlers of the content page (unload, resize, etc.) are reset to null or the previously existing handlers, depending on what the HM_f_CheckMenuSync function can sucessfully locate.
The HM_a_TopMenus array, HM_Initialized sentinel, and the HM_NavWindow variables of the content frame are cleared.
The net result is that the menu system of the content page is uninitialized, and the next time a user rolls over a link in the navigation page it will be reinitialized in the same manner that it would if the user had visited a page outside of the site's domain and then returned.
This menu synchronization check appears to address all of the remaining stability problems we've encountered as a result of leaving the menus in place between page loads in Opera, but it does admittedly create a couple of minor annoyances. First, any permanently visible menus that existed in the page will disappear when they are first rolled over in an unsynchronized state, and then not reappear until the user rolls over a link in the navigation page. Similarly, any popped up menus which were not hidden when the navigation page originally changed will be immediately hidden when rolled over. Keep in mind, however, that both of these situations will only occur when retrieving a historical page that was created with a different version of the navigation page that is currently being displayed, a fairly rare situation. In addition, the disappearing menus are much less annoying than the JavaScript errors that you would otherwise encounter.
Clearly, relying on a browser-specific feature such as this is not a desirable coding practice, and we don't make the change lightly. We'll keep an eye out for a more elegant solution, as well as watch future Opera versions to confirm the behavior is still present.
We close by discussing the remaining notable changes in HM 5.2, including one more minor Opera gotcha, a stability improvement for Safari, and variable-width menu corrections that affect nearly all browser platforms.
Created: August 28, 2003
Revised: August 28, 2003
URL: https://www.webreference.com/dhtml/hiermenus/inprogress/7/5.html