Button Variations

Dijit provides drop-in, degradable replacements for standard push buttons and checkboxes, yet it also gives you a lot of sophisticated options, such as the kinds of buttons that you normally find in toolbars. Let's start out with an ordinary Button and work our way up through more sophisticated options.

Button

Figure 13-4 shows a button, and Table 13-12 gives the rundown on the most basic kind of button dijit, a Button, which inherits _FormWidget.

A typical Button

Figure 13-4. A typical Button

Table 13-12. Button properties

Name

Comment

label

Used to provide the label for the button in markup or via programmatic creation.

showLabel

A Boolean value designating whether to display the text label in the Button. true by default.

iconClass

A class specifying an image that can make a button appear like an icon.

onClick(/* DOM Event*/ evt)

An extension point that is called in response to a click. This is a very common method to override.

setLabel(/* String */ label)

A method accepting an HTML string that can change a Button 's label.

Tip

Unlike TextBox and its descendants, the Button widgets require you to use the setAttribute('value', /*...*/) function, inherited from _FormWidget, to set value because Buttons don't have a widget value so much as they have a form value that is relayed to the server.

Let's dust off the code from Example 13-4 and provide some final polish by replacing those ugly buttons, as shown in Example 13-8. Remembering to add an obligatory dojo.require("dojo.form.Button") to the head of the page, the replacement is straightforward. Note how convenient providing the onClick handler in markup is for this situation.

Example 13-8. Typical Button usage

<button dojoTye="dijit.form.Button" type="submit">Sign Up!
    <script type="dojo/method" event="onClick" args="evt">
        alert("You just messed up...but it's too late now! Mwahahaha");
    </script>
</button>
<button dojoTye="dijit.form.Button" type="reset">Reset</button>

The Button 's iconClass is especially snazzy in that it doesn't just replace the entire button with an icon. Instead, it embeds the icon into the button alongside an optional label if one is specified and showLabel is true. For example, if you had a small 20 × 20px thumbnail image of some spam that you wanted to embed into the "Sign Up!" button, you could do it by including iconClass="spamIcon" in the button tag and ensuring that the following class appeared in your page:

.spamIcon {
    background-image:url('spam.gif');
    background-repeat:no-repeat;
    height:20px;
    width:20px;
}

Of course, you can provide any customized styles you'd like for buttons to make them look any way that you'd like by applying an inline style or a custom class.

ToggleButton

Because form dijits leverage inheritance so heavily, they often have common ancestors that provide common functionality for descendant classes. ToggleButton is one such class; it inherits from Button and adds in functionality for a button that has an on/off state, like a RadioButton or a CheckBox. The only notable attribute it adds is checked, which can be toggled with setAttribute.

Although you would probably use a more conventional control like CheckBox to designate on/off states, you could choose to use ToggleButton directly, or subclass it and implement your own custom ToggleButton. The onChange extension point (common to all form dijits) is one particularly useful feature:

<button dojoType="dijit.form.ToggleButton">
  <script type="dojo/method" event="onChange" args="newValue">
     console.log(newValue);
  </script>
</button>

Most of the buttons that appear in a toolbar such formatting a text with italics, bold, underline, etc., use the ToggleButton. The Menu and MenuItem dijits are introduced in Chapter 15.

Warning

Several button dijits are not included in their own designated resource file. In particular, you should dojo.require("dijit.form.Button") for Button, ToggleButton, DropDownButton, and ComboButton. While it may seem odd to require one thing when you actually want another, the rationale is that the (inheritance-driven) implementations for the various buttons are so similar that they are included in the same physical file to minimize overhead in acquiring resources from the server. Additionally, recall that the mapping between classes simulated via dojo.declare and resource files is not designed to be a one-to-one mapping (although traditional object-oriented programming philosophy often deems it so).

This technique remains a source of consternation amongst Dojo circles, as the overhead from a synchronous request to the server would be a moot point in a production setting that uses the facilities from Util to optimize layers for each page of an application.

These kinds of nuances result from so many (well-intentioned) competing interests in the Dojo community.

CheckBox

CheckBox descends directly from ToggleButton and is a standard drop-in replacement for an ordinary <input type="checkbox"> element. Using it is as simple as requiring it into the page and then using the dojoType tag. We might introduce it into Example 13-4 page by disabling the "Sign Up!" button until after user click the CheckBox to confirm that they're aware of our covert intentions to spam them:

