google maps api v3 zoom_changed event fired twice after fitBounds called - javascript

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()

Related

Bing Map pin's dragend called successfully, but Bing Map move event continues

I have used Bing Maps AJAX Control, Version 7.0. and shows the Pins on map. the requirement is like, user can drag and drop the pin and when pin gets drop, it should move to its actual location.
now the issue is, when pin's dragend event called, pin successfully set to its original location, but the mouse is not released and when mouse move, map also gets moved.
code is as below :
var isPinDragged = false;
Microsoft.Maps.Events.addHandler(pin, 'dragstart', startDragDetails);
Microsoft.Maps.Events.addHandler(pin, 'drag', dragDetails);
Microsoft.Maps.Events.addHandler(pin, 'dragend', endDragDetails);
function startDragDetails(e) {
lastLocation = e.entity.getLocation();
currentLatitude = e.entity._location.latitude;
currentLongitude = e.entity._location.longitude;
isPinDragged = false;
$(e.entity.cm1002_er_etr.dom).find("img").removeClass("droppable");
}
dragDetails = function (e) {
isPinDragged = true;
};
endDragDetails = function (e) {
if (isPinDragged) {
isPinDragged = false;
$(e.entity.cm1002_er_etr.dom).find("img").addClass("droppable");
e.entity.setLocation(new Microsoft.Maps.Location(currentLatitude, currentLongitude)); //update pin: move back to old position
}
};
Update:
I don't know, this info. how much helps to resolve. My pin creation is dynamic, there would be N no. of pins as per user logged in.
Microsoft.Maps.loadModule('Microsoft.Maps.Overlays.Style',
{
callback: function () {
map = new Microsoft.Maps.Map(document.getElementById("divDailyCashMap"), mapOptions);
var center = map.getCenter();
var loc = null;
var pin = null;
var pinLayers = new Microsoft.Maps.EntityCollection();
map.entities.push(pinLayers);
var infoboxLayers = new Microsoft.Maps.EntityCollection();
map.entities.push(infoboxLayers);
pinInfobox = new Microsoft.Maps.Infobox(new Microsoft.Maps.Location(0, 0), { visible: false });
infoboxLayers.push(pinInfobox);
$.each(codes, function (index, item) {
loc = new Microsoft.Maps.Location(item.Latitude, item.Longitude);
pin = new Microsoft.Maps.Pushpin(loc, { text: '', draggable: true });
pin.Title = item.Title;
pin.Description = item.CodeDescription;
Microsoft.Maps.Events.addHandler(pin, 'click', displayInfobox);
Microsoft.Maps.Events.addHandler(pin, 'dragstart', startDragDetails);
Microsoft.Maps.Events.addHandler(pin, 'drag', dragDetails);
Microsoft.Maps.Events.addHandler(pin, 'dragend', endDragDetails);
Microsoft.Maps.Events.addHandler(map, 'viewchange', hideInfobox);
Microsoft.Maps.Events.addHandler(map, 'mousemove', mousemoveDetails);
pinLayers.push(pin);
$(pin.cm1002_er_etr.dom).find('img').addClass("droppable").attr({ "data-pinid": item.Code, "data-lat": item.Latitude, "data-long": item.Longitude });
});
}
}
In your dragDetail() function, try:
e.handled = true;
It will tell the event engine that other underlying events should not be fired.
You can also try to add this if it does not work:
return false;

How do i let the infowindow stay on mouseout of the marker in google maps?

I need the info window to appear on mouseover over the marker.I want to close the info window when the mouse is anywhere except the info window itself. I need this because, i have a hyperlink on the info window and the user should be able to click on the hyperlink. Right now i have the below code:
marker.addListener('mouseover', function() {
infowindow.open(map, marker);
}, false);
marker.addListener('mouseout', function() {
infowindow.close(map, marker);
}, false);
This does not work because the minute i remove the focus from marker the infowindow goes off.
Please let me know.
Thanks
Your problem has three parts:
Setting a timeout for handler of marker's mouseover event so that InfoWindow would not disapear until you move your mouse on it.
Clearing that timeout if mouse comes on the InfoWindow:
var closeInfoWindowWithTimeout;
marker.addListener('mouseout', function() {
closeInfoWindowWithTimeout = setTimeout(() => infowindow.close(map, marker), 1000);
}, false);
infoWindowElement.mouseover(() => clearTimeout(closeMarkerInfoWithTimeout));
Setting mouseleave event on the parent of the parent of the parent of your InfoWindow's content. I Know it's dirty and dependent to how Google Maps implemented InfoWindow, but it's all you can do by now.
Be careful that you should do this after InfoWindow attached to your dom! You can wait for it by listening to domready event of InfoWindow:
infowindow.addListener('domready', () => {
infowindowelement.parent().parent().parent().mouseleave(() => infowindow.close(map, marker));
});
Note that you should use an Element to initialising your InfoWindow's content (Here I used variable infoWindowElement).
It sure isn't perfect, but look at this:
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script>
var markers = []; // store the marker objects here
var infoWindow;
var locations = [
[50, 4],
[50.5, 4.5],
[51, 5],
[51.5, 5.5]
]
var contentStrings = [
'Hello',
'World',
'Foo',
'Bar'
];
var mouseIn = false;
function initialize() {
var mapObject = {
mapTypeId: google.maps.MapTypeId.ROADMAP,
zoom: 8,
center: new google.maps.LatLng(locations[1][0], locations[1][1])
};
map = new google.maps.Map(document.getElementById("googleMap"), mapObject);
// make 1 infowindow. We will use it again and again
infoWindow = new google.maps.InfoWindow({
content: ''
});
loadMarkers();
}
google.maps.event.addDomListener(window, 'load', initialize);
function loadMarkers() {
for (var i=0; i<locations.length; i++) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][0], locations[i][1]),
map: map // replaces marker.setMap(map);
});
markers.push(marker); // store the marker in the array
google.maps.event.addListener(marker, 'mouseover', function () {
// first we want to know which marker the client clicked on.
var i=markers.indexOf(this);
// we set the content of infoWindow, then we open it.
infoWindow.setContent('<div onmouseout="mouseOutsideInfowindow()" onmouseover="mouseinsideInfowindow()">' + contentStrings[i] + '</div>')
infoWindow.open(map, markers[i]);
mouseIn = false;
});
}
}
function mouseinsideInfowindow() {
mouseIn = true;
}
function mouseOutsideInfowindow() {
if(mouseIn) {
infoWindow.close();
mouseIn = false;
}
}
</script>
<style>
#googleMap {
height: 400px;
}
</style>
<div id="googleMap"></div>

