From cc83eb8ef9bf9805129d361f06898c4c6045abbf Mon Sep 17 00:00:00 2001 From: Micah Miller-Eshleman Date: Mon, 15 Jan 2018 15:49:24 -0800 Subject: [PATCH 1/4] Ensure clicks on .noUi-base pseudo-elements update value. Fixes #842. Solves bug where pseudo-elements can be used for 'contained handles' styles but cannot be reliably clicked on if outside the bounds of .noUi-base --- src/js/scope_helpers.js | 9 ++++- tests/slider.html | 1 + tests/slider_contained_handles.js | 63 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/slider_contained_handles.js diff --git a/src/js/scope_helpers.js b/src/js/scope_helpers.js index 1ae13f7f..5ae4feab 100644 --- a/src/js/scope_helpers.js +++ b/src/js/scope_helpers.js @@ -143,6 +143,13 @@ function calcPointToPercentage ( calcPoint ) { var location = calcPoint - offset(scope_Base, options.ort); var proposal = ( location * 100 ) / baseSize(); + + // If a .noUi-base pseudo-element is clicked (e.g. for contained handles), + // the proposed percentage should never exceed 100. (see #842) + if ( proposal > 100 ) { + proposal = 100; + } + return options.dir ? 100 - proposal : proposal; } @@ -161,7 +168,7 @@ var pos = Math.abs(scope_Locations[index] - proposal); - if ( pos < closest ) { + if ( pos < closest || (pos === 100 && closest === 100) ) { handleNumber = index; closest = pos; } diff --git a/tests/slider.html b/tests/slider.html index 111e9fc7..52ba8351 100644 --- a/tests/slider.html +++ b/tests/slider.html @@ -68,6 +68,7 @@ + diff --git a/tests/slider_contained_handles.js b/tests/slider_contained_handles.js new file mode 100644 index 00000000..2271381a --- /dev/null +++ b/tests/slider_contained_handles.js @@ -0,0 +1,63 @@ + function simulateMousedown(clickTarget, x, y) { + // Based on https://stackoverflow.com/a/19570419/1367431 + var clickEvent= document.createEvent('MouseEvents'); + clickEvent.initMouseEvent( + 'mousedown', true, true, window, 0, + 0, 0, x, y, false, false, + false, false, 0, null + ); + clickTarget.dispatchEvent(clickEvent); + } + + QUnit.test( "Slider with contained handles", function( assert ){ + + Q.innerHTML = '\ +
\ + \ + '; + + var slider = document.getElementById('slider1'); + + noUiSlider.create(slider, { + start: 50, + connect: [true, false], + range: { + 'min': 0, + 'max': 100 + }, + animate: false, + animationDuration: 0 + }); + + // Click the leftmost edge of the bar + var rect = slider.getBoundingClientRect(), + base = slider.querySelector('.noUi-base'), + midY = rect.y + rect.height / 2; + + // These clicks aren't on .noUi-base itself but rather its child pseudo-elements. + // The "contained handles" style relies on clickable pseudo-elements. If the click is + // outside the bounds of .noUi-base, it should snap to the end (0% or 100%). + + // Click on leftmost edge of slider. + simulateMousedown(base, rect.x + 1, midY); + assert.strictEqual(slider.noUiSlider.get(), '0.00', 'Click near left edge should update value to 0'); + + // Click on rightmost edge of slider + simulateMousedown(base, rect.x + rect.width - 1, midY); + assert.strictEqual(slider.noUiSlider.get(), '100.00', 'Click near right edge should update value to 100'); + + }); From ca913314b7dca8c3e910b0dbe4ce97e046bc50be Mon Sep 17 00:00:00 2001 From: Micah Miller-Eshleman Date: Tue, 16 Jan 2018 16:06:21 -0800 Subject: [PATCH 2/4] Handle click on .noUi-base pseudoelement to the left of .noUi-base --- src/js/scope_helpers.js | 9 ++++----- tests/slider_contained_handles.js | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/js/scope_helpers.js b/src/js/scope_helpers.js index 5ae4feab..ef24928e 100644 --- a/src/js/scope_helpers.js +++ b/src/js/scope_helpers.js @@ -144,11 +144,10 @@ var location = calcPoint - offset(scope_Base, options.ort); var proposal = ( location * 100 ) / baseSize(); - // If a .noUi-base pseudo-element is clicked (e.g. for contained handles), - // the proposed percentage should never exceed 100. (see #842) - if ( proposal > 100 ) { - proposal = 100; - } + // Clamp proposal between 0% and 100% + // Out-of-bound coordinates may occur when .noUi-base pseudo-elements + // are used (e.g. contained handles feature) + proposal = Math.max(0, Math.min(100, proposal)); return options.dir ? 100 - proposal : proposal; } diff --git a/tests/slider_contained_handles.js b/tests/slider_contained_handles.js index 2271381a..428a2792 100644 --- a/tests/slider_contained_handles.js +++ b/tests/slider_contained_handles.js @@ -60,4 +60,8 @@ simulateMousedown(base, rect.x + rect.width - 1, midY); assert.strictEqual(slider.noUiSlider.get(), '100.00', 'Click near right edge should update value to 100'); + // Click leftmost edge again + simulateMousedown(base, rect.x + 1, midY); + assert.strictEqual(slider.noUiSlider.get(), '0.00', 'Click near left edge should update value to 0'); + }); From b5f36dd7bfae6488069480bea390200b25a0677d Mon Sep 17 00:00:00 2001 From: Micah Miller-Eshleman Date: Wed, 17 Jan 2018 18:22:09 -0800 Subject: [PATCH 3/4] Use existing limit() helper function --- src/js/scope_helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/scope_helpers.js b/src/js/scope_helpers.js index ef24928e..200257d9 100644 --- a/src/js/scope_helpers.js +++ b/src/js/scope_helpers.js @@ -147,7 +147,7 @@ // Clamp proposal between 0% and 100% // Out-of-bound coordinates may occur when .noUi-base pseudo-elements // are used (e.g. contained handles feature) - proposal = Math.max(0, Math.min(100, proposal)); + proposal = limit(proposal); return options.dir ? 100 - proposal : proposal; } From 784f793be98e7a7804da3b24b98a8fc77a895dde Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sat, 20 Jan 2018 17:49:01 +0100 Subject: [PATCH 4/4] Fix wide origin causing overflow Take care to not break rtl documents --- src/js/scope.js | 2 +- src/js/scope_start.js | 4 ++++ src/nouislider.core.less | 12 +++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/js/scope.js b/src/js/scope.js index ed855a20..6643f462 100644 --- a/src/js/scope.js +++ b/src/js/scope.js @@ -82,7 +82,7 @@ // Convert the value to the slider stepping/range. scope_Values[handleNumber] = scope_Spectrum.fromStepping(to); - var rule = 'translate(' + inRuleOrder(toPct(transformDirection(to, 0)), '0') + ')'; + var rule = 'translate(' + inRuleOrder(toPct(transformDirection(to, 0) - scope_DirOffset), '0') + ')'; scope_Handles[handleNumber].style[options.transformRule] = rule; updateConnect(handleNumber); diff --git a/src/js/scope_start.js b/src/js/scope_start.js index f7c9d27e..f72f5419 100644 --- a/src/js/scope_start.js +++ b/src/js/scope_start.js @@ -21,3 +21,7 @@ function closure ( target, options, originalOptions ){ var scope_Document = target.ownerDocument; var scope_DocumentElement = scope_Document.documentElement; var scope_Body = scope_Document.body; + + // For horizontal sliders in standard ltr documents, + // make .noUi-origin overflow to the left so the document doesn't scroll. + var scope_DirOffset = (scope_Document.dir === 'rtl') || (options.ort === 1) ? 0 : 100; diff --git a/src/nouislider.core.less b/src/nouislider.core.less index 5196889c..22f67704 100644 --- a/src/nouislider.core.less +++ b/src/nouislider.core.less @@ -45,6 +45,12 @@ -webkit-transform-origin: 0 0; transform-origin: 0 0; } +/* Offset direction + */ +html:not([dir="rtl"]) .@{noUi-css-prefix}-horizontal .@{noUi-css-prefix}-origin { + left: auto; + right: 0; +} /* Give origins 0 height/width so they don't interfere with clicking the * connect elements. @@ -56,7 +62,7 @@ height: 0; } .@{noUi-css-prefix}-handle { - position: relative; + position: absolute; } .@{noUi-css-prefix}-state-tap .@{noUi-css-prefix}-connect, .@{noUi-css-prefix}-state-tap .@{noUi-css-prefix}-origin { @@ -87,6 +93,10 @@ left: -6px; top: -17px; } +html:not([dir="rtl"]) .@{noUi-css-prefix}-horizontal .@{noUi-css-prefix}-handle { + right: -17px; + left: auto; +} /* Styling; * Giving the connect element a border radius causes issues with using transform: scale