The second in a three-part series on the technical architecture of the iD editor. Yesterday we looked at iD’s core data types and actions.

Closer to the user interface of iD lie three abstractions for a user’s interaction with the system: modes, behaviors, and operations. Modes contain the code behind the ‘Browse’, ‘Point’, ‘Line’ and ‘Area’ buttons at the top left, behaviors provide a means of reusing common bits of code across multiple modes, and operations provide the user interface for actions. Let’s look at each in detail.


Modes are settings that change the behavior of an application: for instance, drawing applications tends to have a ‘move’ mode, which moves the picture, as well as a ‘brush’ mode, in which the same clicks on the picture draw lines.

Modes are useful because they allow user interfaces to be customized for certain narrow uses, and for common actions, like clicks and drags, to have multiple meanings. However, they pose a challenge for new users, who might not discover them and have access to functionality, or will not realize what mode they’re currently in.

In iD modes are manifested in the interface by the four buttons at the top left:

Mode buttons

The modality of existing OSM editors runs the gamut from Potlatch 2, which is nearly modeless, to JOSM, which sports half a dozen modes out of the box and has many more provided by plugins. iD seeks a middle ground: too few modes can leave new users unsure where to start, whereas too many can be overwhelming.

iD’s initial mode is ‘Browse’: one can drag the map and select entities to edit. From there, users can enter three geometrically-oriented drawing modes, Point, Line, and Area, through the mode buttons or key-shortcuts.

The geometric modes are also split into a mode for the initial point drawn object and another for continuing an existing object. The exception to this rule is points, which have a single step.

Selection is handled by a specific ‘Select’ mode, which displays editing tools for tags and operations.

The API for each mode consists of two methods: enter and exit.

The enter method sets up all the behavior that should be present when that mode is active. This typically means adding listeners to DOM events that will be triggered on map elements, installing keybindings, and showing certain parts of the interface like the inspector in Select mode.

The exit method does the opposite, removing the behavior installed by the enter method. Together the two methods ensure that modes are self-contained and exclusive: each mode knows exactly the behavior that is specific to that mode, and exactly one mode’s behavior is active at any time.


Modes share functionality, which we abstract into behaviors. For example, in both the Browse and Draw modes, iD indicates interactive map elements by drawing a halo around them on mouse hover. Instead of duplicating the code to implement this behavior in all these modes, we extract it to iD.behavior.Hover.

Behaviors are inspired by d3’s behaviors. Like d3’s d3.behavior.zoom and d3.behavior.drag, each iD behavior is a function that takes as input a d3 selection (assumed to consist of a single element) and installs DOM event bindings necessary to implement the behavior. iD.behavior.Hover, for example, installs bindings for the mouseover and mouseout events that add and remove a hover class from map elements.

Because certain behaviors are appropriate to some but not all modes, we need the ability to remove a behavior when entering a mode where it is not appropriate. d3’s own behaviors don’t offer this functionality yet. Each behavior implements an off function that uninstalls the behavior. This is very similar to the exit method of a mode, and in fact many modes do little else but uninstall behaviors in their exit methods.

To make modes and behaviors more concrete, here’s an annotated extract from iD.modes.Browse:

// Each mode is constructed with a context. This is a singleton object which
// encapsulates some global state. It's constructed via the top-level iD()
// function.
iD.modes.Browse = function(context) {
    // Using the module pattern here: the constructor creates an object,
    // sets various properties, some of which are functions, and then returns
    // it.
    var mode = {
        id: 'browse'

    // The browse mode is just a collection of behaviors.
    var behaviors = [                    // In browse mode, you can:
        iD.behavior.Hover(),             // * Highlight entities on hover
        iD.behavior.Select(context),     // * Select entities via clicks
        iD.behavior.Lasso(context),      // * Select entities via a shift-drag lasso
        iD.behavior.DragNode(context)];  // * Drag nodes around the map

    // When entering the mode, install each of the behaviors.
    mode.enter = function() {
        behaviors.forEach(function(behavior) {

    // When exiting the mode, uninstall each of the behaviors.
    mode.exit = function() {
        behaviors.forEach(function(behavior) {

    return mode;


Operations wrap actions, providing their user-interface: tooltips, key bindings, and the logic that determines whether an action can be performed given the current map state and selection. Each operation is constructed with the list of IDs which are currently selected and a context object which provides access to the history and other important parts of iD’s internal state. After being constructed, an operation can be queried as to whether or not it should be made available (i.e., show up in the context menu) and if so, if it should be enabled.

Operations menu

We make a distinction between availability and enabled state for the sake of learnability: most operations are available so long as an entity of the appropriate type is selected. Even if it remains disabled for other reasons (e.g., because you can’t split a way on its start or end vertex), a new user can still learn that “this is something I can do to this type of thing”, and a tooltip can provide an explanation of what that operation does and the conditions under which it is enabled.

To execute an operation, call it as a function, with no arguments. The typical operation will perform the appropriate action, creating a new undo state in the history, and then enter the appropriate mode. For example, iD.operations.Split performs iD.actions.Split, then enters iD.modes.Select with the resulting ways selected.

// An array of the ids of the currently selected entities.
var selection = ['w12345'];

// Construct the operation. `context` is a context object created by calling `iD()`.
var operation = iD.operations.Split(selection, context);

// Can the operation be performed given the current selection
// and state of the map?
if (operation.available() && operation.enabled()) {
    // If so, do it!

Next time

We wrap up our architecture series with a discussion of map rendering and other UI components.

Devlogging work on the OpenStreetMap project by the MapBox team.

Much of this work is currently focused on improvements to OpenStreetMap funded by the Knight Foundation

Follow our work here on this blog or subscribe to our Twitter feed. You can subscribe to this blog’s feed or follow us at