jQuery UI - prevent Shake effect being fires multiple times - javascript

just added some shake effect when user submit form on the inputs that not being full.
But when user press many times on submit button the shake effect fired many times.
How can I prevent it from being fired more than one time?
stop() - not workings.
clearQueue() - stop it even if it moves from it's first place in the page.
This is what I came with:
$(this).effect("shake");
Thanks :)

if you can use an extra variables you can use a flag:
first_submit = true
if(first_submit){
first_submit = false
$(this).effect("shake");
}

You could create a variable to set the effect is active (running). When your submit call is intercepted by a callback you verify if it's true, if yes you return. Preventing from another call.
In the effect callback (success) you set the variable false, for example. That's the cycle.
I have added an extension for the effect to callback it's success, look here: What is the true way of adding callback functionality to custom JQuery Animation?
Something like:
// Your new control
var active = false;
// Extending callback functionality to the animation
$.fn.shake = function ( times, distance, duration, callback ) {
return this.css({ position: 'relative' }).each( function () {
for ( var i = 0, t = duration / times; i < times; i+= 1 ) {
$( this ).
animate({ left: -distance }, t / 3 ).
animate({ left: distance }, t / 3 ).
animate({ left: 0 }, t / 4 );
}
$( this ).show( callback );
});
};
// DOM load
$(document).ready(function(){
// Triggering form the event
$('#form_ID').submit({function(){
if(active == true)
return false;
// trigger your event..
shake(5, 10, 500, function () {
$('#your_component_ID').removeClass( 'shaking' );
});
});
});
I guess you could take a look in this post too: jQuery prevent multiple clicks until animation is done

Related

How to detect when the scroll starts using Javascript/Jquery?

I want to make a one pager website, without using any third party libraries, like FullPage.js.
when scroll starts --> instead of waiting for the end of the natural scrolling, I want it to take no effect (so no visible scroll caused by the mouse) and to run my code instead. (so it could always go to next section, or previous one, without relying on the amount of the users scroll)
Do you have any idea how could I achieve this? My code snippet waits for the end of scroll, and then jumps to where it should, so it's not working as intended.
(the first section has a "current" class and then the code snippet works by manipulating the 100vh sections by adding/removing this class)
You can see the code snippet I am using below or here:
https://codepen.io/makiwara/pen/PoqjdNZ
Thank you very much for your help, have a nice day!
var script = document.createElement('script');script.src = "https://code.jquery.com/jquery-3.4.1.min.js";document.getElementsByTagName('head')[0].appendChild(script);
var timerId;
var scrollableElement = document.body; //document.getElementById('scrollableElement');
scrollableElement.addEventListener('wheel', checkScrollDirection);
function checkScrollDirection(event) {
var $current = $('.current');
if (checkScrollDirectionIsUp(event)) {
console.log('UP');
$prev = $current.prev();
if ($prev.length) {
clearTimeout(timerId);
timerId = setTimeout(function(){
$current.removeClass('current');
$prev.addClass('current');
$('body,html').animate({
scrollTop: $('.current').offset().top
}, 100);
}, 100)
}
} else {
console.log('Down');
$next = $current.next();
if ($next.length) {
clearTimeout(timerId);
timerId = setTimeout(function(){
$current.removeClass('current');
$next.addClass('current');
$('body,html').animate({
scrollTop: $('.current').offset().top
}, 100);
} , 100)
}
}
}
function checkScrollDirectionIsUp(event) {
if (event.wheelDelta) {
return event.wheelDelta > 0;
}
return event.deltaY < 0;
}
What you need is throttling the event listener, i.e. limit a function call only once per a time period.
What your code is doing is essentially debouncing i.e limit a function call only after a wait time period has passed.
Firstly ditch the timers you're using. You need to somehow block scrolling from happening more than once. The JavaScript part can be easy if you use Underscore.js's throttle function with one caveat though: It passes through subsequent events after the time period has passed. Luckily, its debouncing method accepts a third argument that gives the behavior you'd want:
scrollableElement.addEventListener(
"wheel",
_.debounce(checkScrollDirection, 200, true) // immediately call the function _once_
);
This third argument makes the debounced function behave like a throttled one, that is it will fire only once and at the same time it will fire immediately.
So assuming that your event handler is now free from the original timeout
function checkScrollDirection(event) {
var $current = $(".current");
if (checkScrollDirectionIsUp(event)) {
console.log("UP");
$prev = $current.prev("section");
if ($prev.length) {
$current.removeClass("current");
$prev.addClass("current");
$("body,html").animate(
{
scrollTop: $prev.offset().top
},
100
);
}
} else {
console.log("Down");
$next = $current.next("section");
if ($next.length) {
$current.removeClass("current");
$next.addClass("current");
$("body,html").animate(
{
scrollTop: $next.offset().top
},
100
);
}
}
}
btw, try to get into the habit of specifying selectors inside .next() and .prev() since jQuery will match all possible siblings, which most likely you don't want. In this case, codepen appends additional <script> elements and jQuery will match those as well.
Now if you try this, you'll notice that the window still responds to every scroll event. Scroll events are one of those events that cannot be cancelled so you need to disable it via CSS
The easiest way is to hide the overflow of the body
body { max-height: 100vh; overflow: hidden; }
And that's it. You may need to adjust the throttle waiting time period to match your preferences.
You can find a working version of the codepen here: https://codepen.io/vassiliskrikonis/pen/XWbgxLj

Firing multiple Events on JQuery Resize

I have been using the JQuery Code below to handle a little bit of responsiveness for a menu on a Drupal site. In the two commented lines in the resize function, I am essentially trying to enable and disable the opposite events dependent on the screen size. My first question would be since this handler triggering would be in the resize function, would it cause any kind of significant performance hit to attempt something like this? My second question would be how? I've been trying to use the on and off functions to enable/disable those handlers as needed, but I don't think I'm getting the overall syntax correct. I figure it would be best to break the existing event handlers into functions, but have left them as is for the code example.
jQuery(document).ready(function($) {
$('.nav-toggle').click(function() {
$('#main-menu div ul:first-child').slideToggle(250);
return false;
});
if( ($(window).width() > 600) || ($(document).width() > 600) ) {
$('#main-menu li').mouseenter(function() {
$(this).children('ul').css('display', 'none').stop(true,
true).slideToggle(1).css('display',
'block').children('ul').css('display', 'none');
});
$('#main-menu li').mouseleave(function() {
$(this).children('ul').stop(true, true).fadeOut(1).css('display', 'block');
})
}
else {
$('.drop-down-toggle').click(function() {
$(this).parent().children('ul').slideToggle(500);
});
}
$(window).resize(function() {
if($(window).width() > 600) {
$('div.menu-navigation-container ul.menu').css('display','block');
$('div.menu-navigation-container ul.menu ul.menu').hide();
//**Disable dropdown click and enable mouse enter and mouse leave**
}
else{
$('div.menu-navigation-container ul.menu').hide();
//**Disable mouse enter and mouse leave but enable dropdown click**
}
});
});
Use a throttle function
function throttle (callback, limit) {
var wait = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!wait) { // If we're not waiting
callback.call(); // Execute users function
wait = true; // Prevent future invocations
setTimeout(function () { // After a period of time
wait = false; // And allow future invocations
}, limit);
}
}
}
$(window).on('resize', throttle(yourResizeFunction, 200))
Read why here: http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
As I said, move your event binding outside of the resize function as binding event handlers within resize/scroll is not a good idea at all as you'd bind the same event over and over for every pixel resized!.
An example would look like this:
$(document) // or you can even use 'div.menu-navigation-container' as opposed to document
.on("click", ".click", function() {})
.on("mouseenter", ".hover", function() {})
.on("mouseleave", ".hover", function() {});
$(window).resize(function() {
//A bit of breathing time when the resize event pauses. Remember, the statements within the resize will trigger for every pixel resize, otherwise.
setTimeout(function() {
if( $(window).width() > 600 ) {
$('div.menu-navigation-container ul.menu').css('display','block');
$('div.menu-navigation-container ul.menu ul.menu').hide();
//I am assuming your selector on which the events are bound to be '.menu-trigger' as you did not post any HTML. Replace this with the appropriate selector.
$(".menu-trigger").removeClass("click").addClass("hover");
}
else{
$('div.menu-navigation-container ul.menu').hide();
//I am assuming your selector on which the events are bound to be '.menu-trigger' as you did not post any HTML. Replace this with the appropriate selector.
$(".menu-trigger").removeClass("hover").addClass("click");
}
}, 250);
});
Hope that helps.

