PDA

View Full Version : Checking if mouse is moving or not



shachi
06-30-2007, 07:02 PM
Hello everyone,

Does anyone know of a technique which allows you to check if the mouse is moving or not? What I basically need to do is to check if the mouse is moving or not and send an ajax request back to the server or execute a function(as soon as the mouse is not moving). I'd like to do this because sending ajax requests onmousemove would be extremely overloading.

Any help would be greatly appreciated.

Thanking you for your time and patience.

Twey
06-30-2007, 07:55 PM
document.onmousemove = (function() {
var onmousestop = function() {
/* do stuff */
}, thread;

return function() {
clearTimeout(thread);
thread = setTimeout(onmousestop, 500);
};
})();It'll execute the supplied code if the mouse is still for half a second.

shachi
07-02-2007, 06:19 AM
Thanks a lot Twey!! It works. :)

shachi
07-09-2007, 04:36 PM
Sorry to bring up an old post but can you tell me how can I capture events? I tried:



document.onmousemove = (function(e) {
var onmousestop = function() {
/* do stuff */
}, thread;

console.log(e.clientX)

return function() {
clearTimeout(thread);
thread = setTimeout(onmousestop, 500);
};
})();


But it says that e has no properties. Any clue? Thanks again.

mwinter
07-09-2007, 06:10 PM
Sorry to bring up an old post but can you tell me how can I capture events? I tried:



document.onmousemove = (function(e) {
var onmousestop = function() {
/* do stuff */
}, thread;

console.log(e.clientX)

return function() {
clearTimeout(thread);
thread = setTimeout(onmousestop, 500);
};
})();

But it says that e has no properties.

Quite. You need to look more carefully at the code Twey posted: there are three functions involved.

The outer function is not assigned to the onmousemove property. If you look at the end, you should see that this function is actually called immediately. It is the function expression following the return statement that is used; the return value of the outer function is assigned as a listener.



var global = this;
document.onmousemove = function () {
var task;
return function (event) {
if (task) clearTimeout(task);
if ((event = event || global.event)) {
console.log(event.clientX);
task = setTimeout(mousestop, 500);
}
};

function mousestop() {
/* Do stuff when the mouse stops moving. This function will be called as soon as the mouse
* stops moving within the viewport for 500ms or more. Note that movement outside the
* viewport (in other applications, across the desktop, or amongst the browser chrome) will
* not be considered; it cannot be tracked.
*/
}
}();

Hope that helps

jscheuer1
07-09-2007, 06:19 PM
That bit of code is so complex with anonymous functions nested within an anonymous function. It may not be able to be done at (the event may not be able to penetrate to) the level where you have:


console.log(e.clientX)

You could perhaps use the addEventListener and attachEvent methods to do the logging as a separate function. In any case, it isn't clear what event you want to capture and you would need an alternate route to capture it in IE, which doesn't do the 'e' thing. It looks like, the way you have it nested there, you want to capture the mouse moving position, not the mouse stopped position, but logically I would suspect it is the other way around.

jscheuer1
07-09-2007, 07:12 PM
This seems to work out:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript">

function logx(v){
document.getElementById('lx').value=v;
}

function mymove(e){
clearTimeout(mymove.thread);
var e=e? e : window.event, x=e.clientX,
mymousestop=function(){
logx(x);
};
mymove.thread=setTimeout(mymousestop, 500);
}

if ( typeof window.addEventListener != "undefined" )
document.addEventListener( "mousemove", mymove, false );
else if ( typeof window.attachEvent != "undefined" )
document.attachEvent( "onmousemove", mymove );

</script>
</head>
<body>
<input type="text" id="lx">
</body>
</html>

jscheuer1
07-09-2007, 07:26 PM
Even adding the event in the 'normal way' works:


function mymove(e){
clearTimeout(mymove.thread);
var e=e? e : window.event, x=e.clientX,
mymousestop=function(){
logx(x);
};
mymove.thread=setTimeout(mymousestop, 500);
}

