Despite my attempts to force a repaint, the progress bar remained static during intensive tasks

My JavaScript method works fine in Chrome, taking about 2000 ms to iterate over ~200 inputs, insert values, and trigger onchange events. However, it's a different story in IE where it takes about 10000 ms.

To show the progress of this process, I decided to implement a progress bar but encountered an issue - the browser wouldn't repaint the progress bar until the very end when it immediately jumps to 100%.

I tried three different methods I found during my research:

  • Running the progress bar update in a setTimeout with zero or non-zero timeout
  • Accessing the height property of the element or adjacent elements to force repaint
  • Show/hide the element and access the offsetHeight property simultaneously

Here is a snippet of my code:

    <div class="cg-progress-bar">
        <div class="cg-progress-bar-completed">
            &nbsp;
        </div>
        <div class="cg-inline-block cg-progress-bar-force-repaint">
            &nbsp;
        </div>
    </div>

JavaScript:

var progressBarCurrent = 0;
var progressBarTotal = 236;

$.each(sqlData, function(column, value){
        //performing some tasks to update input values

        //update progress bar
        progressBarCurrent++;
        if (progressBarCurrent % 22 === 0) {
            var percentageComplete = Math.floor( (progressBarCurrent/progressBarTotal)*100 ) + "%";    

            var bar = $(".cg-progress-bar-completed").width(percentageComplete)[0];

            //workarounds to force repaint
            bar.style.display = 'none';
            bar.offsetHeight;
            bar.style.display = '';

            setTimeout(function() { bar.style.display = 'block'}, 0);

            $(".cg-progress-bar-completed").animate({ width: percentageComplete }, 100).height();

            bar.appendChild(document.createTextNode(' '));
            $(bar).hide().show(0);
            bar.appendChild(document.createTextNode(' '));

            $(window).trigger("resize");
        }
    }
});

How can I effectively force the browser to repaint?

Answer №1

If your process is too heavy, it could be causing the execution thread to collapse. This can lead to the browser freezing momentarily while calculations are being carried out, making it impossible to interact with the UI during this time.

To prevent this from happening, HTML5 offers web workers technology. This technology allows you to simulate multitasking in browser environments and enables you to perform resource-intensive tasks without causing the browser to freeze.

Check out this informative article on web workers that I found helpful when creating a file uploader capable of uploading large files while displaying a live progress bar:

https://www.html5rocks.com/en/tutorials/workers/basics/

I hope this information proves useful to you.

Answer №2

After my previous attempt failed, I have come up with a working solution:

index.html


        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="utf-8">
            <title>-</title>
            <style type="text/css" media="screen">
                div{
                    margin: 0px;
                    padding: 0px;
                    height: 30px;
                }
                #wrapper{
                    width: 300px;
                    border: solid 1px black;
                }
                #progressBar{
                    width: 0px;
                    background-color: black;
                    color: grey;
                }
            </style>
        </head>
        <body>
            <div id="wrapper"><div id="progressBar"></div></div>
            <script type="text/javascript">

                var progressBar = document.getElementById("progressBar")    
                var workerThread = new Worker('worker.js');

                function drawProgress(progress){
                    var percentageComplete = Math.floor( (progress.current/progress.maximum)*100 ) + "%";
                    progressBar.style.setProperty("width",percentageComplete)
                }

                workerThread.onmessage = function(e){
                    drawProgress(e.data)
                }
            </script>
        </body>
        </html>
    

worker.js


        var sqlData = new Array( 236 );
        var i = 0;
        for ( let entry of sqlData ) {
            console.log( 'iteration of each' );
            postMessage( {
                current: i,
                maximum: sqlData.length
            } )
            for ( var n = 0; n < 200000; n++ ) {
                for ( let entry of sqlData ) {
                    entry = Math.random()
                }
            }
            i++;
        }
    

