mapbox-gl-js
mapbox-gl-js copied to clipboard
Marker needs more events such as "click"
Motivation
Marker has only three events "dragstart", "drag", "dragend". What if you want to get something from marker when it's cliked? You can use addEventListener
on its element from getElement
but the data from DOM Listener doesn't include what Marker object has. e.g. position of Marker, its draggability, etc.
So, it needs more event types including "click", "dblclick", "mouseenter", etc. Then we can get what we want when we need.
Design Alternatives
I'm not so sure about inner-logic how it implements for now. But I can guess, the whatever events are added into map's container.
I tried to use preventDefault
and stopPropagation
but it does not work because it actually does not have a event on it.
- Use case for needs of
prevendDefault
,stopPropagation
thing- There are two markers on map.
- Add event on each marker but second marker should not trigger map's event.
Mock-Up
The look should be like:
marker.on(type, listener, stopPropagation);
stopPropagation
for prevent map's event.
Implementation
Following is the workaround:
// for map
map.on("click", function(e) {
if (e.originalEvent.target !== marker.getElement()) {
// it's map!
}
});
// for marker
map.on("click", function(e) {
if (e.originalEvent.target === marker.getElement()) {
// it's marker!
}
});
It seems good when we have only one marker, but if we have lots of markers, we can not use condition like:
if (e.originalEvent.target !== marker.getElement() &&
e.originalEvent.target !== marker2.getElement() &&
... ) {
Sorry about mixing up with adding click
event and needs of stopPropagation
thing, I think it should be considered when the event things are implemented.
here is fiddle: https://jsfiddle.net/roqh9gjw/4/
A non ideal way to do this is to add the event listener to the marker element - I've added that on to your fiddle over at https://jsfiddle.net/qnr246xd/ .
I think it definitely makes sense to have marker based events - eg if clicking a marker on the map needs to trigger a scroll in a list in html instead of showing a popup.
@martsie Yes, absolutely the implementation as workround is not ideal. The events should be added on the Marker object itself but element. What I meant was the same as you wrote on your fiddle. Maybe my writing on the section(Implementation
) made you confused.
I accidentally left some code in at the bottom of the fiddle that was a distraction. https://jsfiddle.net/a26pxgr5/ is the latest. The difference is that in your implementation you are adding the event listener to the map - whereas in the jsfiddle I posted the click event listener is added to the marker element itself. I think from a developer-experience perspective it makes better sense to be assigning a click event to a marker element than the map.
Umm, that's why my code is workaround. It should be used as you code to get position of marker:
el.addEventListener('click', function(){
console.log(marker1.getLngLat());
})
It's not part of API, and we should add addEventListener
explicitly which is just Web API. It's suggestion for making the functionality as a part of mapbox API no matter the inner logic is the same as you suggested or other ways.
Any update?
I use marker.on('dragend', function(e) ... and there is no way to do e.stopPropagation(), therefore I must (a) detect distance dragged in 'dragend' event to detect a "click", and (b) somehow set a global variable with a timeout that a marker was clicked, so in my map.on('click' function() ... I can ignore if that marker-clicked-global-variable is set...
Like so:
marker.on('dragend', function(e) {
var that = this;
var marker = e.target;
var new_lnglat = marker.getLngLat();
var new_pos = marker._pos;
var old_pos = map.project([marker.lon, marker.lat]);
var dist = Math.sqrt((new_pos.x - old_pos.x)**2 + (new_pos.y - old_pos.y)**2);
if (dist < 16) {
// "clicked"
setTimeout(function() {
if (!that.getPopup().isOpen()) that.togglePopup();
}, 20);
} else {
// "dragged"
blink_location(marker.id);
var row = $("tr[data-location_id='" + marker.id + "']");
toggle_location_edit(row, true);
row.find('.location-edit-lat').val(new_lnglat.lat.toFixed(3))
row.find('.location-edit-long').val(new_lnglat.lng.toFixed(3))
}
marker.lat = new_lnglat.lat;
marker.lon = new_lnglat.lng;
});
Catching click events on marker would make life much easier. Right now it's hard.
Any update?...
+1 on this, being able to handle marker clicks with custom events is an important part of creating a rich UI
The documentation shows how to add a marker in the first doc.
It then proceeds to explain all other things through the use the more complex layers.
Please add marker.on()
event handlers to make markers more useful for basic UI.
+1 on this.
I want to intercept click events on HTML map markers while being able to access the markers location.
I've worked around this using a DOM event listener in combination with a closure as follows:
// the Mapbox GL JS API does not provide a mechanism for getting click events
// on HTML markers. We can hook the DOM click event on the marker element but
// that then does not give us the location or the marker the was clicked.
//
// By using a closure we can associate the click event with it's corresponding
// marker.
let markerElement = <some div element>
let marker = <marker Instance>
let popup = <popup Instance>
let map = <mapbox instance>
function createClickListener() {
return function() {
// marker location is available as marker.getLngLat()
popup.setLngLat( marker.getLngLat() ).addTo( map );
}
}
this.onClickListener = createClickListener();
markerElement.addEventListener( 'click', this.onClickListener );
Please add this, at least "click" event but more would be preferable. Also an easy way to detach handlers or a way to pass option to marker#remove
method that could also detach handlers.
Would be great to have this feature from library. In the meantime you could use this workaround and build your own onClick method by extending the class: https://bl.ocks.org/chriswhong/8977c0d4e869e9eaf06b4e9fda80f3ab
+1 on this.
+1
Sort of solution for the marker;
I had to hijack by creating my own onClick event which works and gives me what I need for over a year now.
// Override internal functionality
mapbox.Marker.prototype.onClick = function(handleClick) {
this._handleClick = handleClick;
return this;
};
mapbox.Marker.prototype._onMapClick = function(t) {
const targetElement = t.originalEvent.target;
const element = this._element;
if (this._handleClick && (targetElement === element || element.contains((targetElement)))) {
this.togglePopup();
this._handleClick();
}
};
const marker = new mapbox.Marker({ color: createColor(property) })
.setLngLat([lng || lon, lat])
.setPopup(popup)
.onClick(() => {
dispatch('showpreview', property.id);
//map.setCenter([lon, lat]);
})
.addTo(map);
hopefully that helps. But it would be great if we had Marker.on('click', => {})
you can see full code here: https://github.com/secretgspot/cariari-realty/blob/master/src/components/Map/Marker.svelte
this does help! thanks. but still, why such an easy obvious call doesnt exsit to begin with? leaflet, coooome on!!!
I had to hijack by creating my own onClick event which works and gives me what I need for over a year now.
// Override internal functionality mapbox.Marker.prototype.onClick = function(handleClick) { this._handleClick = handleClick; return this; }; mapbox.Marker.prototype._onMapClick = function(t) { const targetElement = t.originalEvent.target; const element = this._element; if (this._handleClick && (targetElement === element || element.contains((targetElement)))) { this.togglePopup(); this._handleClick(); } }; const marker = new mapbox.Marker({ color: createColor(property) }) .setLngLat([lng || lon, lat]) .setPopup(popup) .onClick(() => { dispatch('showpreview', property.id); //map.setCenter([lon, lat]); }) .addTo(map);
hopefully that helps. But it would be great if we had Marker.on('click', => {})
you can see full code here: https://github.com/secretgspot/cariari-realty/blob/master/src/components/Map/Marker.svelte
this does help! thanks. but still, why such an easy obvious call doesn't exsit to begin with? leaflet, coooome on!!!
it only doesn't exist for individual marker (dunno why), but they do have built-in onClick for marker when you provide it as large dataset of markers. I too wish they would make it for individual marker so I wouldn't have to hijack actions and possibly deal with bugs in the future
cool, good to know!
thank you very much for your answer
Juliet Gavison 0526737323
On Wed, May 19, 2021 at 4:15 AM secretgspot @.***> wrote:
this does help! thanks. but still, why such an easy obvious call doesn't exsit to begin with? leaflet, coooome on!!!
it only doesn't exist for individual marker (dunno why), but they do have built-in onClick for marker when you provide it as large dataset of markers. I too wish they would make it for individual marker so I wouldn't have to hijack actions and possibly deal with bugs in the future
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mapbox/mapbox-gl-js/issues/7793#issuecomment-843672296, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIAD3VRS3O62CDWEBISLBDLTOMGMRANCNFSM4GRJTVEQ .
3 years later and we still waiting for a click event?
Yes, indeed we do
3 years later and we still waiting for a click event?
Still waiting 😴
Oh, come on! :(
still waiting :/