Kendo Slider With Text Labels

1 Answer 15 Views
Slider
Lee
Top achievements
Rank 2
Bronze
Bronze
Bronze
Lee asked on 24 Sep 2025, 09:38 PM

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;

1 Answer, 1 is accepted

Sort by
0
Neli
Telerik team
answered on 29 Sep 2025, 11:07 AM

Hello,

The Kendo UI for jQuery Slider does not provide built-in support for custom text labels on tick marks—numeric values are displayed by default. We have a knowledge base article that demonstrates how the labels can be customized using the same approach as in your implementation:

https://www.telerik.com/kendo-jquery-ui/documentation/knowledge-base/customizing-tick-labels-on-kendo-slider

We have a dedicated Feature Request for adding a template option to the label. I noticed you’ve already voted for it. For the convenience of others in the forum, here’s the link to the issue:

https://feedback.telerik.com/kendo-jquery-ui/1358187-implement-templates-for-the-labels-of-the-slider-range-slider 

Let me know in case you have any additional questions on the matter.

    Regards,
    Neli
    Progress Telerik

    Your perspective matters! Join other professionals in the State of Designer-Developer Collaboration 2025: Workflows, Trends and AI survey to share how AI and new workflows are impacting collaboration, and be among the first to see the key findings.
    Start the 2025 Survey
    Lee
    Top achievements
    Rank 2
    Bronze
    Bronze
    Bronze
    commented on 29 Sep 2025, 12:49 PM

    Thank you. That knowledge base article is a start, however I quickly ran into many issues with it. For example, I get console errors when I try to initialize it before it is visible on the screen (hidden div). When you need to set or get the value you have to know the index, When you resize, the ball becomes misaligned.  If your values and labels are different but not equal to an integer. This contains functions to fix all of that. 
    Neli
    Telerik team
    commented on 02 Oct 2025, 07:07 AM

    Hi Lee,

    You are correct that initializing the Kendo Slider inside a hidden container can lead to different issues. These challenges stem from the widget’s reliance on visible DOM elements for proper layout calculations.

    The Kendo Slider (and similar widgets) should be initialized only when the container is visible. Initializing in a hidden element (e.g., display: none) prevents the widget from calculating sizes and positions correctly.

    The misalignment of the slider handle after resizing is a known issue when the widget is in a container that changes size or visibility. Your resizeKendoSlider function, where the resize() method is called, is the correct approach to resolve such issues. 

    Console errors often occur when the widget tries to access dimensions or methods on elements that are not available due to visibility or timing. Initializing only in visible containers and ensuring all required DOM elements are present before widget creation can prevent these errors.

    If you encounter scenarios where the slider still becomes misaligned or errors persist, could you provide specific error messages or example situations? This would help in offering more targeted troubleshooting steps.

    If your slider is used in dynamic interfaces (such as modals or tabs), ensure that initialization and value setting always occur after the container is shown and fully rendered.

    Regards,

    Neli

    Neli
    Telerik team
    commented on 02 Oct 2025, 11:58 AM

    Hi Lee,

    I’ve shared a reply in the other thread regarding this issue. Unfortunately, your comments there were removed by mistake, and I sincerely apologize for that. Even though the comments are no longer visible, you can still find my response in the link below:

    - https://www.telerik.com/forums/slider-issue-when-not-initially-visible-on-the-view

    Thank you for your understanding, and please accept our apologies for any inconvenience this may have caused.

    Regards,

    Neli

    Tags
    Slider
    Asked by
    Lee
    Top achievements
    Rank 2
    Bronze
    Bronze
    Bronze
    Answers by
    Neli
    Telerik team
    Share this question
    or