jQuery UI Sliders - Select overlapping sliders based on dragging direction
Asked Answered
G

7

9

I have this simple jQuery UI Slider setup with a range and a two default values that overlap. The entire thing (with a few bells and whistles) can be found in this jsfiddle: http://jsfiddle.net/yijiang/XeyGS/

$('#slider').slider({
    min: 1,
    max: 11,
    range: true,
    values: [4, 4]
});

The problem with this is that when you attempt the drag the single visible handle to the right, it fails, because jQuery UI always places the minimum handle on top. This is obviously bad, for a number of reasons.

Is there a way to allow jQuery UI to choose which handle to drag depending on which direction the user starts dragging?

Gand answered 21/11, 2010 at 18:23 Comment(1)
I have just updated my answer please see hereTrowbridge
H
6

Ah, I like it to eat through 11k libs, don't you too? :)

Note: The following is for jQuery UI 1.8.5

Anyways, here's a pretty clean solution:

// add some stuff to the slider instance
this._handleIndex = null;
this._handleStartValue = -1; 

// remember the starting values in _mouseCapture
this._handleStartValue = this.values( this._handleIndex ); 
this._mouseDownOffset = this._normValueFromMouse( { x: event.pageX, y: event.pageY } );


// modify _mouseDrag
oldValue = this.values( this._handleIndex ),
curValue;

curValue = this.values(this._handleIndex);
if ( curValue === oldValue && this._handleStartValue !== -1 ) {
    if ( normValue - this._mouseDownOffset > 0
         && ( curValue === this.values( ( this._handleIndex + 1 ) % 2 ) )
         && oldValue === this._handleStartValue) {

        this._handleIndex = (this._handleIndex + 1) % 2;
    }

} else {
    this._handleStartValue = - 1
}

// reset everything in _mouseStop
this._handleIndex = null;
this._handleStartValue = -1; 

And that's all there is to it, oh how it works, of course:

  1. Save the starting mouse offset as well as the value of the initially select handle
  2. When dragging compare the old value to the current value of the active handle and also check whether the start position is valid
  3. In case there's no difference we check whether there would be a difference if the active handle could be dragged further
  4. If that's the case we check whether both handles have the same value, that means they're on top of each other
  5. Now we check if the the currently selected handle hasn't been dragged yet
  6. And finally if all that is true, we switch the handles
  7. In case the user now changes the value we invalidate our start position so that there's no more switching between the handles

And for your pleasure here's a diff:

9960c9960,9962
< 
---
>       
>       this._handleIndex = null;
>       this._handleStartValue = -1; 
10215a10218,10219
>         this._handleStartValue = this.values( this._handleIndex ); 
>       this._mouseDownOffset = this._normValueFromMouse( { x: event.pageX, y: event.pageY } );
10243c10247,10249
<           normValue = this._normValueFromMouse( position );
---
>           normValue = this._normValueFromMouse( position ),
>           oldValue = this.values( this._handleIndex ),
>           curValue;
10246c10252,10263
< 
---
>       curValue = this.values(this._handleIndex);
>       if ( curValue === oldValue && this._handleStartValue !== -1 ) {
>           if ( normValue - this._mouseDownOffset > 0
>                && ( curValue === this.values( ( this._handleIndex + 1 ) % 2 ) )
>                && oldValue === this._handleStartValue) {
>             
>               this._handleIndex = (this._handleIndex + 1) % 2;
>           }
>         
>       } else {
>           this._handleStartValue = - 1
>       }
10257a10275,10276
>       this._handleStartValue = -1; 
>       this._handleIndex = null;

Save it to ui.diff then do patch -i ui.diff jquery-ui.js.

Hit answered 22/11, 2010 at 19:36 Comment(0)
G
4

With apologies to Ivo there, whose excellent answer there I'm sure took a lot of effort to create. The problem is that I couldn't apply that solution, because I'm already on 1.8.6, I can't find the 1.8.5 sources anywhere to apply it to, and somehow I just couldn't get the patch to work for 1.8.6, even when I tried adding in the lines manually.

