說明如何建構自動調整且可存取的浮動式訊息元件的基本總覽。
在這篇文章中,我想分享如何建構浮動式訊息元件。立即試用示範。
如果你偏好使用影片,也可以觀看這篇 YouTube 文章:
總覽
浮動式訊息是對使用者的非互動式、被動和非同步簡短訊息。一般而言,這些方塊可做為介面回饋模式,向使用者告知動作結果。
互動
浮動式訊息與通知不同,快訊和提示由於不互動,因此不能關閉或保留。通知是用來提供更為重要資訊、需要互動的同步訊息,或是系統層級的訊息 (而非頁面層級)。浮動式訊息比其他通知策略更加被動。
標記
<output>
元素是不錯的浮動式訊息選擇,因為系統會向螢幕閱讀器朗讀該元素。正確的 HTML 可為我們提供安全的 JavaScript 和 CSS 增強基礎,並會有許多 JavaScript 供您使用。
浮動式訊息
<output class="gui-toast">Item added to cart</output>
加入 role="status"
就能更多元包容。如果瀏覽器並未根據規格將隱含角色授予 <output>
元素,這可以提供備用選項。
<output role="status" class="gui-toast">Item added to cart</output>
浮動式訊息容器
一次可以顯示多個浮動式訊息。為了自動化調度管理多個浮動式訊息,請使用容器。這個容器也會處理浮動式訊息在螢幕上的位置。
<section class="gui-toast-group">
<output role="status">Wizard Rose added to cart</output>
<output role="status">Self Watering Pot added to cart</output>
</section>
版面配置
我選擇將浮動式訊息固定至可視區域的 inset-block-end
,如果新增更多浮動式訊息,就表示這些浮動式訊息從螢幕邊緣堆疊。
GUI 容器
浮動式訊息容器會執行所有版面配置的呈現作業,用來顯示浮動式訊息。這是可視區域的 fixed
,並使用邏輯屬性 inset
指定要固定的邊緣,以及距離相同 block-end
邊緣些許的 padding
。
.gui-toast-group {
position: fixed;
z-index: 1;
inset-block-end: 0;
inset-inline: 0;
padding-block-end: 5vh;
}
除了定位在可視區域中本身外,浮動式訊息容器是一種格線容器,可對齊及發布浮動式訊息。項目會以 justify-content
群組為中心,並分別以 justify-items
置中。稍微丟棄 gap
,這樣浮動式訊息就不會碰到。
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
統一發票
個別的浮動式訊息具有一些 padding
、border-radius
和 min()
函式,以配合行動裝置和電腦的大小調整。下列 CSS 中的回應式大小可避免浮動式訊息成長超過 90% 的可視區域或 25ch
。
.gui-toast {
max-inline-size: min(25ch, 90vw);
padding-block: .5ch;
padding-inline: 1ch;
border-radius: 3px;
font-size: 1rem;
}
風格
設定版面配置和位置後,請新增 CSS,以便配合使用者的設定和互動進行調整。
浮動式訊息容器
浮動式訊息沒有互動,輕觸或滑動浮動式訊息不會執行任何動作,但目前會耗用指標事件。利用下列 CSS 防止浮動式訊息竊取點擊。
.gui-toast-group {
pointer-events: none;
}
統一發票
利用自訂屬性、HSL 和偏好設定媒體查詢,為浮動式訊息提供淺色或深色的自動調整主題。
.gui-toast {
--_bg-lightness: 90%;
color: black;
background: hsl(0 0% var(--_bg-lightness) / 90%);
}
@media (prefers-color-scheme: dark) {
.gui-toast {
color: white;
--_bg-lightness: 20%;
}
}
動畫
新的浮動式訊息應會在進入螢幕畫面時以動畫形式顯示。透過將 translate
值預設為 0
,即可在動作偏好設定媒體查詢中將動態值更新為長度,藉此調整減速動作。每人都得到動畫,但只有部分使用者能夠移動浮動式訊息的距離。
以下是用於浮動式訊息動畫的主要畫面格。CSS 會在單一動畫中控制進入、等待及結束浮動式訊息的事宜。
@keyframes fade-in {
from { opacity: 0 }
}
@keyframes fade-out {
to { opacity: 0 }
}
@keyframes slide-in {
from { transform: translateY(var(--_travel-distance, 10px)) }
}
接著,浮動式訊息元素會設定變數,並自動化調度管理主要畫面格。
.gui-toast {
--_duration: 3s;
--_travel-distance: 0;
will-change: transform;
animation:
fade-in .3s ease,
slide-in .3s ease,
fade-out .3s ease var(--_duration);
}
@media (prefers-reduced-motion: no-preference) {
.gui-toast {
--_travel-distance: 5vh;
}
}
JavaScript
當樣式和螢幕閱讀器具有可存取的 HTML 後,JavaScript 才能根據使用者事件協調浮動式訊息的建立、新增和刪除作業。浮動式訊息元件的開發人員體驗應盡可能精簡且易於上手,如下所示:
import Toast from './toast.js'
Toast('My first toast')
建立浮動式訊息群組和浮動式訊息
從 JavaScript 載入浮動式訊息模組時,必須建立浮動式訊息容器,並新增至頁面中。我選擇在 body
之前新增元素,這會導致 z-index
堆疊問題發生,因為容器位於所有主體元素的容器上方。
const init = () => {
const node = document.createElement('section')
node.classList.add('gui-toast-group')
document.firstElementChild.insertBefore(node, document.body)
return node
}
系統會在模組內部呼叫 init()
函式,將元素儲存為 Toaster
:
const Toaster = init()
建立浮動式訊息 HTML 元素是透過 createToast()
函式完成。該函式需要一些文字做為浮動式訊息、建立 <output>
元素、為其搭配一些類別和屬性,並設定文字,並傳回節點。
const createToast = text => {
const node = document.createElement('output')
node.innerText = text
node.classList.add('gui-toast')
node.setAttribute('role', 'status')
return node
}
管理一或多個浮動式訊息
JavaScript 現在會為包含浮動式訊息的文件新增容器,並準備好新增已建立的浮動式訊息。addToast()
函式會自動化調度管理處理一或多個浮動式訊息。首先,檢查浮動式訊息的數量及運動情況是否合適,然後利用這項資訊附加浮動式訊息或執行一些精美的動畫,讓其他浮動式訊息出現在「釋出空間」來供新浮動式訊息使用。
const addToast = toast => {
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Toaster.children.length && motionOK
? flipToast(toast)
: Toaster.appendChild(toast)
}
新增第一個浮動式訊息時,Toaster.appendChild(toast)
會在觸發 CSS 動畫的頁面中加入浮動式訊息:建立動畫、等待 3s
、顯示動畫。在有浮動式訊息時,系統會呼叫 flipToast()
,採用 Paul Lewis 所稱的 FLIP 技術。這個做法的目的是計算容器在新浮動式訊息之前、之後的位置差異。不妨試著找出
const flipToast = toast => {
// FIRST
const first = Toaster.offsetHeight
// add new child to change container size
Toaster.appendChild(toast)
// LAST
const last = Toaster.offsetHeight
// INVERT
const invert = last - first
// PLAY
const animation = Toaster.animate([
{ transform: `translateY(${invert}px)` },
{ transform: 'translateY(0)' }
], {
duration: 150,
easing: 'ease-out',
})
}
CSS 格線會執行版面配置的提升作業。新增浮動式訊息時,格線會將其置於開頭,並與其他浮動式訊息保持空格。與此同時,網頁動畫則會從舊位置建立容器動畫。
將所有 JavaScript
呼叫 Toast('my first toast')
時,系統會建立浮動式訊息並新增至頁面 (可能甚至會為了配合新浮動式訊息而建立動畫),並傳回「承諾」,且系統會「觀看」建立的浮動式訊息,以實現預期的解析度。
const Toast = text => {
let toast = createToast(text)
addToast(toast)
return new Promise(async (resolve, reject) => {
await Promise.allSettled(
toast.getAnimations().map(animation =>
animation.finished
)
)
Toaster.removeChild(toast)
resolve()
})
}
我發現程式碼在 Promise.allSettled()
函式和 toast.getAnimations()
對應中造成混淆。由於我針對浮動式訊息使用多個主要畫面格動畫,因此想很確定所有動畫都已完成,則必須向 JavaScript 要求每個動畫,以及每個觀察到的 finished
承諾完成。allSettled
會為我們提供服務,一旦完成所有承諾,即可自行解決。使用 await Promise.allSettled()
表示下一行程式碼可以放心移除元素,並假設浮動式訊息已完成其生命週期。最後,呼叫 resolve()
可完成高階浮動式訊息的承諾,讓開發人員可在浮動式訊息顯示後清除或執行其他工作。
export default Toast
最後,Toast
函式是從模組匯出,供其他指令碼匯入及使用。
使用浮動式訊息元件
使用浮動式訊息或浮動式訊息的開發人員體驗是透過匯入 Toast
函式,並以訊息字串呼叫的方式完成。
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
如果開發人員想要清除工作或任何事項,在浮動式訊息顯示後,就能使用非同步和「await」await功能。
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
結語
現在既然你已經知道我怎麼做,你會怎麼做‽ 🙂?
讓我們帶您更多元的方法,並瞭解運用網路打造網站的所有方式。 請建立示範並透過 Twitter 推文連結,我就能將這項工具新增至下方的「社群重混」部分!
社群重混作品
- 透過 HTML/CSS/JS 使用 @_developit:示範和程式碼
- 使用 HTML/CSS/JS 搭配 Joost van der Schee:示範和程式碼