I have a requirement to have a range slider with min and max and 2 textboxes to show those selected values in the given design of the range slider. I tried to refer below links.
https://jsfiddle.net/b68fp7qd/
. But no css is mentioned in that code snippet.
And in this link, https://jqueryui.com/slider/#range
there is no tooltip and bit difference in the design and color to change.
Can anyone help to achieve this.
I have tried below code
HTML:
<div class="filter-container">
<div class="filter-section">
<div class="slider-wrapper">
<div id="slider" class="store-slider"></div>
</div>
<div class="row filter-row">
<div class="col-6">
<div class="input-group">
<input type="text" class="form-control price-input" id="price_from">
</div>
</div>
<div class="col-6">
<div class="input-group">
<input type="text" class="form-control price-input" id="price_to">
</div>
</div>
</div>
</div>
</div>
CSS:
.filter-container {
max-width: 280px;
margin: 50px auto;
background: #fff;
border-radius: 6px;
}
.filter-section {
padding: 20px;
}
.filter-section + .filter-section {
border-top: 1px solid #f1f3f4;
}
.slider-wrapper {
margin: 60px auto 0;
padding: 0 8px;
}
.store-slider.ui-slider {
display: flex;
align-items: center;
justify-content: center;
margin-top: 30px;
}
.store-slider .ui-slider-range {
display: flex;
align-items: center;
justify-content: center;
background: rgb(233, 236, 239);
}
.store-slider .ui-slider-handle {
top: auto;
width: 20px;
height: 20px;
display: flex;
justify-content: center;
margin: 0;
margin-left: -10px;
border-radius: 50%;
border: none;
background: #fff;
box-shadow: inset 0 0 0 transparent, 0 0 2px rgba(103, 118, 125, 1), 0 4px 10px rgba(91, 111, 144, 0.3);
cursor: pointer;
}
.store-slider .ui-slider-handle:hover,
.store-slider .ui-slider-handle.ui-state-hover {
background: #89cd20;
box-shadow: inset 0 0 0 1px #75b316, 0 0 2px rgba(103, 118, 125, 0.5), 0 4px 10px rgba(91, 111, 144, 0.3);
}
.store-slider.ui-widget.ui-widget-content {
border: 1px solid rgb(206, 212, 218);
}
.ui-slider-handle:hover,
.ui-slider-handle:active,
.ui-slider-handle:focus {
outline: none;
}
.ui-slider-handle:active {
background: #89cd20;
border-color: #75b316;
}
.tippy-content {
font-size: 13px;
}
.row.filter-row {
margin-top: 15px;
margin-left: -7px;
margin-right: -8px;
}
.row.filter-row [class*="col-"] {
padding-left: 7px;
padding-right: 8px;
}
JS:
console.clear();
var currentValue = {
from: 1106,
to: 10000
}
var slider = $('#slider').slider({
range: true,
min: 0,
max: 11850,
values: [currentValue.from, currentValue.to],
create: function (event, ui) {
var $slider = $(this);
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var $sliderHandlers = $slider.find('.ui-slider-handle');
var $sliderHandlersMin = $sliderHandlers.eq(0);
var $sliderHandlersMax = $sliderHandlers.eq(1);
var $sliderRange = $slider.find('.ui-slider-range');
var values = {
from: currentValue.from,
to: currentValue.to
}
$sliderHandlersMin.attr('title', values.from);
$sliderHandlersMax.attr('title', values.to);
$sliderRange
.attr('title', values.from + ' - ' + values.to)
.attr('data-tippy-distance', 15);;
var tippyOptions = {
trigger: 'manual',
sticky: true,
dynamicTitle: true,
hideOnClick: false,
arrow: true,
arrowType: 'round',
animation: 'fade',
placement: 'top',
livePlacement: false,
flipBehavior: [],
createPopperInstanceOnInit: true,
appendTo: $(this).closest('.filter-section')[0],
popperOptions: {
modifiers: {
// preventOverflow: {
// boundariesElement: $(this).closest('.filter-section')[0],
// }
computeStyle: {
gpuAcceleration: true
}
},
}
};
tippy($sliderHandlersMin[0], tippyOptions);
tippy($sliderHandlersMax[0], tippyOptions);
tippy($sliderRange[0], tippyOptions);
updateSliderTooltip($slider, values);
},
slide: function (event, ui) {
var $slider = $(this);
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var values = {
from: ui.values[0],
to: ui.values[1]
}
updateSliderTooltip($slider, values, true);
}
});
function updateSliderTooltip($slider, values, updateInput) {
if (typeof updateInput === "undefined")
var updateInput = true;
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var $sliderHandlers = $slider.find('.ui-slider-handle');
var $sliderHandlersMin = $sliderHandlers.eq(0);
var $sliderHandlersMax = $sliderHandlers.eq(1);
var $sliderRange = $slider.find('.ui-slider-range');
$sliderHandlersMin.attr('title', values.from);
$sliderHandlersMax.attr('title', values.to);
$sliderRange.attr('title', values.from + ' ' + values.to);
if (updateInput) {
$('#price_from').val(values.from);
$('#price_to').val(values.to);
}
setInterval(function () {
var tooltipMinLeft = getCoords($sliderHandlersMin[0]).left;
var tooltipMaxLeft = getCoords($sliderHandlersMax[0]).left;
var tooltipRange = tooltipMaxLeft - tooltipMinLeft;
var singleTooltip = tooltipRange < 80;
if (!singleTooltip) {
if (!$sliderHandlersMin[0]._tippy.state.visible)
$sliderHandlersMin[0]._tippy.show(0);
if (!$sliderHandlersMax[0]._tippy.state.visible)
$sliderHandlersMax[0]._tippy.show(0);
if ($sliderRange[0]._tippy.state.visible)
$sliderRange[0]._tippy.hide(0);
} else {
if ($sliderHandlersMin[0]._tippy.state.visible)
$sliderHandlersMin[0]._tippy.hide(0);
if ($sliderHandlersMax[0]._tippy.state.visible)
$sliderHandlersMax[0]._tippy.hide(0);
if (!$sliderRange[0]._tippy.state.visible)
$sliderRange[0]._tippy.show(0);
}
}, 1);
}
function updateValues() {
var from = $('#price_from').val();
var to = $('#price_to').val();
updateSliderTooltip(slider, {
from: from,
to: to
}, false);
slider.slider({
values: [from, to]
});
}
$('#price_from').on('blur', function () {
$(this).val($(this).val().replace(/[^0-9]/i, ''));
if (+$(this).val() > +$('#price_to').val()) {
$(this).val($('#price_to').val())
}
updateValues();
});
$('#price_to').on('blur', function () {
$(this).val($(this).val().replace(/[^0-9]/gi, ''));
if (+$(this).val() < +$('#price_from').val()) {
$(this).val($('#price_from').val())
}
updateValues();
});
function getCoords(elem) {
var box = elem.getBoundingClientRect();
var body = document.body;
var docEl = document.documentElement;
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
var clientTop = docEl.clientTop || body.clientTop || 0;
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
return { top: Math.round(top), left: Math.round(left) };
}
But with this I'm unable to see the slider. Design is coming as per the below image. Also the code for the tooltip is breaking and getting below console error:
VM391:620 Uncaught TypeError: Cannot read properties of undefined (reading 'getBoundingClientRect') at getCoords (:620:20) at :561:30
I just implement it in my sample project with no error. You can modify it according your needs.
1. Create a Slider.cshtml and define it in HomeController.
Slider.cshtml
@{
ViewData["Title"] = "jQuery UI range slider with tooltip tippy.js";
}
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-size: 14px;
background: #f1f3f4;
height: 10000px
}
.filter-container {
max-width: 280px;
margin: 50px auto;
background: #fff;
border-radius: 6px;
}
.filter-section {
padding: 20px;
}
.filter-section + .filter-section {
border-top: 1px solid #f1f3f4;
}
.slider-wrapper {
margin: 60px auto 0;
padding: 0 8px;
}
.store-slider.ui-slider {
display: flex;
align-items: center;
justify-content: center;
margin-top: 30px;
}
.store-slider .ui-slider-range {
display: flex;
align-items: center;
justify-content: center;
background: rgb(233, 236, 239);
}
.store-slider .ui-slider-handle {
top: auto;
width: 20px;
height: 20px;
display: flex;
justify-content: center;
margin: 0;
margin-left: -10px;
border-radius: 50%;
border: none;
background: #fff;
box-shadow:
inset 0 0 0 transparent,
0 0 2px rgba(103, 118, 125, 1),
0 4px 10px rgba(91, 111, 144, 0.3);
cursor: pointer;
}
.store-slider .ui-slider-handle:hover,
.store-slider .ui-slider-handle.ui-state-hover {
background: #89cd20;
box-shadow:
inset 0 0 0 1px #75b316,
0 0 2px rgba(103, 118, 125, 0.5),
0 4px 10px rgba(91, 111, 144, 0.3);
}
.store-slider.ui-widget.ui-widget-content {
border: 1px solid rgb(206, 212, 218);
}
.ui-slider-handle:hover,
.ui-slider-handle:active,
.ui-slider-handle:focus {
outline: none;
}
.ui-slider-handle:active {
background: #89cd20;
border-color: #75b316;
}
.tippy-content {
font-size: 13px;
}
.row.filter-row {
margin-top: 15px;
margin-left: -7px;
margin-right: -8px;
}
.row.filter-row [class*="col-"] {
padding-left: 7px;
padding-right: 8px;
}
</style>
<div class="filter-container">
<div class="filter-section">
<div class="h5">По цене</div>
<div class="slider-wrapper">
<div id="slider" class="store-slider"></div>
</div>
<div class="row filter-row">
<div class="col-6">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">от</div>
</div>
<input type="text" class="form-control price-input" id="price_from">
</div>
</div>
<div class="col-6">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">до</div>
</div>
<input type="text" class="form-control price-input" id="price_to">
</div>
</div>
</div>
</div>
<div class="filter-section">
<div class="h5">Мы ценим Вас</div>
Главное преймущество нашего магазина - это качество товаров и то как мы относимся к нашим клиентам. Если Вы остались не довольны работой наших сотрудников или возникли проблемы с качеством товара, пожалуйста, сходите лесом.
</div>
</div>
@section Scripts {
<script src="https://unpkg.com/tippy.js@2.3.0/dist/tippy.all.min.js"></script>
<script>
console.clear();
var currentValue = {
from: 1106,
to: 10000
}
var slider = $('#slider').slider({
range: true,
min: 0,
max: 11850,
values: [currentValue.from, currentValue.to],
create: function (event, ui) {
var $slider = $(this);
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var $sliderHandlers = $slider.find('.ui-slider-handle');
var $sliderHandlersMin = $sliderHandlers.eq(0);
var $sliderHandlersMax = $sliderHandlers.eq(1);
var $sliderRange = $slider.find('.ui-slider-range');
var values = {
from: currentValue.from,
to: currentValue.to
}
$sliderHandlersMin.attr('title', 'от ' + values.from + ' грн');
$sliderHandlersMax.attr('title', 'до ' + values.to + ' грн');
$sliderRange
.attr('title', values.from + ' - ' + values.to + ' грн')
.attr('data-tippy-distance', 15);;
var tippyOptions = {
trigger: 'manual',
sticky: true,
dynamicTitle: true,
hideOnClick: false,
arrow: true,
arrowType: 'round',
animation: 'fade',
placement: 'top',
livePlacement: false,
flipBehavior: [],
createPopperInstanceOnInit: true,
appendTo: $(this).closest('.filter-section')[0],
popperOptions: {
modifiers: {
// preventOverflow: {
// boundariesElement: $(this).closest('.filter-section')[0],
// }
computeStyle: {
gpuAcceleration: true
}
},
}
};
tippy($sliderHandlersMin[0], tippyOptions);
tippy($sliderHandlersMax[0], tippyOptions);
tippy($sliderRange[0], tippyOptions);
updateSliderTooltip($slider, values);
},
slide: function (event, ui) {
var $slider = $(this);
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var values = {
from: ui.values[0],
to: ui.values[1]
}
updateSliderTooltip($slider, values, true);
}
});
function updateSliderTooltip($slider, values, updateInput) {
if (typeof updateInput === "undefined")
var updateInput = true;
var $sliderWrapper = $slider.closest('.store-slider-wrapper');
var $sliderHandlers = $slider.find('.ui-slider-handle');
var $sliderHandlersMin = $sliderHandlers.eq(0);
var $sliderHandlersMax = $sliderHandlers.eq(1);
var $sliderRange = $slider.find('.ui-slider-range');
$sliderHandlersMin.attr('title', 'от ' + values.from + ' грн');
$sliderHandlersMax.attr('title', 'до ' + values.to + ' грн');
$sliderRange.attr('title', 'от ' + values.from + ' и до ' + values.to + ' грн');
if (updateInput) {
$('#price_from').val(values.from);
$('#price_to').val(values.to);
}
setInterval(function () {
var tooltipMinLeft = getCoords($sliderHandlersMin[0]).left;
var tooltipMaxLeft = getCoords($sliderHandlersMax[0]).left;
var tooltipRange = tooltipMaxLeft - tooltipMinLeft;
var singleTooltip = tooltipRange < 80;
if (!singleTooltip) {
if (!$sliderHandlersMin[0]._tippy.state.visible)
$sliderHandlersMin[0]._tippy.show(0);
if (!$sliderHandlersMax[0]._tippy.state.visible)
$sliderHandlersMax[0]._tippy.show(0);
if ($sliderRange[0]._tippy.state.visible)
$sliderRange[0]._tippy.hide(0);
} else {
if ($sliderHandlersMin[0]._tippy.state.visible)
$sliderHandlersMin[0]._tippy.hide(0);
if ($sliderHandlersMax[0]._tippy.state.visible)
$sliderHandlersMax[0]._tippy.hide(0);
if (!$sliderRange[0]._tippy.state.visible)
$sliderRange[0]._tippy.show(0);
}
}, 1);
}
function updateValues() {
var from = $('#price_from').val();
var to = $('#price_to').val();
updateSliderTooltip(slider, {
from: from,
to: to
}, false);
slider.slider({
values: [from, to]
});
}
$('#price_from').on('blur', function () {
$(this).val($(this).val().replace(/[^0-9]/i, ''));
if (+$(this).val() > +$('#price_to').val()) {
$(this).val($('#price_to').val())
}
updateValues();
});
$('#price_to').on('blur', function () {
$(this).val($(this).val().replace(/[^0-9]/gi, ''));
if (+$(this).val() < +$('#price_from').val()) {
$(this).val($('#price_from').val())
}
updateValues();
});
function getCoords(elem) {
var box = elem.getBoundingClientRect();
var body = document.body;
var docEl = document.documentElement;
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
var clientTop = docEl.clientTop || body.clientTop || 0;
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
return { top: Math.round(top), left: Math.round(left) };
}
</script>
}
2. Add jquery and jquery-ui in _Layout.cshtml.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - signalr_iwa</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/signalr_iwa.styles.css" asp-append-version="true" />
@* add this line *@
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">signalr_iwa</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
<p class="nav navbar-text">Hello, @User.Identity?.Name!</p>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2023 - signalr_iwa - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@* add this line *@
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
@* add this line *@
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
3. Then we can test it