開放式 UI 計畫的目標是協助開發人員輕鬆提供優質使用者體驗。為此,我們試圖處理開發人員遇到的更多問題模式。實際做法是提供更完善的平台內建 API 和元件。
彈出式視窗之一就是彈出式視窗,在「開啟使用者介面」中描述為「彈出式視窗」。
彈出式視窗長期以來都相當惡名昭彰。部分原因是他們建構和部署應用程式的方式。要營造良好形象,這並不容易,但只要將使用者帶往特定內容,或是讓他們注意到網站上的內容 (尤其是在展現巧思的情況下使用),就可以帶來大量價值。
建立彈出式視窗時,通常需要兩個主要疑慮:
- 如何確保放在適當位置的排名。
- 如何提升易用性 (鍵盤好上手、可聚焦等)。
內建的 Popover API 提供了各種目標,且全部的目標都一樣,是讓開發人員能輕鬆建構這種模式。這些目標包括:
- 讓使用者能夠輕鬆在文件的其他部分上方顯示元素及其子系。
- 打造無障礙環境
- 無須針對常見行為 (淺色關閉、單例模式、堆疊等) 要求 JavaScript。
如要查看彈出式視窗的完整規格,請造訪 OpenUI 網站。
瀏覽器相容性
您現在可以在哪裡使用內建的 Popover API?這是 Chrome Canary 的「實驗性網頁平台功能」後端提供的功能旗標。
如要啟用該標記,請開啟 Chrome Canary 並前往 chrome://flags
。然後啟用「實驗性網頁平台功能」旗標。
開發人員如果想在正式環境中測試這項功能,可以選擇來源試用。
最後,該 API 還在開發階段中有一個 polyfill。請務必前往 github.com/oddbird/popup-polyfill 查看存放區。
您可以使用下列指令,檢查是否有彈出式視窗支援:
const supported = HTMLElement.prototype.hasOwnProperty("popover");
目前的解決方案
下列何者是你目前的內容宣傳方式?如果您的瀏覽器支援這項功能,您就可以使用 HTML 對話方塊元素。您必須在「Modal」中使用表單中要求的資訊。而且必須搭配 JavaScript 才能使用。
Dialog.showModal();
關於無障礙功能的考量,舉例來說,如要為 Safari 15.4 以下版本的使用者提供服務,建議使用 a11y-dialog。
此外,您也可以使用眾多採用彈出式視窗、快訊或工具提示等格式的程式庫。其中有許多工具的運作方式大致相同。
- 將一些容器附加至內文,以顯示彈出式視窗。
- 調整樣式,使其高於所有其他項目。
- 建立元素並附加到容器,即可顯示彈出式視窗。
- 從 DOM 移除彈出元素即可隱藏該元素。
這需要額外的依附元件以及開發人員的更多決定。您也需要進行研究,找到可提供您一切需求的方案。Popover API 的宗旨是滿足許多使用情境 (包括工具提示) 的需求。目標是涵蓋所有常見情境,讓開發人員不必做出另一項決定,可以專心打造體驗。
您的第一個彈出式視窗
這樣就大功告成了。
<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>
但這裡是怎樣的呢?
- 您不用將彈出式視窗元素放入容器或任何內容內,系統預設會隱藏該元素。
- 無須撰寫任何 JavaScript 就能顯示。該操作會由
popovertoggletarget
屬性處理。 - 顯示時就會提升至頂層圖層。這表示在可視區域中處於
document
的上方。您無需管理z-index
,也無需擔心彈出式視窗在 DOM 中的位置。它可能會在 DOM 的巢狀結構下,加上裁切上階。您也可以透過開發人員工具查看目前位於頂層的元素。如要進一步瞭解頂層,請參閱這篇文章。
- 你會看到「淺色關閉」立即使用也就是說,您可以使用關閉信號關閉彈出式視窗,例如點選彈出式視窗以外的位置、使用鍵盤導覽至其他元素,或是按下 Esc 鍵。重新開啟應用程式並試用!
彈出式視窗還有什麼好處?讓我們進一步舉例說明。不妨透過這個示範頁面瞭解頁面部分內容。
該懸浮動作按鈕的固定位置具有較高的 z-index
值。
.fab {
position: fixed;
z-index: 99999;
}
彈出內容是在 DOM 的巢狀結構中,但當您開啟彈出式視窗時,該視窗會優先於該固定位置元素上方進行升級。您不需要設定任何樣式。
您可能也會發現,彈出式視窗現在有 ::backdrop
虛擬元素。頂層圖層中的所有元素都會取得樣式化的 ::backdrop
虛擬元素。此範例採用較低的 Alpha 背景顏色和背景篩選器的 ::backdrop
樣式,將基本內容模糊處理。
設定彈出式視窗樣式
現在讓我們專注在設定彈出式視窗的樣式。根據預設,彈出式視窗具有固定位置,有些則套用了邊框間距。也提供 display: none
。您可以覆寫此設定,顯示彈出式視窗。但頂層架構並不會升到最上層。
[popover] { display: block; }
無論您以何種方式宣傳彈出式視窗,一旦將彈出式視窗推送到上層,您可能需要將其放置在上層或放置其位置。但無法指定頂層圖層
:open {
display: grid;
place-items: center;
}
根據預設,系統會使用 margin: auto
在可視區域中央安排彈出式視窗。但在某些情況下,您可能會想更明確的定位。例如:
[popover] {
top: 50%;
left: 50%;
translate: -50%;
}
如果您想使用 CSS 格線或 Flexbox 在彈出式視窗中安排內容的版面配置,非常適合納入此元素。否則,您需要宣告另外一項規則,當彈出式視窗出現在頂層圖層時,變更 display
。根據預設,如果設為顯示這個欄位,則預設會覆寫 display: none
。
[popover]:open {
display: flex;
}
如果您嘗試切換範例,就會看到彈出式視窗進入和轉出作業。您可以使用 :open
虛擬選取器,將彈出式視窗移入和移出。:open
虛擬選取器會與顯示 (因此位於頂層) 的彈出式視窗相符。
本範例使用自訂屬性來推動轉換。也可以將轉場效果套用至彈出式視窗的 ::backdrop
。
[popover] {
--hide: 1;
transition: transform 0.2s;
transform: translateY(calc(var(--hide) * -100vh))
scale(calc(1 - var(--hide)));
}
[popover]::backdrop {
transition: opacity 0.2s;
opacity: calc(1 - var(--hide, 1));
}
[popover]:open::backdrop {
--hide: 0;
}
提示是將媒體查詢中的轉場效果和動畫分組。這也有助於維持時間。這是因為您無法透過自訂屬性在 popover
和 ::backdrop
之間共用值。
@media(prefers-reduced-motion: no-preference) {
[popover] { transition: transform 0.2s; }
[popover]::backdrop { transition: opacity 0.2s; }
}
到目前為止,您已經看過使用 popovertoggletarget
顯示彈出式視窗。如要關閉,請使用「淺色關閉」。不過,您還是能使用 popovershowtarget
和 popoverhidetarget
屬性。將在彈出式視窗中新增可隱藏按鈕的按鈕,並將切換鈕變更為使用 popovershowtarget
。
<div id="code-popover" popover>
<button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>
如前所述,Popover API 不僅涵蓋我們過去對彈出式視窗的看法,這種架構適用於所有類型的情境,例如通知、選單、工具提示等。
有些情況需要不同的互動模式。懸停等互動。popoverhovertarget
屬性的使用經過實驗,但目前尚未導入。
<div popoverhovertarget="hover-popover">Hover for Code</div>
將滑鼠遊標停在元素上即可顯示目標。您可以透過 CSS 屬性設定這項行為。這些 CSS 屬性會定義從彈出式視窗回應元素上,懸停在元素上和結束的時間範圍。實驗的預設行為是彈出的 :hover
之後顯示了彈出式視窗。0.5s
這時你需要關閉閃光燈,或需要開啟另一個彈出式視窗才能關閉 (稍後會進一步說明)。這是因為彈出式視窗隱藏時間設為 Infinity
。
在此同時,您可以使用 JavaScript 來 polyfill 這項功能。
let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
const popover = document.querySelector(
`#${trigger.getAttribute("popoverhovertarget")}`
);
trigger.addEventListener("pointerenter", () => {
hoverTimer = setTimeout(() => {
if (!popover.matches(":open")) popover.showPopOver();
}, 500);
trigger.addEventListener("pointerleave", tearDown);
});
});
設定明確懸停視窗的好處是可確保使用者的操作是有意執行 (例如使用者將指標傳遞至目標)。我們不會刻意顯示彈出式視窗,除非這是特意。
您可以立即試用這項示範,並將視窗懸停在目標上,並將視窗設為 0.5s
。
在探索一些常見用途與範例前,讓我們先看看一些內容。
彈出式視窗類型
我們說明瞭非 JavaScript 互動行為。不過,就整個彈出行為而言,如果不想要「光源關閉」,該怎麼辦?還是想要對彈出式視窗套用單例模式?
Popover API 可讓您指定三種不同行為的彈出式視窗。
[popover=auto]/[popover]
:
- 建立巢狀結構支援。這不代表 DOM 中的巢狀結構也包含在內。祖系彈出式視窗的定義如下:
- 與 DOM 位置 (子項) 建立關聯
- 例如觸發
popovertoggletarget
、popovershowtarget
等子元素的屬性。 - 與
anchor
屬性有關 (在開發中的 CSS Anchoring API 中)。
- 光線關閉。
- 開啟後,系統會關閉其他非祖系彈出式視窗的彈出式視窗。觀看下方的示範影片,瞭解如何運用祖系彈出式視窗建立巢狀結構。瞭解如何將部分
popoverhidetarget
/popovershowtarget
執行個體變更為popovertoggletarget
,藉此變更各種操作。 - 淺色關閉一個項目會全部關閉,但針對堆疊中的某一個關閉,則只會關閉堆疊中位於上方的項目。
[popover=manual]
:
- 不會關閉其他彈出式視窗。
- 沒有光源關閉。
- 必須透過觸發條件元素或 JavaScript 明確關閉。
JavaScript API
如果您需要進一步控制彈出式視窗,可以使用 JavaScript 處理。您同時會取得 showPopover
和 hidePopover
方法。您還有 popovershow
和 popoverhide
事件可監聽:
顯示彈出式視窗
js
popoverElement.showPopover()
隱藏彈出式視窗:
popoverElement.hidePopover()
監聽系統顯示的彈出式視窗:
popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)
監聽系統顯示的彈出式視窗,並取消當中顯示:
popoverElement.addEventListener('popovershow',event => {
event.preventDefault();
console.warn(‘We blocked a popover from being shown’);
})
監聽隱藏的彈出式視窗:
popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)
你無法取消隱藏的彈出式視窗:
popoverElement.addEventListener('popoverhide',event => {
event.preventDefault();
console.warn("You aren't allowed to cancel the hiding of a popover");
})
檢查彈出式視窗是否位於頂層:
popoverElement.matches(':open')
為一些較不常見的情境,提供額外電力。例如在閒置一段時間後顯示彈出式視窗。
此示範影片內含有聲音的彈出式視窗,因此我們需要 JavaScript 才能播放音訊。點擊時,我們會隱藏彈出式視窗,接著播放音訊,然後再顯示一次。
無障礙設定
無障礙中心是 Popover API 的最前線,無障礙功能對應會視需要將彈出式視窗與觸發條件元素建立關聯。這表示假設您使用 popovertoggletarget
等觸發屬性的其中一項觸發屬性,則無須宣告 aria-haspopup
等 aria-*
屬性。
如要管理焦點,你可以使用自動對焦屬性,將焦點移至彈出式視窗中的元素。這與對話方塊相同,但返回焦點時會出現差異,這是因為系統會關閉光線。在大多數情況下,關閉彈出式視窗後,焦點會回到先前聚焦的元素。但焦點會移至淺色關閉上所點選的元素 (如果可以聚焦)。請參閱說明文件的焦點管理相關章節。
您需要開啟「全螢幕版本」看看效果如何
在本示範中,聚焦的元素會顯示綠色外框。試試看用鍵盤在介面各處分頁。請注意當彈出式視窗關閉時,焦點會傳回的位置。你可能也會發現,如果按下 Tab 鍵,彈出式視窗就會關閉。這是刻意安排的雖然彈出式視窗可以管理焦點,但並不代表焦點。當焦點從彈出式視窗移出時,鍵盤導覽功能會找出關閉訊號。
錨定 (開發中)
就彈出視窗而言,有個難以因應的模式,就是將元素固定在觸發條件。舉例來說,假設將工具提示設為顯示在觸發程序上方,但文件遭捲動。工具提示可能會遭可視區域截斷。目前有可處理的 JavaScript 項目,例如「浮動式使用者介面」。他們會調整工具提示的位置,讓您停止這個情形,並且依賴想要的位置順序。
但是,我們希望您能用自己的樣式來定義。除了 Popover API 仍在開發階段,還提供一個輔助 API 可以解決此問題。「CSS 錨定位置」API 可讓您將元素與其他元素共用,而且這麼做會重新調整元素的位置,避免在可視區域遭到截斷。
此示範會使用處於目前狀態的 Anchoring API。船隻的位置會回應錨點在檢視點中的位置。
以下的 CSS 程式碼片段可讓這個範例順利運作。不需要 JavaScript。
.anchor {
--anchor-name: --anchor;
}
.anchored {
position: absolute;
position-fallback: --compass;
}
@position-fallback --compass {
@try {
bottom: anchor(--anchor top);
left: anchor(--anchor right);
}
@try {
top: anchor(--anchor bottom);
left: anchor(--anchor right);
}
}
您可以前往這裡查看規格資訊。這個 API 也會有 polyfill。
範例
現在你已瞭解彈出式視窗的功能與做法,以下將列舉一些例子。
通知
這個示範顯示「複製到剪貼簿」通知。
- 使用
[popover=manual]
。 - 操作中的彈出式視窗會顯示
showPopover
。 - 超過
2000ms
逾時後,請使用hidePopover
隱藏該值。
吐司
此示範使用頂層圖層顯示浮動式訊息樣式通知。
- 有一個類型為
manual
的彈出視窗可當做容器使用。 - 新通知會附加至彈出式視窗,並顯示彈出式視窗。
- 按下時則會透過網頁動畫 API 移除這些物件,並從 DOM 中移除。
- 如果沒有可顯示的浮動式訊息,系統會隱藏該彈出式視窗。
巢狀選單
這個範例說明巢狀導覽選單的運作方式。
- 請使用
[popover=auto]
,因為允許巢狀彈出式視窗。 - 在每個下拉式選單的第一個連結上使用
autofocus
,即可透過鍵盤瀏覽。 - 很適合使用 CSS Anchoring API。不過,在這個示範中,您可以使用少量 JavaScript 使用自訂屬性來更新位置。
const ANCHOR = (anchor, anchored) => () => {
const { top, bottom, left, right } = anchor.getBoundingClientRect();
anchored.style.setProperty("--top", top);
anchored.style.setProperty("--right", right);
anchored.style.setProperty("--bottom", bottom);
anchored.style.setProperty("--left", left);
};
PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));
請注意,由於此示範內容使用autofocus
,因此需要以「全螢幕檢視」開啟以及鍵盤瀏覽選項
媒體彈出式視窗
此示範是如何彈出媒體。
- 使用
[popover=auto]
關閉燈光。 - JavaScript 會監聽影片的
play
事件,並彈出影片。 - 彈出式視窗
popoverhide
事件會暫停影片。
威基風格流行樂
以下示範說明如何建立包含媒體的內嵌內容工具提示。
- 使用
[popover=auto]
。顯示其中一人不會遮住其他組織,因為他們不是祖先。 - 透過 JavaScript 顯示在
pointerenter
上。 - 另一個最適合使用 CSS Anchoring API 的方法。
導覽匣
這項示範會使用彈出式視窗建立導覽匣。
- 使用
[popover=auto]
關閉燈光。 - 使用
autofocus
將焦點移至第一個導覽項目。
管理背景幕
本示範示範如何管理多個彈出式視窗的背景,但這些彈出式視窗只需要顯示一個 ::backdrop
。
- 使用 JavaScript 來維護可見的彈出式視窗清單。
- 將類別名稱套用至頂端圖層最低的彈出。
自訂遊標彈出式視窗
本示範說明如何使用 popover
將 canvas
升級為頂層圖層,並用來顯示自訂遊標。
- 使用
showPopover
和[popover=manual]
將canvas
升級為頂層圖層。 - 其他彈出式視窗開啟時,請隱藏及顯示
canvas
彈出式視窗,確保它位於頂端。
操作單彈出式視窗
以下示範說明如何使用彈出式視窗做為操作單。
- 預設顯示彈出式視窗,覆寫
display
。 - 已透過彈出式視窗觸發條件開啟 Actionsheet。
- 此彈出式視窗顯示時,會升級為頂層圖層,並轉譯成檢視畫面。
- 光線關閉可用來返回。
鍵盤已啟用彈出式視窗
本示範示範如何將彈出式視窗用於指令調色盤樣式 UI。
- 使用 cmd + j 鍵顯示彈出式視窗。
input
目前聚焦於autofocus
。- 組合方塊是位於主要輸入欄位下方的第二個
popover
。 - 如果沒有下拉式選單,則光源關閉會關閉調色盤。
- Anchoring API 的另一個候選項目
計時性彈出式視窗
此示範顯示四秒後顯示閒置的彈出式視窗。通常用於保留使用者安全資訊的應用程式使用的 UI 模式,以顯示登出互動視窗。
- 使用 JavaScript 在閒置一段時間後顯示彈出式視窗。
- 在彈出式視窗顯示時重設計時器。
螢幕保護程式
與上一個示範類似,您可以在網站中加入螢幕保護程式,
- 使用 JavaScript 在閒置一段時間後顯示彈出式視窗。
- 關閉可隱藏及重設計時器。
文字追蹤
此示範是如何隨著輸入插入點接續的彈出式視窗。
- 根據選取項目、按鍵事件或特殊字元顯示彈出式視窗。
- 使用 JavaScript 以限定範圍的自訂屬性更新彈出位置。
- 因此必須考量內容的顯示和無障礙程度。
- 在文字編輯 UI 和應用程式中,通常用於標記。
懸浮動作按鈕選單
本示範示範如何使用彈出式視窗,在不使用 JavaScript 的情況下實作懸浮動作按鈕選單。
- 使用
showPopover
方法升級manual
類型彈出式視窗。這是主要按鈕。 - 選單是主按鈕目標的另一個彈出式視窗。
- 已開啟「
popovertoggletarget
」選單。 - 使用
autofocus
即可將焦點移至節目上的第一個選單項目。 - 光線關閉會關閉選單。
- 轉動圖示表示使用
:has()
。如要進一步瞭解:has()
,請參閱這篇文章。
這樣就大功告成了!
以上就是彈出式視窗簡介,這是「開放式 UI 計畫」中預計會加入的計畫。而且大概會因為使用這項技術,成為網路平台的絕佳利器。
請務必查看開啟 UI。彈出式視窗說明會隨著 API 發展持續更新。以下是所有示範的系列作品。
感謝你透過「彈出」訊息!
相片來源:Madison Oren,發表於 Unsplash 網站上