Enabling event propogation effectively in Google Maps API v3 - javascript

As covered in an existing question, I have a piece of code which (nice and simply) keeps a div updated with the current location of the cursor like so.
function updateLocation(e) {
document.getElementById('current_coordinate').innerText = e.latLng.toUrlValue(6);
}
It works brilliantly until the cursor moves over an overlay. The overlay consumes the event so the event listener I have defined on the map never fires.
google.maps.event.addListener(map, 'mousemove', updateLocation);
Questions:
Must I redefine that listener on every overlay I create or is there simpler way?
If I must define listeners on every overlay, roughly how many overlays may I have without performance suffering?
The overlay is interactive and so setting clickable to false is not an option.

See also these discussions that indicate this is a bug - as yet unfixed:
https://groups.google.com/forum/#!topic/google-maps-js-api-v3/z_K7hxKhonI
https://groups.google.com/forum/?hl=en#!topic/google-maps-js-api-v3/mM0mB9FcyAU

Related

using the proper google map listener

I have a google map that I am building.
I have a listener on it:
google.maps.event.addListener(map, 'idle', function(ev){
plotzips();
});
The function on the inside calls for a kml:
var kmlLayer = new google.maps.KmlLayer(url, {
suppressInfoWindows: false,
map:map,
zindex: 0,
clickable : false
});
Now. Whenever there's a movement on the map done by a user, the kml refreshes.
This part works.
The problem is that it works too well. the trigger 'idle' works also during the refresh when the kml loads. How do I use the trigger ONLY when a user moves the map?
Thanks
Even though the API documentation (check the section under "Events") states that the 'idle' event is "fired when the map becomes idle after panning or zooming," I (like you) have found that it also fires for other things.
You might have better luck with the 'dragend' event. Check the "Events" section on the "Map" class here. To get the precise behavior you're looking for, you may need to bind to multiple events. For example, you may also wish to bind 'zoom_changed' to load KML data on map zoom in or out.

Google Maps API event listener priority

