File: extending-3-controls.md

package info (click to toggle)
leaflet 1.7.1~dfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 16,972 kB
  • sloc: javascript: 16,627; makefile: 49; sh: 26; xml: 23
file content (100 lines) | stat: -rw-r--r-- 4,675 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
---
layout: tutorial_v2
title: Extending Leaflet, New Handlers and Controls
---

<br>

This tutorial assumes you've read the [theory of Leaflet class inheritance](./extending-1-classes.html).

In Leaflet, a "layer" is anything that moves with the map. In contraposition to that, a "control" is a HTML element that remains static relative to the map container, and a "handler" is a piece of invisible code that changes the map's behaviour.

## Handlers

Map handlers are a new concept in Leaflet 1.0, and their function is to process DOM events from the browser (like `click`, `dblclick` or `mousewheel`) and change the state of the map.

Handlers are relatively simple: they just need an `addHooks()` method (which runs when the handler is enabled in a map) and a `removeHooks()`, which runs when the handler is disabled. A skeleton for handlers is:

	L.CustomHandler = L.Handler.extend({
		addHooks: function() {
			L.DomEvent.on(document, 'eventname', this._doSomething, this);
		},

		removeHooks: function() {
			L.DomEvent.off(document, 'eventname', this._doSomething, this);
		},

		_doSomething: function(event) { … }
	});

This can be illustrated with a simple handler to pan the map when a mobile device is tilted, through [`deviceorientation` events](https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation):

	L.TiltHandler = L.Handler.extend({
		addHooks: function() {
			L.DomEvent.on(window, 'deviceorientation', this._tilt, this);
		},

		removeHooks: function() {
			L.DomEvent.off(window, 'deviceorientation', this._tilt, this);
		},

		_tilt: function(ev) {
			// Treat Gamma angle as horizontal pan (1 degree = 1 pixel) and Beta angle as vertical pan
			this._map.panBy( L.point( ev.gamma, ev.beta ) );
		}
	});

The handler can be attached to the map using `map.addHandler('tilt', L.TiltHandler)` - this will store an instance of `L.TiltHandler` as `map.tilt`. However, it's more usual to attach handlers to all maps with the `addInitHook` syntax:

	L.Map.addInitHook('addHandler', 'tilt', L.TiltHandler);

Our handler can now be enabled by running `map.tilt.enable()` and disabled by `map.tilt.disable()`

Moreover, if the map has a property named the same as the handler, then that handler will be enabled by default if that options is `true`, so this will enable our handler by default:

	var map = L.map('mapDiv', { tilt: true });

To see this example, you'll need a mobile browser which [supports the `deviceorientation` event](http://caniuse.com/#search=deviceorientation) - and even so, this event is particularly flaky and ill-specified, so beware.

{% include frame.html url="tilt.html" %}

Depending on the type of event, a map handler can attach event listeners to the `document`, the `window`, or the container of the `L.Map` it's attached to.

## Controls

You already know controls - the zoom control in the top left corner, the scale at the bottom left, the layer switcher at the top right. At their core, an `L.Control` is an HTML Element that is at a static position in the map container.

To make a control, simply inherit from `L.Control` and implement `onAdd()` and `onRemove()`. These methods work in a similar way to their `L.Layer` counterparts (they run whenever the control is added to or removed from the map), except that `onAdd()` must return an instance of `HTMLElement` representing the control. Adding the element to the map is done automatically, and so is removing it.

The simplest example of a custom control would be a watermark, which is just an image:

	L.Control.Watermark = L.Control.extend({
		onAdd: function(map) {
			var img = L.DomUtil.create('img');

			img.src = '../../docs/images/logo.png';
			img.style.width = '200px';

			return img;
		},

		onRemove: function(map) {
			// Nothing to do here
		}
	});

	L.control.watermark = function(opts) {
		return new L.Control.Watermark(opts);
	}

	L.control.watermark({ position: 'bottomleft' }).addTo(map);

{% include frame.html url="watermark.html" %}

If your custom control has interactive elements such as clickable buttons, remember to use `L.DomEvent.on()` inside `onAdd()` and `L.DomEvent.off()` inside `onRemove()`.

If your custom control consists of more than one HTML element (like `L.Control.Zoom`, which has two buttons), you'll have to create the whole hierarchy of elements and return the topmost container.

## Publishing your plugin

If you have understood everything so far, you're ready to make some Leaflet plugins! But make sure to read the [`PLUGIN-GUIDE.md` file](https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md), as it contains some tips and good practices about naming and publishing your plugin.