I need to do some operations when the map is paned or zoomed, so I attached a callback to the event moveend.
map.on('moveend', function() {
// code stuff
});
It works fine, but when the page is load the event is fired three times and I don't know why.
Probably because during its creation the map is moved.
To avoid this i tried to wait the load event before subscribing moveend event, but nothing changed. So I tried to attach it within whenReady callaback, but again it is fired three times.
map.whenReady(function() {
map.on('moveend', function() {
// code stuff
});
});
Finally, I discovered that after the resize event it works quite fine: moveend is fired jonly one time. But I really believe there is a best way to fix the problem.
Another solution could be to attach my callback to both events zoomend and dragend, to cover paning and zooming cases.
But I didn't find a way to do it.
Thank you for your help.
The best solution I found is to attach the callback to both events:
map.on('zoomend', function() {
// callback
});
map.on('dragend', function() {
// callback
});
Although this way the code is a bit replicated, this is by far the best solution.
For others looking into this, research the options.debounceMoveend option on the invalidateSize function. It's mentioned in briefly in the documentation, but unfortunately it looks like it's only for that function, rather than generally for the moveend event.
[...] If options.debounceMoveend is true, it will delay moveend event so that it doesn't happen often even if the method is called many times in a row.
Reference to the line in source code (L3541)
You can use mouseenter and mouseleave events.
Example:
block.addEventListener('mouseenter', ()=>{
//some code when hover
})
block.addEventListener('mouseleave', ()=>{
// some code when leaving block
})
link to developer.mozilla.org
The 'zoomend' and 'dragend' option didn't work for me. I searched a lot for a suitable option and realized that the "moveend" event fires several times because this event is created every time you move the map. Therefore it is necessary to stop this event. I got out of the situation in this way.
Immediately after the map was initialized, I wrote:
map.off('moveend');
and for me it worked. Now it works fine.
I will be very happy if this is useful to someone.
Related
I'm trying to skip a delay event in JavaScript. Say( If I mouse wheel for 4 times continuously the first one ill get fired followed by the second, third and fourth one. In my scenario if the first event fired and when the second/third/fourth events are in delay or slow I want to speak the second and third event getting fired and must directly fire the final event (say fourth event))
Is this possible using JS/Jquery! And can anyone suggest me some sample for it if possible!
Sorry if I don’t make much sense, I am a beginner here.
It sounds like you're describing a debounce function, which will fire on the first event but merge subsequent events within a defined timeframe.
This plugin should achieve what you're looking for: http://benalman.com/projects/jquery-throttle-debounce-plugin/
This question and answer may also be helpful.
Based on the autocomplete example on this page, http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/, the final (4th in your example) event should be fired, but any intermediate events that happen in quick succession will be skipped.
Here is a Stackblitz example, debouncing a mousewheel event to every 0.1s. You simply need to include the plugin and then do:
$(document).ready(function () {
$('body').bind('mousewheel', $.debounce(100, debounceEvent));
function debounceEvent(event) {
// Process mouse wheel event here
console.log(event);
}
});
If you do a quick scroll, only the last event should be emitted.
OpenLayers3 API has a map.on("moveend") , however I cannot find a movestart. Any one know how I can achieve this? Is there a equivalent event?
OpenLayers 2 had a movestart event on map. I am looking an exact parallel in OpenLayers3
Here's a basic jsFiddle. If someone want's to play around. I did add a movestart event there, to show what I want, but it doesn't actually exist I think.
Use Case! one might ask: I have stops on maps that have nearly fullscreen infowindows. Users can switch to next marker from infowindow. I make the windows translucent to show the map panning underneath, so users get a context of where next location is. This work's great in OpenLayers2 with movestart and moveend events. But in the new OL3 version of the map, I can't get the movestart event.
Update: I did answer the question my self, but I am still offering bounty if anyone would like to propose a better solution.
UPDATE v4.2.0 now supports native movestart and moveend events
map.on('movestart', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
map.on('moveend', function(event) {
//To Remove after first use: ol.Observable.unByKey(event);
});
For OpenLayers 3 versions before release of v4.2.0
Okay so in the mean while without the movestart event, and with the moveend only triggering if there is a actual movement in map, here's how I was able to achive movestart and moveend behavior.
jsFiddle:
var pan = ol.animation.pan({
duration: 700,
source: this.map.getView().getCenter()
});
map.beforeRender(function(map, frameState) {
var stillPanning = pan(map, frameState); // returns false panning is done
if (stillPanning) {
// do movestart stuff here
if (!everDone) {
doSomething();
everDone = true;
}
} else {
// do move end stuff here
undoSomething();
everDone = false;
}
return stillPanning;
});
map.getView().setCenter(geom);
So why this works?
ol.animation.pan returns a ol.PreRenderFunction, which returns false if animation is not complete
Writing custom function and providing it to map.renderBefore can be now used to write a wrapper around pan animation as shown above
The whole business with everDone is because, stillPanning section will get called multiple times. This is okay if what you want to do there can take repeated calls, but if you want to toggle something then you want to do it only once.
behavior of 'moveend'
moveend callback is only triggered if map actually moves. This is fine, but it prevents us from doing pre-animation activities, by just doing them before animation done. If you had a scenario where map doesn't actually move, then what ever you did before animation will never undo because that behavior is in moveend which never gets called!
Hope this helps someone. I had to spend good two hours to get it to work for me, because a movestart callback is absent :(
UPDATE
Upon more discussion on this thread there is another solution as suggested by #ahocevar. That is to use the propertychange event on the view like so:
function onpropertychange() {
map.dispatchEvent('movestart');
var view = map.getView();
view.un('propertychange', onpropertychange);
map.on('moveend', function() {
view.on('propertychange', onpropertychange);
});
};
map.getView().on('propertychange', onpropertychange);
Here's a working example of this approach: jsFiddle
you could use the pointerdrag or pointermove event, but you will want to debounce them. I did so here with a variable called dragStarted
http://jsfiddle.net/sean9999/j2cP4/115/
http://openlayers.org/en/v3.8.2/apidoc/ol.MapBrowserEvent.html
I already checked answers about click events fired twice. But I have a question about using .on().
Usually, I use it like this on dynamically added elements, and it always worked fine:
$(document).on("click", "dynElement", function(){})
In the current website I'm working on, I use it several times. But in the function that I'm trying to achieve, let's say, a dynamic "jump to page", click on page number is triggered twice:
$(document).on("click", ".jumpTo .number", function(){
console.log("Jump");
});
Trying to find the origin of this behaviour, I tried this syntax that works fine:
$(".jumpTo").on("click", ".number", function(){
console.log("Jump");
});
Can anyone explain what is the difference between these two different syntaxes (which look quite similar to me)?
And optionally, why is $(document).on("click", ".jumpTo .number", function(){}) triggered twice? (Optionally because I am not able to reproduce this behaviour in a Fiddle, everything works as it is supposed to).
$(document).on("click", ".jumpTo .number", function(){
console.log("Jump");
});
In this case the click handler is set on the document object. So whenever you click somewhere on the page, it will fire and look for a ".jumpTo .number" element inside it. If it finds it, it will check if the click was on it and your function will execute.
$(".jumpTo").on("click", ".number", function(){
console.log("Jump");
});
Here the click handler will be on .jumpTo
As Al.G said probably this code gets executed multiple times, so you actually add that handler multiple times, hence the double firing.
One way to solve it is to do something like this:
$(".jumpTo").unbind("click").on("click"...
Another is to change your code to make sure the .on() call doesn't get executed twice.
This question is asked often, but never really answered well. Let's see if we can remedy it!
Event Propagation
Google allows you to bind to events in a Google Map View via their API using event handlers.
Sometimes you may bind your event handler to an event that Google itself is already bound to. Thus, when your event fires and does whatever you told it to do you may find Google also doing its own little thing at the same time.
Hmm, can I handle the event so my code runs, but stop the event from continuing on and firing Google's event handler?
You sure can! Welcome to Event Propagation (aka Event Bubbling).
Take a look at this code
Here I bind an event handler to double clicking on the Google Map:
var aListener = google.maps.event.addListener(map, 'dblclick', function(event) {
// Try to prevent event propagation to the map
event.stop();
event.cancelBubble = true;
if (event.stopPropagation) {
event.stopPropagation();
}
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
});
Here map is a Google Map object to bind to.
This doesn't work. The event bubbles, and the map zooms in. I don't know why.
You ask, have you read the documentation?
Indeed. The documentation says to use event.stop();
I have looked at what others are saying. This issue is exactly my problem. It was marked as fixed, but the solution does not work.
Ideas?
Workaround
A possible workaround for the doubleclick event is to disable Google's default behavior when you need it to not fire, and then re-enable it later.
You do this with the disableDoubleClickZoom argument. See the documentation here.
Here is some code to disable:
map.set("disableDoubleClickZoom", true);
Now to re-Enable:
map.set("disableDoubleClickZoom", false);
Of course, you can set the property in the MapOptions argument for when the map object is created in the first place.
UPDATE: Unfortunately, I had to find out that Firefox does not make the current event accessible via window.event thus this code won't work there. I haven't found a workaround for that, yet.
It turns out the fix to your code is minimal: just remove the event parameter from your event handler function, thus accessing the global window.event object inside the handler.
The following example code worked for me in IE and Chrome, but not Firefox:
google.maps.event.addListener(map, "dblclick", function(googleMapsEvent) {
console.debug("caught double click");
// reference the global event object
// ignore the googleMapsEvent passed in by Google Maps!
event.preventDefault();
});
This answer put me on the right track!
Not a solution but some ideas, as requested ...
event.stopPropagation() is not a generalised panacea for issues of this type for two reasons :
your event handler may be responding to an already bubbled event, ie it too late to prevent event handling (zoom in this case) which was triggered earlier in the bubbling cycle.
it may not be a bubbling issue but an event being handled by at the same level. .addListener() implements an observer pattern, meaning that new additions are added to the element's "concrete observer" queue without removing any handlers previously added by the same mechanism.
A workaround is almost undoubtedly available for both of these situations but would probably require "inside knowledge" of Google Maps to solve.
Maybe someone else knows more than I.
I am looking for the most proper and efficient way to bind Javascript events; particularly the onload event (I would like the event to occur after both the page AND all elements such as images are loaded). I know there are simple ways to do this in jQuery but I would like the more efficient raw Javascript method.
There are two different ways to do it. Only one will work; which one depends on the browser. Here's a utility method that uses both:
function bindEvent(element, type, handler) {
if(element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
element.attachEvent('on'+type, handler);
}
}
In your case:
bindEvent(window, 'load', function() {
// all elements such as images are loaded here
});
I know you did only ask about how to bind events. But Ooooo boy the fun doesn't end there. There's a lot more to getting this right cross-browser than just the initial binding.
#d.'s answer will suffice just fine for the specific case of the load event of window you're looking for. But it may give novice readers of your code a false sense of "getting it right". Just because you bound the event doesn't mean you took care to normalize it. You may be better of just fixing window.onload:
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
// now do your thing here
}
}(window.onload))
But for the general case of event binding #d.'s answer is so far from satisfying as to be frustrating. About the only thing it does right is use feature-detection as opposed to browser detection.
Not to go on too much of a rant here but JavaScript event binding is probably the #1 reason to go with a JavaScript library. I don't care which one it is, other people have fixed this problem over and over and over again. Here're the issues in a home-brewed implementation once inside your handler function:
How do I get a hold of the event object itself?
How do I prevent the default action of the event (eg clicking on a link but not navigating)
Why does this point to the window object all the time?
(Re mouse events) What are the x/y coords of the event?
Which mouse button triggered the event?
Was it a Ctrl-click or just a regular click?
What element was the event actually triggered on?
What element is the event going to? (ie relatedTarget, say for blur)
How do I cancel the bubbling of the event up through its parent DOM?
(Re event bubbling) what element is actually receiving the event now? (ie currentTarget)
Why can't I get the freaking char code from this keydown event?
Why is my page leaking memory when I add all these event handlers?? Argh!
Why can't I Unbind this anonymous function I bound earlier?
And the list goes on...
The only good reason to roll your own in these days is for learning. And that's fine. Still, read PPK's Intro to browser events. Look at the jQuery source. Look at the Prototype source. You won't regret it.
Something like that
window.onload = (function(onload) {
return function(event) {
onload && onload(event);
$(".loading-overlay .spinner").fadeOut(300),
$(".loading-overlay").fadeOut(300);
$("body").css({
overflow: "auto",
height: "auto",
position: "relative"
})
}
}(window.onload));
window.onload = function() {
// ...
};