VisualViewport
Baseline
Widely available
*
This feature is well established and works across many devices and browser versions. It’s been available across browsers since August 2021.
* Some parts of this feature may have varying levels of support.
Giao diện VisualViewport của CSSOM view API đại diện cho khung nhìn trực quan của một cửa sổ nhất định. Đối với một trang chứa iframe, mỗi iframe cũng như trang chứa đó sẽ có một đối tượng cửa sổ riêng. Mỗi cửa sổ trên trang sẽ có một VisualViewport riêng đại diện cho các thuộc tính liên quan đến cửa sổ đó.
Web trên thiết bị di động chứa hai khung nhìn: khung nhìn bố cục và khung nhìn trực quan. Khung nhìn bố cục bao phủ tất cả các phần tử trên trang, còn khung nhìn trực quan là những gì thực sự hiển thị trên màn hình. Khi người dùng thu phóng bằng cử chỉ nhéo ngón tay vào trang, khung nhìn trực quan thu nhỏ lại nhưng khung nhìn bố cục không thay đổi. Các tính năng giao diện người dùng như bàn phím hiển thị trên màn hình (OSK) có thể thu nhỏ khung nhìn trực quan mà không ảnh hưởng đến khung nhìn bố cục.
Điều gì xảy ra khi một phần tử trang web cần hiển thị trên màn hình bất kể phần hiển thị của trang web là gì? Ví dụ, nếu bạn cần một bộ điều khiển hình ảnh luôn hiển thị bất kể mức thu phóng nhéo ngón tay của thiết bị là bao nhiêu? Các trình duyệt hiện tại xử lý điều này theo những cách khác nhau. Khung nhìn trực quan cho phép các nhà phát triển web giải quyết vấn đề này bằng cách định vị các phần tử tương đối với những gì đang hiển thị trên màn hình.
Bạn có thể lấy khung nhìn trực quan của cửa sổ bằng Window.visualViewport.
Note:
Chỉ cửa sổ cấp cao nhất mới có khung nhìn trực quan khác biệt với khung nhìn bố cục. Do đó, nhìn chung chỉ đối tượng VisualViewport của cửa sổ cấp cao nhất mới hữu ích. Đối với một <iframe>, các thông số khung nhìn trực quan như VisualViewport.width luôn tương ứng với các thông số khung nhìn bố cục như document.documentElement.clientWidth.
Thuộc tính phiên bản
Cũng kế thừa các thuộc tính từ giao diện cha, EventTarget.
VisualViewport.offsetLeftRead only-
Trả về độ lệch của cạnh trái khung nhìn trực quan so với cạnh trái khung nhìn bố cục tính bằng pixel CSS.
VisualViewport.offsetTopRead only-
Trả về độ lệch của cạnh trên khung nhìn trực quan so với cạnh trên khung nhìn bố cục tính bằng pixel CSS.
VisualViewport.pageLeftRead only-
Trả về tọa độ x của khung nhìn trực quan tương đối với gốc tọa độ của khối chứa ban đầu tính bằng pixel CSS.
VisualViewport.pageTopRead only-
Trả về tọa độ y của khung nhìn trực quan tương đối với gốc tọa độ của khối chứa ban đầu tính bằng pixel CSS.
VisualViewport.widthRead only-
Trả về chiều rộng của khung nhìn trực quan tính bằng pixel CSS.
VisualViewport.heightRead only-
Trả về chiều cao của khung nhìn trực quan tính bằng pixel CSS.
VisualViewport.scaleRead only-
Trả về hệ số thu phóng nhéo ngón tay được áp dụng cho khung nhìn trực quan.
Phương thức phiên bản
Cũng kế thừa các phương thức từ giao diện cha, EventTarget.
Sự kiện
Lắng nghe các sự kiện này bằng addEventListener() hoặc bằng cách gán một trình lắng nghe sự kiện vào thuộc tính oneventname liên quan của giao diện này.
resize-
Được kích hoạt khi khung nhìn trực quan bị thay đổi kích thước. Cũng có thể truy cập thông qua thuộc tính
onresize. scroll-
Được kích hoạt khi khung nhìn trực quan bị cuộn. Cũng có thể truy cập thông qua thuộc tính
onscroll. scrollend-
Được kích hoạt khi thao tác cuộn trên khung nhìn trực quan kết thúc. Cũng có thể truy cập thông qua thuộc tính
onscrollend.
Ví dụ
>Lấy thông tin khung nhìn trực quan trong khi cuộn và thu phóng
Ví dụ visual viewport của chúng tôi cung cấp minh họa cơ bản về cách hoạt động của các tính năng khung nhìn trực quan khác nhau, bao gồm cả ba loại sự kiện. Tải trang trên các trình duyệt máy tính và di động hỗ trợ, rồi thử cuộn trang và thu phóng bằng nhéo ngón tay. Khi xảy ra resize và scroll, hộp thông tin được định vị lại để giữ nguyên vị trí tương đối với khung nhìn trực quan, và thông tin khung nhìn và cuộn được hiển thị cập nhật. Ngoài ra, khi xảy ra resize và scroll, chúng tôi thay đổi màu hộp để cho thấy có gì đó đang xảy ra, và khôi phục màu khi scrollend.
Bạn sẽ thấy rằng trên trình duyệt máy tính, các giá trị Window.scrollX và Window.scrollY được cập nhật khi cửa sổ cuộn, còn vị trí của khung nhìn trực quan không thay đổi. Tuy nhiên, trên trình duyệt di động, các giá trị VisualViewport.offsetLeft và VisualViewport.offsetTop thường được cập nhật, vì thường là khung nhìn trực quan thay đổi chứ không phải vị trí cửa sổ.
Trong ví dụ, hộp thông tin HTML được đại diện bởi một <div> với id là output, còn CSS được ẩn để ngắn gọn.
<p id="instructions">
Try scrolling around and pinch-zooming to see how the reported values change.
</p>
<div id="output">
<p id="visual-info"></p>
<hr />
<p id="window-info"></p>
</div>
Trong JavaScript, chúng tôi bắt đầu bằng cách lấy tham chiếu đến hộp thông tin sẽ được cập nhật khi trang được thu phóng và cuộn, cũng như hai đoạn văn bên trong nó. Đoạn đầu tiên sẽ chứa các giá trị VisualViewport.offsetLeft và VisualViewport.offsetTop được báo cáo, còn đoạn thứ hai sẽ chứa các giá trị Window.scrollX và Window.scrollY được báo cáo.
const output = document.getElementById("output");
const visualInfo = document.getElementById("visual-info");
const windowInfo = document.getElementById("window-info");
Tiếp theo, chúng tôi định nghĩa hai hàm chính sẽ chạy khi các sự kiện được kích hoạt:
- Hàm
scrollUpdater()sẽ được thực thi khi xảy raresizevàscroll: hàm này cập nhật vị trí của hộp thông tin tương đối với khung nhìn trực quan bằng cách truy vấn các thuộc tínhVisualViewport.offsetTopvàVisualViewport.offsetLeftvà sử dụng giá trị của chúng để cập nhật các giá trị của inset properties liên quan. Chúng tôi cũng thay đổi màu nền của hộp thông tin để cho thấy có gì đó đang xảy ra, và chạy hàmupdateText()để cập nhật các giá trị hiển thị trong hộp. - Hàm
scrollEndUpdater()sẽ được kích hoạt khi xảy rascrollend: hàm này trả lại màu ban đầu cho hộp thông tin và chạy hàmupdateText()để đảm bảo các giá trị mới nhất được hiển thị khiscrollend.
const scrollUpdater = () => {
output.style.top = `${visualViewport.offsetTop + 10}px`;
output.style.left = `${visualViewport.offsetLeft + 10}px`;
output.style.background = "yellow";
updateText();
};
const scrollendUpdater = () => {
output.style.background = "lime";
updateText();
};
Hàm updateText() thiết lập HTMLElement.innerText của đoạn đầu tiên để hiển thị các giá trị VisualViewport.offsetLeft và VisualViewport.offsetTop hiện tại, và HTMLElement.innerText của đoạn thứ hai để hiển thị các giá trị Window.scrollX và Window.scrollY hiện tại. Sau khi định nghĩa updateText(), chúng tôi gọi nó ngay lập tức để hộp thông tin hiển thị chính xác khi trang tải.
function updateText() {
visualInfo.innerText = `Visual viewport left: ${visualViewport.offsetLeft.toFixed(2)}
top: ${visualViewport.offsetTop.toFixed(2)}`;
windowInfo.innerText = `Window scrollX: ${window.scrollX.toFixed(2)}
scrollY: ${window.scrollY.toFixed(2)}`;
}
updateText();
Chúng tôi làm tròn tất cả các giá trị đến hai chữ số thập phân bằng phương thức Number.toFixed() vì một số trình duyệt hiển thị các giá trị dưới pixel với số thập phân lớn.
Bây giờ chúng tôi thiết lập các thuộc tính trình xử lý sự kiện trên cả khung nhìn trực quan và đối tượng Window để chạy các hàm chính vào thời điểm thích hợp trên cả thiết bị di động và máy tính:
- Chúng tôi thiết lập các trình xử lý trên
windowđể vị trí và nội dung hộp thông tin sẽ cập nhật khi cuộn cửa sổ thông thường, ví dụ khi bạn cuộn trang trên trình duyệt máy tính. - Chúng tôi thiết lập các trình xử lý trên
visualViewportđể vị trí và nội dung hộp thông tin sẽ cập nhật khi thực hiện các thao tác cuộn/thu phóng khung nhìn trực quan, ví dụ khi bạn cuộn và thu phóng nhéo ngón tay trang trên trình duyệt di động.
visualViewport.onresize = scrollUpdater;
visualViewport.onscroll = scrollUpdater;
visualViewport.onscrollend = scrollendUpdater;
window.onresize = scrollUpdater;
window.onscroll = scrollUpdater;
window.onscrollend = scrollendUpdater;
Hàm scrollUpdater() sẽ được kích hoạt khi xảy ra resize và scroll, còn scrollEndUpdater() sẽ được kích hoạt khi xảy ra scrollend.
Ẩn một hộp phủ khi thu phóng
Ví dụ này, được lấy từ Visual Viewport README, cho thấy cách viết một đoạn mã sẽ ẩn một hộp phủ (có thể chứa quảng cáo) khi người dùng thu phóng. Đây là cách hay để cải thiện trải nghiệm người dùng khi thu phóng trên các trang. Cũng có một bản thử nghiệm trực tiếp.
const bottomBar = document.getElementById("bottom-bar");
const viewport = window.visualViewport;
function resizeHandler() {
bottomBar.style.display = viewport.scale > 1.3 ? "none" : "block";
}
window.visualViewport.addEventListener("resize", resizeHandler);
Mô phỏng position: device-fixed
Ví dụ này, cũng được lấy từ Visual Viewport README, cho thấy cách sử dụng API này để mô phỏng position: device-fixed, giúp cố định các phần tử vào khung nhìn trực quan. Cũng có một bản thử nghiệm trực tiếp.
const bottomBar = document.getElementById("bottom-bar");
const viewport = window.visualViewport;
function viewportHandler() {
const layoutViewport = document.getElementById("layoutViewport");
// Since the bar is position: fixed we need to offset it by the visual
// viewport's offset from the layout viewport origin.
const offsetLeft = viewport.offsetLeft;
const offsetTop =
viewport.height -
layoutViewport.getBoundingClientRect().height +
viewport.offsetTop;
// You could also do this by setting style.left and style.top if you
// use width: 100% instead.
bottomBar.style.transform = `translate(${offsetLeft}px, ${offsetTop}px) scale(${
1 / viewport.scale
})`;
}
window.visualViewport.addEventListener("scroll", viewportHandler);
window.visualViewport.addEventListener("resize", viewportHandler);
Note:
Kỹ thuật này nên được sử dụng cẩn thận; việc mô phỏng position: device-fixed theo cách này có thể khiến phần tử cố định bị nhấp nháy trong khi cuộn.
Thông số kỹ thuật
| Thông số kỹ thuật |
|---|
| CSSOM View Module> # the-visualviewport-interface> |
Tương thích trình duyệt
Xem thêm
- Web Viewports Explainer — giải thích hữu ích về các khái niệm khung nhìn web, bao gồm sự khác biệt giữa khung nhìn trực quan và khung nhìn bố cục.