Problem with animations and a lot of stop() calls in jQuery - javascript

here is what I'm trying to do:
I have a few div's with prices in them, and a slider with fixed minimum where I can set the maximum price. With that I can filter the divs, so only the div's with prices in the slider range will be displayed.
Without animation it would be no problem, just hide() and show(), but I'm trying to do it smooth.
vehicles[0] = { id: 1, price: 100 };
vehicles[1] = { id: 2, price: 250 };
vehicles[2] = { id: 3, price: 700 };
vehicles[3] = { id: 4, price: 300 };
...
slide: function(event, ui) {
for (i = 0; i < vehicles.length; i++) {
if (vehicles[i].price > ui.value && $('#vehicle'+vehicles[i].id).data('visible') == true) {
$('#vehicle'+vehicles[i].id).data('visible',false).stop(true).hide('blind',500);
}
if (vehicles[i].price <= ui.value && $('#vehicle'+vehicles[i].id).data('visible') == false) {
$('#vehicle'+vehicles[i].id).data('visible',true).stop(true).show('blind',500);
}
}
}
...
<div id="vehicle1">100€</div>
<div id="vehicle1">250€</div>
<div id="vehicle1">700€</div>
<div id="vehicle1">300€</div>
That's my code and here is my problem: When pushing the slider to one side or point, it works fine, but f.e. pushing it to 0€ and immediately back to 700€ (while the hide() animation is still running), all divs are hidden (but their data('visible') is set to true). You can see my running code here: http://work4.bywulf.de/index.php?page=Vehicles Just slide the slider fast to the left and back to the right.
It looks like the stop() method is not correctly stopping their current "hide" animation, and the "show" animation is not playing.
Now what am I doing wrong or is there another way to hide elements animated, but stop them half way and show them again completely?
I hope you know what I mean and what I'm trying to do, thank you for your help.
(jQuery 1.5, jQueryUI 1.8.9)
--Wulf

Problem solved, just did an own .animation(). I think the problem was, that show() and hide() see the item as it is, and when the item is shown only 50%, it struggles. .animation() will start at the 50% and end at the given dimentions. So what I did in detail was:
First i initialized the container, so the height is saved:
$(selector).data('visible',true)
.data('initialHeight',$(selector).height())
.data('initialOuterHeight',$(selector).outerHeight())
.data('initialMarginBottom',$(selector).css('marginBottom'));
Then, when animation was needed, this part is executed:
function startAnimation(selector, show, duration) {
$(selector).data('visible',show).stop(true);
if (show) {
$(selector).animate({
height: $(selector).data('initialHeight'),
opacity: 1 ,
marginTop: 0,
marginBottom: $(selector).data('initialMarginBottom')
}, duration);
} else {
$(selector).animate({
height: 0,
opacity: 0 ,
marginTop: $(selector).data('initialHeight') - $(selector).data('initialOuterHeight'),
marginBottom: 0
}, duration);
}
}
Thanks for your advice anyway.

I would propose:
if ((vehicles[i].price > priceRange || search == false || (category > 0 && vehicles[i].category != category) || ($('#availability').is(':checked') && vehicles[i].availability != 0)) && $('#vehicle'+vehicles[i].id).data('visible') == true) {
$('#vehicle'+vehicles[i].id).data('visible',false).stop(true, true).fadeOut(function(){$(this).hide()});
}
if (vehicles[i].price <= priceRange && search == true && (category == 0 || vehicles[i].category == category) && (!$('#availability').is(':checked') || vehicles[i].availability == 0) && $('#vehicle'+vehicles[i].id).data('visible') == false) {
$('#vehicle'+vehicles[i].id).data('visible',true).stop(true, true).show().fadeIn();
}
Or maybe slideUp/slideDown for a different effect:
if ((vehicles[i].price > priceRange || search == false || (category > 0 && vehicles[i].category != category) || ($('#availability').is(':checked') && vehicles[i].availability != 0)) && $('#vehicle'+vehicles[i].id).data('visible') == true) {
$('#vehicle'+vehicles[i].id).data('visible',false).stop(true, true).slideUp();
}
if (vehicles[i].price <= priceRange && search == true && (category == 0 || vehicles[i].category == category) && (!$('#availability').is(':checked') || vehicles[i].availability == 0) && $('#vehicle'+vehicles[i].id).data('visible') == false) {
$('#vehicle'+vehicles[i].id).data('visible',true).stop(true, true).slideDown();
}
show() and hide() seems buggy indeed

