75

I use this jQuery code to set the mouse pointer to its busy state (hourglass) during an Ajax call...

$('body').css('cursor', 'wait');

and this corresponding code to set it back to normal...

$('body').css('cursor', 'auto');

This works fine... on some browsers.

On Firefox and IE, as soon as I execute the command, the mouse cursor changes. This is the behavior I want.

On Chrome and Safari, the mouse cursor does not visibly change from "busy" to "auto" until the user moves the pointer.

What is the best way to get the reluctant browsers to switch the mouse pointer?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Nosredna
  • 83,000
  • 15
  • 95
  • 122
  • Chrome: This did it for me: http://stackoverflow.com/a/561397/511438 – Valamas Nov 11 '14 at 21:13
  • As noted below by @j-allen, this issue is fixed in Chrome v50+ when the Developer Tools/Console is closed. – ErikusMaximus Aug 14 '17 at 15:36
  • Despite the bugs in Chrome, I [found a solution, which I describe in my answer below](https://stackoverflow.com/a/52665545/604687). – Ninjakannon Oct 05 '18 at 12:28
  • TLDR; Most important thing to know (even 13 years later) is that if you have devtools open it won't revert back to auto until you move the mouse. You can filter css properties by 'cursor' and watch it change to the correct value, but nothing will happen. It's probably related to the different cursor logic required for highlighting elements in devtools mode. – Simon_Weaver May 05 '23 at 20:28
  • 1
    @Simon_Weaver For me (in Firefox 114) it's precisely the opposite. When the devtools are open the cursor gets correctly updated without movement (even in fullscreen) but when in fullscreen with DevTools closed the cursor only updates if it's not inside an imaginary diamond shape with its corners at the screen edge centers, so if the cursor is close to the coners it updates but on the same element closer to the center it fails to update for me. – Lampe2020 Jun 23 '23 at 15:56

14 Answers14

39

It is a bug in both browsers at the moment. More details at both links (in comments as well):

http://code.google.com/p/chromium/issues/detail?id=26723

and

http://code.google.com/p/chromium/issues/detail?id=20717

Korayem
  • 12,108
  • 5
  • 69
  • 56
user213788
  • 646
  • 4
  • 7
  • 4
    Bug reported in 2009, and still unconfirmed. Sad. – StriplingWarrior Oct 05 '11 at 20:51
  • 3
    thanks for the links. The first link has some useful comments, including #87 (https://code.google.com/p/chromium/issues/detail?id=26723#c87) which is the workaround I'm now using – Greg Jackman Jun 03 '13 at 02:17
  • This code works for me. `myDiv.scrollLeft +=1;` `myDiv.scrollLeft -1;` https://code.google.com/p/chromium/issues/detail?id=26723#c32 – Clr Feb 16 '15 at 18:35
  • 12
    For people coming across this now, although the bug was apparently fixed in 2013, it still sometimes occurs when dev tools is open (see the comments on the chromium bug 26723 from Dec 2016). So, close dev tools like your users will have, and then all good :) – Tim Malone Jan 10 '17 at 05:19
  • 1
    @TimMalone This is the specific issue for the dev tools thing. https://bugs.chromium.org/p/chromium/issues/detail?id=676644 – Cobertos Feb 23 '17 at 19:43
  • @TimMalone WOW. Thanks. – Le-roy Staines Jun 27 '17 at 09:50
  • 3
    Still happening in 2017/08/20 chrome 60, with or without dev tools open – Seabizkit Aug 20 '17 at 07:31
  • Still happening for me on v70.0.3538.77 - wow this is really a long time bug. Same as OP, it works fine on FF/IE it is just Chrome that doesn't update cursor until the mouse is moved. – Phil B Oct 30 '18 at 15:09
  • Also seeing this bug on v70.0.3538.7 when devtools is open. Closing and opening devtools again sorts out the problem. – Nick De Beer Nov 07 '18 at 21:07
  • I'm doing this with react, setting the body to "grab". It doesn't update even while moving, only when i rest and do mouse up. At this point it should actually be "default" and it shows in the dev tools, but not in the view. Closing the devtools just straight up fixes the issue! – pailhead Jan 20 '19 at 21:18
  • 1
    2023 still happening in latest Chrome, I'm gonna switch to farming with you ppl – capr Jul 09 '23 at 18:26
38

I would rather do it more elegantly like so:

$(function(){  
  $("html").bind("ajaxStart", function(){  
     $(this).addClass('busy');  
   }).bind("ajaxStop", function(){  
     $(this).removeClass('busy');  
   });  
});