The following is my solution to the problem. It is simpler than Ivo's, but that might be because of differences between the two point releases. The solution is as follows:

  1. Splice in an additional variable to keep track of the original values
  2. When the conditions are met, (two handles, same value, new value larger than current), to flip an internal variable (_handleIndex) around, causing the maximum handle to be used for reference for the internal _slide function instead of the first.

The diff is as follows:

46a47,48
>       
>       this._originalVal;
310a313,314
>       
>       this._originalVal = this.values();
323a328,331
>       
>       if(this._originalVal[0] === this._originalVal[1] && normValue > this._originalVal[0]){
>           this._handleIndex = 1;
>       }

The first part should be inserted into the initialising area, where the variables are declared, the second into the _mouseCapture function right before the call to _slide, and the last part into the _mouseDrag function, also right before the call to _slide.

Here's a working example with the patch: http://www.jsfiddle.net/HcGXZ/

A copy of the patched jQuery UI 1.8.6 Slider file can be found here: http://dl.dropbox.com/u/1722364/jQuery%20UI%20Slider%20Patch/jquery.ui.slider.patched.js

As always, this patch has not been extensively tested and is not guaranteed to work for all scenarios.

Gand answered 23/11, 2010 at 10:14 Comment(1)
Awesome, just utilised this, with the addition of Dr. Gibbs answer (https://mcmap.net/q/1144743/-jquery-ui-sliders-select-overlapping-sliders-based-on-dragging-direction) in the latest jQuery UI (1.8.18). Thanks!M
T
2

I'd say this is a bug, doubly so because there's already logic preventing a ‘deadlock’ from happening when both slider handles are at the minimum / far left position:

// workaround for bug #3736 (if both handles of a range are at 0,
// the first is always used as the one with least distance,
// and moving it is obviously prevented by preventing negative ranges)
if( o.range === true && this.values(1) === o.min ) {
  index += 1;
  closestHandle = $( this.handles[index] );
}

It looks like ticket #3736 is still open, with mentioning of the specific problem you're seeing.

Transmigrate answered 22/11, 2010 at 18:25 Comment(0)
E
2

I tried Yi Jiang's solution with 1.8.12 and it broke on a slider with a single handle, but worked perfectly after I changed

this._originalVal = this.values();

to

this._originalVal = this.options.values === null ? this.value() : this.values();
Evite answered 5/5, 2011 at 16:37 Comment(0)
M
2

Put this into the initialising area, where the variables are declared:

this._originalVal;

Put this into the _mouseCapture function right before the call to _slide:

this._originalVal = this.values();

And this into the _mouseDrag function, also right before the call to _slide

if (this._originalVal[0] === this._originalVal[1]
      && normValue > this._originalVal[0]) {
    this._handleIndex = 1;
} else if (this._originalVal[0] === this._originalVal[1]
             && normValue < this._originalVal[0]) {
    this._handleIndex = 0;
}

I just added an else if to the above code; it's working fine in jQuery 1.8.14 for both left and right button handlers.

Mongeau answered 1/7, 2013 at 13:30 Comment(0)
E
1

Another solution: Test for both values and if they are the same, return to the last step.

 $("#slider").slider({ 
            range:true, 
            min:0, 
            max:250,
            step: 25,
            slide: function( event, ui ) {
                if (ui.values[0] == ui.values[1]){
                    return false;
                }else{
                    $( "#amount" ).html( "&euro;" + ui.values[0] + " - &euro;" + ui.values[1] );
                }
            }
      });
Electrocardiograph answered 9/10, 2011 at 21:32 Comment(0)
H
1

Here's a new version for jQuery UI 1.10.2

Patch file: http://pastebin.com/Dsxw8NYR

Complete file: http://pastebin.com/ibSpAAX1

Howenstein answered 17/4, 2013 at 9:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.