简体   繁体   中英

Build scrollable table with fixed column

I need to achieve something like this

在此处输入图片说明

The scroll to the right is dynamic (the number of columns is variable), and the first column in the left is fixed. The scrolling should be with the arrows at the top of the image, but I can negotiate to discard that.

My first thought was to use flexbox like this (this is an example to mock, I will use angular with ng-repeat to dynamically generate the structure)

 .reporting-table-container { display: flex; padding-left: 25px; padding-right: 10px; margin-bottom: 15px; } .reporting-table-container .columns-container { display: flex; overflow-x: scroll; margin-right: 15px; } .reporting-table-container .columns-container .single-column-container { display: flex; flex-direction: column; justify-content: space-evenly; } .reporting-table-container .columns-container .single-column-container:nth-child(even), .reporting-table-container .labels-container { background-color: #e9e9e9; } .reporting-table-container .labels-container { display: flex; flex-direction: column; justify-content: space-evenly; } .reporting-table-container .labels-container > div { width: 100px; font-weight: 800px; } 
 <div class="reporting-table-container"> <div class="labels-container"> <div>ID</div> <div>Product Title</div> <div>Strategy Name</div> <div>Score</div> <div>Message</div> <div>Feedback</div> <div>Clicks</div> <div>Conversion Rate</div> <div>Revenue</div> </div> <div class="columns-container"> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> <div class="single-column-container"> <div>2249</div> <div>25.5 cu. ft. French Door Refrigerator in Stainless Steel</div> <div>Online co-purchase 2wks</div> <div>230</div> <div>Customer also bough this other item</div> <div>43</div> <div>0.50%</div> <div>$282.830</div> </div> </div> </div> 

but quickly it became apparent that the borders won't follow between the label columns, and the data columns. Besides, there's no way to me (at least without javascript) to match the heights for the very same row (I should expand the row height given the largest data for that row)

Then I moved into CSS Grid Layouts, but I am not quite sure how should it look - Difference from my current example, all "cells" would be within the same unit so I am not sure how to fix the first column nor how to set the css dynamic, depending on the number of items to render.

How can I achieve that?

Notice : Although my code will use angularjs to dynamically generate the structure, I believe the problem is pure CSS/HTML (I would like to avoid the usage of javascript to generate this).

Thanks in Advance

I finally manage to fix this by using css grids.

The following is a mock with the essential, but of course I will use real data (as well as an image that loads properly).

The key was in using grid-template-columns and ng-style to dynamically assign the number of columns

I am still missing the custom scrollbar next to the images, but I believe that's another story

 (function(){ function reportingTableController() { this.recommendations = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } angular .module('app', []) .component('reportingTable', { template: `<div class="reporting-table-container" ng-style="{'grid-template-columns': '90px repeat({{$ctrl.recommendations.length + 1}}, 130px)'}"> <div style="grid-row:1;" class="row-label-title"></div> <div style="grid-row:2;" class="row-label-title">ID</div> <div style="grid-row:3;" class="row-label-title">Product Title</div> <div style="grid-row:4;" ng-style="{'grid-column-end': $ctrl.recommendations.length + 2}" class="orange-delimiter"></div> <div style="grid-row:5;" class="row-label-title">Strategy Name</div> <div style="grid-row:6;" class="row-label-title">Score</div> <div style="grid-row:7;" class="row-label-title">Message</div> <div style="grid-row:8" ng-style="{'grid-column-end': $ctrl.recommendations.length + 2}" class="orange-delimiter">></div> <div style="grid-row:9;" class="row-label-title">Feedback</div> <div style="grid-row:10;" class="row-label-title">Clicks</div> <div style="grid-row:11;" class="row-label-title">Conversion Rate</div> <div style="grid-row:12;" class="row-label-title">Revenue</div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 1, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> <img src="Angular/app/RecommendationsTab/mock.png" alt="recommendation image" /> </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 2, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> 2249 </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 3, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> 25.5 cu. ft. French Door Refrigerator in Stainless Steel </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 5, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> Online co-purchase 2wks </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 6, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> 230 </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 7, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> Customer also bough this other item </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 9, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> Mailbox </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 10, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> 43 </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 11, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> 0.50% </div> <div ng-class-even="'even-cell'" class="reporting-table-cell" ng-style="{'grid-row': 12, 'grid-column': $index + 2 }" ng-repeat="recommendation in $ctrl.recommendations"> $282.830 </div> </div>`, controller: reportingTableController, }); })(); 
 .reporting-table-container { display:grid; padding-right: 10px; margin-bottom: 15px; margin-left: 25px; background-color: white; overflow-x: scroll; } .reporting-table-container .row-label-title { font-weight: 800; left: 0; position: sticky; grid-column: 1; } /* grid cells*/ .reporting-table-container > div { padding-left: 6px; padding-right: 6px; padding-top: 8px; padding-bottom: 10px; } .reporting-table-container .row-label-title, .reporting-table-container .reporting-table-cell.even-cell { background-color: #e9e9e9; } .reporting-table-container .reporting-table-cell, .reporting-table-container .row-label-title { border-bottom: #d1d1d1 1px solid; } .reporting-table-container .row-label-title:first-of-type { background-color: white; border-bottom: none; } .reporting-table-container .orange-delimiter { border: 1px solid #f96302; background-color: white; grid-column-start: 1; height: 1px; margin-bottom: 3px; margin-top: 3px; padding: 0; } .reporting-table-container .reporting-table-cell > img { max-width: calc(100% - 12px);/* 12 px of padding*/ } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script> <body> <div ng-app="app"> <reporting-table></reporting-table> </div> </body> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM