Broadcasters and Observers
- Feature Owner
- David Hyatt
In many cases user interface elements actually represent the same object. For example, a
Cut
menu item represents the same command as a
Cut
toolbar button. The two elements should both be
enabled or disabled at the same time, and the actions they perform should also be
identical.
Broadcasters and observers provide a mechanism for allowing multiple elements to observe a single element that can broadcast state and event information to its observers.
Broadcasters and observers should only be used when attribute or event information must be communicated to multiple observers simultaneously. Broadcasters and observers should not be used simply to transmit information from one element to another element. The relationship should be one-to-many.
Any element in a XUL document can be a broadcaster or observer. This relationship is completely arbitrary and can exist between any two elements, although typically this relationship will exist between an invisible element (used simply for storing common state and event handlers) and multiple user interface elements (usually key bindings, menus, and toolbar buttons).
An element can become an observer of another element either programmatically or
through the use of observes
nodes. An
observes
node can be
attached as a child of the node that will become the observer. The observes node
can have several attributes.
The element
attribute points to the node that
represents the broadcaster. Its value is the id
of the
broadcaster. getElementById
is used to retrieve the broadcaster
and to attach it to the observer.
The attribute
attribute indicates an attribute of the
broadcaster that the
observer is watching. For example, a toolbar button could observe the
disabled
attribute of the broadcaster. If the attribute is set or unset
on the broadcaster,
it will automatically be set or unset in the observer.
If this attribute has a value of*, then the observer will
pick up all of the broadcaster's attributes (except for id
).
The * functionality is not yet implemented.
<broadcaster id="cut"/> ... <menuitem name="Cut"> <observes element="cut" attribute="disabled"/> </menuitem> ... <titledbutton value="Cut"> <observes element="cut" attribute="disabled"/> </titledbutton> ...
There is also a simpler syntax that can be used when one node simply wishes to observe
all the attributes on a broadcaster node. Instead of having to use an
observes
node, an observes
attribute can be placed on the
observer instead. Its value is the id of the broadcaster being observed. When this is done
all attributes on the observer
(with the exception of id) will be picked up from the broadcaster.
Not yet implemented, probably won't be
The mapto
attribute can be used to select a different attribute
to set/unset in the observer when the attribute specified by the attribute
is set/unset in the broadcaster. For example, an action
attribute in the broadcaster might be mapped to an onclick
attribute in a menu item and to an onkeydown
attribute in a
key binding.
<broadcaster id="cut" action="performCut()"/> ... <menuitem name="Cut"> <observes element="cut" attribute="action" mapto="onclick"/> </menuitem> ...
Not yet implemented, probably won't be
The event
attribute can be used to indicate that an observer
wants to observe a particular event, such as a mouse click. The value of this
attribute corresponds
to the event that would be specified using the addEventListener
AOM API. (See Event Observation for details.)
When this attribute is used, then any event handlers that are added or removed from
the broadcaster will also be added or removed from the observer.
<broadcaster id="cut"/> ... <menuitem name="Cut"> <observes element="cut" event="click"/> </menuitem> ... document.getElementById('cut').addEventListener('click', performCut); ... function performCut() { ... }
The observes
node can also have a change
event handler placed on it, either through script or by placing an onchange
attribute on the observes
node. This handler is invoked whenever
an attribute changes. Currently the handler is only invoked when an attribute
is set, but not when it is unset. This is a known problem. The code in the handler
executes in the context of the element that is the observer, i.e., the parent of the
observes
element.
Implemented, but the map argument hasn't been added yet.
In addition to observes
nodes, the broadcaster/observer
relationship can be set up programmatically using the addBroadcastListener
,
removeBroadcastListener
, addBroadcastEventListener
,
and removeBroadcastEventListener
AOM methods.
addBroadcastListener
takes a DOM element
,
an attribute
, and a mapto
.
removeBroadcastListener
takes a DOM element
and
an attribute
.
The event listener versions of the methods have the same set of arguments, with the
event
in place of the attribute
argument.
Not yet implemented.
The addBroadcastChangeListener
and
removeBroadcastChangeListener
methods
can be used to programmatically add and remove
change
handlers. They take the same
arguments as addBroadcastListener
and removeBroadcastListener
,
except that the mapto
argument is a function that will be invoked
when the attribute changes instead.
All of these methods are invoked on the elements that will be the broadcasters.
<broadcaster id="cut"/> ... <menuitem id="cutMenu" name="Cut"/> ... <titledbutton id="cutButton" value="Cut"/> ... var cutMenu = document.getElementById('cutMenu'); var cutButton = document.getElementById('cutButton'); document.getElementById('cut').addBroadcastListener(cutMenu, 'disabled', 'disabled'); document.getElementById('cut').addBroadcastListener(cutButton, 'disabled', 'disabled'); ...
Finally it is worth noting that a broadcaster must be in the XUL namespace. An HTML element cannot function as a broadcaster. Any element, however, can be an observer.
Command Nodes
Broadcasters are typically used to represent command nodes that communicate state and/or the appropriate command execution code to multiple UI elements. The three common UI elements that observe command broadcasters are menu items, toolbar buttons, and key bindings.
For example, imagine a "save" broadcaster. That broadcaster can have an
oncommand
handler hooked up to it that contains the JS code that should be
executed when the command is invoked. You can also place the display
text of the command in the value
attribute and poke the
disabled
attribute on the broadcaster to enable or disable the command.
<broadcaster id="saveCommand" value="Save" oncommand="PerformSave()" disabled="true"/> ... <menuitem id="saveMenu" key="saveKey" observes="saveCommand"/> ... <titledbutton id="saveButton" observes="saveCommand"/> ... <key id="saveKey" control="true" key="s" observes="saveCommand"/>