The second type of listener is used to handle particular actions related to trees. The tree builder implements the nsITreeView interface, so handles the gathering of data and passing it on to the tree. The tree widget informs the view when certain operations are performed that might affect the data. The tree view handles all of these operations, but allows an observer to be attached which is invoked during these operations. For instance, the observer may have an onToggleOpenState method which will be called when the user opens or closes a row. The tree builder will handle the adding or removing of rows, but will call the observer so that it can perform some task.

The tree builder observer implements the nsIXULTreeBuilderObserver interface and may be attached to a tree builder using the builder's addObserver method. You can add more than observer if needed, and can remove them again with the builder's removeObserver method.

The observer is always invoked before the appropriate operation is performed. For instance, the onToggleOpenState method of any observers will be called before the tree item is opened. After the observers have finished, the tree builder opens the row and adds any child rows inside. Note that you cannot cancel the operation from within the observer.

Some useful functions of the observer are the drag and drop related callbacks to handle when an item is dragged onto the tree. This makes handling dragging onto a tree fairly simple. All you need to do is implement two methods, canDrop and onDrop. Note that in Firefox 1.0 (Mozilla 1.7) and earlier, the drag functions are slightly different. There, three functions are used, canDropOn, canDropBeforeAfter and onDrop. The two 'can' functions were combined into one with an extra argument. If you want to support both earlier and newer releases, you can implement all of the functions in the observer, sharing code as necessary.

The tree observer receives drag related events in three places: over a container row, before a row, and after a row. This allows you to handle dragging with more flexibility. For example, in some situations you may want to require dragging onto a folder type of row. In other situations, you may wish to allow items to be dragged between (before or after) rows. This would be the situation if you were dragging items from that tree around, for instance dragging a bookmark from one location to another. The tree widget will draw a small line between the rows while dragging. All you need to do is add a tree builder observer which returns true for the canDrop method. Note that the 'drag on' case only allows dragging onto containers, not ordinary rows.

var treeBuilderObserver = {
  canDropBeforeAfter : function(idx, orient) { return false; },
  canDropOn : function(idx, orient) { return true; },
  canDrop : function(idx, orient) { return !orient; },
  onDrop : function(idx, orient) {
    // do something here
  },
};
tree.builder.addObserver(treeBuilderObserver);

This observer implements both the older and newer methods and only allows dragging on rows. The canDropBeforeAfter method returns false since we do not want to allow before and after drops. The canDropOn method returns true however. The Mozilla 1.8 method canDrop checks the orientation and returns the opposite. This works as the 'on' value is 0 and the 'between' values are -1 and 1. Obviously, this code is much simpler than what we would really want to use -- we should be checking what is being dragged to make sure that it is compatible with the tree. Or, we might want to allow dropping on specific rows only; the drop methods are supplied with an index argument so we can check for this.