最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I add or remove shadows from a tdth with "position: sticky" on scroll? - Stack Overflow

programmeradmin2浏览0评论

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>

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>

Share Improve this question edited Jun 17, 2020 at 11:11 Rafael Tavares asked Jun 8, 2020 at 11:41 Rafael TavaresRafael Tavares 6,4814 gold badges37 silver badges56 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 7

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:

  1. Intersection Observer API
  2. 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 e 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:

  1. if scrollLeft is equal to scrollWidth - clientWidth, add the .noShadow class on all the td elements in last column

  2. 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.

发布评论

评论列表(0)

  1. 暂无评论