I'm going to provide you with the code that makes it possible to set bounds to range slider handles (minimum and maximum) and some more sugar along with it.
I needed to create a range slider that is a bit smarter than the one provided by jQuery UI library that only allows to set minimum and maximum values for the whole slider, but I needed to set a few things more. I had to provide a range slider that would allow users to select time range between midnight and midday the next day (36 hours all together). These were my requirements:
- Lower handle can only move to midnight the next day (so it can move between 00:00 and 1d 00:00) - this simply means that time range must start today
- Selected range must be at least 1 hour wide
- Selected range can't be wider than 24 hours all together
slide
handler, but tha wouldn't be reusable and it would also not allow me to do some additional things I implemented along. Want to know which ones?
Parsing jQuery UI code
The first thing was to delve deep into jQuery UI code and try to decipher its inner workings. When that was done I also had to come up with a solution to just change existing plugin code instead of providing my own plugin based on original one which would 99% copy it and just add some additions that I needed. But I accomplished both of these tasks so instead of coming up with my own range slider plugin I've come up with an extension to existing range slider.
Extension raw code
This extension code will only extend slider if it's loaded so it's somehow unobtrusive since it doesn't do anything when your page scripts don't include jQuery UI slider plugin.
1: /*
2: * jQuery UI Slider Range extension 1.1 (25 Oct 2011)
3: *
4: * This extension is based code provided by
5: * jQuery UI Slider version 1.8.13
6: * it may happen tht it won't work with newer versions
7: *
8: * Copyright (c) 2011 Robert Koritnik
9: * Licensed under the terms of the MIT and GPL-2.0 license
10: * http://www.opensource.org/licenses/mit-license.php
11: * http://www.opensource.org/licenses/GPL-2.0
12: *
13: */
14:
15: (function($) {
16: if ($.ui.slider)
17: {
18: // add minimum range length option
19: $.extend($.ui.slider.prototype.options, {
20: minRangeSize: 0,
21: maxRangeSize: 100,
22: autoShift: false,
23: lowMax: 100,
24: topMin: 0
25: });
26:
27: $.extend($.ui.slider.prototype, {
28: _slide: function(event, index, newVal) {
29: var otherVal,
30: newValues,
31: allowed,
32: factor;
33:
34: if (this.options.values && this.options.values.length)
35: {
36: otherVal = this.values(index ? 0 : 1);
37: factor = index === 0 ? 1 : -1;
38:
39: if (this.options.values.length === 2 && this.options.range === true)
40: {
41: // lower bound max
42: if (index === 0 && newVal > this.options.lowMax)
43: {
44: newVal = this.options.lowMax;
45: }
46:
47: // upper bound min
48: if (index === 1 && newVal < this.options.topMin)
49: {
50: newVal = this.options.topMin;
51: }
52:
53: // minimum range requirements
54: if ((otherVal - newVal) * factor < this.options.minRangeSize)
55: {
56: if (this.options.autoShift === true)
57: {
58: otherVal = newVal + this.options.minRangeSize * factor;
59: }
60: else
61: {
62: newVal = otherVal - this.options.minRangeSize * factor;
63: }
64: }
65:
66: // maximum range requirements
67: if ((otherVal - newVal) * factor > this.options.maxRangeSize)
68: {
69: if (this.options.autoShift === true)
70: {
71: otherVal = newVal + this.options.maxRangeSize * factor;
72: }
73: else
74: {
75: newVal = otherVal - this.options.maxRangeSize * factor;
76: }
77: }
78: }
79:
80: if (newVal !== this.values(index))
81: {
82: newValues = this.values();
83: newValues[index] = newVal;
84: newValues[index ? 0 : 1] = otherVal;
85:
86: // A slide can be canceled by returning false from the slide callback
87: allowed = this._trigger("slide", event, {
88: handle: this.handles[index],
89: value: newVal,
90: values: newValues
91: });
92: if (allowed !== false)
93: {
94: this.values(index, newVal, true);
95: this.values(index ? 0 : 1, otherVal, true);
96: }
97: }
98: }
99: else
100: {
101: if (newVal !== this.value())
102: {
103: // A slide can be canceled by returning false from the slide callback
104: allowed = this._trigger("slide", event, {
105: handle: this.handles[index],
106: value: newVal
107: });
108: if (allowed !== false)
109: {
110: this.value(newVal);
111: }
112: }
113: }
114: }
115: });
116: }
117: })(jQuery);
Additional slider configuration options
There are a few new slider options that this range slider uses to accomplish upper requirements:
minRangeSize
andmaxRangeSize
- provide these two if you want your range slider to have range width limitations; one sets minimum range width the other maximumautoShift
- this is a special option that sets range slider behaviour when any of your handles reaches maximum range width; if you set this option totrue
dragging the handle beyond may range width will automatically drag the other handle along; if you set it tofalse
handle will simply stop moving beyond maximum range widthlowMax
- sets the low handle upper bound value; you can set your lower range bound handle to not move over a certain value (in my case it wasn't allowed to move beyond midnight next day and this setting made it possible)topMin
- similar tolowMax
except that it sets minimum value for the upper range bound handle; I didn't have to use this one, but since I've set one bound it made sense to also provide limitation for the other.
Additional questions?
If you happen to use this extension and have additional questions, do let me know. Hope this extension will help many of you, since I've seen many have requested for bounds settings for range slider yet core jQuery UI team didn't support this functionality.
This is fantastic. I have made a small adjustment... and added the following to line 54 to add autoShift on the minimum too. Thanks for sharing.
ReplyDeleteif (this.options.autoShift === true)
{
otherVal = newVal + this.options.minRangeSize * factor;
}
else
{
newVal = otherVal - this.options.minRangeSize * factor;
}
@Aaron Vanderzwan: That's actually something that should be included in the first place.
ReplyDeleteWhen auto shifting is enabled for maximum range it should be for minimum as well.
Thanks for sharing. I'll put it in.
Thanks for putting this together. It handily resolved a problem.
ReplyDeleteI put your code into it's own .js file and included it along with the jquery ui code. I set the options like so within the .js file...
$.extend($.ui.slider.prototype.options, {
minRangeSize : 1,
maxRangeSize : 48,
autoShift : false,
lowMax : 23,
topMin : -23
});
But this makes the extension that you wrote specific to the slider. I can't reuse the .js file with another slider that has a different minRangeSize for example.
How do I set the options from within the .jsp file where the slider instance exists?
I tried something like this...
$(function() {
$("#slider-range").slider({
range: true,
min: -24,
max: 24,
values: [-10, 10],
slide: function(event, ui) {
//
});
// set the slider range extension options
$("#slider-range").slider.prototype {
minRangeSize : 1,
maxRangeSize : 48,
autoShift : false,
lowMax : 23,
topMin : -23
});
The above doesn't work though.
I can imagine why this doesn't work but I don't have a strong enough understanding of jquery extensions to get my head around it.
Thanks once again for putting this together and as well for any insight you can offer about setting the options.
Cheers,
Andrew
@Andrew: Just add my extension script after jquery UI. Then just use these additional options as if they were part of the original slider plugin when creating a new instance (and not changing prototype as you tried doing). Like so:
ReplyDelete$("#slider-range").slider({
range: true,
min: -24,
max: 24,
values: [-10, 10],
// additional ones
minRangeSize : 1,
lowMax : 23,
topMin : -23,
// functions
slide: function(event, ui) {
// functionality
}
});
And don't use defaults and lowMax and topMin because they're already constrained by the minimum range width.
But... Try with and without if it works in both cases because you're using negative values, so it may be tricky actually. I admit I haven't tested for such values.
Hi Robert,
ReplyDeleteThanks, that was how I thought I should be setting the options, but when I tried it my syntax must have been wrong.
My colleague and I played around with your extension's option values for a little bit and he remarked that the slider is kind of game-like, that is to say that it could be useful for expressing and gathering information in a playful way.
By the way, the slider and extension worked fine with negative numbers. Also I already had the file with the extension after jquery-ui.js, but just to be sure I also placed it after the jquery-ui.css theme. Didn't test if that made any difference, just throwing it out for future readers.
Thanks again.
Andrew
@Andrew: I'm not sure I understand your colleague's remarks but I take your comment as extension's now working as expected. Thanks for letting me know that it works well with negatives so I don't have to test it myself. :)
ReplyDeleteGreat! Just what i was looking for. Thank you for sharing.
ReplyDeleteCheers from Italy!
@fpar: Thanks neighbour. :)
ReplyDeleteI am giving my first steps with jQuery and I used a slider to set a price range. The problem is that I put two different pictures on each end and do not see how can i configure
ReplyDeletethe css background properties...
Thanks,
Xa0z
Xa0z: I'm also using the same approach in my slider use. but I have to tell you that styling the slider is not part of my plugin. You can easily set CSS styles as you need. I suggest you check CSS classes of the generated slider elements by using ie. Firebug in FF...
ReplyDeleteI've set these styles:
.ui-slider
.ui-slider .ui-slider-range
.ui-slider .ui-slider-handle
.ui-slider .ui-slider-handle.ui-state-focus
.ui-slider .ui-slider-handle + .ui-slider-handle
.ui-slider .ui-slider-handle + .ui-slider-handle.ui-state-focus
Particularly the last couple of CSS selectors set a different handle image for the second slider handle.
Robert,
ReplyDeleteI noted that on Stackoverflow a while back you were looking for a slider that supported multiple ranges. As you said then JQueryUI Slider can do multiple handles but not multiple ranges.
Any luck finding such a extension/control?
Thanks,
John Kelleher
Hi John,
Deleteno not really. I needed a range exclude range selection so everything else except range you select. I would likely change existing jQuery UI slider to support this, but eventually I didn't need it.
usually i never comment on this things, but ey you deserve it. great job, so glad that you coded this
ReplyDeleteThanks with a long delay. :D
DeleteGreat extension. One comment though. I wish you would package this with a demo page explaining the new options. Thanks again.
ReplyDeleteI should be putting this on GitHub anyway. Will have to find the time some day.
DeleteCan we have same options for Date Range Slider as well?
ReplyDeleteThis slider doesn't imply any particular data type. You can apply it to dates if you wanted. I've used it with time, although it's internally working with integers.
DeleteCan you please provide me that example...
DeleteDoes this help?
DeleteDate range slider example
Actually I am using date range slider like this. Please check date slider here : http://ghusse.github.io/jQRangeSlider/
DeleteSo can we apply same to this?
To some extent yes. You can use my range slider extension, just be aware that my range slider doesn't support range dragging. Everything else seems pretty much the same.
DeleteWhat else seems to be bothering you? Minimum/maximum range width? Automatic range shifting when minimum/maximum range is reached? What seem to be the problem?
Yes, the Automatic range shifting when minimum/maximum range is reached. Autoshift does not work with this. I have tried
DeleteOk. So let me repeat your question just to make sure I understand what you want:
DeleteYou're implementing a specific plugin called jQRangeSlider and you would like to implement some of my functionality in it.
1. If your slider is basically existing jQuery UI slider widget but you've only added particular functionality to it, then you should likely be able to just add my code to your plugin and see where it breaks.
2. If your plugin is completely unrelated to existing jQuery UI slider widget then your best bet is to examine my code and see what it does and how it does it and implement it in similar fashion.
That's the most I can help. I don't intend to develop your plugin. I also think that if the main purpose of your plugin is to support dates, then you've flawed from the very beginning. Sliders should not imply any types, they should work with integers and it's up to users how to use those... If you do support range margin labels it would then be better to provide users the ability to provide a "valueFormatter" that returns a string to display however they want to interpret slider's values...
Just my opinion. If you on the other hand imply a type you'll have to support more and more types making your code more complex and covering all possible cases.