Progress Bar (Unbounded)

Progress Bar (Unbounded)

The unbounded progress bar is not tied to any progressive increments. It is basically a "busy" or "in progress" message. It will continue until it is cancelled by another event. If there is no such event, it will continue forever.

Note:

The unbounded progress bar is not keyboard-focusable, so if you want to hear it read by a screen reader, you will need to navigate to it via text navigation (down arrow key in JAWS and NVDA; Alt + right arrow in Narrator; Control + Option + Right arrow in VoiceOver)



Turn on a screen reader to experience this example in action.

Attribute/Option Description
message This atrribute/option specifies message for the screen readers. If this option is not provided, a default message is set for the screen readers.
height This atrribute/option specifies the height of the progressbar widget. By default, it is set to 50px.
padding This atrribute/option specifies the padding to be used for the progressbar widget. By default, it is set to 5px.
indicator_color_gradient This atrribute/option sets the progress indicator as a color gradient of your choice. If this option is not provided, a default color gradient is set.

Data Processing

HTML Source Code

<table class="data">
    <tr>
        <th width="150px" >Attribute/Option</th>
        <th>Description</th>
    </tr>
    <tr>
        <td>message</td>
        <td>This atrribute/option specifies message for the screen readers. 
            If this option is not provided, a default message is set for the screen readers.
        </td>
    </tr>
    <tr>
        <td>height</td>
        <td>This atrribute/option specifies the height of the progressbar widget.
            By default, it is set to 50px. 
        </td>
    </tr>
    <tr>
        <td>padding</td>
        <td>This atrribute/option specifies the padding to be used for the progressbar widget.
            By default, it is set to 5px. 
        </td>
    </tr>
    <tr>
        <td> indicator_color_gradient </td>
        <td>This atrribute/option sets the progress indicator as a color gradient of your choice. 
            If this option is not provided, a default color gradient is set.
        </td>
    </tr>
</table>
<h2 id="task_label">
    Data Processing 
</h2>
<div id="progressbar-container"></div>
<p>
    <button class="button" id="start-progressbar">
        Start
    </button>
    <button class="button" id="stop-progressbar">
        Stop
    </button>
</p>

JavaScript Source Code

//Language object to be modified as per the language
var langText = {
    "errorNodeNotObject": "Node provided is not a DOM object.",
    "errorNodeNotDivObject": "Node provided is not a DIV HTML object.",
    "msgProcessing": "processing in progress...",
    "msgPleaseWait": "please wait...",
    "msgProcessingCompleted": "processing completed.",
    
}
var progressbar;
window.addEventListener('load', function () {
    var div = document.getElementById("progressbar-container");
    var options = [];
    //options["indicator_color_gradient"] = "linear-gradient(to right, #c12800 0, rgba(255, 255, 255, 0) 50%, #c12800 100%)";
    options["height"] = 10;
    progressbar = new ProgressbarUnbounded(div, options);
    var startButton = document.getElementById("start-progressbar");
    startButton.addEventListener("click", startProgressbar);
    var stopButton = document.getElementById("stop-progressbar");
    stopButton.addEventListener("click", stopProgressbar);
    
});

var intervalId = null;
var progressValue = 10;
function startProgressbar(event)
{
    progressbar.show();
}

function stopProgressbar()
{
    progressbar .hide(langText["msgProcessingCompleted"]);
}

