Sử dụng vùng thanh tiêu đề bên cạnh các chế độ điều khiển cửa sổ để PWA của bạn trông giống một ứng dụng hơn.
Nếu nhớ bài viết Giúp PWA trông giống ứng dụng hơn của tôi, có thể bạn sẽ nhớ cách tôi đề cập đến việc tuỳ chỉnh thanh tiêu đề của ứng dụng như một chiến lược để tạo trải nghiệm giống ứng dụng hơn. Sau đây là ví dụ về cách thông tin này có thể xuất hiện trong ứng dụng Podcasts trên macOS.

Có thể bạn sẽ phản đối bằng cách nói rằng Podcasts là một ứng dụng macOS dành riêng cho nền tảng, không chạy trong trình duyệt và do đó có thể làm những gì mình muốn mà không phải tuân theo các quy tắc của trình duyệt. Đúng vậy, nhưng tin vui là tính năng Lớp phủ điều khiển cửa sổ (chủ đề của bài viết này) sẽ sớm cho phép bạn tạo giao diện người dùng tương tự cho PWA của mình.
Các thành phần Lớp phủ chế độ điều khiển cửa sổ
Lớp phủ chế độ điều khiển cửa sổ bao gồm 4 tính năng phụ:
- Giá trị
"window-controls-overlay"
cho trường"display_override"
trong tệp kê khai ứng dụng web. - Các biến môi trường CSS
titlebar-area-x
,titlebar-area-y
,titlebar-area-width
vàtitlebar-area-height
. - Việc chuẩn hoá thuộc tính CSS độc quyền trước đây
-webkit-app-region
thành thuộc tínhapp-region
để xác định các vùng có thể kéo trong nội dung web. - Một cơ chế để truy vấn và xử lý khu vực điều khiển cửa sổ thông qua thành viên
windowControlsOverlay
củawindow.navigator
.
Lớp phủ chế độ điều khiển cửa sổ là gì
Khu vực thanh tiêu đề là khoảng trống ở bên trái hoặc bên phải của các nút điều khiển cửa sổ (tức là các nút thu nhỏ, phóng to, đóng, v.v.) và thường chứa tiêu đề của ứng dụng. Lớp phủ chế độ điều khiển cửa sổ cho phép các ứng dụng web tiến bộ (PWA) mang lại cảm giác giống như ứng dụng hơn bằng cách thay thế thanh tiêu đề có chiều rộng đầy đủ hiện có bằng một lớp phủ nhỏ chứa các chế độ điều khiển cửa sổ. Điều này cho phép nhà phát triển đặt nội dung tuỳ chỉnh vào vùng thanh tiêu đề do trình duyệt kiểm soát trước đây.
Trạng thái hiện tại
Bước | Trạng thái |
---|---|
1. Tạo video giải thích | Hoàn tất |
2. Tạo bản nháp ban đầu của quy cách | Hoàn tất |
3. Thu thập ý kiến phản hồi và lặp lại quy trình thiết kế | Đang tiến hành |
4. Bản dùng thử theo nguyên gốc | Hoàn chỉnh |
5. Ra mắt | Hoàn tất (trong Chromium 104) |
Cách sử dụng Lớp phủ chế độ điều khiển cửa sổ
Thêm window-controls-overlay
vào tệp kê khai ứng dụng web
Ứng dụng web tiến bộ có thể chọn sử dụng lớp phủ các nút điều khiển cửa sổ bằng cách thêm "window-controls-overlay"
làm thành phần "display_override"
chính trong tệp kê khai ứng dụng web:
{
"display_override": ["window-controls-overlay"]
}
Lớp phủ điều khiển cửa sổ sẽ chỉ xuất hiện khi tất cả các điều kiện sau đây được đáp ứng:
- Ứng dụng không mở trong trình duyệt mà mở trong một cửa sổ PWA riêng.
- Tệp kê khai bao gồm
"display_override": ["window-controls-overlay"]
. (Sau đó, bạn có thể dùng các giá trị khác.) - PWA đang chạy trên một hệ điều hành máy tính.
- Nguồn gốc hiện tại khớp với nguồn gốc mà PWA được cài đặt.
Kết quả là một vùng thanh tiêu đề trống với các nút điều khiển cửa sổ thông thường ở bên trái hoặc bên phải, tuỳ thuộc vào hệ điều hành.

