如何在滚动时使用“位置:粘性”在 td / th 中添加或删除阴影?

Raf*_*res 6 html javascript css html-table

我制作了一个带有标题和最后一列的表格,使用position: sticky. 我想当滚动条水平位于末尾时删除最后一列的阴影,并当滚动条垂直位于开头时删除标题中的阴影。在 AntDesign 中有一个所需结果的示例,但在这种情况下,表格仅在固定列中有阴影,而在标题中没有。

虽然由于性能原因我不想要使用滚动事件侦听器的解决方案(请参阅滚动链接效果),但如果有人以这种方式解决它,他们可以分享它以供参考并帮助其他人。

该代码也可从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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

You*_*saf 6

首先,您需要有一个 CSS 类来移除tdth元素的阴影。

.noShadow {
  box-shadow: none !important;
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以通过 javscript 通过两种方式执行此操作:

  1. 路口观察器 API
  2. 滚动事件

如何通过 Intersection Observer API 做到这一点?

您需要td通过 的两个实例观察两个元素InterSectionObserver:每个实例观察一个td元素。当观察到的td元素进入视图时,我们想要将noShadowCSS 类添加到表格行或列。类似地,当观察到的元素移出视图noShadow时,类将被删除。td

observed-column我在其中一个元素上添加了类td,将在最后一列中添加或删除阴影,并observed-row在其中一个td元素上添加类以添加或删除标题中的阴影。

这是您需要的 JavaScript 代码

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"));
Run Code Online (Sandbox Code Playgroud)

演示

.noShadow {
  box-shadow: none !important;
}
Run Code Online (Sandbox Code Playgroud)
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"));
Run Code Online (Sandbox Code Playgroud)
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"));
Run Code Online (Sandbox Code Playgroud)

您还可以在codesandbox上查看此演示

编辑具有固定列和标题的表格

如何通过滚动事件来做到这一点?

在元素上添加滚动事件侦听器main。在事件处理程序中,您需要检查两件事:

  1. 如果scrollLeft等于scrollWidth - clientWidth,则为最后一列中的.noShadow所有元素添加类td

  2. 如果scrollTop等于零,.noShadow则为所有th元素添加类

以下是您需要的 javascript 代码以及noShadowcss 类

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();
Run Code Online (Sandbox Code Playgroud)

演示

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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)
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();
Run Code Online (Sandbox Code Playgroud)

您还可以在codesandbox上查看演示

编辑具有固定列和标题的表格

PS出于性能原因,我建议您通过使用Intersection Observer API 的第一种方法来执行此操作。