How to Create Leaflet Control and Layer Plugins
In this article, I’ll explain how to implement control and layer plugins for the Leaflet JavaScript mapping library. I’ll focus on explaining the structure and lifecycle of Leaflet plugins. Template code for creating Leaflet control and layer plugins is available on GitHub under the MIT license.
Leaflet Control Plugins
Leaflet control plugins add user interface controls that act on a Leaflet map or it’s layers. Two examples of Leaflet controls are the zoom control and the layers control shown at the top left and top right of the following Leaflet map:
Creating a Leaflet Control Plugin
Leaflet control plugins are JavaScript classes that extend the Leaflet L.Control
class. A common naming convention is to add the name of the control plugin to Leaflet’s namespace, which is L
. The following code creates a control plugin with a class named L.MyLeafletControl
:
The extend
method of L.Control
takes a single object parameter that contains the properties and methods the plugin will add to the L.Control
subclass. Many control plugins have a set of default settings. In the code snippet above, the options
object contains a single position
setting with a default value of topright
.
The standard Leaflet plugin creation pattern is to implement a factory function that enables the creation of the plugin to be chained with other function calls:
The common convention is to name the factory function after the class of the control plugin but make the first letter lower case.
The Control Plugin Lifecycle
Leaflet calls the following methods of a control plugin when the control is added to a Leaflet map:
initialize()
onAdd()
Leaflet calls the initialize
method when an instance of a control plugin is created by calling new
directly or by using the factory function:
L.myLeafletControl()
new L.MyLeafletControl()
In the following initialize
method, we call L.Util.setOptions
to combine the values of the default settings (specified by the options
object parameter passed to the L.Class.extend
method) with the values of the settings for this instance of the control plugin, which are specified by the options
object passed as a parameter to the initialize
method.
Any further set up code for the control plugin should be added to the initialize
method after the call to setOptions
.
Leaflet calls the onAdd
method when the control is added to the map with the following method calls:
control.addTo(map);
map.addControl(control);
Control plugins are user interface elements that Leaflet displays on top of the map. The onAdd
method must return the DOM element that contains the user interface of the control. In the following onAdd
method, we create a HTML div
element with a class of my-leaflet-control
to enable the control to be styled with CSS (line 2).
1
2
3
4
5
6
7
8
9
onAdd: function(map) {
var controlElementTag = 'div';
var controlElementClass = 'my-leaflet-control';
var controlElement = L.DomUtil.create(controlElementTag, controlElementClass);
// Continue implementing the control here.
return controlElement;
}
After creating the div
container for the control, add any other code required to implement the control before returning the control’s DOM element (line 8).
Leaflet calls a third control plugin method called onRemove
when the control is removed from the map:
control.removeFrom(map);
map.removeControl(control);
The onRemove
method is the place to tear down your control by releasing resources and removing event listeners, etc.
Styling
Leaflet control plugins can be styled with CSS like any other DOM element. Here we add style rules to the my-leaflet-control
CSS class that we added to the control’s container div
element in the onAdd
method:
Leaflet Layer Plugins
While control plugins enhance Leaflet with new functionality for interacting with layers and base maps, layer plugins enable Leaflet to overlay new types of content on base maps and other layers. For example, layer plugins have been created to cluster markers, draw polygons and visualize data.
Creating a Leaflet Layer Plugin
Creating a Leaflet layer plugin follows a similar pattern to creating a control plugin. Leaflet layer plugins are JavaScript classes that extend the Leaflet L.Layer
class. A common naming convention is to add the name of the layer plugin to Leaflet’s namespace, which is L
. Here, let’s create a plugin control with a class named L.MyLeafletLayer
:
Agina, the standard Leaflet plugin creation pattern is to implement a factory function that enables the creation of the plugin to be chained with other function calls:
The common convention is to name the factory function after the class of the layer plugin but make the first letter lower case.
The Layer Plugin Lifecycle
Leaflet calls the following methods of a layer plugin when the layer is added to a Leaflet map:
initialize()
onAdd()
Leaflet calls the initialize
method when an instance of a layer plugin is created by calling new
directly or by using the factory function:
L.myLeafletLayer()
new L.MyLeafletLayer()
A common layer plugin pattern is to pass the latitude and longitude position of the layer as a key/value pair of the options
parameter passed to the initialize
method. Recording the position of the layer (line 2) enables the plugin to update the layer correctly when responding to zoom events (described below).
1
2
3
4
initialize: function(options) {
this._latLng = options.latLng;
// Continue initializing the layer plugin here.
}
Leaflet calls the onAdd
method when the layer is added to the map:
layer.addTo(map);
map.addControl(layer);
So far, creating a Leaflet layer plugin has been very similar to creating a control plugin. The main difference between layer and control plugins is in the amount of work the onAdd
method needs to perform. A common pattern when implementing the onAdd
method of a layer plugin is to begin by retaining a reference to the map (line 2) to be able to use it when handling events.
1
2
3
4
5
6
7
8
9
10
11
12
13
onAdd: function(map) {
this._map = map;
var layerElementTag = 'div';
var layerElementClasses = '.my-leaflet-layer leaflet-zoom-hide';
this._layerElement = L.DomUtil.create(layerElementTag, layerElementClasses);
// Continue implementing the layer here.
map.getPanes().overlayPane.appendChild(this._layerElement);
map.on('viewreset', this._updatePosition, this);
this._updatePosition();
}
Next, the onAdd
method creates the DOM element that will contain the layer (line 4). Although it is common to implement a layer as a DOM element, it is not required. For example, Mike Bostock has a nice example of overlaying an SVG element on a Leaflet map.
Regardless of whether the layer is implemented as a DOM or SVG element, it is important to add the leaflet-zoom-hide
CSS class to the element (line 5). Leaflet hides elements with the leaflet-zoom-hide
class while the map is zooming to improve performance.
Unlike a Leaflet control plugin, which is added to the map by Leaflet itself after calling map.addControl(control)
, Leaflet layer plugins must explicitly add themselves to the overlay pane Leaflet provides for plugins (line 10).
After adding itself to a Leaflet map, a layer plugin must start listening for Leaflet’s viewreset
event (line 11). Leaflet generates a viewreset
event whenever the user zooms the map. The _updatePosition
method is our custom viewreset
event handler that is responsible for repositioning the layer when the map is zoomed.
Since the _updatePosition
method performs the calculation for positioning the layer correctly on the map, we call it now to give the layer its correct initial position (line 12).
Whenever the map is zoomed, the latitude and longitude position of the layer (set when the layer was created) will have different coordinates on the screen. To reposition the layer after a zoom, the _updatePosition
method first recalculates the screen coordinates of the layer with the latLngToLayerPoint
method (line 2). Next, the screen coordinates of the layer’s DOM element are updated with the new screen coordinates (line 3).
1
2
3
4
_updatePosition: function() {
var position = this._map.latLngToLayerPoint(this._latLng);
L.DomUtil.setPosition(this._layerElement, position);
}
Leaflet calls a layer’s onRemove
method when the layer is removed from the map:
layer.removeFrom(map)
map.removeLayer(layer)
Just like the onRemove
method of a Leaflet control plugin, the onRemove
method of a layer plugin is the place to tear down the layer by releasing resources and removing event listeners. In addition, the layer should remove itself from Leaflet’s overlay pane (line 2) and stop listening for viewreset
events (line 3).
1
2
3
4
5
onRemove: function(map) {
map.getPanes().overlayPane.removeChild(this._layerElement);
map.off('viewreset', this._updatePosition, this);
// Continue tearing down the layer here.
}
Styling
Leaflet layer plugins can be styled with CSS like any other DOM or SVG element. Here we add style rules to the my-leaflet-layer
CSS class that we added to the layer’s container div
element in the onAdd
method:
Further Reading
The Leaflet documentation has a plugin authoring guide that explains best practices for organising, presenting and demonstrating Leaflet plugin code. The Leaflet plugins page lists a variety of plugins created by the Leaflet community. Most Leaflet plugins are open source and are available on GitHub. Studying the implementation of these plugins is a great way to learn more about creating your own plugins.