和BEM的戰鬥:10個常見問題及如何避免
無論你是剛剛發現BEM或者已經是個中熟手(作為web術語來說),你可能已經意識到它是一種有用的方法。如果你還不知道BEM是什麼,我建議你在繼續閱讀這篇文章之前去BEM website瞭解一下它,因為我會假設你對這種CSS的方法有一個基礎的理解。
本文旨在對那些已經是BEM的愛好者或是想要去更有效率的使用它或是非常好奇並且想去學習它的人有所幫助。
現在,我對BEM是一個優雅的命名方式已經不報有任何幻想。它完全不是。我曾經很長一段時間放棄接受它的原因之一就是它的語法看起來非常醜陋。我心中的設計因子不希望我優雅的html結構被醜陋的雙下劃線和連字元弄得一團糟。
而我心中的開發者因子讓我務實地看待它。最終,這種用來構建使用者介面並且有邏輯性的、模組化的方式戰勝了我右半邊大腦的抱怨:“但是它不夠漂亮!”我當然不會建議你在像起居室這樣小的範圍內使用這種方式,但是當你需要一件救生衣(就像你遨遊在CSS的大海中),我會選擇實用而不是形式。話題拓展的差不多了,以下是10種我已經遇到過的困境和一些如何解決它們的技巧。
1. 如何使用子代甚至更深層次的選擇器?
首先來解釋這個問題,當你需要選擇一個巢狀超過兩層的元素,你就會需要用到子孫選擇器。這些選擇器簡直就是我的夢魘,而且我很確定他們的濫用是人們對BEM產生厭惡的原因之一,看下面這個例子:
<div class="c-card">
<div class="c-card__header">
<!-- Here comes the grandchild… -->
<h2 class="c-card__header__title">Title text here</h2 >
</div>
<div class="c-card__body">
<img class="c-card__body__img" src="some-img.png" alt="description">
<p class="c-card__body__text">Lorem ipsum dolor sit amet, consectetur</p>
<p class="c-card__body__text">Adipiscing elit.
<a href="/somelink.html" class="c-card__body__text__link">Pellentesque amet</a>
</p>
</div>
</div>
就像你想的那樣,以這種方式命名會很快就會脫離控制,並且一個元件巢狀的越深,越醜陋也越不可讀的類名就會出現。我已經使用了一個短塊名稱c-card
和短元素名,比如:body
,text
,link
,但是你可以想象當塊和元素的初始部分被命名為c-drop-down-menu
會有多麼失控。
我認為雙下劃線在選擇器名稱中只應該出現一次。BEM代表的是Block__Element--Modifier
,而不是Block__Element__Element--Modifier
。所以,避免多個元素級的命名。如果存在多級巢狀,你可能就需要重新審查一下你的元件結構。
BEM命名和DOM沒有很嚴格的聯絡,所以無論子元素的巢狀程度有多深都沒有關係。命名約定只是用來幫助你識別子元素和頂層元件塊的關係,在這裡就是c-card
。
這是我對相同card元件的處理:
<div class="c-card">
<div class="c-card__header">
<h2 class="c-card__title">Title text here</h2>
</div>
<div class="c-card__body">
<img class="c-card__img" src="some-img.png" alt="description">
<p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
<p class="c-card__text">Adipiscing elit.
<a href="/somelink.html" class="c-card__link">Pellentesque amet</a>
</p>
</div>
</div>
這意味著所有的子元素都僅僅會被card塊影響。所以,我們可以將文字和圖片移動到c-card__header
,甚至在不破壞語義結構的情況下新增一個c-card__footer
元素。
2. 我應該使用名稱空間嗎?
現在,你可能已經注意到我的程式碼示例中使用了c-
。這代表“元件”和形成了我命名BEM類名的規範。這個想法來自於致力於提升程式碼可讀性的Harry Robert’snamespacing technique
這是我採用的規範,很多字首會貫穿這篇文章的程式碼示例。
TYPE | PREFIX | EXAMPLES |
---|---|---|
Component | c- |
c-card c-checklist |
Layout module | l- |
l-grid l-container |
Helpers | h- |
h-show h-hide |
States | is- has- |
is-visible has-loaded |
JavaScript hooks | js- |
js-tab-switcher |
我發現使用這些名稱空間會使我的程式碼非常具有可讀性。即使我不能強求你使用BEM,這也絕對是一個值得你使用的關鍵點。
你可以採用很多其它的名稱空間,像qa-
可以用作質量保證測試,ss-
用作伺服器端的鉤子,等等。但是上面的列表是一個好的開始,當你覺得這項技術還不錯,你可以把它介紹給其他人。
在下個問題中,會有一個比較實用的關於樣式名稱空間的示例。
3. 我該如何命名包裹容器?
一些組建需要一個掌控子元素佈局的容器。在這種情況下,我通常會嘗試把佈局抽象到一個佈局模組中,比如l-grid
,並且將每一個元件作為l-grid__item
的內容插入。
在我們card的示例中,如果我們想要去生成擁有四個c-card
的列表,我會使用下面的html結構:
<ul class="l-grid">
<li class="l-grid__item">
<div class="c-card">
<div class="c-card__header">
[…]
</div>
<div class="c-card__body">
[…]
</div>
</div>
</li>
<li class="l-grid__item">
<div class="c-card">
<div class="c-card__header">
[…]
</div>
<div class="c-card__body">
[…]
</div>
</div>
</li>
<li class="l-grid__item">
<div class="c-card">
<div class="c-card__header">
[…]
</div>
<div class="c-card__body">
[…]
</div>
</div>
</li>
<li class="l-grid__item">
<div class="c-card">
<div class="c-card__header">
[…]
</div>
<div class="c-card__body">
[…]
</div>
</div>
</li>
</ul>
你現在應該理解佈局模組和元件的名稱空間是如何一起工作的。
不要害怕使用一些額外的標記會非常令人頭痛。沒有人會拍拍你的背然後告訴你去把<div>
標籤移除掉的。
在一些情景下,佈局模組是不可能的完全滿足要求的。比如說你的網格沒有能給你想要的結果,或者你只是想要去語義化的命名一個父元素,你應該怎麼做?在不同的場景我傾向去選擇contaniner
或者list
。還是我們card的例子,我可能會用<div class="l-cards-container">[…]</div>
or <ul class="l-cards-list">[…]</ul>
或者是<ul class="l-cards-list">[…]</ul>
,這取決於使用的條件。關鍵是要和你的命名約定保持一致。
4. 跨元件的組建?
我們面臨的另一個常見的問題是元件的樣式和位置會受到父級容器的影響。就這個問題Simurai有很多詳細的解決辦法。我這裡說一個拓展性最好的方式。
假設我們想要在之前的示例的card__body
中加入一個c-button
。這個按鈕本身已經是一個元件並且結構如下:
`<button class="c-button c-button--primary">Click me!</button>`
如果和常規的按鈕元件沒有樣式差別,那麼就沒有問題。我們只要像下面這樣直接使用:
<div class="c-card">
<div class="c-card__header">
<h2 class="c-card__title">Title text here</h3>
</div>
<div class="c-card__body">
<img class="c-card__img" src="some-img.png">
<p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
<p class="c-card__text">Adipiscing elit. Pellentesque.</p>
<!-- Our nested button component -->
<button class="c-button c-button--primary">Click me!</button>
</div>
</div>
舉個例子,如果我們想要讓按鈕變小一點並且完全是圓角,而這些樣式只是c-card
元件的一部分。也就是說,當它有一些微小的不同時我們應該怎麼辦?
之前我說過,我找到一個最好用的跨元件類名的解決方式。
<div class="c-card">
<div class="c-card__header">
<h2 class="c-card__title">Title text here</h3>
</div>
<div class="c-card__body">
<img class="c-card__img" src="some-img.png">
<p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p>
<p class="c-card__text">Adipiscing elit. Pellentesque.</p>
<!-- My *old* cross-component approach -->
<button class="c-button c-card__c-button">Click me!</button>
</div>
</div>
這就是BEM官網上著名的“mix”。但是,參考了一些從Esteban Lussich的評價之後,我改變了對這種方式的看法。
在上面的例子中,c-card__c-button
類嘗試去改變一個或多個c-button
中存在的屬性,但是成功應用這些樣式取決於他們的源順序(或者特殊的指定)。c-card__c-button
類只會當它在原始碼裡宣告在c-button
類之後才會生效。在你構建更多跨元件的元件時會很快失控。(當然,使用!important
也是一種選擇,但是我不建議你這樣做)
一個真正模組化的UI元素的父元素應該是完全不可知的-無論你在何處使用它,效果都應該是一致的。像“mix”方式那樣為另一個元件新增具有特定樣式的類名,違反了元件驅動設計的開/關原則,即樣式在各模組之間不應該有依賴關係。
最好的辦法就是在這些微小的樣式差別中使用同一個類,因為你會發現隨著專案的增長你會在別的地方對它進行復用。
`<button class="c-button c-button--rounded c-button--small">Click me!</button>`
即使你不會再使用這些類,為了應用這些修改,至少也不能把他們和父容器、特殊屬性和源順序耦合在一起。
當然,另一個選擇就是回到你的設計師崗位,告訴他們這個按鈕應該和網站上的其他按鈕保持一致,這樣就可以完全避免這個問題。但這是另一碼事了。
5. 修飾器還是新元件?
決定元件的起止是一個大問題。在c-card
這個示例裡,你可能之後會建立另一個叫c-panel
的元件,他們兩個樣式相仿,只有一些細微的差別。
但是是什麼決定他們應該是兩個元件呢?c-panel
和c-card
這個塊名,或者僅僅是因為一個修飾器在c-card
裡應用了特殊的樣式。
這樣很容易過度模組化並且讓一切都變成一個元件。我建議從修飾器開始,但是如果你發現你特定元件的CSS檔案正變得很難維護,這時候就可以停止使用修飾器。當你發現你為了適應你新的修飾器而不得不去重置這個“塊”所有的CSS時,就是需要新元件的好時機-起碼對我來說是這樣的。
如果你和其它開發者或者設計師協作,最好的方式是去詢問他們的意見並且花幾分鐘去討論。我知道這樣可能有點逃避責任,但是對於一個大型專案來說,理解哪些模組是可複用的並且在元件的構成上達成一致是至關重要的。
6. 如何處理狀態?
這是一個常見的問題,特別是當你給一個活躍狀態的元件編寫樣式的時候。讓我們假設cards有一個活躍狀態,當我們點選它時,它們會被新增上一個好看的邊框。你會怎麼去命名這個類?
在我看來你有兩種選擇:獨立的狀態鉤或者是一個在元件級類似BEM方式命名的修飾器。
<!-- 獨立狀態勾 -->
<div class="c-card is-active">
[…]
</div>
<!-- BEM修飾器 -->
<div class="c-card c-card--is-active">
[…]
</div>
儘管我更傾向於保持一致性的類似BEM的命名方式,獨立類名的好處是使用JavaScript來在任意一個元件中應用一般的狀態鉤更容易。當你不得不使用指令碼去應用特定的基於修飾器的狀態類時,類BEM的方式就很讓人頭疼了。這當然是完全可行的,但是意味著你需要為每種可能性去編寫更多的JavaScript程式碼。
堅持使用一系列標準的狀態鉤是有意義的。Chris Pearece有一個編譯好的列表,我推薦你去了解一下。
7. 什麼時候可以不在元素上新增類?
我可以理解很多人在需要構建一個複雜UI的時候面臨的痛苦,特別是他們不習慣去在每個標籤上新增一個類。
通常,我會在需要特殊樣式的部分上下文新增類名。我會把p
標籤級的捨棄,除非在這個元件中有特殊的需求。
可以預見,這意味著你的html中會包括非常多類名。最終,你的元件可以獨立執行並且在沒有副作用的條件下在任何地方使用。
由於CSS的全域性特性,在所有部分都新增類讓我們可以完全控制我們元件的渲染。最初的心理不適在一整個模組化的系統完成後是完全值得的。
8. 如何巢狀元件?
假設我們想要在c-card
元件中展示一個選項列表,下面這是一個反面教材:
<div class="c-card">
<div class="c-card__header">
<h2 class="c-card__title">Title text here</h3>
</div>
<div class="c-card__body">
<p>I would like to buy:</p>
<!-- Uh oh! A nested component -->
<ul class="c-card__checklist">
<li class="c-card__checklist__item">
<input id="option_1" type="checkbox" name="checkbox" class="c-card__checklist__input">
<label for="option_1" class="c-card__checklist__label">Apples</label>
</li>
<li class="c-card__checklist__item">
<input id="option_2" type="checkbox" name="checkbox" class="c-card__checklist__input">
<label for="option_2" class="c-card__checklist__label">Pears</label>
</li>
</ul>
</div>
<!-- .c-card__body -->
</div>
<!-- .c-card -->
這裡有很多問題。第一個是我們在第一點裡提到的子孫選擇器。第二點是所有應用c-card__checklist__item
樣式都被限定使用,不能複用。
我更傾向於這裡需要打破在這個佈局模組中的列表本身,而是應該把選項列表單獨抽象成一個元件,這樣就可以在其它地方獨立使用它們。這裡我們使用l-
名稱空間。
<div class="c-card">
<div class="c-card__header">
<h2 class="c-card__title">Title text here</h3>
</div>
<div class="c-card__body"><div class="c-card__body">
<p>I would like to buy:</p>
<!-- Much nicer - a layout module -->
<ul class="l-list">
<li class="l-list__item">
<!-- A reusable nested component -->
<div class="c-checkbox">
<input id="option_1" type="checkbox" name="checkbox" class="c-checkbox__input">
<label for="option_1" class="c-checkbox__label">Apples</label>
</div>
</li>
<li class="l-list__item">
<div class="c-checkbox">
<input id="option_2" type="checkbox" name="checkbox" class="c-checkbox__input">
<label for="option_2" class="c-checkbox__label">Pears</label>
</div>
</li>
</ul>
<!-- .l-list -->
</div>
<!-- .c-card__body -->
</div>
<!-- .c-card -->
這樣你就不用重複哪些樣式,同時也意味著我們可以在專案中的其它地方使用l-list
和c-checkbox
。可能這意味著更多的標記,但是對於可讀性,封裝性和可複用性來說代價可以忽略。你可能已經注意到這些是共同的主題!
9. 元件會不會最終有無數個類名?
有些人認為每個元素有大量類名是不好的,--modifiers
會越積越多。就我個人而言,我不認為這是個問題,因為這意味著程式碼更具有可讀性,我也能更清楚的知道它是用來實現什麼的。
舉個例子,這是一個具有四個類的按鈕:
`<button class="c-button c-button--primary c-button--huge is-active">Click me!</button>`
我第一眼看到的時候覺得語法不是最簡潔的,但是非常清晰。
如果這讓你非常頭痛,你可以檢視Sergey Zarouski提出的拓展技術,我們可以在樣式表中使用.className [class^="className"]
和[class*=" className"]
來效仿vanilla CSS的拓展功能。如果語法看起來很眼熟,可能是因為和Icomoon處理它的icon選擇器的方式非常類似。
使用這種技術,你的程式碼可能會看起來像下面這樣:
`<button class="c-button--primary-huge is-active">Click me!</button>`
我不知道使用class^=
和class*=
選擇器是否比獨立的類名錶現更好,但是理論上來說這是一個不錯的選擇。我喜歡使用複合類名,但我覺得這值得那些傾向於尋找替代品的人注意一下。
10. 我們可以響應式的改變元件的樣式嗎?
這是Arie Thulank給我提出的問題,我花費了很多心思去想出一個100%具體具體的解決辦法。
一個例子就是下拉選單在給定斷點處轉換為選項卡或者是隱式導航在給定斷點處轉換為選單欄。本質上是一個元件在媒體查詢的控制下有兩種不同的樣式表現。
我傾向於給這兩個例子去構建一個c-navigation
元件,因為他們在兩個斷點處的行為本質是相同的。但這讓我陷入沉思,如果是圖片列表在大螢幕上轉化為輪播圖呢?這對我來說是一個邊緣情況,只要它有可行的文件及評論,我認為這是合理的。可是使用明確的命名(像c-image-list-to-carousel
)來為這種型別的UI構造一次性的獨立元件。
Harry Roberts寫過一篇響應式字尾來解決這個問題。他的做法是為了適應更多佈局和樣式的變化,而不是整個元件的變化。但我不明白為什麼這項技術不能被應用在這裡。所以,你會發現作者寫的樣式像下面這樣:
`<ul class="[email protected] [email protected]">`
對於不同的螢幕尺寸,這些類就會保留各自的媒體查詢。提示:在CSS中你需要在@
前加上\
來進行轉義,像下面這樣:
.c-image-list\@small-screen {
/* styles here */
}
我沒有太多構造這種元件的示例,但是如果你需要構造這種元件,這將是一個對開發者非常友好的方式。下一個加入的人應該可以輕鬆理解你的想法。我不提倡你使用像small-screen
和large-screen
這樣的命名,他們只是單純為了可讀性。
總結
BEM在我建立一個模組化和元件驅動的應用時幫了大忙。我已經使用它大概有三年了,上面的這些問題是我在探索時遇到的阻礙。我希望你認為這篇文章是有用的。如果你還沒有想要體驗BEM,我非常鼓勵你去嘗試一下。
相關推薦
和BEM的戰鬥:10個常見問題及如何避免
無論你是剛剛發現BEM或者已經是個中熟手(作為web術語來說),你可能已經意識到它是一種有用的方法。如果你還不知道BEM是什麼,我建議你在繼續閱讀這篇文章之前去BEM website瞭解一下它,因為我會假設你對這種CSS的方法有一個基礎的理解。 本文旨在對
Hibernate 的 10 個常見面試問題及答案
原文出處: 開源中國 在Java J2EE方面進行面試時,常被問起的Hibernate面試問題,大多都是針對基於Web的企業級應用開發者的角色的。Hibernate框架在Java界的成功和高度的可接受性使得它成為了Java技術棧中最受歡迎的物件關係影射(ORM)解決方案。
[51CTO]區塊鏈在美國:10個案例、10個問題和5個解決方案
區塊鏈在美國:10個案例、10個問題和5個解決方案 近日,美國國際戰略研究中心(CSIS, Center for Strategic and International Studies)釋出報告《利用區塊鏈實現美國商業與繁榮:10個案例、10個問題、5個解決方案》。詳細介紹了區塊鏈改進醫療、製造業、
Linux:10個實用的網路和監控命令
本文列出了 10 個基礎的每個 Linux 使用者都應該知道的網路和監控命令。網路和監控命令類似於這些: hostname, ping, ifconfig, iwconfig, netstat, nslookup, traceroute, finger, telnet, ethtool 用於檢視 linux
合併請求的 10 個常見問題及建議
我參加過很多 GitHub 託管的專案,不管是個人的、開源的還是合約的。有時候用免費的 GitHub,其他時候用 GitHub 企業版。但有一件事是一樣的:提交合並請求(Pull Request)很容易的,但審查好 PR 真的很難。 所以乾脆寫一篇合併請求的 10
Hibernate的10個常見面試問題及答案
在Java J2EE方面進行面試時,常被問起的Hibernate面試問題,大多都是針對基於Web的企業級應用開發者的角色的。Hibernate框架在Java界的成功和高度的可接受性使得它成為了Java技術棧中最受歡迎的物件關係影射(ORM)解決方案。Hibernate將你從
PHP:10個不常見卻非常有用的PHP函式
1. sys_getloadavg() sys_getloadavt()可以獲得系統負載情況。該函式返回一個包含三個元素的陣列,每個元素分別代表系統再過去的1、5和15分鐘內的平均負載。 與其讓伺服器因負載過高而宕掉,不如在系統負載很高時主動die掉一個指令碼,sys_getloadavg()就是用來幫你實
分針網——每日分享:10個程序員常用的代碼簡寫技術
代碼簡寫 加入職業技能圈 q群:272292492 更多文章:www.f-z.cn 今天小編我給大家整理了一份10個程序員常用的代碼簡寫技術,看懂一種是入門,全懂就是大神,你能知道幾個呢?
10個常見的JavaScript BUG
分享 需要 you 慎用 div -a 地址 var 最好 譯者按: 安全起見,在開發中我基本不用==。 原文: 10 COMMON JAVASCRIPT BUGS AND HOW TO AVOID THEM 譯者: Fundebug 為了保證可讀性,本文采用意譯而非直
10個實戰及面試常用的shell腳本--1
shell###10個實戰及面試常用的shell腳本 寫腳本之前的一些註意事項 1.開頭加解釋器: #!/bin/bash 2.語法縮進,使用4個空格,多加註視說明 3.命名規則: 變量名大寫 局部變量小寫 函數名小寫 名字體現出實際作用 4.默認變量是全局的,在函數中的變量local指定為局部變量,避免
推進你的計算機網絡事業:10個技巧
我們 不可 一點 協議 團隊 新技術 基礎設施 創新 需要 希望將自己的職業生涯提升到新水平的網絡專業人士有很多選擇,有時可能會讓人不知所措。關鍵是找出你想去的地方,然後評估你如何到達那裏的選擇。對於一些人來說,職業發展可能就像在一項流行的新技術上學習一周的課程一樣簡單。對
10個常見的Redis面試題
導讀:在程式設計師面試過程中Redis相關的知識是常被問到的話題。作為一名在網際網路技術行業打擊過成百上千名的資深技術面試官,本文作者總結了面試過程中經常問到的問題。十分值得一讀。 作者簡介:錢文品(老錢),網際網路分散式高併發技術十年老兵,目前任掌閱科技資深後端工程師。熟練使用 Java、Py
10 個實戰及面試常用 Shell 指令碼編寫
注意事項 1)開頭加直譯器:#!/bin/bash 2)語法縮排,使用四個空格;多加註釋說明。 3)命名建議規則:變數名大寫、區域性變數小寫,函式名小寫,名字體現出實際作用。 4)預設變數是全域性的,在函式中變數local指定為區域性變數,避免汙染其他作用域。 5)有兩個命令能幫助我除錯指令碼:set -e
運維請注意:10個“非常危險”的Linux命令,果斷收藏!
計算機作業系統是管理計算機軟硬體資源的一個平臺,沒有它,任何計算機都無法正常執行,市面上主流的作業系統有Windows、Unix、Linux、Mac OS X。 這裡主要說一下Linux系統,Linux是一個類Unix的開源版作業系統,主要有以下幾個特點: 開放式作業系統 Linu
10 個面試及實戰常用 Shell 指令碼
注意事項 1)開頭加直譯器:#!/bin/bash 2)語法縮排,使用四個空格;多加註釋說明。 3)命名建議規則:變數名大寫、區域性變數小寫,函式名小寫,名字體現出實際作用。 4)預設變數是全域性的,在函式中變數local指定為區域性變數,避免汙染其他作用域。 5)有兩個命令能幫助我除錯指令碼:se
Java乾貨分享:10個非常有用的Java程式碼片段,記得收藏哦!
最近有粉絲私信我說,能不能分享一些技術型的乾貨,方便開發,於是我抽時間總結了一些經常會用帶的程式碼片段,分享給大家! 下面是10個非常有用的Java程式片段,希望能對你有用。 字串有整型的相互轉換 向檔案末尾新增內容 轉字串到日期 4.使用JDBC連結O
世行首席經濟學家:10個增加學術研究影響力的方法
編者按: 傳統觀念中,象牙塔中的研究似乎是不需要宣傳的——學者們所謂的**「圈子」**其實也就那麼幾個人。然而,在自媒體時代,一切似乎在迅速改變。我們用納稅人的血汗錢做研究,理應彙報和分享我們的研究發現。事實上,我們還可以再向前推進一步——主動推銷/展示我們的研究發現。 本
驅散謬見 :7個常見的 DevOps 誤區解讀
作者介紹: 張樂 DevOps時代聯合創始人,高效運維社群合夥人,DevOpsDays大會、GOPS全球運維大會金牌講師。國內首批DevOps Master,前百度資深敏捷教練、架構師。超過十四年敏捷轉型、工程效能提升和大型專案管理實踐經驗,曾主導數百人團隊實施DevOps轉型,在保證質量的前
10個常見的Redis面試"刁難"問題
導讀:在程式設計師面試過程中Redis相關的知識是常被問到的話題。作為一名在網際網路技術行業打擊過成百上千名的資深技術面試官,本文作者總結了面試過程中經常問到的問題。十分值得一讀。 作者簡介:錢文品(老錢),網際網路分散式高併發技術十年老兵,目前任掌閱科技資深後端工程師。熟
經驗分享:10個簡單實用的 jQuery 程式碼片段
儘管各種 JavaScirpt 框架和庫層出不窮,jQuery 仍然是 Web 前端開發中最常用的工具庫。今天,向大家分享我覺得在網站開發中10個簡單實用的 jQuery 程式碼片段。 您可能感興趣的相關文章 平滑滾動到錨點 這個功能很常見,在網站底部新增一個讓訪客快速回到頁面頂部的功能