CSS:

html.busy, html.busy * {  
  cursor: wait !important;  
}  

Source: http://postpostmodern.com/instructional/global-ajax-cursor-change/

Korayem
  • 12,108
  • 5
  • 69
  • 56
  • 2
    Korayem - +1. nice clean approach. i nicked it as a replacement for a rather messy implementation that i had cobbled together. thanks!! ;-) – jim tollan Apr 11 '11 at 09:38
  • 2
    for my use case, I had to put this logic into a setTimeout of 1 millisecond to get it to take effect – jedierikb Feb 28 '13 at 19:40
  • 15
    This doesn't actually answer the question. If you don't move the mouse, the cursor doesn't change back to auto when you remove the class. – Greg Jackman Jun 03 '13 at 02:19
  • I like this solution, but I needed to bind the event to $(document), and then add the class to $('html) to get it to work. – Zach Greenberger Jun 21 '13 at 14:53
30

I believe this issue (including the mousedown problem) is now fixed in Chrome 50.

But only if you are not using the developer tools!!

Close the tools and the cursor should immediately respond better.

J. Allen
  • 620
  • 5
  • 12
7

I got inspired from Korayem solution.

Javascript:

jQuery.ajaxSetup({
    beforeSend: function() {
       $('body').addClass('busy');
    },
    complete: function() {
       $('body').removeClass('busy');
    }
});

CSS:

.busy * {
    cursor: wait !important;
}

Tested on Chrome, Firefox and IE 10. Cursor changes without moving the mouse. "!important" is needed for IE10.

Edit: You still have to move cursor on IE 10 after the AJAX request is complete (so the normal cursor appear). Wait cursor appears without moving the mouse..

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
Sergiu
  • 345
  • 7
  • 7
3

Working solution on CodeSandbox

Some of the other solutions do not work in all circumstances. We can achieve the desired result with two css rules:

body.busy, .busy * {
  cursor: wait !important;
}

.not-busy {
  cursor: auto;
}

The former indicates that we are busy and applies to all elements on the page, attempting to override other cursor styles. The latter applies only to the page body and is used simply to force a UI update; we want this rule to be as non-specific as possible and it doesn't need to apply to other page elements.

We can then trigger and end the busy state as follows:

function onBusyStart() {
    document.body.classList.add('busy');
    document.body.classList.remove('not-busy');
}

function onBusyEnd() {
    document.body.classList.remove('busy');
    document.body.classList.add('not-busy');
}

In summary, although we have to change the cursor style to update the cursor, directly modifying document.body.style.cursor or similar does not have the intended effect, on some engines such as Webkit, until the cursor is moved. Using classes to affect the change is more robust. However, in order to reliably force the UI to update (again, on some engines), we have to add another class. It seems removing classes is treated differently from adding them.

Community
  • 1
  • 1
Ninjakannon
  • 3,751
  • 7
  • 53
  • 76
  • Solution does not work for me on Chrome 70. The cursor stays as wait cursor until the mouse is moved. – Phil B Oct 30 '18 at 15:33
  • 1
    @PhilB Strange. It is working for me on Chrome 70.0.3538.77 on Windows 10. I have also successfully deployed this solution across dozens of environments including both Windows and MacOS with no such bug report yet. – Ninjakannon Oct 30 '18 at 15:56
  • I'm on Windows 7, if that could make some difference? :/ – Phil B Oct 30 '18 at 16:00
  • @PhilB It may well do, perhaps hence the variety of solutions. I will keep an eye open for this – Ninjakannon Oct 30 '18 at 16:04
  • 2
    Ok I did a little more testing and it seems (as other mentioned) that I can only reproduce the issue with dev tools open. Good for me I guess. Solution ok anyways. :) – Phil B Oct 30 '18 at 16:16
  • Yes, this works only with the dev-tools closed. – Omiod Sep 14 '22 at 17:22
1

Korayem's solution works for me in 100% cases in modern Chrome, Safari, in 95% cases in Firefox, but does not work in Opera and IE.

I improved it a bit:

$('html').bind('ajaxStart', function() {
    $(this).removeClass('notbusy').addClass('busy');
}).bind('ajaxStop', function() {
    $(this).removeClass('busy').addClass('notbusy');
});

CSS:

html.busy, html.busy * {
    cursor: wait !important;
}

html.notbusy, html.notbusy * {
    cursor: default !important;
}