I have a map which has a google.maps.event.addListener set on the map object for click events to place a marker on the map. Now I want to add a context menu to the markers, so am creating a custom overlay in the floatPane. Again google.maps.event.addListener is used to add a rightclick event to each marker to position and display the menu.
Once the menu is displayed I want it to be cleared by either a menu item being selected, escape being pressed, or a click on the map.
The menu has a div for each items using jQuery .on to attach a click handler to them, whilst when the menu is displayed a keydown handler is attached to the document using .on to check for escape being pressed. These work as desired but I am unable to find a satisfactory solution to detecting a click on the map.
If I use google.maps.event.addListener on the map object to cancel the menu it works, but also registers with the listener to add a marker and event.stopPropagation() does not affect this. i.e. Cancelling the menu also adds a marker. I believe the Google API triggers the handlers in the order they were added with no way to change the priority.
I have also tried using a jQuery .on handler on the div to which the map is attached. But if I use click or mouseup event it is triggered by the right click which adds the menu. i.e. The menu flashes on screen as it appears but immediately closes. A mousedown event avoids this problem, but obviously still triggers the Google API handler to add a marker. It also registers the click to drag the map to scroll it, which the Google API does not and would be the preferred behaviour.
So it seems to me there are only two solutions to this problem.
One would be to cancel any handlers on the map when the menu is displayed, then re-add them once it closes. This seems unnecessarily excessive though.
The other is to make wrap the content of the handlers in a conditional statement to detect whether the menu is open. This could be either by using a global variable as a flag or adding a status property to the menu's object.
But this would make the code less reusable and go against the point of having separate event handlers. I may as well just put the code to close the menu in the original handler too.
Is there anything I am missing? It does not seem to be too obscure a thing to want to do but the inability to set the priority of handlers in the Google API means either having to code the handlers around each other.
For what it's worth, I believe your first approach (adding and removing handlers) is the "cleanest" (or most elegant?) approach.
If you think of the handlers as only having use/meaning when the menu is present, from a conceptual perspective, there's no need for them to exist when the menu is not visible.
If you bundle the event-wiring into the same function(s) or method(s) that handle the menu, then it becomes a self-contained, re-usable element that doesn't leave any "garbage" behind.
Since I don't have your code in front of you, here's some pseudocode:
function displayMenu() {
$menu.show()
.on('click', function() { /* etc */ });
}
function hideMenu() {
$menu.hide()
.off() // remove ALL event handlers
}
Or, even better, encapsulate it in an object:
var menu = {
show: function() {
// ...
}
hide: function() {
// ...
}
};
Or you could use a jQuery function too (though I generally eschew jQuery when I'm working with a map API, just to keep the number of dependencies low).

How to implement "zoom" and "drag" functionality in Bing Maps 7.0?

I am developing a web application that uses Bing maps 7.0. I was wondering if there was a way to bind a function to a Map class event that listens for when the map is either dragged or zoomed. From what I see in the documentation, the viewchangeend event for the Map class is close to what I am looking for, however this event is called in situations that I do not particularly want to account for, such as if I hide the map's containing div using jQuery or on map creation/initialization. Ideally, I would also like this to be a throttled event, where the handler function gets called after a given amount of time. Any idea on how this could be implemented?
Update
As suggested by psousa, I used the viewchangeend event.
This is the hack around that I tried implementing to sort of try and detect if the map has zoomed or been dragged. I still bind the handler to the viewchangeend event, but this time I check to see if the Northwest coordinates or Southeast coordinates of the map have changed.
var map; // contains Microsoft.Maps.Map instance
var nw; // contains the NW Microsoft.Maps.LocationRect instance
var se; // contains the SE Microsoft.Maps.LocationRect instance
// initialization code goes here some where
Microsoft.Maps.Events.addThrottledHandler(map, "viewchangeend", function(arg) {
var curNW = map.getBounds().getNorthwest();
var curSE = map.getBounds().getSoutheast();
if (nw.latitude != curNW.latitude ||
nw.longitude != curNW.longitude ||
se.latitude != curSE.latitude ||
se.longitude != curSE.longitude)
{
nw = curNW;
se = curSE;
// Execute actual handler code here
}
}, 1000);
This code does not work as I had intended. As I'm debugging the code, if I go and hide the map's containing div (using jQuery), I notice that curNW and and curSE do not have the same coordinates as the ones I stored on the previous call. Why is this if I never dragged the map around or zoomed in or out? I am just hiding the map, why would the map's coordinates change? It makes no sense to me. Ideally, I wish Bing maps would provide developers with a zoomend or dragend event so I can say:
Microsoft.Maps.Map.addThrottledHandler(map, "zoomend", handler, 1000);
Microsoft.Maps.Map.addThrottledHandler(map, "dragend", handler, 1000);
Google Maps I believe provides this functionality. If anyone knows a better way to check for these events, please let me know. I don't want to add additional handlers to the elements of the page that may trigger a viewchangeend as there is way to much stuff going on in this page as it is. Appreciate the help.
The viewchangeend and viewchange are the best events for that, so no great alternative there.
Anyway, you have out-of-the-box support for throttled events. So, instead of:
Microsoft.Maps.Events.addHandler(map, 'viewchangeend', handler);
you can just use (the last argument uses milliseconds):
Microsoft.Maps.Events.addThrottledHandler(map, 'viewchangeend', handler, 500);
Reference: http://msdn.microsoft.com/en-us/library/gg427623.aspx

How to fire action when ANY Google Map Event Has Been Fired

I am implementing an app using Google Maps API 3. I would like to know what is the best implementation in dealing with this problem. I want to execute an action once ANY event in Google Map has been fired. Currently, what I am doing is that I call the function every time a specific event is called. I find this redundant and I have to make a listener for all of the events. So, is there a way to generalize this where I can do the following:
google.maps.addListener(everything_on_the_map_canvas, 'ANY_EVENT', function(event) {
foo();
})
function foo(){
//do something here
}
Thank you.
If the user decides to continue with map operations, they will have to move the mouse over the map to do anything. So you could simply trap a mousemove.
google.maps.event.addListener(map, 'mousemove', function(event) {foo();})
If the user can manipulate the map without moving the mouse over the map, then you will need to listen to other events.
bounds_changed will cover most eventualities with zoom and pan, heading and the like
maptype_id_changed
But that's only three instead of all of them. If the bounds don't change when the tilt does (and you can change tilt from outside the map) you may need to listen for tilt_changed as well.

GMarker onmouseout event fired too soon

Here's the situation:
On my Google Map, I'm trying to open an html info window whenever the user moves its mouse over a GMarker.
This window should be closed when the pointer is out of the marker.
GEvent.addListener(my_marker, "mouseover", function() {
MaCarte.openInfoWindowHtml(new GLatLng(my_marker.getLatLng().lat()+0.002, my_marker.getLatLng().lng()+0.001),"some text");
});
GEvent.addListener(my_marker, "mouseout", function() {
if((MaCarte.getInfoWindow().getPoint().lat() == my_marker.getLatLng().lat()+0.002)
&& (MaCarte.getInfoWindow().getPoint().lng() == my_marker.getLatLng().lng()+0.001))
MaCarte.closeInfoWindow();
});
What happens is that the onmouseout event is fired too soon, so the info window opens and closes right after it.
My guess is that the mouse no longer is over the marker but over the info window causing the onmouseout to be fired.
How can I do to let the info window open until my pointer is actually out of the marker?
I would use a timer and variable that dictates whether it's ok to close the window. Basically, have a timer start in the mouseover event and that timer changes a variable. The mouseout event then only closes the window if it's ok to close
like
GEvent.addListener(my_marker, "mouseover", function() {
timer.start()
MaCarte.openInfoWindowHtml(new GLatLng(my_marker.getLatLng().lat()+0.002, my_marker.getLatLng().lng()+0.001),"some text");
});
GEvent.addListener(my_marker, "mouseout", function() {
if (okToClose){
if((MaCarte.getInfoWindow().getPoint().lat() == my_marker.getLatLng().lat()+0.002)
&& (MaCarte.getInfoWindow().getPoint().lng() == my_marker.getLatLng().lng()+0.001))
MaCarte.closeInfoWindow();
}
});
This doesn't directly answer your question, but it will work as a workaround.
Hope it helps!
Chris
One thing that can happen is that opening a Google infowindow can cause the map to pan in order for the whole of the infowindow to be visible in the viewport and not obscured by any of the controls. The pan motion can cause the marker to move out from underneath the mouse, causing a mouseout. One way to deal with that effect is to use the undocumented {suppressMapPan:true} option on your infowindow. Another way to deal with it is to use a non-Google infowindow that doesn't pan the map.
Another thing that can happen is that you might have an incorrectly designed custom GIcon. If the .infoWIndowAnchor is too low, the infowindow itself could steal the mouseover, causing a mouseout on the marker. You can deal with that by setting the y coordinate of the .infoWindowAnchor more negative.
However, when you get it all working, you'll probably find that a map that opens the infowindow on marker mouseover is awkward to use. You get a better user interface, and one that some users will already be familiar with, by only displaying a small tooltip on mouseover, and only displaying the full infowindow when the marker is clicked.
It happens in other areas of JavaScript/HTML also.
Sometimes you have to bind a handler to an event but only after handled the current one because it gets called immediately...
So instead of
GEvent.addListener(...);
I do
setTimeout(function() { GEvent.addListener(...); }, 1);
To give the current thread the time to finish up handling the current event.
Hope this helps.

Categories

Resources