<div name="confirmation" dojoType="dijit.form.CheckBox">
  <script type="dojo/method" event="onClick" args="evt">
    if (this.checked)
      dijit.byId("signup").setAttribute('disabled', false);
    else
      dijit.byId("signup").setAttribute('disabled', true);
  </script>
</div> I understand that you intend to spam me.<br>

<button id="signup" disabled dojoType="dijit.form.Button" type="submit">
  Sign Up!
</button>

Figure 13-5 shows a series of CheckBox dijits.

A series of CheckBox dijits

Figure 13-5. A series of CheckBox dijits

Warning

The reason that DIV tags are being used instead of INPUT tags is because you cannot embed SCRIPT tags inside of INPUT tags, and if you try, it is almost a certainty that the browser will strip them out. Thus, if you want to use SCRIPT markup inside of dijits, you should be especially cognizant that you can't use INPUT tags. If degradability is so important that this isn't acceptable for your application, simply write the methods in pure JavaScript instead of markup.

Thus, to programmatically check the CheckBox, you might use the setValue(true) method, which would check the box as well as set its checked attribute to true and its value attribute to true.

Tip

If it is really important to ensure every page is as degradable as possible, you can go the extra mile to explicitly include ordinary HTML attributes in tags. For example, instead of just specifying <input dojoType="dijit.form.CheckBox"/>, you could also include the extra type attribute, resulting in <input dojoType="dijit.form.CheckBox type="checkbox"/>.

Like ordinary HTML checkbox elements, however, there is a difference in the state of the checkbox versus the value of the checkbox. The state of the checkbox is either that it is or is not checked, and you can detect the state via the standard checked attribute. The value attribute, however, may take on non-Boolean values to pass special values to the server if the box is checked when the form is submitted. For example, a tag like <input name="pleaseSpamMe" value="yes"/> would append pleaseSpamMe=yes to the query string if the form was submitted via GET. (The default for value is "on".)

The confusion comes in, however, when you find out that the getValue method and the value attribute do not always return the same thing. The way it works is that getValue returns whether the box is checked regardless of what the actual value attribute happens to be. The rationale for this design is that the most common use case for a getValue function would be to determine a visible on/off state—not getting the actual value, which may not reflect the on/off state.

Because it is possible to get yourself tangled up in the differences between some of the different possibilities, consider some of the common cases for a CheckBox dijit:

<input id="foo" dojoType="dijit.form.CheckBox"></input>

Example 13-9 shows a series of calls to manipulate the dijit along with extensive commenting to show the effects.

Example 13-9. Typical CheckBox usage

/* Check the initial state */
dijit.byId("foo").checked // false
dijit.byId("foo").getValue(  ) // "on"

/* Use setValue with true */
dijit.byId("foo").setValue(true) // check the box and set the value to true
dijit.byId("foo").checked // true
dijit.byId("foo").getValue(  ) // true

/* Use setValue with false */
dijit.byId("foo").setValue(false) //uncheck the box and set the value to false
dijit.byId("foo").checked // false
dijit.byId("foo").getValue(  ) // false


/* Use setValue with a String */
dijit.byId("foo").setValue("bar") //check the box and set the value to "bar"
dijit.byId("foo").checked //true
dijit.byId("foo").getValue(  ) // "bar"

These most common use cases for the CheckBox are using getValue and setValue with Boolean values as parameters, so the chances are reasonably good that you won't need to wade through the potentially esoteric effects that can arise when you start mixing state and values.

Warning

Here's one particularly unintuitive combination that accentuates some of the issues involved in mixing state and value that you should be especially aware of:

dijit.byId("foo").setAttribute("value", "foo")
// changes the value attribute but does not check the box
dijit.byId("foo").value // "foo"
dijit.byId("foo").getValue(  )
//false, because the box is not checked

The unintuitive part is that after setting a value you wouldn't expect a call to getValue( ) to return false because common idioms in JavaScript involve the ability to test a string value, and if it's not "", null, or undefined, then it evaluates as true. However, the thing to remember is that getValue( ) consistently returns whether the box is checked or not—regardless of what is actual value attribute is set to be. In this case, the box is not checked, so getValue( ) returns false.

Likewise, the dijit's onChange event will not fire for a dijit.byId("foo").setAttribute("value", "foo") method call since the checked state of the box did not visibly change.

RadioButton

