I have a need for a slider switch with text labels instead of numbers. Instead of 1,2,3 I need Small, Medium, Large. It does not appear that Kendo UI has a built in way to do this so here is the function I came up with:
/*
This helper is designed to allow you to substitute a datasource for values in a kendo slider to do things like:
"small", "medium", "large".
It includes 4 functions that should be used instead of the ones included with Kendo UI:
initializeKendoSlider(options);
getKendoSliderSelection(elementId);
resizeKendoSlider(elementId);
setKendoSliderValue(elementId, newValue);
*/
function initializeKendoSlider(options) {
/*
options = {
elementId: string - required,
dataSource: [{ label: string, value: any}],
value: any - the value prop of any dataSource item
orientation: string - defaults to "horizontal",
tickPlacement: string - defaults to "bottomRight",
change: custom change function. Available parameters are: selectedDataItem, e
};
Include this in your html to start:
<input id="mySlider" name="mySlider" type="text" />
NOTE: If a proper dataSource is not passed, the slider will build as a numeric Kendo Slider.
NOTE: The value returned by the .value() function is the index, not the actual value. You must use
the included getKendoSliderSelection and setKendoSliderValue functions.
NOTE: The change function will return the selected item as the second parameter which can be used in your custom change function.
NOTE: Kendo's built in max and min methods will not work properly when using a dataSource.
NOTE: Small steps are not supported.
*/
if (!options.elementId || typeof options.elementId !== "string") {
console.error("No Element ID Passed");
return false;
}
let elementId = options.elementId;
if (Array.isArray(options.dataSource)) {
// Destroy the existing slider
if ($(`#${elementId}`).data("kendoSlider")) {
$(`#${elementId}`).data("kendoSlider").destroy();
if ($(`#${elementId}`).closest(".custom-form-slider").length > 0) {
$(`#${elementId}`).closest(".custom-form-slider").replaceWith($(`#${elementId}`));
} else if ($(`#${elementId}`).closest(".k-slider").length > 0) {
$(`#${elementId}`).closest(".k-slider").replaceWith($(`#${elementId}`));
}
}
// Prep options for slider
let defaultValue = options.value ? options.dataSource.findIndex(x => x.value === options.value) : 0;
let tickPlacement = options.tickPlacement;
if (tickPlacement !== "topLeft" && tickPlacement !== "both") {
tickPlacement = "bottomRight";
}
options.tickPlacement = tickPlacement;
// Build the slider
let objectSlider = $(`#${elementId}`).kendoSlider({
showButtons: false,
dataSource: options.dataSource,
min: 0,
max: options.dataSource.length - 1,
orientation: options.orientation === "vertical" ? options.orientation : "horizontal",
tickPlacement: tickPlacement,
value: defaultValue,
largeStep: 1,
change: function (e) {
let selectedDataItem = getKendoSliderSelection(options.elementId);
if (typeof options.change === "function") {
options.change(selectedDataItem, e);
}
},
tooltip: {
enabled: false
}
}).data("kendoSlider");
// Changes the numbers to text labels
var sliderItems = $(`#${elementId}`).siblings(".k-slider-items");
$.each(options.dataSource, function (index, step) {
var item = sliderItems.find("li:eq(" + (index) + ")");
item.attr("title", step.label);
item.find("span").text(step.label);
});
// Fixes the alignment due to absulutely positioned labels
if (sliderItems.length > 0) {
if (options.orientation !== "vertical" && (tickPlacement === "bottomRight" || tickPlacement === "both")) {
let left = sliderItems.find("li:eq(0) span")[0].getBoundingClientRect().width;
let right = sliderItems.find(`li:eq(${options.dataSource.length - 1}) span`)[0].getBoundingClientRect().width;
$(`#${elementId}`).closest(".k-slider").wrap(`<div class="custom-form-slider" style="padding-left: ${left / 2}px; padding-right: ${right / 2}px; padding-bottom: 1.2em"></div>`);
} else if (options.orientation === "vertical" && (tickPlacement === "bottomRight" || tickPlacement === "both")) {
let maxLabelWidth = 0;
$.each(options.dataSource, function (index) {
var item = sliderItems.find("li:eq(" + (index) + ") span");
let thisWidth = item[0].getBoundingClientRect().width;
if (thisWidth > maxLabelWidth) {
maxLabelWidth = thisWidth;
}
});
$(`#${elementId}`).closest(".k-slider").wrap(`<div class="custom-form-slider" style="display: inline-block; padding-right: ${maxLabelWidth + 10}px; padding-bottom: 14px"></div>`);
} else if (options.orientation === "horizontal" && (tickPlacement === "topLeft")) {
let left = sliderItems.find("li:eq(0) span")[0].getBoundingClientRect().width;
let right = sliderItems.find(`li:eq(${options.dataSource.length - 1}) span`)[0].getBoundingClientRect().width;
$(`#${elementId}`).closest(".k-slider").wrap(`<div class="custom-form-slider" style="padding-left: ${left / 2}px; padding-right: ${right / 2}px; padding-top: 1.2em"></div>`);
} else if (options.orientation === "vertical" && (tickPlacement === "topLeft")) {
let maxLabelWidth = 0;
$.each(options.dataSource, function (index) {
var item = sliderItems.find("li:eq(" + (index) + ") span");
let thisWidth = item[0].getBoundingClientRect().width;
if (thisWidth > maxLabelWidth) {
maxLabelWidth = thisWidth;
}
});
$(`#${elementId}`).closest(".k-slider").wrap(`<div class="custom-form-slider" style="display: inline-block; padding-left: ${maxLabelWidth + 10}px; padding-bottom: 14px"></div>`);
} else {
$(`#${elementId}`).closest(".k-slider").wrap('<div class="custom-form-slider"></div>');
}
} else {
$(`#${elementId}`).closest(".k-slider").wrap('<div class="custom-form-slider"></div>');
}
// Add event listener
const debouncedResize = debounce(() => resizeKendoSlider(elementId), 300);
window.addEventListener("resize", debouncedResize);
return objectSlider;
} else if (options.options) {
// NOTE: This will use the standard KendoSlider initialization.
let objectSlider = $(`#${options.elementId}`).kendoSlider(options.options).data("kendoSlider");
return objectSlider;
} else {
console.error("No dataSource or options passed to initializeKendoSlider. Must have at least one");
return false;
}
}
function resizeKendoSlider(elementId) {
let thisSlider = $(`#${elementId}`).data("kendoSlider");
let options = thisSlider.options;
let tickPlacement = options.tickPlacement;
// Trigger the kendo slider resize to get any missing ticks
thisSlider.resize();
// Changes the numbers to text labels so we can calculate the size
let sliderItems = $(`#${elementId}`).siblings(".k-slider-items");
$.each(options.dataSource, function (index, step) {
let item = sliderItems.find("li:eq(" + (index) + ")");
item.find("span").text(step.label);
});
// Fixes the alignment due to absulutely positioned labels
if (sliderItems.length > 0) {
if (options.orientation !== "vertical" && (tickPlacement === "bottomRight" || tickPlacement === "both")) {
let left = sliderItems.find("li:eq(0) span")[0].getBoundingClientRect().width;
let right = sliderItems.find(`li:eq(${options.dataSource.length - 1}) span`)[0].getBoundingClientRect().width;
$(`#${elementId}`).closest(".custom-form-slider").css({
paddingRight: `${right / 2}px`,
paddingLeft: `${left / 2}px`,
paddingBottom: "1.2em"
});
} else if (options.orientation === "vertical" && (tickPlacement === "bottomRight" || tickPlacement === "both")) {
let maxLabelWidth = 0;
$.each(options.dataSource, function (index) {
var item = sliderItems.find("li:eq(" + (index) + ") span");
let thisWidth = item[0].getBoundingClientRect().width;
if (thisWidth > maxLabelWidth) {
maxLabelWidth = thisWidth;
}
});
$(`#${elementId}`).closest(".custom-form-slider").css({
display: "inline-block",
paddingRight: `${maxLabelWidth + 10}px`,
paddingBottom: "14px"
});
} else if (options.orientation === "horizontal" && (tickPlacement === "topLeft")) {
let left = sliderItems.find("li:eq(0) span")[0].getBoundingClientRect().width;
let right = sliderItems.find(`li:eq(${options.dataSource.length - 1}) span`)[0].getBoundingClientRect().width;
$(`#${elementId}`).closest(".custom-form-slider").css({
paddingTop: "1.2em",
paddingLeft: `${left / 2}px`,
paddingRight: `${right / 2}px`
});
} else if (options.orientation === "vertical" && (tickPlacement === "topLeft")) {
let maxLabelWidth = 0;
$.each(options.dataSource, function (index) {
var item = sliderItems.find("li:eq(" + (index) + ") span");
let thisWidth = item[0].getBoundingClientRect().width;
if (thisWidth > maxLabelWidth) {
maxLabelWidth = thisWidth;
}
});
$(`#${elementId}`).closest(".custom-form-slider").css({
display: "inline-block",
paddingLeft: `${maxLabelWidth + 10} px`,
paddingBottom: "14px"
});
} else {
$(`#${elementId}`).closest(".custom-form-slider").css({
display: "",
paddingLeft: "",
paddingBottom: ""
});
}
}
// Resize again to fix the track since we changed the padding.
thisSlider.resize();
// Then replace the labels since the resize erases them.
sliderItems = $(`#${elementId}`).siblings(".k-slider-items");
$.each(options.dataSource, function (index, step) {
var item = sliderItems.find("li:eq(" + (index) + ")");
item.attr("title", step.label);
item.find("span").text(step.label);
});
}
function getKendoSliderSelection(elementId) {
let thisSlider = $(`#${elementId}`).data("kendoSlider");
let dataSource = thisSlider.options.dataSource;
let value = thisSlider.value();
if (dataSource) {
return dataSource[value];
} else {
return {
label: value,
value: value
};
}
}
function setKendoSliderValue(elementId, newValue) {
// newValue must exist in the dataSource or nothing will change
let thisSlider = $(`#${elementId}`).data("kendoSlider");
let dataSource = thisSlider.options.dataSource;
let newValueIndex = dataSource.findIndex(x => x.value === newValue);
if (newValueIndex > -1) {
thisSlider.value(newValueIndex);
} else {
console.error(`Attempted to set the value of #${elementId} to non-existing value of ${newValue}`);
}
return newValueIndex;
}
function debounce(func, timeout = 300){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
window.initializeKendoSlider = initializeKendoSlider;
window.getKendoSliderSelection = getKendoSliderSelection;
window.setKendoSliderValue = setKendoSliderValue;
window.resizeKendoSlider = resizeKendoSlider;