"Kéo để hiện" là một mẫu giao diện người dùng, trong đó nội dung (chẳng hạn như thanh tìm kiếm hoặc chế độ điều khiển làm mới) bị ẩn phía trên đầu vùng có thể cuộn khi tải lần đầu và người dùng có thể kéo xuống (cuộn lên) để hiện nội dung đó. Mẫu này thường được dùng trong các ứng dụng di động và ứng dụng web cho thanh tìm kiếm, bộ lọc và các chế độ điều khiển phụ khác cần truy cập được nhưng không hiển thị ngay lập tức.
Thuộc tính CSS scroll-initial-target cung cấp một cách khai báo, chỉ dùng CSS để triển khai mẫu này. Bằng cách đặt scroll-initial-target: nearest trên phần tử nội dung chính, vùng chứa cuộn sẽ hiển thị với nội dung bị ẩn được cuộn ra khỏi khung hiển thị. Trước đây, nhà phát triển dựa vào JavaScript (Element.scrollIntoView()) hoặc giá trị nhận dạng phân mảnh của URL (#content-id) để thực hiện việc này, nhưng cả hai đều có hạn chế và khó triển khai.
Cách triển khai
Cách triển khai mẫu kéo để hiện:
- Đảm bảo có vùng chứa cuộn: Phần tử mục tiêu phải nằm trong vùng chứa cuộn (một phần tử có thuộc tính tràn cho phép cuộn, chẳng hạn như
overflow: auto). Đây có thể là bất kỳ phần tử gốc nào, bao gồm cả phần tử gốc<html>. - Xác định phần tử ẩn: Đặt nội dung bạn muốn ẩn (ví dụ: thanh tìm kiếm) làm phần tử con đầu tiên bên trong vùng chứa cuộn. Phần tử này sẽ bị cuộn ra khỏi khung hiển thị khi tải lần đầu.
- Xác định nội dung chính: Đặt phần tử nội dung chính ngay sau phần tử ẩn. Đây là phần tử mà người dùng sẽ thấy đầu tiên.
- Nhắm đến nội dung chính: Áp dụng
scroll-initial-target: nearestcho phần tử nội dung chính để vùng chứa cuộn hiển thị khi được cuộn vào khung hiển thị, ẩn phần tử phía trên nó. - Thêm tính năng cố định vị trí cuộn: Để đảm bảo phần tử bị ẩn luôn hiển thị đầy đủ hoặc bị ẩn hoàn toàn (và không ở trạng thái cuộn một phần), hãy thêm
scroll-snap-type: y mandatoryvào vùng chứa cuộn vàscroll-snap-align: startvào cả phần tử bị ẩn và phần tử nội dung chính.
Mã ví dụ: Kéo để hiện thanh tìm kiếm
/**
* ANCESTOR: Define the scroll container.
* Scroll snapping ensures the search bar is always
* either fully visible or fully hidden.
*/
.scroll-container {
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
/**
* HIDDEN ELEMENT: The search bar hidden above the fold on load.
* It has scroll-snap-align so it snaps into place when pulled down.
*/
.search-bar {
height: 60px;
scroll-snap-align: start;
}
/**
* MAIN CONTENT + TARGET: The element the user sees first.
* scroll-snap-align makes it a valid snap point.
* scroll-initial-target tells the browser to scroll here on
* initial render, hiding the search bar above it.
*/
.main-content {
scroll-snap-align: start;
scroll-initial-target: nearest;
}
Triển khai chiến lược và các phương pháp hay nhất
- NÊN sử dụng
scroll-initial-target: nearestkhi bạn muốn thu hút sự chú ý của người dùng vào một phần cụ thể của vùng có thể cuộn khi tải và cố ý ẩn các đơn vị giao diện người dùng ngoại vi như thanh tìm kiếm ở trên cùng. - KHÔNG nhầm lẫn điều này với tiêu điểm hỗ trợ tiếp cận. Thuộc tính này chỉ di chuyển khung nhìn hình ảnh chứ không di chuyển tiêu điểm bàn phím. Bạn phải quản lý
element.focus()theo cách thủ công nếu đích đến là điểm bắt đầu cho người dùng bàn phím. - KHÔNG sử dụng thuộc tính này nếu bạn cần ảnh động "cuộn" mượt mà khi tải; thuộc tính này là riêng biệt và đặt vị trí ngay lập tức trong giai đoạn bố cục.
- KHÔNG đặt
scroll-initial-targettrên nhiều phần tử trong cùng một vùng chứa có thể cuộn. Nếu nhiều phần tử chỉ địnhscroll-initial-target: nearest, thì trình duyệt sẽ chọn phần tử xuất hiện đầu tiên theo thứ tự cây DOM. - NÊN tính đến Hệ thống phân cấp ưu tiên: Một phân mảnh của URL (ví dụ:
example.com/#top) và thuộc tínhscroll-startở cấp vùng chứa đều được ưu tiên hơnscroll-initial-target.
Chiến lược dự phòng
Đối với những trình duyệt chưa hỗ trợ API này, hãy sử dụng phương án dự phòng JavaScript. Lưu ý rằng để kéo nội dung nhằm hiển thị, bạn cần liên kết nội dung chính với start (trên cùng) của vùng chứa.
/**
* Progressive Enhancement Fallback
*/
document.addEventListener("DOMContentLoaded", () => {
// Check for native CSS support
if (!CSS.supports("scroll-initial-target", "nearest")) {
const targetContent = document.querySelector('.main-content.target');
if (targetContent) {
// Use behavior: "instant" to mimic the native CSS behavior
// 'block: start' should match your CSS 'scroll-snap-align' (or expected top position)
targetContent.scrollIntoView({ behavior: 'instant', block: 'start' });
}
}
});