可捲動性示能提示

網頁功能: 容器捲動狀態查詢

總覽

陰影或漸層等視覺提示,可讓使用者知道可以捲動查看更多內容。本指南說明如何使用 CSS container-scroll-state-queries 建構這些提示,根據容器的可捲動狀態設定元素樣式,而不必依賴 JavaScript 捲動事件監聽器或觀察器。

導入作業

1. 建立捲動容器

捲動容器必須宣告為捲動狀態查詢容器。

.scroller {
  overflow-y: auto;
  /* Establish this element as a scroll-state query container */
  container-type: scroll-state;
  position: relative;
}

2. 設定指標樣式

將指標元素 (例如陰影、漸層或箭頭) 放入容器中,並設定樣式。根據預設,這些控制項不會顯示。顯示時,應設定 pointer-events: none,使其不具互動性。

.indicator-top, .indicator-bottom {
  position: sticky;
  left: 0;
  right: 0;
  height: 20px;
  opacity: 0;
  transition: opacity 0.2s;
  pointer-events: none; /* Let clicks pass through */
}

.indicator-top {
  top: 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}

.indicator-bottom {
  bottom: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}

3. 查詢捲動狀態

搭配 scroll-state 函式使用 @container 規則。檢查容器是否可向上或向下捲動,以顯示對應的指標。

/* Show top indicator when the user can scroll up */
@container scroll-state(scrollable: top) {
  .indicator-top {
    opacity: 1;
  }
}

/* Show bottom indicator when the user can scroll down */
@container scroll-state(scrollable: bottom) {
  .indicator-bottom {
    opacity: 1;
  }
}

備用策略

基本備用

如果裝置不支援這項功能,就不會顯示指標。由於這些是提示,並非功能的重要部分,因此在不支援的瀏覽器中省略這些提示是可以接受的。

進階備用廣告 (Intersection Observer)

如果需要提示,請在捲動器頂端和底部的標記元素移入和移出捲動區塊時,使用 IntersectionObserver 切換類別。

<!-- Sentinel elements placed at the ends of the scroller -->
<div class="sentinel-top"></div>
<!-- Content goes here -->
<div class="sentinel-bottom"></div>
/* Marker styling to ensure it does not affect layout */
.sentinel-top, .sentinel-bottom {
  height: 0;
  width: 0;
  visibility: hidden;
}

.scroller.scrolled-down .indicator-top {
  opacity: 1;
}

.scroller.can-scroll-down .indicator-bottom {
  opacity: 1;
}
if (!CSS.supports('container-type', 'scroll-state')) {
  const topSentinel = document.querySelector('.sentinel-top');
  const bottomSentinel = document.querySelector('.sentinel-bottom');
  const scroller = document.querySelector('.scroller');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.target === topSentinel) {
        // If top sentinel is not intersecting, we have scrolled down
        scroller.classList.toggle('scrolled-down', !entry.isIntersecting);
      }
      if (entry.target === bottomSentinel) {
        // If bottom sentinel is intersecting, we reached the bottom
        scroller.classList.toggle('can-scroll-down', !entry.isIntersecting);
      }
    });
  }, { root: scroller });

  observer.observe(topSentinel);
  observer.observe(bottomSentinel);
}