摘要:但是如果一剎那我不想選江疏影了,我想選張雨綺因為胸大,首先我要從霍思燕換到高圓圓,然后轉(zhuǎn)到張雨綺,選中展示出來,這時候就要先刪除霍思燕,然后把高圓圓和張雨綺進來。
QingUI是一個UI組件庫寫在前面
目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, Checkbox, Radio, Switch, InputNumber, Input
ES6語法編寫,無依賴
原生模塊化,Chrome63以上支持,請開啟靜態(tài)服務(wù)器預(yù)覽效果,靜態(tài)服務(wù)器傳送門
采用CSS變量配置樣式
辛苦造輪子,歡迎來github倉庫star:QingUI
去年年底項目中嘗試著寫過一個分頁的Angular組件,然后就有了寫QingUI的想法
過程還是非常有意思的
接下來我會用幾篇文章分別介紹每個組件的大概思路,請大家耐心等待
這一篇介紹Cascader級聯(lián)選擇器
點個star就是對我最好的支持
repo: QingUI少廢話,先上圖 數(shù)據(jù)
既然是級聯(lián)選擇器,數(shù)據(jù)肯定是樹形結(jié)構(gòu),像這樣
const data = [ { label: "霍思燕", sub: [ { label: "江疏影", }, { label: "倪妮", }, ], }, { label: "高圓圓", sub: [ { label: "張雨綺", }, { label: "宋佳", }, ], }, ];
你猜我最喜歡哪位女明星 :)
而且用戶點到哪一個label上,就呼出下一級列表
那么問題來了,我怎么知道我當(dāng)前在哪一級,又在哪一個分支?
所以我需要給每一個分支做標(biāo)記,點到哪一個label上,就取出標(biāo)記,該標(biāo)記能指引我找到對應(yīng)的數(shù)據(jù)分支
我的方法是這樣的,新建一個字段queue,字段值是一個字符串形式的數(shù)字,位數(shù)表示當(dāng)前是第幾級,數(shù)字表示當(dāng)前是第幾個分支
比如說queue = 131;,表示當(dāng)前在第一個分支下面的第三個分支下面的第一個分支
buildQueue(data, queue = "") { for (let i = 0; i < data.length; i++) { const item = data[i]; const sub = item.sub; const newQueue = `${queue}${i}`; item[this.queue] = newQueue; if (sub) { this.buildQueue(sub, newQueue); } } }
字段名其實我并沒有用queue,因為有可能已經(jīng)被占用,我用的是時間戳組成的hash值,所以賦值的時候要寫成item[this.queue] = newQueue;
渲染的時候給DOM元素加一個data-v="${item[this.queue]}就行了
那么怎么讀呢?
這些數(shù)字其實就是數(shù)組的索引,加一個遞歸搞定
findSubByQueue(data, queue) { const n = Number.parseInt(queue.charAt(0)); for (let i = 0; i < data.length; i++) { if (i === n) { if (queue.length > 1) { return this.findSubByQueue(data[i].sub, queue.slice(1)); } else { return data[i].sub; } } } }事件
級聯(lián)肯定要支持點擊和懸浮兩種事件觸發(fā)機制
所以我用this.eventType來保存事件類型,其實兩種事件大部分代碼是可以復(fù)用的
for (let i = 0; i < $trunks.length; i++) { const $trunk = $trunks[i]; const v = $trunk.dataset.v; const label = $trunk.querySelector(".label").innerHTML; const CL = $trunk.classList; $trunk.addEventListener(this.eventType, function(event) { event.stopPropagation(); // 遍歷清除trunk的active self.removeTrunkActive($trunks); // 當(dāng)前trunk變成active CL.add("active"); // 構(gòu)建路徑 self.buildPath(v.length, label, false); // 找到子數(shù)據(jù) const sub = self.findSubByQueue(self.data, v); // 填充子board $subBoard.innerHTML = self.renderCascade(sub); // 添加事件 self.$rowEvent($subBoard); }); }
注釋也寫的很清楚,首先是一個高亮的處理,然后要把當(dāng)前路徑保存下來,通過queue找到子數(shù)據(jù),然后渲染出來,最后給子節(jié)點添加事件
要知道,分支可以分為兩種,一種是下面還有分支,我把它稱作trunk,另一種是末梢,下面沒有分支了,我把它稱作leaf
點擊很容易,只給trunk添加事件就可以了
但是懸浮,leaf也要有事件,就是把之前的高亮和子數(shù)據(jù)清空
if (this.trigger === "hover") { for (let i = 0; i < $leafs.length; i++) { $leafs[i].addEventListener("mouseenter", function() { self.removeTrunkActive($trunks); $subBoard.innerHTML = ""; }); } // 離開curtain this.$curtain.addEventListener("mouseleave", function() { self.removeTrunkActive(self.$trunks); self.$subBoard.innerHTML = ""; }); }
那么怎么選中呢?
到leaf才是一個完整的路徑,所以leaf特殊處理,無論是什么事件,點擊leaf選中,把路徑渲染出來
路徑保存路徑是一個動態(tài)的過程
因為我可能查看了某一個分支,然后又查看另一個分支,最終選中了別的分支
所以保存路徑要根據(jù)路徑的長度和當(dāng)前級別來確定是添加還是刪除
比如我現(xiàn)在在江疏影這里,還沒有選中,那么當(dāng)前是第二級,路徑里只保存了霍思燕,如果我選中,那么簡單,直接把江疏影push到數(shù)組里,展示出來。但是如果一剎那我不想選江疏影了,我想選張雨綺(因為胸大),首先我要從霍思燕換到高圓圓,然后轉(zhuǎn)到張雨綺,選中展示出來,這時候就要先刪除霍思燕,然后把高圓圓和張雨綺push進來。
buildPath(level, label, render) { if (this.path.length < level) { // 往下選擇,直接push this.path.push(label); } else { // 退回選擇,根據(jù)退回長度刪除path元素,再push this.path = [...this.path.slice(0, level - 1), label]; } if (render) { this.renderPath(); } }搜索
突發(fā)奇想,我又想加一個搜索功能
比如說我現(xiàn)在搜集了好幾千個女明星,打亂格式化成樹形數(shù)據(jù),那我選擇起來可就困難了,難道每一個分支都查看一遍嗎?如果有搜索,我只要搜張雨綺,所有包含張雨綺的級聯(lián)都展示出來,是不是方便很多!
看起來很復(fù)雜的樣子
其實,換一個思路,初始化的時候就把所有的路徑都遍歷出來,緩存在一個數(shù)組里,搜索的時候只要檢索這些字符串有沒有張雨綺,是不是回到我們熟悉的字符串操作上來了?
遍歷所有路徑
iterateAllPath() { const self = this; let temp = []; const data = pathPush([...this.data]); function pathPush(data, arr = []) { for (const item of data) { item.path = []; // 將路徑存入item中的數(shù)組 item.path.push(...arr, item.label); } return data; } function recursive(data) { for (const item of data) { const sub = item.sub; if (sub) { // 將下一層放入temp temp.push(...pathPush(sub, item.path)); } else { // 沒有下一層則路徑結(jié)束 self.pathPool.push(item.path.join(self.seperator)); } } if (temp.length) { // 重新初始化 data = temp; temp = []; recursive(data); } } recursive(data); }
for (const item of this.pathPool) { const match = item.match(reg); if (!match) { continue; } result.push(item); }
別急,還有需求,我想把關(guān)鍵詞高亮
比如說我搜張雨綺,所有結(jié)果中張雨綺都要高亮
我搜張雨,所有結(jié)果中張雨都要高亮
這個也不復(fù)雜,用關(guān)鍵詞把路徑截成三段,如果關(guān)鍵詞在首尾那就截成兩段
這里有一個小問題,如果分隔符與關(guān)鍵詞之間有空格,展示結(jié)果總是不符合預(yù)期
后來才發(fā)現(xiàn),如果標(biāo)簽內(nèi)第一個字符是空格,空格會被忽略
所以還需要小小的處理一下
const reg = new RegExp(value, "i"); for (const item of this.pathPool) { const match = item.match(reg); if (!match) { continue; } result.push(item); const index = match.index; let [left, center, right] = [item.slice(0, index), match[0], item.slice(index + value.length)]; // 如果標(biāo)簽內(nèi)第一個字符是空格,空格會被忽略 if (right && right.startsWith(" ")) { right = ` ${right.trimLeft()}`; } tpl += `寫在后面${left ? `${left}` : ""} ${center} ${right ? `${right}` : ""}`; } if (!tpl) { tpl = "No Result"; }
Cascader比較核心的邏輯就在這里了
相較前幾篇文檔,隔的時間有點長,不過Cascader不會讓你失望的
如果覺得QingUI還不錯,點個star激勵一下老夫
repo: QingUI
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95316.html
摘要:是一個組件庫目前擁有的組件語法編寫,無依賴原生模塊化,以上支持,請開啟靜態(tài)服務(wù)器預(yù)覽效果,靜態(tài)服務(wù)器傳送門采用變量配置樣式辛苦造輪子,歡迎來倉庫四月份找工作,求內(nèi)推,坐標(biāo)深圳寫在前面去年年底項目中嘗試著寫過一個分頁的組件,然后就有了寫的想法 QingUI是一個UI組件庫目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, ...
摘要:是一個組件庫目前擁有的組件語法編寫,無依賴原生模塊化,以上支持,請開啟靜態(tài)服務(wù)器預(yù)覽效果,靜態(tài)服務(wù)器傳送門采用變量配置樣式辛苦造輪子,歡迎來倉庫四月份找工作,求內(nèi)推,坐標(biāo)深圳寫在前面去年年底項目中嘗試著寫過一個分頁的組件,然后就有了寫的想法 QingUI是一個UI組件庫目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, ...
摘要:是一個組件庫目前擁有的組件語法編寫,無依賴原生模塊化,以上支持,請開啟靜態(tài)服務(wù)器預(yù)覽效果,靜態(tài)服務(wù)器傳送門采用變量配置樣式辛苦造輪子,歡迎來倉庫四月份找工作,求內(nèi)推,坐標(biāo)深圳寫在前面去年年底項目中嘗試著寫過一個分頁的組件,然后就有了寫的想法 QingUI是一個UI組件庫目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, ...
摘要:是一個組件庫目前擁有的組件語法編寫,無依賴原生模塊化,以上支持,請開啟靜態(tài)服務(wù)器預(yù)覽效果,靜態(tài)服務(wù)器傳送門采用變量配置樣式辛苦造輪子,歡迎來倉庫四月份找工作,求內(nèi)推,坐標(biāo)深圳寫在前面去年年底項目中嘗試著寫過一個分頁的組件,然后就有了寫的想法 QingUI是一個UI組件庫目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, ...
摘要:我們就可以將這些請求合并,達到一定數(shù)量我們統(tǒng)一提交。總結(jié)一個比較生動的例子給大家講解了一些多線程的具體運用。學(xué)習(xí)多線程應(yīng)該多思考多動手,才會有比較好的效果。地址徒手擼框架系列文章地址徒手擼框架實現(xiàn)徒手擼框架實現(xiàn) 原文地址:https://www.xilidou.com/2018/01/22/merge-request/ 在高并發(fā)系統(tǒng)中,我們經(jīng)常遇到這樣的需求:系統(tǒng)產(chǎn)生大量的請求,但是這...
閱讀 2259·2023-04-26 01:50
閱讀 714·2021-09-22 15:20
閱讀 2595·2019-08-30 15:53
閱讀 1596·2019-08-30 12:49
閱讀 1714·2019-08-26 14:05
閱讀 2714·2019-08-26 11:42
閱讀 2309·2019-08-26 10:40
閱讀 2602·2019-08-26 10:38