什麼時候按鈕不是按鈕?

已發表: 2022-03-10
快速總結↬並不是所有的圓形和突出的東西都被認為是一個按鈕。 在這篇文章中,Vadim 解釋瞭如何為您的用戶創建一個合適的交互式按鈕——一個不應該與其他任何東西混淆的按鈕。

假設您有用戶單擊界面的一部分並發生了一些事情。 對我來說,這聽起來像是一個按鈕,但我們暫時稱它為“可點擊的東西”。 我知道,你相信它也是一個按鈕:它是圓形的,並以漂亮的番茄色突出,要求與之交互。 但讓我們想一想。 從長遠來看,這會節省時間,我保證。

類似番茄按鈕的東西,上面有“Something”文字。
為您的“可點擊的東西”設計(大預覽)

如果這個可點擊的東西中的文本是“閱讀更多”,並且點擊它會將用戶引導到另一個頁面上的文章怎麼辦? 唔。 如果有一個帶藍色下劃線的單詞“關閉”來關閉彈出對話框怎麼辦? 它是一個鏈接,只是因為它是藍色的並帶有下劃線嗎? 當然不是。

類似按鈕的鏈接和類似鏈接的按鈕
鏈接或按鈕困境(大預覽)
跳躍後更多! 繼續往下看↓

哇! 似乎無法僅通過查看它來判斷它是鏈接還是按鈕。 太瘋狂了! 在選擇正確的元素之前,我們需要了解這個東西的作用。 但是,如果我們還不知道它在做什麼或者只是感到困惑怎麼辦? 好吧,我們有一個方便的流程圖:

流程圖:這是一個按鈕。如果不是,那麼它是一個鏈接。而已。
選擇正確元素的科學流程圖(大預覽)
  1. 這是一個按鈕。
  2. 如果沒有,那麼它是一個鏈接。
  3. 而已。

那麼,一切都是按鈕嗎? 不,但您總是可以從一個按鈕開始,用於幾乎任何可以以類似方式單擊或交互的元素。 如果它缺少某些東西,例如導航到另一個頁面,請改用鏈接。 不,指針不是使其成為<a href>的理由。 我們有cursor: pointer

帶有“某物”文字的重點番茄按鈕
不要忘記提供焦點樣式。 (大預覽)

好吧,這是一個<button>按鈕——我們同意這一點。 讓我們把它放在我們的模板中,並根據設計對其進行樣式設置:一些填充、舍入、番茄填充、白色文本,甚至一些焦點樣式。 哦,你真好。

 <button type="button" class="button"> Something </button> <style> .button { display: inline-block; padding: 10px 20px; border-radius: 20px; background-color: tomato; color: white; } .button:focus { outline: none; box-shadow: 0 0 0 5px #006AE3; } </style>

沒過多久。 您想快速構建它並吃點午餐,因為您餓了。 好的,讓我們看看它的外觀並開始吧。

在瀏覽器中呈現的醜陋的番茄按鈕
有時瀏覽器不是你最好的朋友。 (大預覽)

我的天啊! 瀏覽器有問題。 為什麼這個按鈕這麼難看? 文本很小,即使我們明確地將body設置為16px ,甚至font-family也是錯誤的。 帶有傻乎乎的偽陰影的圓形邊框是如此復古,以至於它甚至還不是一種趨勢。

啊,這是瀏覽器的默認樣式。 您需要小心地撤消它,甚至添加 Normalize.css 或 Reset.css……或者您可以只使用<div>並忘記它。 他們付錢給你的不是快速解決問題嗎? 你餓了,這根本沒有幫助。 但你是專業人士:振作起來思考。

<button><div>有什麼區別呢? 內置的<button>是一個交互元素,這意味著它可以被交互。 這很深。 你可以點擊它,你可以使用鍵盤專注於它,它還向屏幕閱讀器傳達了一個可訪問的button角色,讓用戶可以理解它是一個按鈕。

感人的! 您不僅了解 HTML 的<button>元素,而且還對 ARIA 和屏幕閱讀器支持有所了解。 您甚至可能嘗試過 VoiceOver 或 NVDA 來測試您的界面的可訪問性。

所以,你決定做一個把戲。 您不會弄亂瀏覽器的樣式,並且會使元素看起來像一個適合可能需要它的用戶的交互式按鈕。 這很聰明!

 <div class="button" tabindex="0" role="button"> Something </div>

現在它不僅看起來正確,而且由於tabindex="0"屬性,它可以通過鍵盤獲得焦點,並且屏幕閱讀器會將它視為正確的按鈕,因為您已經明智地添加了role="button"到它。 然後 Git 提交 && 推送! 這件事還有一些額外的任務,但我們已經完成了樣式。 什麼可能出錯? 午餐時間。 太好了,我們走吧!

一小時後…

那是一頓美味的午餐! 讓我們回到我們點擊的東西。 在繼續之前,我們需要完成一些任務。 讓我們看看……我們需要在單擊按鈕後調用doSomething函數,並且應該有一種方法可以禁用按鈕,使其不可單擊。 聽起來很容易。 讓我們為這個按鈕添加一個事件監聽器:

 <script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); }); function doSomething() { console.log('Something!'); } </script>

完畢。 用戶現在可以在桌面上用鼠標單擊它,然後用手指在觸摸屏上點擊它。 點擊事件會可靠地觸發,你會看到很多東西! 在您的控制台中。 下一個任務是什麼?