You might be experiencing a re-entrancy problem.
The code is already running when another event triggers it to run again.
You could try something like this:
function Sample(event, ui)
{
var running; // prevent re-entrant code
if (running == true)
return;
else
running = true;
// a bunch of code
running = false;
}
You would still need a way to make sure the view is synced to the slider position when the user finally lets go of the slider.

Related

After preventing the default scroll event, scrollIntoView(); doesn't work Properly on Firefox desktop?

In my scenario the code should prevent the the default scroll event and use scrollIntoView() to move the user to a specific section according to the scroll direction.
https://stackoverflow.com/a/4770179/9164633 I used this method to prevent the scroll default event.
And I detect the direction like so,
preventDefault(e) {
e.preventDefault();
if(this.waiting == false && this.forceInitialScroll != true) {
if(e.deltaY && e.deltaY > 7) {
this.scrollDirection = 'down';
this.checkScroll()
}else if(e.deltaY && e.deltaY < -7) {
this.scrollDirection = 'up';
this.checkScroll()
}else {
}
}
},
and Im pretty sure that both are working fine.
After preventing the scroll and detecting the scroll direction I try to scroll the user to the section like so,
checkScroll() {
let element;
if(this.scrollDirection == 'down' && this.scrollIndex != 4 ) {
element = document.getElementById(`section-${this.scrollIndex+1}`);
}else if(this.scrollDirection == 'up' && this.scrollIndex != 0) {
element = document.getElementById(`section-${this.scrollIndex-1}`);
}
this.waiting = true;
if(element) {
console.log(element)
element.scrollIntoView({
behavior: 'smooth',
block: 'center',
})
}
setTimeout(() => {
if(this.waiting == true) {
this.waiting = false;
this.scrollDirection = null;
}
}, 450)
},
this.waiting is used to prevent the user form scrolling more than one section at a time.
On firefox the browser doesnt scroll the user properly though its working fine on chrome.
Apparently Firefox's behavior with event.preventDefault() was that it blocked any kind of user scrolling and even the js manual scrolling.
The problem in my case was solved by hiding the scroll in css instead of js by using overflow: hidden; and then controlling the scroll manually instead of preventing the default behavior.

Disabled button functional, not working properly

$("#qty-plus3").click(function() {
var val = parseInt($("#qty3").val(),10);
$("#qty3").val(val+1);
$("#add-to-cart").removeAttr('disabled').removeClass('view-disable').addClass('view-add');
$('#wednesday').attr('value',$("#qty3").val());
});
$("#qty-minus3").click(function() {
var val = parseInt($("#qty3").val(),10);
if(val != 0) {
$("#qty3").val(val-1);
$('#wednesday').attr('value',$("#qty3").val());
$("#add-to-cart").removeAttr('disabled').removeClass('view-disable').addClass('view-add');
}
});
This works that when the value is more than one my "Add to Cart" becomes enabled, then I realized that if I add one, then I remove one, my button should go back to being disabled but now it stays enabled even when both are 0. For example I land on page its disabled, then I add , it gets enabled then I remove so that it is back to 0 , it is still enabled, how can I fix it and make it disabled if there is 0 on it?
I tired this but it didnt work inside the minus function
$("#qty-minus3").click(function() {
var val = parseInt($("#qty3").val(),10);
if(val != 0) {
$("#qty3").val(val-1);
$('#wednesday').attr('value',$("#qty3").val());
$("#add-to-cart").removeAttr('disabled').removeClass('view-disable').addClass('view-add');
}
if (val == 0)
{
$("#add-to-cart").addAttr('disabled').addClass('view-disable').removeClass('view-add');
}
});
Use .prop('disabled', true) instead. Your working code will look like this.
$("#qty-minus3").click(function() {
var val = parseInt($("#qty3").val(), 10);
if (val != 0) {
$("#qty3").val(val - 1);
$('#wednesday').attr('value', $("#qty3").val());
$("#add-to-cart").removeAttr('disabled').removeClass('view-disable').addClass('view-add');
}
else if (val == 0) {
$("#add-to-cart").prop('disabled', true).addClass('view-disable').removeClass('view-add');
}
});

