Custom cursor outside of browser window - javascript

I have a element on my website which is freely resizable. This is done by 4 handles on the edges. On hovering these handles and while resizing the element I want to show the respective resize arrows.
Currently I implemented this behavior by setting the css cursor style of the body/root to these arrows. The problem about it is the limit to the client area of the browser window. It would be visually more consistent and less confusing, if the arrow cursor would be visible everywhere while the mouse is hold down.
Google Maps is doing the same thing with their hand cursor while moving the map. So my question is how to achive this effect on my own.
My current (relevant) source:
function startObjectScaling(e){
e.stopPropagation();
e.preventDefault();
document.documentElement.style.cursor = this.style.cursor;
window.addEventListener("mouseup", stopObjectScaling, false);
}
function stopObjectScaling(e){
e.stopPropagation();
document.documentElement.style.cursor = '';
window.removeEventListener("mouseup", stopObjectScaling);
}
[...]
var tg = document.getElementById("transformGadget");
var handle = tg.firstChild.nextSibling;
for(var i=0;i<4;i++){
handle.addEventListener("mousedown", startObjectScaling, false);
handle = handle.nextSibling;
}

There is a special function implemented in the more modern browsers for this purpose. The name is setCapture(). It redirects all mouse input to the object the method was called on. Now a simple css cursor definition on that element is enough to archive the desired effect. After mouse release this effect stops (for security for sure). It can also be stopped manually by calling releaseCapture
example:
<style type="text/css">
#testObj {
/* this cursor will also stay outside the window.
could be set by the script at the mousedown event as well */
cursor: hand;
}
</style>
[...]
document.getElementById('testObj').onmousedown = function(e){
// these 2 might be useful in this context as well
//e.stopPropagation();
//e.preventDefault();
// here is the magic
e.target.setCapture();
}

if the arrow cursor would be visible everywhere while the mouse is hold down.
You're relying on a potential OS quirk to create your behavior. This is not something you can ASSUME will always hold true. However, once you start a mousedown, the cursor at that point will normally stay the same, no matter where you move the mouse to, UNTIL something else (another window that you may mouse over? the desktop? a system-interrupt?) changes the cursor.
In other words, don't rely on this behavior. Find something else that will work for you. If you must do this, re-examine your business requirements.

Related

How to prevent page scrolling when pinch-zooming with Pointer Event?

I've got functionality that allows to zoom images in and out, and it works properly if there's no scrollable parent element.
But when there is a scrollable parent, it doesn't work properly.
The thing is touch-action: none can't be used directly, because it prevents a page from scrolling, but I want to allow users to scroll the page only if one finger is down, and allow to zoom an image if two fingers are down.
It's strange, but the code below wouldn't work:
let fingerCount; // Assume that it has the right value
element.addEventListener("pointerdown", e => {
if (fingerCount === 2) {
// This line will be ignored
element.style.touchAction = "none";
}
});
Is there a way to combine page scrolling and pinch-zooming?
Oh, I was young and silly)) I had to do this:
// This line has to be outside of `pointerdown`
element.style.touchAction = "pan-y";

Dragging elements

Im just wondering whether its possible to drag images/elements (whether inside a div or not) where the image/element is dragged to the far left and comes in from the right side of the screen - therefore dragging the image/element left to get the same result, or vice versa.
Similar to the google maps (on zoom level 1) the user can continuously drag the image left or right, and those images are on a continuous loop.
If so, what languages would you recommend using? javascript?
I hope this makes sense.
Many thanks
This is certainly possible using Javascript. Just have two copies of the image, and as you slide the images left, move the second copy to the right side. (Or vice versa if sliding right).
You can add event handlers to any element, including your images, that execute code while dragging. I apologize for not providing a complete working solution, I am pressed for time, I hope this helps as a starting point
var myGlobals = {
state: {
dragging: false;
}
};
$('.carouselImg').on('mousedown', function(e){
myGlobals.state.dragging = true;
});
$(window).on('mouseup', function(e){
// stop movement even if mouseup happens somewhere outside the carousel
myGlobals.state.dragging = false;
});
$(window).on('mousemove', function(e){
// allow user to drag beyond confines of the carousel
if(myGlobals.state.dragging == true){
recalculateCarouselPosition(e);
}
});
function recalculateCarouselPosition(e){
// These fun bits are left as an exercise for the reader
// the event variable `e` gives you the pixel coordinates where the mouse is
// You will have to do some math to determine what you need to do after this drag.
// You may want to store the coordinates of the initial click in
// `myGlobals.dragStart.x, .y` or similar so you can easily compare them
// You will probably set a negative CSS margin-left property on some element
// of your carousel.
// The carousel will probably have overflow:hidden.
// You will also have to manipulate the DOM, by adding a duplicate image to the
// opposite end and deleting the duplicates once they have scrolled out of view
};