document.onmousemove=mymove;

Twey
07-09-2007, 07:29 PM
Or simply:
document.onmousemove = (function() {
var onmousestop = function() {
/* do stuff */
}, thread;

return function() {
clearTimeout(thread);
thread = setTimeout(onmousestop, 500);
console.log((arguments[0] || event).clientX);
};
})();

jscheuer1
07-09-2007, 09:02 PM
Twey, your version is logging the moving mouse position, not the position when and only when it stops, as mine does. However, I'm still not sure which shachi is after.

Twey
07-09-2007, 10:19 PM
Ah, yes, that's true, and this raises an interesting point: perhaps it would be more useful to pass the event object to the onmousestop function:
document.onmousemove = (function() {
var onmousestop = function(e) {
console.log(e.clientX);
/* do stuff */
}, thread;

return function(e) {
e = e || event;
clearTimeout(thread);
thread = setTimeout(function(){onmousestop(e);}, 500);
};
})();

jscheuer1
07-10-2007, 03:47 AM
I had worked this out earlier using your basic approach:


document.onmousemove = (function() {
var onmousestop = function() {
/* do stuff */
console.log(x);
}, thread, x;

return function() {
clearTimeout(thread);
thread = setTimeout(onmousestop, 500);
x=(arguments[0] || event).clientX;
};
})();

Which will actually work. Doing:


console.log(e.clientX);

so deep into the nest of functions as you have, loses the intrinsic properties of the event.

Depending upon if this is a custom job or for wide distribution, using a 'loader' like (but more complete than) I had with attachEvent, etc. would be better for wide use.

Like (added later):


function mymove(e){
clearTimeout(mymove.thread);
var e=e? e : window.event, x=e.clientX,
mymousestop=function(){
console.log(x);
};
mymove.thread=setTimeout(mymousestop, 500);
}


if ( typeof window.addEventListener != "undefined" )
document.addEventListener( "mousemove", mymove, false );
else if ( typeof window.attachEvent != "undefined" )
document.attachEvent( "onmousemove", mymove );
else {
if ( document.onmousemove != null ) {
var oldOnmousemove = document.onmousemove;
document.onmousemove = function ( e ) {
oldOnmousemove( e );
mymove(e);
};
}
else
document.onmousemove = mymove;
}

which can coexist with other onmousemove events on the same page.

Twey
07-10-2007, 08:02 AM
so deep into the nest of functions as you have, loses the intrinsic properties of the event.Actually, it's because it's on a timeout.
Depending upon if this is a custom job or for wide distribution, using a 'loader' like (but more complete than) I had with attachEvent, etc. would be better for wide use.Agreed, but you forgot to fix this in IE. My general-purpose one is:
function addEvent(el, ev, f) {
if(el.addEventListener)
el.addEventListener(ev, f, false);
else if(el.attachEvent) {
var t = function() {
f.apply(el);
};
addEvent.events.push({'element': el, 'event': ev, 'handler': t});
el.attachEvent("on" + ev, t);
} else
if(el['on' + ev]) {
var a = el['on' + ev];
el['on' + ev] = function() {
a.apply(this);
f.apply(this);
};
} else el['on' + ev] = f;
}
addEvent.events = [];

if(typeof window.event !== "undefined")
addEvent(window, "unload", function() {
for(var i = 0, e = addEvent.events; i < e.length; i) {
e[i].element.detachEvent("on" + e[i].event, e[i].handler);
e[i].element['on' + e[i].event] = null;
}
}
);

shachi
07-10-2007, 03:30 PM
Thanks a lot everyone!! I finally managed to get the mouse position while moving and stopped position. Here's what I came up with:



