views:

67

answers:

2

Is there an easy way to add padding to the slider-bar of a jQuery UI Slider? I'ld like to be able to have the slider-handle not reach the ends of the bar. So if this is the regular left end:

no padding
\/
[]------------------

I would like it to have padding on the left (and right) end:

padding of 10px for instance
\/
--[]----------------

I tried this in the css file:

.ui-slider-horizontal { height: .8em; padding: 0 10px; }

But it seems jQuery UI Slider uses the the complete ( outerWidth() ) width to determine the sliding range of the handle, instead of the width without padding. I also tried hacking the code, replacing outerWidth() with width(), but it didn't do anything.

I explicitly don't want to resort to a hack of setting min and max values. The values should always keep working as expected.

Any suggestions?

Edit:

Following pkauko's comment; I am using the slider as an element that has three labels above it:

   not                      very
important    important    important
----[]-----------[]----------[]----

The above illustration indicates the positions where I want the handle to appear, centered underneath the labels. But the bar visually needs to stretch to the edges.

+2  A: 

Would the "snap to increments" coupled with "incompatible" (uneven to the snap values) min and max values give the desired effect? I know that this would more than likely lead to application side value conversion as this probably cannot be obtained using the values used now, but here's a "hack and slash" 'solution' if it works.

What I'm suggesting is set the min value to, say, 750. Snap value to 1200 and max value to 4050. That way the lowest snap is 450 higher than the minimum value and the slider should never reach the leftmost edge. Same for the max value, that's 3 x 1200 + 450 = 4050. The values used here are arbitrary and for example only.

Haven't tested this and have no idea if it works and I know this is not what you were looking for, but just an idea that came to mind, when I checked the examples of how jquery UI slider works at jqueryui.com.

Edit: couldn't let go of this and now I know this works in the way that it prevents the slider from reaching the ends of the slide. You just need to set the initial value accordingly to prevent the slider from being in one end or the other when the page loads.

