Hide Table of Contents
Working with events

The ArcGIS API for JavaScript is an event driven API. Events occur when you interact with a JavaScript application. Loading the page, clicking the mouse, executing a task, and many other actions all trigger events. You can make your application interactive by listening for events and writing code that responds to them, which is known as "handling" the event.

Why the event driven API?

Events are used for two main reasons:

  1. The JavaScript API is asynchronous, and not every operation on an object returns a result immediately. A trip to a server to get additional information may be required before the object is ready for use.
  2. The event model allows multiple handlers for every event. You can perform multiple operations based on a single event fired by the dispatching object.

Adding and removing event listeners

In order to handle an event, you need to add code that listens for it. Registering a listener for an event puts your application on alert that it needs to do something when that particular event occurs. Specifically, it will call the handler function in response to the event.

In the ArcGIS API for JavaScript, the recommended way to listen for events is to use on. It is also possible to use dojo/aspect or the older dojo/_base/connect module to listen to or respond to events. As of version 3.6, all modules in the ArcGIS API support the evented on() style event listeners.

The following code examples demonstrate how to use both methods. on example:

var mapExtentChange = map.on("extent-change", changeHandler);

function changeHandler(evt){
  var extent = evt.extent,
      zoomed = evt.levelChange;
  // ... Do something ...

  // in some cases, you may want to disconnect the event listener
  mapExtentChange.remove();
}

dojo/_base/connect (also dojo.connect) example:

