<h1>(WORK IN PROGRESS)</h1>
<component class='multiRange'></component>
<component class='multiRange type1'></component>
html, body{ height:100%; }
body{ 
    font:16px Arial;
    display: flex; 
    flex-flow: column;
    align-items: center;
    justify-content: center; 
}
.multiRange{ margin:3em 0; width:40%; min-width:150px; 
    &.type1{
        $C: #EC5564;
        .multiRange__range{ 
            color:#EEE; 
            transition:50ms;
            
            &:nth-child(2){ color:$C; }
            
            .multiRange__handle{ 
                box-shadow:none; 
                background:$C;
                &__value{
                    background:$C;
                    color:white;
                    &::after{
                        border-color:$C transparent transparent;
                    }
                }
            }
        }
    }
}

//// COMPONENT //////////////////////////////

body.multiRange-grabbing{ cursor:grabbing; }
.multiRange{
    $height: 12px;
    
    // can be used to set automatic color to each range slice
    @mixin rangesColors( $size:6 ){
        @for $i from 1 through $size{
            &:nth-child(#{$i}){ 
                color:hsl($i * 35, 70%, 66%);
            }
        }
    }
    
    user-select:none;

    &__rangeWrap{
        height: $height;
        background:#E8E8E8;
        border-radius:3px;
        position:relative;
        z-index:5;
    }
    
    &__range{
        @include rangesColors(5);
        
        height: 100%;
        position:absolute;
        right:0;
        background:currentColor; 

        // hide the first handle
        &:first-child{
            > .multiRange__handle{ display:none; }
        }
        
        // a class is added when a handle is grabbed (mousedown)
        &.grabbed{
            > .handle{ background:black; }
        }

        .multiRange__handle{ 
            $out: -3px;
            width: 2px;
            
            position: absolute;
            top: $out;
            bottom: $out;
            left: -1px;
            
            cursor: grab;
            background: currentColor; 
            box-shadow: 1px 0 white, -1px 0 white;
            transition: .2s;
            
            &:active{ cursor:inherit; }
            
            &__value{
                $C: #333;
                position: absolute;
                transform: translate(-50%, -6px);
                min-width:10px;
                background: $C;
                color: white;
                padding: 2px 6px;
                top: -100%;
                left: 0;
                white-space: nowrap;
                font-size: 11px;
                text-align:center;
                border-radius:4px;
                cursor:default;
              //  pointer-events:none;

                &::after{
                    content: "";
                    position: absolute;
                    left: 50%;
                    bottom: -3px;
                    border-color: $C transparent transparent;
                    border-style: solid;
                    border-width: 3px 4px;
                    transform: translate(-50%, 50%);
                    color: $C;
                    font-size: 15px;
                }
                
                &--bottom{
                    &::after{
                    }
                }
            }
        }
        
        &:hover{
         //   box-shadow:0 0 0 1000px rgba(black, .1) inset;
        }
    }
    
    &__ticks{
        display:flex;
        justify-content: space-between;
        height: 6px;
        margin: 2px 0 0 0;
        font:10px Arial;
        cursor:default;
        
        > div{
            height:100%;
            width:1px;
            background:#DDD;
            color: #888;

            &:nth-child(5n - 4){
                height: 200%;
                &::before{
                    display:block;

                    content: attr(data-value);
                    transform:translate(-50%, 100%);
                    text-align: center;
                    width:40px;
                }
            }
        }
    }
}
View Compiled
// (C) Yair Even-Or 2017
// DO NOT COPY 