Di chuyển nội dung vào thanh tiêu đề
Vì đã có khoảng trống trên thanh tiêu đề, nên bạn có thể di chuyển một mục vào đó. Đối với bài viết này, tôi đã tạo một PWA Nội dung nổi bật của Wikimedia. Một tính năng hữu ích cho ứng dụng này có thể là tìm kiếm các từ trong tiêu đề bài viết. HTML cho tính năng tìm kiếm có dạng như sau:
<div class="search">
<img src="logo.svg" alt="Wikimedia logo." width="32" height="32" />
<label>
<input type="search" />
Search for words in articles
</label>
</div>
Để di chuyển div
này lên thanh tiêu đề, bạn cần có một số CSS:
.search {
/* Make sure the `div` stays there, even when scrolling. */
position: fixed;
/**
* Gradient, because why not. Endless opportunities.
* The gradient ends in `#36c`, which happens to be the app's
* `<meta name="theme-color" content="#36c">`.
*/
background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
/* Use the environment variable for the left anchoring with a fallback. */
left: env(titlebar-area-x, 0);
/* Use the environment variable for the top anchoring with a fallback. */
top: env(titlebar-area-y, 0);
/* Use the environment variable for setting the width with a fallback. */
width: env(titlebar-area-width, 100%);
/* Use the environment variable for setting the height with a fallback. */
height: env(titlebar-area-height, 33px);
}
Bạn có thể thấy hiệu ứng của mã này trong ảnh chụp màn hình bên dưới. Thanh tiêu đề có khả năng thích ứng hoàn toàn. Khi bạn đổi kích thước cửa sổ PWA, thanh tiêu đề sẽ phản ứng như thể được tạo thành từ nội dung HTML thông thường.

