The menu runs an initialization function at load time, to insert submenu indicator arrows and bind event-handlers to the tree. This only happens once and therefore assumes a stable structure - any new items inserted after page load would simply not work.
However, thanks to a new public method called um.refresh it's now possible to re-initialise the menu tree whenever you like. This means that the menus can be populated asynchronously, or elements added or removed dynamically at any time after page load, and they'll work just as you expect.
This functionality was new in Version 4.43. If you're using an earlier build please download the latest one first.
um.refresh is one of a number of public methods made available by the menu script, to assist with API scripting, or with scripting in general on pages which include the menu codebase.
You can call um.refresh
at any time
after the menu has first loaded.
If you cannot assert that absolutely
(for example, if it's called during page construction from a non-user-initiated event),
then you should use one of the
API events to call it - any event is fine,
because none of them occur before primary initialisation.
However, if you use um.refresh
from
an API event
between '000'
and '010'
(inclusive) then you
should include the false
parameter
to prevent those API
events being fired again on re-initialisation.
If you have it set to true
then your script
will be caught in an endless loop.
//refresh the tree without firing API events
um.refresh(false);
Other things being equal, it's best to use false
all the time, and this is the default if the argument is not included.
Of course you may have a specific reason for wanting
to fire those events again, for example, to re-initialise other
API
scripting from an independent event; that's why the option is available.
In this first example we'll see a function which creates a new menu,
adds it to the tree, then calls um.refresh
to re-initialise:
//add a new menu
function addMenu()
{
//identify the UDM tree
var tree = document.getElementById('udm');
//create a new list-item and append it to the tree
var li = tree.appendChild(um.createElement('li'));
//create a new link inside the list item
var attrs = {
'href' : '/menu/modules/#extensionsInfo',
'text' : 'New extensions'
};
var link = li.appendChild(um.createElement('a', attrs));
//create a new menu inside the list item
var menu = li.appendChild(um.createElement('ul'));
//array of menu links
var links = [
['/menu/modules/horizontal/', 'Horizontal Menus'],
['/menu/modules/loadxml/', 'Load XML']
];
//populate the menu with these links
var len = links.length;
for(var i=0; i<len; i++)
{
li = menu.appendChild(um.createElement('li'));
attrs = { 'href' : links[i][0], 'text' : links[i][1] };
li.appendChild(um.createElement('a', attrs));
}
//refresh the tree
um.refresh();
};
It's pretty straight forward really - we're building the menu by
creating nodes using
um.createElement; once we've finished we simply call
um.refresh
to re-initialise the tree.
The same principle applies to removing elements from the tree,
or indeed any kind of dynamic alterations you want
to make - do what you're doing, and then call um.refresh
.
In this second example we'll see a framework for
populating the menus asynchronously, using
XMLHttpRequest
(aka Ajax)
to get the data. I won't go into
details about the technology, as there are
plenty of resources already available;
I'm just showing you a functional outline for this use:
//add receiver for pre-initialisation event
um.addReceiver(function()
{
//request object
var request = null;
//try to establish a request
if(typeof window.ActiveXObject != 'undefined')
{
try { request = new ActiveXObject('Microsoft.XMLHTTP'); }
catch(err) { request = null; }
}
else if(typeof window.XMLHttpRequest != 'undefined')
{
request = new XMLHttpRequest();
}
//if we're good
if(request != null)
{
//bind readyState change handler
request.onreadystatechange = function()
{
//if document has loaded and the status is okay or not-modified
//(or there is no status number, which is opera viewing a page locally)
if(request.readyState == 4 && /^(0|200|304)$/.test(request.status))
{
//POPULATE THE TREE WITH RETURNED INFORMATION ...
//refresh the tree without refiring API events
um.refresh(false);
}
};
//make the request
request.open('GET', './menudata.php', true);
request.send(null);
}
},'000');
The scripting is called from
API event
'000'
, which occurs right at
the beginining of the menu's initialisation. By calling it this early we may end
up re-initialising the tree before its primary initialisation
has finished, but that doesn't matter.
The XMLHttpRequest
object makes a server-side request,
which could be for a PHP script
(the hypothetical menudata.php
in this example) that does something like
retrieving user-specific information from a database;
or it might be an XML
document or other data source.
You can then use the data from that to populate the menus,
finally calling um.refresh(false)
to re-initialise the tree
(without re-firing API events,
or the script will loop infinitely).
You can use the same technique again, at any point after page load, to inject new data
into the tree. Once you're done, simply call um.refresh
to re-initialise.
You can see an XML-based implementation of this technique in the Load XML extension
UDM 4 is valid XHTML, and in our judgement, meets the criteria for WAI Triple-A conformance.