I think I'm missing something basic about javascript. I could use your help!
Scenario: After clicking "search" the google map loads. As it loads, a checkbox appears that says, "Redo results when map is moved" (similar to yelp). If the map is dragged or zoomed, the results update. That part works fine.
Problem: The value of the checkbox (true or false) is recognized by the redo() function when the map loads, and it doesn't check it again after that (because even though redo() is updating the results it's not reloading the map). So if you toggle the checkbox, the "redo" function doesn't notice!
Simplified Code:
//HAML
%input{:type => "checkbox", :id => "followCheck", :name => "followCheck", :onclick => 'handleClick();'}
//load the map
function showLocations() {
map = new google.maps.Map(document.getElementById("map_canvas"), {
bunch of map stuff;
map.fitBounds(bounds);
redo(); //this is the redo function, see below
}
}
function handleClick(checkbox) {
var chk = document.getElementById("followCheck").checked;
console.log("checkbox value is currently " + chk);
return chk;
}
function redo() {
var chk = handleClick();
console.log("inside redo, value is " + chk);
if (chk == true) {
google.maps.event.addListener(map, 'mouseup', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
}
}
(note, that redo() function was written by my collaborator, who is smarter than me and currently out of town.)
So, Redo() loads when the map loads. If the checkbox is true when the map loads, then the redo() function processes zoom/drag. But it still does it even after changing the value to false (and if the value is false when the page loads, the opposite happens).
What is needed to get redo() to see that the value has changed? push()? bind()? get/set? change()? addEventListener()? Should I reload the map when the checkbox is toggled (I'd rather not)? A whole new approach? I'm stuck! Thanks. I'm open to JQuery ideas, too.
I solved this. First off, with a small adjustment to the function I was able get it to turn ON after checking the box (even if the results loaded while the box was unchecked via a previous page session). Here is that code (I actually found like three variations that all had the same result):
function redo() {
if ($("#followCheck").is(':checked')) {
google.maps.event.addListener(map, 'mouseup', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
mapAdjusted = 1;
updateMapAfterUserInteraction();
});
}
}
The problem was that it didn't turn off. And that's because I've added two listeners! This answer helped me remove the listeners. Note that (for me) it only worked when the removeListener() function is within the addListener() function. Kind of strange.
function redo() {
var listener1 = google.maps.event.addListener(map, 'mouseup', function() {
if (!$("#followCheck").is(':checked')) {
google.maps.event.removeListener(listener1);
} else {
mapAdjusted = 1;
updateMapAfterUserInteraction();
}
});
var listener2 = google.maps.event.addListener(map, 'zoom_changed', function() {
if (!$("#followCheck").is(':checked')) {
google.maps.event.removeListener(listener2);
} else {
mapAdjusted = 1;
updateMapAfterUserInteraction();
}
});
}
Oh and I also changed onclick() to onchange()
Related
After page load function "intro" is launch. This function displays two buttons, after click on one of them another function "startGame" starts with parameter "innerHTML" from click button.
Then the right panel appears and "START" button counts time from 10. Countdown stops when user click on map or time reaches 0.
After "START" user can click on map (to add a marker) only ones, and click "START" again to add another marker.
When users click on map 4 times "game" finishes and two buttons appear again.
And that is when a problem starts. When function "startGame" starts again and user clicks "START" button, countdown doubles (you can see it in console).
If user clicks on map one countdown stops but second still counts to zero.
Can anyone tell me why time is doubled?
Link to live version: http://najlepszekomisy.co.pl/
Thank you.
var elem =
{
map: document.getElementById("mapa"),
panel: document.getElementById("right_panel"),
games: document.getElementById("games"),
draw: document.getElementById("draw"),
points: document.getElementById("pointsGet"),
timer: document.getElementById("timer")
};
(function intro()
{
elem.games.addEventListener("click",function(e){
if(e.target.tagName==="H4")
{
TweenMax.to(elem.games,1,{ease: Back.easeOut,top: -50,onComplete:function(){
startGame(e.target.innerHTML);}})
}
},false)
})();
function startGame(hit)
{
var gameElement =
{
mapa:0,
clickListener:0,
number:0,
usingSet:4,
timeNum:10
};
(function loadMap()
{
var mapOptions =
{
zoom: 7,
disableDefaultUI: true,
zoomControl: true,
center: new google.maps.LatLng(51.95442214470796, 19.14093017578125)
};
gameElement.mapa = new google.maps.Map(elem.map, mapOptions);
google.maps.event.addListener(gameElement.mapa,'idle',function()
{
TweenMax.to("#right_panel",2,{ease: Back.easeIn,right: 0,onComplete:function(){
TweenMax.set(".anim_from_bottom", { display: 'block' });
TweenMax.staggerFrom(".anim_from_bottom",0.5,{y:1600},0.2);
google.maps.event.clearListeners(gameElement.mapa, 'idle');
}});
});
})();
elem.draw.addEventListener("click",function(){
if(gameElement.number<gameElement.usingSet)
{
gameElement.number++;
timer.time=gameElement.timeNum;
timer.calcTime();
gameElement.clickListener = google.maps.event.addListener(gameElement.mapa, "click", function (e) {
addMarker(e.latLng.lng(), e.latLng.lat());
});
elem.draw.disabled=true;
}else{result()}},false);
function addMarker(lng,lat)
{
timer.stopTime();
var opcjeMarkera =
{
position: new google.maps.LatLng(lat,lng),
map: gameElement.mapa,
title: hit
};
var marker = new google.maps.Marker(opcjeMarkera);
google.maps.event.removeListener(gameElement.clickListener);
elem.draw.disabled=false;
}
var timer =
{
time: 0,
startTime:0,
calcTime:function()
{
elem.timer.className = "elem";
elem.timer.innerHTML = timer.time;
console.log(timer.time);
if(timer.time===0){elem.timer.className = " ";clearTimeout(timer.startTime);}
else
{
--timer.time;
timer.startTime = setTimeout(timer.calcTime, 1000);
}
},
stopTime:function()
{
clearTimeout(timer.startTime);
elem.timer.className = " ";
this.time=gameElement.timeNum;
}
};
function result ()
{
console.log("koniec");
gameElement.number=0;
TweenMax.to("#right_panel",2,{ease: Back.easeIn,right: -300});
TweenMax.to(elem.games,1,{ease: Back.easeOut,top: 50})
}
}
Every time your H4 button is clicked it calls startGame function. Every time startGame function is called it adds one more event listener to the Start button, so start handler is called once when you play first time, twice the second time, thrice next time etc.
To avoid this you can:
add event listener in init function (which is called once)
remove event listeners before adding new one (see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
use onclick property instead of .addEventListener() method if there is only one handler needed for this button
You need to make sure that the calcTime function which includes this code:
timer.startTime = setTimeout(timer.calcTime, 1000);
cannot be invoked while it is already running, because if it does, then another call to calcTime will be queued up causing the clock to tick twice as fast.
Applying a filter to an image object that occupies the whole canvas can take a couple seconds with larger images and I want to display a loading screen while this is happening but the .show() method isn't firing until after the filter has been applied.
Current method that applies the filter:
applyFilter: function (index, filter) {
var obj = designer.baseImage,
$loading = $('#loading-canvas');
console.log('show loading');
$loading.show();
obj.filters[index] = filter;
obj.applyFilters(function () {
console.log('hide loading');
$loading.hide();
designer.canvas.renderAll();
});
}
When the method is called (on click) 'show loading' is logged to the console immediately. The loading div is not displayed until the filter has been applied, at which point the loading screen flashes up, goes away, and 'hide loading' is logged to the console. Any ideas why the loading screen isn't displayed when 'show loading' is logged to the console?
Edit: The loading screen's sole purpose so to indicate to the user that something is happening
I think #A wolff is right, you may use
$(".btnDoStuff").click(function () {
$(".loading").show(function(){
//dosomestuff
//remove for Loop, it is just to demonstrate that some code is taking time
for (var i = 0; i < 1000000000; i++) {
var j = i + 1;
}
$(".loading").hide();
});
});
however you may also use window.setTimeout
#A.Wolff was super close but it appeared to be a timing issue? Here's my solution:
applyFilter: function (index, filter) {
var obj = designer.baseImage,
$loading = $('#loading-canvas');
$loading.show('slow', function () {
obj.filters[index] = filter;
obj.applyFilters(function () {
$loading.hide();
designer.canvas.renderAll();
});
});
}
The original solution didn't work with show()'s first argument being 0, worked better with 'fast' and works perfectly with 'slow'.
I asked this question yesterday hopefully this one is clearer as I've now provided a working example of my store.
I'm developing a Shopify Theme. I've been using Timber as my base and I'm currently having a problem with my Quick Cart and Quick Shop/View drawers.
I have 2 drawers on the right of my site, 1 for the cart and 1 for the product quick view option. The drawers currently slide open - #PageContainer moves to the left on click to reveal each drawer.
As they are currently sitting on top of each other I need to alter the JS so that on click the z-index changes so that the correct drawer being called is highest in the stack.
I'm not great with JS so not sure if this is a simple task?
Here is a link to my Dev Store
JS:
timber.Drawers = (function () {
var Drawer = function (id, position, options) {
var defaults = {
close: '.js-drawer-close',
open: '.js-drawer-open-' + position,
openClass: 'js-drawer-open',
dirOpenClass: 'js-drawer-open-' + position
};
this.$nodes = {
parent: $('body, html'),
page: $('#PageContainer'),
moved: $('.is-moved-by-drawer')
};
this.config = $.extend(defaults, options);
this.position = position;
this.$drawer = $('#' + id);
if (!this.$drawer.length) {
return false;
}
this.drawerIsOpen = false;
this.init();
};
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
Drawer.prototype.open = function (evt) {
// Keep track if drawer was opened from a click, or called by another function
var externalCall = false;
// Prevent following href if link is clicked
if (evt) {
evt.preventDefault();
} else {
externalCall = true;
}
// Without this, the drawer opens, the click event bubbles up to $nodes.page
// which closes the drawer.
if (evt && evt.stopPropagation) {
evt.stopPropagation();
// save the source of the click, we'll focus to this on close
this.$activeSource = $(evt.currentTarget);
}
if (this.drawerIsOpen && !externalCall) {
return this.close();
}
// Add is-transitioning class to moved elements on open so drawer can have
// transition for close animation
this.$nodes.moved.addClass('is-transitioning');
this.$drawer.prepareTransition();
this.$nodes.parent.addClass(this.config.openClass + ' ' + this.config.dirOpenClass);
this.drawerIsOpen = true;
// Run function when draw opens if set
if (this.config.onDrawerOpen && typeof(this.config.onDrawerOpen) == 'function') {
if (!externalCall) {
this.config.onDrawerOpen();
}
}
if (this.$activeSource && this.$activeSource.attr('aria-expanded')) {
this.$activeSource.attr('aria-expanded', 'true');
}
// Lock scrolling on mobile
this.$nodes.page.on('touchmove.drawer', function () {
return false;
});
this.$nodes.page.on('click.drawer', $.proxy(function () {
this.close();
return false;
}, this));
};
Drawer.prototype.close = function () {
if (!this.drawerIsOpen) { // don't close a closed drawer
return;
}
// deselect any focused form elements
$(document.activeElement).trigger('blur');
// Ensure closing transition is applied to moved elements, like the nav
this.$nodes.moved.prepareTransition({ disableExisting: true });
this.$drawer.prepareTransition({ disableExisting: true });
this.$nodes.parent.removeClass(this.config.dirOpenClass + ' ' + this.config.openClass);
this.drawerIsOpen = false;
this.$nodes.page.off('.drawer');
};
return Drawer;
})();
Update
As instructed by Ciprian I have placed the following in my JS which is making the #CartDrawer have a higher z-index. I'm now unsure how I adapt this so that it knows which one to have higher dependant on which button is clicked. This is what I've tried:
...
Drawer.prototype.init = function () {
$(this.config.open).on('click', $.proxy(this.open, this));
$('.js-drawer-open-right-two').click(function(){
$(this).data('clicked', true);
});
if($('.js-drawer-open-right-two').data('clicked')) {
//clicked element, do-some-stuff
$('#QuickShopDrawer').css('z-index', '999');
} else {
//run function 2
$('#CartDrawer').css('z-index', '999');
}
this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this));
};
...
The approach would be like this:
$('.yourselector').css('z-index', '999');
Add it (and adapt it to your needs) inside your onclick() function.
if you need to modify the z-index of your div when clicking a buton, you shoud put in this code on your onclick() function, else if you need to activate it when you looding the page you shoud put it on a $( document ).ready() function , the code is :
$('#yourID').css('z-index', '10');
You can use:
document.getElementById("your-element-id").style.zIndex = 5;
It's pure Javascript and sets the z-index to 5. Just bind this to onClick event!
I have a question concerning clicks on a map in leaflet. If I click on the map I want to set a marker there, but if doubleclick on the map I just want to zoom in without setting a marker. So I have the follwing code:
var map = L.map(attrs.id, {
center: [scope.lat, scope.lng],
zoom: 14
});
var marker = L.marker([scope.lat, scope.lng],{draggable: true});
map.on('click', function(event){
marker.setLatLng(event.latlng);
marker.addTo(map);
});
The problem now is, when I doublclick on the map the click event is also fired and I would like to remove that behavior. How can I achieve that?
Thanks
Magda
So, I found a way to do that, I am still not sure, if there is a better way to do it.
var map = L.map(attrs.id, {
center: [scope.lat, scope.lng],
zoom: 14
});
map.clicked = 0;
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
var marker = L.marker([scope.lat, scope.lng],{draggable: true});
map.on('click', function(event){
map.clicked = map.clicked + 1;
setTimeout(function(){
if(map.clicked == 1){
marker.setLatLng(event.latlng);
marker.addTo(map);
map.clicked = 0;
}
}, 300);
});
map.on('dblclick', function(event){
map.clicked = 0;
map.zoomIn();
});
Had the same problem of 2 x unwanted click events firing when listening for dblclick.
Note: I wanted single and double clicks on the same element to perform different actions.
I adapted this approach to my leaflet map, which is not bullet proof but eliminates 99% of the conflicts:
var timer = 0;
var delay = 200;
var prevent = false;
$("#target")
.on("click", function() {
timer = setTimeout(function() {
if (!prevent) {
doClickAction();
}
prevent = false;
}, delay);
})
.on("dblclick", function() {
clearTimeout(timer);
prevent = true;
doDoubleClickAction();
});
Credit: CSS-Tricks
Code Pen Example
This is still an issue on recent (leaflet 1.4) versions.
Alternative approach I used that:
legit usage of setTimeout and clearTimeout
without adding random props to the map object
no jQuery:
map.on('click', function(event) {
if (_dblClickTimer !== null) {
return;
}
_dblClickTimer = setTimeout(() => {
// real 'click' event handler here
_dblClickTimer = null;
}, 200);
})
.on("dblclick", function() {
clearTimeout(_dblClickTimer);
_dblClickTimer = null;
// real 'dblclick' handler here (if any). Do not add anything to just have the default zoom behavior
});
Note that the 200 ms delay must be tested. On my environment using a value like 100 was not enough as the doubleclick event is triggered with a delay.
i have some troubles using google map api v2. What i want is to be able to go with the mouse over the tooltip.
Below is the code that i have. In firefox works but i have to move the mouse very gently over the tooltip, if i go to fast it will hide (solved : added a delay on the mouseout function, and there check if I trigger the tooltip or not) and the worst part it doesn't wark at all in IE. If i go over the pin point i will be able to see the tooltip, but i can't go with the mouse over it.
function createHotelSearchMarker(point, number, message) {
var newIcon = new GIcon(G_DEFAULT_ICON);
wIcon.image = imageChart + "chst=d_map_spin&chld=0.5|0|CCCCCC|10|_|" + number + "&ext=.png";
var marker = new GMarker(point, {icon: newIcon, clickable:true, zIndexProcess:importanceOrder});
marker.importance = 1;
/*add listener for mouseover*/
GEvent.addListener(marker, "mouseover", function() {
marker.openExtInfoWindow(
map,
"mapInfoWindow",
"<div id='tooltip'>" + message + "</div>",
{beakOffset: 3}
);
marker.setImage(imageChart + "chst=d_map_spin&chld=0.5|0|FF0000|10|_|" + number + "&ext=.png");
marker.importance = 2;
marker.setLatLng(marker.getLatLng());
/*i added a dom listener for the tooltip*/
if(document.getElementById("tooltip") != null) {
GEvent.addDomListener(document.getElementById("tooltip"), "mouseover", function(){
isFiredByDivListener = 1;
GEvent.trigger(marker,"mouseover");
});
GEvent.addDomListener(document.getElementById("tooltip"), "mouseout", function(){
isFiredByDivListener = 0;
GEvent.trigger(marker,"mouseout");
});
}
});
/*add a listener for mouse out*/
GEvent.addListener(marker, "mouseout", function() {
map.closeExtInfoWindow();
marker.setImage(imageChart + "chst=d_map_spin&chld=0.5|0|CCCCCC|10|_|" + number + "&ext=.png");
marker.importance = 1;
marker.setLatLng(marker.getLatLng());
});
return marker;
}
the marker will be return and
if (marker != null){
map.addOverlay(marker);
}
Thank you for your help and time
It seems to me you've got this going on, i.e. a potential problem where you could be recursively triggering the marker mouseover. I'd suggest moving the two addDomListener event listeners outside of the marker's event listener.
GEvent.addListener(marker, "mouseover", function() {
...
GEvent.addDomListener(document.getElementById("tooltip"), "mouseover", function(){
GEvent.trigger(marker,"mouseover");
});