Using jQuery to snap to div on scroll

I have built a simple vertical scrollable website that snaps the view to divs when the user scrolls up or down the page. You cans see a demo here: http://dev.driz.co.uk/snap.html
The JS is fairly simple:
var currentScreen = 0;
var scrollReady = false;
var screens = new Array( 'one',
'two',
'three');
function scrollNext() {
if( currentScreen < screens.length-1 && scrollReady == true ) {
currentScreen++;
performScroll();
}
}
function scrollPrev() {
if( currentScreen > 0 && scrollReady == true ) {
currentScreen--;
performScroll();
}
}
function performScroll() {
scrollReady = false;
var newYPos = Math.ceil($('#'+screens[currentScreen]).offset().top);
$('.snap').animate({scrollTop: newYPos }, 500, function() { scrollReady = true; });
}
$(document).ready(function() {
scrollReady = true;
$('.snap').bind('mousewheel', function (event, aS, aQ, deltaY) {
event.preventDefault();
if (deltaY > 0) {
scrollPrev();
} else {
if (deltaY < 0) {
scrollNext();
}
}
return false;
});
$(document).bind('keyup', function (event) {
if (event.keyCode == 40 || event.keyCode == 38) {
event.preventDefault();
if (event.keyCode == 40) {
if (scrollReady == true) {
scrollNext();
}
} else {
if (event.keyCode == 38) {
if (scrollReady == true) {
scrollPrev();
}
}
}
}
});
$(document).bind('keydown', function (event) {
if (event.keyCode == 40 || event.keyCode == 38 ) {
event.preventDefault();
}
});
});
However I can only scroll to the first two divs and can't get to the third one... Any ideas why this is happening? I can't see issues that would cause this that wouldn't effect the first two from working...
Update: Sometimes you can get it to scroll to the third div (scrolling up and down until it does), but it skips the second div and then when the user scrolls up again, it jumps all the way to the top... so something weird is happening.
Update 2: I've noticed that currentScreen is incorrectly 2 when you scroll to the second div which is why you can't scroll to the third div. Any ideas why though?
Update 3: It seems that the scrollReady variable isn't preventing the functions from being called multiple times in places, as if you scroll up and down a few times, you find that sections are scrolled passed multiple times. Which shouldn't happen, you should only be able to scroll up one and down one at a time.
Store the values of section offsets in variable and then try, it will work.
check this on codepen.
http://codepen.io/sandeshdamkondwar/pen/veGko?editors=100
In scrollNext() function Your conditional checking is wrong
on second screen this condition will be false and therefore it is not moving to third screen.
It should be
currentScreen < screens.length

How to show element when a key is pressed, then if it is pressed again, hide the element?