pkauko
@pkauko: I am unable to wrap my head around as to why these values are needed (I'm not very sharp at this moment), but these seem to work. I also tried min 300, max 3900, step 1200, for instance, but that caused the handle to go outside the left side. Hmmm, strange. I'll divide by 10 though, which is basically the same. And although I was hoping for a solution where I didn't need to convert the selected value, I'll use this for now. Thank you!
fireeyedboy
+1  A: 

Ok, I've often wanted that myself, but never had the motivation actually to sit down and so something about it. Since you asked now, I did sit down and hacked jQuery UI's slider to add the padding options as you requested.

Note: I've only added it for horizontal sliders with a single handle. The other cases aren't any more complicated. I just didn't have the motivation to do the typing and subsequent testing to cover cases that probably weren't needed right now.

Include this code after you've included the original Slider from jQuery UI. The extension overwrites a couple of methods and adds the options 'paddingMin' and 'paddingMax' to the Slider widget (values have to be numerical and will be interpreted as pixels; this too can be easily extended to take other units, obviously, but again would imply more typing/testing for currently unrequested cases).

Simple example usage:

$('#slider').slider({ paddingMin: 50, paddingMax: 100 });

Extension hack code (provided as is, don't sue me):

(
    function($, undefined)
    {
        $.ui.slider.prototype.options =
            $.extend(
                {},
                $.ui.slider.prototype.options,
                {
                    paddingMin: 0,
                    paddingMax: 0
                }
            );

        $.ui.slider.prototype._refreshValue =
            function() {
                var
                    oRange = this.options.range,
                    o = this.options,
                    self = this,
                    animate = ( !this._animateOff ) ? o.animate : false,
                    valPercent,
                    _set = {},
                    elementWidth,
                    elementHeight,
                    paddingMinPercent,
                    paddingMaxPercent,
                    paddedBarPercent,
                    lastValPercent,
                    value,
                    valueMin,
                    valueMax;

                if (self.orientation === "horizontal")
                {
                    elementWidth = this.element.outerWidth();
                    paddingMinPercent = o.paddingMin * 100 / elementWidth;
                    paddedBarPercent = ( elementWidth - ( o.paddingMin + o.paddingMax) ) * 100 / elementWidth;
                }
                else
                {
                    elementHeight = this.element.outerHeight();
                    paddingMinPercent = o.paddingMin * 100 / elementHeight;
                    paddedBarPercent = ( elementHeight - ( o.paddingMin + o.paddingMax) ) * 100 / elementHeight;
                }

                if ( this.options.values && this.options.values.length ) {
                    this.handles.each(function( i, j ) {
                        valPercent =
                            ( ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100 )
                            * paddedBarPercent / 100 + paddingMinPercent;
                        _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
                        $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
                        if ( self.options.range === true ) {
                            if ( self.orientation === "horizontal" ) {
                                if ( i === 0 ) {
                                    self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
                                }
                                if ( i === 1 ) {
                                    self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
                                }
                            } else {
                                if ( i === 0 ) {
                                    self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
                                }
                                if ( i === 1 ) {
                                    self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
                                }
                            }
                        }
                        lastValPercent = valPercent;
                    });
                } else {
                    value = this.value();
                    valueMin = this._valueMin();
                    valueMax = this._valueMax();
                    valPercent =
                        ( ( valueMax !== valueMin )
                        ? ( value - valueMin ) / ( valueMax - valueMin ) * 100
                        : 0 )
                        * paddedBarPercent / 100 + paddingMinPercent;

                    _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";

                    this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );

                    if ( oRange === "min" && this.orientation === "horizontal" ) {
                        this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
                    }
                    if ( oRange === "max" && this.orientation === "horizontal" ) {
                        this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
                    }
                    if ( oRange === "min" && this.orientation === "vertical" ) {
                        this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
                    }
                    if ( oRange === "max" && this.orientation === "vertical" ) {
                        this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
                    }
                }
            };

        $.ui.slider.prototype._normValueFromMouse =
            function( position ) {
                var
                    o = this.options,
                    pixelTotal,
                    pixelMouse,
                    percentMouse,
                    valueTotal,
                    valueMouse;

                if ( this.orientation === "horizontal" ) {
                    pixelTotal = this.elementSize.width - (o.paddingMin + o.paddingMax);
                    pixelMouse = position.x - this.elementOffset.left - o.paddingMin - ( this._clickOffset ? this._clickOffset.left : 0 );
                } else {
                    pixelTotal = this.elementSize.height - (o.paddingMin + o.paddingMax);
                    pixelMouse = position.y - this.elementOffset.top - o.paddingMin - ( this._clickOffset ? this._clickOffset.top : 0 );
                }

                percentMouse = ( pixelMouse / pixelTotal );
                if ( percentMouse > 1 ) {
                    percentMouse = 1;
                }
                if ( percentMouse < 0 ) {
                    percentMouse = 0;
                }
                if ( this.orientation === "vertical" ) {
                    percentMouse = 1 - percentMouse;
                }

                valueTotal = this._valueMax() - this._valueMin();
                valueMouse = this._valueMin() + percentMouse * valueTotal;

                return this._trimAlignValue( valueMouse );
            };
    }
)(jQuery);
Thomas
I've seen in your edit, that you want to use it with pre-defined snapping values. I haven't tested that usage case yet, so I can't guarantee it works. I'm working on it, though. EDIT: Nevermind, apparently my implementation was solid enough to cover that case. :)
Thomas
@Thomas: this is working brilliantly! Thank you so much! The snapping is not particularly necessary, because I use `min: 1, max: 3` which distributes the positions evenly as it is already. But don't mind tweaking it a bit more, and post the new version, if you feel like it. :) Great job so far. Really nice!
fireeyedboy
Refactored the code a little, added support for vertical sliders.
Thomas
@Thomas: seriously awsome! You saved me a lot of work, because otherwise I would have dug into the code myself to. And since I wasn't familiar with jQuery widgets and stuff yet, it would easily have taken me more than a days work. Besides giving you the best answer award; do you have a paypal account? I'll gladly donate a few beers to you!
fireeyedboy
Sure, I won't turn down free beer. ;)[ ende(dot)mail(at)web(dot)de (yes, a dot in the username part)] Incidentally, I've applied for the patch to be pulled in to jQuery-UI's Git repository. No idea how long their revision procedure takes, though.
Thomas
@fireeyedboy: Oh, do you use @username to prompt someone with a message? I'm new to SO.
Thomas
Uh, no it's just a silly habit of me, to make sure you see it is addressed to you. :)\
fireeyedboy
Dammit, pushed enter too early. :( Anyway, I've transfered a few euros to you. But I just realized the bank account that is linked to PayPal is unfunded, so I'll have to fund that one first. :-/
fireeyedboy
Again! Enter too early. Pfff. Not my day today. ;-) I'll make sure you get your free beers. :) Probably will take a couple of days though. :-/
fireeyedboy
Cheers. ;) Glad I could help.
Thomas
@fireeyedboy: Got your donation! Thanks a lot! :)
Thomas
@Thomas: Alright, glad to hear. And you're welcome. It was well deserved.
fireeyedboy