Xác định những phần nào của thanh tiêu đề có thể kéo
Mặc dù ảnh chụp màn hình ở trên cho thấy bạn đã hoàn tất, nhưng bạn vẫn chưa hoàn tất. Cửa sổ PWA không còn kéo được nữa (ngoài một vùng rất nhỏ), vì các nút điều khiển cửa sổ không phải là vùng kéo và phần còn lại của thanh tiêu đề bao gồm tiện ích tìm kiếm. Khắc phục vấn đề này bằng cách sử dụng thuộc tính CSS app-region
với giá trị drag
. Trong trường hợp cụ thể, bạn có thể kéo mọi thứ ngoài phần tử input
.
/* The entire search `div` is draggable… */
.search {
-webkit-app-region: drag;
app-region: drag;
}
/* …except for the `input`. */
input {
-webkit-app-region: no-drag;
app-region: no-drag;
}
Khi có CSS này, người dùng có thể kéo cửa sổ ứng dụng như bình thường bằng cách kéo div
, img
hoặc label
. Chỉ phần tử input
là có thể tương tác để bạn có thể nhập cụm từ tìm kiếm.
Phát hiện đối tượng
Bạn có thể phát hiện tính năng hỗ trợ Lớp phủ chế độ điều khiển cửa sổ bằng cách kiểm tra sự tồn tại của windowControlsOverlay
:
if ('windowControlsOverlay' in navigator) {
// Window Controls Overlay is supported.
}
Truy vấn vùng điều khiển cửa sổ bằng windowControlsOverlay
Đoạn mã cho đến nay có một vấn đề: trên một số nền tảng, các nút điều khiển cửa sổ nằm ở bên phải, trên các nền tảng khác thì nằm ở bên trái. Tệ hơn nữa, trình đơn Chrome "ba dấu chấm" cũng sẽ thay đổi vị trí tuỳ theo nền tảng. Điều này có nghĩa là hình nền có hiệu ứng chuyển màu tuyến tính cần được điều chỉnh linh hoạt để chạy từ #131313
→maroon
hoặc maroon
→#131313
→maroon
, sao cho hình nền này hoà vào màu nền maroon
của thanh tiêu đề do <meta name="theme-color" content="maroon">
xác định. Bạn có thể thực hiện việc này bằng cách truy vấn API getTitlebarAreaRect()
trên thuộc tính navigator.windowControlsOverlay
.
if ('windowControlsOverlay' in navigator) {
const { x } = navigator.windowControlsOverlay.getTitlebarAreaRect();
// Window controls are on the right (like on Windows).
// Chrome menu is left of the window controls.
// [ windowControlsOverlay___________________ […] [_] [■] [X] ]
if (x === 0) {
div.classList.add('search-controls-right');
}
// Window controls are on the left (like on macOS).
// Chrome menu is right of the window controls overlay.
// [ [X] [_] [■] ___________________windowControlsOverlay [⋮] ]
else {
div.classList.add('search-controls-left');
}
} else {
// When running in a non-supporting browser tab.
div.classList.add('search-controls-right');
}
Thay vì có hình nền trong các quy tắc CSS của lớp .search
(như trước đây), mã đã sửa đổi hiện sử dụng 2 lớp mà mã ở trên đặt động.
/* For macOS: */
.search-controls-left {
background-image: linear-gradient(90deg, #36c, 45%, #131313, 90%, #36c);
}
/* For Windows: */
.search-controls-right {
background-image: linear-gradient(90deg, #36c, #131313, 33%, #36c);
}
Xác định xem lớp phủ chế độ điều khiển cửa sổ có hiển thị hay không
Lớp phủ chế độ điều khiển cửa sổ sẽ không xuất hiện trong vùng thanh tiêu đề trong mọi trường hợp. Mặc dù không xuất hiện trên các trình duyệt không hỗ trợ tính năng Lớp phủ điều khiển cửa sổ, nhưng nút này cũng sẽ không xuất hiện khi PWA đang đề cập chạy trong một thẻ. Để phát hiện trường hợp này, bạn có thể truy vấn thuộc tính visible
của windowControlsOverlay
:
if (navigator.windowControlsOverlay.visible) {
// The window controls overlay is visible in the title bar area.
}
Ngoài ra, bạn cũng có thể sử dụng truy vấn nội dung nghe nhìn display-mode
trong JavaScript và/hoặc CSS:
// Create the query list.
const mediaQueryList = window.matchMedia('(display-mode: window-controls-overlay)');
// Define a callback function for the event listener.
function handleDisplayModeChange(mql) {
// React on display mode changes.
}
// Run the display mode change handler once.
handleDisplayChange(mediaQueryList);
// Add the callback function as a listener to the query list.
mediaQueryList.addEventListener('change', handleDisplayModeChange);
@media (display-mode: window-controls-overlay) {
/* React on display mode changes. */
}
Nhận thông báo về các thay đổi về hình học
Việc truy vấn vùng lớp phủ chế độ điều khiển cửa sổ bằng getTitlebarAreaRect()
có thể đủ cho những việc chỉ cần thực hiện một lần như đặt hình nền chính xác dựa trên vị trí của chế độ điều khiển cửa sổ, nhưng trong các trường hợp khác, bạn cần kiểm soát chi tiết hơn. Ví dụ: một trường hợp sử dụng có thể là điều chỉnh lớp phủ chế độ điều khiển cửa sổ dựa trên không gian có sẵn và thêm một câu đùa ngay trong lớp phủ chế độ điều khiển cửa sổ khi có đủ không gian.

Bạn có thể nhận được thông báo về các thay đổi về hình học bằng cách đăng ký navigator.windowControlsOverlay.ongeometrychange
hoặc bằng cách thiết lập một trình nghe sự kiện cho sự kiện geometrychange
. Sự kiện này sẽ chỉ kích hoạt khi lớp phủ điều khiển cửa sổ hiển thị, tức là khi navigator.windowControlsOverlay.visible
là true
.
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
if ('windowControlsOverlay' in navigator) {
navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
span.hidden = e.titlebarAreaRect.width < 800;
}, 250);
}
Thay vì chỉ định một hàm cho ongeometrychange
, bạn cũng có thể thêm một trình nghe sự kiện vào windowControlsOverlay
như bên dưới. Bạn có thể đọc về sự khác biệt giữa hai loại này trên MDN.
navigator.windowControlsOverlay.addEventListener(
'geometrychange',
debounce((e) => {
span.hidden = e.titlebarAreaRect.width < 800;
}, 250),
);
Khả năng tương thích khi chạy trong thẻ và trên các trình duyệt không được hỗ trợ
Có 2 trường hợp có thể xảy ra mà bạn cần cân nhắc:
- Trường hợp ứng dụng đang chạy trong một trình duyệt hỗ trợ Lớp phủ chế độ điều khiển cửa sổ, nhưng ứng dụng được dùng trong một thẻ trình duyệt.
- Trường hợp ứng dụng đang chạy trong một trình duyệt không hỗ trợ Lớp phủ chế độ điều khiển cửa sổ.
Trong cả hai trường hợp, theo mặc định, HTML được tạo cho lớp phủ điều khiển cửa sổ sẽ hiển thị nội tuyến như nội dung HTML thông thường và các giá trị dự phòng của biến env()
sẽ có hiệu lực cho việc định vị. Trên các trình duyệt được hỗ trợ, bạn cũng có thể quyết định không hiển thị HTML được chỉ định cho lớp phủ điều khiển cửa sổ bằng cách kiểm tra thuộc tính visible
của lớp phủ. Nếu thuộc tính này báo cáo false
, thì bạn có thể ẩn nội dung HTML đó.

