Docking boxes (dbx)
Version 3.0 — 26th March 2009
Docking boxes (dbx) adds animated drag 'n' drop, snap-to-grid, and show/hide-contents functionality to any group of elements.
And how could I resist re-creating one of the most iconic interfaces of modern times to show it off!
The script can work with rows, columns, or two-dimensional grids of objects, of any size and shape. And as you've come to expect from brothercake, all the functionality is fully accessible to the keyboard.
First released in March 2006, dbx2 was the first implementation of keyboard-accessible drag 'n' drop ever developed, and remains one of the most robust and elegant solutions on the web.
Version 3 has been more than two years in development, and includes a whole raft of new features, improvements to existing features, and bugfixes from the previous version (2.05). See the Latest update section below for a complete rundown of what's new.
Here's a clutch of demos to highlight the major features, and suggest some ideas for possible applications. You can also download these demos individually:
- Photo-swap puzzle → download [302K]
- Bookshelves → download [131K]
- Sortable list → download [15K]
- Taskbar buttons → download [34K]
- Navigation boxes → download [70K]
- iPhone (larger icons) → download [159K]
- API events → download [24K]
- Rules engine → download [30K]
- Remote-control grid → download [22K]
- Remote-control column → download [21K]
- Ajax-controlled group → download [53K]
Get the script
Download the zipfile [70K] and unzip it into your site directory.
There are two main includes to put in the
<head> section of your page —
the main dbx script,
and the stylesheet:
Then if you're using either the rules engine or the remote controls,
you will also need to include the relevant codebase in the
<head> section; it can go anywhere
after the main
Finally, there's a single script include that should go
at the very end of the
<body> section, which is the
dbx configuration script:
The configuration script contains the object constructors that
configure and initialize the dbx manager, and each of the dbx groups you're using.
Putting it at the end of the body is the preferred
method, because this avoids the need to use any kind of
load wrapper. This consequently also avoids the
possibility of a
flash of unsorted content, that might otherwise occur in
some browsers (the momentary appearance of the boxes in their
original order, before any cookie state is applied).
dbx works in the following or
later browser versions: Opera 8, Firefox 2, Safari 2, Chrome,
Konqueror 3, and Internet Explorer 6. To put this another way,
it works in all browsers that support
If scripting (or this script) is not supported, you'll get the same HTML and CSS layout, but without dynamic behaviors.
The codebase scripts are all compressed (stripped of comments and uneccesary whitespace) so that they load and process faster. The zipfile also includes fully spaced and commented versions, for your interest and reference, and in case you want to do any hacking. I would however discourage you from hacking the main scripts, and encourage you to look at the API for any additional or modified behaviors you want. Editing the core scripts will make future upgrades far more difficult to implement.
The basic setup process is broken down into four steps:
More advanced development is covered in additional documentation:
New features in v3.0:
The script now supports two-dimensional movement,
along horizontal, vertical and diagonal axes. This new orientation can be
further broken down into different movement and insertion modes:
boxes can be sorted continually (
freeformmode) or not until the mouse is released [or the enter key is pressed, for keyboard users] (
a box and its target can be swapped over (
swapmode), or one inserted before or after another (
- boxes can be sorted continually (
A sophisticated, dynamic rules engine
that controls what movement is allowed, using
a token-based syntax for defining distances and shapes. This is intended
for gaming applications, and allows you to specify things like
boxes only moving up or down, or only along
a diagonal, or only in a triangular pattern like the movement of a knight in chess.
This is implemented using
two new API
group.removeRule(). And is accompanied by a new API event:
manager.onruletest(which fires whenever a rule is tested)
You can now have dynamic groups,
ie. the number of boxes can vary between and
during a session, which means that you can implement groups for which the content
is unknown, or where boxes can be added and removed using Ajax.
This is controlled using another new
Boxes can now be remotely controlled, using another four new
group.move()(for moving an object in a specific direction),
group.swap()(for swapping two boxes),
group.insert()(for inserting one box before or after another), and
group.toggle()(for opening or closing a box)
There's a further three new API
manager.onanimate(which fires continually as box animation occurs, so you can tie it into other processes),
manager.onafteranimate(which fires immediately after an animation is complete, for synchronising other things to what is otherwise an asynchronous process), and
manager.onbeforestatechange(which fires just before
onstatechange, so you can control it more precisely)
Bugfixes and other improvements:
- confirmed support for contemporary browser releases: Opera 9, IE7, IE8, Chrome, Firefox 3, Safari 3, Safari 4, Konqueror 4
- full support for konqueror 3.4 or later; partial support for konqueror 3.2–3.3 — boxes can be opened and closed, but not moved
- no conflict with spatial navigation in Opera; in fact spatial navigation is now the best way to interact with it, since you can move between targets using Shift + Arrow and then move them using Arrow
a range of new dynamic and/or state-dependent class names:
"dbx-box-focus"(applied to a box when the handle or anchor has the keyboard focus)
"dbx-box-hover"(applied to a box when the mouse is over its handle or anchor)
"dbx-box-active"(applied to a box when the mouse or key is down on the handle or anchor)
"dbx-dragclone"(a clone being dragged with the mouse, in addition to the
"dbx-aniclone"(an animation clone, in addition to the
"last-child"(the boxes which are currently at the beginning and end of their group, respectively)
toggle buttons / anchors can now be
<button>elements instead of
<a>elements, which means cleaner output (no junk hrefs) and potentially better semantics (hence better usability to assistive devices)
- an expanded range of references available in all API functions
- improved validation and automatic error-correction in object constructors
onboxdragbehaviors, so it only fires from keyboard actions when a target actually exists
- full default action suppression in Opera (as of v9)
- removed support for Opera 7.5 and Safari 1.2, for the sake of leaner and more efficient code, because these older versions are now little used, if at all
a reduced cookie footprint, by rationalizing the state-string format — it now only stores
"-"for closed and otherwise assumes open; but the cookie parser is still backwardly compatible with the previous format
How it works
The real trick here is that the underlying elements don't move — what you're holding when you drag with the mouse, and what you see moving when animation occurs, is just a temporary clone.
The original box is still there, invisible until the drag or animation is complete, at which point the node order is updated for real, the clone destroyed, and the original box made visible once again
This approach worked out significantly cleaner and simpler than restyling the original boxes and creating place-holders in their wake. But more importantly, it means the script can work off elements with float or relative positioning, and having scaleable or fluid widths.
Implementing keyboard accessible drag 'n' drop
(which I've dubbed
press 'n' push)
was actually pretty simple — the processes of re-ordering the boxes,
and the visual transitions that go with them, were already abstracted
into methods; so all I needed was a focusable element
to act as anchor, with a key handler to
determine the action and pass the relevant data to those methods.
Well, it wasn't quite as simple as that, but essentially it was.
My point is that making this functionality accessible to the keyboard was
not desperately difficult, and this is true for extending keyboard
accessibility to scripted components in general. It's not rocket science,
it just takes a little effort and motivation.