document.onmousemove = function () {
var task,finalX,finalY;
return function (e) {
console.log("currently at: "+e.clientX+" :: "+e.clientY);
if (task) clearTimeout(task);
task = setTimeout(mousestop, 500);
finalX = (arguments[0] || event).clientX;
finalY = (arguments[0] || event).clientY;
};

function mousestop(e) {
console.log('stopped at: '+finalX+" :: "+finalY);
}
}();

jscheuer1
07-10-2007, 03:40 PM
. . . you forgot to fix this in IE. My general-purpose one is . . .

An event assigned in IE isn't always ignored by the garbage collector at page unload. My opinion on that is to test the script in IE to see. If it creates a leak, and actively unloading the event(s) fixes that, fine. But, the leak may be from some other cause(s). Interesting looking code though, I must have a thorough look at it, could be very useful.


Thanks a lot everyone!! I finally managed to get the mouse position while moving and stopped position. Here's what I came up with . . .

Looks reasonable for a specific application/use, glad you are happy with it.

Twey
07-10-2007, 05:41 PM
An event assigned in IE isn't always ignored by the garbage collector at page unload.No, not always. Doesn't hurt to unload them all anyway, though (at least in IE; it prevents caching of the page in Firefox, hence the check for IE first).
Here's what I came up with:Hmm... using a function statement conditionally is dodgy. I think that may cause the function to become global, which will cause issues when using more than one instance of the script. What you've done is basically what John suggested, which I agree is the best solution.

mwinter
07-11-2007, 12:29 AM
Doesn't hurt to unload them all anyway, though (at least in IE; it prevents caching of the page in Firefox, hence the check for IE first).

That seems a little misleading: as I recall, it will affect in-memory caching for history navigation. The document itself, however, will still be physically cached (other circumstances permitting).



Hmm... using a function statement conditionally is dodgy.

:confused: There's only one conditional statement (and no operators) in the code shachi posted, and it has nothing to do with function declarations - or even function expressions.

The code can be improved somewhat, though:



var global = this;
document.onmousemove = function () {
var task, finalX, finalY;
return function (event) {
if (task) clearTimeout(task);
if ((event = event || global.event)) {
finalX = event.clientX;
finalY = event.clientY;
console.log('Currently at: ' + finalX + ' :: ' + finalY);
task = setTimeout(mousestop, 500);
}
};

function mousestop() {
console.log('Stopped at: ' + finalX + ' :: ' + finalY);
}
}();

Twey
07-11-2007, 04:03 PM
That seems a little misleading: as I recall, it will affect in-memory caching for history navigation. The document itself, however, will still be physically cached (other circumstances permitting).Yes, you're right, sorry if I was vague.
There's only one conditional statement (and no operators) in the code shachi posted, and it has nothing to do with function declarations - or even function expressions.Hm? This one here:

function mousestop() {
console.log('Stopped at: ' + finalX + ' :: ' + finalY);
}Is defining it inside another function using a function statement not considered conditionally defined? I thought this wouldn't work in IE.

mwinter
07-11-2007, 05:56 PM
Is defining [mousestop] inside another function using a function statement not considered conditionally defined?

Not according to my definition:


A function is considered to be conditionally defined when the representative function object is created via the evaluation of a function expression, and the evaluation of said expression is influenced by the action of some operator [NB: this includes, for instance, the binary logical operators where the value of the first operand is effectively the condition] or conditional statement.

The function, mousestop fails to qualify as it is created via a function declaration (which can never be conditionally evaluated in ECMAScript), and no operators or conditional statements affect evaluation - and couldn't, anyway.

Some common (and uncommon) conditional function definitions are:



if (foo) bar = function () {/* ... */};

foo = bar ? function () {/* ... */} : null;

foo = foo || function () {/* ... */};

The function, mousestop in previous posts is simply an inner function. Such a function declaration is legal so long as the declaration does not occur within any block statement; the declaration must be a direct "child" of the enclosing function.

