Do not fire 'zoom_changed' event when calling fitBounds function on map - google-maps

Is there a way to prevent the zoom_change event from being triggered if it occurs due to fitBounds() ?
I am having an issue where I need to do a search on the server from client when there is a zoom change to map but every time I call fitBounds() it causes zoom_change to trigger which causes the client to do another search on the server. I am only interested in zoom_change done by users and not programmatically using fitBounds.

When you do a fitBounds in your program, set a global flag. When the zoom_changed event fires, if the flag is set, clear it, otherwise send your request off to the server.

After many frustrating hours, here is my solution:
var tiles_listener = google.maps.event.addListenerOnce(map, 'tilesloaded', function() {
var zoom_listener = google.maps.event.addListener(map, 'zoom_changed', function() {
reloadMarkers();
});
});
Note that I am calling addListenerOnce for tilesloaded to ensure that the zoome_changed listener is only added once, after the first fitBounds completes.

It's an old question, but it may be useful to others. I had the same problem, zoom_changed been triggered every time I called fitBounds() when adding several markers to a map. What I did was to add the listener to the map_changed event after the map was completely loaded, like this:
google.maps.event.addListener(map, 'tilesloaded', function() {
google.maps.event.addListener(map, 'zoom_changed', function() {

There is another way to do that by removing that that event. I think it would be much easier and cleaner than adding a global variable.
vm.map.events.bounds_changed = function() {
google.map.event.removeListener(zoom_changed);
}
Another way (forgotten to add that):
vm.map.events.bounds_changed = function() {
google.map.event.addEventListener(zoom_changed,function(mapper,eventName,args){});
}

In my app, I may fire off zoom change events programmatically, either via setZoom(), fitBounds(), or setOptions().
MAN = {};
MAN.globalMap = google.maps.Map(document.getElementById('map'));
I then route all programmatic calls to set the zoom through wrapper functions that flip a flag to false before they go on to set the zoom.
MAN.savedZoom = MAN.globalMap.getZoom();
MAN.saveZoomFlag = true;
// we want it to always be true, unless zoom was
// changed programatically.
MAN.setZoom = function setZoom(zoomLevel) {
console.log("won't save that I'm setting zoomLevel to " + zoomLevel);
this.saveZoomFlag = false;
MAN.globalMap.setZoom(zoomLevel);
};
MAN.fitBounds = function fitBounds(bounds) {
console.log("setting bounds, won't save zoomlevel.");
this.saveZoomFlag = false;
MAN.globalMap.fitBounds(bounds);
};
MAN.setOptions = function setOptions(options) {
console.log("setting options, won't save zoomlevel.");
this.saveZoomFlag = false;
MAN.globalMap.setOptions(options);
};
I then declare a listeners. At first I was declaring only the first, and was puzzled that it wasn't working:
google.maps.event.addListener(
MAN.globalMap,
'zoom_changed',
function zoom_changed_listener() {
console.log(
"zoom changed to " +
MAN.globalMap.getZoom() + "; " +
(MAN.saveZoomFlag ? "saving." : "not saving.")
);
if (MAN.saveZoomFlag) {
console.trace("saving");
MAN.savedZoom = MAN.globalMap.getZoom();
}
MAN.saveZoomFlag = true;
}
);
you may also find the idle event helpful, however, if you're just trying to avoid the initial set. See more about the maps events here: https://developers.google.com/maps/documentation/javascript/events

I suppose this should be simple. Have a variable say
var z = map.getZoom(); //scope this variable appropriately(somewhere before you call the fitbounds()
and then after the map.fitbounds() immediately do
map.setZoom(z); //this automatically changes the zoom level back to the previous level

Related

Google maps on mobile with gestureHandling = cooperative still fires click event

When on a mobile device, if I want to scroll the page by clicking somewhere on the Google map and scrolling down, I still receive a click event.
I see the "use two fingers to move the map" message, and the page is scrolling as expected but I receive a click event after. And I use this click event to add a marker. So at this point, it's messing the behavior of the whole page.
Here is a simple jsfiddle to reproduce (on mobile of course, or in mobile mode on chrome https://developers.google.com/web/tools/chrome-devtools/device-mode/):
Just scroll down while clicking on the map and an alert with the text "Click Event" will popup.
google.maps.event.addListener(map, 'click', function (evt) {
alert("Click Event");
});
https://jsfiddle.net/83o1my1p/
I believe this problem has already been reported in Google issue tracker. Have a look at the following bug:
https://issuetracker.google.com/issues/64586414
Feel free to add a star in the bug to express your interest and subscribe to notifications. Also, note that the person who filed the bug also found a workaround:
The clicks can be prevent after the scroll, the same way they they are prevented after dragend events when using on a desktop. As I said above, I worked around it myself by handling the mousedown event, and comparing the location of it, to the location of the click event.
I hope this helps!
Here is the work around I used.
var mouseDownPos = null;
google.maps.event.addListener(map, 'mousedown', function(e) { mouseDownPos = e.pixel });
google.maps.event.addListener(map, 'click', function(e) {
var mouseUpPos = e.pixel;
var distance = Math.sqrt(Math.pow(mouseDownPos.x - mouseUpPos.x, 2) + Math.pow(mouseDownPos.y - mouseUpPos.y, 2));
if(distance > 10) return; // Adjust for tolerance
// Do what you need here
});
My solution is to use a var and change it state on touch events. Then use it in the click event:
var dragged = false;
google.maps.event.addListener(map, 'click', function (evt) {
if (!dragged) {
// Do stuff here
}
});
google.maps.event.addDomListener(canvas, 'touchmove', function (event) {
if (event.touches.length == 1) {
dragged = true;
}
});
google.maps.event.addDomListener(canvas, 'touchend', function(event) {
dragged = false;
});

Bring GoogleMaps InfoWindow to front

I have a GoogleMaps APIv3 application in which multiple InfoWindows can be open at any one time. I would like to be able to bring an obscured InfoWindow to the front of all other InfoWindows if any part of it is clicked - similar to the behaviour of windows in MS Windows OS.
I had thought to add an onclick event handler which increases the z-index of the InfoWindow, but the event handler does not appear to be firing.
ZIndex is a global variable that keeps increasing as InfoWindows are clicked - or thats the theory anyway.
Can anyone help ?
Here is my code:-
var ZIndex=1;
var iw = new google.maps.InfoWindow({ content:contentString });
google.maps.event.addListener(iw, 'click', handleInfoWindowClick(iw) );
function handleInfoWindowClick(infoWindow) {
return function() {
infoWindow.setZIndex(ZIndex++);
}
}
there is no click-event for an infoWindow, it's a little bit more difficult.
you'll need to use an element(not a string) as content for the infowindow, because you need a DOMListener instead a listener for the infowindow-object
when domready-fires, you must apply the click-DOMListener to the anchestor of this content-node that defines the infowindow
The following code will do this for you, add this to your page:
google.maps.InfoWindowZ=function(opts){
var GM = google.maps,
GE = GM.event,
iw = new GM.InfoWindow(),
ce;
if(!GM.InfoWindowZZ){
GM.InfoWindowZZ=Number(GM.Marker.MAX_ZINDEX);
}
GE.addListener(iw,'content_changed',function(){
if(typeof this.getContent()=='string'){
var n=document.createElement('div');
n.innerHTML=this.getContent();
this.setContent(n);
return;
}
GE.addListener(this,'domready',
function(){
var _this=this;
_this.setZIndex(++GM.InfoWindowZZ);
if(ce){
GM.event.removeListener(ce);
}
ce=GE.addDomListener(this.getContent().parentNode
.parentNode.parentNode,'click',
function(){
_this.setZIndex(++GM.InfoWindowZZ);
});
})
});
if(opts)iw.setOptions(opts);
return iw;
}
Instead of google.maps.InfoWindow() you must call now google.maps.InfoWindowZ()
It also returns a genuine InfoWindow, but with the mentioned listener applied to it. It also creates the node from the content when needed.
Demo: http://jsfiddle.net/doktormolle/tRwnE/
Updated version for visualRefresh(using mouseover instead of click) http://jsfiddle.net/doktormolle/uuLBb/

Google Map API V3 — How to prevent mouse click event for a marker when user actually has double clicked

I have a marker on the map to which I want to bind two events:
click
dblclick
I want to do the following:
When user clicks on the marker, map should zoom-in and will show
more detailed map.
I want to bind 'dblclick' event to the same marker so that it will
load some third-party reports in adjacent 'div' element.
In other words, I want it to behave differently when user clicks or dblclicks. But the problem is, when I bind both these event to marker and user 'double clicks' the marker, 'click' handler is getting fired, which I don't want to let it happen.
Is it true that, when user double-clicks, click event is also fired? If so, how to prevent it from triggering 'click' event when user actually double-clicked?
Is there any way so that I can do different things on either click and double-click event of the marker?
It's a known nuance of the api, you need to install a click counter timeout, like this:
function createMap2() {
var infoWindow = new google.maps.InfoWindow();
var map = new google.maps.Map(document.getElementById("map2"), myOptions);
var doubleClicked=false;
var clickEvent;
google.maps.event.addListener(map, 'dblclick', function(event) {
doubleClicked=true;
});
function handleClick() {
if (!doubleClicked) {
infoWindow.setPosition(clickEvent.latLng);
infoWindow.setContent(createInfo(clickEvent));
infoWindow.open(map);
}
}
google.maps.event.addListener(map, 'click', function(event) {
clickEvent = event;
doubleClicked = false;
window.setTimeout(handleClick, 250);
});
}
Above code extracted from http://www.william-map.com/20100506/1/v3click.htm
Check out these links for more info:
https://groups.google.com/forum/?fromgroups=#!topic/google-maps-js-api-v3/YRAvYHngeNk
https://groups.google.com/forum/?fromgroups=#!topic/google-maps-js-api-v3/2MomDiLMEiw
You can use a pre-handler function that separates single from double clicks. In this case, the second click must come within 500 miliseconds of the first one:
//Global vars
var G = google.maps;
var clickTimeOut = null;
G.event.addListener(marker,'click',mClick);
function mClick(mev) {
if (clickTimeOut) {
window.clearTimeout(clickTimeOut);
clickTimeOut = null;
doubleClick(mev);
}
else {
clickTimeOut = window.setTimeout(function(){singleClick(mev)},500);
}
}
function doubleClick(mev) {
// handle double click here
}
function singleClick(mev) {
window.clearTimeout(clckTimeOut);
clickTimeOut = null;
// handle single click here
}
mev is the mouseEvent object that the event handlers receive as parameter.

Handling together marker click and doubleclick on googlemaps

I have the following problem.
On my google map app I need to distinguish click and doulbe click on the markers.
So when I am creating markers I create two listeners:
google.maps.event.addListener(markerTMP, 'click', (function(routeID) {
return function() {
console.log('click '+routeID);
}
})(pointRoute));
google.maps.event.addListener(markerTMP, 'dblclick', (function(routeID) {
return function() {
p('double click '+routeID);
}
})(pointRoute));
The problem is that when I use double click, click is also used.
My idea how to handle this:
On click - create a timeout, on double click - delete it.
But is there a normal way to handle this?
I was not able to found it in API reference.
Here is my workaround. it uses a global var to work across the two functions. Not the most eloquent, but works.
google.maps.event.addListener(marker, 'click', function() {
x = 0
update_timeout = setTimeout(function(){
if (x == 0) {
// do something ;
};
}, 300);
});
google.maps.event.addListener(marker, 'dblclick', function() {
x=1
// do something ;
});
There is no "official" way to handle this in the API. Creating a timeout seems to be the accepted approach.
Try using a click event and a rightclick event instead of click and dblclick.

Google Maps v3 OverlayView.getProjection()

I cannot seem to figure out why the object returned by getProjection() is undefined. Here is my code:
// Handles the completion of the rectangle
var ne = recBounds.getNorthEast();
var sw = recBounds.getSouthWest();
$("#map_tools_selat").attr( 'value', sw.lat() );
$("#map_tools_nwlat").attr( 'value', ne.lat() );
$("#map_tools_selng").attr( 'value', ne.lng() );
$("#map_tools_nwlng").attr( 'value', sw.lng() );
// Set Zoom Level
$("#map_tools_zoomlevel").attr( 'value', HAR.map.getZoom()+1 );
document.getElementById("map_tools_centerLat").value = HAR.map.getCenter().lat();
document.getElementById("map_tools_centerLong").value = HAR.map.getCenter().lng();
// All this junk below is for getting pixel coordinates for a lat/lng =/
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(HAR.map);
var projection = overlay.getProjection();
// END - all the junk
var p = projection.fromLatLngToContainerPixel(recBounds.getCenter());
alert(p.x+", "+p.y);
My error is: Cannot call method 'fromLatLngToContainerPixel' of undefined
Actually, i the reason why this happens is because the projection object is created after the map is idle after panning / zooming. So, a better solution is to listen on the idle event of the google.maps.Map object, and get a reference to the projection there:
// Create your map and overlay
var map;
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(map);
var projection;
// Wait for idle map
google.maps.event.addListener(map, 'idle', function() {
// Get projection
projection = overlay.getProjection();
})
I kind of figured out what was going on. Even though it is still not crystal clear why this happens, I know that I had to instantiate the variable "overlay" right after instantiating my google map (HAR.map). So I practically moved that code snippet into my HAR class and now i use:
HAR.canvassOverlay.getProjection().fromLatLngToContainerPixel( recBounds.getCenter() );
So now, every time I create a map via my class "HAR" I also have a parallel OverlayView object within my class.
The Error could have been with losing scope of my class object, but I think it was more of the map event "projection_changed" not being fired. I got a hint from the map API docs for map class, under method getProjection():
"Returns the current Projection. If the map is not yet initialized (i.e. the mapType is still null) then the result is null. Listen to projection_changed and check its value to ensure it is not null."
If you are getting the similar issue, make sure that you assign your overlayView.setMAP( YOUR_MAP_OBJECT ) closely after instantiating the map object.