Inheritance causes function to be executed so many times Javascript

I am learning JavaScript and I got stuck creating a function to minimize a window. The problem is that this functions seems to stack in itself so many times.
Gere is my principal function :
function displayChatWindow(user, status, avatar, id){
var template = _.template($("#windowTemplate").html(), {userName: user, userStatus: status, userAvatar: avatar, userId: id});
stackingWidth = stackingWidth - boxWidth;
console.log(stackingWidth);
$("body").prepend(template);
$(".messages-container").slimScroll({
height: '200',
size: '10px',
position: 'right',
color: '#535a61',
alwaysVisible: false,
distance: '0',
railVisible: true,
railColor: '#222',
railOpacity: 0.3,
wheelStep: 10,
disableFadeOut: false,
start: "bottom"
});
$("#" + id).css({
top: absoluteY,
left: stackingWidth
});
$(".minimize-others").on("click", displayOthersChat);
$(".chat input, .chat textarea").on("focus", cleanInputs);
$(".chat input, .chat textarea").on("blur", setInputs);
}
This function receives some parameters and with a template creates the chat window. At the end it applies the function to minimize the window (displayOthersChat) and load plugins and stuff for each window.
My displayOtherChats function:
function displayOthersChat(e){
/*e.preventDefault();*/
var This = $(this).parent().parent();
var minimize = This;
if(!This.hasClass("draggable")){
This.animate({
top: windowHeight - boxHeight - 20
});
This.addClass("draggable");
This.draggable({handle: ".header"});
var timeOut = setTimeout(function() {
This.find(".minimize").toggleClass("rotate");
}, 500);
}else{
This.draggable("destroy");
This.removeClass("draggable");
var timeOut = setTimeout(function() {
This.find(".minimize").toggleClass("rotate");
}, 500);
This.animate({
top: absoluteY
});
}
/*return false;*/
}
This seems to work really well. If I open my first window it displays and also minimizing the window works. When I open another window, the last window works correctly but the first window opens when I try to minimize it.
It seems that it calls the function twice, and if I open a third window, the first window calls the function three times.
I actually don't know whats going on, I will appreciate if you guys could help me. I also leave a link so you guys can see whats going on: http://s3.enigmind.com/jgonzalez/nodeChat.
The problem seems to be that you are binding the same event handler to the same elements over and over again.
$(".minimize-others").on("click", displayOthersChat); will bind displayOthersChat to all existing elements with class minimize-others. .on always adds event handlers, it does not replace them. So if you call displayChatWindow multiple times, you are binding the event handler to the .minimize-others elements multiple times.
You only want to bind the handler to the window that was just created, for example:
// create reusable jQuery object from HTML string.
var $template = $(template).prependTo('body');
// instead of $("body").prepend(template);
// ...
$template.find('.minimize-others').on('click', displayOthersChat);
Same goes for the other event handlers.
Alternatively, you could bind the event handler once, outside of the function and use event delegation to capture the event:
$(document.body).on('click', '.minimize-others', displayOthersChat);