The only confusion I would have expected is due to the position of the function declaration: after a return statement. However, this is irrelevant as a function body is searched in full for both function and variable declarations when the execution context is created (the function is called). Execution flow is ignored.



I thought this wouldn't work in IE.

The only thing that should stop shachi's code from working in MSIE is the use of the argument, e. The value will be undefined, therefore attempting to access a property will raise an exception.

Twey
07-11-2007, 06:08 PM
The only confusion I would have expected is due to the position of the function declaration: after a return statement. However, this is irrelevant as a function body is searched in full for both function and variable declarations when the execution context is created (the function is called). Execution flow is ignored.Yes, I knew that one. I got confused with this:
The function, mousestop in previous posts is simply an inner function. Such a function declaration is legal so long as the declaration does not occur within any block statement; the declaration must be a direct "child" of the enclosing function.It makes sense now, thanks.

John, thanks for the correction, I noticed it when I was flicking through my old tabs and was going to edit it, but you got there first :)

svgquestion
07-18-2007, 02:47 PM
Dear List,

I downloaded your "onmousestop" function. It is exactly what I was looking for. Unfortunatelly, I have some problems if I include the function inside a svg document. The function works perfectly in Firefox and in Opera. But nothing happens in Internet Explorer 6 with the Adobe svg viewer v3.3 ?

I think for you it might be easy to solve the problem. Please find below the svg document with the "onmousestop" function.

Thank's for your help and your very good work

Rainer


<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events">

<title>Onmousestop</title>
<script type="text/ecmascript">
<![CDATA[
///////////START MYMOVE FUNCTION
function mymove(evt){
clearTimeout(mymove.thread);
//var evt=evt? evt : window.event;
var x=evt.clientX;
var y=evt.clientY;
mymousestop=function(){
//CALLBACK FUNCTION
sendInfo(x,y);
}
mymove.thread=setTimeout(mymousestop, 1000);
document.getElementById('lx').firstChild.nodeValue ="x="+x+" y="+y;
}
//////////////END MYMOVE FUNCTION
/////////////CALLBACK
function sendInfo(x,y){
document.getElementById('lx').firstChild.nodeValue ="SEND JSON x="+x+" y="+y;
//alert("SEND JSON");
}
/////////////END CALLBACK

]]>
</script>
<defs>
<symbol id="symbolRect" overflow="visible">
<rect x="-3000" y="-3000" width="6000" height="6000" style="fill:rgb(240,65,25);
fill-opacity:0.8;stroke:rgb(0,0,0);stroke-width:300" />
</symbol>
<symbol id="symbolCirc" overflow="visible">
<circle cx="0" cy="0" r="3000" style="fill:rgb(12,166,107);fill-opacity:0.8;
stroke:rgb(0,0,0);stroke-width:300" />
</symbol>
</defs>

<g id="MainFrame" fill="green">
<rect x="-100" y="-100" width="700" height="700" fill="yellow"/>
</g>

<g id="carte" fill="red" onmousemove="mymove(evt)">
<rect x="50" y="50" width="400" height="400" />
</g>

<text id="lx" x="50" y="50" >log function</text>
</svg>

jscheuer1
07-19-2007, 09:38 PM
Actually, it's because it's on a timeout.Agreed, but you forgot to fix this in IE. My general-purpose one is:
function addEvent(el, ev, f) {
if(el.addEventListener)
el.addEventListener(ev, f, false);
else if(el.attachEvent) {
var t = function() {
f.apply(el);
};
addEvent.events.push({'element': el, 'event': ev, 'handler': t});
el.attachEvent("on" + ev, t);
} else
if(el['on' + ev]) {
var a = el['on' + ev];
el['on' + ev] = function() {
a.apply(this);
f.apply(this);
};
} else el['on' + ev] = f;
}
addEvent.events = [];

if(typeof window.event !== "undefined")
addEvent(window, "unload", function() {
for(var i = 0, e = addEvent.events; i < e.length; i) {
e[i].element.detachEvent("on" + e[i].event, e[i].handler);
e[i].element['on' + e[i].event] = null;
}
}
);

