97

Is it possible to set the cursor to 'wait' on the entire html page in a simple way? The idea is to show the user that something is going on while an ajax call is being completed. The code below shows a simplified version of what I tried and also demonstrate the problems I run into:

  1. if an element (#id1) has a cursor style set it will ignore the one set on body (obviously)
  2. some elements have a default cursor style (a) and will not show the wait cursor on hover
  3. the body element has a certain height depending on the content and if the page is short, the cursor will not show below the footer

The test:

<html>
    <head>
        <style type="text/css">
            #id1 {
                background-color: #06f;
                cursor: pointer;
            }

            #id2 {
                background-color: #f60;
            }
        </style>
    </head>
    <body>
        <div id="id1">cursor: pointer</div>
        <div id="id2">no cursor</div>
        <a href="#" onclick="document.body.style.cursor = 'wait'; return false">Do something</a>
    </body>
</html>

Later edit...
It worked in firefox and IE with:

div#mask { display: none; cursor: wait; z-index: 9999; 
position: absolute; top: 0; left: 0; height: 100%; 
width: 100%; background-color: #fff; opacity: 0; filter: alpha(opacity = 0);}

<a href="#" onclick="document.getElementById('mask').style.display = 'block'; return false">
Do something</a>

The problem with (or feature of) this solution is that it will prevent clicks because of the overlapping div (thanks Kibbee)

Later later edit...
A simpler solution from Dorward:

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

and then

<a href="#" onclick="document.body.className = 'wait'; return false">Do something</a>

This solution only shows the wait cursor but allows clicks.

Aleris
  • 7,981
  • 3
  • 36
  • 42
  • As it seems I am not able to change the cursor for the select elements. Is there a way to change the cursor for the select element also ? – Biswanath Sep 08 '09 at 09:53

16 Answers16

114

If you use this slightly modified version of the CSS you posted from Dorward,

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

you can then add some really simple jQuery to work for all ajax calls:

$(document).ready(function () {
    $(document).ajaxStart(function () { $("html").addClass("wait"); });
    $(document).ajaxStop(function () { $("html").removeClass("wait"); });
});

or, for older jQuery versions (before 1.9):

$(document).ready(function () {
    $("html").ajaxStart(function () { $(this).addClass("wait"); });
    $("html").ajaxStop(function () { $(this).removeClass("wait"); });
});
Ale
  • 1,727
  • 14
  • 26
Dani
  • 1,246
  • 2
  • 8
  • 10
  • 1
    @gloomy.penguin This question was active more than 3 years prior to my answer, but I happened upon it in my own search for a solution, and decided to share the solution that I ended up using: a mixture of Dorward's answer, jQuery, and Kyle's good sense. It's a little different from what the OP was looking for, but if it works for you, then use it! :) – Dani Feb 19 '13 at 17:26
  • I agree. Works great and IMHO it should be the accepted answer. – savehansson Mar 07 '14 at 11:19
  • I don't think this answer takes into account, that the document and the wait lie on the same z-index therefore a sub element can override the cursor style. – Augunrik Oct 08 '15 at 09:15
  • 1
    If this isn't working for you, check here: [JQuery.com](http://jquery.com/upgrade-guide/1.9/#ajax-events-should-be-attached-to-document) "As of JQuery 1.9, the global Ajax events are only triggered on the document element." Ex: `$(document).ajaxStart(function () { $("html").addClass("wait"); });` – Debbie A Jan 07 '16 at 22:05
  • 1
    Before removing the wait class, you probably want to check if `$.active` is 0, incase multiple requests were made and they are all not finished yet. – Shane Reustle Mar 28 '16 at 06:57
  • great solution for those using jQuery. only works when using the jQuery Ajax functions (e.g., $.ajax(), $.get()), not with generic XMLHttpRequest objects. – Ale Aug 10 '16 at 12:27
35

I understand you may not have control over this, but you might instead go for a "masking" div that covers the entire body with a z-index higher than 1. The center part of the div could contain a loading message if you like.

Then, you can set the cursor to wait on the div and don't have to worry about links as they are "under" your masking div. Here's some example CSS for the "masking div":

