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