摘要:前言自己做的項目碰到這樣一個需求,就是對所有的表格添加表頭可以拖動的效果。需要說明的是,表頭固定的那種是需要用兩個去實現,做過的人應該也都明白。拜拜后續補充更改了寬度改變的方式,應該是只改變拖動列后面的列的寬度。
前言
自己做的項目碰到這樣一個需求,就是對所有的表格添加表頭可以拖動的效果。我一想,這不簡單,分分鐘鐘給你做出來。拿起我的電腦,啪啪啪就敲起來了。
一定是哪里不對,以我的聰明才智,結果應該不是這樣的,然后凈下心來,好好理了下思路后,總算是做出來了。
至于結果嘛,我肯定是做出來的,像下面這種:
首先要說明的是,我們的項目使用的表格大概只分為兩類,一類是表頭不固定,就是普通的表格,另一類是表頭固定,tbody部分是可以滾動的。需要說明的是,表頭固定的那種是需要用兩個table去實現,做過的人應該也都明白。前者看起來比較簡單,因為寬度是受thead里的th影響的,后者看起來就不好處理,因為你用兩個table就會出現下面的情況:
emmm,這和我們想象的應該不一樣,這可咋整,感覺處理起來很麻煩啊。想起看過element-ui中的表格,似乎有拖動表頭的實現,先打開控制臺看下結構吧:
呃,話說長這么大我都沒用過
寬度的控制我們是解決了,還有一個問題,就是拖動后,其他列的寬度改怎么改變,如下:
a | b | c | d |
---|
如果我拖動a列,改變的寬度應該怎樣分配到b,c,d上,我這里是這樣處理的,b、c、d有個屬性去表示該列是否已經被拖動過了,如果b、c、d都沒拖動過,那么把a改變的寬度平分到b、c、d三列的寬度上,如果b、c、d都改變了話,那么只改變最后一列d的寬度。好了,思路已經有了,我們可以去實現了。
事實證明,如果按照上面的設計就太蠢了,已經改成只改變拖動列后面的列且這些列沒有改變過寬度。
首先html結構大概是這樣的:
a | b | ||
---|---|---|---|
1 | 2 |
js方面
constructor (id, options) { this._el = document.querySelector(`#${id}`); // 實際使用中需要對dom結構進行判斷,這里就不做了 this._tables = Array.from(this._el.querySelectorAll("table")); setTimeout(() => this._resolveDom()); this.store = { dragging: false, //是否拖動 draggingColumn: null, //拖動的對象 miniWidth: 30, //拖動的最小寬度 startMouseLeft: undefined, //鼠標點擊時的clientX startLeft: undefined, //th右離table的距離 startColumnLeft: undefined, //th左離table的距離 tableLeft: undefined, //table離頁面左邊的距離, HColumns: [], BColumns: [], }; };
添加dom:
const [ THeader ] = this._tables; let TBody; const Tr = THeader.tHead.rows[0]; const columns = Array.from(Tr.cells); const Bcolgroup = document.createElement("colgroup"); const cols = columns.map((item, index) => { const col = document.createElement("col"); item.dataset.index = index; col.width = +item.offsetWidth; return col; }); cols.reduce((newDom, item) => { newDom.appendChild(item); return newDom; }, Bcolgroup); const HColgroup = Bcolgroup.cloneNode(true); THeader.appendChild(HColgroup); //不管是一個table還是兩個,都把header和body提出來 if (this._tables.length === 1) { const [ , tbody ] = Array.from(THeader.children); tbody.remove(); TBody = THeader.cloneNode(); TBody.appendChild(Bcolgroup); TBody.appendChild(tbody); this._el.appendChild(TBody); } else { [ , TBody ] = this._tables; TBody.appendChild(Bcolgroup); } //拖動時的占位線 const hold = document.createElement("div"); hold.classList.add("resizable-hold"); this._el.appendChild(hold);
上面這塊就是添加節點的,對dom進行處理,為了復用,這里我們不管你是表頭固定還是表頭不固定,我們都拆分為兩個table,這樣處理起來也方便的多。
然后就是處理手指移到列右側cursor的值設為col-resize:
handleMouseMove(evt) { //... if (!this.store.dragging) { const rect = target.getBoundingClientRect(); const bodyStyle = document.body.style; if (rect.width > 12 && rect.right - event.pageX < 8) { bodyStyle.cursor = "col-resize"; target.style.cursor = "col-resize"; this.store.draggingColumn = target; } else { bodyStyle.cursor = ""; target.style.cursor = "pointer"; this.store.draggingColumn = null; } } };
需要注意的是,getBoundingClientRect()獲取的rigth是元素右側距離頁面左邊緣的距離,不是離頁面右邊緣的距離。這里就是給thead的tr添加mousemove事件,當鼠標指針距離右邊緣小于8的時候,改變指針形狀,然后改變store里的狀態,表示此時點擊是可以拖動的了。
然后就是mousedown+mousemove+mouseup來處理拖動了:
const handleMouseDown = (evt) => { if (this.store.draggingColumn) { this.store.dragging = true; let { target } = evt; if (!target) return; const tableEle = THeader; const tableLeft = tableEle.getBoundingClientRect().left; const columnRect = target.getBoundingClientRect(); const minLeft = columnRect.left - tableLeft + 30; target.classList.add("noclick"); this.store.startMouseLeft = evt.clientX; this.store.startLeft = columnRect.right - tableLeft; this.store.startColumnLeft = columnRect.left - tableLeft; this.store.tableLeft = tableLeft; document.onselectstart = () => false; document.ondragstart = () => false; hold.style.display = "block"; hold.style.left = this.store.startLeft + "px"; const handleOnMouseMove = (event) => { const deltaLeft = event.clientX - this.store.startMouseLeft; const proxyLeft = this.store.startLeft + deltaLeft; hold.style.left = Math.max(minLeft, proxyLeft) + "px"; }; // 寬度是這樣分配的,舉個?,如果a,b,c,d,他們每個都有個changed狀態,默認false,拖過a,a.changed改為true,改變的寬度就由剩下的b,c,d平攤,如果都改變了,就讓最后一個元素d背鍋 const handleOnMouseUp = (event) => { if (this.store.dragging) { const { startColumnLeft } = this.store; const finalLeft = parseInt(hold.style.left, 10); const columnWidth = finalLeft - startColumnLeft; const index = +target.dataset.index; HColgroup.children[index].width = columnWidth; if (index !== this.store.HColumns.length - 1) { this.store.HColumns[index].isChange = true; } const deltaLeft = event.clientX - this.store.startMouseLeft; const changeColumns = this.store.HColumns.filter(v => !v.isChange && +v.el.width > 30); changeColumns.forEach(item => { item.el.width = +item.el.width - deltaLeft / changeColumns.length; }); this.store.BColumns.forEach((item, i) => { item.el.width = this.store.HColumns[i].el.width; }); //...init store } document.removeEventListener("mousemove", handleOnMouseMove); document.removeEventListener("mouseup", handleOnMouseUp); document.onselectstart = null; document.ondragstart = null; // noclick主要是用來判斷是點擊還是拖動,防止拖動觸發排序 setTimeout(() => { target.classList.remove("noclick"); }, 0); }; document.addEventListener("mouseup", handleOnMouseUp); document.addEventListener("mousemove", handleOnMouseMove); } }; Tr.addEventListener("mousedown", handleMouseDown);
預覽效果 (chrome + Safari + Firefox)
總結覺得很有意思也很有用的東西,也讓自己漲了很多姿勢,源碼,已經做成類的形式,使用起來還算簡單,因為是突然提出的需求,還未做過多測試,可能存在不知道的bug。
祝福寫在最后,馬上就要過年了,心情還是非常happy的。那么,我就在這里提前祝大家新年大吉、、吧,皮一下才開心,哎嘿嘿。拜拜~
更改了寬度改變的方式,應該是只改變拖動列后面的列的寬度。有BUG,colgroup放在了thead下面,導致在safari下面有BUG,已經修復了,看的不仔細,但上面的代碼還沒有改,看代碼的化還是去看源碼,我沒發現這個問題,別人幫我找出來的。
emmmmm,又發現了一個問題,就是拖動最后一列時。。。我想想,先睡了==
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107383.html
摘要:前言自己做的項目碰到這樣一個需求,就是對所有的表格添加表頭可以拖動的效果。需要說明的是,表頭固定的那種是需要用兩個去實現,做過的人應該也都明白。拜拜后續補充更改了寬度改變的方式,應該是只改變拖動列后面的列的寬度。 前言 自己做的項目碰到這樣一個需求,就是對所有的表格添加表頭可以拖動的效果。我一想,這不簡單,分分鐘鐘給你做出來。拿起我的電腦,啪啪啪就敲起來了。showImg(https:...
摘要:今天我就來講講插件的使用,它是如何實現列表表頭自定義顯示字段的,我把我的經驗分享出來,滿足一下不懂英語的人,給你們搭個快車。需求分析實現列表表頭自定義顯示字段,自定義表頭排序。 序言 Yii2框架的擴展性能真的很不錯,很多效果都可以通過插件去實現,你想不到的老外都幫你想好了,于是,人群中就流傳了這么一句話:效果不會寫不要緊,會用插件也不錯。GitHub是一個龐大而且開放的資源庫,平時有...
摘要:懶加載方式常見的有淘寶一屏用元素占據一定的高度,然后再去拉圖片數據。但這種方式還是需要元素占位,淘寶一頁的數據量其實不算大,因為它結合了分頁。 背景 showImg(https://segmentfault.com/img/bVbhSVh?w=1606&h=440);大數據項目根據用戶輸入代碼查詢數據,用戶的代碼不可控(比如select from db limit 5000),有可能...
摘要:將設置為將和的放入一個的中,設置橫向軸可以滾動,軸不可滾動。表頭和表內容的橫向方向滾動能力其實是父級樣式賦予的。 1.為了固定表頭我們需要先把表格的head和tbody切分到兩個table里: 和 里。 2.將table-body放在一個table-body-box的div里設置y軸方向可以滾動,x軸方向不可滾動,這樣表內容就能在自己的區域實現垂直方向的滾動了。3.將table-hea...
閱讀 2405·2021-11-18 10:02
閱讀 1922·2021-10-13 09:40
閱讀 2999·2021-09-07 10:07
閱讀 2105·2021-09-04 16:48
閱讀 1005·2019-08-30 13:18
閱讀 2451·2019-08-29 14:03
閱讀 2921·2019-08-29 12:54
閱讀 3155·2019-08-26 11:41