Issue
I made a table with the header and the last column fixed using position: sticky
. I want to remove the shadows from the last column when the scroll bar is horizontally at the end, and remove the shadows from the header when the scroll bar is vertically at the beginning. In AntDesign there is an example of the desired result, but in this case the table has shadows only in the fixed columns, not in the header.
Although I don't want solutions with Scroll Event Listener because of performance reasons (see Scroll-linked effects), if someone solves it that way, they can share it for reference and help other people.
The code is also available at CodeSandbox.
main {
display: flex;
max-height: 20rem;
overflow: auto;
}
table {
border-spacing: 0;
}
th {
text-align: left;
}
th,
td {
border-bottom: 1px solid #c6c6c6;
height: 5rem;
white-space: nowrap;
padding-right: 2rem;
}
.fixed {
background-color: white;
position: sticky;
}
.fixed-top {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2);
top: 0;
z-index: 1;
}
.fixed-right {
border-left: 1px solid #c6c6c6;
box-shadow: -5px 0px 5px 0px rgba(0, 0, 0, 0.2);
padding-left: 1rem;
right: 0;
z-index: 1;
}
.fixed-top.fixed-right {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2), -5px 0px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 2;
}
<table>
<thead>
<tr>
<th class="fixed fixed-top">Animal</th>
<th class="fixed fixed-top">Age</th>
<th class="fixed fixed-top">Country</th>
<th class="fixed fixed-top">Sentence</th>
<th class="fixed fixed-top">Color</th>
<th class="fixed fixed-top fixed-right">Programming Language</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sable</td>
<td>15 yo</td>
<td>Japan</td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</td>
<td>Purple</td>
<td class="fixed fixed-right">Kotlin</td>
</tr>
<tr>
<td>Toco toucan</td>
<td>35 yo</td>
<td>Brazil</td>
<td>
Sed tortor erat, imperdiet a enim quis, placerat rhoncus nisl.
</td>
<td>Orange</td>
<td class="fixed fixed-right">Swift</td>
</tr>
<tr>
<td>Bull</td>
<td>42 yo</td>
<td>Spain</td>
<td>Donec vitae risus urna.</td>
<td>Red</td>
<td class="fixed fixed-right">JavaScript</td>
</tr>
<tr>
<td>Brown bear</td>
<td>17 yo</td>
<td>Russia</td>
<td>Proin gravida et velit ut congue.</td>
<td>Green</td>
<td class="fixed fixed-right">Python</td>
</tr>
</tbody>
</table>
Solution
First of all, you need to have a CSS class that will remove shadow from the td
and th
elements.
.noShadow {
box-shadow: none !important;
}
Now, there are 2 ways you can do this via javscript:
- Intersection Observer API
- Scroll event
How to do this via Intersection Observer API?
You need to observe 2 td
elements via two instances of InterSectionObserver
, one instance to observe one td
elemenet, and execute the code, that will remove or add noShadow
CSS class from the table row or column, when the observed td
elements come in to view.
I have added observed-column
class on one of the td
element that will be observed for adding or removing shadow from last column and added observed-row
class on one of the td
element to add or remove shadow from the header.
Here's the javascript code you need
const lastColumn = document.querySelectorAll(".fixed-right");
const header = document.querySelectorAll(".fixed-top");
const tableContainer = document.querySelector("main");
let rowHasShadow = false;
let columnHasShadow = true;
const options = {
root: tableContainer,
rootMargin: "0px",
threshold: 1
};
const rowObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
// '.observed-row' element is in view, remove shadow from header
rowHasShadow = false;
header.forEach(th => {
if (th.classList.contains('fixed-header') && !columnHasShadow) {
th.classList.add("noShadow")
} else if (!th.classList.contains('fixed-header')) {
th.classList.add("noShadow")
}
});
}
else {
// '.observed-row' element is not in view, add shadow to header
rowHasShadow = true;
header.forEach(th => th.classList.remove("noShadow"));
}
}, options);
rowObserver.observe(document.querySelector(".observed-row"));
const columnObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
// '.observed-column' element is in view, remove shadow from last column
columnHasShadow = false;
lastColumn.forEach(th => {
if (th.classList.contains('fixed-header') && !rowHasShadow) {
th.classList.add("noShadow");
} else if (!th.classList.contains('fixed-header')) {
th.classList.add("noShadow");
}
});
}
else {
// '.observed-column' element is not in view, add shadow to last column
columnHasShadow = true;
lastColumn.forEach(th => th.classList.remove("noShadow"));
}
}, options);
columnObserver.observe(document.querySelector(".observed-column"));
Demo
const lastColumn = document.querySelectorAll(".fixed-right");
const header = document.querySelectorAll(".fixed-top");
const tableContainer = document.querySelector("main");
let rowHasShadow = false;
let columnHasShadow = true;
const options = {
root: tableContainer,
rootMargin: "0px",
threshold: 1
};
const rowObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
// '.observed-row' element is in view, remove shadow from header
rowHasShadow = false;
header.forEach(th => {
if (th.classList.contains('fixed-header') && !columnHasShadow) {
th.classList.add("noShadow")
} else if (!th.classList.contains('fixed-header')) {
th.classList.add("noShadow")
}
});
}
else {
// '.observed-row' element is not in view, add shadow to header
rowHasShadow = true;
header.forEach(th => th.classList.remove("noShadow"));
}
}, options);
rowObserver.observe(document.querySelector(".observed-row"));
const columnObserver = new IntersectionObserver((entries, observer) => {
if (entries[0].isIntersecting) {
// '.observed-column' element is in view, remove shadow from last column
columnHasShadow = false;
lastColumn.forEach(th => {
if (th.classList.contains('fixed-header') && !rowHasShadow) {
th.classList.add("noShadow");
} else if (!th.classList.contains('fixed-header')) {
th.classList.add("noShadow");
}
});
}
else {
// '.observed-column' element is not in view, add shadow to last column
columnHasShadow = true;
lastColumn.forEach(th => th.classList.remove("noShadow"));
}
}, options);
columnObserver.observe(document.querySelector(".observed-column"));
main {
display: flex;
max-height: 20rem;
overflow: auto;
}
table {
border-spacing: 0;
}
th {
text-align: left;
}
th,
td {
border-bottom: 1px solid #c6c6c6;
height: 5rem;
white-space: nowrap;
padding-right: 2rem;
}
.fixed {
background-color: white;
position: sticky;
}
.fixed-top {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2);
top: 0;
z-index: 2;
}
.fixed-right {
border-left: 1px solid #c6c6c6;
box-shadow: -5px 0px 5px 0px rgba(0, 0, 0, 0.2);
padding-left: 1rem;
right: 0;
z-index: 1;
}
.fixed-top.fixed-right {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2),
-5px 0px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 2;
}
.noShadow {
box-shadow: none !important;
}
<main>
<table>
<thead>
<tr>
<th class="fixed fixed-top">Animal</th>
<th class="fixed fixed-top">Age</th>
<th class="fixed fixed-top">Country</th>
<th class="fixed fixed-top">Sentence</th>
<th class="fixed fixed-top observed-column">Color</th>
<th class="fixed fixed-top fixed-right fixed-header">Programming Language</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sable</td>
<td>15 yo</td>
<td>Japan</td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</td>
<td>Purple</td>
<td class="fixed fixed-right observed-row">Kotlin</td>
</tr>
<tr>
<td>Toco toucan</td>
<td>35 yo</td>
<td>Brazil</td>
<td>
Sed tortor erat, imperdiet a enim quis, placerat rhoncus nisl.
</td>
<td>Orange</td>
<td class="fixed fixed-right">Swift</td>
</tr>
<tr>
<td>Bull</td>
<td>42 yo</td>
<td>Spain</td>
<td>Donec vitae risus urna.</td>
<td>Red</td>
<td class="fixed fixed-right">JavaScript</td>
</tr>
<tr>
<td>Brown bear</td>
<td>17 yo</td>
<td>Russia</td>
<td>Proin gravida et velit ut congue.</td>
<td>Green</td>
<td class="fixed fixed-right">Python</td>
</tr>
</tbody>
</table>
</main>
You can also view this demo on codesandbox
How to do this via Scroll event?
Add a scroll event listener on main
element. Inside the event handler, you need to check 2 things:
if
scrollLeft
is equal toscrollWidth - clientWidth
, add the.noShadow
class on all thetd
elements in last columnif
scrollTop
is equal to zero, add.noShadow
class on allth
elements
Following is the javascript code you need along with the noShadow
css class
const lastColumn = document.querySelectorAll('.fixed-right');
const header = document.querySelectorAll('.fixed-top');
const tableContainer = document.querySelector('main');
tableContainer.addEventListener('scroll', (e) => {
removeShadowFromColumn();
removeShadowFromHeader();
});
function removeShadowFromHeader() {
if (tableContainer.scrollTop === 0) {
header.forEach(th => th.classList.add('noShadow'));
} else {
header.forEach(th => th.classList.remove('noShadow'));
}
}
function removeShadowFromColumn() {
const widthDiff = tableContainer.scrollWidth - tableContainer.clientWidth;
if (tableContainer.scrollLeft === widthDiff) {
lastColumn.forEach(th => th.classList.add('noShadow'));
} else {
lastColumn.forEach(td => td.classList.remove('noShadow'));
}
}
removeShadowFromHeader();
Demo
const lastColumn = document.querySelectorAll('.fixed-right');
const header = document.querySelectorAll('.fixed-top');
const tableContainer = document.querySelector('main');
tableContainer.addEventListener('scroll', (e) => {
removeShadowFromColumn();
removeShadowFromHeader();
});
function removeShadowFromHeader() {
if (tableContainer.scrollTop === 0) {
header.forEach(th => th.classList.add('noShadow'));
} else {
header.forEach(th => th.classList.remove('noShadow'));
}
}
function removeShadowFromColumn() {
const widthDiff = tableContainer.scrollWidth - tableContainer.clientWidth;
if (tableContainer.scrollLeft === widthDiff) {
lastColumn.forEach(th => th.classList.add('noShadow'));
} else {
lastColumn.forEach(td => td.classList.remove('noShadow'));
}
}
removeShadowFromHeader();
main {
display: flex;
max-height: 20rem;
overflow: auto;
}
table {
border-spacing: 0;
}
th {
text-align: left;
}
th,
td {
border-bottom: 1px solid #c6c6c6;
height: 5rem;
white-space: nowrap;
padding-right: 2rem;
}
.fixed {
background-color: white;
position: sticky;
}
.fixed-top {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2);
top: 0;
z-index: 1;
}
.fixed-right {
border-left: 1px solid #c6c6c6;
box-shadow: -5px 0px 5px 0px rgba(0, 0, 0, 0.2);
padding-left: 1rem;
right: 0;
z-index: 1;
}
.fixed-top.fixed-right {
box-shadow: 4px 3px 5px 0px rgba(0, 0, 0, 0.2), -5px 0px 5px 0px rgba(0, 0, 0, 0.2);
z-index: 2;
}
.noShadow {
box-shadow: none !important;
}
<main>
<table>
<thead>
<tr>
<th class="fixed fixed-top">Animal</th>
<th class="fixed fixed-top">Age</th>
<th class="fixed fixed-top">Country</th>
<th class="fixed fixed-top">Sentence</th>
<th class="fixed fixed-top">Color</th>
<th class="fixed fixed-top fixed-right">Programming Language</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sable</td>
<td>15 yo</td>
<td>Japan</td>
<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</td>
<td>Purple</td>
<td class="fixed fixed-right">Kotlin</td>
</tr>
<tr>
<td>Toco toucan</td>
<td>35 yo</td>
<td>Brazil</td>
<td>
Sed tortor erat, imperdiet a enim quis, placerat rhoncus nisl.
</td>
<td>Orange</td>
<td class="fixed fixed-right">Swift</td>
</tr>
<tr>
<td>Bull</td>
<td>42 yo</td>
<td>Spain</td>
<td>Donec vitae risus urna.</td>
<td>Red</td>
<td class="fixed fixed-right">JavaScript</td>
</tr>
<tr>
<td>Brown bear</td>
<td>17 yo</td>
<td>Russia</td>
<td>Proin gravida et velit ut congue.</td>
<td>Green</td>
<td class="fixed fixed-right">Python</td>
</tr>
</tbody>
</table>
</main>
You can also view the demo on codesandbox
P.S. For performance reasons, i suggest you do this via first approach that uses Intersection Observer API.
Answered By - Yousaf
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.