Jessica Jessica - 4 months ago 15
CSS Question

sliderCursor not staying in slider

I'm trying to create a custom range slider. I'm having trouble keeping the slidersCursor in the slider.

Here's the relevant code:

var cursorPosition = 1 - clamp01((sliderDimention - (e.clientX - startPoint.left)) / sliderDimention);
sliderCursor.style.transform = 'translateX(' + (cursorPosition * sliderDimention - cursorRadius) + 'px)';


With the code above, when you drag the slider to the far right or left, the cursor goes halfway out of the slider on either side.

When I remove
- cursorRadius
, it goes too much to the right side. It stays in the slider when you drag to the left, but when you drag it to the right, it goes out of the slider.

When
cursorRadius
is equal to
cursor.offsetWidth
, it goes too much to the left side.

How can I make the sliderCursor stay in the slider when you drag to the far sides?

(Please don't post any JQuery or other "you can use 'this' plugin" answers. I'm doing this for learning purposes, and I want to create my own. Thanks!)

JSFiddle





function RangeSlider( /** DOM Elem */ parentElem) {
var wrapperElem = document.getElementsByClassName('wrapperElem')[0],
slider = document.getElementsByClassName('slider')[0],
sliderCursor = document.getElementsByClassName('sliderCursor')[0];

var sliderDimention = slider.offsetWidth,
cursorRadius = sliderCursor.offsetHeight / 2,
startPoint,
currentTarget;


function sliderDown(e) {
e.preventDefault();

currentTarget = null;
var sliderWithDescendents = wrapperElem.querySelectorAll('*');
for (var i = 0; i < sliderWithDescendents.length; i++) {
sliderWithDescendents[i]
if (sliderWithDescendents[i] === e.target || wrapperElem === e.target) {
currentTarget = wrapperElem.children[0];
break;
}
}
if (currentTarget === null) return;

startPoint = getOrigin(currentTarget);
sliderDimention = slider.offsetWidth;

window.addEventListener('mousemove', sliderMove);
sliderMove(e);
}

function sliderMove(e) {
var cursorPosition = 1 - clamp01((sliderDimention - (e.clientX - startPoint.left)) / sliderDimention);
sliderCursor.style.transform = 'translateX(' + (cursorPosition * sliderDimention - cursorRadius) + 'px)';
}

function mouseUpEvents() {
window.removeEventListener('mousemove', sliderMove);
}
wrapperElem.addEventListener('mousedown', sliderDown);
window.addEventListener('mouseup', mouseUpEvents);
}

var sliderTest = document.getElementById('sliderTest');
var test = new RangeSlider(sliderTest);






function clamp01(val) {
return Math.min(1, Math.max(0, val));
}

function getOrigin(elm) {
var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
top: 0,
left: 0
},
doc = elm && elm.ownerDocument,
body = doc.body,
win = doc.defaultView || doc.parentWindow || window,
docElem = doc.documentElement || body.parentNode,
clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
clientLeft = docElem.clientLeft || body.clientLeft || 0;

return {
left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
};
}

.wrapperElem {
height: 18px;
width: 100%;
cursor: pointer;
display: flex;
}
.slider {
height: 100%;
width: calc(100% - 62px);
border: 1px solid black;
}
.sliderCursor {
width: 14px;
height: 14px;
border-radius: 50%;
border: 2px solid black;
}

<div class="wrapperElem">
<div class="slider">
<div class="sliderCursor"></div>
</div>
</div>




Answer

It should be sliderCursor.style.transform = 'translateX(' + (cursorPosition * (sliderDimention - cursorRadius*2)) + 'px)';

You need the radius times x2 then the brackets are to perform the operations first then multiply by cursorPostions (which is from 0 to 1);

Although it works, I'd try clamp the final value (from [0 + radius] to [dimension - radius]). That way you won't get this weird drag where the mouse is in the left or right of the scroll_cursor. You want the mouse to be in the center of it at all times.

If you are doing it for learning, dissect rangeslider.js? It's very well coded.

function RangeSlider( /** DOM Elem */ parentElem) {
  var wrapperElem = document.getElementsByClassName('wrapperElem')[0],
    slider = document.getElementsByClassName('slider')[0],
    sliderCursor = document.getElementsByClassName('sliderCursor')[0];

  var sliderDimention = slider.offsetWidth,
    cursorRadius = sliderCursor.offsetHeight / 2,
    startPoint,
    currentTarget;


  function sliderDown(e) {
    e.preventDefault();

    currentTarget = null;
    var sliderWithDescendents = wrapperElem.querySelectorAll('*');
    for (var i = 0; i < sliderWithDescendents.length; i++) {
      sliderWithDescendents[i]
      if (sliderWithDescendents[i] === e.target || wrapperElem === e.target) {
        currentTarget = wrapperElem.children[0];
        break;
      }
    }
    if (currentTarget === null) return;

    startPoint = getOrigin(currentTarget);
    sliderDimention = slider.offsetWidth;

    window.addEventListener('mousemove', sliderMove);
    sliderMove(e);
  }

  function sliderMove(e) {
    var cursorPosition = 1 - clamp01((sliderDimention - (e.clientX - startPoint.left)) / sliderDimention);
        sliderCursor.style.transform = 'translateX(' + (cursorPosition * (sliderDimention - cursorRadius*2)) + 'px)';;
  }

  function mouseUpEvents() {
    window.removeEventListener('mousemove', sliderMove);
  }
  wrapperElem.addEventListener('mousedown', sliderDown);
  window.addEventListener('mouseup', mouseUpEvents);
}

var sliderTest = document.getElementById('sliderTest');
var test = new RangeSlider(sliderTest);






function clamp01(val) {
  return Math.min(1, Math.max(0, val));
}

function getOrigin(elm) {
  var box = (elm.getBoundingClientRect) ? elm.getBoundingClientRect() : {
      top: 0,
      left: 0
    },
    doc = elm && elm.ownerDocument,
    body = doc.body,
    win = doc.defaultView || doc.parentWindow || window,
    docElem = doc.documentElement || body.parentNode,
    clientTop = docElem.clientTop || body.clientTop || 0, // border on html or body or both
    clientLeft = docElem.clientLeft || body.clientLeft || 0;

  return {
    left: box.left + (win.pageXOffset || docElem.scrollLeft) - clientLeft,
    top: box.top + (win.pageYOffset || docElem.scrollTop) - clientTop
  };
}
.wrapperElem {
  height: 18px;
  width: 100%;
  cursor: pointer;
  display: flex;
}
.slider {
  height: 100%;
  width: calc(100% - 62px);
  border: 1px solid black;
}
.sliderCursor {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid black;
}
<div class="wrapperElem">
  <div class="slider">
    <div class="sliderCursor"></div>
  </div>
</div>

Comments