Xin lưu ý rằng các trình duyệt không hỗ trợ sẽ không xem xét thuộc tính tệp kê khai ứng dụng web "display_override"
hoặc không nhận dạng "window-controls-overlay"
và do đó sử dụng giá trị có thể có tiếp theo theo chuỗi dự phòng, ví dụ: "standalone"
.

Những điều cần cân nhắc về giao diện người dùng
Mặc dù có thể hấp dẫn, nhưng bạn không nên tạo trình đơn thả xuống thông thường trong vùng Lớp phủ điều khiển cửa sổ. Làm như vậy sẽ vi phạm nguyên tắc thiết kế trên macOS, một nền tảng mà người dùng mong đợi các thanh trình đơn (cả thanh do hệ thống cung cấp và thanh tuỳ chỉnh) ở đầu màn hình.
Nếu ứng dụng của bạn mang đến trải nghiệm toàn màn hình, hãy cân nhắc kỹ xem có nên đưa Lớp phủ điều khiển cửa sổ vào chế độ xem toàn màn hình hay không. Có thể bạn muốn sắp xếp lại bố cục khi sự kiện onfullscreenchange
kích hoạt.
Bản minh hoạ
Tôi đã tạo một bản minh hoạ mà bạn có thể chơi trong nhiều trình duyệt hỗ trợ và không hỗ trợ, cũng như ở trạng thái đã cài đặt và chưa cài đặt. Để có trải nghiệm thực tế với Lớp phủ điều khiển cửa sổ, bạn cần cài đặt ứng dụng. Bạn có thể xem hai ảnh chụp màn hình về những gì bạn có thể mong đợi bên dưới. Mã nguồn của ứng dụng có trên Glitch.

Tính năng tìm kiếm trong lớp phủ chế độ điều khiển cửa sổ hoạt động đầy đủ:

