简体   繁体   中英

Make a custom range slider using jQuery

I have created a custom slider. I want to make the position of the marker change as I drag the slider handle. However the gap between the marker and the handle is getting wider as the value gets bigger.

I think the problem is to do with using margin-left / left or absolute / relative positioning.

Can anyone help me to figure out if the concept of the position of mine is wrong?

 $(function() { $('.h-rs-line').on('input', function() { var val = $(this).val(); var min = $(this).attr('min'); var max = $(this).attr('max'); var portion = (val - min) / (max - min); $('.h-rs-indicator').text(val); $('.h-rs-indicator').css('left', portion * $('.h-rs-line').width()); }); });
 body { background: #000; }.h-rs { position: relative; }.h-rs-index { color: #FFF; }.h-rs-container { display: flex; flex-direction: column; margin: 32px; }.h-rs-indicator { display: block; width: 50px; height: 35px; border-radius: 12px; text-align: center; line-height: 35px; background: #FFF; position: absolute; top: -30px; left: 0; margin-left: -16px; }.h-rs-indicator::after { content: ''; width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; border-top: 9px solid #FFF; position: absolute; top: 98%; left: 50%; margin-left: -8px; }.h-rs-line { width: 300px; margin-top: 24px; -webkit-appearance: none; border-radius: 24px; }.h-rs-line::-webkit-slider-runnable-track { -webkit-appearance: none; height: 7px; border-radius: 24px; }.h-rs-line::-webkit-slider-thumb { -webkit-appearance: none; background: #FFF; height: 18px; width: 18px; border-radius: 100%; box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25); margin-top: -5.5px; }.h-rs-index { display: flex; margin-top: 9px; width: 300px; justify-content: space-between; }.h-rs-index span:first-child { margin-left: 6.5px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="h-rs-container"> <div class="h-rs"> <span class="h-rs-indicator">0<span></span></span> <input class="h-rs-line" type="range" value="0" min="0" max="100" /> </div> <div class="h-rs-index"> <span>0</span><span>100</span> </div> </div>

Compensate for the thumb width

You forgot to compenstate for the size of the range slider thumb. The slider thumb can only travel the width of the slider minus the size of the thumb (so 300px minus ~18px). Because the thumb can only travel 282px, so should the label, or they will be out of sync.

Therefore this:

$('.h-rs-indicator').css('left', portion * $('.h-rs-line').width());

... should become this:

$('.h-rs-indicator').css('left', portion * ($('.h-rs-line').width() - 18));

... where 18 is the width of the range slider thumb in pixels.

 $(function() { $('.h-rs-line').on('input', function() { var val = $(this).val(); var min = $(this).attr('min'); var max = $(this).attr('max'); var portion = (val - min) / (max - min); $('.h-rs-indicator').text(val); $('.h-rs-indicator').css('left', portion * ($('.h-rs-line').width() - 18)); }); });
 body { background: #000; }.h-rs { position: relative; }.h-rs-index { color: #FFF; }.h-rs-container { display: flex; flex-direction: column; margin: 32px; }.h-rs-indicator { display: block; width: 50px; height: 35px; border-radius: 12px; text-align: center; line-height: 35px; background: #FFF; position: absolute; top: -30px; left: 0; margin-left: -16px; }.h-rs-indicator::after { content: ''; width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; border-top: 9px solid #FFF; position: absolute; top: 98%; left: 50%; margin-left: -8px; }.h-rs-line { width: 300px; margin-top: 24px; -webkit-appearance: none; border-radius: 24px; }.h-rs-line::-webkit-slider-runnable-track { -webkit-appearance: none; height: 7px; border-radius: 24px; }.h-rs-line::-webkit-slider-thumb { -webkit-appearance: none; background: #FFF; height: 18px; width: 18px; border-radius: 100%; box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25); margin-top: -5.5px; }.h-rs-index { display: flex; margin-top: 9px; width: 300px; justify-content: space-between; }.h-rs-index span:first-child { margin-left: 6.5px; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="h-rs-container"> <div class="h-rs"> <span class="h-rs-indicator">0<span></span></span> <input class="h-rs-line" type="range" value="0" min="0" max="100" /> </div> <div class="h-rs-index"> <span>0</span><span>100</span> </div> </div>


Cross-browser

Getting your range slider to look the same (and being the same size) in every browser involves a lot of CSS. This is explained very well in this article on CSS tricks: https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/


Non-jQuery

How about a rewrite from jQuery to non-jQuery? That could be much lighter... Something like this:

<input type="range" min="0" max="100" oninput="updateRangeSlider(this)" onchange="updateRangeSlider(this)" />

With this function in the footer:

function updateRangeSlider(el){
    var val = el.value;
    var min = el.getAttribute('min');
    var max = el.getAttribute('max');
    var portion = (val - min) / (max - min);
    var iel = el.parentNode.querySelector('.h-rs-indicator');
    iel.innerHTML = val + '<span></span>';
    iel.style.left = (portion * (el.offsetWidth-18)) + 'px';
} 

Note that this function is reusable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM