Web 前端實戰(二):JQuery 實現樹形控制元件
前言
這是一篇個人練習 Web 前端各種常見的控制元件、元件的實戰系列文章。本篇文章將介紹個人通過 JQuery + 無序列表 + CSS 動畫完成一個簡易的樹形控制元件。
最終實現的效果是:
這樣結構比較複雜的巢狀再巢狀的 HTML 結構必須先寫一個靜態的觀察,不可能一步到位,事情是逐漸發展的。遵循自頂向下、逐步求精、模組化三個原則。
靜態實現
HTML
點選檢視 HTML 程式碼
<ul class="category"> <li class="category-item" data-displayed="false"> <div class="category-tip">分類</div> <ul class="sub-category"> <li class="sub-category-item">item 01</li> <li class="sub-category-item">item 02</li> <li class="sub-category-item">item 03</li> </ul> </li> <li class="category-item" data-displayed="false"> <div class="category-tip">分類</div> <ul class="sub-category"> <li class="sub-category-item">item 01</li> <li class="sub-category-item">item 02</li> </ul> </li> <li class="category-item" data-displayed="false"> <div class="category-tip">分類</div> <ul class="sub-category"> <li class="sub-category-item">item 01</li> </ul> </li> </ul>
基本的結構設計就是,在外層是無序列表 ul,每一項內容 li 下面巢狀 div 和 ul 標籤。每一層資料展示是類名為category-tip
的標籤,如果標籤下面還有可以展開的內容就有sub-cagetory
的 ul。
CSS
點選檢視 CSS 程式碼
.category-item { cursor: pointer; --li-height: 0; } .category-item-active { animation: category-display 0.18s cubic-bezier(0.42, 0, 0.18, 0.98) 0s; } .sub-category { display: none; } .sub-category-active { display: block; animation: category-show 0.6s ease-in-out 0s; } @keyframes category-show { from { opacity: 0; } to { opacity: 1; } } @keyframes category-display { from { height: 0; } to { height: var(--li-height); } }
觀察最開始的效果展示可知,category-item
被點選時需要展開到其子節點sub-category
的高度。子節點需要有一種漸變的效果,動畫設定的時間要比category-item
節點展開的時間長一點。
JQuery
$(".category-item").on("click", function () { let displayed = $(this)[0].dataset.displayed; if ( displayed === "false" ) { $(this).css({ "--li-height": `${ $(this).children(".sub-category").height() }px` }); $(this).addClass("category-item-active"); $(this).children(".sub-category").addClass("sub-category-active"); $(this)[0].dataset.displayed = "true"; } else { $(this).removeClass("category-item-active"); $(this).children(".sub-category").removeClass("sub-category-active"); $(this)[0].dataset.displayed = "false"; } });
要實現列表展開和收縮,必須要知道當前節點是否已經展開?因此,就必須給節點標記一個布林值以便於判斷是否展開的狀態,正好 HTML 標籤支援data-xxx
的屬性,我們可以把這個布林值直接放在每一個需要展開的標籤中:
<li class="category-item" data-displayed="false">......</li>
當點選節點時,JQuery 需要判斷data-displayed
是否已經展開,如果展開就移除category-item-active
以及sub-category-active
的動畫;如果沒有展開就新增這兩個動畫,實現展開效果。
改進
category-item-active
有一個重大問題,就是點選時節點有類似於重影的重合效果。經過多次除錯發現,動畫開始時,高度從 0px 開始,所以發生了重影效果。正確是高度從category-tip
的高度開始,到category-tip
和sub-category
結束。
$(this).css({
"--tip-height": `${ $(this).children(".category-tip").height() }px`,
"--li-height": `${ $(this).children(".sub-category").height() }px`,
});
@keyframes category-display {
from {
height: 0;
}
to {
height: calc(var(--li-height) + var(--tip-height));
}
}
Gitee 倉庫-當前案例-完整程式碼:樹形控制元件-v1.0
Gitee 倉庫-當前案例-改進程式碼:樹形控制元件-v1.1
動態實現
上面都還是靜態實現,樹形控制元件肯定是多層的,接下來我們將寫一個渲染樹形控制元件的函式,並且用到遞迴函式完成 HTML 的渲染。
通過上面靜態案例可知,抽離其資料為 JS 陣列:
let treeOcxData = [
{
tip: "分類",
child: [
{ tip: "設計作品" },
{ tip: "技巧雜燴", child: [ { tip: "Web 前端" } ] }
]
},
{
tip: "導航",
child: [ { tip: "固釘" }, { tip: "回到頂部" }, { tip: "麵包屑" } ]
},
{
tip: "資料展示"
}
];
抽離出來之後資料還是挺複雜的,不要慌,萬事開頭難。首先寫一個遞迴函式能不能實現遍歷所有的資料。
遞迴遍歷資料
function rendTreeOcx(data) {
for ( let i = 0; i < data.length; i++ ) {
console.log(data[i].tip);
if ( data[i].child ) {
rendTreeOcx(data[i].child);
}
}
}
每一個數據都成功的遍歷出來了,接下來就是渲染 HTML。
渲染樹形控制元件函式
點選檢視 JS 程式碼
function rendTreeOcx(data, enableFold) {
let template = `<ul class="tree-ocx-ul">`;
if ( enableFold ) template = `<ul class="tree-ocx-ul tree-ocx-ul-enable-fold">`;
for ( let i = 0; i < data.length; i++ ) {
if ( data[i].child ) {
template += `
<li class="tree-ocx-li tree-ocx-li-enable-fold" data-is-folded="false">
<div class="tree-ocx-tip">
${ data[i].tip }
</div>
${ rendTreeOcx(data[i].child, true) }
`;
} else {
template += `
<li class="tree-ocx-li">
<div class="tree-ocx-tip tree-ocx-tip-normal">${ data[i].tip }</div>
`;
}
template += `</li>`;
}
template += `</ul>`;
return template;
}
如果還有子節點就在if ( data[i].child )
體內執行遞迴函式,否則就完成這一層的 HTML。
後面的樣式基本上沒有什麼變化,完整程式碼請看:Gitee 倉庫-當前案例-完整程式碼-v2.0