require(["dojo/_base/connect", "esri/map"],function(connect, Map) {
  ...
  var mapExtentChange_connect = connect.connect(map, "onExtentChange", changeHandler_connect);
  function changeHandler_connect(extent, delta, levelChange, lod){
    // ... Do something ...

    // in some cases, you may want to disconnect the event listener
    connect.disconnect(mapExtentChange_connect);
  }
}

The arguments for the on method are:

  • the event name to listen for
  • the function to call when the event is fired

The arguments for the connect method are:

  • the element or object to listen on
  • the event to listen for
  • the function to call when the event is fired

Besides the different method signatures, the event handling functions also differ in an important way. Event listeners connected via the on method receive a single event object as an argument. The event information is passed as properties on the event object. This contrasts with the connect method, whose listeners are passed positional arguments. The other important difference, is event names. New on style event names are lowercased, words are hyphen separated, and there is no "on" prefix.

As a best practice, and to avoid memory leaks, event listeners should be removed when the application is closed. This is done by adding another listener, this time for the map's onUnload event:

on example:

var myUnload = map.on("unload", unloadHandler);

connect example:

var myUnload_connect = connect.connect(map, "onUnload", unloadHandler_connect);

Alternatively, you can register your unload handler to be called when the window unloads:

dojo.addOnUnload(myUnloadHandler);

In your "unload" handler function, you disconnect the listeners, including any listener you added for the onUnload event:

on example:

function unloadHandler(evt){
  changeHandler.remove();
  myUnload.remove();
}

connect example:

function unloadHandler_connect(map){
  connect.disconnect(changeHandler_connect);
  connect.disconnect(myUnload_connect);
}

on versus connect

Using the on method is preferable to using connect for several reasons. First, the Dojo documentation states that connect support will be removed in Dojo 2.0, and in fact, connect is now a legacy convenience wrapper for dojo.on. Secondly, especially in AMD style, it makes your code less verbose and is more similar to event attachment syntax in other JavaScript frameworks. Finally, for esri components we add a target property to all "synthetic" events (not mouse or key events) which points to the component which fired the event. Since event handlers fire asynchronously and there is no guarantee of the context in which the event handler will fire, the value of this is not dependable. However, the value of evt.target is dependable.

require([
  "dojo/_base/lang","esri/map", "esri/layers/WebTiledLayer", ...
], function(lang, Map, WebTiledLayer, ...) {
  var map = new Map("mapDiv"),
  layer = new WebTiledLayer(...);
  map.on("load", lang.hitch(layer, mapLoaded));
  ...
  function mapLoaded(evt) {
    var map = evt.target;
    console.log("Initial Map Extent: ", map.extent.toJson());
    // this function is executing with a scope "hitch"-ed to layer
    console.log("Full Layer Extent: ", this.fullExtent.toJson());
  }
});

Common events

This section gives tips for working with some of the most common events in the ArcGIS API for JavaScript.

Map "load"

When you add a map to a page, you cannot use it until the first layer has been added to it. Adding a layer to the map initializes the graphics and fires the onLoad event. At this point, you can interact with the map. One exception to this rule is the setExtent method. You can set the map's initial extent in the map's constructor or by calling setExtent before adding the first layer.

on example:

require(["esri/map", ...], function(Map, ...) {
  var map = new Map("mapDiv"),
  mapOnLoad = map.on("load", configNavigation);
  map.addLayer(...);

  function configNavigation(evt) {
    evt.map.disableMapNavigation();
  }
});

connect example:

require(["esri/map", "dojo/_base/connect", ...], function(Map, connect, ...) {
  var map = new Map("mapDiv"),
    mapOnLoad = connect.connect(map, "onLoad", configNavigation);
  map.addLayer(...);

  function configNavigation(map) {
    map.disableMapNavigation();
  }
});

ArcGISDynamicMapServiceLayer "load" and ArcGISTiledMapServiceLayer "load"

The esri.layers.ArcGISDynamicMapServiceLayer and esri.layers.ArcGISTiledMapServiceLayer work with ArcGIS Server REST service endpoints. When the layer is first created it needs to make a request to ArcGIS Server to get the service info. You should wait until the layer's onLoad event is fired before you interact with the layer. The code below uses an event handler to access the initial extent property only after onLoad has fired.

on example:

require(["esri/layers/ArcGISDynamicMapServiceLayer", ... ], function(ArcGISDynamicMapServiceLayer, ... ){
  var layer = new ArcGISDynamicMapServiceLayer(...);
  layer.on("load", printInitialExtent);

  function printInitialExtent(evt) {
    alert(evt.layer.initialExtent);
  }
});

connect example:

require([
  "dojo/_base/connect","esri/layers/ArcGISDynamicMapServiceLayer", ...
], function(connect, ArcGISDynamicMapServiceLayer, ... ) {
  var layer = new ArcGISDynamicMapServiceLayer(...);
  connect.connect(layer, "onLoad", printInitialExtent);

  function printInitialExtent(layer) {
    console.log(layer.initialExtent);
  }
});

In Internet Explorer, due to resource caching, the onLoad event is fired as soon as the layer is constructed. Consequently you should check whether the layer's loaded property is true before registering a listener for the "load" event:

on example:

require(["esri/layers/ArcGISDynamicMapServiceLayer", ...
], function(ArcGISDynamicMapServiceLayer, ... ) {
  var layer = new ArcGISDynamicMapServiceLayer(...);
  if(layer.loaded){
    printInitialExtent({"layer":layer});
  } else {
    layer.on("load", printInitialExtent);
  }

  function printInitialExtent(evt) {
    console.log(evt.layer.initialExtent);
  }
});

However, on has a companion function, emit. Instead of directly calling the event handler, we could have forced the event to fire using emit:

on example:

require(["esri/layers/ArcGISDynamicMapServiceLayer", ... ], function(ArcGISDynamicMapServiceLayer, ... ) {
  var layer = new ArcGISDynamicMapServiceLayer(...);
  layer.on("load", printInitialExtent);
  if(layer.loaded){
    layer.emit("load",{
      "layer":layer
    });
  }

  function printInitialExtent(evt) {
    console.log(evt.layer.initialExtent);
  }
});

Mouse events on maps and graphics

The ArcGIS JavaScript API map and graphics layer provide a large set of mouse events users can use to interact with these objects. To register to listen to a map's onClick event:

on example:

require(["esri/map", ...], function(Map, ...) {
  var map = new Map("mapDiv"),
  map.on("click", myClickHandler);
  map.addLayer(...);

  function myClickHandler(evt) {
    ...
  }
});

connect example:

require(["dojo/_base/connect", "esri/map", ...], function(connect, Map, ...) {
  var map = new Map("mapDiv"),
  connect.connect(map, "onClick", myClickHandler);
  map.addLayer(...);

  function myClickHandler(evt) {
    ...
  }
});

Notice that both the on style and connect style event handlers have the same signature in the above example. Since, mouse and key events already fired with a single event object, the handlers for such events are the same, no matter which event attachment method was used.

When a user clicks the map, a mouse event is generated and all registered click handlers are called. The mouse event is passed as an argument to each handler. In addition to all properties populated by the browser, a mapPoint and screenPoint are included as properties of the event. The mapPoint represents the coordinates of the click in map coordinates and the screenPoint represents the coordinates of the click in screen coordinates.

function myClickHandler(event) {
  alert("User clicked at " +
    event.screenPoint.x + ", " + event.screenPoint.y +
    " on the screen. The map coordinate at this point is " +
    event.mapPoint.x + ", " + event.mapPoint.y
  );
}

In addition to the mapPoint and screenPoint properties, the event returned includes a graphic property, which is the esri.Graphic object that received the event. The code below shows how you could handle the map's onClick event to report which graphic the user clicked. Notice that the listener for the onClick event will only be in effect after the map's onLoad event has fired. In this situation, a listener is dependent on another listener.

on example:

require(["esri/map", ...], function(Map, ...) {
  var map = new Map("mapDiv"),
  mapOnLoad = map.on("load", function(){
    map.graphics.on("click", myGraphicsClickHandler);
  });
  map.addLayer(...);

  function myGraphicsClickHandler(evt) {
    alert("User clicked on " + evt.graphic);
  }
});

connect example:

require(["esri/map", "dojo/_base/connect", ...], function(Map, connect, ...) {
  var map = new Map("mapDiv"),
  mapOnLoad = connect.connect(map, "onLoad", function() {
    connect.connect(map.graphics, "onClick", myGraphicsClickHandler);
  });
  map.addLayer(...);

  function myGraphicsClickHandler(evt) {
    alert("User clicked on " + evt.graphic);
  }
});

Since the Map.graphics object is only available to use after the Map.onLoad event is fired, you should wait to register event listeners until the the Map's "load" event is fired.

Map "mouse-wheel"

The mouse wheel event has been standardized for use across all browsers. A value property is added to the MouseScroll / Wheel event (Firefox) or MouseWheel event (Internet Explorer/Safari/Chrome/Opera). A positive number indicates mouse wheel scroll up and negative number indicates mouse wheel scroll down:

on example:

map.on("mouse-wheel", myMouseWheelHandler);

connect example:

connect.connect(map, "onMouseWheel", myMouseWheelHandler);

function myMouseWheelHandler(event) {
  alert("Mouse wheel value = " + event.value);
}

Event propagation (bubbling)

The Internet Explorer event model sometimes causes the same event to occur on multiple elements in the page. This can happen if the elements overlap. To prevent the event from propagating (bubbling) from one element to another, you can stop the event.

Suppose you have an application in which a user can click on a map to execute a query. The user can also click on any graphic to recenter the map at the location of the click. Assume the graphic has point geometry:

on example:

require(["dojo/_base/event",
  "esri/map", "esri/tasks/Query", "esri/tasks/QueryTask" ...],
  function(event, Map, Query, QueryTask, ...) {
    var map = new Map("mapDiv"),
    mapOnClick = map.on("click", executeQuery),
    mapOnLoad = map.on("load", function(){
      map.graphics.on("click", recenterMap);
    }),
    queryTask = new QueryTask(...);
    map.addLayer(...);

    function executeQuery(event) {
      var query = new Query({
        geometry: event.mapPoint
      });
      queryTask.execute(query, ...);
    }

    function recenterMap(event) {
      map.centerAt(event.mapPoint);
    }
});

connect example:

require(["dojo/_base/event",
  "esri/map", "esri/tasks/Query", "esri/tasks/QueryTask" ...],
  function(event, Map, Query, QueryTask, ...) {
    var map = new Map("mapDiv"),
    mapOnClick = connect.connect(map, "onClick", executeQuery),
    mapOnLoad = connect.connect(map, "onLoad", function(){
      connect.connect(map.graphics, "onClick", recenterMap);
    }),
    queryTask = new QueryTask(...);
    map.addLayer(...);

    function executeQuery(event) {
      var query = new Query({
        geometry: event.mapPoint
      });
      queryTask.execute(query, ...);
    }

    function recenterMap(event) {
      map.centerAt(event.mapPoint);
    }
});

In Internet Explorer the above code will potentially execute an unneeded query when the user clicks to recenter the map. This is because the click event is fired not only on the graphics container, but on the map div as well. To make sure that the event does not propagate to the map div, you can stop the event by adding the line in bold to the recenterMap function:

function recenterMap(evt) {
  // event is an alias for dojo/_base/event
  event.stop(event);
  map.centerAt(evt.mapPoint);
}