Broadcasters and Observers
There may be times when you want several elements to respond to events or changes of state easily. To do this, we can use broadcasters.
Broadcasters
Once you have built a large application, you will often have several elements that have duplicate functionality. For example, buttons on a toolbar might have equivalent functionality to menu items. In addition, there may also be keyboard shortcuts or popup menus that do the same thing.
Let's say that we want the Back action in a browser to be disabled. We would need to disable the Back command on the menu, the Back button on the toolbar, the keyboard shortcut (Alt+Left for example) and any Back commands on popup menus. Although we could write a script to do this, it is quite tedious. It also has the disadvantage that we need to know all of the places where a Back action could be. This would be a problem when we wanted to add a new one as we would have to update all of the scripts. It would be more convenient to simply disable the Back action and have all the elements that issue the Back action disable themselves.
XUL provides a solution to this using an element called a broadcaster. This element holds the disabled state of the Back button. Each Back action element (the menu items and toolbar buttons) would watch the broadcaster and, when the disabled state changes on it, they will change also.
The simplest broadcaster is shown below. You should always use an id attribute so that it can be referred to by other elements.
<broadcasterset> <broadcaster id="back_command" disabled="true"/> </broadcasterset>
Any elements that are watching the broadcaster will be modified automatically whenever the broadcaster has its disabled attribute changed. This results in these elements becoming disabled themselves.
Like the keyset element, the broadcasterset element serves as a placeholder for broadcasters. You should declare all your broadcasters inside a broadcasterset so that they are kept together.
Elements that are watching the broadcaster are called observers because they observe the state of the broadcaster. To make an element an observer, add an observes attribute to it. For example, to make a Back button an observer:
<button id="back_button" label="Back" observes="back_command"/>
The observes attribute has been placed on the button and its value has been set to the value of the id on the broadcaster we want to observe. Here the Back button will observe the broadcaster which has the id back_command, which is the one defined earlier.
If the value of the disabled attribute on the broadcaster changes, the observers will update the values of their disabled attributes also. The result is that the button will be disabled and enabled when the broadcaster state changes.
We could continue with additional elements. As many elements as you want can observe a single broadcaster. You can also have only one if you wanted to but that would accomplish very little. You should only use broadcasters when you need multiple elements that observe a property. Below, we define some additional observers:
<key id="back_key" modifiers="accel" keycode="VK_LEFT" observes="back_command"/> <menuitem id="back_menuitem" label="Back" observes="back_command"/>
Now all we need to do is change the disabled attribute on the broadcaster element and the Back button, menu command and the keyboard shortcut are all disabled at once.
Observing Other Attributes
You can use a broadcaster to observe any attribute that you wish. The observers will grab all the values of any attributes from the broadcasters whenever they change. We could modify the earlier example to the following:
Example 7.7.1: Source View<broadcasterset>
<broadcaster id="back_command" label="Back" disabled="true"/>
</broadcasterset>
<keyset>
<key id="back_key" modifiers="accel" key="[" observes="back_command"/>
</keyset>
<toolbox>
<menubar id="back-menubar">
<menu id="back_menu" observes="back_command"/>
</menubar>
</toolbox>
<button id="back_button" observes="back_command"/>
Now, it addition to the disabled attribute, each observer will grab the label attribute as well, as both attributes have been placed on the broadcaster. The result is that the label of both the button and the menu will be set to Back. The label on the key will change as well but it won't use the value for anything.
Each observer will grab the attributes from the broadcaster and add them to itself. If the attribute already exists, it will be overwritten. For example, you would get the same effect if you added a label attribute to the button above as if you left it off, because it would be changed by the broadcaster.
Whenever the value of any of the attributes on the broadcaster changes, the observers are all notified and they update their own attributes to match. Attributes of the observers that the broadcaster doesn't have itself are not modified. The only attribute that is not updated is the id attribute. You can also use your own custom attributes if you wish.
The Observes Element
There is also a way in which we can be more specific about which attribute of the broadcaster we observe. This involves an observes element. Like its attribute counterpart, it allows you to define an element to be an observer. The observes element should be placed as a child of the element that is to be the observer. An example is shown below:
<broadcasterset> <broadcaster id="back_command" disabled="false"/> </broadcasterset> <button id="back_button" label="Back"> <observes element="back_command" attribute="disabled"/> </button>
Two attributes have been added. The first, element specifies the id of the broadcaster to observe. The second, attribute, specifies the attribute to observe. The result here is that when the disabled attribute on the broadcaster is changed, the state of the button is changed. The observes element does not change but instead the element it is inside changes, which in this case is a button.
If we change the value of the attribute, we can observe other attributes of the broadcaster.
There is an additional event handler that we can place on the observes element which is onbroadcast. The event is called whenever the observer notices a change to the attributes of the broadcaster that it is watching. An example is shown below.
Example 7.7.2: Source View<broadcasterset>
<broadcaster id="thingy_command" style="color: black"/>
</broadcasterset>
<button label="Test">
<observes element="thingy_command" attribute="style" onbroadcast="alert('Color changed');"/>
</button>
<button label="Observer"
onclick="document.getElementById('thingy_command').setAttribute('style','color: red');"
/>Two buttons have been created, one labeled Test and the other labeled Observer. If you click on the Test button, nothing special happens. However, if you click on the Observer button, two things happen. First, the button changes to red text and, second, an alert box appears with the message 'Color changed'.
What happens is the onclick handler on the second button is called when the user presses on it. The script here gets a reference to the broadcaster and changes the style of it to have a color that is red. The broadcaster is not affected by the style change because it doesn't display on the screen. However, the first button has an observer which notices the change in style. The element and the attribute on the observes tag detect the style change. The style is applied to the first button automatically.
Next, because the broadcast occured, the event handler onbroadcast is called. This results in an alert message appearing. Note that the broadcast only occurs if the attributes on the broadcaster element are changed. Changing the style of the buttons directly will not cause the broadcast to occur so the alert box will not appear.
If you tried duplicating the code for the first button several times, you would end up with a series of alert boxes appearing, one for each button. This is because each button is an observer and will be signaled when the style changes.
(Next) Next, we'll find out how to use XPCOM objects from XUL and scripts.