Adding sidebars

The sidebar in Firefox is a relatively large and flexible space to add rich interfaces without requiring new windows or complicated overlays. Sidebars take as much space as the user wants them to, and provide a frame where you can add elaborate data and controls. You're probably familiar with the Bookmarks and History sidebars. If not, you can open either one from the View > Sidebar menu. They also have shortcuts to open or close them using the keyboard.

The code required to add a sidebar is very simple, as explained in Creating a Firefox Sidebar. It's even simpler than that. All you need is to overlay the View Sidebar menu.

<menupopup id="viewSidebarMenu">
  <menuitem id="xulschoolhello-sidebar"
    label="&xulschoolhello.sidebar.title;"
    accesskey="&xulschoolhello.sidebar.accesskey;"
    type="checkbox" autoCheck="false" group="sidebar"
    sidebarurl="chrome://xulschoolhello/content/sidebar.xul"
    sidebartitle="&xulschoolhello.sidebar.title;"
    oncommand="toggleSidebar('xulschoolhello-sidebar');" />
</menupopup>

The example in the MDC page includes a shortcut key combination to toggle the new sidebar. Keyboard shortcuts are an essential feature of Firefox, and you can add your own into your extensions, which is also great. The problem is that choosing the right keyboard shortcuts is very, very hard, as explained by the creator of AdBlock Plus, and the Mozilla Keyboard Reference. To sum up both of these references: you can choose an obscure key combination that won't conflict with Firefox, such as Ctrl+Shift+(some letter), but there's no way of knowing if any other extension uses that same combination as well. Shortcut keys can be very valuable for advanced users, but don't rely on them.

The XUL page for the sidebar can hold any content you want and it's no different from other XUL windows or overlays. One minor difference is that the XUL sidebar should be defined using the « XUL Reference « root element instead of window or dialog. The load event is fired every time the sidebar is opened, and unload every time it's closed, so you can use those for initialization and clean up .

Another, more important difference to take into account is that users can resize the sidebar to their liking, and in most cases, the sidebar is going to be fairly narrow. You should design the content of your sidebar carefully, so that it is usable and appealing regardless of width. There are ways to limit the size boundaries of your sidebar with CSS or even disable resizing altogether, but none of those are good practices. Forcing sidebars to have a fixed size can cause accessibility and usability problems.

You can still have plenty of content available in your sidebar, you just need to organize it in a way that doesn't take too much space. For this purpose we'll also look at some handy XUL elements in this section. They allow you to stack content on top of other content and switch between different sections easily.

The tabbox Element

The « XUL Reference « element creates a tabbed view of one or more tabpanel elements. You can see an example of a tabbox element if you open the Firefox Preferences window and select the Advanced section. The tabs are styled to match the operating system you're using, so you should avoid changing the CSS in tab boxes. On the other hand, if you need UI that behaves like a tabbox but doesn't look like one, you should still favor using a tabbox and use CSS to change its look. Using custom-made elements are likely to cause accessibility and functional problems.

Creating a tabbed view is simple:

<tabbox id="xulschoolhello-tabbox">
  <tabs>
    <tab label="&xulschoolhello.mainTab.label;" />
    <tab label="&xulschoolhello.advancedTab.label;" />
  </tabs>
  <tabpanels>
    <tabpanel>
      <!-- Content for the main panel. -->
    </tabpanel>
    <tabpanel>
      <!-- Content for the advanced panel. -->
    </tabpanel>
  </tabpanels>
</tabbox>

The first tab is selected by default, but you can change the default selection by setting the selected attribute to true in the « XUL Reference « element.

A tabpanel can hold any kind of content. You should take into account that the whole tab box is going to be as large as the tab strip on the top and the content of the largest panel. You should try to balance the content in the tab panels so that you don't end up with uneven and mostly empty panels.

Decks and stacks

Sometimes you'll need finer grained control than the one provided by a tabbox. In these cases the « XUL Reference « and « XUL Reference « elements are probably what you're looking for. They are extremely useful, and you'll find yourself using them in many situations besides sidebars.

A deck is like a tabbbox without the tabs. It only displays one of its child nodes at a time, depending on its selectedIndex value. In the following example, the second child will be displayed, not the first which would be the default.

<deck selectedIndex="2">
  <hbox>
    <!-- Content for the first child. -->
  </hbox>
  <hbox>
    <!-- Content for the second child. -->
  </hbox>
</deck>
Note how this is one of the few cases where an attribute of an element is named using camel case instead of all small caps.

