Thông tin tổng quan cơ bản về cách tạo một thành phần thẻ tương tự như thành phần trong ứng dụng iOS và ứng dụng Android.
Trong bài đăng này, tôi muốn chia sẻ suy nghĩ về việc xây dựng một thành phần Thẻ cho web có tính thích ứng, hỗ trợ nhiều phương thức nhập của thiết bị và hoạt động trên nhiều trình duyệt. Dùng thử bản minh hoạ.
Nếu bạn thích xem video, thì đây là phiên bản YouTube của bài đăng này:
Tổng quan
Thẻ là một thành phần phổ biến của hệ thống thiết kế nhưng có thể có nhiều hình dạng và hình thức. Đầu tiên là các thẻ trên máy tính được xây dựng trên phần tử <frame>, và giờ đây chúng ta có các thành phần mượt mà trên thiết bị di động giúp tạo ảnh động cho nội dung dựa trên các thuộc tính vật lý.
Tất cả đều cố gắng làm một việc: tiết kiệm không gian.
Ngày nay, các yếu tố thiết yếu của trải nghiệm người dùng theo thẻ là một vùng điều hướng nút giúp bật/tắt chế độ hiển thị nội dung trong khung hiển thị. Nhiều vùng nội dung khác nhau dùng chung một không gian, nhưng được trình bày có điều kiện dựa trên nút được chọn trong thành phần điều hướng.
Thủ đoạn trên web
Nhìn chung, tôi thấy thành phần này khá dễ xây dựng nhờ một vài tính năng quan trọng của nền tảng web:
scroll-snap-pointsđể vuốt và tương tác bằng bàn phím một cách mượt mà với các vị trí dừng cuộn phù hợp- Đường liên kết sâu thông qua hàm băm URL để hỗ trợ việc chia sẻ và neo cuộn trong trang do trình duyệt xử lý
- Hỗ trợ trình đọc màn hình bằng mã đánh dấu phần tử
<a>vàid="#hash" prefers-reduced-motionđể bật hiệu ứng chuyển đổi mờ dần và cuộn trang tức thì- Tính năng web
@scroll-timelineđang trong giai đoạn dự thảo để gạch dưới và thay đổi màu sắc của thẻ đã chọn một cách linh động
HTML
Về cơ bản, trải nghiệm người dùng ở đây là: nhấp vào một đường liên kết, để URL đại diện cho trạng thái trang lồng nhau, sau đó xem nội dung cập nhật khi trình duyệt di chuyển đến phần tử phù hợp.
Có một số thành phần nội dung cấu trúc trong đó: đường liên kết và :target. Chúng ta cần một danh sách các đường liên kết (<nav> là lựa chọn phù hợp) và một danh sách các phần tử <article> (<section> là lựa chọn phù hợp). Mỗi hàm băm liên kết sẽ khớp với một phần,
cho phép trình duyệt cuộn nội dung thông qua tính năng neo.
Ví dụ: khi nhấp vào một đường liên kết, bài viết :target sẽ tự động được lấy tiêu điểm trong Chrome 89 mà không cần JS. Sau đó, người dùng có thể cuộn nội dung bài viết bằng thiết bị đầu vào như bình thường. Đây là nội dung bổ sung, như được chỉ định trong mã đánh dấu.
Tôi đã sử dụng mã đánh dấu sau để sắp xếp các thẻ:
<snap-tabs>
<header>
<nav>
<a></a>
<a></a>
<a></a>
<a></a>
</nav>
</header>
<section>
<article></article>
<article></article>
<article></article>
<article></article>
</section>
</snap-tabs>
Tôi có thể thiết lập mối liên kết giữa các phần tử <a> và <article> bằng các thuộc tính href và id như sau:
<snap-tabs>
<header>
<nav>
<a href="#responsive"></a>
<a href="#accessible"></a>
<a href="#overscroll"></a>
<a href="#more"></a>
</nav>
</header>
<section>
<article id="responsive"></article>
<article id="accessible"></article>
<article id="overscroll"></article>
<article id="more"></article>
</section>
</snap-tabs>
Tiếp theo, tôi điền vào các bài viết một lượng lorem hỗn hợp và các đường liên kết có độ dài hỗn hợp và tập hợp tiêu đề hình ảnh. Khi đã có nội dung, chúng ta có thể bắt đầu bố cục.
Bố cục cuộn
Có 3 loại vùng cuộn khác nhau trong thành phần này:
- Thanh điều hướng (màu hồng) có thể cuộn theo chiều ngang
- Vùng nội dung (màu xanh dương) có thể cuộn theo chiều ngang
- Mỗi mục bài viết (màu xanh lục) đều có thể cuộn theo chiều dọc.
Có 2 loại phần tử khác nhau liên quan đến thao tác cuộn:
- Cửa sổ
Một hộp có kích thước xác định có kiểu thuộc tínhoverflow. - Một nền tảng quá khổ
Trong bố cục này, đó là các vùng chứa danh sách: đường liên kết điều hướng, bài viết trong mục và nội dung bài viết.
Bố cục <snap-tabs>
Bố cục cấp cao nhất mà tôi chọn là flex (Flexbox). Tôi đặt hướng thành column, vì vậy, tiêu đề và phần được sắp xếp theo chiều dọc. Đây là cửa sổ cuộn đầu tiên của chúng ta và nó ẩn mọi thứ bằng thuộc tính overflow: hidden. Phần đầu trang và phần nội dung sẽ sớm sử dụng hiệu ứng cuộn quá mức dưới dạng các vùng riêng lẻ.
<snap-tabs> <header></header> <section></section> </snap-tabs>
snap-tabs { display: flex; flex-direction: column; /* establish primary containing box */ overflow: hidden; position: relative; & > section { /* be pushy about consuming all space */ block-size: 100%; } & > header { /* defend againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
Chỉ vào sơ đồ 3 cuộn giấy đầy màu sắc:
<header>hiện đã sẵn sàng trở thành vùng chứa cuộn (màu hồng).<section>được chuẩn bị để trở thành vùng chứa cuộn (màu xanh dương).
Các khung mà tôi đã đánh dấu bên dưới bằng VisBug giúp chúng ta thấy cửa sổ mà vùng chứa cuộn đã tạo.
Bố cục <header> thẻ
Bố cục tiếp theo gần như giống nhau: Tôi sử dụng flex để tạo thứ tự dọc.
<snap-tabs>
<header>
<nav></nav>
<span class="snap-indicator"></span>
</header>
<section></section>
</snap-tabs>header { display: flex; flex-direction: column; }
.snap-indicator phải di chuyển theo chiều ngang cùng với nhóm đường liên kết và bố cục tiêu đề này giúp thiết lập giai đoạn đó. Không có phần tử nào có vị trí tuyệt đối ở đây!
Tiếp theo là kiểu cuộn. Chúng ta có thể chia sẻ kiểu cuộn giữa 2 vùng cuộn ngang (tiêu đề và phần), vì vậy, tôi đã tạo một lớp tiện ích, .scroll-snap-x.
.scroll-snap-x {
/* browser decide if x is ok to scroll and show bars on, y hidden */
overflow: auto hidden;
/* prevent scroll chaining on x scroll */
overscroll-behavior-x: contain;
/* scrolling should snap children on x */
scroll-snap-type: x mandatory;
@media (hover: none) {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
Mỗi thành phần đều cần có thuộc tính tràn trên trục x, vùng chứa cuộn để giữ lại hiệu ứng cuộn quá mức, thanh cuộn ẩn cho thiết bị cảm ứng và cuối cùng là tính năng cố định vị trí cuộn để khoá các vùng trình bày nội dung. Thứ tự thẻ bàn phím của chúng tôi có thể truy cập được và mọi hoạt động tương tác đều hướng tiêu điểm một cách tự nhiên. Vùng chứa cố định vị trí cuộn cũng có kiểu tương tác băng chuyền đẹp mắt trên bàn phím.
Bố cục <nav> của tiêu đề thẻ
Các đường liên kết điều hướng cần được bố trí trên một dòng, không có dấu ngắt dòng, căn giữa theo chiều dọc và mỗi mục liên kết phải khớp với vùng chứa cố định vị trí cuộn. Nhanh chóng bắt tay vào CSS 2021!
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
Mỗi đường liên kết đều có kiểu và kích thước riêng, vì vậy, bố cục điều hướng chỉ cần chỉ định hướng và luồng. Chiều rộng riêng biệt của các mục điều hướng giúp quá trình chuyển đổi giữa các thẻ trở nên thú vị khi chỉ báo điều chỉnh chiều rộng theo mục tiêu mới. Tuỳ thuộc vào số lượng phần tử trong này, trình duyệt sẽ hiển thị thanh cuộn hoặc không.
Bố cục <section> thẻ
Phần này là một mục linh hoạt và cần chiếm phần lớn không gian. Trang này cũng cần tạo các cột để đặt bài viết vào. Một lần nữa, xin cảm ơn
vì đã nhanh chóng hoàn thành
CSS 2021! block-size: 100% kéo dài phần tử này để lấp đầy phần tử mẹ nhiều nhất có thể, sau đó đối với bố cục riêng, phần tử này sẽ tạo một loạt cột có chiều rộng bằng 100% chiều rộng của phần tử mẹ. Tỷ lệ phần trăm hoạt động rất hiệu quả ở đây vì chúng ta đã viết các quy tắc ràng buộc chặt chẽ cho phần tử mẹ.
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
Điều này giống như chúng ta đang nói "mở rộng theo chiều dọc nhiều nhất có thể, theo cách dồn dập" (hãy nhớ tiêu đề mà chúng ta đặt thành flex-shrink: 0: đây là một biện pháp bảo vệ chống lại sự mở rộng dồn dập này), giúp đặt chiều cao hàng cho một tập hợp các cột có chiều cao đầy đủ. Kiểu auto-flow yêu cầu lưới luôn bố trí các phần tử con theo một đường ngang, không xuống dòng, chính xác là những gì chúng ta muốn; để tràn ra ngoài cửa sổ mẹ.
Đôi khi tôi thấy những khái niệm này khá khó hiểu! Phần tử mục này vừa vặn với một hộp, nhưng cũng tạo ra một tập hợp các hộp. Tôi hy vọng hình ảnh minh hoạ và phần giải thích này sẽ giúp ích cho bạn.
Bố cục <article> thẻ
Người dùng có thể cuộn nội dung bài viết và thanh cuộn chỉ xuất hiện nếu có nội dung bị tràn. Các thành phần này của bài viết được sắp xếp gọn gàng. Chúng đồng thời là phần tử mẹ cuộn và phần tử con cuộn. Trình duyệt thực sự đang xử lý một số tương tác phức tạp bằng thao tác chạm, chuột và bàn phím cho chúng ta tại đây.
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
Tôi chọn đặt các bài viết vừa khít trong trình cuộn mẹ. Tôi thực sự thích cách các mục liên kết điều hướng và phần tử bài viết bám vào điểm bắt đầu cùng dòng của vùng chứa cuộn tương ứng. Mối quan hệ này có vẻ hài hoà.
Bài viết là một phần tử con của lưới và kích thước của bài viết được xác định trước là khu vực khung nhìn mà chúng ta muốn cung cấp trải nghiệm cuộn. Điều này có nghĩa là tôi không cần bất kỳ kiểu chiều cao hoặc chiều rộng nào ở đây, tôi chỉ cần xác định cách nó tràn. Tôi đặt overflow-y thành auto, sau đó cũng chặn các hoạt động tương tác cuộn bằng thuộc tính overscroll-behavior tiện dụng.
Tóm tắt 3 khu vực cuộn
Dưới đây, tôi đã chọn "luôn hiển thị thanh cuộn" trong phần cài đặt hệ thống. Tôi nghĩ rằng việc bố cục hoạt động khi chế độ cài đặt này được bật là vô cùng quan trọng, vì tôi cần xem xét bố cục và cách điều phối thao tác cuộn.
Tôi nghĩ rằng việc nhìn thấy rãnh thanh cuộn trong thành phần này giúp cho thấy rõ vị trí của các vùng cuộn, hướng mà các vùng này hỗ trợ và cách các vùng này tương tác với nhau. Hãy xem xét cách mỗi khung cửa sổ cuộn này cũng là thành phần mẹ flex hoặc grid cho một bố cục.
Công cụ cho nhà phát triển có thể giúp chúng ta hình dung điều này:
Bố cục cuộn đã hoàn tất: có thể chụp nhanh, có thể liên kết sâu và có thể truy cập bằng bàn phím. Nền tảng vững chắc để cải thiện trải nghiệm người dùng, phong cách và sự hài lòng.
Tính năng nổi bật
Các thành phần con được cố định vị trí cuộn sẽ duy trì vị trí đã khoá trong khi đổi kích thước. Điều này có nghĩa là JavaScript sẽ không cần đưa bất kỳ nội dung nào vào khung hiển thị khi xoay thiết bị hoặc đổi kích thước trình duyệt. Hãy dùng thử tính năng này trong Chế độ thiết bị của Công cụ cho nhà phát triển Chromium bằng cách chọn bất kỳ chế độ nào khác ngoài Thích ứng, sau đó đổi kích thước khung thiết bị. Hãy lưu ý rằng phần tử này vẫn hiển thị và được khoá cùng với nội dung của nó. Tính năng này đã có từ khi Chromium cập nhật cách triển khai để phù hợp với quy cách. Sau đây là bài đăng trên blog về tính năng này.
Ảnh động
Mục tiêu của công việc ảnh động ở đây là liên kết rõ ràng các hoạt động tương tác với phản hồi của giao diện người dùng. Điều này giúp hướng dẫn hoặc hỗ trợ người dùng khám phá tất cả nội dung một cách liền mạch (hy vọng là vậy). Tôi sẽ thêm chuyển động có mục đích và có điều kiện. Giờ đây, người dùng có thể chỉ định các lựa chọn ưu tiên về chuyển động trong hệ điều hành của họ và tôi rất thích đáp ứng các lựa chọn ưu tiên của họ trong giao diện của mình.
Tôi sẽ liên kết đường gạch dưới của thẻ với vị trí cuộn của bài viết. Tính năng bám dính không chỉ giúp căn chỉnh đẹp mắt mà còn neo điểm bắt đầu và kết thúc của ảnh động.
Điều này giúp <nav> (hoạt động như một bản đồ thu nhỏ) luôn kết nối với nội dung.
Chúng ta sẽ kiểm tra lựa chọn ưu tiên về chuyển động của người dùng từ cả CSS và JS. Có một vài vị trí
bạn nên cân nhắc!
Hành vi cuộn
Bạn có thể cải thiện hành vi chuyển động của cả :target và element.scrollIntoView(). Theo mặc định, chế độ này là tức thì. Trình duyệt chỉ đặt vị trí cuộn. Vậy nếu chúng ta muốn chuyển đổi sang vị trí cuộn đó,
thay vì nhấp nháy ở đó thì sao?
@media (prefers-reduced-motion: no-preference) {
.scroll-snap-x {
scroll-behavior: smooth;
}
}
Vì chúng ta đang giới thiệu chuyển động ở đây và chuyển động mà người dùng không kiểm soát (như cuộn), chúng ta chỉ áp dụng kiểu này nếu người dùng không có lựa chọn ưu tiên trong hệ điều hành của họ về việc giảm chuyển động. Bằng cách này, chúng tôi chỉ giới thiệu chuyển động cuộn cho những người dùng không gặp vấn đề với chuyển động này.
Chỉ báo thẻ
Mục đích của ảnh động này là giúp liên kết chỉ báo với trạng thái của nội dung. Tôi quyết định sử dụng kiểu chuyển màu mờ dần border-bottom cho những người dùng thích giảm chuyển động, và hiệu ứng chuyển động trượt + mờ dần màu sắc được liên kết với thao tác cuộn cho những người dùng không ngại chuyển động.
Trong Công cụ cho nhà phát triển Chromium, tôi có thể bật/tắt lựa chọn ưu tiên và minh hoạ 2 kiểu chuyển đổi khác nhau. Tôi đã có rất nhiều niềm vui khi xây dựng ứng dụng này.
@media (prefers-reduced-motion: reduce) {
snap-tabs > header a {
border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
transition: color .7s ease, border-color .5s ease;
&:is(:target,:active,[active]) {
color: var(--text-active-color);
border-block-end-color: hsl(var(--accent));
}
}
snap-tabs .snap-indicator {
visibility: hidden;
}
}
Tôi ẩn .snap-indicator khi người dùng muốn giảm chuyển động vì tôi không cần đến nữa. Sau đó, tôi thay thế bằng kiểu border-block-end và transition. Ngoài ra, hãy lưu ý rằng trong hoạt động tương tác của thẻ, mục điều hướng đang hoạt động không chỉ có phần gạch chân nổi bật của thương hiệu mà màu văn bản của mục đó cũng đậm hơn. Phần tử đang hoạt động có độ tương phản màu văn bản cao hơn và có điểm nhấn là ánh sáng rực rỡ bên dưới.
Chỉ cần thêm một vài dòng CSS là bạn có thể khiến người dùng cảm thấy được quan tâm (theo nghĩa là chúng ta đang tôn trọng lựa chọn ưu tiên về chuyển động của họ). Tôi thích thế này.
@scroll-timeline
Trong phần trên, tôi đã cho bạn thấy cách tôi xử lý các kiểu hiệu ứng mờ dần khi giảm chuyển động. Trong phần này, tôi sẽ cho bạn thấy cách tôi liên kết chỉ báo và vùng cuộn với nhau. Tiếp theo là một số nội dung thử nghiệm thú vị. Tôi hy vọng bạn cũng hào hứng như tôi.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
Trước tiên, tôi kiểm tra lựa chọn ưu tiên về chuyển động của người dùng trong JavaScript. Nếu kết quả của việc này là false, tức là người dùng muốn giảm chuyển động, thì chúng tôi sẽ không chạy bất kỳ hiệu ứng chuyển động liên kết cuộn nào.
if (motionOK) {
// motion based animation code
}
Tại thời điểm viết bài này, trình duyệt không hỗ trợ @scroll-timeline. Đây là một thông số kỹ thuật nháp chỉ có các phương thức triển khai thử nghiệm. Tuy nhiên, nó có một polyfill mà tôi sử dụng trong bản minh hoạ này.
ScrollTimeline
Mặc dù cả CSS và JavaScript đều có thể tạo dòng thời gian cuộn, nhưng tôi đã chọn JavaScript để có thể sử dụng các phép đo lường phần tử trực tiếp trong ảnh động.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
Tôi muốn một phần tử theo dõi vị trí cuộn của một phần tử khác. Bằng cách tạo ScrollTimeline, tôi xác định trình điều khiển của liên kết cuộn, tức là scrollSource.
Thông thường, ảnh động trên web chạy theo một khung thời gian chung, nhưng với
sectionScrollTimeline tuỳ chỉnh trong bộ nhớ, tôi có thể thay đổi tất cả những điều đó.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Trước khi đi sâu vào các khung hình chính của ảnh động, tôi nghĩ bạn cần lưu ý rằng phần tử theo dõi thao tác cuộn, tabindicator, sẽ được tạo ảnh động dựa trên một dòng thời gian tuỳ chỉnh, tức là thao tác cuộn của phần. Thao tác này hoàn tất việc liên kết, nhưng thiếu thành phần cuối cùng, các điểm có trạng thái để tạo ảnh động giữa các điểm, còn được gọi là khung hình chính.
Khung hình chính động
Có một cách khai báo CSS thuần tuý rất hiệu quả để tạo ảnh động bằng @scroll-timeline, nhưng ảnh động mà tôi chọn lại quá linh động. Không có cách nào để chuyển đổi giữa chiều rộng auto và không có cách nào để tạo động một số khung hình chính dựa trên độ dài của phần tử con.
Tuy nhiên, JavaScript biết cách lấy thông tin đó, vì vậy, chúng ta sẽ tự lặp lại các phần tử con và lấy các giá trị được tính toán trong thời gian chạy:
tabindicator.animate({
transform: [...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`),
width: [...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Đối với mỗi tabnavitem, hãy phân rã vị trí offsetLeft và trả về một chuỗi sử dụng vị trí đó làm giá trị translateX. Thao tác này sẽ tạo 4 khung hình chính biến đổi cho ảnh động. Điều tương tự cũng được thực hiện cho chiều rộng, mỗi chiều rộng được hỏi chiều rộng động của nó là gì và sau đó được dùng làm giá trị khung hình chính.
Dưới đây là kết quả mẫu, dựa trên phông chữ và lựa chọn ưu tiên của tôi về trình duyệt:
Khung hình chính TranslateX:
[...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`)
// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]
Khung hình chính về chiều rộng:
[...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]
Tóm lại, chỉ báo thẻ giờ đây sẽ tạo ảnh động trên 4 khung hình chính, tuỳ thuộc vào vị trí cố định của thanh cuộn trong phần. Các điểm bám dính tạo ra sự phân định rõ ràng giữa các khung hình chính và thực sự làm tăng cảm giác đồng bộ của ảnh động.
Người dùng điều khiển ảnh động bằng cách tương tác, xem chiều rộng và vị trí của chỉ báo thay đổi từ phần này sang phần tiếp theo, theo dõi chính xác bằng thao tác cuộn.
Có thể bạn không để ý, nhưng tôi rất tự hào về sự chuyển đổi màu sắc khi mục điều hướng được làm nổi bật được chọn.
Màu xám nhạt không được chọn sẽ càng bị đẩy lùi khi mục được làm nổi bật có độ tương phản cao hơn. Việc chuyển đổi màu cho văn bản là điều phổ biến, chẳng hạn như khi di chuột và khi được chọn, nhưng việc chuyển đổi màu đó khi cuộn, đồng bộ hoá với chỉ báo gạch dưới là một bước tiến mới.
Sau đây là cách tôi thực hiện:
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
Mỗi đường liên kết điều hướng trong thẻ đều cần ảnh động màu mới này, theo dõi cùng một dòng thời gian cuộn như chỉ báo gạch dưới. Tôi sử dụng cùng một dòng thời gian như trước: vì vai trò của dòng thời gian này là phát ra một nhịp khi cuộn, nên chúng ta có thể sử dụng nhịp đó trong bất kỳ loại ảnh động nào mà chúng ta muốn. Như trước đây, tôi tạo 4 khung hình chính trong vòng lặp và trả về màu sắc.
[...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
// results in 4 array items, which represent 4 keyframe states
// [
"var(--text-active-color)",
"var(--text-color)",
"var(--text-color)",
"var(--text-color)",
]
Khung hình chính có màu var(--text-active-color) làm nổi bật đường liên kết, còn lại là màu văn bản chuẩn. Vòng lặp lồng nhau ở đó giúp việc này tương đối đơn giản, vì vòng lặp bên ngoài là từng mục điều hướng và vòng lặp bên trong là khung hình chính cá nhân của từng mục điều hướng. Tôi kiểm tra xem phần tử vòng lặp bên ngoài có giống với phần tử vòng lặp bên trong hay không và sử dụng thông tin đó để biết khi nào phần tử được chọn.
Tôi rất vui khi viết bài này. Rất nhiều.
Nhiều điểm cải tiến hơn nữa cho JavaScript
Cần lưu ý rằng cốt lõi của những gì tôi đang cho bạn thấy ở đây hoạt động mà không cần JavaScript. Như đã nói, hãy xem cách chúng ta có thể cải thiện khi có JS.
Liên kết sâu
Đường liên kết sâu là một thuật ngữ thường dùng cho thiết bị di động, nhưng tôi nghĩ mục đích của đường liên kết sâu được đáp ứng ở đây bằng các thẻ, trong đó bạn có thể chia sẻ URL trực tiếp đến nội dung của thẻ. Trình duyệt sẽ điều hướng trong trang đến mã nhận dạng khớp với hàm băm URL. Tôi nhận thấy
trình xử lý onload này tạo ra hiệu ứng trên nhiều nền tảng.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
Đồng bộ hoá khi kết thúc cuộn
Người dùng không phải lúc nào cũng nhấp hoặc sử dụng bàn phím, đôi khi họ chỉ cuộn tự do, như họ có thể làm. Khi thanh cuộn của mục ngừng cuộn, bất kể dừng ở đâu, thanh điều hướng trên cùng cũng cần phải khớp với vị trí đó.
Sau đây là cách tôi chờ thao tác cuộn kết thúc:
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
Bất cứ khi nào các mục được cuộn, hãy xoá thời gian chờ của mục (nếu có) và bắt đầu một mục mới. Khi các phần ngừng cuộn, đừng xoá thời gian chờ và kích hoạt 100 mili giây sau khi nghỉ. Khi sự kiện này kích hoạt, hãy gọi hàm tìm cách xác định vị trí người dùng đã dừng lại.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
Giả sử tính năng cố định vị trí cuộn đã được áp dụng, việc chia vị trí cuộn hiện tại cho chiều rộng của vùng cuộn sẽ cho ra một số nguyên chứ không phải số thập phân. Sau đó, tôi cố gắng lấy một mục điều hướng từ bộ nhớ đệm thông qua chỉ mục đã tính toán này và nếu tìm thấy mục nào đó, tôi sẽ gửi mục trùng khớp để đặt thành mục đang hoạt động.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
Để đặt thẻ đang hoạt động, trước tiên, hãy xoá mọi thẻ đang hoạt động, sau đó gán thuộc tính trạng thái đang hoạt động cho mục điều hướng sắp tới. Lệnh gọi đến scrollIntoView() có một tương tác thú vị với CSS mà bạn nên lưu ý.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
Trong CSS tiện ích cố định vị trí cuộn ngang, chúng tôi đã lồng một truy vấn đa phương tiện áp dụng thao tác cuộn smooth nếu người dùng chấp nhận chuyển động. JavaScript có thể tự do thực hiện
các lệnh gọi để cuộn các phần tử vào khung hiển thị và CSS có thể quản lý trải nghiệm người dùng theo cách khai báo.
Đôi khi, họ tạo nên một cặp đôi khá thú vị.
Kết luận
Giờ thì bạn đã biết cách tôi làm, vậy bạn sẽ làm thế nào?! Điều này tạo nên một cấu trúc thành phần thú vị! Ai sẽ là người tạo phiên bản đầu tiên có khe trong khung yêu thích của họ? 🙂
Hãy đa dạng hoá các phương pháp tiếp cận và tìm hiểu mọi cách để xây dựng trên web. Tạo một Glitch, tweet cho tôi phiên bản của bạn và tôi sẽ thêm phiên bản đó vào phần Bản phối lại của cộng đồng bên dưới.
Bản phối lại của cộng đồng
- @devnook, @rob_dodson và @DasSurma với Thành phần web: bài viết.
- @jhvanderschee có các nút: Codepen.