Reputation: 11116
I'm trying to write a custom event handler for CTRL + click in Leaflet. My problem is that the click location given by the map is different from the click location in the event handler, e.g. LatLng(51.49174, -0.11639)
from the map click becomes LatLng(51.50938, -0.126)
in the handler. The click locations match exactly if the map is the only thing on the page. Adding some other div elements above the map (like an <h1>
title) makes the clicks not match. Panning the map also makes the click locations not match.
I'm wondering if I attached my L.DomEvent.on()
correctly. Following the Leaflet Handlers tutorial, my code looks like
L.CtrlClickHandler = L.Handler.extend({
addHooks: function() {
L.DomEvent.on(document, 'click', this._captureClick, this);
},
removeHooks: function() {
L.DomEvent.off(document, 'click', this._captureClick, this);
},
_captureClick: function(event) {
if (event.ctrlKey) {
console.log('control click registered at layer '
+ map.layerPointToLatLng(new L.point(event.layerX, event.layerY)));
}
}
});
// add this to all maps
L.Map.addInitHook('addHandler', 'ctrlClick', L.CtrlClickHandler);
Here's a live example on JSFiddle.
I'm using Leaflet 0.7.7 due to some other dependencies in my code. Upgrading to Leaflet 1.0.1 makes it match better (e.g., LatLng(51.49868, -0.1018)
vs. LatLng(51.4987, -0.1018)
) but the two locations still are not exactly the same.
Am I attaching the L.DomEvent to the correct thing? Should that be attached to the map div somehow, as opposed to document
?
Edit: Thanks to @AlexParij for the suggestion. I realized that panning the map also makes the clicks not match, with or without div
elements above the map. This happens for Leaflet 1.0.1 as well as 0.7.7. I've tried every combination I can think of, combining different event locations (event.layerX
, event.pageX
, event.clientX
, event.offsetX
, event.screenX
, and event.x
) with projection methods layerPointToLatLng
and unproject
but none of them match the map click. Now I'm really confused... Fiddle with these different options and Leaflet 1.0.1: https://jsfiddle.net/c4tkyewz/
Upvotes: 3
Views: 3323
Reputation: 11116
TL; DR: use map.mouseEventToLatLng()
in a custom handler.
@AlexParij was correct; I was not using the correct definition of the layer points and container points. Inside the handler, event
is different from Leaflet's internal mouse event (where the location is available from e.latlng
).
I looked through Leaflet's core to find the answer. Getting the location from event
requires taking the Mouse Event -> Container Point -> Layer Point -> latLng. Thankfully, the Leaflet developers already programmed a nice function for this: mouseEventToLatLng()
.
/*
* This is a custom handler to check if someone has control clicked
* the map and print the location of the click
*/
L.CtrlClickHandler = L.Handler.extend({
addHooks: function() {
L.DomEvent.on(document, 'click', this._captureClick, this);
},
removeHooks: function() {
L.DomEvent.off(document, 'click', this._captureClick, this);
},
_captureClick: function(event) {
if (event.ctrlKey) {
// translate mouse event to lat/lng (note: `mouseEventToLatLng()`
// calls Leaflet's `mouseEventToContainerPoint()` followed by
// `containerPointToLayerPoint()` and finally `layerPointToLatLng()`)
var latlng = map.mouseEventToLatLng(event);
console.log('Handler detected CTRL + click at ' + latlng);
}
}
});
// add this to all maps
L.Map.addInitHook('addHandler', 'ctrlClick', L.CtrlClickHandler);
Live example with Leaflet 1.0.1: https://jsfiddle.net/c4tkyewz/1/
Also tested with Leaflet 0.7.7.
map.on('click', function(e) {});
, use e.originalEvent.ctrlKey
.
Upvotes: 4