You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KendoGrid滚动问题:页面滚动表头固定与水平滚动条需求

KendoGrid 滚动相关问题解决

问题背景

在门户中使用KendoGrid时,移除了网格自带滚动条改用窗口滚动条,导致网格高度过长时,用户滚动到页面底部表头会不可见。

技术诉求

  • 页面滚动时让表头保持固定(原生粘性表头因网格无滚动未触发)
  • 若当前结构不理想,可为网格添加顶部或上下双水平滚动条

解决方案

一、实现页面滚动时表头固定

KendoGrid原生粘性表头依赖网格自身滚动容器,改用窗口滚动后需通过自定义CSS和JS实现:

  1. 添加固定表头CSS
/* 固定表头,若页面有顶部导航栏需调整top值为导航栏高度 */
.k-grid-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background-color: #fff; /* 匹配主题背景色 */
}
  1. 同步表头与内容列宽
    在dataBound事件中确保表头列和内容列宽度一致,避免错位:
function onDataBound(e) {
    var grid = this;
    // 同步列宽
    grid.thead.find("th").each(function(index) {
        var contentWidth = grid.tbody.find("td").eq(index).width();
        $(this).width(contentWidth);
    });
    // 原有dataBound逻辑...
}

二、添加上下双水平滚动条

若需要保留网格水平滚动并添加顶部滚动条,可通过复制底部滚动条并同步滚动事件实现:

  1. 调整网格配置
    启用网格滚动,移除固定高度或设置适配窗口的高度:
$("#grid").kendoGrid({
    // height: $(window).height() - 100, // 可选:设置网格高度
    scrollable: true, // 启用滚动
    // 其他配置...
});
  1. 添加顶部滚动条并同步滚动
$(document).ready(function() {
    // 网格初始化完成后执行
    var grid = $("#grid").data("kendoGrid");
    var topScroll = $("<div class='k-grid-scrollbar-top'></div>").insertBefore(grid.wrapper);
    var bottomScroll = grid.wrapper.find(".k-grid-content");

    // 双向同步滚动位置
    topScroll.scroll(function() {
        bottomScroll.scrollLeft($(this).scrollLeft());
    });
    bottomScroll.scroll(function() {
        topScroll.scrollLeft($(this).scrollLeft());
    });

    // 设置顶部滚动条样式
    topScroll.css({
        overflowX: "scroll",
        overflowY: "hidden",
        height: "18px",
        width: bottomScroll.width()
    });

    // 添加占位元素让滚动条可拖动
    topScroll.append($("<div></div>").css({
        width: bottomScroll[0].scrollWidth,
        height: 1
    }));
});
  1. 补充顶部滚动条CSS
.k-grid-scrollbar-top {
    margin-bottom: -1px;
    border-bottom: 1px solid #ddd; /* 匹配主题边框色 */
}

修改后的完整代码

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <link href="https://kendo.cdn.telerik.com/themes/7.0.2/default/default-main.css" rel="stylesheet" />
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <script src="js/kendo.all.min.js"></script>
</head>
<body>
    <script src="https://unpkg.com/jszip/dist/jszip.min.js"></script>
<div id="example">
    <div id="grid"></div>
