【问题标题】:Pin header and first row on Excel like table with filterExcel 上的 Pin 标题和第一行,如带有过滤器的表格
【发布时间】:2025-11-25 23:50:01
【问题描述】:

我有类似 Excel 的表格过滤器,我需要的是我想在向下滚动时固定标题和第一行。即使我应用过滤器,它也应该可以工作。

请注意小提琴:

http://jsfiddle.net/6ng3bz5c/1/

我尝试了以下方法但没有用:

$('tbody').scroll(function(e) { //detect a scroll event on the tbody
     $('tbody tr:nth-child(2)').css("down", $("tbody").scrollDown()); 
  });

前两个突出显示的行应保持向下滚动:

请指导我。

【问题讨论】:

    标签: jquery css angularjs html-table scroll


    【解决方案1】:

    我建议您将position: sticky (mdn) 与first-of-type 结合使用来处理第一行(在我自己的项目中,我会使用类来设置这些样式,但我正在使用元素选择器以免更改你的标记)。

    更多地了解您的 CSS,您还可以将第一行上的 top 偏移更改为更具响应性的东西,但为了演示,我只是使用了似乎对我有用的高度。您还需要添加边框。

    thead th{
      position:sticky;
      top:0;
      background:white;
    }
    tbody tr:first-of-type td{
      position:sticky;
      top:34px;
      background:white;
    }
    

    var myApp = angular.module('myApp', [])
      .controller('employeeController', function($scope) {
        $scope.XLfilters = { list: [], dict: {}, results: [] };
        $scope.markAll = function(field, b) {
          $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=b;});
        }
        $scope.clearAll = function(field) {
          $scope.XLfilters.dict[field].searchText='';
          $scope.XLfilters.dict[field].list.forEach((x) => {x.checked=true;});
        }
        $scope.XLfiltrate = function() {
        	var i,j,k,selected,blocks,filter,option, data=$scope.XLfilters.all,filters=$scope.XLfilters.list;
          $scope.XLfilters.results=[];
          for (j=0; j<filters.length; j++) {
          	filter=filters[j];
            filter.regex = filter.searchText.length?new RegExp(filter.searchText, 'i'):false;
            for(k=0,selected=0;k<filter.list.length;k++){
            	if(!filter.list[k].checked)selected++;
              filter.list[k].visible=false;
              filter.list[k].match=filter.regex?filter.list[k].title.match(filter.regex):true;
            }
            filter.isActive=filter.searchText.length>0||selected>0;
          }
          for (i=0; i<data.length; i++){
            blocks={allows:[],rejects:[],mismatch:false};
          	for (j=0; j<filters.length; j++) {
              filter=filters[j]; option=filter.dict[data[i][filter.field]];
              (option.checked?blocks.allows:blocks.rejects).push(option);
              if(filter.regex && !option.match) blocks.mismatch=true;
          	}
            if(blocks.rejects.length==1) blocks.rejects[0].visible=true;
            else if(blocks.rejects.length==0&&!blocks.mismatch){
              $scope.XLfilters.results.push(data[i]);
            	blocks.allows.forEach((x)=>{x.visible=true});
            }
          }
          for (j=0; j<filters.length; j++) {
          	filter=filters[j];filter.options=[];
            for(k=0;k<filter.list.length;k++){
              if(filter.list[k].visible && filter.list[k].match) filter.options.push(filter.list[k]);
            }
          }
        }
        function createXLfilters(arr, fields) {
          $scope.XLfilters.all = arr;
          for (var j=0; j<fields.length; j++) $scope.XLfilters.list.push($scope.XLfilters.dict[fields[j]]={list:[],dict:{},field:fields[j],searchText:"",active:false,options:[]});
          for (var i=0,z; i<arr.length; i++) for (j=0; j<fields.length; j++) {
          z=$scope.XLfilters.dict[fields[j]];
          z.dict[arr[i][fields[j]]] || z.list.push(z.dict[arr[i][fields[j]]]={title:arr[i][fields[j]],checked:true, visible:false,match:false});
          }
        }
    
        createXLfilters(employees, ['Country','City']);
    })
    
    var employees = [{
      "Name": "Manirul Monir",
      "City": "Sylhet",
      "Country": "Bangladesh"
    }, {
      "Name": "Arup",
      "City": "Sylhet",
      "Country": "Bangladesh"
    }, {
      "Name": "Person 1",
      "City": "Dhaka",
      "Country": "Bangladesh"
    }, {
      "Name": "Person 2",
      "City": "Dhaka",
      "Country": "Bangladesh"
    }, {
      "Name": "Munim Munna",
      "City": "Narshingdi",
      "Country": "Bangladesh"
    }, {
      "Name": "Mahfuz Ahmed",
      "City": "Narshingdi",
      "Country": "Bangladesh"
    }, {
      "Name": "Tawkir Ahmed",
      "City": "Gazipur",
      "Country": "Bangladesh"
    }, {
      "Name": "Alfreds 2",
      "City": "Berlin",
      "Country": "Germany"
    }, {
      "Name": "Alfreds Futterkiste",
      "City": "Berlin",
      "Country": "Germany"
    }, {
      "Name": "Blauer See Delikatessen",
      "City": "Mannheim",
      "Country": "Germany"
    }, {
      "Name": "Blondel père et fils",
      "City": "Strasbourg",
      "Country": "France"
    }, {
      "Name": "Bon app'",
      "City": "Marseille",
      "Country": "France"
    }, {
      "Name": "Centro comercial Moctezuma",
      "City": "México D.F.",
      "Country": "France"
    }];
    .filterdropdown{
      background: #eee;
      border: 1px solid #bbb;
      color: #bbb;
      border-radius: 2px;
      font-size: 14px;
      line-height: 14px;
    }
    .filterdropdown.active{
      color: #005b9c;
    }
    a.filterlink{
      font-size: 12px;
    }
    .options {
      height: 150px;
      overflow-y: scroll;
    }
    thead th{
      position:sticky;
      top:0;
      background:white;
    }
    tbody tr:first-of-type td{
      position:sticky;
      top:34px;
      background:white;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
    <div class="col-md-6" ng-app="myApp" ng-controller="employeeController">
      <table class="table table-bordered table-condensed" data="{{XLfiltrate()}}">
        <thead>
          <tr>
            <th>Country
              <div class="dropdown" style="float: right">
                <button class="dropdown-toggle filterdropdown" ng-class="{active:XLfilters.dict['Country'].isActive}" type="button" data-toggle="dropdown"><span class="glyphicon glyphicon-filter"></span></button>
                <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
                  <div class="text-right">
                    <a href="#" class="filterlink" ng-click="clearAll('Country')">Clear All</a> | <a href="#" class="filterlink" data-dismiss="modal">Close(X)</a>
                  </div>
                  <form>
                    <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['Country'].searchText" placeholder="Filter by Country" />
                    <div>
                      <a href="#" class="filterlink" ng-click="markAll('Country',true)">Select All</a> | <a href="#" class="filterlink" ng-click="markAll('Country',false)">Select None</a>
                    </div>
                    <div class="options">
                      <ul style="list-style-type: none; padding: 0">
                        <li ng-repeat="item in XLfilters.dict['Country'].options">
                          <input id="countryOption{{$index}}" type="checkbox" ng-model="XLfilters.dict['Country'].dict[item.title].checked" />&nbsp;<label for="countryOption{{$index}}">{{item.title}}</label>
                        </li>
                      </ul>
                    </div>
                  </form>
                </div>
              </div>
            </th>
            <th>City
              <div class="dropdown" style="float: right">
                <button class="dropdown-toggle filterdropdown" ng-class="{active:XLfilters.dict['City'].isActive}" type="button" data-toggle="dropdown"><span class="glyphicon glyphicon-filter"></span></button>
                <div class="dropdown-menu prop" aria-labelledby="btnSearchBatch" style="width: 250px; padding: 15px;">
                  <div class="text-right">
                    <a href="#" class="filterlink" ng-click="clearAll('City')">Clear All</a> | <a href="#" class="filterlink" data-dismiss="modal">Close(X)</a>
                  </div>
                  <form>
                    <input type="text" class="form-control input-sm" ng-model="XLfilters.dict['City'].searchText" placeholder="Filter by City" />
                    <div>
                      <a href="#" class="filterlink" ng-click="markAll('City',true)">Select All</a> | <a href="#" class="filterlink" ng-click="markAll('City',false)">Select None</a>
                    </div>
                    <div class="options">
                      <ul style="list-style-type: none; padding: 0">
                        <li ng-repeat="item in XLfilters.dict['City'].options">
                          <input id="cityOption{{$index}}" type="checkbox" ng-model="XLfilters.dict['City'].dict[item.title].checked" />&nbsp;<label for="cityOption{{$index}}">{{item.title}}</label>
                        </li>
                      </ul>
                    </div>
                  </form>
                </div>
              </div>
            </th>
            <th>Name</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="emp in XLfilters.results">
            <td>{{emp.Country}}</td>
            <td>{{emp.City}}</td>
            <td>{{emp.Name}}</td>
          </tr>
        </tbody>
      </table>
    </div>

    【讨论】:

    • 它会使标题保持不变,但我想让第一行也保持不变。请参考我在图片中突出显示的图片。
    • 哎呀错过了;在我看来,first-of-type 在那里工作。查看更新的答案。
    • 这可以在分页上实现吗?就像最初显示 10 条记录并显示所有值以进行相应的过滤和应用
    最近更新 更多