I told you I would have a look at this later, and now have. Shouldn't this line:


for(var i = 0, e = addEvent.events; i < e.length; i)

be:


for(var i = 0, e = addEvent.events; i < e.length; i++)

and, I found that in certain cases you should have:


var t = function() {
return f.apply(el);
};

And, I don't think that would hurt those cases where it is not required.

Twey
07-19-2007, 11:23 PM
The former is due to a bug in the Phrase Centre; for the latter, though, I've only my own oversight to blame :) It would originally have been ++i, hence the extra spaces.

jscheuer1
07-20-2007, 05:24 AM
Is that:


++i

a Brit thing? :) Anyways, I forgot to mention again that it is a good looking piece of code. Using return may be useful in the final (add without attaching or listening) section as well:


el['on' + ev] = function() {
return a.apply(this);
return f.apply(this);
};

But, you can only have one return.

Twey
07-20-2007, 10:47 AM
Oh! I think I remember why: returning from an event attached via DOM-2 methods doesn't actually do anything, if I remember correctly. It's necessary to alter the event object to make anything happen. I should thus also pass the arguments:
function addEvent(el, ev, f) {
if(el.addEventListener)
el.addEventListener(ev, f, false);
else if(el.attachEvent) {
var t = function() {
return f.apply(el, arguments);
};
addEvent.events.push({'element': el, 'event': ev, 'handler': t});
el.attachEvent("on" + ev, t);
} else
if(el['on' + ev]) {
var a = el['on' + ev];
el['on' + ev] = function() {
a.apply(this, arguments);
f.apply(this, arguments);
};
} else el['on' + ev] = f;
}
Is that ++i a Brit thing? :)Nope, it's a Mike thing originally :) Apparently it's more efficient in certain old compilers, and it doesn't do any harm anywhere else, so I adopted it as a habit.

// EDIT: added return for IE. See post further down.

svgquestion
07-20-2007, 12:36 PM
Dear John, Twey, jscheuer1, and mwinter,

I just discovered some new replays concerning my email about the onmousestop function in svg with setTimeout and clearTimeout.

Unfortunately, I cannot figure out if your replays concern my email? I simplified the function onmousstop for my svg application (see below). It seems to me, everything works perfect in Firefox, Opera and Batik.

Unfortunately nothing happens using IE6 and the Adobe svg viewer ? Do your replies concern my svg problem ? What should I do with your suggestions ? I attached my event-handler "onmousemove" inside the <g> element ?
Do I have to add it using javascript ?


Please find below my svg document and the simplified function mymove?

Thank you for your help.

Rainer


<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events">

<title>Onmousestop</title>
<script type="text/ecmascript">
<![CDATA[
///////////Global Variable
var myTimer=setTimeout("sendInfo(0,0)", 100);
///////////START MYMOVE FUNCTION
function mymove(evt){
clearTimeout(myTimer);
var x=evt.clientX;
var y=evt.clientY;
myTimer=setTimeout("sendInfo("+x+","+y+")", 1000);
//LOG FUNCTION MOUSE IS MOVING
document.getElementById('lx').firstChild.nodeValue ="x="+x+" y="+y;
}
//////////////END MYMOVE FUNCTION
/////////////CALLBACK
function sendInfo(x,y){
// LOG FUNCTION MOUSE STANDS STILL FOR ONE SECOND
document.getElementById('lx').firstChild.nodeValue ="SEND JSON x="+x+" y="+y;
//alert("SEND JSON");
}
/////////////END CALLBACK
]]>
</script>

<g id="MainFrame" fill="green">
<rect x="-100" y="-100" width="700" height="700" fill="yellow"/>
</g>

<g id="carte" fill="red" onmousemove="mymove(evt)">
<rect x="50" y="50" width="400" height="400" />
</g>