A RadioButton is a drop-in replacement for the ordinary HTML equivalent descending from CheckBox, and like its HTML equivalent, is conceptually a group of checkboxes in which only one can be selected at any given time. Recall that each button in a radio group has the same value for name but distinct values for value. Figure 13-6 shows a RadioButton group.

A RadioButton group

Figure 13-6. A RadioButton group

We might even further refine our working example (Example 13-4) by asking users how many times a day they'd like us to bother them. We could use radio buttons as shown in Example 13-10 to achieve this purpose quite easily, having first required dijit.form.CheckBox in the page.

Warning

Although you'd think that last sentence was a typo, it's not. Recalling that you dojo.require resources, not individual widgets, it turns out that the dijit.form.CheckBox resource provides dijit.formCheckBox and dijit.form.RadioButton.

This warning is along the same lines as the previous warning about how the dijit.form.Button resource provides multiple dijit implementations.

Example 13-10. Typical RadioButton usage

<input name="spamFrequency" value="1 per day" dojoType="dijit.form.RadioButton">
  1 per day<br>
<input name="spamFrequency" value="2 per day" dojoType="dijit.form.RadioButton">
  2 per day<br>
<input name="spamFrequency" value="3+ per day" dojoType="dijit.form.RadioButton">
  3+ per day<br>

DropDownButton

A DropDownButton is simply a descendant of Button that when clicked produces a drop-down menu with options you can select—just like you're used to seeing in a toolbar. DropDownButton and dijit.Menu are closely related, in that a Menu is one of the most common vehicles for supplying a drop-down list; TooltipDialog is another common option. Figure 13-7 shows the DropDownButton dijit.

A DropDownButton dijit

Figure 13-7. A DropDownButton dijit

More complete coverage is given to Menu (and the individual MenuItem s it contains) in Chapter 15. Example 13-11, however, demonstrates a DropDownButton in action and should get the point across. Note that the first child of the parent DropDownButton node is a label that appears on the button.

Example 13-11. Typical DropDownButton usage

<button dojoType="dijit.form.DropDownButton">
    <span>Save...</span>
    <div dojoType="dijit.Menu">
        <div dojoType="dijit.MenuItem" label="Save">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
        <div dojoType="dijit.MenuItem" label="Save as...">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
        <div dojoType="dijit.MenuItem" label="Save to FTP...">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
    </div>
</button>

If you want to use a DropDownButton as part of a form submission, you could do so by creating a hidden INPUT element and programmatically setting its value via the constituent MenuItem 's onClick method. The most common uses for DropDownButton, however, normally involve an application-level behavior, such as saving a document.

Tip

In general, form fields that are submitted to a server via a form submission should be visible to the user at the time of submission. In that regard, DropDownButton may seem a bit misplaced with its inclusion into dijit.form because it isn't that kind of form control. The reason it appears in this section is that it is a descendant of Button, and it would make even less sense to try to have a Button ancestor living in another namespace.

ComboButton

A ComboButton inherits from DropDownButton, but with a twist: it provides a reserved area that produces a drop-down when it is clicked, whereas if you click on the "other" part of the button that is initially visible, it invokes a default action. For example, you might have a "Save" button that triggers an ordinary save action when clicked, while clicking the drop-down portion of the button produces a menu with options such as "Save", "Save as . . . ", "Save to FTP site", and so on. Figure 13-8 shows a ComboButton before and after clicking on the expander.

Left: a ComboButton before clicking on the expander; right: the ComboButton after clicking on the expander

Figure 13-8. Left: a ComboButton before clicking on the expander; right: the ComboButton after clicking on the expander

Example 13-12 illustrates using a ComboButton.

Example 13-12. Typical ComboButton usage

<button dojoType="dijit.form.ComboButton">
    <span>Save</span>

    <script type="dojo/method" event="onClick" args="evt">
        console.log("you clicked the button itself");
    </script>

    <div name="foo" dojoType="dijit.Menu">
        <div dojoType="dijit.MenuItem" label="Save">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
        <div dojoType="dijit.MenuItem" label="Save As...">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
        <div dojoType="dijit.MenuItem" label="Save to FTP...">
            <script type="dojo/method" event="onClick" args="evt">
                console.log("you clicked", this.label);
            </script>
        </div>
    </div>
</button>

Notice that the label for the ComboButton is still provided via the first child element, <span>Save</span> in this case, and the options that are provided via the drop-down are just the same as with a DropDownButton.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.