The Situation
I am using the effect() function of JQuery UI. The type of effect doesn't really matter but for the purpose of this question lets use "bounce". This effect is called when a link is clicked, so my complete example code is as follows:
$('#button').click(function (e) {
e.preventDefault();
$('#box').effect('bounce');
});
Here is a demo
The Problem
The problem I have, or more the behavior I want to get rid of, is that when you click the link multiple times in quick succession then it queues up the animations. (See the demo, click the link 10 times fast, then release and watch it continue to animate)
The Requirement
I just want to prevent effects/animations from being queued. In other words, I am looking for clicks to be ignored if the box is already bouncing. Is there anyway I can do this?
The Failed Attempts
I have already done some research, and I tried a couple of method below but to no success:
$('#box').stop().effect('bounce');
stop() just doesn't seem to have any effect in this case.
$('#box').clearQueue().effect('bounce');
clearQueue actually works in the sense that the effects don't queue, however there are side-effects which causes the layout to mess up. I assume this is because it prevents the effect from returning the styles to their defaults. It may also be related to using a fixed position for the box.
This should do the trick.
$('#button').click(function (e) {
e.preventDefault();
if( !$('#box').is(':animated') ){
$('#box').effect('bounce');
}
});
Yes you are right, clearQueue makes position changes in div but i tried that and it looks like working
$('#button').click(function (e) {
e.preventDefault();
if ($("#box").is(':animated')) {
$('#box').effect = null
}
else {
$('#box').effect('bounce');
}
});
js fiddle example
Try this:
$('#button').click(function (e) {
e.preventDefault();
$('#box').stop(true).effect({
effect: 'bounce',
complete: function() {
$('#box').removeAttr('style');
}
});
});
stop(true) stops and clears the queue immediately but when stopped, effect() leaves inline styles after completion. They just need to be removed afterwards.
The benefit of this approach is that the clicks feel a little more responsive (for the lack of a better word). The box will seem to react quickly to user's clicks.
However, if you cannot afford to simply remove the style attribute upon completion of animation, say you have previously applied inline styles on your object, you can cache them beforehand and re-apply them upon completion, which is demonstrated in this fiddle.
Hope this helps.
Related
I'm creating a site using Bootstrap 3, and also using a script that makes the dropdown-menu appear on hover using the .hover() function. I'm trying to prevent this on small devices by using enquire.js. I'm trying to unbind the .hover() event on the element using this code:
$('.dropdown').unbind('mouseenter mouseleave');
This unbinds the .hover of that script but apparently it also removes the .click() event(or whatever bootstrap uses), and now when I hover or click on the element, nothing happens.
So I just want to how I can remove the .hover() on that element, that is originating from that script, but not change anything else.
Would really appreciate any help.
Thanks!
Edit: Here is how I'm calling the handlers for the hover functions:
$('.dropdown').hover(handlerIn, handlerOut);
function handlerIn(){
// mouseenter code
}
function hideMenu() {
// mouseleave code
}
I'm trying to unbind them with this code.
$('.dropdown').unbind('mouseenter', showMenu);
$('.dropdown').unbind('mouseleave', hideMenu);
But its not working.
Please help!
**Edit2: ** Based on the answer of Tieson T.:
function dropdownOnHover(){
if (window.matchMedia("(min-width: 800px)").matches) {
/* the view port is at least 800 pixels wide */
$('.dropdown').hover(handlerIn, handlerOut);
function handlerIn(){
// mouseenter code
}
function hideMenu() {
// mouseleave code
}
}
}
$(window).load(function() {
dropdownOnHover();
});
$(window).resize(function() {
dropdownOnHover();
});
The code that Tieson T. provided worked the best; however, when I resize the window, until I reach the breakpoint from any direction, the effect doesn't change. That is, if the window is loaded above 800px, the hover effect will be there, but if I make the window smaller it still remains. I tried to invoke the functions with window.load and window.resize but it is still the same.
Edit 3: I'm actually trying to create Bootstrap dropdown on hover instead of click. Here is the updated jsFiddle: http://jsfiddle.net/CR2Lw/2/
Please note: In the jsFiddle example, I could use css :hover property and set the dropdow-menu to display:block. But because the way I need to style the dropdown, there needs to be some space between the link and the dropdown (it is a must), and so I have to find a javascript solution. or a very tricky css solution, in which the there is abot 50px space between the link and the dropdown, when when the user has hovered over the link and the dropdown has appeared, the dropdown shouldn't disappear when the user tries to reach it. Hope it makes sense and thanks.
Edit 4 - First possible solution: http://jsfiddle.net/g9JJk/6/
Might be easier to selectively apply the hover, rather than try to remove it later. You can use window.matchMedia and only apply your script if the browser has a screen size that implies a desktop browser (or a largish tablet):
if (window.matchMedia("(min-width: 800px)").matches) {
/* the view port is at least 800 pixels wide */
$('.dropdown').on({
mouseenter: function () {
//stuff to do on mouse enter
},
mouseleave: function () {
//stuff to do on mouse leave
}
});
}
else{
$('.dropdown').off('mouseenter, mouseleave');
}
Since it's not 100% supported, you'd want to add a polyfill for those browsers without native support: https://github.com/paulirish/matchMedia.js/
If you're using Moderizr, that polyfill is included in that library already, so you're good-to-go.
I still don't understand how you intend to "dismiss" the dropdown-menu once it is displayed upon mousing over the dropdown element partly because there's not enough code in your question, but that's sort of irrelevant to this answer.
I think a much easier way to approach the mousenter event handling portion is not by using off()/on() to unbind/bind events at a specific breakpoints, but rather to do just do a simple check when the event is triggered. In other words, something like this:
$('.dropdown').on('mouseenter', function() {
if($('.navbar-toggle').css('display') == 'none') {
$(this).children('.dropdown-menu').show();
};
});
$('.dropdown-menu').on('click', function() {
$(this).hide();
});
Here's a working fiddle: http://jsfiddle.net/jme11/g9JJk/
Basically, in the mouseenter event I'm checking if the menu toggle is displayed, but you can check window.width() at that point instead if you prefer. In my mind, the toggle element's display value is easier to follow and it also ensures that if you change your media query breakpoints for the "collapsed" menu, the code will remain in sync without having to update the hardcoded values (e.g. 768px).
The on click to dismiss the menu doesn't need a check, as it has no detrimental effects that I can see when triggered on the "collapsed" menu dropdown.
I still don't like this from a UX perspective. I would much rather have to click to open a menu than click to close a menu that's being opened on a hover event, but maybe you have some magic plan for some other way of triggering the hide method. Maybe you are planning to register a mousemove event that checks if the mouse is anywhere within the bounds of the .dropdown + 50px + .dropdown-menu or something like that... I would really like to know how you intend to do this (curiosity is sort of killing me). Maybe you can update your code to show the final result.
EDIT: Thanks for posting your solution!
Background:
I'm writing a component that opens up a sub-menu on click. I can't know where this component will be placed on the page or how far it will be nested in areas that may have the overflow property set.
Given that the overflow may clip the sub-menu I am instead making the sub-menu itself be attached to the body giving it an absolute position and linking it via code to the original component. This takes care of the overflow issue.
Problem:
However if a user scrolls the sub-menu remains in place, rather than moving with its linked component, so I need to be able to listen to any and all scroll events that occur on the page so I can reposition the sub-menu appropriately.
If there's an easy way to listen to all scroll events or if there's another better way to do this component I would appreciate any input.
I've played around with JSFiddle and set up a sandbox but I haven't had any success nor have I found an answer on this site or anywhere else for that matter; though perhaps I was using the wrong search terms, I can't imagine that I'm the first to have this question.
EDIT
To address the close vote, I'm not asking help to debug an issue without providing code nor am I asking something that won't help anyone in the future. I'm asking how I would go about listening to all event of a certain type not matter where the may occur, which I find globally applicable, though perhaps that's subjective.
EDIT
$(window).on('scroll', function(){ /**/ });
is not an option as it only listens to the window scroll, not any nested scrolls.
$('#ex1 #ex2').on('scroll', function(){ /**/ }); is not an option as it requires the person who is implementing the code to be aware of any current or possible future areas on the page that may scroll.
You should be able to attach a document-level listener with a third parameter of true to capture the scroll events on all elements. Here's what that looks like:
document.addEventListener('scroll', function(e){ }, true);
The true at the end is the important part, it tells the browser to capture the event on dispatch, even if that event does not normally bubble, like change, focus, and scroll.
Here's an example: http://jsbin.com/sayejefobe/1/edit?html,js,console,output
You need to see whether scroll is happening to window level or to an element level. Usually in your case '*' should suffice.
$('*').scroll(function() {
alert('scroll');
});
Here is updated link: http://jsfiddle.net/wAadt/1
How about listing on all elements and the window?
$('*').add(window).scroll(function() {
console.log('scroll');
});
The best way to do it would be to find out which elements are scrollable, then attach listeners to them. You could run this function on any page change to make sure you've always got all the scrollables.
This is a benefit over using listeners on every element (as the other solutions would do) in terms of performance: every time the page updates so do the listeners. With lots, this quickly affects performance and memory use.
The updated fiddle: http://jsfiddle.net/ArtOfCode/wAadt/8/
The code:
$("*").each(function() {
if($(this).css("overflow") == "auto" || $(this).css("overflow") == "scroll") {
$(this).scroll(function() {
console.log("scroll");
});
}
});
(thanks to #pebbl for the help)
You could then wrap this in a function and run it on change:
function addListeners() {
$("*").each(function() {
if($(this).css("overflow") == "auto" || $(this).css("overflow") == "scroll") {
$(this).css('border', '1px solid red').scroll(function() {
console.log("scroll");
});
}
});
}
$("body").on("change",function()
addListeners();
}
Admittedly it is a bit convoluted but it addresses the issue with as few event listeners as possible.
Using jQuery's scroll() function you can detect any scroll event whether it is created by whether user (i.e. mouse scroll, dragging the scroller) or DOM engine (add or remove element). How can I discriminate a scroll event fired by the DOM engine.
This jsfiddle clearly shows what I meant.
There are a lot of ways you could handle this, but I don't know of one that's incredibly official.
One easy way is to just set a toggle when you initiate scrolling programmatically (such as setting a class) and removing it when you're done. I've forked your fiddle to show how that would work.
$("#scrollable")
.addClass('programatically-scrolling')
.scrollTo({
top: $("#scrollable").prop("scrollHeight") - $("#scrollable").innerHeight(),
left: 0
}, 1000, {
axis: 'y',
// Add a function to remove the toggle when the animation is complete
onAfter: function () {
$(this).removeClass('programatically-scrolling');
}
});
$("#scrollable").scroll(function (event) {
// This will be true when your $.scrollTo animation is going
// and false when you trigger the scroll with your mouse
console.log($(this).hasClass('programatically-scrolling'));
});
There are other ways of tackling this, and other people who have asked the same question. Making a jQuery special event would probably be the most fun, but the solution I outlined is probably the simplest.
Sql Fiddle example
$('#myBox').click(function(e){
e.preventDefault();
e.stopPropagation();
$('#myBox').prop("checked", !$('#myBox').prop("checked") );
});
I'm attaching this to a checkbox so that the check toggling is controlled by my javascript instead of by the default behavior, and it just won't work. I can't figure out why!
My reason for doing this: IE has a double-click filter, so to speak, so that double-clicking checkboxes only registers as a single click. This happens to stop people from toggling checkboxes really fast, which is a feature I need for my application, strangely enough. So I'm just canceling its default functionality and catching clicks by hand with JavaScript/jQuery and toggling it that way, thus eliminating the "speed limit." Except I don't know how to stop the clicks from toggling it in the first place, hence this question.
setTimeout(function() {
$('#myBox').prop("checked", !$('#myBox').prop("checked") );
}, 1);
The delayed timer of 1 millisecond is all it takes.
See this in your jsFiddle, modified.
I'm guessing you want to run something when your checkbox changes...
In that case you should be catching the change event:
$('#myBox').bind('change', function() {
// Do checks here, if you don't want it to change, then deal with it then
});
I have a BG image animation that relies on the hover callback to revert to it's original state. If I move the mouse quickly over the links, the hovered state sticks. My guess is that I'm moving the mouse off before the first animation completes, so the callback doesn't register.
Is there a more bulletproof way to write this function?
$('.nav li a').hover(function() {
$(this).addClass('hovered', 300);
}, function() {
$(this).removeClass('hovered', 300);
});
(it uses a BGimg plugin to support the speed parameter on add/removeClass)
Testable here: McPherson Industries
This is a common problem and cannot be fixed, I'm afraid – it's up to the browser to keep up with events, and if it can't, well, you're out of luck.
It might be jQuery's fault also, though. Hard to say without dwelling into the source code. Anyways, there's a easy workaround:
$('.nav li a').hover(function() {
// When element is hovered, remove hovered state from all elements..
$('.hovered').removeClass('hovered');
// ..and add it to this one.
$(this).addClass('hovered', 300);
}, function() {
// This is only needed when exiting an element and not enterin another one
$(this).removeClass('hovered', 300);
});
This might not be 100% certain, but it's better than your current.
What is the 300 value in the addClass method ? it does not take two parameters.. just one
Api reference: http://api.jquery.com/addClass/
(ok,noticed the plugin)
It works just fine for me .. all links revert back to their original image, no-matter how fast i move the mouse..
Update
Ok i see the problem now..
Chrome is OK (the only one it seems..)
Firefox shows the problem..
IE shows the problem
Update 2
It seems to me that the problem is with the plugin you use.. It ignores queued requests to the same object until the current animation has finished.. So if you hover out before the first one has completed it will not return to normal..
I can't help more on this, sorry ..