This approach seems to be the most feasible option without any workaround. The only challenge is integrating JQuery into the worker thread. One possible solution is to fetch the SQL dataset entirely in the main thread before transferring it to the worker.

Answer №3

@J97 mentioned that compatibility with IE9+ is important. Thus, to ensure support for IE8+, here is a modified solution:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Enhanced IE8 Support</title>
    <style type="text/css">
        div{
            margin: 0px;
            padding: 0px;
            height: 30px;
        }
        #wrapper{
            width: 300px;
            border: solid 1px black;
        }
        #progressBar{
            width: 0px;
            background-color: black;
            color: grey;
        }
    </style>
</head>
<body>
    <div id="wrapper"><div id="progressBar"></div></div>
    <script type="text/javascript">
        var continueExecution = false;
        var progressBar = document.getElementById( "progressBar" );
        var i = 0;
        var sqlData = new Array( 236 );
        var lastDraw = (new Date).valueOf();

        function heavyloadFunction() {
            var entry = sqlData[ i ]

            // perform tasks on the entry
            for ( var n = 0; n < 2000; n++ ) {
                for ( var h = 0; h < sqlData.length; h++ ) {
                    var cEntry = sqlData[h]
                    cEntry = Math.random()
                }
            }

            // complete entry work

            if(i < sqlData.length){
                i++;
                continueExecution = true;
            }else{
                console.log("finished")
                continueExecution = false;
            }
        }

        function drawProgress( progress ) {
            var percentageComplete = Math.floor( ( progress.current / progress.maximum ) * 100 ) + "%";
            progressBar.style.width = percentageComplete
        }

        function shouldReDraw(){
            var dNow = (new Date).valueOf();
            if(dNow - lastDraw > 16){
                // aiming for around 60fps
                lastDraw = dNow;
                return true;
            }else{
                return false;
            }
        }

        function excutionLoop() {
            heavyloadFunction();

            if ( continueExecution ) {
                if(shouldReDraw()){
                    drawProgress({
                        current: i,
                        maximum: sqlData.length
                    })

                    window.setTimeout( excutionLoop, 0 )
                }else{
                    excutionLoop()
                }
            }else{
                drawProgress({
                    current: i,
                    maximum: sqlData.length
                })
            }           
        }

        excutionLoop();
    </script>
</body>
</html>

The concept is simple: pause execution during each loop iteration to allow for redrawing.

UPDATE: Implemented a minor enhancement to prevent drawing from causing performance issues.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to utilize OpenLayers to showcase various icons for distinct features all within one layer?

Being new to Openlayers/JS and fairly inexperienced with programming in general, there may be other issues in my code that I'm unaware of. I am currently using the latest version of Openlayers (5.3.0). My application sends GeoJson formatted data via ...

Using React Native to integrate a JSON string into a button

I'm currently working on an app to explore the functionality of websockets in react native. I have successfully retrieved data from the websocket in JSON format and can output it to the console using console.log. However, my goal is to display a speci ...

Remove the empty or null value from the Select dropdown using jQuery and automatically select the next option with a valid value

My main goal is to eliminate any empty options from a dynamically generated SELECT input. The select is created by a script written by someone else, based on an object it receives. The issue arises when the object contains empty values, resulting in a sele ...

The 'userEvent.type' function in React Testing Library is failing to update the input value in a Material UI TextField component that has

I am currently facing an issue with a material UI TextField element that is meant to track the latitude value. The requirement is for the latitude to fall within the range of -90 to 90 degrees. I have implemented a unit test as a validation measure, howeve ...

Using Vue.js to invoke an external JavaScript function for search functionality

In my vue.js application, I have a list of users with backend pagination. Now I want to implement a search functionality. I attempted to call the method like this: watch: { search: function() { Crud.methods.getItems(); } }, Howe ...

Node.js middleware for verifying required parameters

In my current application, I am utilizing node.js and express. I have developed multiple middleware functions, each one created in a similar fashion: function loadUser(req, res, next){ ... } I am interested in creating a middleware that can validate th ...

How to Programmatically Assign Bootstrap Class to an Element Using Angular 2

I am working on a page with several input boxes that need to be flagged for certain criteria. <div class="form-group"> <label class="d-block">Allowance Type</label> <div class="input-group"> ...

`The process of adding an element to the beginning of an array``

I find myself in this scenario: I am dealing with 2 array1 variables, defined as array1 = ["fruit","vegetables"]; and array2 = [["apple","banana"],["tomato"]]; // index 0:represent fruit i,e (["apple","banana"]), index 1: vegetables i,e (["tomato"]) T ...

What is the best method for retrieving values from a FlexiGrid?

I'm having trouble finding information on how to retrieve a cell's value from a flexigrid. My goal is to fetch the value of the third column for every checked item (each row has a checkbox). While I have a function that successfully gets the ro ...

Enhancing AngularJS view rendering using ngshow

Currently, I am working on a view where ng-show is used to display a select DOM object when certain conditions are met, and an input DOM for all other scenarios. However, I have noticed that there is a significant delay in the disappearance of the input bo ...

What are the steps for skipping, sorting, and limiting with dynamoose?

After being familiar with MongoDB and mongoose, I am now exploring dynamoose for my app. In order to replicate the below-shown mongoose query using dynamoose, how can I write it? Specifically, I want to achieve the same functionality as the following mong ...

Using innerHTML in React to remove child nodes Tutorial

I'm encountering slow performance when unmounting over 30,000 components on a page using conditional rendering triggered by a button click. This process takes around 10+ seconds and causes the page to hang. Interestingly, setting the parent container& ...

``Change the color of the sections in a 3D pie chart on a Highcharts

I am looking to create a custom pie chart with two different colors: one for the main surface and another for the sides. Currently, I can only configure the lighter blue color for the main surface, but I would like to also change the darker blue color for ...

Eliminating the need for the horizontal scroll bar on the webpage

I'm dealing with a page that has multiple controls. Initially, there is no horizontal scrollbar on the page. However, as soon as I introduce an AjaxControlToolkit.Editor or a treeview onto the page, a horizontal scroll bar mysteriously appears. For ...

Updating an element within a for loop using Angular TypeScript

I'm trying to figure out how to update the value of an HTML DOM element that is bound from a TypeScript file in each iteration of a for loop, rather than at the end of the loop. I want to see all values as the loop is running. For example, imagine I ...

Issues with Vue Router functionality in Leaflet Popup are causing unexpected behavior

Incorporating Leaflet and Vue together in my codebase using the vue2-leaflet wrapper has presented a challenge. Specifically, I am facing difficulties getting Vue $router to function within Leaflet's popup. Below is a snippet of my current code along ...

Looking to display the "loading....." message on a PHP page?

I am working on a PHP webpage where I need to implement the following features: 1. Upon clicking "Say Thanks", it should change to "Done!". 2. Simultaneously, I would like to trigger an action in the indexController. 3. While this action is happening, I wa ...

Encountering an issue with connecting nodejs to mqlight

I have been working with nodejs and mqlight to test out some sample code provided on https://www.npmjs.com/package/mqlight. My current setup consists of nodejs 5.5.0 and npm version 3.3.12. To install mqlight, I used the command npm install mqlight. ...

I'm looking to adjust the padding on the left and right sides of the v-select menu checkbox in Vuetify 3. How

While trying to reduce the left padding of the v-select menu checkbox using the F12 debugger, I encountered an issue where the menu suddenly disappears when clicked. This makes it difficult to see the active menu. Attached is a screenshot of the select me ...

Importing styles from an external URL using Angular-cli

How can I load CSS styles from an external URL? For instance, my domain is domain.eu but my site is located at sub.domain.eu. I want to use styles that are stored on the main domain (common for all sites). The example below does not work: "styles&qu ...