I've been trying to ceate table-like grid with scroll using Flexbox.
Most of it works okay, but is there any way to force rows to have width of their content when horizontal scrolling is on?
As you can see, every even row have white background, so it's easy to spot that there's a problem with width.
http://jsbin.com/fedisozafe/embed?html,css,output
$('.tbody').on('scroll', function(e) {
$(this).siblings().scrollLeft($(this).scrollLeft());
});
$('.tbody').perfectScrollbar();
$(window).on('resize', function(e) {
$('.tbody')
.perfectScrollbar('update')
.siblings()
.scrollLeft($(this).scrollLeft());
});
angular.module('app', [])
.controller('testController', [
'$scope',
function($scope) {
this.n = 5;
$scope.$watch(() => this.n, () => this.collection = _.range(this.n));
}
]);
* {
box-sizing: border-box;
border-collapse: collapse;
}
.table {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
border: solid 1px red;
}
.table > * {
border-top: solid 1px transparent;
border-bottom: solid 1px transparent;
}
.thead {
border-bottom: solid 1px red;
}
.tfoot {
border-top: solid 1px red;
}
.thead {
flex: none;
display: flex;
flex-direction: column;
overflow: hidden;
order: -1;
background-color: lightgoldenrodyellow;
}
.tbody {
position: relative;
flex: 1 1 auto;
display: flex;
flex-direction: column;
overflow: hidden;
justify-content: space-between;
background-color: lightgray;
}
.tfoot {
flex: none;
display: flex;
flex-direction: column;
overflow: hidden;
order: 1;
background-color: lightblue;
}
.tr {
display: flex;
flex: 1 0 20px;
justify-content: space-between;
align-items: center;
}
.tr:nth-child(2n) {
background-color: white;
}
.td,
.th {
flex: 1 0 60px;
display: inline-flex;
}
.th {
font-weight: bold;
}
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.1/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.10/js/min/perfect-scrollbar.jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.10/css/perfect-scrollbar.min.css"/>
</head>
<body ng-controller="testController as ctrl">
<input type="number" ng-model="ctrl.n" ng-max="100" />
<div style="display: flex">
<div style="flex: 1 1 auto">
<table class="table" style="width: 40vw; height: 40vh">
<tbody class="tbody" style="height: calc(100% - 60px)">
<tr class="tr" ng-repeat="item in ctrl.collection" style="min-width: 200px">
<th class="th">{{item}}</th>
<th class="td" style="flex-basis: 200px">{{item}}</th>
<th class="td">{{item}}</th>
<th class="td">{{item}}</th>
<th class="td">{{item}}</th>
</tr>
</tbody>
<thead class="thead">
<tr class="tr">
<td class="th">A</td>
<td class="th" style="flex-basis: 200px">B</td>
<td class="th">C</td>
<td class="th">D</td>
<td class="th">E</td>
</tr>
</thead>
<tfoot class="tfoot">
<tr class="tr">
<th class="th">A</th>
<th class="th" style="flex-basis: 200px">B</th>
<th class="th">C</th>
<th class="th">D</th>
<th class="th">E</th>
</tr>
</tfoot>
</table>
</div>
<div style="flex: 1 1 auto">
<div class="table" style="width: 40vw; height: 40vh">
<div class="tbody" style="height: calc(100% - 60px)">
<div class="tr" ng-repeat="item in ctrl.collection">
<div class="th">{{item}}</div>
<div class="td" style="flex-basis: 200px">{{item}}</div>
<div class="td">{{item}}</div>
<div class="td">{{item}}</div>
<div class="td">{{item}}</div>
</div>
</div>
<div class="thead">
<div class="tr">
<div class="th">A</div>
<div class="th" style="flex-basis: 200px">B</div>
<div class="th">C</div>
<div class="th">D</div>
<div class="th">E</div>
</div>
</div>
<div class="tfoot">
<div class="tr">
<div class="th">A</div>
<div class="th" style="flex-basis: 200px">B</div>
<div class="th">C</div>
<div class="th">D</div>
<div class="th">E</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Any ideas?
It's important that rows calculate their width based on its cells, not the other way round. In an ideal world row would grow when space is available (and allow cells to grow as well), but would not shrink - that's why scrolling is enabled.
I'm aware that there's a possibility it can't be done. I've spent quite some time on it, but I still cling to hope that I'm just not as clever as you guys.
There were no mistakes, HTML, CSS, and JS (I'll assume Angular and loDash are good to go.) all good. So OP just needed a way to shrink wrap the rows. The rows extended in weird looking overflow? There were some changes to the CSS, but they were more for aesthetics and small adjustments. The main style changes are as follows:
.tr {
display: flex;
min-width: -moz-fit-content;
min-width: -webkit-fit-content;
min-width: fit-content;
flex: 1 0 20px;
justify-content: flex-end;
}
fit-content
is an old but little known property still in it's experimental stage. What it basically does is it will make an element wrap it's content in a perfect fit. So if you apply it to a parent element, the parent will be as wide as it's content and it will increase and decrease with the content.
- Result: The overflow was gone but `tr` did not extend.
Removed the
flex-direction: column
so it would default toflex-direction: row
I figured that atr
flowing in a vertical direction would probably stop short and abruptly.
- Result: The `tr` extended, but the `td`s and `th`s were not aligned.
justify-content: flex-end
applied in order to shift the columns to the right recalling the problem was on the right side of the table.
- Result: http://jsbin.com/godoqi/3/edit?css,output
table-layout: fixed
was also applied to the table so there's more control over column widths.
- Result: See previous result.
jsBin
Snippet
$('.tbody').on('scroll', function(e) {
$(this).siblings().scrollLeft($(this).scrollLeft());
});
$('.tbody').perfectScrollbar();
$(window).on('resize', function(e) {
$('.tbody')
.perfectScrollbar('update')
.siblings()
.scrollLeft($(this).scrollLeft());
});
angular.module('app', [])
.controller('testController', [
'$scope',
function($scope) {
this.n = 5;
$scope.$watch(() => this.n, () => this.collection = _.range(this.n));
}
]);
* {
box-sizing: border-box;
}
.table {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
border: solid 1px red;
border-collapse: collapse;
table-layout: fixed;
}
.table > * {
border-top: solid 1px transparent;
border-bottom: solid 1px transparent;
}
.thead {
border-bottom: solid 1px red;
}
.tfoot {
border-top: solid 1px red;
}
.thead {
display: flex;
flex-direction: column;
overflow: hidden;
order: -1;
background-color: lightgoldenrodyellow;
}
.tbody {
position: relative;
flex: 1 1 auto;
display: flex;
flex-direction: column;
align-content: space-between;
background-color: lightgray;
}
.tfoot {
flex: none;
display: flex;
flex-direction: column;
overflow: hidden;
order: 1;
background-color: lightblue;
text-align: center;
}
.tr {
display: flex;
min-width: -moz-fit-content;
min-width: -webkit-fit-content;
min-width: fit-content;
flex: 1 0 20px;
justify-content: flex-end;
}
.tr:nth-child(2n) {
background-color: white;
}
.td,
.th {
flex: 0 1 60px;
text-align: center;
}
.th {
font-weight: bold;
}
.ng-not-empty {
text-align: center;
width: 8ex;
}
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.5.1/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.10/js/min/perfect-scrollbar.jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.10/css/perfect-scrollbar.min.css" />
</head>
<body ng-controller="testController as ctrl">
<input type="number" ng-model="ctrl.n" ng-max="100" />
<div style="display: flex">
<div style="flex: 1 1 auto">
<table class="table" style="width: 40vw; height: 40vh">
<tbody class="tbody" style="height: calc(100% - 60px)">
<tr class="tr" ng-repeat="item in ctrl.collection" style="min-width: 200px">
<th class="th">{{item}}</th>
<th class="td" style="flex-basis: 200px">{{item}}</th>
<th class="td">{{item}}</th>
<th class="td">{{item}}</th>
<th class="td">{{item}}</th>
</tr>
</tbody>
<thead class="thead">
<tr class="tr">
<td class="th">A</td>
<td class="th" style="flex-basis: 200px">B</td>
<td class="th">C</td>
<td class="th">D</td>
<td class="th">E</td>
</tr>
</thead>
<tfoot class="tfoot">
<tr class="tr">
<th class="th">A</th>
<th class="th" style="flex-basis: 200px">B</th>
<th class="th">C</th>
<th class="th">D</th>
<th class="th">E</th>
</tr>
</tfoot>
</table>
</div>
<div style="flex: 1 1 auto">
<div class="table" style="width: 40vw; height: 40vh">
<div class="tbody" style="height: calc(100% - 60px)">
<div class="tr" ng-repeat="item in ctrl.collection">
<div class="th">{{item}}</div>
<div class="td" style="flex-basis: 200px">{{item}}</div>
<div class="td">{{item}}</div>
<div class="td">{{item}}</div>
<div class="td">{{item}}</div>
</div>
</div>
<div class="thead">
<div class="tr">
<div class="th">A</div>
<div class="th" style="flex-basis: 200px">B</div>
<div class="th">C</div>
<div class="th">D</div>
<div class="th">E</div>
</div>
</div>
<div class="tfoot">
<div class="tr">
<div class="th">A</div>
<div class="th" style="flex-basis: 200px">B</div>
<div class="th">C</div>
<div class="th">D</div>
<div class="th">E</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With