Reputation: 1116
I'm creating a table component that will have between 1-10 columns depending on the data it receives. The columns are inside a container that is responsive and they can never be smaller than 100px. Depending on the screen size I would like to show all the columns, but as the container gets smaller I want to hide the columns that don't fit.
e.g. If the width of the container is 550px and there are 7 columns. I want to hide the last two columns and show the remaining 5. The width of those 5 columns should be (550/5 = 110px). I can achieve this by simply adding a class to the parent container that divides the children by 5 (20%).
Once I hide those last two columns I want to be able to click a button to shift the row over to show the hidden columns. Keep in mind that I would want it to shift the columns based on how much room is in the container. e.g. If the user makes the window smaller and the width of the container is now 380px and there are 7 columns you should only see three at a time. If you click the next bottom it will show the next three items. If you click it one more time it will show the last one.
My question is how can I remove the excess columns and apply my class to the parent container? Then how can I click through these so I can view all the columns? Below is the JS code I started writing, but I need help putting it all together.
function calcSize() {
var tableExpenses = document.querySelectorAll('.table-expenses');
var tableExpensesWidth = document.querySelector('.table-expenses').offsetWidth;
var tableExpensesLength = document.querySelector('.table-expenses').children.length;
if (tableExpensesWidth > 699 ) {
tableExpenses.classList.add('Rtable--7cols');
// remove all other 'Rtable--' classes
}
if (tableExpensesWidth > 599 ) {
tableExpenses.classList.add('Rtable--6cols');
// remove all other 'Rtable--' classes
}
if (tableExpensesWidth > 499) {
tableExpenses.classList.add('Rtable--5cols');
// remove all other 'Rtable--' classes
}
// Do the same thing for other widths...
if (tableExpensesWidth/tableExpensesLength < 100) {
//only show children that can fill that space
}
}
Upvotes: 0
Views: 346
Reputation: 26877
I think that there could be a much simpler solution to this, but I tried to figure out how to do this will limited JavaScript and media queries. The approach is to have a "start" class that denotes the "focused" table column. Hide all columns that aren't the "start" column or aren't a configured number of siblings adjacent to "start".
This is done with multiple adjacent sibling selectors.
In my example I have some helper classes to simulate media queries without actually resizing the screen. You can control the simulated screen size with the radio buttons and what column is focused with the number input.
It's a lot of CSS, but you don't have to do any element sizing within the code.
const m = document.getElementsByTagName("main")[0],
table = document.getElementById("table"),
countInput = document.getElementById("count"),
sizeRadios = document.getElementsByName("size");
const vars = {
get size() {
return this._size;
},
set size(val) {
if (this._size === val) return;
this._size = val;
Array.from(sizeRadios).forEach(r => r.removeAttribute("checked"));
let tmp = document.querySelector(`input[type="radio"][name="size"][value="${val}"]`);
tmp.setAttribute("checked", "");
m.className = this._size;
},
get count() {
return this._count;
},
set count(val) {
if (this._count === val) return;
this._count = val;
countInput.value = val;
this.updateTable();
},
updateTable() {
const trs = table.querySelectorAll("tr");
for (const tr of trs) {
const tds = Array.from(tr.querySelectorAll("th, td"));
if (tds.length < this.count) {
return;
}
tds.forEach(td => td.classList.remove("start"));
tds[this.count - 1].classList.add("start");
}
}
};
Array.from(sizeRadios).forEach(radio => radio.addEventListener("change", e => {
if (!e.target.checked) return;
vars.size = e.target.value;
}));
countInput.addEventListener("input", e => {
vars.count = parseInt(countInput.value, 10);
});
vars.size = "medium";
vars.count = 2;
table {
width: 100%;
}
table th,
table td {
border: 1px solid black;
}
main:not(.default) table th,
main:not(.default) table td {
display: none;
}
/* Simulated Media Query Rules */
main.extra-small table th.start,
main.extra-small table td.start,
main.small table th.start,
main.small table td.start,
main.medium table th.start,
main.medium table td.start,
main.large table th.start,
main.large table td.start,
main.small table th.start+th,
main.small table td.start+td,
main.medium table th.start+th,
main.medium table td.start+td,
main.large table th.start+th,
main.large table td.start+td,
main.medium table th.start+th+th,
main.medium table td.start+td+td,
main.large table th.start+th+th,
main.large table td.start+td+td,
main.large table th.start+th+th+th,
main.large table td.start+td+td+td {
display: table-cell;
}
/* Media Query Rules */
main.media-query table th.start,
main.media-query table td.start {
display: table-cell;
}
@media (min-width: 768px) {
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start+th,
main.media-query table td.start+td {
display: table-cell;
}
}
@media (min-width: 992px) {
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start+th,
main.media-query table td.start+td,
main.media-query table th.start+th,
main.media-query table td.start+td,
main.media-query table th.start+th+th,
main.media-query table td.start+td+td {
display: table-cell;
}
}
@media (min-width: 1200px) {
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start,
main.media-query table td.start,
main.media-query table th.start+th,
main.media-query table td.start+td,
main.media-query table th.start+th,
main.media-query table td.start+td,
main.media-query table th.start+th,
main.media-query table td.start+td,
main.media-query table th.start+th+th,
main.media-query table td.start+td+td,
main.media-query table th.start+th+th,
main.media-query table td.start+td+td,
main.media-query table th.start+th+th+th,
main.media-query table td.start+td+td+td {
display: table-cell;
}
}
<main>
<table id="table">
<caption>Table that will adjust how many colums are displayed based on screen size/controlling class.</caption>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
<th>Column 5</th>
<th>Column 6</th>
</tr>
</thead>
<tbody>
<tr>
<td>a1</td>
<td>a2</td>
<td>a3</td>
<td>a4</td>
<td>a5</td>
<td>a6</td>
</tr>
<tr>
<td>b1</td>
<td>b2</td>
<td>b3</td>
<td>b4</td>
<td>b5</td>
<td>b6</td>
</tr>
<tr>
<td>c1</td>
<td>c2</td>
<td>c3</td>
<td>c4</td>
<td>c5</td>
<td>c6</td>
</tr>
</tbody>
</table>
<hr>
<div>
<p>Mock media query setter which applies a class to the <code><main></code> tag to simulate media queries.</p>
<ul>
<li><label><input type="radio" name="size" value="default"> <span>Default (all columns)</span></label></li>
<li><label><input type="radio" name="size" value="media-query"> <span>Media Query (follow MQ rules)</span></label></li>
<li><label><input type="radio" name="size" value="extra-small"> <span>Extra Small (1 column)</span></label></li>
<li><label><input type="radio" name="size" value="small"> <span>Small (2 columns)</span></label></li>
<li><label><input type="radio" name="size" value="medium"> <span>Medium (3 columns)</span></label></li>
<li><label><input type="radio" name="size" value="large"> <span>Large (4 columns)</span></label></li>
</ul>
</div>
<hr>
<div>
<p>Sets which column you are "focused" as in which column you have scrolled to.</p>
<label><span>Starting Column</span> <input id="count" type="number" min="1" value="3" step="1"></label>
</div>
</main>
Upvotes: 2