新聞中心
如果用到了 Ant Design 這樣的框架,那可以直接用現(xiàn)成的組件。如果沒有用到這樣的框架呢?其實純 CSS 也是可以搞定的,下面看看如何實現(xiàn)的,還有很多你可能不知道 CSS 小技巧哦!

“只有客戶發(fā)展了,才有我們的生存與發(fā)展!”這是創(chuàng)新互聯(lián)建站的服務(wù)宗旨!把網(wǎng)站當(dāng)作互聯(lián)網(wǎng)產(chǎn)品,產(chǎn)品思維更注重全局思維、需求分析和迭代思維,在網(wǎng)站建設(shè)中就是為了建設(shè)一個不僅審美在線,而且實用性極高的網(wǎng)站。創(chuàng)新互聯(lián)對成都網(wǎng)站設(shè)計、成都做網(wǎng)站、網(wǎng)站制作、網(wǎng)站開發(fā)、網(wǎng)頁設(shè)計、網(wǎng)站優(yōu)化、網(wǎng)絡(luò)推廣、探索永無止境。
一、details 和 summary
首先,實現(xiàn)這樣一個交互需要利用到 details[1] 和 summary[2],天然地支持內(nèi)容展開和收起。這里有一個 MDN 的例子:
System Requirements
Requires a computer running an operating system. The computer
must have some memory and ideally some kind of long-term storage.
An input device as well as some form of output device is
recommended.
效果如下:
還可以支持多層嵌套,比如:
項目1
文件夾0
文件夾1-1
文件夾1-1-2
文件夾1-1-3
文件夾1-1-3-1
文件夾1-1-3-2
文件夾1-1-4
文件夾1-2
文件夾1-2-1
文件夾1-3
文件夾1-4
效果如下:
是不是有點亂了,還看不出層級關(guān)系?沒關(guān)系,下面可以自定義樣式。
二、自定義樹形結(jié)構(gòu)
1. 縮進(jìn)層級
首先需要突出層級關(guān)系,可以給每一層級加一個內(nèi)邊距。
details{
padding-left: 10px
}全部展開的樣子如下:
2. 自定義三角
這個“黑色三角”太難看了,需要去掉,從開發(fā)者工具可以看到,這個“黑色三角”其實是 ::marker生成的,而這個 ::marker是通過list-style生成,如下:
所以,去除這個“黑色三角”就容易了。
summary{
list-style: none;
}舊版本瀏覽器需要通過專門的偽元素修改,::-webkit-details-marker和::-moz-list-bullet ,現(xiàn)在都統(tǒng)一成了list-style。
然后,可以指定自定義的三角圖標(biāo),展開的樣式可以通過details[open]來定義。
summary{
background: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.354 2.646A.5.5 0 0 0 4.5 3v6a.5.5 0 0 0 .854.354l3-3a.5.5 0 0 0 0-.708l-3-3z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E") 4px center no-repeat;
}
details[open]>summary{
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.354 5.354A.5.5 0 0 0 9 4.5H3a.5.5 0 0 0-.354.854l3 3a.5.5 0 0 0 .708 0l3-3z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E");
}簡單美化以后如下:
3. 樹形結(jié)構(gòu)最深層級
上面的小三角還有點問題,比如這樣一個層級。
當(dāng)沒有展開內(nèi)容時,仍然可以點擊切換,所以需要限制一下,這種情況下不顯示小三角,表示已經(jīng)到最底層目錄了,不可再展開了。
要實現(xiàn)這種也很簡單,仔細(xì)觀察 HTML 結(jié)構(gòu),當(dāng)沒有展開內(nèi)容時,就僅存 summary元素了,是唯一的元素,提到“唯一”,可以想到:only-child,所以實現(xiàn)就是:
summary:not(:only-child){
background: url("xxx") 4px center no-repeat;
}
details[open]>summary:not(:only-child){
background-image: url("xxx");
}這樣就可以很直觀的看到樹形目錄是否已經(jīng)處于最深處了。
三、自定義點擊范圍
一般情況下,自定義到上面這里就可以結(jié)束了。但是,還有一點點小的體驗問題,比如加個 hover 效果。
.tree-item:hover{
background: aliceblue;
}很明顯可以看到 層級越深,點擊范圍越小。那能不能做成通欄都可以點擊的呢?
這時,我們可以借助負(fù)的margin來實現(xiàn),比如給一個足夠大的 padding,然后通過負(fù)的margin 歸位,實現(xiàn)如下:
.tree-item{
/**/
padding-left: 400px;
margin-left: -400px;
}
這樣就是通欄觸發(fā)了,點擊區(qū)域足夠大。
由于左邊是足夠大,已經(jīng)超出樹狀結(jié)構(gòu)了,如果限定在樹狀結(jié)構(gòu)類,可以通過父級超出隱藏或者滾動來解決。
.tree{
overflow: auto;
}還有個問題是 hover背景遮蓋了父級的小三角,而且這種截斷的方式也沒法設(shè)置圓角。怎么解決呢?
可以單獨使用一層偽元素,然后利用“不完全絕對定位”,什么意思呢?設(shè)置一個元素為絕對定位,如果只指定一個方向,比如水平方向(left/right),那么該元素的最終表現(xiàn)是水平方向上的表現(xiàn)依賴于第一個定位的父級,垂直方向上不依賴于定位父級,仍然處于默認(rèn)位置。
在這個例子中,我們可以只指定水平方向上的定位屬性,這樣可以保證水平方向的尺寸跟隨最外層父級,還可以通過z-index改變層級,不遮擋父級小三角,實現(xiàn)如下:
.tree{
position: relative;
}
.tree-item::after{
content: '';
position: absolute;
left: 10px;
right: 10px;/*水平方向的尺寸依賴于父級.tree*/
height: 38px;
background: #EEF2FF;
border-radius: 8px;
z-index: -1;
opacity: 0;
transition: .2s;
}
.tree-item:hover::after{
opacity: 1;
}這樣就比較完美了。
還可以加上文件圖標(biāo)。
.tree-item::before{
content: '';
width: 20px;
height: 20px;
flex-shrink: 0;
margin-right: 8px;
background: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M.833 3.75c0-.92.746-1.667 1.667-1.667h5.417c.247 0 .481.11.64.3l1.833 2.2h7.11c.92 0 1.667.747 1.667 1.667v10c0 .92-.747 1.667-1.667 1.667h-15c-.92 0-1.667-.746-1.667-1.667V3.75zm6.693 0H2.5v4.584h15V6.25H10a.833.833 0 0 1-.64-.3l-1.834-2.2zM17.5 10h-15v6.25h15V10z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E") center no-repeat;
}
details[open]>summary:not(:only-child)>.tree-item::before{
background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M7.917 2.083c.247 0 .481.11.64.3l1.833 2.2h5.443c.92 0 1.667.747 1.667 1.667v1.667h.833a.833.833 0 0 1 .817.997l-1.666 8.333a.833.833 0 0 1-.817.67H1.677a.814.814 0 0 1-.157-.013.83.83 0 0 1-.687-.82V3.75c0-.92.746-1.667 1.667-1.667h5.417zM10 6.25a.833.833 0 0 1-.64-.3l-1.834-2.2H2.5v6.564l.441-1.766a.833.833 0 0 1 .809-.631h12.083V6.25H10zm-7.266 10L4.4 9.584h12.916l-1.334 6.666H2.733z' fill='%23000' fill-opacity='.45'/%3E%3C/svg%3E");
}這樣就得到了文章開頭所示的效果。
完整代碼可以訪問:CSS tree(codepen.io)[3] 或者 CSS tree (juejin.cn)[4] ,可以直接訪問「文章底部原文鏈接」。
四、JS 數(shù)據(jù)渲染
大部分情況下,這類樹狀結(jié)構(gòu)都是通過數(shù)據(jù)渲染出來的,假設(shè)有這樣一段 json數(shù)據(jù)。
const treeData = [
{
"id": 2,
"name": "項目1",
"parentId": 1,
"fileCount": 14,
"children": [
{
"id": 8,
"name": "文件夾",
"parentId": 2,
"fileCount": 12,
"children": [
{
"id": 137,
"name": "sdd",
"parentId": 8,
"fileCount": 0
}
]
},
{
"id": 221,
"name": "chrome test",
"parentId": 2,
"fileCount": 2
}
]
},
{
"id": 52,
"name": "項目2",
"parentId": 1,
"fileCount": 10,
"children": [
{
"id": 54,
"name": "文件夾2-1",
"parentId": 52,
"fileCount": 10,
"children": [
{
"id": 55,
"name": "文件夾2-1-1",
"parentId": 54,
"fileCount": 0,
"children": [
{
"id": 56,
"name": "文件夾2-1-1-1",
"parentId": 55,
"fileCount": 0,
"children": [
{
"id": 57,
"name": "文件夾2-1-1-1-1",
"parentId": 56,
"fileCount": 0,
"children": [
{
"id": 58,
"name": "文件夾2-1-1-1-1-1",
"parentId": 57,
"fileCount": 0
}
]
}
]
}
]
}
]
}
]
},
{
"id": 53,
"name": "文件夾1",
"parentId": 1,
"fileCount": 12,
"children": [
{
"id": 80,
"name": "文件夾",
"parentId": 53,
"fileCount": 11
},
{
"id": 224,
"name": "文件夾2",
"parentId": 53,
"fileCount": 0
}
]
},
{
"id": 69,
"name": "項目3",
"parentId": 1,
"fileCount": 55,
"children": [
{
"id": 70,
"name": "文件夾1",
"parentId": 69,
"fileCount": 12,
"children": [
{
"id": 4,
"name": "1",
"parentId": 70,
"fileCount": 3,
"children": [
{
"id": 51,
"name": "文件夾2",
"parentId": 4,
"fileCount": 1
}
]
}
]
},
{
"id": 91,
"name": "文件夾",
"parentId": 69,
"fileCount": 10
},
{
"id": 102,
"name": "文件夾",
"parentId": 69,
"fileCount": 10
},
{
"id": 113,
"name": "文件夾",
"parentId": 69,
"fileCount": 10
},
{
"id": 121,
"name": "文件夾的副本",
"parentId": 69,
"fileCount": 10
},
{
"id": 136,
"name": "點點點",
"parentId": 69,
"fileCount": 0
},
{
"id": 140,
"name": "hewei",
"parentId": 69,
"fileCount": 3,
"children": [
{
"id": 142,
"name": "hewei02",
"parentId": 140,
"fileCount": 1
}
]
}
]
}
]
這樣一個可以無限嵌套的結(jié)構(gòu)可以用遞歸來實現(xiàn),這里簡單實現(xiàn)一下:
function gen_tree(childs){
var html = ''
childs.forEach(el => {
html+=`
${el.name}
`
if (el.children && el.children.length) {
html += gen_tree(el.children) // 如果有chidren就繼續(xù)遍歷
}
html+= ``
})
return html;
}然后通過innerHTML賦值就行了。
tree.innerHTML = gen_tree(treeData)
效果如下:
五、簡單總結(jié)一下
這樣就通過 CSS 實現(xiàn)了樹狀結(jié)構(gòu)目錄,整體來說并不是很復(fù)雜,主要結(jié)構(gòu)是 details 和 summary,然后是一些 CSS 選擇器的運用,這里簡單總結(jié)一下:
- details 和 summary 原生支持展開收起。
- details 和 summary 支持多層嵌套,這樣就得到了簡易的樹狀結(jié)構(gòu)。
- details 和 summary 支持多層嵌套,這樣就得到了簡易的樹狀結(jié)構(gòu)。
- summary 的黑色三角形是通過 list-style 生成的。
- 展開的樣式可以通過 details[open] 來定義。
- 逐層縮進(jìn)可以通過給 details 添加內(nèi)邊距實現(xiàn)。
- 樹形結(jié)構(gòu)最底層可以通過 :only-child 判斷。
- 默認(rèn)情況下點擊區(qū)域逐層遞減,體驗不是很好。
- 負(fù)的margin 和 padding 可以擴(kuò)大點擊區(qū)域。
- “不完全絕對定位”可以指定一個方向上的尺寸依賴于定位父級。
- 無限嵌套的結(jié)構(gòu)可以用遞歸來實現(xiàn)。
另外,兼容性方面也非常不錯,主流瀏覽器均支持,IE 上雖然不支持 details 和 summary,但是通過 polyfill[5] 解決,總的來說非常實用的,大可以放心使用,也
分享文章:聊一聊CSS實現(xiàn)樹狀結(jié)構(gòu)目錄
本文來源:http://www.fisionsoft.com.cn/article/coooeje.html


咨詢
建站咨詢