<script>
        $(document).ready(function () {
            var crudServiceBaseUrl = "https://demos.telerik.com/kendo-ui/service",
                dataSource = new kendo.data.DataSource({
                    transport: {
                        read: {
                            url: crudServiceBaseUrl + "/detailproducts",
                            dataType: "jsonp"
                        },
                        update: {
                            url: crudServiceBaseUrl + "/detailproducts/Update",
                            dataType: "jsonp"
                        },
                        destroy: {
                            url: crudServiceBaseUrl + "/detailproducts/Destroy",
                            dataType: "jsonp"
                        },
                        parameterMap: function (options, operation) {
                            if (operation !== "read" && options.models) {
                                return { models: kendo.stringify(options.models) };
                            }
                        }
                    },
                    batch: true,
                    pageSize: 20,
                    autoSync: true,
                    aggregate: [{
                        field: "TotalSales",
                        aggregate: "sum"
                    }],
                    group: {
                        field: "Category.CategoryName",
                        dir: "desc",
                        aggregates: [
                            { field: "TotalSales", aggregate: "sum" }
                        ]
                    },
                    schema: {
                        model: {
                            id: "ProductID",
                            fields: {
                                ProductID: { editable: false, nullable: true },
                                Discontinued: { type: "boolean", editable: false },
                                TotalSales: { type: "number", editable: false },
                                TargetSales: { type: "number", editable: false },
                                LastSupply: { type: "date" },
                                UnitPrice: { type: "number" },
                                UnitsInStock: { type: "number" },
                                Category: {
                                    defaultValue: {
                                        CategoryID: 8,
                                        CategoryName: "Seafood"
                                    }
                                },
                                Country: {
                                    defaultValue: {
                                        CountryNameLong: "Bulgaria",
                                        CountryNameShort: "bg"
                                    }
                                }
                            }
                        }
                    }
                });

            $("#grid").kendoGrid({
                dataSource: dataSource,
                columnMenu: {
                    filterable: false
                },
                // 移除固定height,改用窗口滚动
                scrollable: true,
                editable: "incell",
                pageable: true,
                sortable: true,
                navigatable: true,
                resizable: true,
                reorderable: true,
                groupable: true,
                filterable: true,
                dataBound: onDataBound,
                toolbar: ["excel", "pdf", "search"],
                columns: [{
                    selectable: true,
                    width: 75,
                    attributes: {
                        "class": "checkbox-align",
                    },
                    headerAttributes: {
                        "class": "checkbox-align",
                    }
                }, {
                    field: "ProductName",
                    title: "Product Name",
                    template: "<div class='product-photo' style='background-image: url(../content/web/foods/#:data.ProductID#.jpg);'></div><div class='product-name'>#: ProductName #</div>",
                    width: 300
                }, {
                    field: "UnitPrice",
                    title: "Price",
                    format: "{0:c}",
                    width: 105
                }, {
                    field: "Discontinued",
                    title: "In Stock",
                    template: "<span id='badge_#=ProductID#' class='badgeTemplate'></span>",
                    width: 130,
                }, {
                    field: "Category.CategoryName",
                    title: "Category",
                    editor: clientCategoryEditor,
                    groupHeaderTemplate: "Category: #=data.value#, Total Sales: #=kendo.format('{0:c}', aggregates.TotalSales.sum)#",
                    width: 125
                }, {
                    field: "CustomerRating",
                    title: "Rating",
                    template: "<input id='rating_#=ProductID#' data-bind='value: CustomerRating' class='rating'/>",
                    editable: returnFalse,
                    width: 200
                }, {
                    field: "Country.CountryNameLong",
                    title: "Country",
                    template: "<div class='k-text-center'><img src='../content/web/country-flags/#:data.Country.CountryNameShort#.png' alt='Kendo UI for jQuery Grid #: data.Country.CountryNameLong# Flag' title='#: data.Country.CountryNameLong#' width='30' /></div>",
                    editor: clientCountryEditor,
                    width: 120
                }, {
                    field: "UnitsInStock",
                    title: "Units",
                    width: 105
                }, {
                    field: "TotalSales",
                    title: "Total Sales",
                    format: "{0:c}",
                    width: 140,
                    aggregates: ["sum"],
                }, {
                    field: "TargetSales",
                    title: "Target Sales",
                    format: "{0:c}",
                    template: "<span id='chart_#= ProductID#' class='sparkline-chart'></span>",
                    width: 220
                },
                { command: "destroy", title: "&nbsp;", width: 120 }],
            });

            // 添加顶部滚动条
            var grid = $("#grid").data("kendoGrid");
            var topScroll = $("<div class='k-grid-scrollbar-top'></div>").insertBefore(grid.wrapper);
            var bottomScroll = grid.wrapper.find(".k-grid-content");

            topScroll.scroll(function() {
                bottomScroll.scrollLeft($(this).scrollLeft());
            });
            bottomScroll.scroll(function() {
                topScroll.scrollLeft($(this).scrollLeft());
            });

            topScroll.css({
                overflowX: "scroll",
                overflowY: "hidden",
                height: "18px",
                width: bottomScroll.width()
            });

            topScroll.append($("<div></div>").css({
                width: bottomScroll[0].scrollWidth,
                height: 1
            }));
        });

        function onDataBound(e) {
            var grid = this;
            // 同步表头与内容列宽
            grid.thead.find("th").each(function(index) {
                var contentWidth = grid.tbody.find("td").eq(index).width();
                $(this).width(contentWidth);
            });

            grid.table.find("tr").each(function () {
                var dataItem = grid.dataItem(this);
                var themeColor = dataItem.Discontinued ? 'success' : 'error';
                var text = dataItem.Discontinued ? 'available' : 'not available';

                $(this).find(".badgeTemplate").kendoBadge({
                    themeColor: themeColor,
                    text: text,
                });

                $(this).find(".rating").kendoRating({
                    min: 1,
                    max: 5,
                    label: false,
                    selection: "continuous"
                });

                $(this).find(".sparkline-chart").kendoSparkline({
                    legend: {
                        visible: false
                    },
                    data: [dataItem.TargetSales],
                    type: "bar",
                    chartArea: {
                        margin: 0,
                        width: 180,
                        background: "transparent"
                    },
                    seriesDefaults: {
                        labels: {
                            visible: true,
                            format: '{0}%',
                            background: 'none'
                        }
                    },
                    categoryAxis: {
                        majorGridLines: {
                            visible: false
                        },
                        majorTicks: {
                            visible: false
                        }
                    },
                    valueAxis: {
                        type: "numeric",
                        min: 0,
                        max: 130,
                        visible: false,
                        labels: {
                            visible: false
                        },
                        minorTicks: { visible: false },
                        majorGridLines: { visible: false }
                    },
                    tooltip: {
                        visible: false
                    }
                });

                kendo.bind($(this), dataItem);
            });
        }

        function returnFalse() {
            return false;
        }

        function clientCategoryEditor(container, options) {
            $('<input required name="Category">')
                .appendTo(container)
                .kendoDropDownList({
                    autoBind: false,
                    dataTextField: "CategoryName",
                    dataValueField: "CategoryID",
                    dataSource: {
                        data: categories
                    }
                });
        }

        function clientCountryEditor(container, options) {
            $('<input required name="Country">')
                .appendTo(container)
                .kendoDropDownList({
                    dataTextField: "CountryNameLong",
                    dataValueField: "CountryNameShort",
                    template: "<div class='dropdown-country-wrap'><img src='../content/web/country-flags/#:CountryNameShort#.png' alt='Kendo UI for jQuery Grid #: CountryNameLong# Flag' title='#: CountryNameLong#' width='30' /><span>#:CountryNameLong #</span></div>",
                    dataSource: {
                        transport: {
                            read: {
                                url: " https://demos.telerik.com/kendo-ui/service/countries",
                                dataType: "jsonp"
                            }
                        }
                    },
                    autoWidth: true
                });
        }

        var categories = [{
            "CategoryID": 1,
            "CategoryName": "Beverages"
        }, {
            "CategoryID": 2,
            "CategoryName": "Condiments"
        }, {
            "CategoryID": 3,
            "CategoryName": "Confections"
        }, {
            "CategoryID": 4,
            "CategoryName": "Dairy Products"
        }, {
            "CategoryID": 5,
            "CategoryName": "Grains/Cereals"
        }, {
            "CategoryID": 6,
            "CategoryName": "Meat/Poultry"
        }, {
            "CategoryID": 7,
            "CategoryName": "Produce"
        }, {
            "CategoryID": 8,
            "CategoryName": "Seafood"
        }];
    </script>

    <style type="text/css">
        .customer-photo {
            display: inline-block;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background-size: 32px 35px;
            background-position: center center;
            vertical-align: middle;
            line-height: 32px;
            box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2);
            margin-left: 5px;
        }

        .customer-name {
            display: inline-block;
            vertical-align: middle;
            line-height: 32px;
            padding-left: 3px;
        }

        .k-grid tr .checkbox-align {
            text-align: center;
            vertical-align: middle;
        }

        .product-photo {
            display: inline-block;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background-size: 32px 35px;
            background-position: center center;
            vertical-align: middle;
            line-height: 32px;
            box-shadow: inset 0 0 1px #999, inset 0 0 10px rgba(0,0,0,.2);
            margin-right: 5px;
        }

        .product-name {
            display: inline-block;
            vertical-align: middle;
            line-height: 32px;
            padding-left: 3px;
        }

        .k-rating-container .k-rating-item {
            padding: 4px 0;
        }

        .k-rating-container .k-rating-item .k-icon {
            font-size: 16px;
        }

        .dropdown-country-wrap {
            display: flex;
            flex-wrap: nowrap;
            align-items: center;
            white-space: nowrap;
        }

        .dropdown-country-wrap img {
            margin-right: 10px;
        }

        #grid .k-grid-edit-row > td > .k-rating {
            margin-left: 0;
            width: 100%;
        }

        /* 固定表头样式 */
        .k-grid-header {
            position: sticky;
            top: 0;
            z-index: 100;
            background-color: #fff;
        }

        /* 顶部滚动条样式 */
        .k-grid-scrollbar-top {
            margin-bottom: -1px;
            border-bottom: 1px solid #ddd;
        }
    </style>
</div>

</body>
</html>

内容的提问来源于stack exchange,提问作者Kunal Vijan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:35:55