From d93a99accd3d2677e3a1c53216421f8409433a3d Mon Sep 17 00:00:00 2001 From: Jon Gunderson Date: Wed, 11 Dec 2024 18:27:28 -0600 Subject: [PATCH] Multi-Thumb Slider: Add ability to move thumbs by clicking rail (pull #3172) Fixed issue #3118. Updates the multi-thumb slider so clicking the rail moves one of the thumbs. The thumb closest to the click is moved. Also updates javascript code style to use const and let instead of var. --- .../examples/css/slider-multithumb.css | 1 + .../examples/js/slider-multithumb.js | 85 +++++++++++++------ 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/content/patterns/slider-multithumb/examples/css/slider-multithumb.css b/content/patterns/slider-multithumb/examples/css/slider-multithumb.css index deb555ba4b..5c384f6feb 100644 --- a/content/patterns/slider-multithumb/examples/css/slider-multithumb.css +++ b/content/patterns/slider-multithumb/examples/css/slider-multithumb.css @@ -29,6 +29,7 @@ .slider-multithumb .slider-group .range { fill: currentcolor; opacity: 0.4; + pointer-events: none; } .slider-multithumb .slider-group .thumb { diff --git a/content/patterns/slider-multithumb/examples/js/slider-multithumb.js b/content/patterns/slider-multithumb/examples/js/slider-multithumb.js index fcb2ae6288..dbbb735380 100644 --- a/content/patterns/slider-multithumb/examples/js/slider-multithumb.js +++ b/content/patterns/slider-multithumb/examples/js/slider-multithumb.js @@ -8,6 +8,7 @@ */ 'use strict'; + class SliderMultithumb { constructor(domNode) { this.isMoving = false; @@ -21,6 +22,9 @@ class SliderMultithumb { this.railNode = domNode.querySelector('.rail rect'); this.rangeNode = domNode.querySelector('.range rect'); + this.minSliderPosition = 0; + this.maxSliderPosition = 0; + this.minSliderNode = domNode.querySelector('[role=slider].minimum'); this.maxSliderNode = domNode.querySelector('[role=slider].maximum'); @@ -36,7 +40,7 @@ class SliderMultithumb { // Dimensions of the slider focus ring, thumb and rail this.svgWidth = 360; - this.svgHeight = 80; + this.svgHeight = 100; this.valueTop = 24; this.valueHeight = this.minSliderValueNode.getBoundingClientRect().height; @@ -122,6 +126,8 @@ class SliderMultithumb { this.maxSliderNode.addEventListener('focus', this.onSliderFocus.bind(this)); this.maxSliderNode.addEventListener('blur', this.onSliderBlur.bind(this)); + this.railNode.addEventListener('click', this.onRailClick.bind(this)); + this.moveSliderTo(this.minSliderNode, this.getValue(this.minSliderNode)); this.moveSliderTo(this.maxSliderNode, this.getValue(this.maxSliderNode)); } @@ -149,30 +155,29 @@ class SliderMultithumb { } isInRange(sliderNode, value) { - let valueMin = this.getValueMin(sliderNode); - let valueMax = this.getValueMax(sliderNode); + const valueMin = this.getValueMin(sliderNode); + const valueMax = this.getValueMax(sliderNode); return value <= valueMax && value >= valueMin; } isOutOfRange(value) { - let valueMin = this.getValueMin(this.minSliderNode); - let valueMax = this.getValueMax(this.maxSliderNode); + const valueMin = this.getValueMin(this.minSliderNode); + const valueMax = this.getValueMax(this.maxSliderNode); return value > valueMax || value < valueMin; } getXFromThumb(node) { - var points = node.getAttribute('points').split(','); + const points = node.getAttribute('points').split(','); return parseInt(points[0]); } moveSliderTo(sliderNode, value) { - var valueMax, - valueMin, - pos, - x, - points = '', - width, - dollarValue; + let valueMax; + let valueMin; + let pos; + let points = ''; + let x; + let width; if (this.isMinSlider(sliderNode)) { valueMin = this.getValueMin(this.minSliderNode); @@ -185,7 +190,7 @@ class SliderMultithumb { value = Math.min(Math.max(value, valueMin), valueMax); sliderNode.setAttribute('aria-valuenow', value); - dollarValue = '$' + value; + const dollarValue = `$${value}`; pos = this.railX; pos += Math.round( @@ -194,6 +199,8 @@ class SliderMultithumb { ); if (this.isMinSlider(sliderNode)) { + this.minSliderPosition = pos; + // update ARIA attributes this.minSliderValueNode.textContent = dollarValue; this.maxSliderNode.setAttribute('aria-valuemin', value); @@ -209,7 +216,7 @@ class SliderMultithumb { // Position value width = this.minSliderValueNode.getBoundingClientRect().width; - pos = pos + (this.thumbWidth - width) / 2; + pos += (this.thumbWidth - width) / 2; if (pos + width > this.maxSliderLeft - 2) { pos = this.maxSliderLeft - width - 2; } @@ -217,6 +224,8 @@ class SliderMultithumb { this.minSliderRight = pos; } else { // update label and ARIA attributes + this.maxSliderPosition = pos; + this.maxSliderValueNode.textContent = dollarValue; this.minSliderNode.setAttribute('aria-valuemax', value); @@ -229,7 +238,10 @@ class SliderMultithumb { points += ` ${pos + this.thumb2Width},${this.thumbBottom}`; this.maxSliderThumbNode.setAttribute('points', points); - width = this.maxSliderValueNode.getBoundingClientRect().width; + width = Math.max( + 0, + this.maxSliderValueNode.getBoundingClientRect().width + ); pos = pos + this.thumbWidth + (this.thumbWidth - width) / 2; if (pos - width < this.minSliderRight + 2) { pos = this.minSliderRight + width + 2; @@ -249,11 +261,11 @@ class SliderMultithumb { } onSliderKeydown(event) { - var flag = false; - var sliderNode = event.currentTarget; - var value = this.getValue(sliderNode); - var valueMin = this.getValueMin(sliderNode); - var valueMax = this.getValueMax(sliderNode); + let flag = false; + const sliderNode = event.currentTarget; + const value = this.getValue(sliderNode); + const valueMin = this.getValueMin(sliderNode); + const valueMax = this.getValueMax(sliderNode); switch (event.key) { case 'ArrowLeft': @@ -326,14 +338,14 @@ class SliderMultithumb { this.movingSliderNode && this.domNode.contains(event.target) ) { - var x = this.getSVGPoint(event).x - this.railX; + let x = this.getSVGPoint(event).x - this.railX; if (this.isMinSliderMoving) { x = Math.max(0, x - this.thumbWidth / 3); } else { x = Math.max(0, x - (5 * this.thumbWidth) / 3); } x = Math.min(x, this.railWidth - this.thumbWidth); - var value = Math.round( + const value = Math.round( (x * this.sliderDiffValue) / (this.railWidth - this.thumbWidth) ); this.moveSliderTo(this.movingSliderNode, value); @@ -347,11 +359,34 @@ class SliderMultithumb { this.isMoving = false; this.movingSliderNode = false; } + + // handle click event on the rail + onRailClick(event) { + const x = this.getSVGPoint(event).x - this.railX; + const diffMin = x - this.minSliderPosition - (3 * this.thumbWidth) / 2; + const diffMax = this.maxSliderPosition - x; + const sliderNode = + (x < this.minSliderPosition || diffMin < diffMax) && + x < this.maxSliderPosition + ? this.minSliderNode + : this.maxSliderNode; + + let p = Math.max(0, x - this.thumbWidth); + p = Math.min(p, this.railWidth - this.thumbWidth); + const value = Math.round( + (p * this.sliderDiffValue) / (this.railWidth - this.thumbWidth) + ); + + this.moveSliderTo(sliderNode, value); + + event.preventDefault(); + event.stopPropagation(); + } } // Initialize Multithumb Slider widgets on the page -window.addEventListener('load', function () { - var slidersMultithumb = document.querySelectorAll('.slider-multithumb'); +window.addEventListener('load', () => { + const slidersMultithumb = document.querySelectorAll('.slider-multithumb'); for (let i = 0; i < slidersMultithumb.length; i++) { new SliderMultithumb(slidersMultithumb[i]);