Now it works in 100% cases in Chrome, Safari, Firefox and Opera. I do not know what to do with IE :(

Ninjakannon
  • 3,751
  • 7
  • 53
  • 76
Felix
  • 11
  • 1
1

First of all, you should be aware that if you have a cursor assigned to any tag within your body, $('body').css('cursor', 'wait'); will not change the cursor of that tag (like me, I use cursor: pointer; on all my anchor tag). You might want to look at my solution to this particular problem first : cursor wait for ajax call

For the problem that the cursor is only updated once the user move the mouse on webkit browsers, as other people said, there is no real solution.

That being said, there is still a workaround if you add a css spinner to the current cursor dynamically. This is not a perfect solution because you don't know for sure the size of the cursor and if the spinner will be correctly positioned.

CSS spinner following the cursor: DEMO

$.fn.extend(
{
    reset_on : function(event_name, callback)
    { return this.off(event_name).on(event_name, callback); }
});

var g_loader = $('.loader');

function add_cursor_progress(evt)
{
    function refresh_pos(e_)
    {
        g_loader.css({
            display : "inline",
            left : e_.pageX + 8,
            top : e_.pageY - 8
        });
    }
    refresh_pos(evt);
    var id = ".addcursorprog"; // to avoid duplicate events

    $('html').reset_on('mousemove' + id, refresh_pos);

    $(window).
    reset_on('mouseenter' + id, function(){ g_loader.css('display', 'inline'); }).
    reset_on('mouseleave' + id, function(){ g_loader.css('display', 'none'); });
}

function remove_cursor_progress(evt)
{
    var id = ".addcursorprog";
    g_loader.css('display', 'none');

    $('html').off('mousemove' + id);
    $(window).off('mouseenter' + id).off('mouseleave' + id);
}

$('.action').click(add_cursor_progress);
$('.stop').click(remove_cursor_progress);

You will need to check if it is a touch device as well var isTouchDevice = typeof window.ontouchstart !== 'undefined';

In conclusion, you better try to add in your page a static spinner or something else that shows the loading process instead of trying to do it with the cursor.

Community
  • 1
  • 1
pmrotule
  • 9,065
  • 4
  • 50
  • 58
0

HERE is my solution:

function yourFunc(){

$('body').removeClass('wait'); // this is my wait class on body you can $('body').css('cursor','auto');
$('body').blur();
$('body').focus(function(e){
$('body')
 .mouseXPos(e.pageX + 1)
 .mouseYPos(e.pageX - 1);
});

}
cestar
  • 19
  • 1
0

I don't think you'll be able to do it.

However, try changing the scroll position; it might help.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 3
    Tried that. Tried lots of alterations. None of them were sufficient kicks to Chrome or Safari. Seems dumb. Why would the user want to touch the mouse if the cursor is showing wait? – Nosredna Nov 12 '09 at 15:40
0

As of jquery 1.9 you should ajaxStart and ajaxStop to document. They work fine for me in firefox. Have not tested in other browsers.

In CSS:

html.busy *
{
   cursor: wait !important;
}

In javaScript:

// Makes the mousecursor show busy during ajax 
// 
$( document )

   .ajaxStart( function startBusy() { $( 'html' ).addClass   ( 'busy' ) } )     
   .ajaxStop ( function stopBusy () { $( 'html' ).removeClass( 'busy' ) } )
-1

Try using the correct css value for the cursor property:

$('body').css('cursor','wait');

http://www.w3schools.com/CSS/pr_class_cursor.asp

PetersenDidIt
  • 25,562
  • 3
  • 67
  • 72
-1

I haven't tried this, but what about if you create a transparent div that is absolutely positioned and fills the viewport just before changing the CSS. Then, when the css is changed on the body, remove the div. This might trigger a mouseover event on the body, which might cause the cursor to update to the latest CSS value.

Again, I haven't tested this, but it's worth a shot.

Adam Raney
  • 413
  • 3
  • 9
  • I've tried many funky things, and the browsers seem to ignore them all. But if I get time, I'll try yours. – Nosredna Nov 18 '09 at 14:54
  • 2
    I have the same problem with a full size overlay too, and the cursor doesn't change, when not moved. – Omiod Mar 01 '10 at 16:34
-2

Hey Guys, I have a nitty gritty solution which works on all browsers. Assumption is protoype library is used. Someone can write this as plain Javascript too. The solution is to have a div on top of all just after you reset the cursor and shake it a little bit to cause the cursor to move. This is published in my blog http://arunmobc.blogspot.com/2011/02/cursor-not-changing-issue.html.

Arun George
  • 78
  • 10
-3

$('*').css('cursor','wait'); will work everywhere on the page including links

Dan Hunex
  • 5,172
  • 2
  • 27
  • 38