Setup
I'm making an HTML page that replaces the cursor with a div element. The Javascript is below. The div element is simply <div id="cursor"/>.
function fixCursor()
{
var cPos = getCursorPosition();
cursor.style="top:" + (cPos.y) + "; left:" + (cPos.x) + "; position:fixed; width:16px; height:16px; background-color:#DDDDDD; box-shadow: 2px 2px 4px rgba(0,0,0,.5); border:2px solid #111111; border-radius:0 100% 100% 100%;";
return;
}
function getCursorPosition(e) {
e = e || window.event;
var cursor = {x:0, y:0};
if (e.pageX || e.pageY) {
cursor.x = e.pageX;
cursor.y = e.pageY;
}
else if (e.screenX || e.ecreenY) {
cursor.x = e.screenX;
cursor.y = e.screenY;
}
else if (e.x || e.y) {
cursor.x = e.x;
cursor.y = e.y;
}
else if (e.clientX || e.clientY) {
var de = document.documentElement;
var b = document.body;
cursor.x = e.clientX;
cursor.y = e.clientY;
}
return cursor;
}
Problem
This only works on Opera, and shows signs of working on IE, but doesn't show the cursor. On Firefox and Chrome, nothing appears. I haven't tried Safari, as I uninstalled it a while ago, but in my experience, its rendering works alot like Chrome, anyway.
In your code, getCursorPosition takes an event object, e. In fixCursor, you are not passing anything in. You should probably make fixCursor take an event object as well and pass it through to getCursorPosition. Then, in your event handler where you're presumably calling fixCursor, pass in the event object passed into your event handler.
Also, you cannot set style equal to a string. You can, however, set style.cssText.
You can add custom cursors with the CSS cursor property, all major browsers but Opera support it to one extent or another. If you want it to work in IE you'll need to use a .cur or .ani cursor file, the other browsers support at minimum .cur, .png, .jpg and .gif.
Alternatively this answer points to a jQuery plugin that might be easier to use than implementing it yourself.
Personally I've never found a situation where I had to use a custom cursor, I usually just use a .png cursor in my stylesheet and leave a sensible default value for the browsers that don't support .png cursors.
Are you planning to do this onmousemove? That's a lot of overhead. In any case, I see a few problems, but I don't know if fixing them alone will get you a cross-browser solution.
First, let's assume you're triggering this function onmousemove, so you have this in your script:
document.onmousemove = fixCursor;
You have to have fixCursor() pass an event object to getCursorPosition() like this:
function fixCursor(e)
{
var cPos = getCursorPosition(e);
...
And you have to explicitly set each style attribute:
cursor.style.cursor = 'none'; // Didn't see you set this
cursor.style.top = cPos.y;
cursor.style.left = cPos.x;
cursor.style.position = "fixed";
A quick test showed me that this worked in Firefox but not IE. Setting position to "absolute" got closer in IE, but not in a useful way.
EDIT: Oh, and it appears you're not properly referencing the "cursor" div in your fixCursor() function. Use
var cursor = document.getElementById('cursor');
Related
I have this event:
$(window).scroll(function(e){
console.log(e);
})
I want to know, how much I have scroll value in pixels, because I think, scroll value depends from window size and screen resolution.
Function parameter e does not contains this information.
I can store $(window).scrollTop() after every scroll and calculate difference, but can I do it differently?
The "scroll value" does not depend on the window size or screen resolution. The "scroll value" is simply the number of pixels scrolled.
However, whether you are able to scroll at all, and the amount you can scroll is based on available real estate for the container and the dimensions of the content within the container (in this case the container is document.documentElement, or document.body for older browsers).
You are correct that the scroll event does not contain this information. It does not provide a delta property to indicate the number of pixels scrolled. This is true for the native scroll event and the jQuery scroll event. This seems like it would be a useful feature to have, similar to how mousewheel events provide properties for X and Y delta.
I do not know, and will not speculate upon, why the powers-that-be did not provide a delta property for scroll, but that is out of scope for this question (feel free to post a separate question about this).
The method you are using of storing scrollTop in a variable and comparing it to the current scrollTop is the best (and only) method I have found. However, you can simplify this a bit by extending jQuery to provide a new custom event, per this article: http://learn.jquery.com/events/event-extensions/
Here is an example extension I created that works with window / document scrolling. It is a custom event called scrolldelta that automatically tracks the X and Y delta (as scrollLeftDelta and scrollTopDelta, respectively). I have not tried it with other elements; leaving this as exercise for the reader. This works in currrent versions of Chrome and Firefox. It uses the trick for getting the sum of document.documentElement.scrollTop and document.body.scrollTop to handle the bug where Chrome updates body.scrollTop instead of documentElement.scrollTop (IE and FF update documentElement.scrollTop; see https://code.google.com/p/chromium/issues/detail?id=2891).
JSFiddle demo: http://jsfiddle.net/tew9zxc1/
Runnable Snippet (scroll down and click Run code snippet):
// custom 'scrolldelta' event extends 'scroll' event
jQuery.event.special.scrolldelta = {
delegateType: "scroll",
bindType: "scroll",
handle: function (event) {
var handleObj = event.handleObj;
var targetData = jQuery.data(event.target);
var ret = null;
var elem = event.target;
var isDoc = elem === document;
var oldTop = targetData.top || 0;
var oldLeft = targetData.left || 0;
targetData.top = isDoc ? elem.documentElement.scrollTop + elem.body.scrollTop : elem.scrollTop;
targetData.left = isDoc ? elem.documentElement.scrollLeft + elem.body.scrollLeft : elem.scrollLeft;
event.scrollTopDelta = targetData.top - oldTop;
event.scrollTop = targetData.top;
event.scrollLeftDelta = targetData.left - oldLeft;
event.scrollLeft = targetData.left;
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = handleObj.type;
return ret;
}
};
// bind to custom 'scrolldelta' event
$(window).on('scrolldelta', function (e) {
var top = e.scrollTop;
var topDelta = e.scrollTopDelta;
var left = e.scrollLeft;
var leftDelta = e.scrollLeftDelta;
// do stuff with the above info; for now just display it to user
var feedbackText = 'scrollTop: ' + top.toString() + 'px (' + (topDelta >= 0 ? '+' : '') + topDelta.toString() + 'px), scrollLeft: ' + left.toString() + 'px (' + (leftDelta >= 0 ? '+' : '') + leftDelta.toString() + 'px)';
document.getElementById('feedback').innerHTML = feedbackText;
});
#content {
/* make window tall enough for vertical scroll */
height: 2000px;
/* make window wide enough for horizontal scroll */
width: 2000px;
/* visualization of scrollable content */
background-color: blue;
}
#feedback {
border:2px solid red;
padding: 4px;
color: black;
position: fixed;
top: 0;
height: 20px;
background-color: #fff;
font-family:'Segoe UI', 'Arial';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='feedback'>scrollTop: 0px, scrollLeft: 0px</div>
<div id='content'></div>
Note that you may want debounce the event depending on what you are doing. You didn't provide very much context in your question, but if you give a better example of what you are actually using this info for we can provide a better answer. (Please show more of your code, and how you are using the "scroll value").
To detemine how many pixels were scrolled you have to keep in mind that the scroll event gets fired almost every pixel that you move. The way to accomplish it is to save the previous scrolled value and compare that in a timeout. Like this:
var scrollValue = 0;
var scrollTimeout = false
$(window).scroll(function(event){
/* Clear it so the function only triggers when scroll events have stopped firing*/
clearTimeout(scrollTimeout);
/* Set it so it fires after a second, but gets cleared after a new triggered event*/
scrollTimeout = setTimeout(function(){
var scrolled = $(document).scrollTop() - scrollValue;
scrollValue = $(document).scrollTop();
alert("The value scrolled was " + scrolled);
}, 1000);
});
This way you will get the amount of scrolled a second after scrolling (this is adjustable but you have to keep in mind that the smooth scrolling that is so prevalent today has some run-out time and you dont want to trigger before a full stop).
The other way to do this? Yes, possible, with jQuery Mobile
I do not appreciate this solution, because it is necessary to include heavy jQuery mobile. Solution:
var diff, top = 0;
$(document).on("scrollstart",function () {
// event fired when scrolling is started
top = $(window).scrollTop();
});
$(document).on("scrollstop",function () {
// event fired when scrolling is stopped
diff = Math.abs($(window).scrollTop() - top);
});
To reduce the used processing power by adding a timer to a Jquery scroll method is probably not a great idea. The visual effect is indeed quite bad.
The whole web browsing experience could be made much better by hiding the scrolling element just when the scroll begins and making it slide in (at the right position) some time after. The scrolling even can be checked with a delay too.
This solution works great.
$(document).ready(function() {
var element = $('.movable_div'),
originalY = element.offset().top;
element.css('position', 'relative');
$(window).on('scroll', function(event) {
var scrollTop = $(window).scrollTop();
element.hide();
element.stop(false, false).animate({
top: scrollTop < originalY
? 0
: scrollTop - originalY + 35
}, 2000,function(){element.slideDown(500,"swing");});
});
});
Live demo here
without jquery
basically what I am looking for is the ability to see if the mouse is over a div when a countdown finishes
if the user is over the div then perform action for that div
onmouseover only triggers when the mouse crosses the threshold of the div, if the mouse hasn't moved it wouldn't trigger, so that wouldn't work
I need to determine if the mouse is currently over a div at a specific point in time, if it has moved or not from the starting point
all of my hunting has only found onmousover, and nothing to see if the mouse just happens to be there to begin with
I don't have the javascript skills to determine overall coords of div, then map mouse coords and see if it fits there... which is what I believe I need to do
After reading the second answer (the one with millions of a elements) on this SO question, I've came up with this method works without moving the mouse on page load, without involving millions of elements.
HTML
<div id=t></div>
CSS
#t {
/* for illustrative purposes */
width: 10em;
height: 5em;
background-color: #0af;
}
#t:hover {
border-top-style: hidden;
}
JavaScript
document.addEventListener('click', function () {
var c = window.getComputedStyle(document.getElementById('t')).getPropertyValue('border-top-style');
if (c === 'hidden') {
alert('Mouse in box');
} else {
alert('Mouse not in box');
}
}, false);
As stated earlier, bind to the finish event of your countdown instead of the click event on the document.
You may also use any CSS style that's changed on :hover, I chose border-top-style as it is conspicuous. If you're using a border, choose something else.
Here's a jsFiddle.
set a flag to true onmouseover and to false onmouseleave. when countdown finishes if flag is true then it is over element.
HTML
<div id="div-name">the section of the code i am working with has a countdown timer, when it reaches 0 i need to know if the mouse is over a specific box</div>
<button id="notification" onclick="javascript: letsCountIt(5);">click to start countdown</button>
JS
window.ev = false;
document.getElementById('div-name').onmouseover = function () {
window.ev = true;
console.log(window.ev);
}
document.getElementById('div-name').onmouseout = function () {
window.ev = false;
console.log(window.ev);
}
window.letsCountIt = function (cdtimer) {
cdtimer--;
document.getElementById('notification').innerHTML = cdtimer;
if (cdtimer == 0) {
if (window.ev === true) {
alert('over');
} else {
alert('not over');
}
} else {
setTimeout(function(){letsCountIt(cdtimer);}, 1000);
}
}
Look into document.elementFromPoint . When you pass an x,y to elementFromPoint, it will return whatever element (or <body>, if no other specific element) is at that point. You can easily check if this element is the element you want.
The problem then is finding out what point your mouse is at. How to get the mouse position without events (without moving the mouse)? seems to say - don't. At least use mouseMove to track the cursor. The linked question gives examples of how to do so. (Look to the lower scoring answers, as the higher ones only got points for being snarky.)
Just want to say that, I think jQuery's mouseenter and mouseleave events would make this a lot easier, but if you can't use them, maybe this will help you.
Depending on how your page is laid out, this may not be too difficult. You can get the position of your element using the following. Quoting from another answer
element.offsetLeft and element.offsetTop are the pure javascript
properties for finding an element's position with respect to its
offsetParent; being the nearest parent element with a position of
relative or absolute
So, if your element is positioned relatively to the body, so far so good (We don't need to adjust anything).
Now, if we attach an event to the document mousemove event, we can get the current coordinates of the mouse:
document.addEventListener('mousemove', function (e) {
var x = e.clientX;
var y = e.clientY;
}, false);
Now we just need to determine if the mouse falls within the element. To do that we need the height and width of the element. Quoting from another answer
You should use the .offsetWidth and .offsetHeight properties. Note
they belong to the element, not .style.
For example:
var element = document.getElementById('element');
var height = element.offsetHeight;
var width = element.offsetWidth;
Now we have all the information we need, and just need to determine if the mouse falls within the element. We might use something like this:
var onmove = function(e) {
var minX = element.offsetLeft;
var maxX = minX + element.offsetWidth;
var minY = element.offsetTop;
var maxY = minY + element.offsetHeight;
if(e.clientX >= minX && e.clientX <= maxX)
//good horizontally
if(e.clientY >= minY && e.clientY <= maxY)
//good vertically
}
This code works, but the mouse has to be moved once after page load.
var coords;
var getMouseCoordinates = function (e) {
'use strict';
return {
x: e.clientX,
y: e.clientY
};
};
document.addEventListener('mousemove', function (e) {
coords = getMouseCoordinates(e);
}, false);
document.addEventListener('click', function () {
var divCoords = document.getElementById('t').getBoundingClientRect();
if (coords.x >= divCoords.left && coords.x <= divCoords.right && coords.y >= divCoords.top && coords.y <= divCoords.bottom) {
alert('Mouse in box');
} else {
alert('Mouse not in box');
}
}, false);
You wouldn't bind to the click event of document, but rather the finish event of your countdown.
Here's an example. Try clicking in the output window.
You don't need any coordinates or mouse events, if you know a selector for that element:
if (document.querySelector('#elementSelector:hover')) {
alert('I like it when you touch me!');
}
I am writing a script to allow a client to mouse drag table cell borders and resize columns in a table. So far I have a working model in Firefox but there is a flaw in width measurement that leaves the mouse out of sync when the change gets large. Worse, the script fails in other browsers (opera,safari) or even if I change the browser zoom in Firefox.
function doDrag() {document.body.style.cursor='crosshair';}
function noDrag() {document.body.style.cursor='auto';}
var xpos=0;
var sz=0;
var dragObj = {};
function resizeOn(el)
{
dragObj = document.getElementById(el);
document.addEventListener("mousemove",resize, true);
document.addEventListener("mouseup",resizeOff, true);
}
function resize(ev)
{
if(xpos == 0) {xpos=ev.clientX;}
if(xpos != ev.clientX)
{
sz = dragObj.offsetWidth + (ev.clientX - xpos);
dragObj.style.width = sz - 10 + "px";
alert("size="+sz+" offsetwidth="+dragObj.offsetWidth);
if(dragObj.offsetWidth != sz)
{
resizeOff();
return false;
}
xpos=ev.clientX;
}
}
function resizeOff()
{
xpos = 0;
document.removeEventListener("mousemove",resize, true);
document.removeEventListener("mouseup",resizeOff, true);
}
The HTML looks like:
<th id="col0" class="edit">client</th>
<th class="drag" onmouseover="doDrag()" onmouseout="noDrag()" onmousedown="resizeOn('col0')"></th>
The second cell is made to appear as the right edge of the first.
I assume the problem is dragObj.style.width = sz - 10. The -10 was derived purely by trial and error. I suspect this is the difference between the actual width of the cell including borders, padding etc and offsetwidth. It should really be, per my css, 10 for padding + 1 for the left border = 11px. Either my fixed padding/borders aren't staying fixed or there is some other css property between the offsetWidth and the actual with of the element. Is there some way to get the actual width of the element regardless of the browsers scaling?
Have a look at the documentation in www.quirksmode.org and/or http://help.dottoro.com. Confirm whether the properties are supported by your target browser. They also have comments on how zoom affects offsetX and similar.
Also, you should note that ev.clientX has been broken in IE by a recent patch (KB2846071). If the patch is installed, clientX returns a meaningless result.
Hopefully MS will release a patch for their patch!
I'd like to get the X, Y coords relative to the canvas (on mouse-move event).
I'm using this:
<canvas id='c' onmousemove="xy(event)" WIDTH=500 HEIGHT=300>
js:
function xy(e)
{
x = e.offsetX||e.layerX;
y = e.offsetY||e.layerY;
}
This works well in Chrome but dont in Firefox. If the canvas element is in 0,0 position it works also in Firefox.
Take a look at this: http://jsbin.com/ozowaz/10
What can I do to make it working in FF too?
The container must have
position: relative;
style. Don't use that container for styling border, because it means +n px (n= boder size) in coordinates!
thanks to:
http://dev.opera.com/articles/view/html5-canvas-painting/
You could try to compute it:
// ...
var x = e.pageX - canvas.offsetTop;
where canvas is the canvas dom element.
That should work in FF.
This worked for me (pure js)
function getX(event, canvas){
if(event.offsetX){
return event.offsetX;
}
if(event.clientX){
return event.clientX - canvas.offsetLeft;
}
return null;
}
function getY(event, canvas){
if(event.offsetY){//chrome and IE
return event.offsetY;
}
if(event.clientY){// FF
return event.clientY- canvas.offsetTop;
}
return null;
}
#2astalavista I can't comment due to reputation, so i'll put this here, eventhough i'm not supposed to do this:
I tried the accepted answer, but it only solves the problem from other elements positioned left/above the canvas. For me it didn't take the canvas' margin into account. I don't know if i did it correctly though.
Also i don't like the idea of a Html sided solution. Somebody changes the Html, forgets the "position: relative;" resulting in a bug that's almost impossible to track down.
I used Nech's solution, but noticed that it doesn't work correctly when i have elements positioned left or above the canvas. It only takes things like the canvas' margin into account.
I worked around with the following code, the Firefox part is still only about 95 - 99% accurate, compared to the Chrome part. But it gets the job done:
function getEventX(event){
if (event.offsetX) {
return event.offsetX;
}
if (event.clientX) {
var currentElement = event.currentTarget;
var offsetLeft = currentElement.offsetLeft;
while(currentElement.parentElement && currentElement.parentElement.offsetLeft){
currentElement = currentElement.parentElement;
offsetLeft += currentElement.offsetLeft;
}
return event.clientX - offsetLeft;
}
return null;
};
function getEventY(event){
if (event.offsetY) {
return event.offsetY;
}
if (event.clientY) {
var currentElement = event.currentTarget;
var offsetTop = currentElement.offsetTop;
while(currentElement.parentElement && currentElement.parentElement.offsetTop){
currentElement = currentElement.parentElement;
offsetTop += currentElement.offsetTop;
}
return event.clientY - offsetTop;
}
return null;
};
I also removed the canvas being handed to the function, you can get the canvas with:
event.currentTarget;
Probably better when handling multiple canvases.
I have made something like a drag-and-drop element with JS.
function Draggable(elm) {
this.d = elm;
this.style.position = "absolute";
elm.onselectstart = elm.ondragstart = function() { return false; }
elm.addEventListener('mousedown', this._start.bindAsEventListener(this), false);
}
Draggable.prototype._start = function (event) {
this.deltaX = event.clientX;
this.deltaY = event.clientY;
if (!this.dm) {
this.dm = document.createElement("div");
this.dm.setAttribute("class", "dragger");
this.dm.onmousemove = this._move.bindAsEventListener(this);
this.dm.onmouseup = this._stop.bindAsEventListener(this);
this.dm.onselectstart = RetFalse;
this.dm.ondragstart = RetFalse;
}
document.body.appendChild(this.dm);
this.lastX = this.lastY = 0;
this.ondragstart();
return false;
}
Draggable.prototype._move = function (event) {
var newx = (event.clientX - this.deltaX);
var newy = (event.clientY - this.deltaY);
if (newx < this.x0) newx = this.x0;
if (newx > this.x1) newx = this.x1;
if (newy < this.y0) newy = this.y0;
if (newy > this.y1) newy = this.y1;
this.d.style.left = newx + "px";
this.d.style.top = newy + "px";
if (window.getSelection) window.getSelection().removeAllRanges(); else document.selection.empty();
return false;
}
Draggable.prototype._stop = function (event) {
document.body.removeChild(this.dm);
return false;
}
The "dragger" is transparent DIV that fills the whole page, to prevent the dragged target from losing capture when mouse moves too fast. (If I could capture the mouse, I would need it.)
.dragger {
cursor:move;
position:absolute;
width:100%;height:100%;
left:0px;top:0px;
margin:0px;padding:0px;
z-index:32767;
background: transparent;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
However, if I:
Press left mouse button on the draggable element
Drag it outside the client area (outside the brower window)
Release mouse button
The element will lose the capture, so that if I move the cursor back,
without having receive a mouse-up event, the element follows the cursor everywhere.
(until you click to make a mouse-up again.)
Just now, I saw it perfectly done on this website: (www.box.net)
Even if you release mouse button outside the browser window, the blue selecting box can still resize when the cursor moves, and disappear when button is released.
But I cannot receive any mousemove or mouseup when cursor is outside.
What API can I use to capture the mouse?
As you can see, I'm using Chrome Browser.
It is said that there's no API like HTMLElement.setCapture in non-IE browser.
This page uses jQuery, but what does jQuery use?
What is the raw javascript Code to do that?
Instead of creating a big, transparent element (dm), bind your mouse events to window.
It gets mouse events everywhere on the page; during dragging you'll keep getting mousemove events even if the cursor goes outside the window, as well as a mouseup if you release the mouse button outside the window.
P.S. If you call .preventDefault() on the mousedown event, the browser won’t select any text and you won’t have to clear the selection on mousemove.
Although it is a little outdated (FF now supports setCapture), I found this article to be extraordinarily helpful. The basis of the fix goes something like this:
var dragTarget = element.setCapture ? element : document; // setCapture fix
I've set up this little example. The javascript is copied straight from a webpage I'm building for a client where it works perfect*. Unfortunately I haven't been able to find a fix for draggable content inside an iframe, so it will still appear broken in Chrome if viewed at jsFiddle, Codepen, etc. You'll have to trust me that it works (or try it out yourself). If anyone knows of a fix for this iframe issue, please share.
*in Chrome, Safari and FF, I haven't tested in Opera or IE yet