<text id="lx" x="50" y="50" >log function</text>
</svg>

Twey
07-20-2007, 12:41 PM
svgquestion, your issue appears to be one with Adobe SVG Viewer, and as such we have absolutely no idea.

jscheuer1
07-20-2007, 02:08 PM
Oh! I think I remember why: returning from an event attached via DOM-2 methods doesn't actually do anything, if I remember correctly.

Hmm, yes and no, er or true. In the addEventListener model, one must use (if desired and allowed for that event):


event_obj.preventDefault();

to 'return false'. However, if using the attachEvent method, this will throw an error if not tested for before using, so does nothing and the actual return value of the function, however it is established, is used. This is also true when assigning the function directly to the object's event property, as in:


object.onclick=function(){....}

and in:


if(el['on' + ev]) {
var a = el['on' + ev];
el['on' + ev] = function() {
a.apply(this);
f.apply(this);
};
}

When I first mentioned needing the return here in your code:


var t = function() {
return f.apply(el);
};

it wasn't an idle thought, it was from actual experience.

Twey
07-20-2007, 02:48 PM
I see. Code edited, I didn't think it was worth reposting the whole thing for one keyword.

jscheuer1
07-20-2007, 03:44 PM
I see. Code edited, I didn't think it was worth reposting the whole thing for one keyword.

Good enough, except that still doesn't address:


el['on' + ev] = function() {
return a.apply(this);
return f.apply(this);
};

Which, of course cannot be done, at least not like that. But it needs to be addressed if the code is to work as intended with added and/or preexisting assigned functions whose return value is required for proper execution.

Twey
07-20-2007, 04:25 PM
Hmm, perhaps:
el['on' + ev] = function() {
return a.apply(this, arguments)
&& f.apply(this, arguments);
};Thus, if one returns false nothing else happens.

jscheuer1
07-20-2007, 05:27 PM
Hmm, perhaps:
el['on' + ev] = function() {
return a.apply(this, arguments)
&& f.apply(this, arguments);
};Thus, if one returns false nothing else happens.

That could work or it could get dicey, literally. It might be a matter of chance at that point. Testing is required. In IE you should, I think, be able to do:


el['on' + ev] = function() {
a.apply(this, arguments)
f.apply(this, arguments);
if (a.returnValue===false || f.returnValue===false)
return false;
};

If your idea doesn't pan out and there is no other universal solution of some sort, it is just a matter of the designer being aware and setting things up as required in any given situation.

mwinter
07-20-2007, 07:01 PM
<aside>




Is that ++i a Brit thing?

Nope, it's a Mike thing originally :) Apparently it's more efficient in certain old compilers, ...

In C++ (and possibly other languages with similar mechanics), the post-increment and -decrement operators create a temporary object which is the result of the expression when evaluated. The pre-increment and -decrement operators do not as it's the altered value that's used for the result.

As for "old compilers", I'm not sure if that applies: it may be necessary to construct the temporary object so that all side-effects can occur, even if the temporary object itself is immediately destroyed after evaluation of the (expression) statement.
</aside>



Hmm, perhaps:


el['on' + ev] = function() {
return a.apply(this, arguments)
&& f.apply(this, arguments);
};

Thus, if one returns false nothing else happens.

That isn't desirable: even the stopPropagation method is supposed to allow all listeners to complete before stopping event propagation. Instead, use:



el['on' + ev] = function () {
var result = a.apply(this, arguments);
return f.apply(this, arguments) && result;
};

Twey
07-20-2007, 07:26 PM
if (a.returnValue===false || f.returnValue===false)No, this wouldn't happen... the functions would set the return value on event:
el['on' + ev] = function() {
a.apply(this, arguments)
a.returnValue = arguments[0].returnValue;
f.apply(this, arguments);
f.returnValue = arguments[0].returnValue;
return a.returnValue && f.returnValue;
};