FadeIn Div at Mouse Cursor on Keypress [Javascript/JQuery]

I'm trying to make a dynamic javascript menu (contained in a div) appear at a visitor's mouse coordinates when they press ctrl+m on their keyboard (m signifying menu, and ctrl+m not being bound in most browsers as far as I'm aware). This way, wherever they are on my site and wherever their mouse is, they can pull up the menu by just pressing that combination and return to wherever they wish to go. At the same time, having the menu not shown until they press the key allows me to control the design experience completely without having to worry about a nav menu.
I've pulled together two different pieces of code I found on here in an attempt to do this myself, but I'm running into an unexpected issue.
I'm not sure how to denote the ctrl+m key combination in the event handler.
I'm getting a couple of errors on the code checker that I'm not sure how to fix myself.
I'm not sure how to make it so that the menu appears on ctrl+m, and stays there until ctrl+m is pressed again (a toggle switch).
I'm still learning how Javascript works.
Here's the link to the progress I've made so far: http://jsfiddle.net/nrz4Z/
In your example, you're binding the mousemove handler inside the keypress handler. You need to do them separately:
var mouseX;
var mouseY;
$(document).ready(function () {
$(document).mousemove(function (e) {
mouseX = e.pageX;
mouseY = e.pageY;
});
$(document).keypress(function (event) {
if (event.which == 109) {
$('#examples').css({
'top': mouseY,
'left': mouseX
}).fadeIn('slow');
};
});
});
That should allow you to get the position at which to show the menu.
Firstly the use of keypress is not a good idea - web is something that is on an unknowable amount of browsers and devices and plugins and you don't know what shortcuts are bound to especially ones using modifiers with a single key (in this case cmd+m or ctrl+m is an OS shortcut to minimise the window on many OSes. ctrl exists as cmd in os x and not at all on phones.)
To detect multiple key presses check here: Can jQuery .keypress() detect more than one key at the same time?
Next you can detect mouse movements and store them in a variable for use anywhere: How to get mouse position in jQuery without mouse-events?
Your menu should then be at the bottom of the DOM with only body as it's parent:
<nav>
<p>My Menu</p>
<nav>
</body>
Your nav should have whatever styling it needs in the css, as well as:
nav {
position: absolute;
display: none;
/*z-index: 700; nav is at bottom of dom so it will go above anything without a z-index but you may want it to go over other things */
}
When you have detected your key-presses you should do:
$('nav').css({top: mouseYCoord, left: mouseYCoord}).show();
Obviously give your menu a more useful name and don't select upon all 'nav' tags.

Global override of mouse cursor with JavaScript

In my web application I try to implement some drag and drop functionality. I have a global JavaScript component which does the the basic stuff. This object is also responsible for changing the mouse cursor, depending of the current drag operation (move, copy, link). On my web page there are various HTML elements which define an own cursor style, either inline or via a CSS file.
So, is there a way for my central drag and drop component to change the mouse cursor globally, independent from the style of the element the mouse cursor is over?
I tried:
document.body.style.cursor = "move"
and
document.body.style.cursor = "move !important"
But it doesn't work. Every time I drag over an element which defines a cursor style, the cursor changes to that style.
Sure, I could change the style of the element I'm currently dragging over, but then I have to reset it when I leave the element. This seems a little bit to complicated. I'm looking for a global solution.
Important Update (2021):
The MDN page for element.setCapture() clearly indicates that this feature is deprecated and non-standard, and should not be used in production.
The browser support table at the bottom of that page indicates that it's only supported in Firefox and IE.
Original answer below
Please: don't massacre your CSS!
To implement a drag and drop functionality, you have to use a very important API: element.setCapture(), which does the following :
All mouse events are redirected to the target element of the capture, regardless of where they occured (even outside the browser window)
The cursor will be the cursor of the target element of the capture, regardless where the mouse pointer is.
You have to call element.releaseCapture() or document.releaseCapture() to switch back to normal mode at the end of the operation.
Beware of a naïve implementation of drag and drop: you can have a lot of painful issues, like for example (among others): what happens if the mouse is released outside the browser's window, or over an element which has a handler that stops propagation. Using setCapture() solves all this issues, and the cursor style as well.
You can read this excellent tutorial that explains this in detail if you want to implement the drag and drop yourself.
Maybe you could also use jQuery UI draggable if possible in your context.
I tried using setPointerCapture which worked great. The downside is, that (of cause) all pointer events will not work as before. So I lost hover styles etc.
My solution now is pretty straight forward and for my usecase better suited then the above CSS solutions.
To set the cursor, I add a new stylesheet to head:
const cursorStyle = document.createElement('style');
cursorStyle.innerHTML = '*{cursor: grabbing!important;}';
cursorStyle.id = 'cursor-style';
document.head.appendChild(cursorStyle);
To reset it, I simply remove the stylesheet:
document.getElementById('cursor-style').remove();
document.body.style.cursor = "move"
should work just fine.
However, I recommend to do the global styling via CSS.
define the following:
body{
cursor:move;
}
The problem is, that the defined cursors on the other elements override the body style.
You could do someting like this:
your-element.style.cursor = "inherit"; // (or "default")
to reset it to the inherited style from the body or with CSS:
body *{
cursor:inherit;
}
Note however, that * is normally considered a bad selector-choice.
Unfortunately element.setCapture() does not work for IE
I use a brute force approach - open a transparent div on top of entire page for the duration of drag-drop.
.tbFiller {
position:absolute;
z-index:5000;
left:0;
top:0;
width:100%;
height:100%;
background-color:transparent;
cursor:move;
}
...
function dragStart(event) {
// other code...
document.tbFiller=document.createElement("div");
document.tbFiller.className="tbFiller"
}
function dragStop(event) {
// other code...
document.tbFiller.parentNode.removeChild(document.tbFiller);
}
Thanks to some of the other answers here for clues, this works well:
/* Disables all cursor overrides when body has this class. */
body.inheritCursors * {
cursor: inherit !important;
}
Note: I didn't need to use <html> and document.documentElement; instead, <body> and document.body work just fine:
document.body.classList.add('inheritCursors');
This causes all descendant elements of <body> (since it now has this inheritCursors class) to inherit their cursor from <body> itself, which is whatever you set it to:
document.body.style.cursor = 'progress';
Then to yield back control to the descendant elements, simply remove the class:
document.body.classList.remove('inheritCursors');
and to unset the cursor on the <body> to the default do:
document.body.style.cursor = 'unset';
This is what I do and it works in Firefox, Chrome, Edge and IE as of 2017.
Add this CSS rule to your page:
html.reset-all-cursors *
{
cursor: inherit !important;
}
When the <html> element has the "reset-all-cursors" class, this overrides all cursors that are set for elements individually in their style attribute – without actually manipulating the elements themselves. No need to clean up all over the place.
Then when you want to override your cursor on the entire page with that of any element, e. g. the element being dragged, do this in JavaScript:
element.setCapture && element.setCapture();
$("html").addClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", $(element).css("cursor"), "important");
It uses the setCapture function where it is available. This is currently just Firefox although they say it's a Microsoft API. Then the special class is added to the entire document, which disables all custom cursors. Finally set the cursor you want on the document so it should appear everywhere.
In combination with capturing events, this may even extend the dragging cursor to outside of the page and the browser window. setCapture does this reliably in Firefox. But elsewhere it doesn't work every time, depending on the browser, its UI layout, and the path along which the mouse cursor leaves the window. ;-)
When you're finished, clean up:
element.releaseCapture && element.releaseCapture();
$("html").removeClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", "");
This includes jQuery for addClass and removeClass. In simple scenarios you could just plain compare and set the class attribute of document.documentElement. This will break other libraries like Modernizr though. You can get rid of the css function if you know the element's desired cursor already, or try something like element.style.cursor.
A performance-acceptable, but not ideal either, solution that I ended up using, is actually to change the cursor prop of element directly under the pointer, and then return it back to original when pointer moved to another element. It works comparatively fast, as just a few elements change their style while moving pointer around, but visually you might sometimes see a short glimpse of "original" cursor. I consider it a much more acceptable tradeoff.
So, the solution, in TypeScript:
let prevElement: HTMLElement | undefined;
let prevElementOriginalCursor: string | undefined;
export const setTemporaryCursor = (element: HTMLElement, cursor: string | undefined) => {
// First, process the incoming element ASAP
let elementOriginalCursor: string | undefined;
requestAnimationFrame(() => {
if (element && prevElement !== element) {
elementOriginalCursor = element.style.cursor;
element.style.cursor = cursor ?? '';
}
});
// Then process the previous element, not so critical
requestAnimationFrame(() => {
if (prevElement && prevElement !== element) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = elementOriginalCursor;
}
prevElement = element;
});
};
export const resetTemporaryCursor = () => {
if (prevElement) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = undefined;
prevElement = undefined;
}
};
just call setTemporaryCursor while user moves mouse, and call resetTemporaryCursor() when drag process is wrapped up (on MouseUp for instance).
This does the job for me. The use of requestAnimationFrame is optional, and probably could be improved with experimentation.