Determining specific user action that triggers a "bounds_changed" event

I want to be able to keep my map centred and zoomed according to my LatLngBounds when the viewport size changes, but not when a user drags or zooms. I'm using the "bounds_changed" event along with fitBounds() to keep it centred and zoomed properly for viewport changes, but when the map is dragged or zoomed the bounds also change so fitBounds() is essentially preventing these actions too.
function initialize() {
var mapProp = {
center:new google.maps.LatLng(49.8994, -97.1392),
zoom:1,
draggable: true,
zoomControl: true,
scrollwheel: false,
disableDoubleClickZoom: false,
mapTypeId:google.maps.MapTypeId.ROADMAP
};
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(49.803060, -97.229094),
new google.maps.LatLng(49.963223, -97.054686)
);
var map = new google.maps.Map(document.getElementById("googleMap"), mapProp);
google.maps.event.addListener(map, 'bounds_changed', function() {
map.fitBounds(bounds);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
According to the docs I should be able to access event properties, so I thought I'd check for something that would indicate what specifically triggered the "bounds_changed" event, but the event is showing as undefined in the console.
google.maps.event.addListener(map, 'bounds_changed', function(event) {
console.log(event)
});
I can access properties for simple "click" event but not "bounds_changed", so I'm not sure if there is even supposed to be properties for "bounds_changed", or if there is a completely different approach to accomplishing what I'm trying to do.
var drag = false;
var zoom = false;
google.maps.event.addListener(map, 'bounds_changed', function() {
if (!drag && !zoom) {
map.fitBounds(bounds);
}
zoom = false;
});
google.maps.event.addListener(map, 'dragstart', function() {
drag = true;
});
google.maps.event.addListener(map, 'dragend', function() {
drag = false;
});
google.maps.event.addListener(map, 'zoom_changed', function() {
zoom = true;
});

leaflet: don't fire click event function on doubleclick

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.

Javascript events firing in undesired order

The problem:
mouseout event fires too early and causes the infobox to stay open when the user drags their mouse over the marker too fast. In other words... If the user quickly moves in and out of the marker... the mouseover event fires, then the mouseout event fires ... but then, the placesService callback is executed and ib.open() is called.
The code:
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var request = {
key: 'for_my_eyes_only',
location: new google.maps.LatLng(some_lat, some_lng);,
radius: '500',
types: ["restaurant"]
};
var service = new google.maps.places.PlacesService(map);
service.search(request, placesCallback);
function placesCallback(results, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
});
//Infobox settings
var ib = new InfoBox({
//a bunch of irrelevant properties.
});
google.maps.event.addListener(marker, 'mouseover', markerMouseOverFactory(place, marker, ib));
google.maps.event.addListener(marker, 'mouseout', markerMouseOutFactory(ib));
}
}
}
function markerMouseOverFactory(place, marker, ib){
return function(){
var detailService = new google.maps.places.PlacesService(map);
detailService.getDetails({reference: place.reference}, function(details, status){
if (status == google.maps.places.PlacesServiceStatus.OK) {
ib.setContent(/*set some content using details*/);
ib.open(map, marker);
}
});
}
}
function markerMouseOutFactory(ib){
return function(){
ib.close();
}
}
The question:
Is there a way to abandon a google maps AJAX request? If I could abandon the AJAX request in the mouseout listener, all would be good. Or, how would you solve this? I tried using a simple flag in the mouseout, but couldn't get it working.
There is no method to cancel the request in the API as far as I know. But what I would do is to delay the callback executed inside the mouseover event. So that if the user holds the mouse longer than a specified time; it would mean he/she wants the infobox displayed.
Workaround:
var delayTimer, delay = 800; //less than 1 sec. delay
google.maps.event.addListener(marker, 'mouseover', function() {
delayTimer = setTimeout(function() {
markerMouseOverFactory(place, marker, ib);
}, delay);
});
google.maps.event.addListener(marker, 'mouseout', markerMouseOutFactory(ib));
function markerMouseOutFactory(ib){
clearTimeout(delayTimer); //clear timeout here
return function(){
ib.close();
}
}

Categories

Resources