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 two td elements via two instances of InterSectionObserver: one instance to observe one td element each. When the observed td elements come into view, we want to add noShadow CSS class to the table row or column. Similarly, noShadow class will be removed when observed td elements move out of the view.
I have added observed-column class on one of the td elements that will be observed for adding or removing shadow from last column and added observed-row class on one of the td elements 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 to scrollWidth - clientWidth, add the .noShadow class on all the td elements in last column
if scrollTop is equal to zero, add .noShadow class on all th 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.