2

I'm using this custom JavaScript range slider, and I want to be able to set a min and max value in pixels. The default is percentage, from 0 - 100.

I was able to implement a min and max setting. The next step is to convert all the percentage to a pixel amount. I was able to do that for the drag function, but I'm having trouble doing it for value.

When I set value to, say 50, instead of going to 50px, it goes to 50%. How can I change all the percentages to pixels?

If you have a better way of doing this, please let me know.

I think this is the main code to change. It's in the last function. It's called initDragger:

cachePosition = ((config.value / 100) * range[!isVertical ? 'offsetWidth' : 'offsetHeight']);
dragger.style[!isVertical ? 'left' : 'top'] = (cachePosition - (woh / 2)) + 'px';

JSFiddle

function rangeSlider(elem, config) {

  var html = document.documentElement,
    range = document.createElement('div'),
    dragger = document.createElement('span'),
    down = false,
    rangeWidth, rangeOffset, draggerWidth, cachePosition;

  var defaults = {
    min: 20,
    max: 150,
    value: 0, // set default value on initiation from `0` to `100` (percentage based)
    vertical: false, // vertical or horizontal?
    rangeClass: "", // add extra custom class for the range slider track
    draggerClass: "", // add extra custom class for the range slider dragger
    drag: function(v) { /* console.log(v); */ } // function to return the range slider value into something
  };

  for (var i in defaults) {
    if (typeof config[i] == "undefined") config[i] = defaults[i];
  }

  function addEventTo(el, ev, fn) {
    if (el.addEventListener) {
      el.addEventListener(ev, fn, false);
    } else if (el.attachEvent) {
      el.attachEvent('on' + ev, fn);
    } else {
      el['on' + ev] = fn;
    }
  }

  var isVertical = config.vertical;

  elem.className = (elem.className + ' range-slider ' + (isVertical ? 'range-slider-vertical' : 'range-slider-horizontal')).replace(/^ +/, "");
  range.className = ('range-slider-track ' + config.rangeClass).replace(/ +$/, "");
  dragger.className = ('dragger ' + config.draggerClass).replace(/ +$/, "");

  addEventTo(range, "mousedown", function(e) {
    html.className = (html.className + ' no-select').replace(/^ +/, "");
    rangeWidth = range[!isVertical ? 'offsetWidth' : 'offsetHeight'];
    rangeOffset = range[!isVertical ? 'offsetLeft' : 'offsetTop'];
    draggerWidth = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
    down = true;
    updateDragger(e);
    return false;
  });

  addEventTo(document, "mousemove", function(e) {
    updateDragger(e);
  });

  addEventTo(document, "mouseup", function(e) {
    html.className = html.className.replace(/(^| )no-select( |$)/g, "");
    down = false;
  });

  addEventTo(window, "resize", function(e) {
    var woh = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
    dragger.style[!isVertical ? 'left' : 'top'] = (((cachePosition / 100) * range[!isVertical ? 'offsetWidth' : 'offsetHeight']) - (woh / 2)) + 'px';
    down = false;
  });

  function updateDragger(e) {
    e = e || window.event;
    var pos = !isVertical ? e.pageX : e.pageY;
    if (!pos) {
      pos = !isVertical ? e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft : e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    if (down && pos >= rangeOffset && pos <= (rangeOffset + rangeWidth)) {
      dragger.style[!isVertical ? 'left' : 'top'] = (pos - rangeOffset - (draggerWidth / 2)) + 'px';
      cachePosition = Math.round(((pos - rangeOffset) / rangeWidth) * 100);
      cachePositionPixel = Math.round(((cachePosition / 100) * (config.max - config.min)) + config.min);
      config.drag(cachePositionPixel);
    }
  }

  function initDragger() {
    var woh = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
    cachePosition = ((config.value / 100) * range[!isVertical ? 'offsetWidth' : 'offsetHeight']);
    dragger.style[!isVertical ? 'left' : 'top'] = (cachePosition - (woh / 2)) + 'px';
    config.drag(config.value);
  }

  range.appendChild(dragger);
  elem.appendChild(range);

  initDragger();

}

var slide = document.getElementById('range-slider');
var resultP = document.getElementById('results');
var button = document.getElementById('button');

rangeSlider(slide, {
  value: 50,
  drag: function(v) {
    document.getElementById('results').innerHTML = "Your Current Value is: " + v;
  },
  max: 60
});
.range-slider-track {
  height: 20px;
}
.range-slider-track:before {
  content: "";
  display: block;
  width: 100%;
  height: 2px;
  background-color: black;
}
.range-slider-track .dragger {
  display: block;
  width: 10px;
  height: inherit;
  position: relative;
  background-color: red;
}
<div id="range-slider"></div>
<p id="results"></p>
Horay
  • 1,388
  • 2
  • 19
  • 36

3 Answers3

1

https://github.com/tovic/simple-custom-range-slider - scroll this site to the bottom, there is an answer:

var min = 2, max = 40;

function pixelToPercent(pixel) {
    return ((pixel - min) / (max - min)) * 100;
}

function percentToPixel(percent) {
    return ((percent / 100) * (max - min)) + min;
}

rangeSlider(document.getElementById('range-slider-1'), {
    value: pixelToPercent(10),
    drag: function(v) {
        document.getElementById('result-area').innerHTML = Math.round(percentToPixel(v));
    }
});
mwl
  • 1,448
  • 14
  • 18
1

As has become almost customary for me by now, I will offer this answer.

document.getElementById('clickme').onclick = function() {
  document.getElementById('slider').value = 37;
};
<input type="range" min="2" max="40" value="23" id="slider" />
<button id="clickme">Set "pixel" to 37</button>

The best solution to a problem is usually the easiest one.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • Originally I started with that, but I then came to the ie problem of updating it whenever the slider value changes. – Horay Dec 20 '15 at 20:59
0

It's kinda clunky and it's off by 5px and starts off at 21px so it needs fine-tuning. The function tickDragDist() is an adaptation of this code. tickDragDist() measures the distance between .dragger and .tick (a label that's appended to the .range-slider; .tick is the reference point.)

http://plnkr.co/edit/EckPp81v7xYNHnAArCOt?p=preview

enter image description here

        function rangeSlider(elem, config) {
    
          var html = document.documentElement,
            range = document.createElement('div'),
            dragger = document.createElement('span'),
        tick = document.querySelector('.tick'),
            down = false,
            rangeWidth, rangeOffset, draggerWidth, cachePosition;
    
          var defaults = {
            min: 20,
            max: 150,
            value: 0, // set default value on initiation from `0` to `100` (percentage based)
            vertical: false, // vertical or horizontal?
            rangeClass: "", // add extra custom class for the range slider track
            draggerClass: "", // add extra custom class for the range slider dragger
            drag: function(v) { /* console.log(v); */ } // function to return the range slider value into something
          };
    
          for (var i in defaults) {
            if (typeof config[i] == "undefined") config[i] = defaults[i];
          }
    
          function addEventTo(el, ev, fn) {
            if (el.addEventListener) {
              el.addEventListener(ev, fn, false);
            } else if (el.attachEvent) {
              el.attachEvent('on' + ev, fn);
            } else {
              el['on' + ev] = fn;
            }
          }
    
          var isVertical = config.vertical;
    
          elem.className = (elem.className + ' range-slider ' + (isVertical ? 'range-slider-vertical' : 'range-slider-horizontal')).replace(/^ +/, "");
          range.className = ('range-slider-track ' + config.rangeClass).replace(/ +$/, "");
          dragger.className = ('dragger ' + config.draggerClass).replace(/ +$/, "");
    
          addEventTo(range, "mousedown", function(e) {
            html.className = (html.className + ' no-select').replace(/^ +/, "");
            rangeWidth = range[!isVertical ? 'offsetWidth' : 'offsetHeight'];
            rangeOffset = range[!isVertical ? 'offsetLeft' : 'offsetTop'];
            draggerWidth = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
            down = true;
            updateDragger(e);
            return false;
          });
    
          addEventTo(document, "mousemove", function(e) {
            updateDragger(e);
          });
    
          addEventTo(document, "mouseup", function(e) {
            html.className = html.className.replace(/(^| )no-select( |$)/g, "");
            down = false;
          });
    
          addEventTo(window, "resize", function(e) {
            var woh = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
            dragger.style[!isVertical ? 'left' : 'top'] = (((cachePosition / 100) * range[!isVertical ? 'offsetWidth' : 'offsetHeight']) - (woh / 2)) + 'px';
            down = false;
          });
    
          function updateDragger(e) {
            e = e || window.event;
            var pos = !isVertical ? e.pageX : e.pageY;
            if (!pos) {
              pos = !isVertical ? e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft : e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
            }
            if (down && pos >= rangeOffset && pos <= (rangeOffset + rangeWidth)) {
              dragger.style[!isVertical ? 'left' : 'top'] = (pos - rangeOffset - (draggerWidth / 2)) + 'px';
              cachePosition = Math.round(((pos - rangeOffset) / rangeWidth) * 100);
              cachePositionPixel = Math.round(((cachePosition / 100) * (config.max - config.min)) + config.min);
              config.drag(cachePositionPixel);
            }
          }
    
          function initDragger() {
            var woh = dragger[!isVertical ? 'offsetWidth' : 'offsetHeight'];
            cachePosition = ((config.value / 100) * range[!isVertical ? 'offsetWidth' : 'offsetHeight']);
            dragger.style[!isVertical ? 'left' : 'top'] = (cachePosition - (woh / 2)) + 'px';
            config.drag(config.value);
          }
    
          range.appendChild(dragger);
          elem.appendChild(range);
    
          initDragger();
    
        }
    
        var slide = document.getElementById('range-slider');
        var result = document.getElementById('results');
        var button = document.getElementById('button');
    
        var rng = rangeSlider(slide, {
          value: 0,
          drag: function(v) {
        var d = tickDragDist();
            document.getElementById('results').value = 'Relative: '+v+'% | Fixed: '+d;
        
          },
          max: 100
        });
    
    
    
    
    // Calculates the distance between .tick and .dragger
    
    function tickDragDist() {
     var tick = document.querySelector('.tick');
     var drag = document.querySelector('.dragger');
     var tickPos = tick.getBoundingClientRect();
     var dragPos = drag.getBoundingClientRect();
     var tickX = tickPos.left + tickPos.width/2;
     var tickY = tickPos.top + tickPos.height/2;
     var dragX = dragPos.left + dragPos.width/2;
     var dragY = dragPos.top + dragPos.height/2;
     var distSQ = Math.pow(tickX - dragX, 2) + Math.pow(tickY - dragY, 2);
     var dist = Math.sqrt(distSQ);
     return dist;
    }
    .range-slider-track { height: 20px; }
    
    .range-slider-track:before { content: ""; display: block; width: 100%; height: 2px; background-color: black; }
    
    .range-slider-track .dragger { display: block; width: 10px; height: inherit; position: relative; background-color: red; }
    
    .range-slider-track .tick { height: 5px; width: 0; position: absolute; left: 0; top: calc(50% - 2.5px); display: inline-block; }
<div id="range-slider">
  <label for="range-slider" class="tick">0</label>
</div>
<label for="results">Lengths:
  <output id="results"></output>
px</label>
A. Meshu
  • 4,053
  • 2
  • 20
  • 34
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Thanks for the answer! But there's a way to do it without loosing the precision. If you look at the last example here: https://github.com/tovic/simple-custom-range-slider#examples it shows how to do it. My question is how can I implement that in the actual source code so I don't have to call the functions every time. – Horay Dec 20 '15 at 21:34
  • Oh I see, you want to modify rangeSlider so it'll return px by default. – zer00ne Dec 21 '15 at 00:08
  • There's no major difference between using as a declared function or an expression as far as just spitting out a converted value. I think in this case calling a function is negligible if your'e worried about memory or speed. – zer00ne Dec 21 '15 at 00:17
  • I think it's just more convenient to have all the relevant code in 1 place. – Horay Dec 21 '15 at 00:20
  • You could wrap it all together in this `(function() { })();` an Immediately Invoked Function Expression (IIFE). Very modular and no globalspace poisoning. – zer00ne Dec 21 '15 at 00:29