成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

可拖動(dòng)table表頭的實(shí)現(xiàn)

canopus4u / 3535人閱讀

摘要:前言自己做的項(xiàng)目碰到這樣一個(gè)需求,就是對(duì)所有的表格添加表頭可以拖動(dòng)的效果。需要說明的是,表頭固定的那種是需要用兩個(gè)去實(shí)現(xiàn),做過的人應(yīng)該也都明白。拜拜后續(xù)補(bǔ)充更改了寬度改變的方式,應(yīng)該是只改變拖動(dòng)列后面的列的寬度。

前言

自己做的項(xiàng)目碰到這樣一個(gè)需求,就是對(duì)所有的表格添加表頭可以拖動(dòng)的效果。我一想,這不簡(jiǎn)單,分分鐘鐘給你做出來。拿起我的電腦,啪啪啪就敲起來了。

一定是哪里不對(duì),以我的聰明才智,結(jié)果應(yīng)該不是這樣的,然后凈下心來,好好理了下思路后,總算是做出來了。
至于結(jié)果嘛,我肯定是做出來的,像下面這種:

準(zhǔn)備

首先要說明的是,我們的項(xiàng)目使用的表格大概只分為兩類,一類是表頭不固定,就是普通的表格,另一類是表頭固定,tbody部分是可以滾動(dòng)的。需要說明的是,表頭固定的那種是需要用兩個(gè)table去實(shí)現(xiàn),做過的人應(yīng)該也都明白。前者看起來比較簡(jiǎn)單,因?yàn)閷挾仁鞘?b>thead里的th影響的,后者看起來就不好處理,因?yàn)槟阌脙蓚€(gè)table就會(huì)出現(xiàn)下面的情況:

emmm,這和我們想象的應(yīng)該不一樣,這可咋整,感覺處理起來很麻煩啊。想起看過element-ui中的表格,似乎有拖動(dòng)表頭的實(shí)現(xiàn),先打開控制臺(tái)看下結(jié)構(gòu)吧:

呃,話說長(zhǎng)這么大我都沒用過這兩個(gè)標(biāo)簽,但仔細(xì)觀察上面有個(gè)width,看到這大概也知道是怎么回事了,打開MDN看下相關(guān)屬性的描述,和想的一樣,width能控制當(dāng)前列的寬度。
寬度的控制我們是解決了,還有一個(gè)問題,就是拖動(dòng)后,其他列的寬度改怎么改變,如下:

a b c d

如果我拖動(dòng)a列,改變的寬度應(yīng)該怎樣分配到b,c,d上,我這里是這樣處理的,b、c、d有個(gè)屬性去表示該列是否已經(jīng)被拖動(dòng)過了,如果b、c、d都沒拖動(dòng)過,那么把a(bǔ)改變的寬度平分到b、c、d三列的寬度上,如果b、c、d都改變了話,那么只改變最后一列d的寬度。好了,思路已經(jīng)有了,我們可以去實(shí)現(xiàn)了。
事實(shí)證明,如果按照上面的設(shè)計(jì)就太蠢了,已經(jīng)改成只改變拖動(dòng)列后面的列且這些列沒有改變過寬度。

實(shí)現(xiàn)

首先html結(jié)構(gòu)大概是這樣的:

a b
1 2

js方面

  constructor (id, options) {
    this._el = document.querySelector(`#${id}`);
    // 實(shí)際使用中需要對(duì)dom結(jié)構(gòu)進(jìn)行判斷,這里就不做了
    this._tables = Array.from(this._el.querySelectorAll("table"));
    setTimeout(() => this._resolveDom());

    this.store = {
      dragging: false,                 //是否拖動(dòng)
      draggingColumn: null,            //拖動(dòng)的對(duì)象
      miniWidth: 30,                   //拖動(dòng)的最小寬度
      startMouseLeft: undefined,       //鼠標(biāo)點(diǎn)擊時(shí)的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);

//不管是一個(gè)table還是兩個(gè),都把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);
}

//拖動(dòng)時(shí)的占位線
const hold = document.createElement("div");
hold.classList.add("resizable-hold");
this._el.appendChild(hold);

上面這塊就是添加節(jié)點(diǎn)的,對(duì)dom進(jìn)行處理,為了復(fù)用,這里我們不管你是表頭固定還是表頭不固定,我們都拆分為兩個(gè)table,這樣處理起來也方便的多。
然后就是處理手指移到列右側(cè)cursor的值設(shè)為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是元素右側(cè)距離頁面左邊緣的距離,不是離頁面右邊緣的距離。這里就是給theadtr添加mousemove事件,當(dāng)鼠標(biāo)指針距離右邊緣小于8的時(shí)候,改變指針形狀,然后改變store里的狀態(tài),表示此時(shí)點(diǎn)擊是可以拖動(dòng)的了。
然后就是mousedown+mousemove+mouseup來處理拖動(dòng)了:

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";
    };

    // 寬度是這樣分配的,舉個(gè)?,如果a,b,c,d,他們每個(gè)都有個(gè)changed狀態(tài),默認(rèn)false,拖過a,a.changed改為true,改變的寬度就由剩下的b,c,d平攤,如果都改變了,就讓最后一個(gè)元素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主要是用來判斷是點(diǎn)擊還是拖動(dòng),防止拖動(dòng)觸發(fā)排序
      setTimeout(() => {
        target.classList.remove("noclick");
      }, 0);
    };

    document.addEventListener("mouseup", handleOnMouseUp);
    document.addEventListener("mousemove", handleOnMouseMove);
  }
};
Tr.addEventListener("mousedown", handleMouseDown);

