do this
// map is ready to roll
map.on('idle tilesloaded bounds_changed', function interactive(e) {
// remove the remaining events
map.off(interactive)
// do the stuff
})
instead of this
var fired,
// map is ready to roll
interactive = function interactive() {
// don't run this more than once
if (!fired) {
fired = true
// remove the remaining events
interactiveListeners.forEach(google.maps.event.removeListener)
// do the stuff
}
}
var addListener = google.maps.event.addListener,
interactiveListeners = [
addListener(map, 'idle', interactive),
addListener(map, 'tilesloaded', interactive),
addListener(map, 'bounds_changed', interactive)
]
Include the scripts
<script src="https://maps.googleapis.com/maps/api/js?v=3"></script>
<script src="bindy.js"></script>
Add some events
var poi = new google.maps.Marker({
position: {
lat: 45.517,
lng: -122.670
}
})
poi.on({
click: function() {
console.log('clickity click!')
},
'mouseover mouseout': function(e) {
console.log("you " + e.event + "'d me!")
}
})
Let me start by saying that the Google Maps JavaScript API is excellent. It is well documented, easy to work with, and for the most part consistent. This is not intended to be a fix, I do not believe they've done it wrong. Rather, it is an attempt to formalize my project-to-project efforts of creating a familiar event API for Google Maps.
Actual why
Each time I work with the Maps API I create an event wrapper for the
google.maps.event namespace. From scratch. For some reason
I am compelled to do this before getting any actual work done.
Whether or not that's a good thing is a topic for another day. Anyway,
it all started out very simple. Bind multiple events at once, one time
bindings, return a function to remove said events, etc. Over time it
started to look a bit like jQuery's unintelligent cousin so I figured hey,
why not match the jQuery event API sans selectors and data? It's familiar
to many and brilliantly concise. This is my best crack at doing just that.
The following API for any object or class that extends
google.maps.MVCObject. There are many, many classes in the
Maps API that extend MVCObject. 33 or more according to my ctrl+f of "extends MVCObject".
will come packed with .on(), .one(), and .off()
the EventObjectNot all events are created equal. That is, not all of them have an EventObject. For the sake of consistency we will always pass an EventObject as the first argument to a handler function.
// custom
EventObject {
event : event name,
timeStamp : event creation time in milliseconds
... props from native event, if any
}
// native
google.maps.MouseEvent {
latLng : the latlng where the event occurred,
stop() : prevents event from propagating further
}
Attaches the given handler function to one or more events
object.on(events, handler)
/**
* NOTE
* this object is used in all the demos so just know that
* when you see portland.*() elsewhere, it's a real thing
*/
var portland = new google.maps.Marker({
position: {
lat: 45.5177241,
lng: -122.6704882
}
})
portland.on('mouseover mouseout', function(e) {
if ('mouseover' == e.event) {
console.log("welcome to a whole lot of Awesome")
} else {
console.log('aww, sad to see you go')
}
})
or
portland.on({
'mouseover mouseout': function(e) {
if ('mouseover' == e.event) {
console.log("welcome to a whole lot of Awesome")
} else {
console.log('aww, sad to see you go')
}
}
})
Attaches the given handler function to one or more events. At most, the handler will be called once per event
object.one(events, handler)
/**
* THE HAPS
* user -> clickity click (2x)
* console -> you clicked on Portland! (1x)
*/
portland.one('click', function(e) {
console.log('you clicked on Portland!')
})
or
portland.one({
click: function(e) {
console.log('you clicked on Portland!')
}
})
Removes a previously bound handler function
object.off(events [, handler])
var handler
portland.on('click mouseover', handler = function(e) {
// do the things
})
// remove all events bound to `handler`
portland.off(handler)
// remove all click events
portland.off('click')
// remove all click events bound to `handler`
portland.off('click', handler)
// remove all mouseover events
portland.off('mouseover')
// remove all mouseover events bound to `handler`
portland.off('mouseover', handler)
// remove all click and mouseover events
portland.off('click mouseover')
// remove all click and mouseover events that are bound to `handler`
portland.off('click mouseover', handler)
▼
portland.one('click', function() {
// do stuff once
})
is equivalent to
portland.on('click', function(e) {
this.off(e)
// do stuff once
})
▲
portland.on('click position_changed', function handler(e) {
// do stuff once
/**
* remove the remaining events bound to `handler`
* making sure we only run this code one time
*/
this.off(handler)
/**
* `e.event` can be 'click' or 'position_changed'
* depending on which event happened first
*/
console.log(e.event)
})
object.off() explained
// remove all the things
portland.off()
Okay, I'll admit that this method doesn't look crazy special.. but it
does offer a single important advantage over the provided
clearInstanceListeners method.
// remove all the things
portland.off()
// remove ALL the things, actually
google.maps.event.clearInstanceListeners(portland)
While object.off() will remove all the events that have
been bound via object.on and object.one,
it will not remove events bound at the objects
instantiation.
When and why is this useful? Awesome question. As it stands, the
Maps API doesn't actually support full tear-down of an
google.maps.Map instance. If you're into recycling you may
use a single, or as few as necessary, map instances throughout the
lifetime of your single page app. While you may want to remove a map's
event listeners while it's not in use, you likely don't want to remove
the ones it came with. So how do you remove not too many, and not too few,
but just the right amount?
map.off()
And if you really do want to remove all the events?
map.off(true)
tada