I have this project where I have a side menu which can be toggled using [Ctrl] + [Z]. I want it to hide the very next time the [Ctrl] + [Z] pattern is pressed. My mediocre knowledge of JavaScript hinders me being able to phrase it using google, so I ultimately didn't find anything, so I'm coming here. With the amount of JavaScript I know this technically should work, but logically wouldn't work. Any ideas? Here's my code:
var letter = {
z: 90
...
};
$(document).ready(function() {
$("body").keydown(function(event) {
// toggles element the first time
if(event.ctrlKey && event.which === letter.z) {
$("[data-location='top']").slideDown("fast");
$("[data-location='bottom']").slideDown("fast");
}
// hides element the second time
if(event.ctrlKey && event.which === letter.z) {
$("[data-location='top']").slideUp("fast");
$("[data-location='bottom']").slideUp("fast");
}
});
});
Any help would be very much appreciated! :-)
The .slideToggle() function is what you're looking for.
var letter = {
z: 90
...
};
$(document).ready(function() {
$("body").keydown(function(event) {
if(event.ctrlKey && event.which === letter.z) {
$("[data-location='top']").slideToggle("fast");
$("[data-location='bottom']").slideToggle("fast");
}
});
});
JavaScript:
var letter = {
z: 90
};
$(document).ready(function() {
var visible = false;
$("body").keydown(function(event) {
// toggles element the first time
if(!visible && event.ctrlKey && event.which === letter.z) {
visible = true;
$("[data-location='top']").slideDown("fast");
$("[data-location='bottom']").slideDown("fast");
} else if(visible && event.ctrlKey && event.which === letter.z) {
visible = false;
$("[data-location='top']").slideUp("fast");
$("[data-location='bottom']").slideUp("fast");
}
});
});​
HTML:
<div id="top" class="hidden" data-location="top"></div>
<div id="bottom" class="hidden" data-location="bottom"></div>​
CSS:
#top {height:100px;width:500px;background-color:red;}
#bottom {height:100px;width:500px;background-color:blue;}
.hidden {display:none;}
Fiddle
You only need to bind once to keydown, and then thrw your logic in there. So you code will become:
var letter = {
z: 90
...
};
$(document).ready(function() {
$("body").keydown(function(event) {
// toggles element the first time
if(event.ctrlKey && event.which === letter.z) {
$("[data-location='top']").toggle("fast");
$("[data-location='bottom']").toggle("fast");
}
});
});

Continuous movement when a key is held down

Is it possible in jQuery to have an element continuously move when the key is held down?
I've tried a few ways but they always have a break in between animation calls. The code I currently have:
$(document).keydown(function (e) {
if (e.which == 37) {
$('#you').stop().animate({
left: '-=16px'
}, 10);
}
});
$(document).keyup(function (e) {
$('#you').stop();
});
.animate() isn't always the best way.
// cache jQuery objects for performance
var you = $( "#you" )
, doc = $( document )
// variable to hold motion state
, activeMotion
// goDown motion, adjust numbers to taste
, goDown = function(){
you.css( "left" , you.css( "left" ) - 16 );
if ( activeMotion === goDown ) {
setTimeout( goDown , 10 );
}
}
doc.keydown( function( e ) {
if ( e.which === 37 && activeMotion !== goDown ) {
activeMotion = goDown;
goDown();
}
// all directions can go here in seperate if/else statements
// be sure to include "activeMotion !== goDown" else every time
// keydown event fires, it will start a new goDown loop.
} );
doc.keyup( function () {
// simply ends any motion that checked activeMotion
activeMotion = null;
} );
Instead of animating the element, rather just move it by some amount of pixels. 16 will probably be too much because it will go too fast.
I think you are seeing the break between animation calls because of your timing.
If you look at your operating systems keyboard repeat time interval, it should be around 35 milliseconds - test it here.
On top of that you are animating every 10ms, not counting the time it takes to run all the animation functions, etc. So why not simplify the animation and not worry about time intervals (try this demo):
var block = $('.block'),
leftPos;
$(document).keydown(function (e) {
leftPos = block.position().left;
leftPos += (e.which == 37) ? -5 : 0;
leftPos += (e.which == 39) ? 5 : 0;
block.css('left', leftPos);
});

Categories

Resources