Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #2812 from redaemn/flip-switch-improvements
Fix for issue #2756 - flip toggle slider animated snapping. Thanks redaemn!
  • Loading branch information
toddparker committed Oct 28, 2011
2 parents fde88b2 + ee05bb5 commit 6264d95
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 21 deletions.
2 changes: 1 addition & 1 deletion css/structure/jquery.mobile.forms.slider.css
Expand Up @@ -18,7 +18,7 @@ a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; }

div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; }
div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; }
a.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; }
a.ui-slider-handle-snapping { -webkit-transition: left 70ms linear; }
div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; }
div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; }
div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; }
Expand Down
57 changes: 46 additions & 11 deletions js/jquery.mobile.forms.slider.js
Expand Up @@ -70,12 +70,16 @@ $.widget( "mobile.slider", $.mobile.widget, {
handle: handle,
dragging: false,
beforeStart: null,
userModified: false
userModified: false,
mouseMoved: false
});

if ( cType == "select" ) {

slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );

// make the handle move with a smooth transition
handle.addClass( "ui-slider-handle-snapping" );

options = control.find( "option" );

Expand All @@ -99,7 +103,10 @@ $.widget( "mobile.slider", $.mobile.widget, {
// monitor the input for updated values
control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
.change( function() {
self.refresh( val(), true );
// if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
if (!self.mouseMoved) {
self.refresh( val(), true );
}
})
.keyup( function() { // necessary?
self.refresh( val(), true, true );
Expand All @@ -111,19 +118,31 @@ $.widget( "mobile.slider", $.mobile.widget, {
// prevent screen drag when slider activated
$( document ).bind( "vmousemove", function( event ) {
if ( self.dragging ) {
// self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
self.mouseMoved = true;

if ( cType === "select" ) {
// make the handle move in sync with the mouse
handle.removeClass( "ui-slider-handle-snapping" );
}

self.refresh( event );
self.userModified = self.userModified || self.beforeStart !== control[0].selectedIndex;

// only after refresh() you can calculate self.userModified
self.userModified = self.beforeStart !== control[0].selectedIndex;
return false;
}
});

slider.bind( "vmousedown", function( event ) {
self.dragging = true;
self.userModified = false;
self.mouseMoved = false;

if ( cType === "select" ) {
self.beforeStart = control[0].selectedIndex;
}

self.refresh( event );
return false;
});
Expand All @@ -134,14 +153,31 @@ $.widget( "mobile.slider", $.mobile.widget, {

self.dragging = false;

if ( cType === "select" ) {

if ( !self.userModified ) {
//tap occurred, but value didn't change. flip it!
handle.addClass( "ui-slider-handle-snapping" );
self.refresh( !self.beforeStart ? 1 : 0 );
if ( cType === "select") {

// make the handle move with a smooth transition
handle.addClass( "ui-slider-handle-snapping" );

if ( self.mouseMoved ) {

// this is a drag, change the value only if user dragged enough
if ( self.userModified ) {
self.refresh( self.beforeStart == 0 ? 1 : 0 );
}
else {
self.refresh( self.beforeStart );
}

}
else {
// this is just a click, change the value
self.refresh( self.beforeStart == 0 ? 1 : 0 );
}

}

self.mouseMoved = false;

return false;
}
});
Expand Down Expand Up @@ -215,8 +251,7 @@ $.widget( "mobile.slider", $.mobile.widget, {
refresh: function( val, isfromControl, preventInputUpdate ) {

if ( this.options.disabled || this.element.attr('disabled')) {
this.slider.addClass('ui-disabled');
return;
this.disable();
}

var control = this.element, percent,
Expand Down
132 changes: 123 additions & 9 deletions tests/unit/slider/slider_events.js
Expand Up @@ -161,6 +161,14 @@
ok( $("#enhancetest").trigger("create").find(".ui-slider").length, "enhancements applied" );
});

var createEvent = function( name, target, x, y ) {
var event = $.Event( name );
event.target = target;
event.pageX = x;
event.pageY = y;
return event;
}

test( "toggle switch should fire one change event when clicked", function(){
var control = $( "#slider-switch" ),
widget = control.data( "slider" ),
Expand All @@ -175,15 +183,6 @@
offset = handle.offset(),
currentValue = control[0].selectedIndex;

function createEvent( name, target, x, y )
{
var event = $.Event( name );
event.target = target;
event.pageX = x;
event.pageY = y;
return event;
}

control.bind( "change", changeFunc );

// The toggle switch actually updates on mousedown and mouseup events, so we go through
Expand All @@ -199,4 +198,119 @@
ok( control[0].selectedIndex !== currentValue, "value did change");
same( changeCount, 1, "change event should be fired once during a click" );
});

asyncTest( "toggle switch handle should snap in the old position if dragged less than half of the slider width, in the new position if dragged more than half of the slider width", function() {
var control = $( "#slider-switch" ),
widget = control.data( "slider" ),
slider = widget.slider,
handle = widget.handle,
width = handle.width(),
offset = null;

$.testHelper.sequence([
function() {
// initialize the switch
control.val('on').slider('refresh');
},

function() {
equals(handle.css('left'), '100%', 'handle starts on the right side');

// simulate dragging less than a half
offset = handle.offset();
slider.trigger( createEvent( "mousedown", handle[ 0 ], offset.left + width - 10, offset.top + 10 ) );
slider.trigger( createEvent( "mousemove", handle[ 0 ], offset.left + width - 20, offset.top + 10 ) );
slider.trigger( createEvent( "mouseup", handle[ 0 ], offset.left + width - 20, offset.top + 10 ) );
},

function() {
equals(handle.css('left'), '100%', 'handle ends on the right side');

// initialize the switch
control.val('on').slider('refresh');
},

function() {
equals(handle.css('left'), '100%', 'handle starts on the right side');

// simulate dragging more than a half
offset = handle.offset();
slider.trigger( createEvent( "mousedown", handle[ 0 ], offset.left + 10, offset.top + 10 ) );
slider.trigger( createEvent( "mousemove", handle[ 0 ], offset.left - ( width / 2 ), offset.top + 10 ) );
//slider.trigger( createEvent( "mousemove", handle[ 0 ], offset.left - width + 20, offset.top + 10 ) );
slider.trigger( createEvent( "mouseup", handle[ 0 ], offset.left - ( width / 2 ), offset.top + 10 ) );
},

function() {
equals(handle.css('left'), '0%', 'handle ends on the left side');

start();
}
], 500);
});

asyncTest( "toggle switch handle should not move if user is dragging and value is changed", function() {
var control = $( "#slider-switch" ),
widget = control.data( "slider" ),
slider = widget.slider,
handle = widget.handle,
width = handle.width(),
offset = null;

$.testHelper.sequence([
function() {
// initialize the switch
control.val('on').slider('refresh');
},

function() {
equals(handle.css('left'), '100%', 'handle starts on the right side');

// simulate dragging more than a half
offset = handle.offset();
slider.trigger( createEvent( "mousedown", handle[ 0 ], offset.left + 10, offset.top + 10 ) );
slider.trigger( createEvent( "mousemove", handle[ 0 ], offset.left - width + 20, offset.top + 10 ) );
},

function() {
notEqual(handle.css('left'), '0%', 'handle is not on the left side');
notEqual(handle.css('left'), '100%', 'handle is not on the right side');

// reset slider state so it is ready for other tests
slider.trigger( createEvent( "mouseup", handle[ 0 ], offset.left - width + 20, offset.top + 10 ) );

start();
}
], 500);
});

asyncTest( "toggle switch should refresh when disabled", function() {
var control = $( "#slider-switch" ),
handle = control.data( "slider" ).handle;

$.testHelper.sequence([
function() {
// set the initial value
control.val('off').slider('refresh');
},

function() {
equals(handle.css('left'), '0%', 'handle starts on the left side');

// disable and change value
control.slider('disable');
control.val('on').slider('refresh');
},

function() {
equals(handle.css('left'), '100%', 'handle ends on the right side');

// reset slider state so it is ready for other tests
control.slider('enable');

start();
}
], 500);
});

})(jQuery);

4 comments on commit 6264d95

@johnbender
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@toddparker

I'm a tad wary of making a big code change like this, especially given the low priority of the ticket it addresses. As an aside this also broke the build :(

@redaemn
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnbender

mmhh... it's strange... I ran the slider unit test file on its own and it worked; I didn't notice that running all the test suite caused a problem. I'll look into it.

@johnbender
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@redaemn

I just made a commit to correct the issue. In firefox the .css calls were returning pixel values so that required some juggling.

@redaemn
Copy link
Contributor

@redaemn redaemn commented on 6264d95 Nov 2, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnbender

OK! But I still don't understand why I ran the slider unit test file (/tests/unit/slider) on Chrome and it worked; when I ran it as part of the test suite (/tests/unit) it didn't. Any suggestion?

Please sign in to comment.