It seems that when using two jquery UI droppables that touch each other, the droppable events are not fired correctly. If, while dragging a draggable from over one of the elements to just below it onto the next element, then the out event is fired for the first droppable, but the over event is not fired for the second. If you drop at this point, no drop event is fired.
An example is best. Try this fiddle (tested in IE7,8,9 and Chrome11). Make sure your browser's console log is open. If you drag the draggable over the first row, then slowly drag towards the second row, you'll soon see in the log that the out event for the first row is fired, but the over event for the second row is not. If you drop when this happens, the drop event is not fired.
It seems to just be a 1 pixel line in between the rows that causes the problem. Dragging one more pixel causes the over event to be fired, and the drop event to work correctly.
This looks like a bug to me, but I can't find anyone else who has used table rows as droppables and has reported the problem. I styled the table so you can see that the rows are indeed flush together with no space in between.
This is a big problem for me because in our app, the table rows are nested greedy droppables. So if the user drops when this happens, the drop is actually picked up by the outer droppable instead.
Also, we give feedback to the user in the draggable helper in the form of an icon and message that changes depending on the droppable you are over. When you drag between rows, it flickers for a moment, as it thinks you are not over any droppable when you actually are.
My questions:
Is there any fix or workaround for this issue?
Should I report this as a bug?
Update
#davin,
We did end up changing the drag function in $.ui.ddmanager to fix the event ordering. Our issue was we have nested greedy droppables. When you moved from one of these nested droppables to the other from bottom to top, the over event would actually fire for the parent last, causing bad things to happen.
So we added logic to basically check if moving from one nested greedy to another, and if so, not fire parent events.
Would it be too much to ask to have you look this over real quick and make sure our logic makes sense? There are two logical changes. If we moved from greedy child to greedy child:
Don't unset parentInstance.greedyChild
Don't fire parentInstance._over event.
Here's the code. See the lines dealing with the isParentStateChanged closure var, which we added:
drag: function(draggable, event) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
var isParentStateChanged = false;
//Run through all droppables and check their positions based on specific tolerance options
$.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
if(this.options.disabled || this.greedyChild || !this.visible) return;
var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
if(!c) return;
var parentInstance;
if (this.options.greedy && !isParentStateChanged) {
var parent = this.element.parents(':data(droppable):eq(0)');
if (parent.length) {
parentInstance = $.data(parent[0], 'droppable');
parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
}
}
// we just moved into a greedy child
if (parentInstance && c == 'isover') {
isParentStateChanged = true;
parentInstance['isover'] = 0;
parentInstance['isout'] = 1;
parentInstance._out.call(parentInstance, event);
}
this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
this[c == "isover" ? "_over" : "_out"].call(this, event);
// we just moved out of a greedy child
if (parentInstance && c == 'isout') {
if (!isParentStateChanged) {
parentInstance['isout'] = 0;
parentInstance['isover'] = 1;
parentInstance._over.call(parentInstance, event);
}
}
});
}
It's not a bug per se, it's a feature. It's all a matter of definitions. You've defined the tolerance of your droppable items to be pointer, which according to the docs is:
pointer: mouse pointer overlaps the droppable
When my mouse pointer is at (10,10) and the top left corner of my box ends at (10,10), is that overlapping? It depends on your definition. jQueryUI's definition is strong inequality, or strong overlap (see the relevant code). That makes sense (to me), since I'm not inside the box if I'm only on the edge, so I wouldn't want an event to fire.
Although if for your purposes you require weak inequality in the overlap condition (i.e. weak overlap), you can modify that line of code in your source, or override it, by adding:
$.ui.isOverAxis = function( x, reference, size ) {
return ( x >= reference ) && ( x <= ( reference + size ) );
};
Working example: http://jsfiddle.net/vwLhD/8/
Be aware that with weak inequality comes other bumps in the road: your out event will fire after your over event, so you might have two over events before a single out has fired. That's not so hard to handle, but you need to make sure you deal with that case.
UPDATE:
It's important to note that if you add the code I pasted above it is going to affect all other ui widgets in the scope of $ if that's important. Maybe subbing $ could avoid that.
In any case, I have a second workaround that will solve the above issue entirely, and now on every mouse movement the pointer is either in or out of every element exclusively:
$.ui.isOverAxis2 = function( x, reference, size ) {
return ( x >= reference ) && ( x < ( reference + size ) );
};
$.ui.isOver = function( y, x, top, left, height, width ) {
return $.ui.isOverAxis2( y, top, height ) && $.ui.isOverAxis( x, left, width );
};
Working example: http://jsfiddle.net/vwLhD/10/
Essentially I've made the upper condition a weak inequality and the lower one a strong one. So the borders are entirely adjacent. Now the events fire almost perfectly. Almost and not entirely because the plugin still loops through the droppables in order, so if I'm dragging from top to bottom the firing order is good because first it detects that I have left the higher element, and then detects that I have entered the lower element, whereas dragging from bottom to top the order of firing is reversed - first it fires entering the higher and only then leaving the lower.
The difference between this and the previous workaround is that even though half the time the order is not good, it all happens in one tick, i.e. over-out or out-over are always together, the user can never get stuck as in the original case and first workaround.
You can further hone this to be absolutely perfect by changing the ui code to loop through the items first according to those that have the mouse over them, and only then the rest (in the $.ui.ddmanager function). That way the mouse leave will always fire first. Alternatively you can swap the order and have the reverse order; whatever suits you better.
That certainly would solve your problem entirely.
Sounds like you might be dropping between rows which would mean you were dropping onto the table. Do you have your table borders collapsed? css border-collapse: collapsed;
I had this problem with a project i was working on.
My solution was to see check how far the draggable was over each droppable. If the draggable is 50% over the top droppable then i assume the user wants to drop on the top droppable.
Similar for the bottom.
To do this i changed $.ui.intersect;
added vars -
hw = droppable.proportions.width / 2, hh = droppable.proportions.height / 2,
lhw = l + hw,
thh = t + hh
then add some if statements
// going down
if(y2 < b && y2 >= thh){}
// going up
if(y1 > t && y1 <= thh){}
// covered
if(y1 <= t && y2 >= b){}
Related
Is it possible to get the mouse position with JavaScript after page loads without any mouse movement event (without moving the mouse)?
Real answer: No, it's not possible.
OK, I have just thought of a way. Overlay your page with a div that covers the whole document. Inside that, create (say) 2,000 x 2,000 <a> elements (so that the :hover pseudo-class will work in IE 6, see), each 1 pixel in size. Create a CSS :hover rule for those <a> elements that changes a property (let's say font-family). In your load handler, cycle through each of the 4 million <a> elements, checking currentStyle / getComputedStyle() until you find the one with the hover font. Extrapolate back from this element to get the co-ordinates within the document.
N.B. DON'T DO THIS.
Edit 2020: This does not work any more. It seems so, that the browser vendors patched this out. Because the most browsers rely on chromium, it might be in its core.
Old answer:
You can also hook mouseenter (this event is fired after page reload, when the mousecursor is inside the page). Extending Corrupted's code should do the trick:
var x = null;
var y = null;
document.addEventListener('mousemove', onMouseUpdate, false);
document.addEventListener('mouseenter', onMouseUpdate, false);
function onMouseUpdate(e) {
x = e.pageX;
y = e.pageY;
console.log(x, y);
}
function getMouseX() {
return x;
}
function getMouseY() {
return y;
}
You can also set x and y to null on mouseleave-event. So you can check if the user is on your page with it's cursor.
What you can do is create variables for the x and y coordinates of your cursor, update them whenever the mouse moves and call a function on an interval to do what you need with the stored position.
The downside to this of course is that at least one initial movement of the mouse is required to have it work. As long as the cursor updates its position at least once, we are able to find its position regardless of whether it moves again.
var cursor_x = -1;
var cursor_y = -1;
document.onmousemove = function(event)
{
cursor_x = event.pageX;
cursor_y = event.pageY;
}
setInterval(check_cursor, 1000);
function check_cursor(){console.log('Cursor at: '+cursor_x+', '+cursor_y);}
The preceding code updates once a second with a message of where your cursor is.
#Tim Down's answer is not performant if you render 2,000 x 2,000 <a> elements:
OK, I have just thought of a way. Overlay your page with a div that
covers the whole document. Inside that, create (say) 2,000 x 2,000
elements (so that the :hover pseudo-class will work in IE 6, see),
each 1 pixel in size. Create a CSS :hover rule for those elements
that changes a property (let's say font-family). In your load handler,
cycle through each of the 4 million elements, checking
currentStyle / getComputedStyle() until you find the one with the
hover font. Extrapolate back from this element to get the co-ordinates
within the document.
N.B. DON'T DO THIS.
But you don't have to render 4 million elements at once, instead use binary search. Just use 4 <a> elements instead:
Step 1: Consider the whole screen as the starting search area
Step 2: Split the search area into 2 x 2 = 4 rectangle <a> elements
Step 3: Using the getComputedStyle() function determine in which rectangle mouse hovers
Step 4: Reduce the search area to that rectangle and repeat from step 2.
This way you would need to repeat these steps max 11 times, considering your screen is not wider than 2048px.
So you will generate max 11 x 4 = 44 <a> elements.
If you don't need to determine the mouse position exactly to a pixel, but say 10px precision is OK. You would repeat the steps at most 8 times, so you would need to draw max 8 x 4 = 32 <a> elements.
Also generating and then destroying the <a> elements is not performat as DOM is generally slow. Instead, you can just reuse the initial 4 <a> elements and just adjust their top, left, width and height as you loop through steps.
Now, creating 4 <a> is an overkill as well. Instead, you can reuse the same one <a> element for when testing for getComputedStyle() in each rectangle. So, instead of splitting the search area into 2 x 2 <a> elements just reuse a single <a> element by moving it with top and left style properties.
So, all you need is a single <a> element change its width and height max 11 times, and change its top and left max 44 times and you will have the exact mouse position.
You could try something similar to what Tim Down suggested - but instead of having elements for each pixel on the screen, create just 2-4 elements (boxes), and change their location, width, height dynamically to divide the yet possible locations on screen by 2-4 recursively, thus finding the mouse real location quickly.
For example - first elements take right and left half of screen, afterwards the upper and lower half. By now we already know in which quarter of screen the mouse is located, are able to repeat - discover which quarter of this space...
Here's my solution. It exports window.currentMouseX and window.currentMouseY properties you can use anywhere. It uses the position of a hovered element (if any) initially and afterwards listens to mouse movements to set the correct values.
(function () {
window.currentMouseX = 0;
window.currentMouseY = 0;
// Guess the initial mouse position approximately if possible:
var hoveredElement = document.querySelectorAll(':hover');
hoveredElement = hoveredElement[hoveredElement.length - 1]; // Get the most specific hovered element
if (hoveredElement != null) {
var rect = hoveredElement.getBoundingClientRect();
// Set the values from hovered element's position
window.currentMouseX = window.scrollX + rect.x;
window.currentMouseY = window.scrollY + rect.y;
}
// Listen for mouse movements to set the correct values
window.addEventListener('mousemove', function (e) {
window.currentMouseX = e.pageX;
window.currentMouseY = e.pageY;
}, /*useCapture=*/true);
}())
Composr CMS Source: https://github.com/ocproducts/composr/commit/a851c19f925be20bc16bfe016be42924989f262e#diff-b162dc9c35a97618a96748639ff41251R1202
The most simple solution but not 100% accurate
$(':hover').last().offset()
Result: {top: 148, left: 62.5}
The result depend on the nearest element size and return undefined when user switched the tab
Yes, It's possible.
If you add "mouseover" event to the document it will fire instantly and you can get the mouse position, of course if mouse pointer was over the document.
document.addEventListener('mouseover', setInitialMousePos, false);
function setInitialMousePos( event ) {
console.log( event.clientX, event.clientY);
document.removeEventListener('mouseover', setInitialMousePos, false);
}
Previously it was possible to read mouse position through window.event but it's deprecated now.
var x = 0;
var y = 0;
document.addEventListener('mousemove', onMouseMove, false)
function onMouseMove(e){
x = e.clientX;
y = e.clientY;
}
function getMouseX() {
return x;
}
function getMouseY() {
return y;
}
I implemented a horizontal/vertical search, (first make a div full of vertical line links arranged horizontally, then make a div full of horizontal line links arranged vertically, and simply see which one has the hover state) like Tim Down's idea above, and it works pretty fast. Sadly, does not work on Chrome 32 on KDE.
jsfiddle.net/5XzeE/4/
You do not have to move the mouse to get the cursor's location. The location is also reported on events other than mousemove. Here's a click-event as an example:
document.body.addEventListener('click',function(e)
{
console.log("cursor-location: " + e.clientX + ',' + e.clientY);
});
Riffing on #SuperNova's answer, here's an approach using ES6 classes that keeps the context for this correct in your callback:
class Mouse {
constructor() {
this.x = 0;
this.y = 0;
this.callbacks = {
mouseenter: [],
mousemove: [],
};
}
get xPos() {
return this.x;
}
get yPos() {
return this.y;
}
get position() {
return `${this.x},${this.y}`;
}
addListener(type, callback) {
document.addEventListener(type, this); // Pass `this` as the second arg to keep the context correct
this.callbacks[type].push(callback);
}
// `handleEvent` is part of the browser's `EventListener` API.
// https://developer.mozilla.org/en-US/docs/Web/API/EventListener/handleEvent
handleEvent(event) {
const isMousemove = event.type === 'mousemove';
const isMouseenter = event.type === 'mouseenter';
if (isMousemove || isMouseenter) {
this.x = event.pageX;
this.y = event.pageY;
}
this.callbacks[event.type].forEach((callback) => {
callback();
});
}
}
const mouse = new Mouse();
mouse.addListener('mouseenter', () => console.log('mouseenter', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove A', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove B', mouse.position));
Not mouse position, but, if you're looking for current cursor postion (for use cases like getting last typed character etc) then, below snippet works fine.
This will give you the cursor index related to text content.
window.getSelection().getRangeAt(0).startOffset
I envision that maybe you have a parent page with a timer and after a certain amount of time or a task is completed, you forward the user to a new page. Now you want the cursor position, and because they are waiting, they aren't necessarily touching the mouse. So track the mouse on the parent page using standard events and pass the last value to the new page in a get or a post variable.
You can use JHarding's code on your parent page so that the latest position is always available in a global variable:
var cursorX;
var cursorY;
document.onmousemove = function(e){
cursorX = e.pageX;
cursorY = e.pageY;
}
This won't help users that navigate to this page by means other than your parent page.
I think i may have a reasonable solution with out counting divs and pixels..lol
Simply use animation frame or a time interval of a function. you will still need a mouse event one time though just to initiate, but technically you position this where ever you like.
Essentially we are tracking a dummy div at all times with out mouse movement.
// create a div(#mydiv) 1px by 1px set opacity to 0 & position:absolute;
Below is the logic..
var x,y;
$('body').mousemove(function( e ) {
var x = e.clientX - (window.innerWidth / 2);
var y = e.clientY - (window.innerHeight / 2);
}
function looping (){
/* track my div position 60 x 60 seconds!
with out the mouse after initiation you can still track the dummy div.x & y
mouse doesn't need to move.*/
$('#mydiv').x = x; // css transform x and y to follow
$('#mydiv)'.y = y;
console.log(#mydiv.x etc)
requestAnimationFrame( looping , frame speed here);
}
I'm trying to develop a menu where I can hover over the icons using my hand and then click on them using a pushing forward movement.
To achieve that, I'm using the velocity my hand on the z-axis, plus the touch zone and the touch distance as you can see in this snippet of code.
var controller = new Leap.Controller({ enableGestures: flag });
controller.on('frame', function(frame) {
if (frame.pointables.length > 0) {
var pointable = frame.pointables[0];
// Params used to navigation and touching on menu interfaces
var touchZone = pointable.touchZone, // None, hovering, or touching
touchDistance = pointable.touchDistance, // [+1, 0, -1]
zNotFinger= pointable.tipVelocity[0], // For the case pointable isnn't a hand
zIndex = pointable.tipVelocity[1], // Index finger velocity on z-axis
zMiddle = pointable.tipVelocity[2], // Middle finger velocity on z-axis
x = pointable.tipPosition[0],
y = pointable.tipPosition[1],
// Getting highest tipVelocity
tempVelocity = zIndex >= zNotFinger ? zIndex : zNotFinger,
velocity = zMiddle > tempVelocity ? zMiddle : tempVelocity;
// The circle is defined as a gesture to go back to homepage
if (frame.hands.length === 1 && origin !== 'home' && frame.gestures.length > 0) {
var gesture = frame.gestures[0],
hand = frame.hands[0],
oneExtended = hand.fingers[1].extended && !hand.fingers[3].extended;
if (gesture.type === 'circle' && oneExtended && gesture.pointableIds.length >= 1) {
window.open('../html/home.html','_self');
}
}
// Sending data...
if (origin === 'home') {
homeHover(x, y, touchZone, touchDistance, velocity);
} else if (origin === 'bio') {
bioHover(x, y, touchZone, touchDistance, velocity);
} else if (origin === 'nature') {
natureHover(x, y, touchZone, touchDistance, velocity);
}
}
});
controller.connect();
}
and then...
if (velocity > 150) {
if ($(".hovered").attr("href") && touchZone === 'touching' && touchDistance <= -0.4) {
window.location.replace($(".hovered").attr("href"));
}
}
The main problem is to accidentally "click" on the links while hovering over the icons or set up the requires too high making difficult to click.
Could anyone give me a hand on that? Maybe new methods that I should use or even a completely different approach.
OBS: I already tried the screenTap and keyTap.
Many thanks!
The too hard or too easy to click is a common problem. The built-in taps have the same problem.
You could explore the stabilizedTipPosition instead of velocity (or in addition to velocity) and make the user move forward a predetermined amount after hovering. Using stabilizedTip position should make it easier for the user to push forward without accidentally moving off the target. Only clicking when the motion is primarily along the z axis should greatly reduce accidental clicks, both those that occur when the user is moving to a target menu item and those that occur when the user is just moving their hands (unrelated to the menu).
Other common approaches to menus include:
Hover to activate -- the cursor shows a countdown indicator and the user just has to hold the cursor over the menu item or button for the requisite amount of time. It works, but is a bit inelegant and makes the user wait a bit for each menu option when they've already made up their mind what to do. This is one of the most common methods and can be seen in many apps in the Leap Motion app store.
Pull-out to activate -- the user hovers over the menu item and moves their finger to "drag" the item to the side to activate. You can see an example of this type of menu in the Sculpting app.
Physical -- the menus and buttons are activated by "touch" (collision) in 3D space. This technique works well in VR-style apps because the user has a better sense of depth perception. It can also work for non-VR 3D apps (with careful design). You could do a hybrid 2D-3D web app where the content is essentially 2D, but the hands are 3D and shown above the content.
There are some menu design guidelines (older) here: https://developer.leapmotion.com/documentation/javascript/practices/Leap_Menu_Design_Guidelines.html
And several examples (not all menus, though): https://developer.leapmotion.com/gallery/category/javascript
I've got a problem in my code, I can move the red one (square1) by clicking on it,what I want to do is to detect a collision when the red one touch the blue one. I want it to change colour when there is a collision...
Here is my code to be more clear:
http://jsbin.com/iFAlIyIv/4/edit
Your basic approach was right, the problems were things like this:
Instead of square1.left, you want square1.offsetLeft. Elements don't have a left property. Same goes for top, width, and height.
Instead of document.getElementById("square2"), you need to select the element by class name, since there's no element with that ID (only an element with that class).
You're calling changecouleur, but you probably meant to call colorswap. Looks like a bit of unfinished refactoring.
You also don't need any of the additional checks after this:
if (bl > ar || br < al) {
return false;
} //overlap not possible
if (bt > ab || bb < at) {
return false;
} //overlap not possible
If neither of these conditions are true, they must overlap.
http://jsbin.com/iFAlIyIv/13/edit
Ok - here is what I am trying to do. I was looking online for a cool timeline that I can purchase - allowing zoom in zoom out, posting of events on it, and so on. However, all the examples I found are either too expensive or just downright useless.
So, I have decided to create my own, but there are two elements that I am having trouble with.
1) Converting the wheel scroll to left-right scrolling (so not up-down). I can't seem to find an easy and quick way to do this.
But, more importantly..
2) I need the area I will be showing the timeline on to automatically expand as I go about my scrolling. So, if I scroll down, it will add an "equivalent" area on the right, and down, on the left. So I was thinking like making an iFrame (already use these) and when you scroll it just adds more "timeline" on the left or the right, loads what ever it needs to load from the DB/list of events, and so on, ad infinitum, thus creating an ever-expanding list of blocks that are time-sized.
If I can do the two things above, then I am set - the rest (loading/positioning) I can figure out - just these two things are eluding my imagination and ability to find an answer.
Basically you need a horizontal infinite scroll script.
Take this plugin I wrote:
$.fn.hScroll = function( options )
{
function scroll( obj, e )
{
var evt = e.originalEvent;
var direction = evt.detail ? evt.detail * (-120) : evt.wheelDelta;
if( direction > 0)
{
direction = $(obj).scrollLeft() - 120;
}
else
{
direction = $(obj).scrollLeft() + 120;
}
$(obj).scrollLeft( direction );
e.preventDefault();
}
$(this).width( $(this).find('div').width() );
$(this).bind('DOMMouseScroll mousewheel', function( e )
{
scroll( this, e );
});
}
Initialize it with:
$('body').hScroll();
Makes your website a horizontally scrollable website.
Your content div must be wider than your body (ex. 3000px).
As for the infinite scrolling effect you pretty much gotta do that your self because I can't know what kind of data you'll input. But I'll explain.
Your children elements in the content div must be floated to left. (every new appended div will not go to new line).
Set an interval to check if the user's scrollLeft position is near the end of the content (just like pinterest and similar site).
function loadNewData(){ /* Your search for data and update here. */ }
setInterval('loadNewData', 500);
search for new data according to your last one with AJAX. When you get new data, append it into your content div (in a div that's floated left, as I wrote previously), and mark it as your last item.
Maybe you could use your ID to mark the last item on it's div.
<div data-id="467" class="item"> // your data here </div>
You can fetch it with
$('.item:last').attr('data-id');
with jQuery.
Is it possible to get the mouse position with JavaScript after page loads without any mouse movement event (without moving the mouse)?
Real answer: No, it's not possible.
OK, I have just thought of a way. Overlay your page with a div that covers the whole document. Inside that, create (say) 2,000 x 2,000 <a> elements (so that the :hover pseudo-class will work in IE 6, see), each 1 pixel in size. Create a CSS :hover rule for those <a> elements that changes a property (let's say font-family). In your load handler, cycle through each of the 4 million <a> elements, checking currentStyle / getComputedStyle() until you find the one with the hover font. Extrapolate back from this element to get the co-ordinates within the document.
N.B. DON'T DO THIS.
Edit 2020: This does not work any more. It seems so, that the browser vendors patched this out. Because the most browsers rely on chromium, it might be in its core.
Old answer:
You can also hook mouseenter (this event is fired after page reload, when the mousecursor is inside the page). Extending Corrupted's code should do the trick:
var x = null;
var y = null;
document.addEventListener('mousemove', onMouseUpdate, false);
document.addEventListener('mouseenter', onMouseUpdate, false);
function onMouseUpdate(e) {
x = e.pageX;
y = e.pageY;
console.log(x, y);
}
function getMouseX() {
return x;
}
function getMouseY() {
return y;
}
You can also set x and y to null on mouseleave-event. So you can check if the user is on your page with it's cursor.
What you can do is create variables for the x and y coordinates of your cursor, update them whenever the mouse moves and call a function on an interval to do what you need with the stored position.
The downside to this of course is that at least one initial movement of the mouse is required to have it work. As long as the cursor updates its position at least once, we are able to find its position regardless of whether it moves again.
var cursor_x = -1;
var cursor_y = -1;
document.onmousemove = function(event)
{
cursor_x = event.pageX;
cursor_y = event.pageY;
}
setInterval(check_cursor, 1000);
function check_cursor(){console.log('Cursor at: '+cursor_x+', '+cursor_y);}
The preceding code updates once a second with a message of where your cursor is.
#Tim Down's answer is not performant if you render 2,000 x 2,000 <a> elements:
OK, I have just thought of a way. Overlay your page with a div that
covers the whole document. Inside that, create (say) 2,000 x 2,000
elements (so that the :hover pseudo-class will work in IE 6, see),
each 1 pixel in size. Create a CSS :hover rule for those elements
that changes a property (let's say font-family). In your load handler,
cycle through each of the 4 million elements, checking
currentStyle / getComputedStyle() until you find the one with the
hover font. Extrapolate back from this element to get the co-ordinates
within the document.
N.B. DON'T DO THIS.
But you don't have to render 4 million elements at once, instead use binary search. Just use 4 <a> elements instead:
Step 1: Consider the whole screen as the starting search area
Step 2: Split the search area into 2 x 2 = 4 rectangle <a> elements
Step 3: Using the getComputedStyle() function determine in which rectangle mouse hovers
Step 4: Reduce the search area to that rectangle and repeat from step 2.
This way you would need to repeat these steps max 11 times, considering your screen is not wider than 2048px.
So you will generate max 11 x 4 = 44 <a> elements.
If you don't need to determine the mouse position exactly to a pixel, but say 10px precision is OK. You would repeat the steps at most 8 times, so you would need to draw max 8 x 4 = 32 <a> elements.
Also generating and then destroying the <a> elements is not performat as DOM is generally slow. Instead, you can just reuse the initial 4 <a> elements and just adjust their top, left, width and height as you loop through steps.
Now, creating 4 <a> is an overkill as well. Instead, you can reuse the same one <a> element for when testing for getComputedStyle() in each rectangle. So, instead of splitting the search area into 2 x 2 <a> elements just reuse a single <a> element by moving it with top and left style properties.
So, all you need is a single <a> element change its width and height max 11 times, and change its top and left max 44 times and you will have the exact mouse position.
You could try something similar to what Tim Down suggested - but instead of having elements for each pixel on the screen, create just 2-4 elements (boxes), and change their location, width, height dynamically to divide the yet possible locations on screen by 2-4 recursively, thus finding the mouse real location quickly.
For example - first elements take right and left half of screen, afterwards the upper and lower half. By now we already know in which quarter of screen the mouse is located, are able to repeat - discover which quarter of this space...
Here's my solution. It exports window.currentMouseX and window.currentMouseY properties you can use anywhere. It uses the position of a hovered element (if any) initially and afterwards listens to mouse movements to set the correct values.
(function () {
window.currentMouseX = 0;
window.currentMouseY = 0;
// Guess the initial mouse position approximately if possible:
var hoveredElement = document.querySelectorAll(':hover');
hoveredElement = hoveredElement[hoveredElement.length - 1]; // Get the most specific hovered element
if (hoveredElement != null) {
var rect = hoveredElement.getBoundingClientRect();
// Set the values from hovered element's position
window.currentMouseX = window.scrollX + rect.x;
window.currentMouseY = window.scrollY + rect.y;
}
// Listen for mouse movements to set the correct values
window.addEventListener('mousemove', function (e) {
window.currentMouseX = e.pageX;
window.currentMouseY = e.pageY;
}, /*useCapture=*/true);
}())
Composr CMS Source: https://github.com/ocproducts/composr/commit/a851c19f925be20bc16bfe016be42924989f262e#diff-b162dc9c35a97618a96748639ff41251R1202
The most simple solution but not 100% accurate
$(':hover').last().offset()
Result: {top: 148, left: 62.5}
The result depend on the nearest element size and return undefined when user switched the tab
Yes, It's possible.
If you add "mouseover" event to the document it will fire instantly and you can get the mouse position, of course if mouse pointer was over the document.
document.addEventListener('mouseover', setInitialMousePos, false);
function setInitialMousePos( event ) {
console.log( event.clientX, event.clientY);
document.removeEventListener('mouseover', setInitialMousePos, false);
}
Previously it was possible to read mouse position through window.event but it's deprecated now.
var x = 0;
var y = 0;
document.addEventListener('mousemove', onMouseMove, false)
function onMouseMove(e){
x = e.clientX;
y = e.clientY;
}
function getMouseX() {
return x;
}
function getMouseY() {
return y;
}
I implemented a horizontal/vertical search, (first make a div full of vertical line links arranged horizontally, then make a div full of horizontal line links arranged vertically, and simply see which one has the hover state) like Tim Down's idea above, and it works pretty fast. Sadly, does not work on Chrome 32 on KDE.
jsfiddle.net/5XzeE/4/
You do not have to move the mouse to get the cursor's location. The location is also reported on events other than mousemove. Here's a click-event as an example:
document.body.addEventListener('click',function(e)
{
console.log("cursor-location: " + e.clientX + ',' + e.clientY);
});
Riffing on #SuperNova's answer, here's an approach using ES6 classes that keeps the context for this correct in your callback:
class Mouse {
constructor() {
this.x = 0;
this.y = 0;
this.callbacks = {
mouseenter: [],
mousemove: [],
};
}
get xPos() {
return this.x;
}
get yPos() {
return this.y;
}
get position() {
return `${this.x},${this.y}`;
}
addListener(type, callback) {
document.addEventListener(type, this); // Pass `this` as the second arg to keep the context correct
this.callbacks[type].push(callback);
}
// `handleEvent` is part of the browser's `EventListener` API.
// https://developer.mozilla.org/en-US/docs/Web/API/EventListener/handleEvent
handleEvent(event) {
const isMousemove = event.type === 'mousemove';
const isMouseenter = event.type === 'mouseenter';
if (isMousemove || isMouseenter) {
this.x = event.pageX;
this.y = event.pageY;
}
this.callbacks[event.type].forEach((callback) => {
callback();
});
}
}
const mouse = new Mouse();
mouse.addListener('mouseenter', () => console.log('mouseenter', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove A', mouse.position));
mouse.addListener('mousemove', () => console.log('mousemove B', mouse.position));
Not mouse position, but, if you're looking for current cursor postion (for use cases like getting last typed character etc) then, below snippet works fine.
This will give you the cursor index related to text content.
window.getSelection().getRangeAt(0).startOffset
I envision that maybe you have a parent page with a timer and after a certain amount of time or a task is completed, you forward the user to a new page. Now you want the cursor position, and because they are waiting, they aren't necessarily touching the mouse. So track the mouse on the parent page using standard events and pass the last value to the new page in a get or a post variable.
You can use JHarding's code on your parent page so that the latest position is always available in a global variable:
var cursorX;
var cursorY;
document.onmousemove = function(e){
cursorX = e.pageX;
cursorY = e.pageY;
}
This won't help users that navigate to this page by means other than your parent page.
I think i may have a reasonable solution with out counting divs and pixels..lol
Simply use animation frame or a time interval of a function. you will still need a mouse event one time though just to initiate, but technically you position this where ever you like.
Essentially we are tracking a dummy div at all times with out mouse movement.
// create a div(#mydiv) 1px by 1px set opacity to 0 & position:absolute;
Below is the logic..
var x,y;
$('body').mousemove(function( e ) {
var x = e.clientX - (window.innerWidth / 2);
var y = e.clientY - (window.innerHeight / 2);
}
function looping (){
/* track my div position 60 x 60 seconds!
with out the mouse after initiation you can still track the dummy div.x & y
mouse doesn't need to move.*/
$('#mydiv').x = x; // css transform x and y to follow
$('#mydiv)'.y = y;
console.log(#mydiv.x etc)
requestAnimationFrame( looping , frame speed here);
}