Lưu ý về bảo mật
Nhóm Chromium đã thiết kế và triển khai API Lớp phủ chế độ điều khiển cửa sổ dựa trên các nguyên tắc cốt lõi được xác định trong Kiểm soát quyền truy cập vào các tính năng mạnh mẽ của nền tảng web, bao gồm quyền kiểm soát của người dùng, tính minh bạch và tính công thái học.
Giả mạo
Việc cho phép các trang web kiểm soát một phần thanh tiêu đề sẽ tạo điều kiện cho nhà phát triển giả mạo nội dung trong khu vực trước đây do trình duyệt kiểm soát và được tin cậy. Hiện tại, trong các trình duyệt Chromium, chế độ độc lập có một thanh tiêu đề. Khi khởi chạy lần đầu, thanh này sẽ hiển thị tiêu đề của trang web ở bên trái và nguồn gốc của trang ở bên phải (tiếp theo là nút "cài đặt và tuỳ chọn khác" cùng các nút điều khiển cửa sổ). Sau vài giây, văn bản gốc sẽ biến mất. Nếu trình duyệt được đặt thành ngôn ngữ từ phải sang trái (RTL), bố cục này sẽ bị đảo ngược sao cho văn bản gốc nằm ở bên trái. Thao tác này sẽ mở lớp phủ các nút điều khiển cửa sổ để giả mạo nguồn nếu không có đủ khoảng đệm giữa nguồn và cạnh phải của lớp phủ. Ví dụ: nguồn "evil.ltd" có thể được thêm vào một trang web đáng tin cậy "google.com", khiến người dùng tin rằng nguồn này đáng tin cậy. Kế hoạch là giữ nguyên văn bản gốc này để người dùng biết nguồn gốc của ứng dụng và có thể đảm bảo rằng nguồn gốc đó đáp ứng được kỳ vọng của họ. Đối với các trình duyệt được định cấu hình theo hướng từ phải sang trái, phải có đủ khoảng đệm ở bên phải văn bản gốc để ngăn một trang web độc hại nối nguồn gốc không an toàn với một nguồn gốc đáng tin cậy.
Tạo vân tay số
Việc bật lớp phủ chế độ điều khiển cửa sổ và các vùng có thể kéo không gây ra những lo ngại đáng kể về quyền riêng tư ngoài việc phát hiện tính năng. Tuy nhiên, do kích thước và vị trí khác nhau của các nút điều khiển cửa sổ trên các hệ điều hành, phương thức navigator.
sẽ trả về một DOMRect
có vị trí và kích thước cho biết thông tin về hệ điều hành mà trình duyệt đang chạy. Hiện tại, nhà phát triển có thể phát hiện hệ điều hành từ chuỗi tác nhân người dùng, nhưng do lo ngại về việc lấy dấu vân tay, nên có cuộc thảo luận về việc đóng băng chuỗi UA và hợp nhất các phiên bản hệ điều hành. Cộng đồng trình duyệt đang nỗ lực tìm hiểu tần suất thay đổi kích thước của lớp phủ điều khiển cửa sổ trên các nền tảng, vì giả định hiện tại là các lớp phủ này khá ổn định trên các phiên bản hệ điều hành và do đó sẽ không hữu ích cho việc quan sát các phiên bản hệ điều hành phụ. Mặc dù đây là một vấn đề tiềm ẩn về việc nhận dạng thiết bị, nhưng vấn đề này chỉ áp dụng cho các PWA đã cài đặt sử dụng tính năng thanh tiêu đề tuỳ chỉnh và không áp dụng cho việc sử dụng trình duyệt nói chung. Ngoài ra, API navigator.
sẽ không dùng được cho iframe được nhúng bên trong một PWA.
Di chuyển
Việc chuyển đến một nguồn gốc khác trong PWA sẽ khiến PWA quay lại thanh tiêu đề độc lập thông thường, ngay cả khi PWA đáp ứng các tiêu chí nêu trên và được khởi chạy bằng lớp phủ chế độ điều khiển cửa sổ. Điều này là để điều chỉnh thanh màu đen xuất hiện trên thanh điều hướng khi chuyển đến một nguồn gốc khác. Sau khi quay lại nguồn gốc ban đầu, lớp phủ điều khiển cửa sổ sẽ được dùng lại.

Phản hồi
Nhóm Chromium muốn biết trải nghiệm của bạn khi sử dụng Window Controls Overlay API.
Hãy cho chúng tôi biết về thiết kế API
Có vấn đề gì về API khiến bạn không hài lòng không? Hoặc có phương thức hay thuộc tính nào bị thiếu mà bạn cần triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc bình luận về mô hình bảo mật? Báo cáo vấn đề về quy cách trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một vấn đề hiện có.
Báo cáo vấn đề về việc triển khai
Bạn có phát hiện thấy lỗi trong quá trình triển khai Chromium không? Hoặc việc triển khai có khác với quy cách không?
Báo cáo lỗi tại new.crbug.com. Nhớ cung cấp càng nhiều thông tin chi tiết càng tốt, hướng dẫn đơn giản để tái hiện và nhập UI>Browser>WebAppInstalls
vào hộp Thành phần.
Thể hiện sự ủng hộ đối với API
Bạn có dự định sử dụng Window Controls Overlay API không? Sự ủng hộ công khai của bạn giúp nhóm Chromium ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác thấy tầm quan trọng của việc hỗ trợ các tính năng này.
Gửi một tweet đến @ChromiumDev kèm theo thẻ bắt đầu bằng #WindowControlsOverlay
và cho chúng tôi biết bạn đang sử dụng tính năng này ở đâu và như thế nào.
Đường liên kết hữu ích
- Thông tin giải thích
- Bản nháp quy cách kỹ thuật
- Lỗi Chromium
- Mục trạng thái của nền tảng Chrome
- Bài đánh giá về TAG
- Tài liệu liên quan của Microsoft Edge
Lời cảm ơn
Lớp phủ chế độ điều khiển cửa sổ được Amanda Baker trong nhóm Microsoft Edge triển khai và chỉ định. Bài viết này được Joe Medley và Kenneth Rohde Christiansen xem xét. Hình ảnh chính của Sigmund trên Unsplash.