The size of the deck depends on the size of the largest child node, just like in a tabbox.

We recommend that you use hbox or vbox elements as children of a deck or stack. The code is easier to read and maintain this way.

A deck can be very useful when you have a large piece of XUL code that only changes in a small way depending on different circumstances. For instance, you could have a window that is used for two different purposes, and the only difference between the two cases is a label that has a value in one case and something else in another. Using a .properties file and a string bundle is a viable option, but it involves a lot of code for something so simple, specially if that's the only case where you need dynamic text. The alternative is to use a deck with the two labels, and change the selected index depending on the purpose of the window. This way you can still use DTD, and the code remains simple.

A stack is like a deck, except that all of its children are always on display, one of top of the other. It allows you to decompose complex UI into individual layers, broadening the layout possibilities. One common use for a stack is to have an image background that stretches horizontally and vertically depending on the size of the foreground object:

<stack>
  <hbox flex="1">
    <image src="chrome://xulschoolhello/skin/stack-bg.png" flex="1" />
  </hbox>
  <hbox>
    <!-- Some content here. -->
  </hbox>
</stack>

This workaround is necessary because you can't have an background image stretch using CSS.

A less common use for the stack element is to use the left and top attributes on its children in order to have absolute positioning for the content on the layers. This kind of positioning can be useful for various artistic effects, as well as some type of desktop-like or Dashboard-like interface, where items are located in positions determined by the user, and they can overlap with each other. This can become complicated very easily, though.

Remember that you can't have flexibility and absolute positioning at the same time.

Trees

The bookmarks and history sidebars in Firefox use the « XUL Reference « element to show their content. Trees are another strong option when you need to show a great amount of information in a compact presentation. With a tree you can show a rather small amount of root nodes, giving the user the possibility to expand whichever ones are needed. Trees are specially powerful when combined with data templates, a topic that will be covered later on. You can also read much more about trees in the XUL Tutorial page.

The tree element is possibly the most complex element in XUL. It is meant to be very versatile and malleable, but it can require a good amount of work before it fits your specific needs. It is in reality a hierarchical table view, so its content is defined in terms of rows and columns. Here's an example of a simple tree:

<tree flex="1">
  <treecols>
    <treecol label="&xulschoolhello.nameColumn.label;" flex="1" />
    <treecol label="&xulschoolhello.greetingColumn.label;" flex="3" />
  </treecols>
  <treechildren>
    <treeitem>
      <treerow>
        <treecell label="Peter" />
        <treecell label="Hey, what's up?" />
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label="John"/>
        <treecell label="Good evening, how are you doing?" />
      </treerow>
    </treeitem>
  </treechildren>
</tree>

The text in the rows of the tree is hardcoded because we wouldn't generally use text from locale files. We would use data from a datasource such as a database or a remote API. This tree is not much of a tree because it only has one level of depth. A more elaborate tree would look like this:

<tree flex="1">
  <treecols>
    <treecol label="&xulschoolhello.nameColumn.label;" flex="1" />
    <treecol label="&xulschoolhello.greetingColumn.label;" flex="3" />
  </treecols>
  <treechildren>
    <treeitem>
      <treerow>
        <treecell label="Peter" />
        <treecell label="Hey, what's up?" />
      </treerow>
    </treeitem>
    <!-- Notice that you need to specify the container attribute. -->
    <treeitem container="true" open="true">
      <treerow>
        <treecell label="John"/>
        <treecell label="Good evening, how are you doing?" />
      </treerow>
      <treechildren>
        <treeitem>
          <treerow>
            <treecell label="John Jr."/>
            <treecell label="Bah, bah!" />
          </treerow>
        </treeitem>
      </treechildren>
    </treeitem>
  </treechildren>
</tree>

In this case, the row for "John" has a child row for "John Jr.". The « XUL Reference « element is placed as a child for John's « XUL Reference « element, and the container attribute must be set.

It should be evident at this point that hand-coding a tree would take quite some time and yields a great deal of XML code that is hard to follow. This is why most of the uses of the tree element are for displaying data from an external datasource. Even handling the construction of a tree using Javascript and DOM functions can become very convoluted, which is why trees are better used along with templates. This topic will be covered later when we look at different kinds of datasources and templates.

Adding style to a tree can also be challenging, which is why there's a guide specifically covering how to Style a Tree. Looking at the Bookmarks and History sidebars should make it clear that trees are quite customizable with CSS.

This tutorial was kindly donated to Mozilla by Appcoast.