Javascript "while hovered" loop - javascript

Can anybody help me on this one...I have a button which when is hovered, triggers an action. But I'd like it to repeat it for as long as the button is hovered.
I'd appreciate any solution, be it in jquery or pure javascript - here is how my code looks at this moment (in jquery):
var scrollingposition = 0;
$('#button').hover(function(){
++scrollingposition;
$('#object').css("right", scrollingposition);
});
Now how can i put this into some kind of while loop, so that #object is moving px by px for as #button is hovered, not just when the mouse enters it?

OK... another stab at the answer:
$('myselector').each(function () {
var hovered = false;
var loop = window.setInterval(function () {
if (hovered) {
// ...
}
}, 250);
$(this).hover(
function () {
hovered = true;
},
function () {
hovered = false;
}
);
});
The 250 means the task repeats every quarter of a second. You can decrease this number to make it faster or increase it to make it slower.

Nathan's answer is a good start, but you should also use window.clearInterval when the mouse leaves the element (mouseleave event) to cancel the repeated action which was set up using setInterval(), because this way the "loop" is running only when the mouse pointer enters the element (mouseover event).
Here is a sample code:
function doSomethingRepeatedly(){
// do this repeatedly when hovering the element
}
var intervalId;
$(document).ready(function () {
$('#myelement').hover(function () {
var intervalDelay = 10;
// call doSomethingRepeatedly() function repeatedly with 10ms delay between the function calls
intervalId = setInterval(doSomethingRepeatedly, intervalDelay);
}, function () {
// cancel calling doSomethingRepeatedly() function repeatedly
clearInterval(intervalId);
});
});
I created a sample code on jsFiddle which demonstrates how to scroll the background-image of an element left-to-right and then backwards on hover with the code shown above:
http://jsfiddle.net/Sk8erPeter/HLT3J/15/

If its an animation you can "stop" an animation half way through. So it looks like you're moving something to the left so you could do:
var maxScroll = 9999;
$('#button').hover(
function(){ $('#object').animate({ "right":maxScroll+"px" }, 10000); },
function(){ $('#object').stop(); } );

var buttonHovered = false;
$('#button').hover(function () {
buttonHovered = true;
while (buttonHovered) {
...
}
},
function () {
buttonHovered = false;
});
If you want to do this for multiple objects, it might be better to make it a bit more object oriented than a global variable though.
Edit:
Think the best way of dealing with multiple objects is to put it in an .each() block:
$('myselector').each(function () {
var hovered = false;
$(this).hover(function () {
hovered = true;
while (hovered) {
...
}
},
function () {
hovered = false;
});
});
Edit2:
Or you could do it by adding a class:
$('selector').hover(function () {
$(this).addClass('hovered');
while ($(this).hasClass('hovered')) {
...
}
}, function () {
$(this).removeClass('hovered');
});

var scrollingposition = 0;
$('#button').hover(function(){
var $this = $(this);
var $obj = $("#object");
while ( $this.is(":hover") ) {
scrollingposition += 1;
$obj.css("right", scrollingposition);
}
});

Related

prevent animation double click issue

