UNPKG

@vdt-jquery/jquery-rangeslider

Version:

A jQuery plugin that makes it easy to transform any html-element into a range slider with definable range that is consistent across browsers.

231 lines (195 loc) 8.42 kB
(function ($) { // Extension for creating range slider; supports multiple creations in one call $.fn.rangeslider = function (settings, callback) { // Allow callback to be the only argument if ($.isFunction(settings)) { callback = settings; settings = null; } return $(this).each(function () { // Get object from data let rangeslider = $(this).data('rangeslider'); if (!rangeslider) { // Create object let options = $.extend({}, $.fn.rangeslider.defaults, settings); rangeslider = new Rangeslider($(this), options); // Add object to data $(this).data('rangeslider', rangeslider); } // Call the callback, bound to the rangeslider if ($.isFunction(callback)) { callback.bind(rangeslider)(rangeslider); } }); } // Set defaults for extension $.fn.rangeslider.defaults = { // The starting point of the value range // Defaults to the data-property range-start with as fallback 0 getRangeStart: function (element) { let rangeStart = parseInt($(element).data('range-start')); if (isNaN(rangeStart)) { rangeStart = 0; } return rangeStart; }, // The number of steps // The number of possible values is step count + 1 // Defaults to the data-property step-count with as fallback 10 getStepCount: function (element) { let stepCount = parseInt($(element).data('step-count')); if (isNaN(stepCount) || stepCount <= 0) { stepCount = 10; } return stepCount; }, // The value increase for each step // The range end value will be range start + step count * step size // Defaults to the data-property step-size with as fallback 10 getStepSize: function (element) { let stepSize = parseInt($(element).data('step-size')); if (isNaN(stepSize) || stepSize <= 0) { stepSize = 10; } return stepSize; }, // The value when initializing the range slider // Values outside the range of valid values will be corrected when initializing // Defaults to the data-property value with as fallback 0 getValue: function (element) { let value = parseInt($(element).data('value')); if (isNaN(value)) { value = 0; } return value; }, // The field name to use for the generated hidden input field // Defaults to the data-property field-name getFieldName: function (element) { return $(element).data('field-name'); }, // The input is the hidden input field that contains the current value // It always gets at least the class 'rangeslider-input', type 'hidden' and name as specifified by data-property field-name getInputAttributes: function () { return {}; }, // The thumb is the element that drags // It always gets at least the class 'rangeslider-thumb' getThumbAttributes: function () { return {}; }, // The track is the background element along which the thumb drags // It always gets at least the class 'rangeslider-track' getTrackAttributes: function () { return {}; } } // Rangeslider implementation function Rangeslider(element, options) { this.element = element; this.options = options; this.rangeStart = options.getRangeStart(this.element); this.stepCount = options.getStepCount(this.element); this.stepSize = options.getStepSize(this.element); this.dragStatus = { dragging: false }; this.element.addClass('rangeslider'); this.element.children().hide(); this.thumb = this.createElement('<div>', 'rangeslider-thumb', this.options.getThumbAttributes()); this.track = this.createElement('<div>', 'rangeslider-track', this.options.getTrackAttributes()); this.input = this.createElement('<input>', 'rangeslider-input', this.options.getInputAttributes(), { name: this.options.getFieldName(this.element), type: 'hidden' }) this.element.append(this.track, this.thumb, this.input); // Set value only after elements are in place this.setValue(options.getValue(this.element)); // Event handlers this.element.click(this, eventHandlers.click); this.thumb.on('mousedown', this, eventHandlers.thumbDragStart); $(document).on('mousemove', this, eventHandlers.thumbDrag); $(document).on('mouseup', this, eventHandlers.thumbDragEnd); } // Create an element and merge attribute objects to attributes Rangeslider.prototype.createElement = function (tagName, className) { let attributes = $.extend.apply({}, Array.prototype.slice.call(arguments, 2)); let element = $(tagName, attributes).addClass(className); return element; } // Remove the entire slider; resets the base element to its former state Rangeslider.prototype.remove = function () { this.element.removeClass('rangeslider'); this.thumb.remove(); this.track.remove(); this.input.remove(); this.element.removeData('rangeslider'); this.element.off('click', eventHandlers.click); this.element.children().show(); } // Get step width in pixels based on current page layout Rangeslider.prototype.getStepWidth = function () { return this.element.width() / this.stepCount; } // Set current step and value Rangeslider.prototype.setStep = function (step) { if (this.step !== step) { this.step = step; this.thumb.css('left', 'calc(' + 100 / this.stepCount * step + '% - ' + this.thumb.outerWidth(true) / 2 + 'px)'); this.input.val(this.rangeStart + this.step * this.stepSize); this.element.trigger('rangeslider.valueChanged', this.getValue()); } } // Get current value Rangeslider.prototype.getValue = function () { return this.rangeStart + this.stepSize * this.step; } // Set current value Rangeslider.prototype.setValue = function (value) { if (value < this.rangeStart) { this.setStep(0); } else if (value > this.rangeStart + this.stepCount * this.stepSize) { this.setStep(this.stepCount); } else { this.setStep(Math.round((value - this.rangeStart) / this.stepSize)); } } // Event handlers should not be accessible from the object itself let eventHandlers = { thumbDragStart: function (e) { if (e.which !== 1) { return; } e.data.dragStatus.dragging = true; e.data.dragStatus.startPosition = e.pageX; e.data.dragStatus.startStep = e.data.step; }, thumbDrag: function (e) { if (!e.data.dragStatus.dragging) { return; } let delta = e.data.dragStatus.startPosition - e.pageX; let step = e.data.dragStatus.startStep - Math.round(delta / e.data.getStepWidth()); if (step < 0) { step = 0; } else if (step > e.data.stepCount) { step = e.data.stepCount; } e.data.setStep(step); }, thumbDragEnd: function (e) { e.data.dragStatus.dragging = false; }, click: function (e) { // If we're clicking the thumb, ignore the click here if (e.data.thumb.filter(e.target).length === 1) { return; } let step = Math.round(e.offsetX / e.data.getStepWidth()); e.data.setStep(step); } }; }(jQuery));