[極致用戶(hù)體驗(yàn)] 你的 Link Button 能讓用戶(hù)選擇新頁(yè)面打開(kāi)嗎?
作者是公眾號(hào)「線下聚會(huì)游戲」HullQin,開(kāi)發(fā)了一些聯(lián)機(jī)桌游網(wǎng)頁(yè)(UNO、斗地主、五子棋等),總結(jié)了一些前端經(jīng)驗(yàn),分享給大家。
1. 什么是Link Button?
我想表達(dá)的是「需要導(dǎo)航能力的可點(diǎn)擊元素」(Link Button是為了方便溝通而創(chuàng)造的名詞)
我用Link表示導(dǎo)航能力,用Button表示可點(diǎn)擊元素。
什么是導(dǎo)航能力?
切換路由(URL)的能力。
<a>標(biāo)簽因?yàn)閔ref屬性,天然具備導(dǎo)航能力。而<button>標(biāo)簽沒(méi)href,只能在onclick事件中,用JS控制打開(kāi)新頁(yè)面。
2. 用戶(hù)怎么選擇新頁(yè)面打開(kāi)?
分2種情況,你可以在掘金頁(yè)面試一下:
2.1 新標(biāo)簽頁(yè)(tab)打開(kāi)
Command(Mac)/Ctrl(Windows) + 鼠標(biāo)左鍵click
鼠標(biāo)中鍵click
鼠標(biāo)右鍵click,在菜單選擇“在新標(biāo)簽頁(yè)中打開(kāi)鏈接”
(無(wú)障礙)通過(guò)Tab,選中鏈接時(shí),按Command(Mac)/Ctrl(Windows) + 回車(chē)鍵Enter
2.2 新窗口(window)打開(kāi)
Shift + 鼠標(biāo)左鍵click
鼠標(biāo)右鍵click,在菜單選擇“在窗口中打開(kāi)鏈接”
(無(wú)障礙)通過(guò)Tab,選中鏈接時(shí),按Shift + 回車(chē)鍵Enter
3. 什么是極致的用戶(hù)體驗(yàn)?
一切導(dǎo)航功能,都應(yīng)該給用戶(hù)完整的『新窗口』打開(kāi)能力。
只要你的按鈕會(huì)導(dǎo)致頁(yè)面切換,就應(yīng)該允許用戶(hù)用1.2提到的任意方式,在新頁(yè)面打開(kāi)。
4. 如何優(yōu)雅的實(shí)現(xiàn)“Link Button”
4.1 新手方案:<button>+onclick
我剛學(xué)前端時(shí),常常喜歡用<button>實(shí)現(xiàn)導(dǎo)航功能,只要在onclick里寫(xiě)window.open(url)或window.document.href = url或window.location.replace(URL)就好了。
缺點(diǎn)很明顯
用戶(hù)根本無(wú)法選擇在新頁(yè)面or本頁(yè)面打開(kāi),只能接受你的實(shí)現(xiàn)。
用戶(hù)根本不知道點(diǎn)擊按鈕后會(huì)發(fā)生什么。(如果是<a>標(biāo)簽,用戶(hù)hover時(shí),會(huì)在瀏覽器左下方看到新頁(yè)面 URL)
4.2 中手方案:<button>+onclick+event
工作2個(gè)月后,我懂了點(diǎn)用戶(hù)體驗(yàn),但知識(shí)局限于:用戶(hù)點(diǎn)擊Command(Mac)/Ctrl(Windows) + 鼠標(biāo)左鍵click,可以新標(biāo)簽頁(yè)打開(kāi)。所以我這么寫(xiě):
// buttonElement 是html中某個(gè)<button>元素
buttonElement.onclick = function (event) {
if (event.ctrlKey || event.metaKey) {
window.open('某個(gè)url');
} else {
window.document.href = '某個(gè)url';
}
};
復(fù)制代碼
觸發(fā)onclick時(shí),通過(guò)event參數(shù)判斷下有沒(méi)有按下Ctrl或Command:如果有按下,就新標(biāo)簽頁(yè)打開(kāi);否則本頁(yè)面跳轉(zhuǎn)。
其實(shí)這種方案只比新手方案好一點(diǎn)點(diǎn),問(wèn)題沒(méi)有得到根本解。
4.3 高手方案:<a>+onclick+event
工作半年后,同事告訴我中鍵click也能新標(biāo)簽頁(yè)打開(kāi)。我又學(xué)了點(diǎn)html無(wú)障礙規(guī)范,才明白一個(gè)道理:
導(dǎo)航能力,就交給專(zhuān)業(yè)的<a>標(biāo)簽做,兼容性最好,能力最全面。
<a>除了天然支持新頁(yè)面打開(kāi),還有些好處:鼠標(biāo)Hover上去時(shí),瀏覽器會(huì)提示新頁(yè)面地址,并且也能直接右鍵復(fù)制地址,便于分享!
但是!有2個(gè)問(wèn)題需要解決:
4.3.1 樣式問(wèn)題
<button>和<a>樣式是有差異的。產(chǎn)品形態(tài)上希望用按鈕,我們就不能用超鏈接樣式。
該問(wèn)題很好解,把你按鈕樣式復(fù)制一份,或者把相關(guān)class放到<a>標(biāo)簽上,就基本一樣了,如果還有樣式差異,就再覆蓋一下<a>的默認(rèn)樣式(大多數(shù)瀏覽器會(huì)給它設(shè)置字體顏色、下劃線這2個(gè)默認(rèn)樣式):
a, a:link, a:visited, a:hover, a:active {
color: inherit;
text-decoration: none;
}
復(fù)制代碼
4.3.2 JS邏輯問(wèn)題
如果導(dǎo)航,需要其它JS邏輯,就無(wú)法只用<a>和href實(shí)現(xiàn),并且這種情況還不少。例如:
跳轉(zhuǎn)時(shí),需要傳路由state。
某些邏輯,只希望本頁(yè)面跳轉(zhuǎn)時(shí)執(zhí)行,不允許新頁(yè)面打開(kāi)時(shí)執(zhí)行(因?yàn)镴S只能執(zhí)行本頁(yè)面的JS,如果在新頁(yè)面打開(kāi),本頁(yè)面應(yīng)該保持不變,不能執(zhí)行那段JS,例如React Router中的<Link>)。
某個(gè)按鈕,直接點(diǎn)擊時(shí)是window.history.back(),但也允許新窗口打開(kāi)上個(gè)頁(yè)面地址(這個(gè)問(wèn)題更加復(fù)雜,請(qǐng)期待我的下篇文章,會(huì)做詳細(xì)講解)
現(xiàn)在我想告訴你:這些問(wèn)題,也是有解的!
這些問(wèn)題的解決方案
還記得我曾經(jīng)是個(gè)“中手”嗎?我的“中手方案”剛好可以解決這個(gè)難題!把中手方案邏輯改改,就可以了:
// aElement是html中的某個(gè)包含href的<a>元素: <a href="某個(gè)url">某個(gè)鏈接</a>
aElement.onclick = function (event) {
if (event.button !== 0) return;
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) return;
event.preventDefault();
// 如果用戶(hù)期望本頁(yè)面跳轉(zhuǎn)(而非新窗口打開(kāi)),則執(zhí)行以下邏輯
window.history.pushState({ pageState: 123 }, '', 'new-page.html');
};
復(fù)制代碼
解釋下:
event.button
表示按下的是鼠標(biāo)哪個(gè)按鍵:
0:主按鍵,通常指鼠標(biāo)左鍵或默認(rèn)值
1:輔助按鍵,通常指鼠標(biāo)滾輪中鍵
2:次按鍵,通常指鼠標(biāo)右鍵
3:第四個(gè)按鈕,通常指瀏覽器后退按鈕
4:第五個(gè)按鈕,通常指瀏覽器的前進(jìn)按鈕
這里我們只管理左鍵就好,其它按鍵都保持瀏覽器默認(rèn)行為(所以非0直接return,不執(zhí)行JS邏輯,執(zhí)行原生行為)。
event.xxxKey
event.ctrlKey: MAC上表示Control鍵,Windows上表示Ctrl鍵。
event.metaKey: MAC上表示Command鍵,Windows上表示W(wǎng)indows鍵。
event.shiftKey: Shift鍵。
event.altKey: MAC上表示Option鍵,Windows上表示Alt鍵。
按照規(guī)范,這些鍵按下時(shí),不應(yīng)該在本頁(yè)面繼續(xù)跳轉(zhuǎn),而是會(huì)發(fā)生這些事:
ctrlKey + click: Mac上表示右鍵點(diǎn)擊該元素,Windows上表示新標(biāo)簽頁(yè)打開(kāi)頁(yè)面。
metaKey + click: Mac上表示新標(biāo)簽頁(yè)打開(kāi)頁(yè)面,Windows上打開(kāi)Windows開(kāi)始菜單。
shiftKey + click: 新窗口打開(kāi)頁(yè)面。
altKey + click: 下載頁(yè)面。
event.preventDefault()
如果用戶(hù)只是普通的左鍵點(diǎn)擊了鏈接,沒(méi)按任何xxxKey,就應(yīng)該阻止<a>標(biāo)簽?zāi)J(rèn)行為,由我們的JS去接管,自由操控跳轉(zhuǎn)。
但如果用戶(hù)按了任何xxxKey,或是點(diǎn)了鼠標(biāo)其它鍵,都應(yīng)該讓瀏覽器接管后續(xù)邏輯。
Oh!真是完美的方案!
5. 寫(xiě)在最后
如果你像我一樣,喜歡代碼純粹一點(diǎn),不夾雜冗余功能,就可以自己寫(xiě)Link Button,封裝自己所需的組件 ??
如果你只是為了完成別人的需求,還是直接用組件庫(kù)吧 ??
但是,即使你用組件庫(kù),里面有Menu、Button組件,你一定要想清楚,如果需要頁(yè)面跳轉(zhuǎn),務(wù)必找找Link組件,盡量使用Link來(lái)表達(dá)導(dǎo)航。
關(guān)于導(dǎo)航的用戶(hù)體驗(yàn),非常細(xì)節(jié),太重要了!一個(gè)網(wǎng)頁(yè)的質(zhì)量,一個(gè)前端開(kāi)發(fā)者的水平,能直接從導(dǎo)航欄細(xì)節(jié)中看出。
最后希望大家都能開(kāi)發(fā)出用戶(hù)體驗(yàn)完美的“Link Button”!
關(guān)于本文
作者:公眾號(hào)「線下聚會(huì)游戲」HullQin
https://juejin.cn/post/7089483164908781604
作者:HullQin
歡迎關(guān)注微信公眾號(hào) :前端陽(yáng)光