(function(){  
function extend(o1, o2){
    for( var key in o2 )
        if( o2.hasOwnProperty(key) )
            o1[key] = o2[key];
};
    
this.MultiRange = function MultiRange( placeholderElm, settings ){
    settings = typeof settings == 'object' ? settings : {}; // make sure settings is an 'object'

    this.settings = {
        minRange   : typeof settings.minRange == 'number' ? settings.minRange : 1,
        tickStep   : settings.tickStep || 5,
        step       : typeof settings.step == 'number' ? settings.step : 1,
        scale      : 100,
        min        : settings.min || 0,
        max        : settings.max || 100,
    }
    
    this.delta = this.settings.max - this.settings.min;
    
    // if "ticks" count was defined, re-calculate the "tickStep"
    if( settings.ticks )
        this.settings.tickStep = this.delta / settings.ticks;

    // a list of ranges (ex. [5,20])
    this.ranges = settings.ranges || [
        this.settings.min + this.settings.tickStep, 
        this.settings.max - this.settings.tickStep
    ]

    this.id = Math.random().toString(36).substr(2,9), // almost-random ID (because, fuck it)
    this.DOM = {}; // Store all relevant DOM elements in an Object
    extend(this, new this.EventDispatcher());
    this.build(placeholderElm);
    this.events.binding.call(this);
}

MultiRange.prototype = {
    build : function( placeholderElm ){
        var that = this,
            scopeClasses = placeholderElm.className.indexOf('multiRange') == -1 ? 
                            'multiRange ' + placeholderElm.className : 
                            placeholderElm.className; 

        this.DOM.scope = document.createElement('div');
        this.DOM.scope.className = scopeClasses;
      
        this.DOM.rangeWrap = document.createElement('div');
        this.DOM.rangeWrap.className = 'multiRange__rangeWrap';
        this.DOM.rangeWrap.innerHTML = this.getRangesHTML();

        this.DOM.ticks = document.createElement('div');
        this.DOM.ticks.className = 'multiRange__ticks';
        this.DOM.ticks.innerHTML = this.generateTicks();

        // append to Scope
        this.DOM.scope.appendChild(this.DOM.rangeWrap);
        this.DOM.scope.appendChild(this.DOM.ticks);

        // replace the placeholder component element with the real one
        placeholderElm.parentNode.replaceChild(this.DOM.scope, placeholderElm);
    },

    generateTicks(){
        var steps = (this.delta) / this.settings.tickStep,
            HTML = '',
            value, 
            i;

        for( i = 0; i <= steps; i++ ){
            value =(+this.settings.min) + this.settings.tickStep * i; // calculate tick value
            value = value.toFixed(1).replace('.0', ''); // cleaup
            HTML += '<div data-value="'+ value +'"></div>';
        }
        
        return HTML;
    },
    
    getRangesHTML(){
        var that = this,
            rangesHTML = '',
            ranges;
        
        this.ranges.unshift(0)
      //  if( this.ranges[0] > this.settings.min )
      //      this.ranges.unshift(this.settings.min)
        if( this.ranges[this.ranges.length - 1] < this.settings.max )
            this.ranges.push(this.settings.max);
        
        ranges = this.ranges;
      
        ranges.forEach(function(range, i){
            if( i == ranges.length - 1 ) return; // skip last ltem
            
            var leftPos = (range - that.settings.min) / (that.delta) * 100;
            
            // protection..
            if( leftPos < 0 )
                leftPos = 0;

           // range =  ranges[i+1] - range;
            rangesHTML += '<div data-idx="'+i+'" class="multiRange__range" \
                style="left:'+ leftPos +'%">\
                <div class="multiRange__handle">\
                    <div class="multiRange__handle__value">'+ range.toFixed(1).replace('.0', '') +'</div>\
                </div>\
            </div>';              
        })
        
        return rangesHTML;
    },
    
    /**
     * A constructor for exposing events to the outside
     */
    EventDispatcher : function(){
        // Create a DOM EventTarget object
        var target = document.createTextNode('');

        // Pass EventTarget interface calls to DOM EventTarget object
        this.off = target.removeEventListener.bind(target);
        this.on = target.addEventListener.bind(target);
        this.trigger = function(eventName, data){
            if( !eventName ) return;
            var e = new CustomEvent(eventName, {"detail":data});
            target.dispatchEvent(e);
        }
    },

    /**
     * DOM events listeners binding
     */
    events : {
        binding : function(){
            this.DOM.rangeWrap.addEventListener('mousedown', this.events.callbacks.onMouseDown.bind(this))
            //prevent anything from being able to be dragged
            this.DOM.scope.addEventListener("dragstart", function(e){ return false }); 
           // this.eventDispatcher.on('add', this.settings.callbacks.add)
        },
        callbacks : {
            onMouseDown : function(e){
                var target = e.target;
                if( !target ) return;
                
                if( target.className == 'multiRange__handle__value' )
                    target = target.parentNode;
                
                else if( target.className != 'multiRange__handle' )
                    return;
                
                // set some variables (so percentages could be calculated on mousemove)
                var _BCR = this.DOM.scope.getBoundingClientRect();
                this.offsetLeft = _BCR.left;
                this.scopeWidth = _BCR.width;
                this.DOM.currentSlice = target.parentNode;
                
                
                this.DOM.currentSlice.classList.add('grabbed');
                this.DOM.currentSliceValue = this.DOM.currentSlice.querySelector('.multiRange__handle__value');
                
                document.body.classList.add('multiRange-grabbing');
                
                // bind temporary events (save "bind" reference so events could later be removed)
                this.events.onMouseUpFunc = this.events.callbacks.onMouseUp.bind(this);
                this.events.mousemoveFunc = this.events.callbacks.onMouseMove.bind(this);

                window.addEventListener('mouseup', this.events.onMouseUpFunc)
                window.addEventListener('mousemove', this.events.mousemoveFunc)
            },

            onMouseUp : function(e){
                this.DOM.currentSlice.classList.remove('grabbed');
                window.removeEventListener('mousemove', this.events.mousemoveFunc);
                window.removeEventListener('mouseup', this.events.onMouseUpFunc);
                document.body.classList.remove('multiRange-grabbing');
   
                // publish the event
                var value = parseInt( this.DOM.currentSlice.style.left );
                this.trigger('changed', {idx:+this.DOM.currentSlice.dataset.idx, value:value, ranges:this.ranges})
                
                this.DOM.currentSlice = null;
            },

            onMouseMove : function(e){
                if( !this.DOM.currentSlice ){
                    window.removeEventListener('mouseup', this.events.onMouseUpFunc);
                    return;
                }
                
                // do not continue if the mouse was overflowing of the left or the right side of the range
                if(  e.clientX < this.offsetLeft || e.clientX > (this.offsetLeft + this.scopeWidth) )
                    return;
                
                var that = this,
                    value, // the numeric value
                    // minLeftPerc = this.settings.minRange/this.delta*100,
                    // minRightPerc = (this.delta - this.settings.minRange)/this.delta*100,
                    xPosScopeLeft = e.clientX - this.offsetLeft, // the left percentage value
                    leftPrecentage = xPosScopeLeft / this.scopeWidth * 100,
                    prevSliceValue = this.ranges[+this.DOM.currentSlice.dataset.idx - 1],
                    nextSliceValue = this.ranges[+this.DOM.currentSlice.dataset.idx + 1];    
                
                value = this.settings.min + (this.delta/100*leftPrecentage);
                
                if( this.settings.step ){
                   // if( value%this.settings.step > 1 ) return;
                    value = Math.round((value) / this.settings.step ) * this.settings.step
                }
                
                
                // make sure a slice value doesn't go above the next slice value and not below the previous one
                if( value < prevSliceValue + this.settings.minRange )             
                     value = prevSliceValue + this.settings.minRange;
                if( value > nextSliceValue - this.settings.minRange )             
                     value = nextSliceValue - this.settings.minRange;
                
                // define min and max move points 
                if( value < (this.settings.min + this.settings.minRange) )             
                     value = this.settings.min + this.settings.minRange;
                if( value > (this.settings.max - this.settings.minRange) )             
                     value = this.settings.max - this.settings.minRange;
                
                leftPrecentage = (value - this.settings.min) / this.delta * 100;

                // update the DOM
                window.requestAnimationFrame(function(){  
                    if( that.DOM.currentSlice ){
                        that.DOM.currentSlice.style.left = leftPrecentage + '%';
                        that.DOM.currentSliceValue.innerHTML = value.toFixed(1).replace('.0', '');
                    }
                })
                // update "ranged" Array
                this.ranges[this.DOM.currentSlice.dataset.idx] = +value.toFixed(1);  
                
                // publish the event
                this.trigger('change', {idx:+this.DOM.currentSlice.dataset.idx, value:value, ranges:this.ranges})
            }
        }
    }
}
})(this);

//////// USAGE //////
var multiRange1 = new MultiRange(document.querySelectorAll('.multiRange')[0], {
    ranges : [15, 44, 77, 88],
    step   : 0
});

var multiRange2 = new MultiRange(document.querySelectorAll('.multiRange')[1], {
    min      : 150,
    max      : 5000,
    ticks    : 80,
    step     : 10,
    minRange : 50
});

multiRange1.on('changed', onrangeChanged);
multiRange2.on('changed', onrangeChanged);
               
function onrangeChanged(e){
    console.log( e, e.detail )
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.