首頁 > web前端 > js教程 > 主體

如何實作可拖曳table表頭

小云云
發布: 2018-02-10 15:30:13
原創
2287 人瀏覽過

首先要說明的是,我們的項目使用的表格大概只分為兩類,一類是表頭不固定,就是普通的表格,另一類是表頭固定,tbody#部分是可以滾動的。需要說明的是,表頭固定的那種是需要用兩個table去實現,做過的人應該也都明白。前者看起來比較簡單,因為寬度是受thead裡的th影響的,後者看起來就不好處理,因為你用兩個table就會出現下面的情況:
如何實作可拖曳table表頭
emmm,這和我們想像的應該不一樣,這可咋整,感覺處理起來很麻煩啊。想起看過element-ui中的表格,似乎有拖曳表頭的實現,先打開控制台看下結構吧:
如何實作可拖曳table表頭
呃,話說長這麼大我都沒用過<colgroup><col>這兩個標籤,但仔細觀察上面有個width,看到這大概也知道是怎麼回事了,打開MDN看下相關屬性的描述,和想的一樣,width能控制目前列的寬度。
寬度的控制我們是解決了,還有一個問題,就是拖曳後,其他列的寬度改怎麼改變,如下:

##a#bcd

如果我拖曳a列,改變的寬度應該怎樣分配到b,c,d上,我這裡是這樣處理的,b、c、d有個屬性去表示該列是否已經被拖曳過了,如果b、c、d都沒拖曳過,那麼把a改變的寬度平分到b、c、d三列的寬度上,如果b、c、d都改變了話,那麼只改變最後一列d的寬度。好了,思路已經有了,我們可以去實現了。
事實證明,如果按照上面的設計就太蠢了,已經改成只改變拖曳列後面的列且這些列沒有改變過寬度。

實作

首先html結構大概是這樣的:

<table>
  <thead>
    <tr>
      <th>a<th>
      <th>b<th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1<th>
      <th>2<th>
    </tr>
  </tbody>
</table>
登入後複製

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('p');
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 = &#39;col-resize&#39;;
      target.style.cursor = &#39;col-resize&#39;;
      this.store.draggingColumn = target;
    } else {
      bodyStyle.cursor = &#39;&#39;;
      target.style.cursor = &#39;pointer&#39;;
      this.store.draggingColumn = null;
    }
  }
};
登入後複製

需要注意的是,getBoundingClientRect( )取得的rigth是元素右側距離頁面左邊緣的距離,不是離頁面右邊緣的距離。這裡就是給theadtr新增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';
    };

    // 寬度是這樣分配的,舉個
登入後複製

以上是如何實作可拖曳table表頭的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!