Progress bar widget experiment
A segmented progress bar widget, compared to the native HTML5 <progress> element.
by John Pisello
HTML
<section>
HTML5 <progress> element:
<progress id="p1" max="4" value="1">Fallback content</progress>
</section>
<section>
My progressbar widget:
<div id="p2" role="progressbar" aria-valuenow="1" aria-valuemin="0" aria-valuemax="4">Fallback content</div>
</section>
SCSS
body {
background-color: #fff;
display: flex;
justify-content: space-around;
font-family: Arial, Helvetica, sans-serif;
}
section {
flex: 1 0 200px;
padding: 10px;
border: 1px solid #ccc;
margin: 10px;
line-height: 2;
}
div[role="progressbar"] {
display: block;
color: #8af;
width: 10em;
height: 1em;
border: 1px solid #999;
// border-radius: .5em;
background-color: #f8f8f8;
text-indent: -9999px;
position: relative;
background-image: linear-gradient(to right, #acf 0%, #8af 100%); // Note: using currentColor in the linear gradient causes the ::after element's background not to display--at least in Chrome and MS Edge. Not sure why.
background-position: 0 0;
background-size: 0% 100%;
background-repeat: no-repeat;
&::after {
content: ".";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
}
JavaScript
/*
* Adds a method to HTMLElement to dynamically add a class to style a pseudo-element. See http://mcgivery.com/htmlelement-pseudostyle-settingmodifying-before-and-after-in-javascript/.
*/
(function() {
var UID = {
_current: 0,
getNew: function() {
this._current++;
return this._current;
}
};
HTMLElement.prototype.pseudoStyle = function(element, prop, value) {
var _this = this;
var _sheetId = "pseudoStyles";
var _head = document.head || document.getElementsByTagName('head')[0];
var _sheet = document.getElementById(_sheetId) || document.createElement('style');
_sheet.id = _sheetId;
var className = "pseudoStyle" + UID.getNew();
_this.className += " " + className;
_sheet.innerHTML += " ." + className + "::" + element + "{" + prop + ":" + value + "}";
_head.appendChild(_sheet);
return this;
};
})();
/* ------------------------------------- */
var $p1 = $('#p1'), // Native <progress> element
$p2 = $('#p2'); // My widget
function setP1(val) {
var maxVal = +$p1.attr('max') || 1.0,
minVal = 0,
newVal = Math.min(Math.max(minVal, val), maxVal);
$p1.attr('value', newVal);
$p1.text(newVal + " of " + maxVal);
};
function incP1(inc) {
setP1(+$p1.attr('value') + inc);
};
/*
* Initializes the widget.
* Creates an overlay (using the ::after pseudo-element) to show
* the desired number of divisions (based on the aria-valuemax/min
* attributes), then calls setP2() to draw the progress indicator.
*/
function initProgressBar(id) {
var cssSelector = '#' + id + "[role='progressbar']",
$pb = $(cssSelector),
wht = "rgba(0,0,0,0) ",
blk = "rgba(0,0,0,.375) ",
lg = "linear-gradient(to right, " + wht + " 0";
ticks = 0, tickDist = 0, nextTickPos = 0, nextTickPosRnd = 0, i = 0;
if ($pb.length === 0) {
return;
}
ticks =...