CSS Knobs

by Santiago J

HTML

<br>
<br>
<br>
<br>
<br>
<br>

<div class="knob-container" data-title="a"></div>
<div class="knob-container"></div>
<br>
<div class="knob-container"></div>
<div class="knob-container"></div>

CSS

body {
  background-color: #333;
  color: #eee;
}
.knob-container {
  display: inline-block;
  font-family: verdana, geneva, sans-serif;
  font-size: 12px;
  position: relative;
  text-align: center;
  outline:1px solid red;
}
.knob-title {
  display: block;
}
.knob-inner-container {
  display: block;
  width: 50px;
  height: 50px;
  position: relative;
  outline:1px solid red;
}
.knob-value {
  background-color: transparent;
  border: none;
  color: inherit;
  display: block;
  font-family: verdana;
  font-size: 10px;
  position: absolute;
  top: 18px;
  left: 13px;
  text-align: center;
  width: 25px;
  z-index: 5;
}
.knob-knob {
  border: 1px solid #eee;
  border-radius: 50%;
  display: block;
  width: 50px;
  height: 50px;
}
.knob-knob::before {
  position: absolute;
  content: "|";
  left: 23px;
  bottom: 5px;
}

JavaScript

window.addEventListener("load", function() {

function tap(t,a,p) {
    var el = document.createElement(t);
    if (typeof a === "object") {
        for (var i in a) {
            el[i] = a[i];
        }
    }
    if (p) p.appendChild(el);
    return el;
}

var forEach = Array.prototype.forEach;
var currentKnob = null;
var lastY = 0;

function updateKnobAngle(knob, angle) {
    knob.style.transform =
    knob.style.webkitTransform = "rotate(" + angle/100 + "turn)";
}

var mousedownfn = function(e) {
    e.preventDefault();
    lastY = e.clientY;
    currentKnob = this;
};

window.addEventListener("mouseup", function() {
    currentKnob = null;
}, false);

window.addEventListener("mousemove", function(e) {
    if (!currentKnob) return;

    var
    valueNode = currentKnob.parentNode.querySelector(".knob-value"),
    y = e.clientY,
    val = Math.min(Math.max(+valueNode.value + lastY - y, 0), 100);

    updateKnobAngle(currentKnob, val);
    valueNode.value = val;
    lastY = y;
}, false);

var onblurfn = function() {
    updateKnobAngle(this.parentNode.querySelector(".knob-knob"), this.value);
};

var FALSEFN = function() { return false };
forEach.call(document.querySelectorAll(".knob-container"), function(el) {
    var knobValue = tap("span", {
        className: "knob-title",
        textContent: el.getAttribute("data-title")
    }, el);
    var knobInner = tap("div", {
        className: "knob-inner-container"
    }, el);
    tap("input", {
        className: "knob-value",
        value: "0"
    }, knobInner);
    var knobKnob = tap("span", {
        className: "knob-knob",
        value: "0"
    }, knobInner);

    knobValue.addEventListener("blur", onblurfn, false);
    knobKnob.addEventListener("mousedown", mousedownfn, false);
});

}, false);