Menus may be generated using a template in the same manner as other elements. However, unlike with other elements, the content of a menu is only generated once the menu has been opened. This means that a menu created with a template will not have any of the generated items until the user opens the menu, or a script opens the menu. This applies to menus on menubars, submenus, as well as menu-type buttons such as those with a type
attribute set to menu
. This allows for better performance as the entire content of a complex menu does not need to be created until the user needs to use the menu.
It is very simple to create a menu using a template. Here is an example using an XML source:
<button type="menu" datasources="people.xml" ref="*" querytype="xml"> <template> <query expr="person"/> <action> <menupopup> <menuitem uri="?" label="?name"/> </menupopup> </action> </template> </button> <button label="Children" oncommand="alert(this.previousSibling.childNodes.length);"/>
In this example, the datasources
attribute has been placed on a menu-type button. The query generates a list of the person
tags within the datasource and outputs the action body for each one. The uri
attribute has not been placed on the menupopup
element which is the direct child of the action
element but has instead been placed on the menuitem
element. This causes the menupopup
to only be created once. The menuitem
elements however will be repeated for each result from the query. This makes sense, as we only want one menupopup
to be created but multiple menuitem
elements.
The second button in the example displays an alert with the number of children the template-driven button has. If you press this button before opening the menu, there will be one child (the template
itself). However, after the menu has been opened, there will be two children, one is the template
and the other is the generated menupopup
. Note that the generated content does not get removed again when the menu is closed again; this extra feature of menus is only intended to be a performance enhancement to speed up the time it takes to display a window by avoiding extra generation that can be put off until later.
You might be wondering why we couldn't just put the datasources
attribute on the menupopup
instead and not have a menupopup
inside the action body. You can actually do this, however, you no longer receive the performance benefit as the entire content will be generated right away. The special feature of menus to not generate content immediately applies only to the menu itself.
Generating a Recursive Menu
Menus are often generated from a template recursively. When creating recursive menus, you will need to use multiple rules, since leaf items will need to be created differently than non-leaf items. Leaf items will need to use a menuitem
element whereas non-leaf items will need to use a menu
element. This will involve at least two rules, although you might use other rules if you had other differences to handle. Here is an RDF example:
<button label="Houses in my Neighbourhood" type="menu" datasources="template-guide-streets.rdf" ref="https://www.xulplanet.com/rdf/myneighbourhood" xmlns:rdf="https://www.w3.org/1999/02/22-rdf-syntax-ns#"> <template> <rule rdf:type="https://www.xulplanet.com/rdf/House"> <menupopup> <menuitem uri="rdf:*" label="rdf:https://www.xulplanet.com/rdf/address"/> </menupopup> </rule> <rule> <menupopup> <menu uri="rdf:*" label="rdf:https://purl.org/dc/elements/1.1/title"/> </menupopup> </rule> </template> </button>
This example uses the simple RDF query syntax. The first rule matches all houses, while the second rule is used for streets. The content generated for each rule differs in only two ways. First, the menu
tag is different (menuitem
versus menu
), and the label is taken from a different RDF predicate. At the outer level, the second rule matches the streets, so a menupopup
and menu
element are created. The uri attribute is on the menu
element since we don't want to repeat the popup for every result. After the first level of the menu has been generated, the content will be equivalent to the following (ignoring the template related content):
<button label="Houses in my Neighbourhood" type="menu"> <menupopup> <menu uri="https://www.xulplanet.com/rdf/marion" label="Marion Street"/> <menu uri="https://www.xulplanet.com/rdf/garden" label="Garden Avenue"/> </menupopup> </button>
The inner pass through the data handles the houses. The houses match the first rule so a menupopup
and menuitem
element are generated and inserted inside the street content (the menu
element). Again, the popup is only created once since the uri attribute is on the menuitem
element. The effect is a menu with a submenu. There's nothing special about the way menus are handled -- the builder follows the same method for any type of content. However, the nature of menus can make this tricky to follow. Here is the result of the above example after both levels have been handled.
<button label="Houses in my Neighbourhood" type="menu"> <menupopup> <menu uri="https://www.xulplanet.com/rdf/marion" label="Marion Street"> <menupopup> <menuitem uri="https://www.xulplanet.com/rdf/garden/16" label="16"/> <menuitem uri="https://www.xulplanet.com/rdf/garden/18" label="18"/> </menupopup> </menu> <menu uri="https://www.xulplanet.com/rdf/garden" label="Garden Avenue"> <menupopup> <menuitem uri="https://www.xulplanet.com/rdf/garden/25" label="25"/> <menuitem uri="https://www.xulplanet.com/rdf/garden/37" label="37"/> </menupopup> </menu> </menupopup> </button>