預(yù)覽效果 (chrome + Safari + Firefox)

總結(jié)

覺得很有意思也很有用的東西,也讓自己漲了很多姿勢(shì),源碼,已經(jīng)做成類的形式,使用起來還算簡(jiǎn)單,因?yàn)槭峭蝗惶岢龅男枨螅€未做過多測(cè)試,可能存在不知道的bug。

祝福

寫在最后,馬上就要過年了,心情還是非常happy的。那么,我就在這里提前祝大家新年大吉、、吧,皮一下才開心,哎嘿嘿。拜拜~

后續(xù)補(bǔ)充

更改了寬度改變的方式,應(yīng)該是只改變拖動(dòng)列后面的列的寬度。有BUG,colgroup放在了thead下面,導(dǎo)致在safari下面有BUG,已經(jīng)修復(fù)了,看的不仔細(xì),但上面的代碼還沒有改,看代碼的化還是去看源碼,我沒發(fā)現(xiàn)這個(gè)問題,別人幫我找出來的。
emmmmm,又發(fā)現(xiàn)了一個(gè)問題,就是拖動(dòng)最后一列時(shí)。。。我想想,先睡了==

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/51835.html

相關(guān)文章

  • 拖動(dòng)table表頭實(shí)現(xiàn)

    摘要:前言自己做的項(xiàng)目碰到這樣一個(gè)需求,就是對(duì)所有的表格添加表頭可以拖動(dòng)的效果。需要說明的是,表頭固定的那種是需要用兩個(gè)去實(shí)現(xiàn),做過的人應(yīng)該也都明白。拜拜后續(xù)補(bǔ)充更改了寬度改變的方式,應(yīng)該是只改變拖動(dòng)列后面的列的寬度。 前言 自己做的項(xiàng)目碰到這樣一個(gè)需求,就是對(duì)所有的表格添加表頭可以拖動(dòng)的效果。我一想,這不簡(jiǎn)單,分分鐘鐘給你做出來。拿起我的電腦,啪啪啪就敲起來了。showImg(https:...

    paulli3 評(píng)論0 收藏0
  • 列表——表頭自定義顯示字段

    摘要:今天我就來講講插件的使用,它是如何實(shí)現(xiàn)列表表頭自定義顯示字段的,我把我的經(jīng)驗(yàn)分享出來,滿足一下不懂英語的人,給你們搭個(gè)快車。需求分析實(shí)現(xiàn)列表表頭自定義顯示字段,自定義表頭排序。 序言 Yii2框架的擴(kuò)展性能真的很不錯(cuò),很多效果都可以通過插件去實(shí)現(xiàn),你想不到的老外都幫你想好了,于是,人群中就流傳了這么一句話:效果不會(huì)寫不要緊,會(huì)用插件也不錯(cuò)。GitHub是一個(gè)龐大而且開放的資源庫,平時(shí)有...

    Yangyang 評(píng)論0 收藏0
  • vueTable大數(shù)據(jù)展示優(yōu)化

    摘要:懶加載方式常見的有淘寶一屏用元素占據(jù)一定的高度,然后再去拉圖片數(shù)據(jù)。但這種方式還是需要元素占位,淘寶一頁的數(shù)據(jù)量其實(shí)不算大,因?yàn)樗Y(jié)合了分頁。 背景 showImg(https://segmentfault.com/img/bVbhSVh?w=1606&h=440);大數(shù)據(jù)項(xiàng)目根據(jù)用戶輸入代碼查詢數(shù)據(jù),用戶的代碼不可控(比如select from db limit 5000),有可能...

    JasinYip 評(píng)論0 收藏0
  • 一種局部固定表頭實(shí)現(xiàn)方案(橫向跟隨內(nèi)容一同滾動(dòng),縱向鎖定表頭

    摘要:將設(shè)置為將和的放入一個(gè)的中,設(shè)置橫向軸可以滾動(dòng),軸不可滾動(dòng)。表頭和表內(nèi)容的橫向方向滾動(dòng)能力其實(shí)是父級(jí)樣式賦予的。 1.為了固定表頭我們需要先把表格的head和tbody切分到兩個(gè)table里: 和 里。 2.將table-body放在一個(gè)table-body-box的div里設(shè)置y軸方向可以滾動(dòng),x軸方向不可滾動(dòng),這樣表內(nèi)容就能在自己的區(qū)域?qū)崿F(xiàn)垂直方向的滾動(dòng)了。3.將table-hea...

    zhkai 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<