Jquery fadeTo on mouseover

I am using a plugin called anything slider and am trying to make the controls fade after 4 seconds (which is working) then change opacity back to 1 on mouseover (not working). Here is what I have so far...what am I doing wrong?
$(slider.$controls).mouseover(function()
slider.$controls.fadeTo(400, 1.0);
});
$(function () {
var fadeTime = 400,
fadeDelay = 4000,
timer, hideControls = function (slider) {
clearTimeout(timer);
setTimeout(function () {
slider.$controls.fadeTo(fadeTime, 0.3);
$('.tooltip').fadeOut(fadeTime);
}, fadeDelay);
};
});
you sould replace every slider.$controls.fadeTo with slider.controls.fadeTo
$(slider.controls).mouseover(function()
$(this).fadeTo(400, 1.0);
});
You have a syntax error on line 2. Where you have
slider.$controls.fadeTo...
you should have
$(this).fadeTo...
because once you've entered the anonymous function on line 1, the this object now refers to the DOM element upon which you're executing this code, which is the element represented by slider.controls.
First you use slider.controls to target the control element(s), then you use slider.$controls to target the same element(s). I think you need to decide which one it is.
Also, inside a callback function you can use this as a reference to the element that has had the event fired on it:
$(slider.controls).bind('mouseover', function () {
$(this)...
});
Otherwise if you want to fade-in-out all the controls at the same time then you just need to figure out if you need to use slider.$controls or slider.controls.
UPDATE
I see you have changed your question and now you are using slider.$controls both times. You should put your mouseover code inside the document.ready event handler so you know the DOM elements are available:
$(function () {
slider.$controls.mouseover(function()
slider.$controls.fadeTo(400, 1.0);
});
var fadeTime = 400,
fadeDelay = 4000,
timer, hideControls = function (slider) {
clearTimeout(timer);
setTimeout(function () {
slider.$controls.fadeTo(fadeTime, 0.3);
$('.tooltip').fadeOut(fadeTime);
}, fadeDelay);
};
Also I noticed that you wrapped slider.$controls in a jQuery object the first time, but not the second, make sure to do that if slider.$controls is not already a jQuery object (many times developers put a $ as the first character of a variable name to denote that it is a jQuery object).

jQuery - Disable Click until all chained animations are complete

Question
The solution below is intended to slide down the groupDiv displaying div1 and enough space for div2 to slide in. It's all achieved by chaining the animations on the #Link.Click() element.
It seems to bug out, though, when the link is clicked rapidly. Is there a way to prevent this? By perhaps disabling the Click function until the chained animations are complete? I currently have checks in place, but they don't seem to be doing the job :(
Here's the code i'm using:
Custom animate functions.
//Slide up or down and fade in or out
jQuery.fn.fadeThenSlideToggle = function(speed, easing, callback) {
if (this.is(":hidden")) {
visibilityCheck("show", counter--);
return this.slideDown({duration: 500, easing: "easeInOutCirc"}).animate({opacity: 1},700, "easeInOutCirc", callback);
} else {
visibilityCheck("hide", counter++);
return this.fadeTo(450, 0, "easeInOutCirc").slideUp({duration: 500, easing: "easeInOutCirc", complete: callback});
}
};
//Slide off page, or into overflow so it appears hidden.
jQuery.fn.slideLeftToggle = function(speed, easing, callback) {
if (this.css('marginLeft') == "-595px") {
return this.animate({marginLeft: "0"}, speed, easing, callback);
} else {
return this.animate({marginLeft: "-595px"}, speed, easing, callback);
}
};
In the dom ready, i have this:
$('#Link').toggle(
function() {
if (!$("#div2 .tab").is(':animated')) {
$("#GroupDiv").fadeThenSlideToggle(700, "easeInOutCirc", function() {$('#div2 .tab').slideLeftToggle();});
}
},
function(){
if (!$("#groupDiv").is(':animated')) {
$('#div2 .tab').slideLeftToggle(function() {$("#groupDiv").fadeThenSlideToggle(700, "easeInOutCirc", callback);} );
}
}
);
HTML structure is this:
<div id="groupDiv">
<div id="div1">
<div class="tab"></div>
</div>
<div id="div2">
<div class="tab"></div>
</div>
</div>
The issue is your first animating the div#GroupDiv so your initial check if (!$("#div2 .tab").is(':animated')) will be false until the groupDiv has finished animated and the callback is fired.
You could maybe try
if (!$("#div2 .tab").is(':animated') && !$("#GroupDiv").is(':animated'))
however I doubt this will cover really quick clicking. The safest is to unbind the event using
$(this).unbind('toggle').unbind('click');
as the first line inside the if and you can then do away with the animated check. The downside to this is you will have to rebind using the callback you are passing through to your custom animation functions.
You can easily disable your links while animation is running
$('a').click(function () {
if ($(':animated').length) {
return false;
}
});
You can of course replace the $('a') selector to match only some of the links.
Animating something that can be clicked repeatedly is something to look out for because it is prone for errors. I take it that you Problem is that animations queue up and are executed even when you have stopped clicking. The way I solved it was to use the stop() function on an Element.
Syntax: jQuery(selector).stop(clearQueue,gotoEnd) //both parameters are boolean
More Info
When I click on a button, I first stop the animation and clear the Queue, then i proceed to define the new animation on it. gotoEnd can stay false (default value) but you can try tochange it to true if you want, you might like the result.
Usage Example: jQuery('button#clickMe').stop(true).animate({left:+=10}).
you can put this first thing inside the click event
$(element).css({ "pointer-events":"none"});
, and this in the callback function of the animation
$(element).css({ "pointer-events":"auto"});
you can unbind... but this should work too:
if (!$("#div2 .tab").is(':animated') && !$("#GroupDiv").is(':animated')) return;
I have recently made an AJAX jQuery plugin, featuring plenty of animation. The workaround to the AJAX animation bug that I have found is as follows.
$(options.linkSelector).click(function(e){
if ($("#yourNav").hasClass("disabled")) {
return false;
} else {
e.preventDefault();
$("#yourNav").addClass("disabled")
// Prepare DOM for new content
$(content).attr('id', 'content-old');
$('<div/>', {id: 'ajMultiLeft'}).css({'top': '100%'}).insertAfter('#content-old');
// Load new content
$(content).load(linkSrc+ ' ' +options.content+ ' > *', function() {
// Remove old content
$(content).animate({top: '100%'}, 1000, function(){
$(content-old).remove();
$("#yourNav").removeClass("disabled")
});
setBase();
}
What this does is makes the click event for each link respond to nothing whilst the parent div has a class of disabled. The disabled class is set by the function upon initial click and removed via a callback on the final animation.

Categories

Resources