I have a question concerning clicks on a map in leaflet. If I click on the map I want to set a marker there, but if doubleclick on the map I just want to zoom in without setting a marker. So I have the follwing code:
var map = L.map(attrs.id, {
center: [scope.lat, scope.lng],
zoom: 14
});
var marker = L.marker([scope.lat, scope.lng],{draggable: true});
map.on('click', function(event){
marker.setLatLng(event.latlng);
marker.addTo(map);
});
The problem now is, when I doublclick on the map the click event is also fired and I would like to remove that behavior. How can I achieve that?
Thanks
Magda
So, I found a way to do that, I am still not sure, if there is a better way to do it.
var map = L.map(attrs.id, {
center: [scope.lat, scope.lng],
zoom: 14
});
map.clicked = 0;
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
var marker = L.marker([scope.lat, scope.lng],{draggable: true});
map.on('click', function(event){
map.clicked = map.clicked + 1;
setTimeout(function(){
if(map.clicked == 1){
marker.setLatLng(event.latlng);
marker.addTo(map);
map.clicked = 0;
}
}, 300);
});
map.on('dblclick', function(event){
map.clicked = 0;
map.zoomIn();
});
Had the same problem of 2 x unwanted click events firing when listening for dblclick.
Note: I wanted single and double clicks on the same element to perform different actions.
I adapted this approach to my leaflet map, which is not bullet proof but eliminates 99% of the conflicts:
var timer = 0;
var delay = 200;
var prevent = false;
$("#target")
.on("click", function() {
timer = setTimeout(function() {
if (!prevent) {
doClickAction();
}
prevent = false;
}, delay);
})
.on("dblclick", function() {
clearTimeout(timer);
prevent = true;
doDoubleClickAction();
});
Credit: CSS-Tricks
Code Pen Example
This is still an issue on recent (leaflet 1.4) versions.
Alternative approach I used that:
legit usage of setTimeout and clearTimeout
without adding random props to the map object
no jQuery:
map.on('click', function(event) {
if (_dblClickTimer !== null) {
return;
}
_dblClickTimer = setTimeout(() => {
// real 'click' event handler here
_dblClickTimer = null;
}, 200);
})
.on("dblclick", function() {
clearTimeout(_dblClickTimer);
_dblClickTimer = null;
// real 'dblclick' handler here (if any). Do not add anything to just have the default zoom behavior
});
Note that the 200 ms delay must be tested. On my environment using a value like 100 was not enough as the doubleclick event is triggered with a delay.
Related
I think I'm missing something basic about javascript. I could use your help!
Scenario: After clicking "search" the google map loads. As it loads, a checkbox appears that says, "Redo results when map is moved" (similar to yelp). If the map is dragged or zoomed, the results update. That part works fine.
Problem: The value of the checkbox (true or false) is recognized by the redo() function when the map loads, and it doesn't check it again after that (because even though redo() is updating the results it's not reloading the map). So if you toggle the checkbox, the "redo" function doesn't notice!
Simplified Code:
//HAML
%input{:type => "checkbox", :id => "followCheck", :name => "followCheck", :onclick => 'handleClick();'}
//load the map
function showLocations() {
map = new google.maps.Map(document.getElementById("map_canvas"), {
bunch of map stuff;
map.fitBounds(bounds);
redo(); //this is the redo function, see below
}
}
function handleClick(checkbox) {
var chk = document.getElementById("followCheck").checked;
console.log("checkbox value is currently " + chk);
return chk;
}
function redo() {
var chk = handleClick();
console.log("inside redo, value is " + chk);
if (chk == true) {
google.maps.event.addListener(map, 'mouseup', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
}
}
(note, that redo() function was written by my collaborator, who is smarter than me and currently out of town.)
So, Redo() loads when the map loads. If the checkbox is true when the map loads, then the redo() function processes zoom/drag. But it still does it even after changing the value to false (and if the value is false when the page loads, the opposite happens).
What is needed to get redo() to see that the value has changed? push()? bind()? get/set? change()? addEventListener()? Should I reload the map when the checkbox is toggled (I'd rather not)? A whole new approach? I'm stuck! Thanks. I'm open to JQuery ideas, too.
I solved this. First off, with a small adjustment to the function I was able get it to turn ON after checking the box (even if the results loaded while the box was unchecked via a previous page session). Here is that code (I actually found like three variations that all had the same result):
function redo() {
if ($("#followCheck").is(':checked')) {
google.maps.event.addListener(map, 'mouseup', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
}
}
The problem was that it didn't turn off. And that's because I've added two listeners! This answer helped me remove the listeners. Note that (for me) it only worked when the removeListener() function is within the addListener() function. Kind of strange.
function redo() {
var listener1 = google.maps.event.addListener(map, 'mouseup', function() {
if (!$("#followCheck").is(':checked')) {
google.maps.event.removeListener(listener1);
} else {
mapAdjusted = 1;
updateMapAfterUserInteraction();
}
});
var listener2 = google.maps.event.addListener(map, 'zoom_changed', function() {
if (!$("#followCheck").is(':checked')) {
google.maps.event.removeListener(listener2);
} else {
mapAdjusted = 1;
updateMapAfterUserInteraction();
}
});
}
Oh and I also changed onclick() to onchange()
After page load function "intro" is launch. This function displays two buttons, after click on one of them another function "startGame" starts with parameter "innerHTML" from click button.
Then the right panel appears and "START" button counts time from 10. Countdown stops when user click on map or time reaches 0.
After "START" user can click on map (to add a marker) only ones, and click "START" again to add another marker.
When users click on map 4 times "game" finishes and two buttons appear again.
And that is when a problem starts. When function "startGame" starts again and user clicks "START" button, countdown doubles (you can see it in console).
If user clicks on map one countdown stops but second still counts to zero.
Can anyone tell me why time is doubled?
Link to live version: http://najlepszekomisy.co.pl/
Thank you.
var elem =
{
map: document.getElementById("mapa"),
panel: document.getElementById("right_panel"),
games: document.getElementById("games"),
draw: document.getElementById("draw"),
points: document.getElementById("pointsGet"),
timer: document.getElementById("timer")
};
(function intro()
{
elem.games.addEventListener("click",function(e){
if(e.target.tagName==="H4")
{
TweenMax.to(elem.games,1,{ease: Back.easeOut,top: -50,onComplete:function(){
startGame(e.target.innerHTML);}})
}
},false)
})();
function startGame(hit)
{
var gameElement =
{
mapa:0,
clickListener:0,
number:0,
usingSet:4,
timeNum:10
};
(function loadMap()
{
var mapOptions =
{
zoom: 7,
disableDefaultUI: true,
zoomControl: true,
center: new google.maps.LatLng(51.95442214470796, 19.14093017578125)
};
gameElement.mapa = new google.maps.Map(elem.map, mapOptions);
google.maps.event.addListener(gameElement.mapa,'idle',function()
{
TweenMax.to("#right_panel",2,{ease: Back.easeIn,right: 0,onComplete:function(){
TweenMax.set(".anim_from_bottom", { display: 'block' });
TweenMax.staggerFrom(".anim_from_bottom",0.5,{y:1600},0.2);
google.maps.event.clearListeners(gameElement.mapa, 'idle');
}});
});
})();
elem.draw.addEventListener("click",function(){
if(gameElement.number<gameElement.usingSet)
{
gameElement.number++;
timer.time=gameElement.timeNum;
timer.calcTime();
gameElement.clickListener = google.maps.event.addListener(gameElement.mapa, "click", function (e) {
addMarker(e.latLng.lng(), e.latLng.lat());
});
elem.draw.disabled=true;
}else{result()}},false);
function addMarker(lng,lat)
{
timer.stopTime();
var opcjeMarkera =
{
position: new google.maps.LatLng(lat,lng),
map: gameElement.mapa,
title: hit
};
var marker = new google.maps.Marker(opcjeMarkera);
google.maps.event.removeListener(gameElement.clickListener);
elem.draw.disabled=false;
}
var timer =
{
time: 0,
startTime:0,
calcTime:function()
{
elem.timer.className = "elem";
elem.timer.innerHTML = timer.time;
console.log(timer.time);
if(timer.time===0){elem.timer.className = " ";clearTimeout(timer.startTime);}
else
{
--timer.time;
timer.startTime = setTimeout(timer.calcTime, 1000);
}
},
stopTime:function()
{
clearTimeout(timer.startTime);
elem.timer.className = " ";
this.time=gameElement.timeNum;
}
};
function result ()
{
console.log("koniec");
gameElement.number=0;
TweenMax.to("#right_panel",2,{ease: Back.easeIn,right: -300});
TweenMax.to(elem.games,1,{ease: Back.easeOut,top: 50})
}
}
Every time your H4 button is clicked it calls startGame function. Every time startGame function is called it adds one more event listener to the Start button, so start handler is called once when you play first time, twice the second time, thrice next time etc.
To avoid this you can:
add event listener in init function (which is called once)
remove event listeners before adding new one (see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
use onclick property instead of .addEventListener() method if there is only one handler needed for this button
You need to make sure that the calcTime function which includes this code:
timer.startTime = setTimeout(timer.calcTime, 1000);
cannot be invoked while it is already running, because if it does, then another call to calcTime will be queued up causing the clock to tick twice as fast.
I am using Opnelayers control for selection of feature
OpenLayers.Control.SelectFeature
when I triggered the single click event then feature get selected and its working fine for me. Now I want to use double click event for another operation.
I have get the idea from this link feature to have both a single click and double click event?
I have used the same code and both click events working fine but I cannot get the feature on which click event performed. This is the code
handler = new OpenLayers.Handler.Click(
select,
{
click: function(evt)
{
var feature = this.layer.getFeatureFromEvent(evt);
console.log(feature); // output null
if(this.layer.selectedFeatures){
this.unselect(this.layer.selectedFeatures[0]);
}
},
dblclick: function(evt)
{
// some other operation
}
},
{
single: true,
double: true,
stopDouble: true,
stopSingle: true
}
);
handler.activate();
Any idea which thing is missing in this code?
Thank you
For The Above Question this might solve your answer with few changes
var handler = new OpenLayers.Handler.Click(
layerName, {
click: function(evt) {
console.log('click has triggered');
var feature = layerName.getFeatureFromEvent(evt);
}
,dblclick: function(evt) {
console.log('dblclick has triggered');
}
},{
single: true
,double: true
,stopSingle: true
,stopDouble: true
}
);
handler.activate();
I have been in a similar situation and used the getClosestFeatureToCoordinate method of the layer source. The code would be something like:
click: function(evt) {
var coord = evt.coordinate;
var source = this.layer.getSource();
var feature = source.getClosestFeatureToCoordinate(coord);
// do something with feature
}
I have done in this way.
click: function(evt) {
var pos = map.getLonLatFromPixel(xy);
var point = new OpenLayers.Geometry.Point(pos.lon, pos.lat);
var list = $.map(this.layer.features, function(v) {
return v.geometry.distanceTo(point);
}); // get distance of all features
var min = (Math.min.apply(null, list)); // minimum distance
var closest = $.map(this.layer.features, function(v) {
if (v.geometry.distanceTo(point) == min)
return v;
});
feature = closest[0];
}
I'm working on a web app that integrates with google maps and I'm having an issue with zoom_changed event firing twice after calling fitBounds. The only reference I have to fitBounds is in updateMap. The only time I attach the event listener is in createMap.
The second call to the zoom changed handler is causing another trigger to update the map.
I've read this article already: Do not fire 'zoom_changed' event when calling fitBounds function on map
The flag is working fine for the first event. But why is there another zoom_changed event being fired?
Even better, how can I prevent the second zoom_changed event from firing?
Here is what I'm seeing in my console log:
updateMap, fitbounds: 0
calling fitbounds...
zoom changed
mapEventHandler, fitbounds: 1
zoom changed
mapEventHandler, fitbounds: 0
Here is the code for my update map function which calls fitBounds:
var mapZoomOrDragEventInProgress = false;
var fitBoundsCalledCount = 0;
var map = null;
updateMap = function () {
console.log("updateMap, fitbounds: " + fitBoundsCalledCount);
var bounds = fitBoundsForVenues();
deleteAndUpdateMarkers();
if (!mapZoomOrDragEventInProgress) {
bn.spg.map.vars.fitBoundsCalledCount++;
if (markers.length > 1) {
console.log("calling fitbounds...");
map.fitBounds(bounds);
} else {
console.log("setting zoom...");
map.setCenter(bounds.getCenter());
map.setZoom(13);
}
}
mapZoomOrDragEventInProgress = false;
}
Here is my create map function:
createMap = function() {
if (map === null) {
var bounds = fitBoundsForVenues();
var mapOptions = {
center: bounds.getCenter(),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
google.maps.event.addListener(map, 'zoom_changed', zoomChangedEventHandler)
google.maps.event.addListener(map, 'dragend', dragendEventHandler);
}
}
Here is my event handler:
zoomChangedEventHandler = function () {
console.log("zoom changed");
console.log("mapEventHandler, fitbounds: " + fitBoundsCalledCount);
//we want to handle only user zoom events, NOT zoom_events triggered from fit bounds or set_zoom
if (fitBoundsCalledCount === 0) {
mapZoomOrDragEventInProgress = true;
var coords = getViewportCoordinates();
updateVenuesAndMapAsync(coords.lat, coords.lng, coords.radius);
} else {
fitBoundsCalledCount--;
}
}
I can't tell you where the 2nd zoom_changed-even has been triggered(I'm sure a single call of fitBounds() is not be the reason)
But instead of using these counters I would suggest to remove the zoom_changed-listener at the begin of updateMap() and to re-assign the listener at the end of updateMap()
i have some troubles using google map api v2. What i want is to be able to go with the mouse over the tooltip.
Below is the code that i have. In firefox works but i have to move the mouse very gently over the tooltip, if i go to fast it will hide (solved : added a delay on the mouseout function, and there check if I trigger the tooltip or not) and the worst part it doesn't wark at all in IE. If i go over the pin point i will be able to see the tooltip, but i can't go with the mouse over it.
function createHotelSearchMarker(point, number, message) {
var newIcon = new GIcon(G_DEFAULT_ICON);
wIcon.image = imageChart + "chst=d_map_spin&chld=0.5|0|CCCCCC|10|_|" + number + "&ext=.png";
var marker = new GMarker(point, {icon: newIcon, clickable:true, zIndexProcess:importanceOrder});
marker.importance = 1;
/*add listener for mouseover*/
GEvent.addListener(marker, "mouseover", function() {
marker.openExtInfoWindow(
map,
"mapInfoWindow",
"<div id='tooltip'>" + message + "</div>",
{beakOffset: 3}
);
marker.setImage(imageChart + "chst=d_map_spin&chld=0.5|0|FF0000|10|_|" + number + "&ext=.png");
marker.importance = 2;
marker.setLatLng(marker.getLatLng());
/*i added a dom listener for the tooltip*/
if(document.getElementById("tooltip") != null) {
GEvent.addDomListener(document.getElementById("tooltip"), "mouseover", function(){
isFiredByDivListener = 1;
GEvent.trigger(marker,"mouseover");
});
GEvent.addDomListener(document.getElementById("tooltip"), "mouseout", function(){
isFiredByDivListener = 0;
GEvent.trigger(marker,"mouseout");
});
}
});
/*add a listener for mouse out*/
GEvent.addListener(marker, "mouseout", function() {
map.closeExtInfoWindow();
marker.setImage(imageChart + "chst=d_map_spin&chld=0.5|0|CCCCCC|10|_|" + number + "&ext=.png");
marker.importance = 1;
marker.setLatLng(marker.getLatLng());
});
return marker;
}
the marker will be return and
if (marker != null){
map.addOverlay(marker);
}
Thank you for your help and time
It seems to me you've got this going on, i.e. a potential problem where you could be recursively triggering the marker mouseover. I'd suggest moving the two addDomListener event listeners outside of the marker's event listener.
GEvent.addListener(marker, "mouseover", function() {
...
GEvent.addDomListener(document.getElementById("tooltip"), "mouseover", function(){
GEvent.trigger(marker,"mouseover");
});