dash
dash copied to clipboard
[Feature Request] `dcc.RangeSlider` tooltip - allow displaying something else than `value`
Problem
Currently, the tooltip of dcc.RangeSlider
can be used to display the value
property of the slider. It would be nice if it could display an arbitrary string. E.g. using a dictionary, where keys are the value
s of the slider and values are the strings to be displayed in the tooltip.
Solution(?)
Apparently, such functionality is present in the react slider.
http://react-component.github.io/slider/?path=/story/rc-slider–handle
<div style={wrapperStyle}>
<p>Range with custom tooltip</p>
<Range min={0} max={20} defaultValue={[3, 10]} tipFormatter={value => `${value}%`} />
</div>
See the tipFormatter
property. Could this property be added to dcc.RangeSlider
?
Example use-case
Imagine that your slider value
s are actually indexes of a list of the real values. You want to present the real values to the user instead of the indexes. You cannot use the marks
property of the slider because there are too many values and it would look messy. Thus, you want to use the tooltip to display something else than the value
property of the slider.
I second this feature request. I would find it quite useful to be able to format the tooltip, e.g. with a thousands comma or with units.
Yes please! This is absolutely needed and completely enhances the user experience when working with values such as dates!
This would greatly improve my dash app when working with date strings, and when marks overlap, such as this...

This would greatly improve my dash app when working with date strings, and when marks overlap, such as this...
![]()
.
Temp workaround: update marks with style = {"display": "none"} for marks not currently on.
ie
@callback( Output("time_slider", "marks"), Input("time_slider", "value"), State("time_slider", "marks"), prevent_initial_call=True, ) def update_slider_marks(slider_value, current_marks):
for k, v in current_marks.items():
v["style"] = {} if slider_value == int(k) else {"display": "none"}
current_marks[k] = v
return current_marks
hope this helps while request remains open!

