Drop-down Selects with dgrid
In this tutorial, we will show you how to use one of dgrid's lightweight base classes to create a drop-down select component that will handle thousands of options.
Getting started
The first thing we will need is some HTML to represent the <select>
element. We'll
use the following HTML:
<div id="select" class="mySelect"> <div class="label button">Label</div> <div class="arrow button"></div> </div>
And we'll style it with the following CSS:
.mySelect { border: 1px solid #b5bcc7; background-color: #ffffff; width: 200px; height: 17px; /* Make this position: relative so our arrow is positioned within */ position: relative; padding: 0; } .mySelect .label { line-height: 17px; vertical-align: middle; } .mySelect .arrow { /* Position the arrow on the right-hand side */ position: absolute; top: 0; right: 0; /* Use claro's arrow image */ background-image: url("path/to/dijit/themes/claro/form/images/commonFormArrows.png"); background-position: -35px 70%; background-repeat: no-repeat; /* 16x16 with a white border and a gray background */ width: 16px; height: 16px; border: 1px solid #ffffff; border-top: none; background-color: #efefef; }View Demo
Adding a drop-down
The next task will be to add the drop-down. For our needs, dgrid/OnDemandList
will work
well because it doesn't have a header and is only concerned with rendering a row — it has no concept
of columns. We'll also use dgrid/Selection
and dgrid/Keyboard
to add selection and
keyboard navigation to our list.
require([ "dojo/_base/declare", "dgrid/OnDemandList", "dgrid/Selection", "dgrid/Keyboard", "dojo/dom-construct", "dojo/domReady!" ], function(declare, List, Selection, Keyboard, domConstruct){ var DropDown = declare([List, Selection, Keyboard]); var dropDown = new DropDown(); domConstruct.place(dropDown.domNode, "select"); dropDown.startup(); });
.mySelect .dgrid { position: absolute; top: 17px; left: -1px; width: 100%; }View Demo
Hiding and showing the drop-down
You may have noticed that we have appended the list to the select's node rather than the document body. The reason for this is so we don't have to position and size the list manually when it is opened; instead, we're doing it with CSS. Let's modify our CSS to make the dgrid initially hidden and add a class that will show it when applied to the parent node:
.mySelect .dgrid { position: absolute; top: 17px; left: -1px; width: 100%; display: none; } .mySelect .opened { display: block; }
Now all we need to do to show the list is to add the opened
class to its node. This can
easily be accomplished by using dojo/on
and event delegation:
require([ "dojo/_base/declare", "dojo/on", "dgrid/OnDemandList", "dgrid/Selection", "dgrid/Keyboard", "dojo/dom", "dojo/dom-construct", "dojo/dom-class", "dojo/query", "dojo/domReady!" ], function(declare, on, List, Selection, Keyboard, dom, domConstruct, domClass){ var DropDown = declare([List, Selection, Keyboard]); var dropDown = new DropDown(); domConstruct.place(dropDown.domNode, "select"); dropDown.startup(); var open = false; on(dom.byId("select"), ".button:click", function(e){ open = !open; domClass[open ? "add" : "remove"](dropDown.domNode, "opened"); }); });View Demo
Adding items to the drop-down
Now that we have the basic behavior working, it's time that we add some items to our drop-down; this
is accomplished by assigning a store and a renderRow
function to the list:
require([ "dojo/_base/declare", "dojo/on", "dgrid/OnDemandList", "dgrid/Selection", "dgrid/Keyboard", "dojo/store/Memory", "dojo/dom", "dojo/dom-construct", "dojo/dom-class", "dojo/query", "dojo/domReady!" ], function(declare, on, List, Selection, Keyboard, Memory, dom, domConstruct, domClass){ var DropDown = declare([List, Selection, Keyboard]); var store = new Memory({ identifier: "id", data: [ { id: 0, name: "One", value: 1 }, { id: 1, name: "Two", value: 2 }, { id: 2, name: "Three", value: 3 }, { id: 3, name: "Four", value: 4 } ] }); var dropDown = new DropDown({ selectionMode: "single", store: store, renderRow: function(item){ var divNode, textNode; textNode = document.createTextNode(item.name); divNode = domConstruct.create("div", { "class": item.name }); domConstruct.place(textNode, divNode); return divNode; } });View Demo
Unlike a dgrid/Grid
, a dgrid/List
instance (and by inheritance,
dgrid/OnDemandList
) has no concept of "columns" and only cares about rendering rows. The
renderRow
function receives a store item and needs to return a DOM node; in our case, it's
simply a <div>
with the name of the item as its contents and the color of the text set to
the item's color
property. You can get as creative as you'd like with the rendering of your items.
Selecting items
We have items rendering, but nothing happens when we select them. The next step is to hook up the list's selection event, hide the list on selection, and replace the text of the label with the text from the item. In our case, we want the text in the label to match what is in the drop-down. We'll need to break out some of the functionality we've written into separate functions so we can re-use it:
function renderItem(item){ var divNode, textNode; textNode = document.createTextNode(item.name); divNode = domConstruct.create("div", { "class": item.name }); domConstruct.place(textNode, divNode); return divNode; } var dropDown = new DropDown({ selectionMode: "single", store: store, renderRow: renderItem }); domConstruct.place(dropDown.domNode, "select"); dropDown.startup(); var open = false; function toggle(state){ open = state; domClass[open ? "add" : "remove"](dropDown.domNode, "opened"); } on(dom.byId("select"), ".button:click", function(e){ toggle(!open); }); var label = query(".label", dom.byId("select"))[0]; dropDown.on("dgrid-select", function(e){ var node = renderItem(e.rows[0].data); domConstruct.place(node, label, "only"); toggle(false); });View Demo
To be notified when a row is selected, we must listen for the dgrid-select
event; the event
object will have a rows
property which is an array of the row objects that are currently selected.
Since the list is set up in single selection mode, there will only ever be one item in the array. We use the
data
property from the row object to render a node and then replace the contents of the label
node with that node. Finally, the list is toggled closed.
One more exercise that can be undertaken is to widgetize this component: as it stands, it's not very reusable. This can be done many different ways, but an example method has been provided to get you started:
View DemoConclusion
As you can see, using dgrid/OnDemandList
as a drop-down list is quite simple. The modular nature
of dgrid allows us to pick and choose which features to include in our application without adding any more than
we absolutely need.