Event-bubbling of a draw() function in JS - javascript

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;
}

Related

javascript - setTimeout and clearTimeout issue

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);

onmouseover() to invoke onclick after 1 second?

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 =)

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);
}
);

How can i do button on hold for two second call function1 if less then two second call function2?

I have this button which is not working correctly for hold button for a period (but it works like click only).
Where i was trying to do if the button is hold for greater/equal then 2 seconds then callfunction1, if the button was pressed less then 2 seconds then callfuntion2.
var clickDisabled = false;
function clickLocker() {
/* #Button: 2 seconds */
clickDisabled = true;
setTimeout(function(){clickDisabled = false;}, 2000);
}
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
$('.button').live("click",function()
{
if (clickDisabled) {
alert("locked for 2 second");
return;
}
clickLocker();
});
I think this solution should work. I have not tested it but it should give you the right idea.
var startTime;
function callfunction1() { // you have hold he button for greater then or equal 2 second }
function callfunction2() { // you have hold the button less then 2 second }
function buttonDownEvent() {
var Time = new Date();
startTime = Time.getTime();
}
function buttonUpEvent() {
if(new Date().getTime() - startTime < 2000)
callfunction2()
else
callfunction1()
}
$('.button').live("mousedown",function()
{
buttonDownEvent();
});
$('.button').live("mouseup",function()
{
buttonUpEvent();
});
Listen for both events, mousedown and mouseup, measuring the time between both:
var timeDown;
var timeUp;
$('.button').live("mousedown",function(){
timeDown = event.timeStamp;
});
$('.button').live("mouseup",function(){
timeUp = event.timeStamp;
time = timeUp-timeDown;
if (time>2000){
function1();
}else{
function2();
}
});
please note that event.timeStamp wont work well in firefox. For firefox you can do (new Date).getTime();
You can do this using events to the mouseup and mousedown events and timing the difference between them. Also, you need to remember which element caused the click - if the user released the mouse on a different element then it should just do the "non 2-second hold" function. A JSFiddle showing this working is available here: http://jsfiddle.net/35rw3/6/.
That was a great suggestion from slash. This is how you can do this
var clickstart;
var clickstop;
$("a").on('mousedown', function(e) {
clickstart = e.timeStamp;
}).on('mouseup', function(e) {
clickstop = e.timeStamp- clickstart
if(clickstop >= 2000) two()
else one();
});
Demo
Updates:
It might be necessary to track the mouse movement like #MarkRhodes wrote in his comments. So for that, check this update

Detecting when the mouse is not moving

I am able to find the cursor position. But I need to find out if the mouse is stable. If the mouse wasn't moved for more than 1 minute, then we have to alert the user.
How its possible, are there any special events for this? (Only for IE in javascript)
Set a timeout when the mouse is moved one minute into the future, and if the mouse is moved, clear the timeout:
var timeout;
document.onmousemove = function(){
clearTimeout(timeout);
timeout = setTimeout(function(){alert("move your mouse");}, 60000);
}
Here's a one-and-done function that can check any element for movement:
function mouse (element, delay, callback) {
// Counter Object
element.ms = {};
// Counter Value
element.ms.x = 0;
// Counter Function
element.ms.y = function () {
// Callback Trigger
if ((++element.ms.x) == delay) element.ms.callback(element, element.ms);
};
// Counter Callback
element.ms.callback = callback;
// Function Toggle
element.ms.toggle = function (state) {
// Stop Loop
if ([0, "off"][state]) clearInterval(element.ms.z);
// Create Loop
if ([1, "on"][state]) element.ms.z = setInterval(element.ms.y, 1);
};
// Function Disable
element.ms.remove = function () {
// Delete Counter Object
element.ms = null; return delete element.ms;
};
// Function Trigger
element.onmousemove = function () {
// Reset Counter Value
element.ms.x = -1;
};
// Return
return element.ms;
};
Usage:
mouse(element, delay, callback)
Examples:
Make a video player hide the mouse after 5 seconds when idle and fullscreen
let x = mouse(video, 5000, function (a) {
if (document.webkitIsFullScreen) video.style.cursor = "none";
});
x.toggle(1); addEventListener("mousemove", function () {
video.style.cursor = "auto";
});
Chat Room AFK (45 Seconds) (assuming you have a chat box and a send message function):
let x = mouse(chatBox, (45e3), function (a) {
chatBox.send({ text: chatBox.username + " is AFK.", italic: true });
});
x.toggle(1); x.addEventListener("mousemove", function () {
chatBox.send({ text: chatBox.username + " is no longer AFK", italic: true });
});
Is there not a way to set a timer to start incrementing after every mouse movement event?
If it gets to a minute then pop up the message box, but every time the mouse moves the timer gets reset.
Use a timer that resets its value on mousemove event.
If timer reaches 1 minute --> Do something.
More info on timer here http://www.w3schools.com/js/js_timing.asp
And more info on catchin mouse events here http://www.quirksmode.org/js/events_mouse.html
Yes, you have a onmousemove event in Javascript, so to achieve what you need you just have to do code something like this:
startTimer();
element.onmousemove = stopTimer(); //this stops and resets the timer
You can use it on the document body tag for instance.
UPDATE: #Marius has achieved a better example than this one.
You can use the onmousemove event. Inside it, clearTimeout(), and setTimeout(your_warning, 1 minute).
You could use this script/snippet to detect the mouse pointer position and "remember" it. Then use a timer "setTimeout(...)" to check the position let's say every second and remember that time.
If more than one minute passed and the position hasn't changed, you could alert the user.

Categories