I am using google 'idle' event listener to upload markers on map (with angularjs). My code is
$scope.LoadSearchPropertyOnPortFolioSales = function (PropertyName) {
if(PropertyName == ''){
google.maps.event.addListener(map, 'idle', function () {
var PropertyList = PropertyService.GetSearchPropertyForPortfolio($scope.PortfolioId, $scope.PropertyName, this.center.lat(), this.center.lng(), 0.005, 0.005);
PropertyList.then(function (pl) {
DrawPropertyOnPortFolioSale(pl.data, $scope.PropertyName);
},
});
}
else
{
//Stop event listener
}
}
I want the event listener only work when PropertyName that is passed have no value. But when PropertyName have some value in it i want to stop event listener. How do i stop event listener......
There's also a function which removes all of the listeners at the same time:
clearListeners(instance:Object, eventName:string);
//In your case:
google.maps.event.clearListeners(map, 'idle');
Here's the Google Maps API reference where you can read about it.
Related
The following code actually works, but I don't understand why. How come that when I pass the "event"-parameter to the function zaehle(), the function actually "knows" that it is supposed to react on what happens in the setup function?
I just can't see what connnects the zaehle() and the setup() function or how the parameter that I pass to zaehle() would be involved.
I hope I could make the question clear. If not I'll gladly try to explain it somehow else. It really bugs me and I feel like I can't go on studying until I get it.
<body>
<div id="eins">0</div>
<div id="zwei">0</div>
<div id="drei">0</div>
<div id="vier">0</div>
<div id="funf">0</div>
</body>
JS
var mouseoverZaehler = 0;
function zaehle(event) {
mouseoverZaehler++;
event.target.innerHTML = mouseoverZaehler;
}
function setup() {
document.getElementById("eins").addEventListener("mouseover", zaehle);
document.getElementById("zwei").addEventListener("mouseover", zaehle);
document.getElementById("drei").addEventListener("mouseover", zaehle);
document.getElementById("vier").addEventListener("mouseover", zaehle);
document.getElementById("funf").addEventListener("mouseover", zaehle);
}
window.addEventListener("load", setup);
Here is what happens step by step:
Page loads
setup function is called (because of window.addEventListener("load", setup))
Each element in setup function gets a mouseover event listener attached to it and when it fires zaehle function is called (because of document.getElementById("number").addEventListener("mouseover", zaehle))
You move your mouse over any of the elements
zaehle function gets called - mouseoverZaehler is incremented and innerHTML of the targeted element is set to the updated value of mouseoverZaehler
Check out addEventListener docs for further details.
The addEventListener calls in your setup function tell the browser that when a mouseover event occurs on the relevant element, it should call the function you're giving it (zaehle, in your case). It's the browser that passes the argument to zaehle, later, when calling it.
You could imagine addEventListener, conceptually, as putting that handler function on a list for the event on the element:
// VERY conceptual, leaves out a lot of details
function addEventListener(eventName, handler) {
this.events[eventName].handlers.push(handler);
}
...and then later, when the event occurs, the browser creates an event object and calls those handlers:
// Again, VERY conceptual, leaves out a lot of details
var event = /*...*/;
element.events[eventName].handlers.forEach(function(handler) {
handler.call(element, event);
});
Here's a working analogue of what's going on:
function FakeElement () {
this.events = Object.create(null);
}
FakeElement.prototype.addEventListener = function(eventName, handler) {
var eventEntry = this.events[eventName];
if (!eventEntry) {
eventEntry = this.events[eventName] = {
handlers: []
};
}
eventEntry.handlers.push(handler);
};
FakeElement.prototype.trigger = function(eventName) {
var event = {type: eventName}; // "Browser" creates the event
var eventEntry = this.events[eventName];
var handlers = eventEntry && eventEntry.handlers;
if (handlers) {
handlers.forEach(function(handler) {
handler.call(this, event); // "Browser" calls handler, passing
}); // the event into it
}
};
// Using it:
function zaehle(event) {
console.log("zaehle got event: " + event.type);
}
var e = new FakeElement();
e.addEventListener("mouseover", zaehle);
console.log("added handler for mouseover to element");
// Simulate the event occurring
var timer = setInterval(function() {
e.trigger("mouseover");
}, 500);
setTimeout(function() {
clearInterval(timer);
}, 3000);
You have registered your callback/function zaehle() for mouseover event. So when that event occurs for a specific div, browser calls the callback with event object which contains information about the event and the target i.e event occurred on which element.
I'm trying to use Leaflet to get the map coordinates of somewhere a user has right clicked. I've been going through the Leaflet API and so far I've figured out that I need to listen to the contextmenu event and use mouseEventToLatLng method to get the coordinates when clicked. However, when I go through and debug my code I'm not seeing an accessible latLng variable anywhere. Did I miss understand something in the API?
function setMarkers() {
document.getElementById("transitmap").addEventListener("contextmenu", function( event ) {
// Prevent the browser's context menu from appearing
event.preventDefault();
var coords = L.mouseEventToLatLng( event );
});
};
What you want to get is mousemove event. This is basically how you do it,
var lat, lng;
map.addEventListener('mousemove', function(ev) {
lat = ev.latlng.lat;
lng = ev.latlng.lng;
});
document.getElementById("transitmap").addEventListener("contextmenu", function (event) {
// Prevent the browser's context menu from appearing
event.preventDefault();
// Add marker
// L.marker([lat, lng], ....).addTo(map);
alert(lat + ' - ' + lng);
return false; // To disable default popup.
});
The coordinates of the right click event should be directly available as latlng property of the event argument of the "contextmenu" listener.
map.on("contextmenu", function (event) {
console.log("Coordinates: " + event.latlng.toString());
L.marker(event.latlng).addTo(map);
});
Demo: http://plnkr.co/edit/9vm81YsQxnkAFs35N8Jo?p=preview
Is there a way to retrieve the event object of the DOMContentLoaded event even if it has been triggered before setting an eventListener?
I have found some timing data for DOMContentLoaded and was hoping the event data might be stored as well.
window.performance.timing.domContentLoadedEventStart
window.performance.timing.domContentLoadedEventEnd
I would like to pass the event object to my callback whether it was called directly or as a result of the eventlistener.
var callback = function(event){
console.log(event)
}
if (document.readyState !== "loading") {
var event = window.DOMContentLoadedEvent; // doesnt exist
callback.call(this, event);
} else {
document.addEventListener('DOMContentLoaded', callback, false)
}
I suppose I could create a new object and return that but i would like my code a small as possible.
var event = {
srcElement: document,
target:document,
timeStamp:window.performance.timing.domContentLoadedEventEnd,
type:"DOMContentLoaded",
}
callback.call(this, event);
What I have done instead is to add another event listener which will definitely be registered before the DOMContentLoaded event if fired, the handler stores the event object;
document.addEventListener('DOMContentLoaded', function(event){
window.DOMContentLoadedEvent = event;
});
Edit
My code is a very simple domready function
document.addEventListener('DOMContentLoaded', function(event){
window.DOMContentLoadedEvent = event;
});
domready = function(callback) {
if (document.readyState === "loading") {
return document.addEventListener('DOMContentLoaded', callback, false);
}
return callback.call(this, window.DOMContentLoadedEvent);
}
The following code may reside in an external script an be inserted after the DOMContentLoaded event has fired.
domready(function(event){
console.log(event)
});
Am I able to retrieve the event data without setting up an additional event listener ?
check this:
<body>
...
<script>
var DOMevent; // global variable
document.addEventListener('DOMContentLoaded', function(event) {
DOMevent = event; // save it
}, false);
window.addEventListener('load', function () {
processEvent(DOMevent); // works fine
}, false);
function processEvent(e) {
console.log(e);
}
</script>
</body>
console.log(e); will show DOMevent.
more about readyState
https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
more about DOMContentLoaded event
https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
more about 'load' event
https://developer.mozilla.org/en-US/docs/Web/Events/load *typo in example! document -> window!
this is more of an academic question than a problem I'm having.
I've written a function that sets up a click handler for Google Analytics Event Tracking. It starts off by removing any previous click handlers using a namespace so as not to interfere with other click handlers that may be applied. Using jQuery's $.each() it basically loops through the IDs in an array, applies the category,action and label as data-* attributes and a class to reference it by later.
It then uses the data attributes set previously to set up a click handler which in turn fires the Google Analytics Event Tracking.
My question is basically, can I bind this function to an .unbind() or .off() method that targets all click handlers? (not namespaced ones)
So if someone else unbinds all click handlers on a button that the Event Tracking code is applied to, the setupEventHandlers() function is run automatically and reapplies the lost GA Event Tracking code?
UPDATED QUESTION
My original question might have been unclear in what I was trying to achieve.
So there is a button in the DOM.
<input type='submit' value='Confirm' id='confirm-btn'>
setupEventHandlers() runs as soon as the DOM is ready, and applies an event listener to #confirm-btn, which is listening for the click event. Another function randomFunction() decides that it needs to remove all event listeners from #confirm-btn, using the $.off() method. I would like to be able to allow randomFunction() to remove all event listeners, as it may not have a namespace (for reason x), and then run setupEventHandlers() again once $.off() has finished.
Consider that I can't directly edit the HTML because it's used across multiple sites, and the tracking code is for a single instance.
function setupEventHandlers(){
var buttonsToTrack = [['#buttonId','category','action', 'label'],
['#buttonId2','category2','action2', 'label2'],
['#buttonId3','category3','action3', 'label3']]
$('.js-buttonTracker').off('click.eventTrackingHandler');
$.each(buttonsToTrack, function(index, buttonDetails){
if(document.querySelector(buttonDetails[0]) != null){
$(buttonDetails[0]).attr('data-category', buttonDetails[1]);
$(buttonDetails[0]).attr('data-action', buttonDetails[2]);
$(buttonDetails[0]).attr('data-label', buttonDetails[3]);
$(buttonDetails[0]).addClass('js-buttonTracker');
}
});
$('.js-buttonTracker').on('click.eventTrackingHandler', function(){
_gaq.push(['_trackEvent', $(this).attr('data-category'), $(this).attr('data-action'), $(this).attr('data-label')]);
console.log($(this).attr('data-category') + ' ' + $(this).attr('data-action'), $(this).attr('data-label'));
});
}
// Save the original .off in a variable
(function(jq_off, recursive) {
// Redefine .off
$.fn.off = function(event, selector, function) {
var save_recursive = recursive;
recursive = true;
// Then call the original $.off
var return_val = jq_off.apply(this, arguments);
recursive = save_recursive;
if (!recursive) {
setupEventHandlers();
}
return return_val;
};
)($.fn.off, false);
You don't need to redefine .unbind, as it just calls .off.
My advise to you is to use data- tag elements and assign them a click listener independent of other functionalities, for example :
HTML:
<a href="http://google.com" data-toggle="GAPush" data-event="_trackEvent" data-action="google"....>..</a>
JS:
$('[data-toggle="GAPush"]').click(function() {
var eventname = $(this).data('name');
var action = $(this).data('action');
//do your login here and read other parameters from data-eventname m use _gapush()
});
you know you can have unlimited data- tag elements if you need them for other functionalities.
Try (this pattern)
Edit
v2
It should be possible to utilize setInterval to "poll" for the event namespace being attached, or not , to the target elements
window.setInterval(function() {
$._data($(".js-buttonTracker")[0], "events") != undefined &&
$._data($(".js-buttonTracker")[0], "events")
.click[0].namespace === "eventTrackingHandler"
? $.noop()
: setupEventHandlers()
}, 1000);
v1
html
<button>1</button>
<button>2</button>
<button>3</button>
<!-- `data`, `em` for `event` status notifications at jsfiddle -->
<data></data>
<em></em>
js
$(function () {
setupEventHandlers = function (status) {
if (status === "on") {
// if `events` `off`, turn back `on` by calling `_events()`
_events();
$(window).trigger("mousemove.check");
} else {
$("data").html(status);
};
};
eventsCheck = function () {
$(window).on("mousemove.check", function (e) {
// `target` `elements` ;
// e.g., `.js-buttonTracker`
var el = $("button");
// if `events` attached to `el`,
// check `events` `namespace`
if ($._data($(el)[0], "events") != undefined) {
$.each($._data($(el)[0], "events").click, function (k, v) {
if (v.namespace != "eventTrackingHandler") {
setupEventHandlers("attach `eventTrackingHandler`"
+ " namespace called");
} else {
$("data").html("`eventTrackingHandler`"
+ " namespace attached");
};
});
} else {
// if `events` _not_ attached to `el`,
// turn `events` back `on` by calling
// `setupEventHandlers("on")`
$("data").html("`eventTrackingHandler` namespace `off` ,"
+ " turning back `on` in 1 seconds");
// `setTimeout()` utilized here to show operability,
// alternatively, could call `setupEventHandlers("on")`
// without delay
setTimeout(function () {
setupEventHandlers("on");
}, 1000);
};
});
};
eventsCheck();
_events = function () {
$("button").on("click.eventTrackingHandler", function (e) {
$("em").html("clicked at " + $.now());
// turn `off` `events` for `button`'s when `button`
// having `text` of `"3"` `click`ed,
// simulating the condition
// "if someone else unbinds all click handlers on a button
// that the Event Tracking code is applied to" ,
// "observed" by `eventsCheck()` ,
// which calls `_events()` within `setTimeout()`
if ($(e.target).text() === "3") {
$("button").off("click.eventTrackingHandler");
};
});
};
_events();
});
jsfiddle http://jsfiddle.net/guest271314/z5u97/
With the Google Maps JS API v3, I want to drop a marker where the user clicks on the map, while keeping the default behavior when the user double clicks (and not adding any marker on the map).
I thought about defining a timeout on click event. If a double click event is triggered within the next few milliseconds, the timeout is cancelled. If not, the marker is placed on the map when the timeout expires.
But it doesn't really look like the best solution ever.
Is there a more elegant way to handle this?
Thanks.
I just found an hackish solution which works but introduce a small waiting time (200ms, this is the minimum to make it work, but I don't know if it is client dependent)
var update_timeout = null;
google.maps.event.addListener(map, 'click', function(event){
update_timeout = setTimeout(function(){
do_something_here();
}, 200);
});
google.maps.event.addListener(map, 'dblclick', function(event) {
clearTimeout(update_timeout);
});
Hope this helps!
The easiest way to solve it.
var location;
var map = ...
google.maps.event.addListener(map, 'click', function(event) {
mapZoom = map.getZoom();
startLocation = event.latLng;
setTimeout(placeMarker, 600);
});
function placeMarker() {
if(mapZoom == map.getZoom()){
new google.maps.Marker({position: location, map: map});
}
}
shogunpanda's solution is better (see below)
You can take advantage of, dblclick fires if it is a double click, and single click fires in such occations only once.
runIfNotDblClick = function(fun){
if(singleClick){
whateverurfunctionis();
}
};
clearSingleClick = function(fun){
singleClick = false;
};
singleClick = false;
google.maps.event.addListener(map, 'click', function(event) {// duh! :-( google map zoom on double click!
singleClick = true;
setTimeout("runIfNotDblClick()", 500);
});
google.maps.event.addListener(map, 'dblclick', function(event) {// duh! :-( google map zoom on double click!
clearSingleClick();
});
See http://www.ilikeplaces.com
If you're using underscore.js or* lodash here's a quick and elegant way to solve this problem
// callback is wrapped into a debounce function that is called after
// 400 ms are passed, it provides a cancel function that can be used
// to stop it before it's actually executed
var clickHandler = _.debounce(function(evt) {
// code called on single click only, delayed by 400ms
// adjust delay as needed.
console.debug('Map clicked with', evt);
}, 400);
// configure event listeners for map
google.maps.event.addListener(map, 'click', clickHandler);
google.maps.event.addListener(map, 'dblclick', clickHandler.cancel);
* Debounce.cancel is implemented only in lodash (with this commit), underscore.js does not implement it
A cleaner way to implement the setTimeout() approach is to trigger custom events for single clicks.
The following function takes any Google Maps interface object (e.g. map, marker, polygon etc.) and sets up two custom events:
singleclick: called 400ms after a click if no other clicks have occured
firstclick: called whenever a click event occurs, unless a click has already occured in the last 400ms (this is handy for showing some kind of immediate click feedback to the user)
function addSingleClickEvents(target) {
var delay = 400;
var clickTimer;
var lastClickTime = 0;
google.maps.event.addListener(target, 'click', handleClick);
google.maps.event.addListener(target, 'dblclick', handleDoubleClick);
function handleClick(e) {
var clickTime = +new Date();
var timeSinceLastClick = clickTime - lastClickTime;
if(timeSinceLastClick > delay) {
google.maps.event.trigger(target, 'firstclick', e);
clickTimer = setTimeout(function() {
google.maps.event.trigger(target, 'singleclick', e);
}, delay);
} else {
clearTimeout(clickTimer);
}
lastClickTime = clickTime;
}
function handleDoubleClick(e) {
clearTimeout(clickTimer);
lastClickTime = +new Date();
}
}
You can use it like so:
var map = ....
addSingleClickEvents(map);
google.maps.event.addListener(map, 'singleclick', function(event) {
console.log("Single click detected at: " + event.latLng);
}
I'm not sure, but if you add event handlers to both 'click' & 'dblclick' events, where you say to put marker on a click, and don't take any action on double click, then you can skip the adding of timeouts (the maps API can differ what is click and what is double click)
google.maps.event.addListener(map, 'click', function (event) {
placeMarker(event.latLng);
});
google.maps.event.addListener(map, 'dblclick', function(event) {
//DO NOTHING, BECAUSE IT IS DOUBLE CLICK
});
The placeMarker(latLng) is custom added function which adds marker on the given location:
var marker = new google.maps.Marker({
position: location,
draggable: true,
map: map
});
map.setCenter(location);