Hi I have problem with my slider please visit this site and check http://paruyr.bl.ee/
after click on my arrows it becomes work in an asynchronous way, ones it changes very fast and then slow and it repeats.
I think it is from start slider and stop slider.
var sliderPrev = 0,
sliderNext = 1;
$("#slider > img").fadeIn(1000);
startSlider();
function startSlider(){
count = $("#slider > img").size();
loop = setInterval(function(){
if (sliderNext>(count-1)) {
sliderNext = 0;
sliderPrev = 0;
};
$("#slider").animate({left:+(-sliderNext)*100+'%'},900);
sliderPrev = sliderNext;
sliderNext=sliderNext+1;
},6000)
}
function prev () {
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
function next () {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
function stopLoop () {
window.clearInterval(loop);
}
function showSlide(id) {
stopLoop();
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
startSlider();
};
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function onlyNext () {
var newSlide=sliderPrev+1;
onlyShowSlide(newSlide);
}
function onlyShowSlide(id) {
if (id>(count-1)) {
id = 0;
} else if(id<0){
id=count-1;
}
$("#slider").animate({left:+(-id)*100+'%'},900);
sliderPrev = id;
sliderNext=id+1;
};
I think the best option would be to check if the animation is in progress and prevent the action if it is, something like this:
function prev () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev-1;
showSlide(newSlide);
}
}
function next () {
if(!$('#slider').is(":animated"))
{
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
To illustrate the difference between this and just sticking a stop() in, check this JSFiddle. You will notice some choppy movements if you click multiple times in the stop() version.
What I would do is add a class to your slider when the animation starts and remove the class when it finishes:
$("#slider").animate({left:+(-id)*100+'%'}, {
duration: 900,
start: function() {
$('#slider').addClass('blocked');
},
complete: function() {
$('#slider').removeClass('blocked');
}
});
Now check on each click event if the slider is blocked or not:
function next () {
if (!$('#slider').hasClass('blocked')) {
var newSlide=sliderPrev+1;
showSlide(sliderNext);
}
}
This is a very simple solution, I'm sure there is a better one.
EDIT: As marcjae pointed out, you could stop the animations from queuing. This means when you double click, the slideshow still will move 2 slides. With my approach the second click will be ignored completely.
You can use a variable flag to control if the animation is still being done, or simply use .stop() to avoid stacking the animation.
$("#pull").click(function(){
$("#togle-menu").stop().slideToggle("slow");
});
It is occurring because your animations are being queued.
Try adding:
.stop( true, true )
Before each of your animation methods. i.e.
$("#slider").stop( true, true ).animate({left:+(-id)*100+'%'},900);
The answers about stop are good, but you have a bigger issue that is causing the described behavior. The issue is here:
$("#slider, .arrows").hover(function() {
stopLoop()
}, function() {
startSlider()
});
You have bound this to the .arrows as well as the #slider and the arrows are contained within the slider. So, when you mouse out of an arrow and then out of the entire slider, you are calling start twice in a row without calling stop between. You can see this if you hover onto the arrow and then off of the slider multiple times in a row. The slides will change many times after 6 seconds.
Similarly, consider the case of a single click:
Enter the `#slider` [stopLoop]
Enter the `.arrows` [stopLoop]
Click the arrow [stopLoop]
[startSlider]
Leave the `.arrows` [startSlider]
Leave the `#slider` [startSlider]
As you can see from this sequence of events, startSlider is called 3 times in a row without calling stopLoop inbetween. The result is 3 intervals created, 2 of which will not be stopped the next time stopLoop is called.
You should just have this hover on the #slider and more importantly, add a call to stopLoop as the first step in startSlider. That will ensure that the interval is always cleared before creating a new one.
$("#slider").hover(function() {
stopLoop()
}, function() {
startSlider()
});
function startSlider(){
stopLoop();
/* start the slider */
}

Simulating Spinner Number with mousedown

I made a control (numeric spinner up and down), to work in a table:
JSFIDDLE: http://jsfiddle.net/Leandro1981/wn8vd/1/
and I want simulate the "mousedown, increment while mouse button is helding" but I can't do it. I tried to mix it with the following and functional script:
JSFIDDLE: http://jsfiddle.net/Leandro1981/kKW85/
but I couldn't make it.
My last attempt here:
http://jsfiddle.net/Leandro1981/S8Zt9/1/
Maybe the wrong is the
timeout = setInterval(function () {
But I couldn't figure out. I'm using bootstrap 3, so I can't use some JQuery UI plugins...
Any help will be preciated!
Please comment below if you have any question, comment or anything to improve this question, and sorry for my english :)
Please be free to use my code/control in any way.
Thanks and kind regards
Write a factory to set up each control so you get a closure over the variables, now it's just a matter of being able to make it work given the relevant elements. For this, you'll need to
Listen for mousedown on the up and down nodes to set off the changes
Start a timeout loop to keep doing your change
Listen for mouseup on window to ensure you cancel the timeout loop (you may also want to listen for mouseout/loss of focus)
So all together,
function spinFactory(node, up, down) { // I wrote this vanilla :D
var spinning, delta;
window.addEventListener('mouseup', stopSpin);
function spin() {
node.value = +node.value + delta;
spinning = setTimeout(spin, 500);
}
function stopSpin() { // maybe also invoke this on mouseout/loss of focus
window.clearTimeout(spinning);
delta = 0;
}
up.addEventListener('mousedown', function spinUp() {
delta = 1;
spin();
});
down.addEventListener('mousedown', function spinDown() {
delta = -1;
spin();
});
}
// apply to your control, used a bit of jQuery to make life easier
$('.PNET-spinner').each(function () {
spinFactory(
this.getElementsByTagName('input')[0],
$(this).find('.btn:first-of-type')[0],
$(this).find('.btn:last-of-type')[0]
);
});
DEMO
I have updated the Fiddle here ... Please check this and it might helps you..
Script
$('.PNET-spinner .btn:first-of-type').on('mousedown', function (e) {
var timer, proxy = this;
timer = setInterval(function () {
increment(proxy);
}, 200);
$(document).one("mouseup", function () {
increment(proxy);
if (timer) clearInterval(timer);
});
});
$('.PNET-spinner .btn:last-of-type').on('mousedown', function () {
var timer, proxy = this;
timer = setInterval(function () {
decrement(proxy);
}, 200);
$(document).one("mouseup", function () {
decrement(proxy);
if (timer) clearInterval(timer);
});
});
function increment(proxy) {
var numupdown = $('.PNET-spinner input', $(proxy).closest("tr"));
var inputValue = parseInt($(numupdown).val(), 10);
inputValue++;
$(numupdown).val(inputValue);
}
function decrement(proxy) {
var numupdown = $('.PNET-spinner input', $(proxy).closest("tr"));
var inputValue = parseInt($(numupdown).val(), 10);
if (inputValue > 1) {
inputValue--;
$(numupdown).val(inputValue);
}
}
You simply need to take care of two things. First, your function to increment and decrement the value in the textbox should be called again and again till user do mouseout or mouseup. Second, make surethis has the right value in var numupdown = $('.PNET-spinner input', $(this).closest("tr"));
Following code shows how to do it for the increment button. Similar thing, you can implement for decrement button.
var timeout;
var inc = function () {
var myThis = this;
var numupdown = $('.PNET-spinner input', $(this).closest("tr"));
var inputValue = parseInt($(numupdown).val(), 10);
inputValue++;
console.log(inputValue);
$(numupdown).val(inputValue);
clearTimeout(timeout);
timeout = setTimeout(function(){
//http://stackoverflow.com/questions/3630054/how-do-i-pass-the-this-context-to-a-function
inc.apply(myThis, arguments);
}, 1000);
};
var incStop = function(){
clearTimeout(timeout);
}
$('.PNET-spinner .btn:first-of-type').on('mousedown', inc);
$('.PNET-spinner .btn:first-of-type').on('mouseup', incStop);
$('.PNET-spinner .btn:first-of-type').on('mouseout', incStop);
Check this DEMO here.

Starting and ending functions in javascript

I am playing around with a short little code to see if I can get a function going while the user has their mouse down and then end it when they bring their mouse up. For this example I am trying to increment a number that I am displaying on the screen as the user moves their mouse while holding the button down. I want it to freeze and stop once they release the button, however the counter just resets and the count continues from 0 even though the button is not being pressed...
function dragInit(state, e) {
var i = 0;
$(document).on("mousemove", function() {
if (state) {
i+=1;
$('#debug').text(i); //Show the value in a div
}
});
}
$(document).ready(function() {
$(document).on(
{mousedown: function(e) {
var state = true;
dragInit(e, state);
},
mouseup: function(e) {
var state = false;
dragInit(e, state);
}
});
});
As an aside, is there a way I can display whether a variable is true or false onscreen? When I try it just says [object Object].
There are a lot of mistakes in your code. I suggest you to read more basic concepts before starting to use jQuery.
The order of the parameters passed to dragInit() is wrong on both mouseup and mousedown event bindings.
The reason your counter is restarting is because your variable i is local, so it exists only during the function context it is declared in.
You are making the same mistake with the state variable, but in this case it is completely unnecessary to declare it.
Consider making your counter a global (even though it is not a good practice).
I can't provide you code because I am answering from my phone. A solution would be create a mousemove event that checkes whether the mouse button is pressed before incrementing your counter.
Hope I helped
You could do something like this:
function dragInit() {
$(document).on("mousemove", function () {
if (eventState.state) {
eventState.count += 1;
$('#debug').text(eventState.count); //Show the value in a div
}
});
}
// Create an object to track event variables
var eventState = {
count:0, //replaces your previous 'i' variable
state: false //keeps track of mouseup or mousedown
};
$(document).ready(function () {
$(document).on({
mousedown: function (e) {
eventState.state = true;
dragInit(); //don't need to pass anything anymore
},
mouseup: function (e) {
eventState.state = false;
dragInit(); //don't need to pass anything anymore
}
});
});
jsFiddle
Or keep everything together as one object
var dragInit = function () {
var count = 0;
var state = false;
var action = function () {
$(document).on("mousemove", function () {
if (state) {
count += 1;
$('#debug').text(count); //Show the value in a div
}
})
};
$(document).on({
mousedown: function (e) {
state = true;
action(); //don't need to pass anything anymore
},
mouseup: function (e) {
state = false;
action(); //don't need to pass anything anymore
}
});
}
$(document).ready(function () {
var obj = new dragInit();
});
jsFiddle 2
Example in response to comment
jsFiddle: This shows why the following code snippets differ in execution.
// Works
$(document).on("mousemove", function () {
if (state) {
}
})
// Doesn't
if (state) {
$(document).on("mousemove", function () {
});
}
Less code, You just need this.
Use jquery on and Off to turn on and off mousemove event.
Counter Reset http://jsfiddle.net/kRtEk/
$(document).ready(function () {
var i = 0;
$(document).on({
mousedown: function (e) {
$(document).on("mousemove", function () {
$('#debug').text(i++); //Show the value in a div
});
},
mouseup: function (e) {
i = 0;
$('#debug').text(i);
$(document).off("mousemove");
}
});
});
W/O Reset http://jsfiddle.net/gumwj/
$(document).ready(function () {
var i = 0;
$(document).on({
mousedown: function (e) {
$(document).on("mousemove", function () {
$('#debug').text(i++); //Show the value in a div
});
},
mouseup: function (e) {
$(document).off("mousemove");
}
});
});
WithNoCounter http://jsfiddle.net/F3ESx/
$(document).ready(function () {
$(document).on({
mousedown: function (e) {
$(document).on("mousemove", function () {
$('#debug').data('idx',parseInt($('#debug').data('idx')|0)+1).text($('#debug').data('idx')); //Show the value in a div
});
},
mouseup: function (e) {
$(document).off("mousemove");
}
});
});
Assuming you are married to Jquery (nothing wrong with that) - check out and consider entirely re-thinking your approach leveraging the ".one()" (http://api.jquery.com/one/) method.
edit: and if that taste doesn't sit well - familiarize yourself with the "deferred" object (http://api.jquery.com/category/deferred-object/)
lots of ways to approach this via jquery - what you decide in the end depends on what you really intend to do with this.

How to stop click events from queuing up on multiple click?

What I need to achieve is if we click on submit button, there is particular div should show up.
Here is my code:
http://jsfiddle.net/7tn5d/
But if I click on submit button multiple times, the function calls sort of queue up and run one after other.
Is there a way to invalidate other onclicks when current animation is running?
Code:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", {}, 1000);
animating = 0;
});
});
To prevent it from performing the action multiple times, simple cease the previous animation. So:
$('#submit_cont').stop().show("blind",{},1000);
However, I have noticed that you have attempted to prevent the animation from running, if an animation is already running. Although it takes 1 second or 1000 milliseconds to show the div, the execution of the condition does not pause until the animation is complete. You must define a function to run after the animation is complete, like so:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() { animation = 0; });
});
});
Hope that helped...
You almost got it right with the semaphore! It's just that, in jQuery's show(), you would have to put the semaphore reset as an argument. Here's the fixed version - http://jsfiddle.net/snikrs/xe5A3/
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() {
animating = 0;
});
});
});
You can use the :animated selector to check:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $("#submit_cont");
if (!$cont.is(':animated')) {
$cont.show("blind", {}, 1000);
}
});
});
Now if you stick with the external semaphore idea then its better to stick that on the elemnt with .data() instead of using a global variable:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $('#submit_cont'),
animating = $cont.data('isAnimating');
if (animating) {
return;
} else {
$cont.data('isAnimating', 1);
$("#submit_cont").show("blind", 1000, function() { $cont.data('isAnimating', 0); });
}
});
});
Something like this (see documentation) :)
$("#submit_cont").show("blind", function(){
animating = 0;
});
You can add a $("#submit_cont").clearQueue(); after the animation finished :
$("#submit_tab").click(function (e) {
$("#submit_cont").show("blind", 1000, function() {
$("#submit_cont").clearQueue();
});
});
Updated JSFiddle
I found a different solution for this, which in my opinion looks cleaner:
var tab = $("submit_tag");
tab.on("click", function(){
var cont = $("submit_cont");
var animating = tab.queue("fx").length;
if(animating === 0){
cont.show("blind", {}, 1000);
}
});