body { height: 100%; }
div#mask { cursor: wait; z-index: 999; height: 100%; width: 100%; }
Eric Wendelin
  • 43,147
  • 9
  • 68
  • 92
  • 3
    This can often cause problems. In Firefox, puting a fixed div over the entire page causes the entire page to become unclickable. ie, you can't click on links, because you aren't clicking on the link you are clicking on the div in front of the link. – Kibbee Oct 10 '08 at 20:59
  • 1
    Works ok in firefox. No luck in IE :(. In IE it behaves exactly as if the div is not there. The only way to have the desired behavior is to set a color on the div background. – Aleris Oct 10 '08 at 21:31
  • 2
    Ok after a bit more playing it finally worked with: div#mask { display: none; cursor: wait; z-index: 9999; position: absolute; top: 0; left: 0; height: 100%; width: 100%; background-color: #fff; opacity: 0; filter: alpha(opacity = 0);} – Aleris Oct 10 '08 at 21:36
  • Agreed, Kibbee, it really depends on your desired behavior. It sound like Aleris does not want the user to do anything (hence the wait cursor) until AJAX calls back. – Eric Wendelin Oct 13 '08 at 14:49
  • 3
    I used position:fixed so it is always on screen, with position:absolute it wouldn't show when I was at the bottom of the page. – Jj. Feb 09 '11 at 18:16
  • The other solution is now a much better option. Just a little CSS with an add/remove class javascript call. – kwerle Sep 12 '14 at 18:17
  • Don't forget the "html" when creating a body with 100% height. Correct solution: html, body {height: 100%;} – Benny Code Oct 21 '16 at 13:03
12

This seems to work in firefox

<style>
*{ cursor: inherit;}
body{ cursor: wait;}
</style>

The * part ensures that the cursor doesn't change when you hover over a link. Although links will still be clickable.

Kibbee
  • 65,369
  • 27
  • 142
  • 182
  • *{ cursor: wait !important; } would be simpler and more likely to work in IE (which has lots of bugs with the inherit keyword) – Quentin Oct 10 '08 at 21:30
  • 1
    Can * { cursor: wait } be applied from javascript? - except by iterating all elements in the entire DOM :) – Aleris Oct 10 '08 at 21:43
  • maybe you could add a style element and set the innerHTML. Wouldn't be very elegant, but it might work. – Kibbee Oct 11 '08 at 00:05
  • 3
    .wait, .wait * { cusor: wait !important; } and then set document.body.className = 'wait'; with JavaScript – Quentin Oct 11 '08 at 09:03
  • 1
    Note there's a typo of "cursor" in the above comment, in case you copy/paste it. – devios1 Jan 17 '13 at 00:34
7

I have been struggling with this problem for hours today. Basically everything was working just fine in FireFox but (of course) not in IE. In IE the wait cursor was showing AFTER the time consuming function was executed.

I finally found the trick on this site: http://www.codingforums.com/archive/index.php/t-37185.html

Code:

//...
document.body.style.cursor = 'wait';
setTimeout(this.SomeLongFunction, 1);

//setTimeout syntax when calling a function with parameters
//setTimeout(function() {MyClass.SomeLongFunction(someParam);}, 1);

//no () after function name this is a function ref not a function call
setTimeout(this.SetDefaultCursor, 1);
...

function SetDefaultCursor() {document.body.style.cursor = 'default';}

function SomeLongFunction(someParam) {...}

My code runs in a JavaScript class hence the this and MyClass (MyClass is a singleton).

I had the same problems when trying to display a div as described on this page. In IE it was showing after the function had been executed. So I guess this trick would solve that problem too.

Thanks a zillion time to glenngv the author of the post. You really made my day!!!

pasx
  • 2,718
  • 1
  • 34
  • 26
4

Easiest way I know is using JQuery like this:

$('*').css('cursor','wait');
jere_hr
  • 284
  • 3
  • 11
4

css: .waiting * { cursor: 'wait' }

jQuery: $('body').toggleClass('waiting');

redbmk
  • 4,687
  • 3
  • 25
  • 49
2

Why don't you just use one of those fancy loading graphics (eg: http://ajaxload.info/)? The waiting cursor is for the browser itself - so whenever it appears it has something to do with the browser and not with the page.

unexist
  • 2,518
  • 23
  • 27
  • I agree partially. At least on windows the cursor that appears, if the browser itself is loading a page, is `cursor: progress` not `cursor: wait`. Using that cursor would be much more coherent with the user experience of the browser. But I really like the idea of changing the cursor to indicate that the browser is working just like it's working when it loads a page. – flu Oct 30 '13 at 10:29
2

To set the cursor from JavaScript for the whole window, use:

document.documentElement.style.cursor = 'wait';

From CSS:

html { cursor: wait; }

Add further logic as needed.

  • This javascript solution is the best, does the job without touching the HTML baggage. This is far better than toggling classes on elements which is very slow if you have lot of nodes in your DOM. – Rajshekar Reddy May 07 '20 at 03:59
  • Docs: [documentElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement) – djvg Nov 02 '22 at 10:12
1

Try the css:

html.waiting {
cursor: wait;
}

It seems that if the property body is used as apposed to html it doesn't show the wait cursor over the whole page. Furthermore if you use a css class you can easily control when it actually shows it.

GameFreak
  • 2,881
  • 7
  • 34
  • 38
1

Here is a more elaborate solution that does not require external CSS:

function changeCursor(elem, cursor, decendents) {
    if (!elem) elem=$('body');

    // remove all classes starting with changeCursor-
    elem.removeClass (function (index, css) {
        return (css.match (/(^|\s)changeCursor-\S+/g) || []).join(' ');
    });

    if (!cursor) return;

    if (typeof decendents==='undefined' || decendents===null) decendents=true;

    let cname;

    if (decendents) {
        cname='changeCursor-Dec-'+cursor;
        if ($('style:contains("'+cname+'")').length < 1) $('<style>').text('.'+cname+' , .'+cname+' * { cursor: '+cursor+' !important; }').appendTo('head');
    } else {
        cname='changeCursor-'+cursor;
        if ($('style:contains("'+cname+'")').length < 1) $('<style>').text('.'+cname+' { cursor: '+cursor+' !important; }').appendTo('head');
    }

    elem.addClass(cname);
}

with this you can do:

changeCursor(, 'wait'); // wait cursor on all decendents of body
changeCursor($('#id'), 'wait', false); // wait cursor on elem with id only
changeCursor(); // remove changed cursor from body
kofifus
  • 17,260
  • 17
  • 99
  • 173
1

I used a adaptation of Eric Wendelin's solution. It will show a transparent, animated overlay wait-div over the whole body, the click will be blocked by the wait-div while visible:

css:

div#waitMask {
    z-index: 999;
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 100%;
    cursor: wait;
    background-color: #000;
    opacity: 0;
    transition-duration: 0.5s;
    -webkit-transition-duration: 0.5s;
}

js:

// to show it
$("#waitMask").show();
$("#waitMask").css("opacity"); // must read it first
$("#waitMask").css("opacity", "0.8");

...

// to hide it
$("#waitMask").css("opacity", "0");
setTimeout(function() {
    $("#waitMask").hide();
}, 500) // wait for animation to end

html:

<body>
    <div id="waitMask" style="display:none;">&nbsp;</div>
    ... rest of html ...
ungalcrys
  • 5,242
  • 2
  • 39
  • 23
1

My Two pence:

Step 1: Declare an array. This will be used to store the original cursors that were assigned:

var vArrOriginalCursors = new Array(2);

Step 2: Implement the function cursorModifyEntirePage

 function CursorModifyEntirePage(CursorType){
    var elements = document.body.getElementsByTagName('*');
    alert("These are the elements found:" + elements.length);
    let lclCntr = 0;
    vArrOriginalCursors.length = elements.length; 
    for(lclCntr = 0; lclCntr < elements.length; lclCntr++){
        vArrOriginalCursors[lclCntr] = elements[lclCntr].style.cursor;
        elements[lclCntr].style.cursor = CursorType;
    }
}

What it does: Gets all the elements on the page. Stores the original cursors assigned to them in the array declared in step 1. Modifies the cursors to the desired cursor as passed by parameter CursorType

Step 3: Restore the cursors on the page

 function CursorRestoreEntirePage(){
    let lclCntr = 0;
    var elements = document.body.getElementsByTagName('*');
    for(lclCntr = 0; lclCntr < elements.length; lclCntr++){
        elements[lclCntr].style.cursor = vArrOriginalCursors[lclCntr];
    }
}

I have run this in an application and it works fine. Only caveat is that I have not tested it when you are dynamically adding the elements.

0

BlockUI is the answer for everything. Give it a try.

http://www.malsup.com/jquery/block/

kulNinja
  • 526
  • 6
  • 14
0

This pure JavaScript seems to work pretty well ... tested on FireFox, Chrome, and Edge browsers.

I'm not sure about the performance of this if you had an overabundance of elements on your page and a slow computer ... try it and see.

Set cursor for all elements to wait:

Object.values(document.querySelectorAll('*')).forEach(element => element.style.cursor = "wait");

Set cursor for all elements back to default:

Object.values(document.querySelectorAll('*')).forEach(element => element.style.cursor = "default");

An alternative (and perhaps a bit more readable) version would be to create a setCursor function as follows:

function setCursor(cursor)
{
    var x = document.querySelectorAll("*");

    for (var i = 0; i < x.length; i++)
    {
        x[i].style.cursor = cursor;
    }
}

and then call

setCursor("wait");

and

setCursor("default");

to set the wait cursor and default cursor respectively.

javocity
  • 9
  • 1
0

Lots of good answers already, but none of them mentions the <dialog> element.

Using this element we can create a solution similar to the masking <div>.

Here we use showModal() to "hide" elements, and we use ::backdrop to set the cursor style to wait on the entire page:

function showWaitDialog() {
    document.getElementById('id_dialog').showModal();
}
#id_dialog, #id_dialog::backdrop {
    cursor: wait;
}
<button onclick="showWaitDialog()">click me</button>
<dialog id="id_dialog">busy...</dialog>

The dialog is hidden by default, and can be shown using either the show() method, or the showModal() method, which prevents clicking outside the dialog.

The dialog can be forced to close using the close() method, if necessary. However, if your button links to another page, for example, then the dialog will disappear automatically as soon as the new page is loaded.

Note that the dialog can also be closed at any time by hitting the Esc key.

CSS can be used to style the dialog however you like.

The example uses the html onclick attribute, just for simplicity. Obviously, addEventListener() could also be used.

djvg
  • 11,722
  • 5
  • 72
  • 103
-1

Late to the party but simply give the Html tag an id by targeting

document.documentElement

and in the CSS place at the top

html#wait * {
    cursor: wait !important;
}

and simply remove it when you want to stop this cursor.

Francisco Jesus
  • 115
  • 1
  • 10