Is it possible to hide the cursor in a webpage using CSS or Javascript?

I want to hide the cursor when showing a webpage that is meant to display information in a building hall. It doesn't have to be interactive at all. I tried changing the cursor property and using a transparent cursor image but it didn't solve my problem.
Does anybody know if this can be done? I suppose this can be thought of as a security threat for a user that can't know what he is clicking on, so I'm not very optimistic... Thank you!
With CSS:
selector { cursor: none; }
An example:
<div class="nocursor">
Some stuff
</div>
<style type="text/css">
.nocursor { cursor:none; }
</style>
To set this on an element in Javascript, you can use the style property:
<div id="nocursor"><!-- some stuff --></div>
<script type="text/javascript">
document.getElementById('nocursor').style.cursor = 'none';
</script>
If you want to set this on the whole body:
<script type="text/javascript">
document.body.style.cursor = 'none';
</script>
Make sure you really want to hide the cursor, though. It can really annoy people.
Pointer Lock API
While the cursor: none CSS solution is definitely a solid and easy workaround, if your actual goal is to remove the default cursor while your web application is being used, or implement your own interpretation of raw mouse movement (for FPS games, for example), you might want to consider using the Pointer Lock API instead.
You can use requestPointerLock on an element to remove the cursor, and redirect all mousemove events to that element (which you may or may not handle):
document.body.requestPointerLock();
To release the lock, you can use exitPointerLock:
document.exitPointerLock();
Additional notes
No cursor, for real
This is a very powerful API call. It not only renders your cursor invisible, but it actually removes your operating system's native cursor. You won't be able to select text, or do anything with your mouse (except listening to some mouse events in your code) until the pointer lock is released (either by using exitPointerLock or pressing ESC in some browsers).
That is, you cannot leave the window with your cursor for it to show again, as there is no cursor.
Restrictions
As mentioned above, this is a very powerful API call, and is thus only allowed to be made in response to some direct user-interaction on the web, such as a click; for example:
document.addEventListener("click", function () {
document.body.requestPointerLock();
});
Also, requestPointerLock won't work from a sandboxed iframe unless the allow-pointer-lock permission is set.
User-notifications
Some browsers will prompt the user for a confirmation before the lock is engaged, some will simply display a message. This means pointer lock might not activate right away after the call. However, the actual activation of pointer locking can be listened to by listening to the pointerchange event on the element on which requestPointerLock was called:
document.body.addEventListener("pointerlockchange", function () {
if (document.pointerLockElement === document.body) {
// Pointer is now locked to <body>.
}
});
Most browsers will only display the message once, but Firefox will occasionally spam the message on every single call. AFAIK, this can only be worked around by user-settings, see Disable pointer-lock notification in Firefox.
Listening to raw mouse movement
The Pointer Lock API not only removes the mouse, but instead redirects raw mouse movement data to the element requestPointerLock was called on. This can be listened to simply by using the mousemove event, then accessing the movementX and movementY properties on the event object:
document.body.addEventListener("mousemove", function (e) {
console.log("Moved by " + e.movementX + ", " + e.movementY);
});
If you want to hide the cursor in the entire webpage, using body will not work unless it covers the entire visible page, which is not always the case. To make sure the cursor is hidden everywhere in the page, use:
document.documentElement.style.cursor = 'none';
To reenable it:
document.documentElement.style.cursor = 'auto';
The analogue with static CSS notation is html {cursor:none} (or, depending on what exactly you want * {cursor:none} / :root {cursor:none}).
I did it with transparent *.cur 1px to 1px, but it looks like small dot. :( I think it's the best cross-browser thing that I can do.
CSS2.1 has no value 'none' for 'cursor' property - it was added in CSS3. Thats why it's workable not everywhere.
If you want to do it in CSS:
#ID { cursor: none !important; }
For whole html document try this
html * {cursor:none}
Or if some css overwrite your cursor: none use !important
html * {cursor:none!important}

Categories