Leaflet User Triggered Events - leaflet

Is there any way to determine whether an event was triggered programmatically or by a user?
We want to reload marker listings when the map moves or zooms, but we are initially setting the bounds of the map with setBounds() (http://leafletjs.com/reference.html#rectangle-setbounds) which is also triggering the moveend (http://leafletjs.com/reference.html#map-moveend) and zoomend (http://leafletjs.com/reference.html#map-zoomend) events which is causing the markers to reload twice.

There seems to be a (undocumented) property on the event object called hard that gets set when the map is moved by setBounds and doesn't get set when the user drags the map or uses the cursors:
map.on('moveend', function (e) {
if (e.hard) {
// moved by bounds
} else {
// moved by drag/keyboard
}
});
Testcase here on Plunker: http://plnkr.co/edit/SloKuB?p=preview
As another option you could bind to event after you've set the bounds so it won't fire when you set the bounds and when you do want to set the bounds afterwards you could first unbind using .off and rebind again after setting with .on. Something like (untested/hacky):
function moveEndHandler () {
....
}
map.on('moveend', moveEndHandler);
function mySetBounds (bounds) {
map.off('moveEnd', moveEndHandler);
map.setBounds(bounds);
map.on('moveend', moveEndHandler);
}

Related

Double on click event with mapbox gl

I am redrawing layers on style.load event and removing the layers
map.on('style.load', function() {
loadByBounds(tempBounds)
});
function loadByBounds(b) {
if (map.getLayer("cluster-count")) {
map.removeLayer("cluster-count");
}
...
map.on('click', 'unclustered-point', function(e) {
var popup = new mapboxgl.Popup()
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(text)
.addTo(map);
})}
But how to remove map.on('click') events? As when I click the point the Popup() displays 2 times. And when I change layer one more time the onclick event fires 3 times and so on. So I think I have to remove the click event but how? Thanks
You might wanna use map.once(). This will add a listener that will be called only once to a specified event type. However after 1 click event got fired this event listener won't listen to any further click events.
https://www.mapbox.com/mapbox-gl-js/api/#evented#once
With map.off() it's basically the opposite of map.on() and you can use it to unregister any applied event listeners. However you would need to add event listeners without an anonymous function in order to use map.off().
https://www.mapbox.com/mapbox-gl-js/api/#map#off
// you would need to use a named function
function clickHandler(e) {
// handle click
}
map.on('click', clickHandler);
// then you can use
map.off('click', clickHandler);
// With an anonymous function you won't be able to use map.off
map.on('click', (e) => {
// handle click
});
To prevent your app from registering multiple listeners you maybe need to set a flag that gets set after your first event listener got applied.
let notListening = true;
function loadByBounds(b) {
// ....
if (notListening) {
notListening = false;
map.on('click', (e) => {
// do something
});
}
}

Make node fixed in echarts force graph

There is a way to set the initial position of nodes in a force graph in echarts. But is there a way to have this node stay at this position?
Of course,there is an attribute named fixed decide a node fix or not.So you can set an event listener on echarts.For example,I set a click event listener like:
myChart.on('click', function (params) {
fixNode(params.data);
});
The params.data is the object that you click on echarts.Then you should write a fixNode method like:
function fixNode(currentNode){
_nodes.forEach(function (node) {
if(node.name==currentNode.name) node.fixed = true;
});
refresh();
}
This method is to traverse the node array named _nodes and find the same node as currentNode in _nodes.when we find it,we can set its attribute fixed true.Finally, we have to rebind data to echarts,so we write a method to refresh echarts:
function refresh(){
option={
series:[
{
data:_nodes
}
]
};
mycharts.setOption(option);
}
Now,the node that you click will fixed in a position.

Find out if a leaflet control has already been added to the map

I wrote a custom Leaflet control. It's some kind of legend that may be added for each layer. The control itself has a close button to remove it from the map (like a popup).
The control can be added by clicking a button.
My problem is that the user may add the same control to the map several times. So what I need is to test if this specific control has already been added to the map and, if so, don't add it again.
I create a control for each layer, passing some options
var control = L.control.customControl(mylayer);
and add it to my map on button click
control.addTo(map);
Now imagine the control has a close button and may be closed. Now if the user clicks the button again, I only want to add the control if it's not already on the map - something like this (hasControl is pseudocode, there is afaik no such function)
if(!(map.hasControl(control))) {
control.addTo(map);
}
For simplicity I made an example where I create a zoom control and add it twice here.
Easiest way is to check for the existence of the _map property on your control instance:
var customControl = new L.Control.Custom();
console.log(customControl._map); // undefined
map.addControl(customControl);
console.log(customControl._map); // returns map instance
But please keep in mind, when using the _map property, that the _ prefix of the property implies that it's a private property, which you are normally not supposed to use. It could be changed or removed in future versions of Leaflet. You're not going to encounter that if you use the follow approach:
Attaching a reference of your custom control to your L.Map instance:
L.Control.Custom = L.Control.extend({
options: {
position: 'bottomleft'
},
onAdd: function (map) {
// Add reference to map
map.customControl = this;
return L.DomUtil.create('div', 'my-custom-control');
},
onRemove: function (map) {
// Remove reference from map
delete map.customControl;
}
});
Now you can check for the reference on your map instance like so:
if (map.customControl) { ... }
Or create a method and include it in L.Map:
L.Map.include({
hasCustomControl: function () {
return (this.customControl) ? true : false;
}
});
That would work like this:
var customControl = new L.Control.Custom();
map.addControl(customControl);
map.hasCustomControl(); // returns true
map.removeControl(customControl);
map.hasCustomControl(); // returns false
Here's a demo of the concept on Plunker: http://plnkr.co/edit/nH8pZzkB1TzuTk1rnrF0?p=preview

Ignore multiple button taps after first one on iPhone webapp using jQuery Mobile?

Assume button A in an HTML5 webapp built with jQuery Mobile.
If someone taps button A, we call foo(). Foo() should get called once even if the user double taps button A.
We tried using event.preventDefault(), but that didn't stop the second tap from invoking foo(). event.stopImmediatePropagation() might work, but it also stops other methods further up the stack and may not lead to clean code maintenance.
Other suggestions? Maintaining a tracking variable seems like an awfully ugly solution and is undesirable.
You can set a flag and check if it's OK to run the foo() function or unbind the event for the time you don't want the user to be able to use it and then re-bind the event handler after a delay (just a couple options).
Here's what I would do. I would use a timeout to exclude the subsequent events:
$(document).delegate('#my-page-id', 'pageinit', function () {
//setup a flag to determine if it's OK to run the event handler
var okFlag = true;
//bind event handler to the element in question for the `click` event
$('#my-button-id').bind('click', function () {
//check to see if the flag is set to `true`, do nothing if it's not
if (okFlag) {
//set the flag to `false` so the event handler will be disabled until the timeout resolves
okFlag = false;
//set a timeout to set the flag back to `true` which enables the event handler once again
//you can change the delay for the timeout to whatever you may need, note that units are in milliseconds
setTimeout(function () {
okFlag = true;
}, 300);
//and now, finally, run your original event handler
foo();
}
});
});
I've created a sample here http://jsfiddle.net/kiliman/kH924/
If you're using <a data-role="button"> type buttons, there is no 'disabled' status, but you can add the appropriate class to give it the disabled look.
In your event handler, check to see if the button has the ui-disabled class, and if so, you can return right away. If it doesn't, add the ui-disabled class, then call foo()
If you want to re-enable the button, simply remove the class.
$(function() {
$('#page').bind('pageinit', function(e, data) {
// initialize page
$('#dofoo').click(function() {
var $btn = $(this),
isDisabled = $btn.hasClass('ui-disabled');
if (isDisabled) {
e.preventDefault();
return;
}
$btn.addClass('ui-disabled');
foo();
});
});
function foo() {
alert('I did foo');
}
});

can't unregister click event in openlayers

I am having issues with OpenLayers and unregistering the click events that are added to a layer. Basically, what I need to do is this:
When a user clicks on a marker, they get a bubble that has an "edit" link in it. The user clicks that and it creates a new layer on the map and then registers a click event to the map waiting for the user to click on the map. When they click somewhere on the map, it then moves the marker to where they clicked. This all works perfectly.
However, the issue is when the user clicks to edit the marker and then clicks on a button OUTSIDE OF THE MAP to cancel the action and NOT move the marker, the unregister of the click event doesn't work. They can still click on the map and it moves the marker.
Here is a sample of the code:
function move_marker(marker) {
lmLayer = new OpenLayers.Layer.Markers("Landmark Creation",{displayInLayerSwitcher: false});
map.addLayer(lmLayer);
map.events.register("click", lmLayer, function(evt){
var pixel = new OpenLayers.Pixel(evt.clientX,evt.clientY);
position = map.getLonLatFromPixel(pixel);
marker.lonlat = pixel;
marker.moveTo(pixel);
marker.draw();
lmLayer.redraw();
OpenLayers.Event.stop(evt);
});
}
function cancel_move() { // this function is triggered by a button outside of the map element
lmLayer = map.getLayersByName('Landmark Creation');
lmLayer[0].events.unregister("click");
map.events.unregister("click");
map.removeLayer(lmLayer[0]);
}
As you can see in the cancel function, I am getting the layer by the layer name, which according to the console.log it is finding at index 0. I added the unregister to the lmLayer in hopes that would help, but so far, no luck. Then on the map element I add the unregister call and then finally I remove that new layer because we don't want it interfering.
I'd appreciate some feedback on this. I'm losing my mind.
Thanks!
I think you need to tell OpenLayers which click event you want it to unregister:
var method = function() {
// Do stuff...
}
map.events.register('click', map, method);
map.events.unregister('click', map, method);
According to the OpenLayers.Events source it checks whether the scope and the method is present in the listener stack:
unregister: function (type, obj, func) {
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
if (listeners != null) {
for (var i=0, len=listeners.length; i<len; i++) {
HERE --> if (listeners[i].obj == obj && listeners[i].func == func) { <-- HERE
listeners.splice(i, 1);
break;
}
}
}
},
I hope that works for you :)