class ProgressbarUnbounded {
constructor(node, options) {
    // Check whether node is a DOM element
    if (typeof node !== 'object') {
        console.log(langText["errorNodeNotObject"]);
        return;
    }
    if( (typeof node.nodeName === "undefined") || ( node.nodeName.toLowerCase() !== "div" ))
    {
        console.log(langText["errorNodeNotDivObject"]);
        return;
    }
    this.parentNode = node;
    if ( (typeof options == "undefined") || (!Array.isArray(options)) ) options = [];
    this.processingMessage = langText["msgProcessing"];
    if(typeof options["message"] === "string")
        this.processingMessage = options["message"];
    var height = "30";
    if(typeof options["height"] === "number")
        height = options["height"];
    var padding = "5";
    if(typeof options["padding"] === "number")
        padding = options["padding"];
    var indicatorColorGradient = "linear-gradient(to right, #006cc1 0, rgba(255, 255, 255, 0) 50%, #006cc1 100%)";
    if(typeof options["indicator_color_gradient"] === "string")
        indicatorColorGradient = options["indicator_color_gradient"];    

    var progressbar = document.createElement("progressbar");
    progressbar.classList.add("progressbara");
    progressbar.style.width = "calc(100%-"+padding+"px)";
    progressbar.style.height = height+"px";
    progressbar.style.display = "none";
    progressbar.style.padding = padding + "px";
    this.parentNode.appendChild(progressbar);
    progressbar.style.backgroundImage = indicatorColorGradient;
    progressbar.style.animation = "20s linear infinite move";
    this.progressbar = progressbar;

    var notifyElement = document.createElement("p");
    notifyElement.setAttribute("aria-live", "polite");
    notifyElement.classList.add("visually-hidden");
    this.parentNode.appendChild(notifyElement);
    this.notifyElement = notifyElement;  
}

show()
{
    this.progressbar.style.display = "block";
    var self = this;
    var announceMessage = true;
    if(typeof this.intervalId != "undefined") clearInterval(this.intervalId);
    this.intervalId = setInterval(function(){ 
        if(announceMessage)
            self.announce(self.processingMessage,120,2000,"show");
        else
            self.announce(langText["msgPleaseWait"],120,1500,"show");
        announceMessage = !announceMessage;
    }, 2000);
}

hide(message)
{
    this.progressbar.style.display = "none";
    if(typeof this.intervalId != "undefined") clearInterval(this.intervalId);
    if(typeof message === "string")
        this.announce(message,0,2000,"hide");
}

announce(message, initialDelay, msgTime, scope)
{
    var self=this;    
    if(typeof scope == "undefined") scope = "unknown";
    if(typeof this.scopeIds == "undefined" ) this.scopeIds = [];
    if( typeof this.scopeIds[scope] == "number")
    {
        clearTimeout(this.scopeIds[scope]);
        this.scopeIds[scope] = null;
    }
            
    if (initialDelay > 1)
    {
        this.scopeIds[scope] = setTimeout(function() {
            self.notifyElement.innerHTML = message;
        }, initialDelay);    
    }    
    else
        this.notifyElement.innerHTML = message;
    setTimeout(function() {
        self.notifyElement.innerHTML ="";
        self.scopeIds[scope] = null;
    }, msgTime);
}

}  

CSS Source Code

.button { 
    padding: 5px 10px; 
    display: inline-block; 
    width: 150px;
    border: 2px solid gray;
    border-left: 1px solid lightgray;
    border-top: 1px solid lightgray;
    border-right: 2px solid gray;
    border-bottom: 2px solid gray;
    margin: 2px;
    background-color: #e8e5e5;
}

.pressed {
    border-left: 2px solid gray;
    border-top: 2px solid gray;
    border-right: 1px solid lightgray;
    border-bottom: 1px solid lightgray;
    
}

button {     padding: 5px 10px; width: 170px;}

.button:hover{
    outline: 2px solid blue;
}

.button:focus{
    margin: 2px;
    outline: 2px solid blue;
}  

.visually-hidden{
        border: 0;
        clip: rect(0 0 0 0);
        height: 1px;
        margin: -1px;
        overflow: hidden;
        padding: 0;
        position: absolute;
        white-space: nowrap;
        width: 1px;
}
.progressbar {
    width: calc(100% -10);
height: 50px;
display: block;
background-color: aliceblue;
padding: 5px;
}   

@keyframes move {
0% {
    background-position: 0 0
    }

100% {
    background-position: -10000px 0
    }
}

Copy and Paste Full Page Example