1. 程式人生 > 其它 >Web 前端實戰(二):JQuery 實現樹形控制元件

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-tipsub-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