A Function to Check if the Mouse is Still Hovering an Element Every 10 Milliseconds (and run a function if it is)

I was wondering if there is a function to be run after an element (e.g. div class="myiv") is hovered and check every X milliseconds if it's still hovered, and if it is, run another function.
EDIT: This did the trick for me:
http://jsfiddle.net/z8yaB/
For most purposes in simple interfaces, you may use jquery's hover function and simply store in a boolean somewhere if the mouse is hover. And then you may use a simple setInterval loop to check every ms this state. You yet could see in the first comment this answer in the linked duplicate (edit : and now in the other answers here).
But there are cases, especially when you have objects moving "between" the mouse and your object when hover generate false alarms.
For those cases, I made this function that checks if an event is really hover an element when jquery calls my handler :
var bubbling = {};
bubbling.eventIsOver = function(event, o) {
if ((!o) || o==null) return false;
var pos = o.offset();
var ex = event.pageX;
var ey = event.pageY;
if (
ex>=pos.left
&& ex<=pos.left+o.width()
&& ey>=pos.top
&& ey<=pos.top+o.height()
) {
return true;
}
return false;
};
I use this function to check that the mouse really leaved when I received the mouseout event :
$('body').delegate(' myselector ', 'mouseenter', function(event) {
bubbling.bubbleTarget = $(this);
// store somewhere that the mouse is in the object
}).live('mouseout', function(event) {
if (bubbling.eventIsOver(event, bubbling.bubbleTarget)) return;
// store somewhere that the mouse leaved the object
});
You can use variablename = setInterval(...) to initiate a function repeatedly on mouseover, and clearInterval(variablename) to stop it on mouseout.
http://jsfiddle.net/XE8sK/
var marker;
$('#test').on('mouseover', function() {
marker = setInterval(function() {
$('#siren').show().fadeOut('slow');
}, 500);
}).on('mouseout', function() {
clearInterval(marker);
});​
jQuery has the hover() method which gives you this functionality out of the box:
$('.myiv').hover(
function () {
// the element is hovered over... do stuff
},
function () {
// the element is no longer hovered... do stuff
}
);
To check every x milliseconds if the element is still hovered and respond adjust to the following:
var x = 10; // number of milliseconds
var intervalId;
$('.myiv').hover(
function () {
// the element is hovered over... do stuff
intervalId = window.setInterval(someFunction, x);
},
function () {
// the element is no longer hovered... do stuff
window.clearInterval(intervalId);
}
);
DEMO - http://jsfiddle.net/z8yaB/
var interval = 0;
$('.myiv').hover(
function () {
interval = setInterval(function(){
console.log('still hovering');
},1000);
},
function () {
clearInterval(interval);
}
);

Categories