yes we do need this feature, as mentioned by others, this is especially useful for date values. As RangeSlider currently does not support date, we need to use some workaround that convert date into epoch values which works nicely. Unfortunately since tooltip is not configurable, the tooltip shows the epoch date which is not useful
Join the issue. Currently, i've added two text components and update them with selected dates.
The downside is doing in on a server side is overhead.
I have the same problem here, this feature can be very useful!
I second this feature request! I am using a numeric index mapped to a vector of datetimes, and would like to show the datetime as a tooltip (I am using markers=None) instead of numeric index.
This feature would be very helpful, for now i tinkered with it and tried to find a CSS hack that uses the least amount of callbacks :
First, I thought of using tooltips, and updating their content by putting a :before
pseudo-element inside it, with something like content: var(--tooltip-value)
and updating the css var by having a callback with the slider's drag_value
as input, and a parent element's style
as output.
This works fine but i noticed that the tooltip are actually added to the DOM by appending them on hover, and there's no way to differenciate which corresponds to the start or the end handle, so you can't use :nth-child
and the like to select which tooltip has which value.
Second more hacky way but that works in most scenarios : Using marks for each element shifted by one to the left (each mark should display the value of the previous tick), then you need to add a mark to the end which has the value of the last tick, then you can use this CSS :
.rc-slider-mark-text {
white-space: nowrap;
display: none;
}
.rc-slider-mark-text-active:nth-child(2),
.rc-slider-mark-text:not(.rc-slider-mark-text-active) + .rc-slider-mark-text-active + .rc-slider-mark-text-active,
.rc-slider-mark-text-active + .rc-slider-mark-text:not(.rc-slider-mark-text-active),
.rc-slider-mark-text-active:last-child {
display: block;
color: #666;
}
Shifting to the left is mandatory if you want it to be compatible on most browsers,
if you don't care, you can use :has
and you don't need to touch the inputs and outputs of your RangeSlider
(caniuse) :
.rc-slider-mark-text {
white-space: nowrap;
display: none;
}
.rc-slider-mark-text-active:first-child,
.rc-slider-mark-text:not(.rc-slider-mark-text-active) + .rc-slider-mark-text-active,
.rc-slider-mark-text-active:not(:has(~ .rc-slider-mark-text-active)),
.rc-slider-mark-text-active:last-child {
display: block;
color: #666;
}
This is a pure front-end solution that works nicely in most usecases where you want to emulate react's tipFormatter
Edit, fully functionnal workaround, with usecase :
Found out that by fusing the 2 tricks i described earlier, i made it work as i wanted, and here's the code i use to have a date RangeSlider with marks that follow the selected range :
RangeSlider
init code :
from dash import html, dcc
from dateutil.rrule import rrule, DAILY
from datetime import datetime, timedelta
min_day: datetime
max_day: datetime
day_interval = (max_day - min_day).days
date_range_slider = html.Div(
dcc.RangeSlider(
min=0,
max=day_interval,
value=[0, day_interval],
marks={
i: f'{date - timedelta(days=1):%Y-%m-%d}' for i, date in enumerate(rrule(freq=DAILY, dtstart=min_day, until=max_day))
},
),
style={"--max-tooltip-val": f'"{max_day:%Y-%m-%d}"'}
)
Here's the CSS :
.rc-slider-mark-text {
white-space: nowrap;
display: none;
}
.rc-slider-mark-text-active:nth-child(2),
.rc-slider-mark-text:not(.rc-slider-mark-text-active) + .rc-slider-mark-text-active + .rc-slider-mark-text-active,
.rc-slider-mark-text-active + .rc-slider-mark-text:not(.rc-slider-mark-text-active),
.rc-slider-mark-text-active:last-child {
display: block;
color: #666;
}
.rc-slider-mark-text-active:last-child {
font-size: 0;
}
.rc-slider-mark-text-active:last-child:before {
content: var(--max-tooltip-val);
font-size: 12px;
}
Note that you might wanna move the tooltips a bit to the left (as we take each next tooltip except for the last one), but i let that to your taste.
I have the same problem. I make a Dash table and on a desktop, all look fine. But if I open my table on a mobile device, the values on my sliders overlap each other. I need a tooltip with a financial format.
In case anyone is interested i spend some time digging into this with CSS I do not find it possible for my tooltips to change on the slider when i move them with only CSS
But i did find a solution using JS, it is only possible to format dynamically the value from the slider though. Here is the code that would change values of dates forexample: 20230101 to 2023-01-01
console.log("js loaded")
function formatToolTip(tooltipElement, value) {
strVal = value.toString()
tooltipElement.innerHTML = `${strVal.slice(0,4)}-${strVal.slice(4,6)}-${strVal.slice(6,8)}` //Value is the value from the sliders val, and format is how you want it formatted
}
var firstObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'attributes') {
val = mutation.target.getAttribute('aria-valuenow')
firstTooltip = document.querySelectorAll('.rc-slider-tooltip-inner')[0]
formatToolTip(firstTooltip, val)
}
})
})
var secondObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'attributes') {
val = mutation.target.getAttribute('aria-valuenow')
secondToolTip = document.querySelectorAll('.rc-slider-tooltip-inner')[1]
formatToolTip(secondToolTip, val)
}
})
})
const addEventListener = () => {
var firstDot = document.getElementsByClassName("rc-slider-handle rc-slider-handle-1")[0];
var secondDot = document.getElementsByClassName("rc-slider-handle rc-slider-handle-2")[0];
if (!firstDot || !secondDot) {
setTimeout(() => addEventListener(), 500)
return
}
firstTooltip = document.querySelectorAll('.rc-slider-tooltip-inner')[0]
secondToolTip = document.querySelectorAll('.rc-slider-tooltip-inner')[1]
formatToolTip(firstTooltip, firstDot)
formatToolTip(firstTooltip, secondDot)
firstObserver.observe(firstDot, {attributes: true})
secondObserver.observe(secondDot, {attributes: true})
}
addEventListener()
Just add this file to your assets under tooltip-fixer.js
or whatever you like
Here is a gif of how it looks without any styling of css:
I second this would be very helpful
In case anyone is interested i spend some time digging into this with CSS I do not find it possible for my tooltips to change on the slider when i move them with only CSS
But i did find a solution using JS, it is only possible to format dynamically the value from the slider though. Here is the code that would change values of dates forexample: 20230101 to 2023-01-01
console.log("js loaded") function formatToolTip(tooltipElement, value) { strVal = value.toString() tooltipElement.innerHTML = `${strVal.slice(0,4)}-${strVal.slice(4,6)}-${strVal.slice(6,8)}` //Value is the value from the sliders val, and format is how you want it formatted } var firstObserver = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'attributes') { val = mutation.target.getAttribute('aria-valuenow') firstTooltip = document.querySelectorAll('.rc-slider-tooltip-inner')[0] formatToolTip(firstTooltip, val) } }) }) var secondObserver = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'attributes') { val = mutation.target.getAttribute('aria-valuenow') secondToolTip = document.querySelectorAll('.rc-slider-tooltip-inner')[1] formatToolTip(secondToolTip, val) } }) }) const addEventListener = () => { var firstDot = document.getElementsByClassName("rc-slider-handle rc-slider-handle-1")[0]; var secondDot = document.getElementsByClassName("rc-slider-handle rc-slider-handle-2")[0]; if (!firstDot || !secondDot) { setTimeout(() => addEventListener(), 500) return } firstTooltip = document.querySelectorAll('.rc-slider-tooltip-inner')[0] secondToolTip = document.querySelectorAll('.rc-slider-tooltip-inner')[1] formatToolTip(firstTooltip, firstDot) formatToolTip(firstTooltip, secondDot) firstObserver.observe(firstDot, {attributes: true}) secondObserver.observe(secondDot, {attributes: true}) } addEventListener()
Just add this file to your assets under
tooltip-fixer.js
or whatever you like
In case you need a more robust solution i have improved the code to observe when the tooltip is generated in the body aswell:
document.addEventListener("DOMContentLoaded", () => {
waitForElm('.rc-slider-handle-1').then(elm => {
createObserver(0).observe(elm, {attributes: true})
})
waitForElm('.rc-slider-handle-2').then(elm => {
createObserver(1).observe(elm, {attributes: true})
})
})
function waitForElm(selector, elementNumber) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function formatToolTip(tooltipElement, value) {
var dateObj = new Date(parseInt(value))
var month = dateObj.getMonth() + 1
var date = dateObj.getDate();
tooltipElement.innerHTML = `${dateObj.getFullYear()}/${month < 10 ? '0' + month : month}/${date < 10 ? '0' + date : date}` //Value is the value from the sliders val, and format is how you want it formatted
}
var createObserver = (tooltipNr) => new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === 'attributes') {
val = mutation.target.getAttribute('aria-valuenow')
tooltip = document.querySelectorAll('.rc-slider-tooltip-inner')[tooltipNr]
formatToolTip(tooltip, val)
}
})
})
Is someone still pushing that? Would be a very basic, yet great feature.
I would also enjoy this feature very much -- want to append a "%" sign to show values as percentages.
(Hacked the CSS to use a slider to show performance against a target value -- disabled the slider on the Python side and increased the linewidth of the "track" and "rail". Also need to set the cursor back to default for the slider handle.)
EDIT: noticed that this issue was originally for dcc.RangeSlider, not dcc.Slider, but the logic should be almost the same.
I'd also love this feature! My range slider is logaritmic so the input values x (the ones shown in the tooltip) are converted to:
f = 10^x
So I'd like to be able to convert the tooltip value to 10^x.