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

Display elements on hover of thumbnails using CSS

I'm struggling with the logic of displaying images when hovering over corresponding thumbnails using only CSS. If necessary, I can do it in JavaScript. Here's my latest attempt. <div id='img-container' class='grd12'> ...

Using Javascript to Manuever an Element via Arrow Inputs

Currently, I am in the process of developing a Tetris game with a rotating Tetris piece controlled by pressing the space bar. My next objective is to enable the movement of objects to the left and right using the arrow keys. After exploring various simila ...

Get Angular events in the proper order

I am currently facing challenges with event handling in AngularJs. In my service, I send out events using the following line of code : $rootScope.$emit("FNH."+this.status, flowNodeHelper); When receiving the event in service "S1," I handle it as follows ...

Executing a message in Azure Service Bus using Javascript/Node.js

I am utilizing the @azure/service-bus library in a Node.js application to receive messages. The code I am using is as follows: const { ServiceBusClient } = require("@azure/service-bus"); const sbClient = new ServiceBusClient(connectionString); ...

What causes the picturesArray to remain consistently void?

const fetch = require("node-fetch"); let images = []; fetch('http://www.vorohome.com//images/assets/159314_887955.png') .then(response => response.buffer()) .then(buffer => { const data = "data:" + response.headers.get ...

When an image is clicked, I would like it to redirect to a different page

In my slider image list, each image has an ID. If the ID becomes equal to a specific number, such as 24, I want that particular image to move to another page when clicked. Here is the code snippet: <?php $sql = "select * from ".$tblImages." Where cid= ...

The interval in the AJAX callback is causing the variable to not update as expected

After successfully fetching data using the get_data() function within a setInterval, an issue arises when the AJAX call returns a new price, as calling get_data() no longer updates it. Instead, it continues to display the initial value obtained during the ...

Need to return false even though Jquery form check currently returns true

Upon form submission, the following check is executed: if((formData[4].value == 1 || formData[4].value == 2) && !formData[2].value) { alert('Please fill out the key field'); return false; } else { $.ajax({ url: "/aja ...

encountering a problem with permissions while attempting to update npm

Has anyone encountered a permission error with npm when trying to update to the latest version? I recently tried updating npm and received this error message. I'm unsure of how to resolve it. Any suggestions? marshalls-MacBook-Air:Desktop marshall$ n ...

Issue with implementing autocomplete using AJAX and PHP for multiple input fields

HTML code: <table> <tr> <td align="center"> <label for="InputBox1"><b>~</b><font color="red">*</font>: &nbsp;</label> </td> </tr> <tr> <td align="center"> <div id="1"> ...

Creating a Vue.js component during the rendering process of a Laravel Blade partial view

In my Vue.js project, I have a component that is used in a partial view called question.blade.php: {{--HTML code--}} <my-component type='question'> <div class="question">[Very long text content...]</div> </my-component& ...

What is the best way to manage a promise in Jest?

I am encountering an issue at this particular section of my code. The error message reads: Received promise resolved instead of rejected. I'm uncertain about how to address this problem, could someone provide assistance? it("should not create a m ...

Every time I use my NodeJS MySQL Query function, the results I get are never

Working on a new gaming project involving MySQL for multiplayer functionality. Issue arises when requesting specific queries, where the system retrieves incorrect data or results from previous queries. dat("SELECT * FROM server1;"); Misdirected queries r ...

Problem with Onsen UI navigation: It is not possible to provide a "ons-page" element to "ons-navigator" when attempting to navigate back to the initial page

Hi, I am having trouble with navigation using Onsen UI. Here is the structure of my app: start.html: This is the first page that appears and it contains a navigator. Clicking on the start button will open page1.html page1.html: Performs an action that op ...

Posting several pictures with Protractor

In my test suite, I have a specific scenario that requires the following steps: Click on a button. Upload an image from a specified directory. Wait for 15 seconds Repeat Steps 1-3 for all images in the specified directory. I need to figure out how to up ...

Navigation through dots on a single page

Having an issue with my dot navigation and anchor links placement. I want the anchors to be vertically centered in the middle of the section, regardless of window size. Here's what I'm aiming for : For larger windows : And for smaller windows : ...

Tips for increasing the size of a parent div when a child div is displayed with a set width?

Is there a way to make the parent div automatically expand when the child div with a fixed width is displayed onclick? Goal: I want the child div to show up when I click the link, and at the same time, I need the parent div to expand or scale to fit the ...

Implement a redux-form within a react-bootstrap modal

I am facing a challenge with incorporating a multipage 'redux-form' form into a react-bootstrap modal. My goal is to have the form displayed within the modal overlay when the modal is opened. How can this be achieved? The code below is producin ...

"Performing validation on a number input by using the ng-change event

Im using a number input that dynamically sets the min and max values based on another form field. I have two scenarios: Level 1: Min = 2, Max = 50 Level 2: Min = 5, Max = 1000 I've set up an ng-change event on the input field to check if the entere ...

What could be causing my webpage to freeze every time a filter button is selected?

Tasked with developing a webpage similar to Pinterest by utilizing data from a JSON response. Each JSON object contains a service_name key, which can be manual, twitter, or instagram. I made an effort to implement three filter buttons to only display the r ...