Using container scroll-state queries
Container scroll-state queries là một loại container query. Thay vì áp dụng có chọn lọc các style cho các phần tử descendant dựa trên kích thước của container, scroll-state queries cho phép bạn áp dụng có chọn lọc các style cho các phần tử descendant dựa trên trạng thái cuộn của container. Điều này có thể bao gồm việc container có đang được cuộn một phần, được snap vào ancestor scroll snap container, hoặc được định vị qua position: sticky và đang bám vào ranh giới của ancestor scroll container.
Bài viết này giải thích cách sử dụng container scroll-state queries, đi qua ví dụ của từng loại. Nó giả định rằng bạn biết cơ bản về container queries. Nếu bạn chưa quen với container queries, hãy đọc CSS container queries trước khi tiếp tục.
Các loại container scroll-state query
Có ba descriptor @container bạn có thể dùng trong truy vấn scroll-state():
scrollable: Truy vấn xem container có thể được cuộn theo hướng đã cho qua cuộn do người dùng khởi tạo (ví dụ bằng cách kéo thanh cuộn hoặc sử dụng cử chỉ trackpad). Nói cách khác, có nội dung tràn theo hướng đã cho có thể được cuộn đến không? Điều này hữu ích để áp dụng style liên quan đến vị trí cuộn của scroll container. Ví dụ, bạn có thể hiển thị gợi ý khuyến khích mọi người cuộn xuống và xem thêm nội dung khi thanh cuộn đang ở trên cùng, và ẩn nó khi người dùng đã thực sự bắt đầu cuộn.scrolled: Truy vấn xem container được cuộn gần đây nhất theo hướng đã cho. Điều này cho phép bạn áp dụng có chọn lọc các style dựa trên hướng cuộn của người dùng, ví dụ thanh menu trên cùng chỉ hiển thị khi người dùng đang cuộn lên.snapped: Truy vấn xem container có sắp được snap vào ancestor scroll snap container dọc theo trục đã cho không. Điều này hữu ích để áp dụng style khi một phần tử được snap vào scroll snap container. Ví dụ, bạn có thể muốn làm nổi bật một phần tử được snap theo một cách nào đó, hoặc tiết lộ một số nội dung của nó trước đây bị ẩn.stuck: Truy vấn xem container có giá trịpositionlàstickycó đang bám vào một cạnh của ancestor scroll container không. Điều này hữu ích để tạo style cho các phần tửposition: stickykhác nhau khi bị bám — ví dụ, bạn có thể đặt cho chúng bảng màu hoặc layout khác.
Tổng quan cú pháp
Để thiết lập container element như là scroll-state query container, đặt thuộc tính container-type trên nó với giá trị scroll-state. Bạn cũng có thể tùy chọn đặt container-name cho nó, để bạn có thể nhắm mục tiêu bằng container query cụ thể:
.container {
container-type: scroll-state;
container-name: my-container;
}
Sau đó bạn có thể tạo block @container chỉ định truy vấn, các quy tắc được áp dụng cho children của container nếu kiểm tra vượt qua, và tùy chọn, container-name của container bạn muốn truy vấn. Nếu bạn không chỉ định container-name, container query sẽ được áp dụng cho tất cả scroll-state query containers trên trang.
Ở đây, chúng ta chỉ truy vấn các containers có tên my-container để xác định xem container có thể được cuộn về phía cạnh trên cùng của nó không:
@container my-container scroll-state(scrollable: top) {
/* CSS rules go here */
}
Note:
Để tách scroll-state queries khỏi các container queries khác, các scroll-state descriptor và giá trị được đặt trong ngoặc đơn, đứng trước bởi scroll-state (scroll-state( ... )). Các cấu trúc này trông như hàm, nhưng chúng không phải.
Sử dụng truy vấn scrollable
Truy vấn scroll-state scrollable, được viết là scroll-state(scrollable: <keyword>), kiểm tra xem ancestor cuộn của container có thể được cuộn theo hướng đã cho qua cuộn do người dùng khởi tạo không. Nếu không, truy vấn trả về false.
Giá trị keyword cho biết hướng bạn đang kiểm tra khả năng cuộn, ví dụ:
top: Kiểm tra xem container có thể được cuộn về phía cạnh trên cùng của nó không.inline-end: Kiểm tra xem container có thể được cuộn về phía cạnh inline-end của nó không.y: Kiểm tra xem container có thể được cuộn theo một hoặc cả hai hướng dọc theo trục y của nó không.
Nếu kiểm tra vượt qua, các quy tắc bên trong block @container được áp dụng cho các descendant của scroll container phù hợp.
Hãy xem ví dụ trong đó chúng ta có scroll container đầy nội dung, và một liên kết nhỏ tiện dụng để cuộn trở lại đầu trang nếu muốn. Chúng ta sẽ dùng truy vấn scrollable để chỉ hiển thị liên kết khi người dùng đã bắt đầu cuộn xuống nội dung.
HTML
Trong HTML, chúng ta có phần tử <article> chứa đủ nội dung để tài liệu cuộn, đứng trước bởi liên kết back-to-top:
<a class="back-to-top" href="#" aria-label="Top of page">↑</a>
<article>
<h1>Reader with container query-controlled "back-to-top" link</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
...
</section>
...
</article>
Chúng ta đã ẩn hầu hết HTML để ngắn gọn.
CSS
Liên kết .back-to-top được đặt giá trị position là fixed, đặt ở góc dưới bên phải của viewport, và di chuyển ra ngoài viewport bằng giá trị translate là 80px 0. Giá trị transition sẽ tạo hiệu ứng chuyển động cho translate và background-color khi một trong hai giá trị thay đổi.
.back-to-top {
width: 64px;
height: 64px;
color: white;
text-align: center;
position: fixed;
bottom: 10px;
right: 10px;
translate: 80px 0;
transition:
0.4s translate,
0.2s background-color;
}
scroll container trong ví dụ này là chính phần tử <html>, được ký hiệu là scroll-state query container với giá trị container-type là scroll-state. container-name không thực sự cần thiết nhưng hữu ích trong các trường hợp code được thêm vào codebase với nhiều scroll-state query containers được nhắm mục tiêu bằng các truy vấn khác nhau.
html {
container-type: scroll-state;
container-name: scroller;
}
Tiếp theo, chúng ta định nghĩa block @container đặt tên container được nhắm mục tiêu bởi truy vấn này, và bản thân truy vấn — scrollable: top. Truy vấn này áp dụng các quy tắc trong block chỉ nếu phần tử <html> có thể được cuộn về phía cạnh trên cùng của nó — nói cách khác, nếu container trước đây đã được cuộn xuống. Nếu đó là trường hợp, translate: 0 0 được áp dụng cho liên kết .back-to-top, chuyển đổi nó trở lại màn hình.
@container scroller scroll-state(scrollable: top) {
.back-to-top {
translate: 0 0;
}
}
Chúng ta đã ẩn phần còn lại của CSS ví dụ để ngắn gọn.
Kết quả
Thử cuộn tài liệu xuống, và chú ý cách liên kết "back-to-top" xuất hiện kết quả, chuyển động mượt mà từ phía bên phải của viewport do transition. Nếu bạn cuộn trở lại đầu bằng cách kích hoạt liên kết hoặc cuộn thủ công, liên kết "back-to-top" chuyển đổi ra ngoài màn hình.
Sử dụng truy vấn scrolled
Truy vấn scroll-state scrolled, được viết là scroll-state(scrolled: <keyword>), kiểm tra xem ancestor cuộn của container được cuộn gần đây nhất theo hướng đã cho không. Nếu không, truy vấn trả về false.
Giá trị keyword cho biết hướng bạn đang kiểm tra. Ví dụ:
block-start: Kiểm tra xem container được cuộn gần đây nhất về phía cạnh block-start của nó không.right: Kiểm tra xem container được cuộn gần đây nhất về phía cạnh bên phải của nó không.y: Kiểm tra xem container được cuộn lên hoặc xuống dọc theo trục y không.none: Kiểm tra xem container không phải là scroll container hoặc chưa được cuộn theo bất kỳ hướng nào kể từ khi render không.
Nếu kiểm tra trả về true, các quy tắc bên trong block @container được áp dụng cho các descendant của scroll container phù hợp.
Hãy xem ví dụ về scroll container với truy vấn scrolled hiển thị các "bar" nội dung trên và dưới chỉ khi người dùng đang cuộn lên hoặc xuống tương ứng.
HTML
Trong HTML, chúng ta có phần tử <article> chứa đủ nội dung để tài liệu cuộn, đứng trước bởi hai phần tử <div> đại diện cho các "bar" trên và dưới:
<div class="bar" id="top-bar">You're currently scrolling towards the top.</div>
<div class="bar" id="bottom-bar">
You're currently scrolling towards the bottom.
</div>
<article>
<h1>Document with scrolled container query</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
...
</section>
...
</article>
Chúng ta đã ẩn hầu hết HTML để ngắn gọn.
CSS
Các "bar" được đặt style cơ bản. Quan trọng nhất, chúng được đặt giá trị position là fixed, được offset từ hai phía bằng các giá trị left và right.
.bar {
border-radius: 10px;
border: 1px solid #000;
background-color: #0009;
padding: 10px;
color: white;
text-shadow: 1px 1px 1px black;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 5px;
right: 5px;
}
Tiếp theo, chúng ta đặt các giá trị độ dài top và bottom âm cho bar trên và dưới để chúng bị ẩn phía trên và phía dưới viewport theo mặc định. Chúng ta thêm transition để tạo hiệu ứng chuyển động mượt mà cho chúng vào view khi giá trị translate của chúng thay đổi.
#top-bar {
top: -50px;
transition: 0.6s translate;
}
#bottom-bar {
bottom: -50px;
transition: 0.6s translate;
}
scroll container trong ví dụ này là chính phần tử <html>, được ký hiệu là scroll-state query container với giá trị container-type là scroll-state. container-name không thực sự cần thiết, nhưng hữu ích khi codebase có nhiều scroll-state query containers được nhắm mục tiêu bằng các truy vấn khác nhau.
html {
container-type: scroll-state;
container-name: scroller;
}
Tiếp theo, chúng ta định nghĩa hai block @container, cả hai đều nhắm mục tiêu tên container scroller. Block đầu tiên định nghĩa truy vấn scrolled: block-end và block thứ hai định nghĩa truy vấn scrolled: block-start. Lần lượt, các truy vấn này áp dụng các quy tắc trong block của chúng chỉ nếu phần tử <html> được cuộn gần đây nhất về phía cạnh block-end hoặc block-start của nó. Nói cách khác, khi container được cuộn xuống hoặc lên. Khi một trong hai điều kiện trở thành true, bar được tham chiếu bên trong block có giá trị translate được đặt để khiến nó chuyển đổi vào màn hình. Bar được tham chiếu trong @condition không còn đúng nữa chuyển đổi ra ngoài màn hình.
@container scroller scroll-state(scrolled: block-start) {
#top-bar {
translate: 0 55px;
}
}
@container scroller scroll-state(scrolled: block-end) {
#bottom-bar {
translate: 0 -55px;
}
}
Chúng ta đã ẩn phần còn lại của CSS ví dụ để ngắn gọn.
Kết quả
Thử cuộn tài liệu lên và xuống, và chú ý cách các bar khác nhau xuất hiện kết quả, chuyển động mượt mà vào và ra khỏi màn hình.
Sử dụng truy vấn snapped
Chỉ liên quan khi scroll snapping được triển khai, truy vấn scroll-state snapped, được viết là scroll-state(snapped: <keyword>), kiểm tra xem container có sắp được snap vào ancestor scroll snap container dọc theo trục đã cho không. Nếu không, truy vấn trả về false.
Giá trị keyword trong trường hợp này cho biết hướng bạn đang kiểm tra khả năng snap của phần tử, ví dụ:
x: Kiểm tra xem container có snap theo chiều ngang vào ancestor scroll-snap container của nó không.inline: Kiểm tra xem container có snap vào ancestor scroll-snap container của nó theo hướng inline không.y: Kiểm tra xem container có snap vào ancestor scroll-snap container của nó theo cả hai hướng không.
Để đánh giá container với truy vấn snapped scroll-state không phải none, nó phải là container có ancestor scroll-snap container, tức là ancestor có giá trị scroll-snap-type khác none. Container query scroll-state(snapped: none) khớp các scroll-state containers không có ancestor scroll container.
Việc đánh giá sẽ xảy ra khi sự kiện scrollsnapchanging kích hoạt trên scroll snap container.
Nếu kiểm tra vượt qua, các quy tắc bên trong block @container được áp dụng cho các descendant của scroll snap target container phù hợp.
Trong ví dụ này, chúng ta sẽ xem xét scroll snap container với các children snap vào nó theo chiều dọc và dùng truy vấn snapped để tạo style cho các children chỉ khi chúng được snap hoặc sắp được snap.
HTML
HTML bao gồm phần tử <main> sẽ là scroll snap container. Bên trong là nhiều phần tử <section> sẽ là snap targets. Mỗi <section> chứa wrapper <div> và <h2> heading. Các wrapper được bao gồm để tạo style target vì container queries cho phép tạo style cho các descendant của container, không phải bản thân container.
<main>
<section>
<div class="wrapper">
<h2>Section 1</h2>
</div>
</section>
...
</main>
Chúng ta đã ẩn hầu hết HTML để ngắn gọn.
CSS
Chúng ta đặt giá trị overflow là scroll và height cố định trên phần tử <main> để biến nó thành scroll container dọc. Chúng ta cũng đặt giá trị scroll-snap-type là y mandatory để biến <main> thành scroll snap container mà snap targets sẽ snap vào dọc theo trục y; mandatory có nghĩa là snap target luôn được snap vào.
main {
overflow: scroll;
scroll-snap-type: y mandatory;
height: 450px;
width: 250px;
border: 3px solid black;
}
Các phần tử <section> được chỉ định là snap targets bằng cách đặt giá trị scroll-snap-align không phải none. Giá trị center có nghĩa là chúng sẽ snap vào container tại điểm trung tâm của chúng.
section {
font-family: "Helvetica", "Arial", sans-serif;
width: 150px;
height: 150px;
margin: 50px auto;
scroll-snap-align: center;
}
Chúng ta muốn cho phép các phần tử <section> được truy vấn. Cụ thể, chúng ta muốn kiểm tra xem các phần tử <section> có đang trong quá trình snap vào container của chúng không, vì vậy chúng ta ký hiệu chúng là scroll-state query containers bằng cách đặt giá trị container-type là scroll-state trên chúng. Chúng ta cũng đặt cho chúng container-name, không thực sự cần thiết, nhưng sẽ hữu ích nếu code của chúng ta trở nên phức tạp hơn sau này và chúng ta có nhiều scroll-state query containers mà chúng ta muốn nhắm mục tiêu bằng các truy vấn khác nhau.
section {
container-type: scroll-state;
container-name: snap-container;
}
Tiếp theo, chúng ta định nghĩa block @container đặt tên container chúng ta đang nhắm mục tiêu với truy vấn này, và bản thân truy vấn — snapped: y. Truy vấn này áp dụng các quy tắc trong block chỉ nếu phần tử <section> đang được snap theo chiều dọc vào container của nó. Nếu đó là trường hợp, chúng ta áp dụng background và color mới cho <div> .wrapper child của phần tử <section> để làm nổi bật nó.
@container snap-container scroll-state(snapped: y) {
.wrapper {
background: purple;
color: white;
}
}
Kết quả
Kết quả được render dưới đây. Thử cuộn container lên và xuống, và chú ý cách style <section> thay đổi khi nó được snap vào container.
Sử dụng truy vấn stuck
Truy vấn scroll-state stuck, được viết là scroll-state(stuck: <keyword>), kiểm tra xem container có giá trị position là sticky có đang bám vào một cạnh của ancestor scroll container không. Nếu không, truy vấn trả về false.
Giá trị keyword trong trường hợp này cho biết cạnh scroll container bạn đang kiểm tra, ví dụ:
top: Kiểm tra xem container có đang bám vào cạnh trên của ancestor scroll container của nó không.block-end: Kiểm tra xem container có đang bám vào cạnh block-end của ancestor scroll container của nó không.none: Kiểm tra xem container có không bám vào bất kỳ cạnh nào của ancestor scroll container của nó không. Lưu ý rằng truy vấnnonesẽ khớp ngay cả khi container không cóposition: stickyđược đặt trên nó.
Nếu truy vấn trả về true, các quy tắc bên trong block @container được áp dụng cho các descendant của container position: sticky phù hợp.
Hãy xem ví dụ trong đó chúng ta có scroll container với nội dung tràn, trong đó các tiêu đề được đặt thành position: sticky và bám vào cạnh trên của container khi chúng cuộn đến vị trí đó. Chúng ta sẽ dùng truy vấn scroll-state stuck để tạo style khác nhau cho các tiêu đề khi chúng bám vào cạnh trên.
HTML
Trong HTML, chúng ta có phần tử <article> chứa đủ nội dung để tài liệu cuộn. Nó được cấu trúc bằng nhiều phần tử <section>, mỗi phần tử chứa <header> với nội dung lồng nhau:
<article>
<h1>Sticky reader with scroll-state container query</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
...
</section>
<section>
<header>
<h2>This one, not so much</h2>
<p>Confecta res esset.</p>
</header>
...
</section>
...
</article>
Chúng ta đã ẩn hầu hết HTML để ngắn gọn.
CSS
Mỗi <header> có giá trị position là sticky và giá trị top là 0, bám chúng vào cạnh trên của scroll container. Để kiểm tra xem các phần tử <header> có đang bám vào cạnh trên container không, chúng được ký hiệu là scroll-state query containers với giá trị container-type là scroll-state. container-name không thực sự cần thiết nhưng sẽ hữu ích nếu code này được thêm vào codebase với nhiều scroll-state query containers được nhắm mục tiêu bằng các truy vấn khác nhau.
header {
background: white;
position: sticky;
top: 0;
container-type: scroll-state;
container-name: sticky-heading;
}
Chúng ta cũng đặt style cơ bản cho các phần tử <h2> và <p> bên trong các phần tử <header>, và giá trị transition để chúng sẽ chuyển động mượt mà khi giá trị background của chúng thay đổi.
h2,
header p {
margin: 0;
transition: 0.4s background;
}
h2 {
padding: 20px 5px;
margin-bottom: 10px;
}
header p {
font-style: italic;
padding: 10px 5px;
}
Tiếp theo, chúng ta định nghĩa block @container đặt tên container chúng ta đang nhắm mục tiêu với truy vấn này, và bản thân truy vấn — stuck: top. Truy vấn này áp dụng các quy tắc trong block chỉ nếu phần tử <header> đang bám vào đầu scroll container của nó. Khi đó là trường hợp, background khác và box-shadow được áp dụng cho <h2> và <p> bên trong.
@container sticky-heading scroll-state(stuck: top) {
h2,
p {
background: #cccccc;
box-shadow: 0 5px 2px #00000077;
}
}
Chúng ta đã ẩn phần còn lại của CSS để ngắn gọn.
Kết quả
Thử cuộn tài liệu xuống và lên, và chú ý cách các phần tử <h2> và <p> chuyển đổi sang bảng màu mới khi chúng bám vào cạnh trên của container.