Tổng quan cơ bản về cách tạo 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ó khả năng 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 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ố cần thiết của trải nghiệm người dùng 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.
Chiến thuật 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 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ợ 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 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 và 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. Tiêu đề và phần 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ác 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ể dùng chung kiểu cuộn cho 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 là .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 cần có thuộc tính tràn trên trục x, vùng chứa cuộn để giữ lại thao tác 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á 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 đến 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 từ bàn phím.
Bố cục tiêu đề thẻ <nav>
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. CSS đã làm việc nhanh chóng trong năm 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. Ứng dụng 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 các bạn đã nhanh chóng hoàn thành công việc cho CSS 2021! block-size: 100% kéo giãn 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 hiệu quả ở đây vì chúng ta đã viết các quy tắc ràng buộc chặt chẽ cho thành phần 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 việc 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 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ử section này vừa nằm gọn trong một hộp, vừa tạo ra một tập hợp các hộp. Tôi hy vọng hình ảnh 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 phần tử này của bài viết được sắp xếp gọn gàng. Chúng đồng thời là thành phần mẹ và thành phần con của thao tác 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ào đúng vị trí 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à các 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à điều quan trọng gấp đôi, vì tôi cần xem xét bố cục và cách sắp xếp 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 quá trình đổ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ử trong Chế độ thiết bị của Công cụ của Chromium cho nhà phát triển 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 thông số kỹ thuật. Sau đây là bài đăng trên blog về tính năng này.
Ảnh động
Mục tiêu của việc tạo ảnh động ở đây là liên kết rõ ràng các lượt 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 (hy vọng) khám phá liền mạch tất cả nội dung. 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 ta 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 tạo 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à ảnh động trượt + mờ dần màu đượ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ụ của Chromium cho nhà phát triển, 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ó điểm nhấn gạch dưới 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 sáng bên dưới.
Chỉ cần thêm một vài dòng CSS là 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 mờ dần chuyển động giảm, còn 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 viết 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 một 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ĩ cần phải chỉ ra 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 linh độ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 cuộn của trình cuộn mục. 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 hoạt động tương tác của họ, 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ề hiệu ứng chuyển màu 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 thậm chí còn bị đẩy lùi hơn nữa khi mục được làm nổi bật có độ tương phản cao hơn. Thông thường, bạn sẽ chuyển đổi màu cho văn bả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ẻ cần có ả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 đang đượ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 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 hoạt động 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 ta đã lồng một truy vấn đa phương tiện áp dụng tính năng cuộn smooth nếu người dùng có thể chịu được 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. Nếu bạn tạo một bản minh hoạ, hãy 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 sau đây.
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.