Thank you in advance for your help with this.
I'm writing a click event that sets an active state on an element, and then after a couple seconds, removes the active state. This is working fine with the exception that there is some weird behavior happening if you click on the link a few times quickly in a row (menu opens and closes quickly, or doesn't show fully before closing again after a subsequent click). My guess is that clearTimeout really isn't clearing the timer quick enough (or not at all) the way I wrote this. The function is firing though so not sure what's going on with the odd behavior. Any help would be appreciated. My code is below. -Chris
$(document).on('click', '.toggle-edit-panel', function () {
var toggleEditPanelTimeout;
// resets timeout function
function resetEditPanelTimeout() {
clearTimeout(toggleEditPanelTimeout);
}
resetEditPanelTimeout();
// declares what this is and toggles active class
var $this = $(this);
var thisParent = $this.parent();
thisParent.find('.edit-panel').toggleClass('active');
$this.toggleClass('active');
toggleEditPanelTimeout = setTimeout(toggleEditPanelTimeoutFired($this), 2000);
// sets initial timeout function
function toggleEditPanelTimeoutFired(thisLinkClicked) {
toggleEditPanelTimeout = setTimeout(function(){
thisParent.find('.edit-panel').removeClass('active');
$(thisLinkClicked).removeClass('active');
},2000);
}
});
Solution below (Thanks Aroth!):
var toggleEditPanelTimeout;
$(document).on('click', '.toggle-edit-panel', function () {
// resets timeout function
clearTimeout(window.toggleEditPanelTimeout);
// declares what this is and toggles active class
var $this = $(this);
var thisParent = $this.parent();
thisParent.find('.edit-panel').toggleClass('active');
$this.toggleClass('active');
// sets initial timeout function
var theLink = $(this);
window.toggleEditPanelTimeout = setTimeout(function(){
$(theLink).parent().find('.edit-panel').removeClass('active');
$(theLink).removeClass('active');
},2000);
});
You've got a definite order-or-operations problem going on here (among other things):
var toggleEditPanelTimeout; //value set to 'undefined'; happens first
// resets timeout function
function resetEditPanelTimeout() {
clearTimeout(toggleEditPanelTimeout); //still undefined; happens third
}
resetEditPanelTimeout(); //value *still* undefined; happens second
// declares what this is and toggles active class
//...
//the value is assigned when this happens; happens fourth:
toggleEditPanelTimeout = setTimeout(toggleEditPanelTimeoutFired($this), 2000);
As a quick fix, you can simply make the variable global, and revise the code along the lines of:
clearTimeout(window.toggleEditPanelTimeout); //clear the previous timeout
// declares what this is and toggles active class
//...
//schedule a new timeout
window.toggleEditPanelTimeout = setTimeout(toggleEditPanelTimeoutFired($this), 2000);
You'll also likely want to remove that intermediate toggleEditPanelTimeoutFired(thisLinkClicked) function that you're using in order to get the code fully working. For instance:
//schedule a new timeout
var theLink = $(this);
window.toggleEditPanelTimeout = setTimeout(function(){
$(theLink).parent().find('.edit-panel').removeClass('active');
$(theLink).removeClass('active');
}, 2000);
Related
Following code snippet to show the core structure:
EDIT1:
var smthing = 0;
change_name_button.addEventListener('click', function(event){
// some query
// .....
// result from query
data = res.name.coords;
// tried to make the function go to GC
smthing = null;
delete smthing;
smthing = new drawMouseMovement(data);
event.stopPropagation();
}, false);
function drawMouseMovement(data){
// bunch of variables set for drawing function
// .....
// .....
var test = 0;
// draw something to canvas until end "maxLength" is reached
var draw = function() {
if(t < maxLength){
// redraw some stuff to canvas
// .....
}else{
return;
}
}
// function to play pause and replay the drawing on canvas
function playPause(boolPP, boolRP){
if(boolPP){
test = setInterval(draw, 10);
}else{
clearInterval(test);
}
if(boolRP){
// some params being reset
// .....
test = setInterval(draw, 10);
}
}
function play(event){
playPause(true, false);
event.stopPropagation();
}
function pause(event){
playPause(false, false);
event.stopPropagation();
}
function replay(event){
playPause(false, true);
event.stopPropagation();
}
play_button.addEventListener('click', play, false);
pause_button.addEventListener('click', pause, false);
replay_button.addEventListener('click', replay, false);
}
Everytime the change_name_button is clicked drawMouseMovement() is called with new parameters.
Following problem: When the draw function does not reach return before clicking change_name_button again, there are two instances of the same function drawing two different things at the same time. Only the last called instance of the function should be drawing.
I tried deleting the pointer to the function, clearInterval() and removeEventListener. But I don't seem to get any of those to work.
I hope my problem is clear.
Thanks in advance.
EDIT
A simple replication of the problem. Is some_button clicked once, smt is printed every 250ms. Is some_button clicked a second time, smt is printed every 125ms etc. How do I overwrite the first instance of printer() with the next click?
some_button.addEventListener('click', foo, false);
function foo(event) {
printer();
event.stopPropagation();
}
function printer(){
setInterval(function() {
console.log("smt");
}, 250);
}
UPDATE 2
A simple replication of the problem. Is some_button clicked once, smt is printed every 250ms. Is some_button clicked a second time, smt is printed every 125ms etc. How do I overwrite the first instance of printer() with the next click?
No way! Don't kill the function, let it ride. First you need a counter to count the clicks so the function knows it's been clicked the second time. Second, just add extra behavior for when the button is clicked the second time, in this case, you are going to half the time interval.
Ok, when I said JavaScript time is quircky, I meant fu##3d. setInterval needs clearInterval to stop it. What most articles and tutorials fail to say is that the damn setInterval still exists, so making it null will allow you to create a new one. I decided to make a constructor timer.
The demo has a button, click it and it'll print out 'smt' every 2 seconds plus the number of clicks to start a new interval.
The 'Go' button is replaced by a 'Stop' button, once clicked it does as advertised--stops
Click it again and it's the 'Go' button, but this time, it is printing every second now.
Lather, rinse, repeat.
Refactor this demo with some prototype wizardry and you got yourself a class.
PLUNKER - TIMER CONSTRUCTOR
PLUNKER - 1 EVENTLISTENER, MULTIPLE EVENT.TARGETS
SNIPPET
<!DOCTYPE html>
<html>
<head>
<style>
.on { display: block; }
button { display: none; }
</style>
</head>
<body>
<button id="btn1" class="on">Go</button><button id="btn2" class="">Stop</button>
<script>
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var counter = 0;
var timer = new Timer();
btn1.addEventListener('click', xStart, false);
btn2.addEventListener('click', xStop, false);
function xStart(event) {
timer.term;
counter++;
timer.start(counter);
btn1.classList.toggle('on');
btn2.classList.toggle('on');
event.stopPropagation();
}
function xStop(event) {
timer.term();
btn1.classList.toggle('on');
btn2.classList.toggle('on');
event.stopPropagation();
}
function Timer(c){
var self = this;
self.start = function(c){
var t = self.printer(c);
self.interval = setInterval(function() { self.printer(c); },t);
};
self.term = function(){
self.clear = clearInterval(self.interval);
self.null;
};
self.printer = function(x){
var d = (x % 2 === 0) ? 2 : 1;
var t = 2000 / d;
console.log('smt'+x+' t: '+t);
return t;
};
}
</script>
</body>
</html>
UPDATE 1
The following Plunker demonstrates how you can make the only eventListener the element that your buttons all share as the parent.
PLUNKER
OLD
None of your eventListeners() have anything for capturing phase, although I believe it's false by default, try setting it to false anyways.
change_name_button.addEventListener('click', function(event){
// some query
// .....
// result from query
data = res.name.coords;
// tried to make the function go to GC
smthing = null;
delete smthing;
smthing = new drawMouseMovement(data);
event.stopPropagation();
}, false);
^^^^^^^^====== There is the capturing phase`
Use event.stopPropagation(); on the other eventhandlers as well and place close to the end of handler. Don't forget to pass the (event) object.
function play(event){
playPause(true, false);
event.stopPropagation();
}
function pause(event){
playPause(false, false);
event.stopPropagation();
}
function replay(event){
playPause(false, true);
event.stopPropagation();
}
play_button.addEventListener('click', play, false);
pause_button.addEventListener('click', pause, false);
replay_button.addEventListener('click', replay, false);
Also, if that doesn't fix the issue then you need to post the HTML because the layout of your event.target and event.currentTarget might have to be adjusted.
EDIT
Try changing the if else
function drawMouseMovement(data){
// bunch of variables set for drawing function
// .....
// .....
var test = 0;
// draw something to canvas until end "maxLength" is reached
var draw = function() {
if(t < maxLength){
// redraw some stuff to canvas
// .....
}
// when t has reached maxlength, then it should quit?
return false;
}
I have an element:
<b onclick="alert('');" onmouseover="this.style.color='red'; setTimeout('........', 1000);" onmouseout="this.style.color='';">123</b>
I need that when element is mouseovered and after 1 second the mouse cursor continue staying above this element, then onclick() event of this element should start.
In other words, what should be instead of '..............' in onmouseover() event?
window.countdown = setTimeout(function(){this.click();}, 1000);
Additionally, you need to clear the interval in the mouseout handler:
clearTimeout(countdown);
Ideally you would give your element an ID and use the new event registration model:
var e = document.getElementById('myelement');
e.addEventListener('click',function(){
alert('');
});
e.addEventListener('mouseenter',function(){
var self = this;
this.style.color='red';
window.countdown = setTimeout(function(){self.click();}, 1000);
});
e.addEventListener('mouseleave',function(){
this.style.color='';
clearTimeout(countdown);
});
You should start the interval on mouse over event as a global variable to refer on mouse out event to clear it like #Asad said.
<b onclick = "alert()"
onmouseover = "window.countdown = setTimeout(function(){this.click();}, 1000);"
onmouseout = "clearTimeout(countdown)">
123
</b>
You'll have to do some extra work, and this won't work out very well for you inside of inline Javascript. This is all pseudocode so I don't recommend copy/pasting!
// We'll need to create an interval and store it
var timerInterval = {}
// And keep track of how many seconds have elapsed
var timeElapsedInSeconds = 0;
function tick (){
timeElapsedInSeconds++;
if (timeElapsedInSeconds > 0){
// YOUR GREAT CODE HERE
}
// Either way, let's be sure to reset everything.
resetTimer();
}
function hoverOverHandler (){
// Start our timer on hover
timerInterval = window.setInterval(tick, 1000);
}
function resetTimer () {
timeElapsedInSeconds = 0;
window.clearInterval(timerInterval);
}
function hoverOutHandler () {
// Kill timer on hoverout
resetTimer();
}
Ok, I did some trick with dynamic id and this is what came out:
<b style="color:red;" onclick="if(this.style.color!='green'){return false;}else{this.style.color='red';} alert(this.parentNode);" onmouseover="if(this.style.color!='green'){var newID='tmpID_'+Math.floor(Math.random() * (10000000)); if(this.id==''){this.id=newID;} setTimeout('top.document.getElementById(\''+this.id+'\').onclick();',1000); this.style.color='green';}" onmouseout="this.style.color='red';">click</b>
crossbrowsered =)
I am not sure what is wrong with my code here, I've set the variable global but the clear timeout is still not working here.
Thank you,
Kelvin
var myslide = null;
$(document).ready(function () {
var current = null;
$('ul#panel li a').click(function () {
stopAuto();
$('ul#panel li a').removeClass('active');
$(this).addClass('active');
current = $(this).attr('href');
$('#wrapperSlide').clearQueue();
$('#wrapperSlide').scrollTo($(current), 800);
return false;
});
if (current==null)
{
$("ul#panel li").each(function(i){
var counter1 = i+1;
var timer1 = counter1 * 5000;
myslide = setTimeout(function(){
setLoop1(counter1);
},timer1);
});
} else {
$('#wrapperSlide').clearQueue();
return false;
}
});
these are the functions for looping the slide images and stop auto
function setLoop1(counter)
{
var counter4 = counter;
var myID = $('a#'+counter4).attr('href');
$('ul#panel li a').removeClass('active');
$('a#'+counter4).addClass('active');
$('#wrapperSlide').scrollTo($(myID), 800);
}
//function to stop the auto slide
function stopAuto() {
clearTimeout(myslide);
}
You're starting those two timers for each separate <li> element:
$("ul#panel li").each(function (i) {
var counter1 = i + 1;
var myID = $('a#' + counter1).attr('href');
setLoop1(counter1, myID);
});
However, that setLoop1() function uses the same two variables. Each timer is distinct from the others, and the return value from setTimeout() is a distinct value each time you call it. You can't store a bunch of different numbers in a single simple variable.
What you should be doing is storing the timer in a jQuery "data" property on each <li> element. Then your "click" handler can cancel the timeout from that. Or, perhaps you don't need a separate pair of timer functions for each list item.
Two cases are here possible as I see, either the stopAuto() function is not being called or if its is being called, the stopAuto() function is undefined when it is called. So I would suggest to define the stopAuto() function before it is being called and use some debugger tool to see the console.
I have a series of links with a class "bloglink".
They have a click event associated with them - but that is irrelevant at this point. I am trying to cycle through them and trigger the click event every X seconds. This is where I'm at:
$('a.bloglink').each(function(){
var $bl = $(this);
setInterval(function(){
$bl.trigger('click')
},2000);
})
But it just triggers the click event for all of them at once.
Any tips?
You could do something like this:
(function Loop(){
var arry = $("a.bloglink").get();
var traverse = function(){
$(arry.shift()).trigger('click');
if (arry.length)
setTimeout(traverse, 2000);
};
setTimeout(traverse,2000);
})();
You can see it in action here: http://jsfiddle.net/Shmiddty/B7Hpf/
To start it over again, you can just add an else case:
(function Loop(){
var arry = $("a.bloglink").get();
var traverse = function(){
$(arry.shift()).trigger('click');
if (arry.length)
setTimeout(traverse, 2000);
else
Loop(); // Do the whole thing again
};
setTimeout(traverse,2000);
})();
See here: http://jsfiddle.net/Shmiddty/B7Hpf/1/
Create a function that sets the timer to run your code, clears the timer, then calls itself on the next element...
function processNext($current)
{
$h = setInterval(function() {
$current.css('color', 'green');//do your business here
clearTimeout($h);
if ($current.next('a.blah').size()>0)
{
processNext($current.next('a.blah'));
}
}, 750);
}
processNext($('a.blah').eq(0));
See here: http://jsfiddle.net/skeelsave/6xqWd/2/
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);
}
);