堅持,稍等! 我們需要確保它對鍵盤用戶同樣有效。 因為我們在按鈕上有這個tabindex="0" ,所以它可以被聚焦,一旦它被聚焦,用戶應該能夠按空格鍵或“Enter”鍵來觸發我們附加的任何內容。

因此,我們需要附加另一個事件偵聽器來捕獲所有按鍵,並且我們將僅針對某些按鍵觸發我們的函數。 感謝上帝,觸摸設備足夠智能,可以將所有點擊轉換為點擊; 否則,我們也必須附加一堆觸摸事件。

 <script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); button.addEventListener('keyup', (event) => { if (event.key == 'Enter' || event.key == ' ') { doSomething(); } }); }); function doSomething() { console.log('Something!'); } </script>

呸! 現在我們的 clicky 東西可以從鍵盤完全訪問了。 我很為你驕傲! JavaScript 真的很神奇——沒有它我們該怎麼辦?

好吧,最後一個任務是什麼:“按鈕應該有一個禁用狀態,它的外觀和行為會變得麻木。” 麻木的? 我想這意味著一些灰色的東西,對交互沒有反應。 好的,讓我們使用 BEM 命名在樣式表中添加一個狀態。

 <div class="button button--disabled" tabindex="0" role="button"> Something </div> <style> .button--disabled { background-color: #9B9B9B; } </style> 
應用了禁用狀態的灰色按鈕
這個按鈕看起來很麻木。 (大預覽)

這對我來說看起來很麻木。 每當需要禁用按鈕時,我們將添加button--disabled修飾符使其變為灰色。 但它還不夠麻木:它仍然可以通過指針和鍵盤來聚焦和触發。

該死,這變得越來越棘手。

不僅如此,按鈕不應該按 Tab 順序訪問,這意味著tabindex屬性不應該在那裡。 我們需要檢查按鈕是否處於禁用狀態,然後停止觸發我們的功能。 此外,該修飾符可以動態應用。 雖然 CSS 動態匹配元素與選擇器並應用樣式不是問題,但我們可能需要某種突變觀察器來觸發此按鈕的其他更改。

我知道,對吧? 我們認為這將是一個觸發功能並處於禁用狀態的簡單小按鈕。 我們試圖通過可訪問性和所有這些東西來使它正確,現在我們深陷這個兔子洞。

我們去吃點外賣吧。 當我們完成並正確測試時,我們不會回家吃晚飯。 該死的W3C! 為什麼他們不嘗試讓我們的生活更輕鬆? 好像他們在乎我們一樣!

事實上,他們確實……

在跳入這個爛攤子之前,讓我們退後幾步。 為什麼我們不嘗試使用<button>元素來做這些事情呢? 它有一些有用的技巧,而不僅僅是瀏覽器的醜陋風格。 哦,別忘了type="button" — 你不希望彈出窗口的“關閉”按鈕意外提交表單,因為type="submit"是默認值。

顯然,當<button>獲得焦點並按下空格鍵或“Enter”鍵時,它將觸發click事件,就像移動設備在獲得點擊、拍拍、舔或任何其他他們能夠接收的內容時所做的那樣今天。 我們的代碼中少了一個事件監聽器! 好的。

 // A click is enough! button.addEventListener('click', doSomething);

至於禁用狀態, disabled屬性可用於<button>元素以及所有表單元素,包括<fieldset> 。 不開玩笑。 您是否知道只需將單個屬性應用於父<fieldset>就可以禁用一大堆組合在一起的輸入?

一組禁用的輸入和一個按鈕
使用單個屬性禁用的一堆輸入(大預覽)
 <fieldset disabled> <legend>A bunch of numb inputs</legend> <p> <label> <input type="radio" name="option"> Of course it's a link </label> </p> <p> <label> <input type="radio" name="option"> Obviously, it's a button </label> </p> <p> <label> <input type="radio" name="option"> I just wanna go home </label> </p> <button type="button">Button</button> </fieldset>

現在你知道了! 此屬性不僅禁用表單元素上的所有事件,而且還將它們從 Tab 鍵順序中刪除。 問題解決了!

 <button disabled type="button" class="button"> Something </button>

但是等等,還有更多! 它還觸發了 CSS 中的:disabled偽類,這意味著我們可以去掉 BEM 修飾符來聲明樣式,而使用內置的動態修飾符。

 .button:disabled { background-color: #9B9B9B; }

至於瀏覽器的醜陋樣式,我們不必使用所有的 Normalize.css 來修復單個按鈕。 將其用作智慧的來源:下面的三行額外的行將解決與<div>的大部分惱人差異。 如果您需要更多,您可以從中復制相關部分。

 .button { font-size: 100%; font-family: inherit; border: none; }

完畢。 畢竟 HTML 並沒有那麼糟糕!

但是,如果它時不時地讓您感到驚訝,請務必檢查 HTML 規範以獲取答案。 多年來,它變得更加友好,並且充滿了良好的用法和可訪問性示例。 當然,好的 ol' HTML5 Doctor 仍然是一個可靠的地方,可以找出<section><article>元素之間的區別,並檢查文檔大綱是否存在(不是真的)。 您很有可能最終也會閱讀 Mozilla 的 HTML 文檔,而且您也不會後悔。

這個任務現在完成了! 下一步是什麼? 帶有搜索字段的下拉輪播日曆? 天啊! 祝你好運。 但請記住: <button>是你的朋友!

關於 SmashingMag 的進一步閱讀

  • 如何設計更好的按鈕
  • 構建包容性切換按鈕
  • 幽靈按鈕設計:這真的還是一件事(為什麼)?
  • 設計模式:當打破規則是可以的