HiveBrain v1.2.0
Get Started
← Back to all entries
debugjavascriptMinor

Fixed table header with scrollable body and aligning columns

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
headercolumnswithandaligningfixedbodytablescrollable

Problem

Here is my solution for a fixed table header with scrollable body and aligning columns.


The requirements I wanted to achieve were:



  • Fix ` while can scroll while the all the and cells of one column all have the same width



  • No fixed width



  • Pure HTML, CSS, JS



  • Bonus points for pure CSS solution





What could still be improved:



  • Make the scroll bar as longer so there is no free area in the top right



  • Improve the Javascript, I'm sure it can be rewrote to be more efficient



  • Clean the code, especially the Javascript



  • Pure CSS solution, if possible




I would also like to have general advice on my coding style, good or bad practices and maybe an evaluation of how efficient this code is. How good would it perform if the matrix size grows? Right now, I'm just happy that my code works in my case. :)

You can find the matrix that needs to be tweaked here:
JsFiddle

The table in this example is from a match plan for last year for my sports team.



// jshint esversion: 6
// jshint browser: true
// jshint devel: true

const thElements = document.getElementsByTagName("th");
const tdElements = document.getElementsByTagName("td");
let width = [];
let tempResult = 0;
// calculate needed width
for (let i = 0; i tdElements[i].offsetWidth) {

// get inner width because thats what we will set
tempResult = window.getComputedStyle(thElements[i], null)
.getPropertyValue("width");
width[i] = ${tempResult.toString()};
} else {

// get inner width because thats what we will set
tempResult = window.getComputedStyle(tdElements[i], null)
.getPropertyValue("width");
width[i] = ${tempResult.toString()};
}
}

// set column width
for (let i = 0; i
`/container /

#club_plan {
clear: both;
overflow-x: auto;
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%)
}

thead tr {
position: relative;
}

tbody {
display: block;
max-height: 15

Solution

Pure HTML and CSS

Solution below may be a little hackery, because CSS wasn't made for things like that, yet. It has it's downs when you look deeper, but well: that's what you get for writing hackery CSS. Also, it resembles design from your question from before the edits you made.



.table-container {
border: 1px solid #963;
display: inline-block;
height: 150px;
padding-top: 1.875em;
position: relative;
}
.table-container > div {
border-color: #888;
border-style: solid;
border-width: 1px 1px 2px 1px;
height: 1.7em;
left: 0;
position: absolute;
right: 0;
top: 0;
}
table {
display: block;
height: 150px;
overflow-y: auto;
}
th {
padding: 0 5px;
text-align: center;
}
th:first-child > div {
border-left: none;
padding-left: 7px;
}
th > div {
border-left: 2px solid #888;
line-height: 1.875em;
margin-left: -6px;
padding-left: 5px;
position: absolute;
top: 0;
}
td {
border-color: #888;
border-style: solid;
border-width: 0 1px 2px 1px;
padding: 5px;
}





Title






Tag
Datum
Uhrzeit
Liga
Heimmannschaft
Gastmannschaft




Do.
Heute
19:45
H1KK
TuS Hiltrup V
DJK Borussia Münster IV


Fr.
30.09.2016
19:30
H3KK
SC Westfalia Kinderhaus V
DJK Borussia Münster VI




20:00 v
HBL
DJK Borussia Münster II
1. TTC Münster III


Sa.
01.10.2016
18:30
HNRWL
DJK Borussia Münster
1. TTC Münster




18:30
DNRWL
DJK Borussia Münster
TTC Werne 98


Di.
04.10.2016
20:15
H1KK
DJK Borussia Münster IV
1. TTC Münster VII


Do.
06.10.2016
20:00
H2KK
1. FC Gievenbeck IV
DJK Borussia Münster V


Fr.
07.10.2016
19:30
HKL
DJK SC Nienberge
DJK Borussia Münster III


Sa.
08.10.2016
17:30 v
HBL
SC Westfalia Kinderhaus II
DJK Borussia Münster II




18:30
DNRWL
TSSV Bottrop
DJK Borussia Münster


So.
09.10.2016
10:00
HNRWL
GSV Fröndenberg
DJK Borussia Münster









Remarks to your code

JS rewrite

Your entire JavaScript code could be as simple as

const thElements = document.getElementsByTagName('th'),
      tdElements = document.getElementsByTagName('td');

for (let i = 0; i  tdElements[i].offsetWidth ? thElements[i] : tdElements[i],
        width        = window.getComputedStyle(widerElement).width;

  thElements[i].style.width = tdElements[i].style.width = width;
}


Unnecessary template literal and toString()

This

width[i] = `${tempResult.toString()}`;


is exactly the same as

width[i] = tempResult;


because tempResult is already a string and as such both template literal and toString() are redundant.

Code repetition

Instead of entire if else, you could decide whether to call getComputedStyle() with thElements or tdElements parameter based on the result of computation of thElements[i].offsetWidth > tdElements[i].offsetWidth. See above code rewrite to see it implemented.

getComputedStyle()'s second parameter

getComputedStyle()'s second parameter null can be fully omitted.

Unnecessary second loop and width[]

Instead of saving all widths to an array and then running one more loop through that array, you could just set the widths right away. That way you wouldn't need neither a second loop nor the width[] array. Right now the said loop makes your solution have the computational complexity of \$O(2n)\$, where \$n\$ is the thElements.length, while it could be \$O(n)\$.

Use const instead of let for array declaration

Since it's better to use const „by default”, this

let width = [];


should be

const width = [];


since the value of width itself doesn't change ― it's always the same array throughout the code; only those array's values change.

Window resizing scenario

This code doesn't account for possibility of viewport's scaling.

Code Snippets

const thElements = document.getElementsByTagName('th'),
      tdElements = document.getElementsByTagName('td');

for (let i = 0; i < thElements.length; i++) {
  const widerElement = thElements[i].offsetWidth > tdElements[i].offsetWidth ? thElements[i] : tdElements[i],
        width        = window.getComputedStyle(widerElement).width;

  thElements[i].style.width = tdElements[i].style.width = width;
}
width[i] = `${tempResult.toString()}`;
width[i] = tempResult;
let width = [];
const width = [];

Context

StackExchange Code Review Q#160979, answer score: 8

Revisions (0)

No revisions yet.