简体   繁体   中英

jquery addClass doesn't take effect immediately

I have a page which loads data (it will use AJAX) so I want to display a "Loading, please wait" message. For now I am simulating the network delay using a loop. I use JQuery to set and display the message, but it isn't being displayed until after the loop completes. What do I need to do to "flush" the JQuery commands so that the message is displayed before the loop is invoked?

I have provided skeletal CSS, HTML and JQuery/JavaScript below.

<!DOCTYPE html>
<html>
<head>
<style>
.ais-card-message {
    padding: 6px;
    margin-top: 6px;
    margin-bottom: 6px;
    text-align: left;
}
.ais-card-message-info {
    background-color: lightskyblue;
}
.ais-card-message-warn {
    background-color: lightpink;
}
</style>
</head>
<body>
    <div id="title">
        <p>Message "Loading, please wait ..." should appear below when button clicked.&nbsp;
        Followed by either "Error" or "Loaded" after a short delay.</p>
    </div>
    <div id="data">Data will be loaded here</div>
    <div id="msgBox" class="ais-card-message">Messages should display here</div>
    <div><button id="btnLoad">Load</button></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        function showMsg(msg, type) {
            // msg is message to display. type is either "info" or "warn"
            $('#msgBox').removeClass("ais-card-message-info ais-card-message-warn");
            $('#msgBox').addClass("ais-card-message-" + type);
            $('#msgBox').text(msg);
            $('#msgBox').show();
            return
        }
        $('#btnLoad').click(function(){
            // For some reason the following message doesn't display
            showMsg('Loading, please wait ...', 'info');
            if (!loadData()) {
                // But this message does display if loadData returns false
                showMsg('Error', 'warn');
                return
            }else{
                // and this message also displays if loadData returns true
                showMsg('Loaded', 'info');
                return
            }
        });
        function loadData() {
            // Just a loop to simulate network delay
            var i;
            for (i=0; i < 100000; i++) {
                var j;
                for (j=0; j < 100000; j++) {
                    var k = Math.sqrt(j);
                }
            }
            $('#data').text("Here is some dummy data");
            return true
        }
    });
</script>    
</body>
</html>

You have a couple of problems in your code. Firstly in your 'mock' AJAX request you're using a loop. This is a synchronous operation and the cause of the issue which prevents the UI being updated; the loop is stopping any other operation from being executed (ie. the update of the element in the DOM). Hence it only appears to update after the loop completes.

Secondly, once you actually start using an AJAX request in the loadData() function you will run in to problems with synchronicity as you're expecting the boolean return value to indicate whether or no the AJAX request worked. This is not possible with async logic. As such you need to work with promises and/or callbacks.

To do that you need to return the promise from $.ajax() in the loadData() function. Then you can use the done() and fail() methods to update the DOM based on the result of the request. It should look something like this:

$('#btnLoad').click(function() {
  showMsg('Loading, please wait ...', 'info');
  loadData().done(function() {
   showMsg('Loaded', 'info');
  }).fail(function() {
    showMsg('Error', 'warn');
  });
});

function loadData() {
  return $.ajax({
    url: '/yourpage',
    data: {
      foo: 'bar'
    }
  });
} 

Below is a working example which mocks the AJAX request by manually resolving a deferred object after 3 seconds. You can see the loading message is now displayed properly due to the corrected use of async logic.

 $(document).ready(function() { function showMsg(msg, type) { $('#msgBox').removeClass("ais-card-message-info ais-card-message-warn").addClass("ais-card-message-" + type).text(msg).show(); } $('#btnLoad').click(function() { showMsg('Loading, please wait...', 'info'); loadData().done(function() { showMsg('Loaded', 'info'); }).fail(function() { showMsg('Error', 'warn'); }); }); function loadData() { // mock AJAX request, just for this demo var deferred = jQuery.Deferred(); setTimeout(function() { deferred.resolve(); }, 3000); return deferred; } });
 .ais-card-message { padding: 6px; margin-top: 6px; margin-bottom: 6px; text-align: left; }.ais-card-message-info { background-color: lightskyblue; }.ais-card-message-warn { background-color: lightpink; }
 <div id="title"> <p>Message "Loading, please wait..." should appear below when button clicked.&nbsp; Followed by either "Error" or "Loaded" after a short delay.</p> </div> <div id="data">Data will be loaded here</div> <div id="msgBox" class="ais-card-message">Messages should display here</div> <div><button id="btnLoad">Load</button></div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM