Developer's manual

Refreshing the tree after dynamic changes

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.

Using um.refresh

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.

Adding new items dynamically

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.

Populating the menus asynchronously

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


Search

We would like your feedback! Take the UDM4 Survey!

UDM 4 is valid XHTML, and in our judgement, meets the criteria for WAI Triple-A conformance.

-