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:
minRangeSizeandmaxRangeSize- 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 totruedragging the handle beyond may range width will automatically drag the other handle along; if you set it tofalsehandle 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 tolowMaxexcept 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.