Progress bar widget experiment

A segmented progress bar widget, compared to the native HTML5 <progress> element.

by John Pisello

HTML

<section>
    HTML5 &lt;progress&gt; 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>

CSS

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%, currentColor 100%);
  background-position: 0 0;
  background-size: 0% 100%;
  background-repeat: no-repeat;
}
div[role="progressbar"]::after {
    content: "Hello!";
    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,.25) ",
        lg = "linear-gradient(to right, " + wht + " 0";
    ticks = 0, tickDist = 0, nextTickPos = 0, nextTickPosRnd = 0, i = 0;
    if ($pb.length === 0) {
        return;
    }
    ticks =...