iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0
Modern Web

web component - 次世代網頁技術的重要拼圖系列 第 24

web component 的實做- infinite-scroller

  • 分享至 

  • xImage
  •  

還在修改程式碼,今天也會晚一點放上關鍵的程式碼。還有,明天一定放上github給連結...

目標

把昨天的virtualized list組件加入infinite-scroller功能。

技術點

可以參考這篇文章。裡面有提到許多有關infinite-scroller的技術點

  1. 在滾動到最下面時,會出現一個Tombstones來標明正在載入新的項目
  2. 增加一個或多個新的節點
  3. 新增節點的事件
  4. 重新計算高度和定位點

主思路

  1. 還是使用slot元素的name屬性,可以限定Tombstones一定在最下面。
  2. 新增節點時由外部增加節點到web component。
  3. 使用MutationObserver這個Web API監視子節點的數量,如果有增加的話就重新計算高度和定位點

新增功能後的未完成問題

  1. 快速捲動時,節點出現的速度可能比較慢。
  2. 因為只有關鍵的程式碼,防呆的機制沒能加入。
  3. 因為之前都是用react實做,這次是第一次改用web component,也許還有其他問題?

實做

用法

簡單來說,就是多了二個事件

  1. handleAddItem會在快到底部時生成事件,如果只要生成一個組件,detail的indexArr就是新組件的索引值。
  2. addItemFinish會在生成組件後生成事件。
<body>
    <my-list id='my-list' height="500" width="120" itemSize="100 " itemCount="15">
        <my-list-item><img src="https://fakeimg.pl/98x98/ff0000/?text=1" loading="lazy"></my-list-item>
        <!-- 中間略過 -->
        <my-list-item><img src="https://fakeimg.pl/98x98/0000ff/?text=15" loading="lazy"></my-list-item>
    </my-list>
    <script type="module">
        import { myList, myListItem } from "./list.js";
        customElements.define("my-list", myList);
        customElements.define("my-list-item", myListItem);
        const list = document.getElementById("my-list");
        list.addEventListener("handleAddItem", (e) => {
            const indexArr = e.detail.indexArr
            return indexArr.forEach((index) => {
                const item = document.createElement("my-list-item");
                if (index % 3 === 0) {
                    item.innerHTML = `<img src="https://fakeimg.pl/98x98/ff0000/?text=${index + 1}" loading="lazy">`;
                } else if (index % 3 === 1) {
                    item.innerHTML = `<img src="https://fakeimg.pl/98x98/00ff00/?text=${index + 1}" loading="lazy">`;
                } else {
                    item.innerHTML = `<img src="https://fakeimg.pl/98x98/0000ff/?text=${index + 1}" loading="lazy">`;
                }
                list.appendChild(item);
            })
        });
        list.addEventListener('addItemFinish', () => {
            console.log('addItemFinish')
        })
    </script>
</body>

組件重要變數

多了一個mutationObserver的變數用來存MutationObserver

mutationObserver = null

Tombstones和對應事件

組件本體:這裡使用的Tombstones沒有太多裝飾,主要是有固定的寬和高

export class myListTombstones extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        :host {
            display: block;
            width: 100px;
            height: 100px;
            background-color: #eee;
            border: 1px solid #ccc;
            box-sizing: border-box;
        }
      </style>
      <div>Loading...</div>
    `;
  }
}

組件內使用:這裡也寫的很簡單,在列表的最後面放入tombstone,並且修改高度和增加定位點

setTombstones() {
  if (!customElements.get('my-list-tombstones') ) {
    customElements.define("my-list-tombstones", myListTombstones);
  }
  this.tombstonesNode = document.createElement('my-list-tombstones');
  this.contentNode.appendChild(this.tombstonesNode);
  this.tombstonesNode.style = `
  position: absolute;
  top: ${this.itemCount * this.itemSize}px;
  left: 0;
`
this.contentNode.style.height = `${this.itemCount * this.itemSize + this.itemSize}px`;
}

引用新增項目的事件

使用MutationObserver,監看組件有沒有增加子組件,有的話就增加定位點,修改高度和加入indexNodeMap,最後再回傳事件

mutationCallback(mutationsList, observer) {
for (const mutation of mutationsList) {
  if (mutation.type === 'childList') {
    const addedNodes = mutation.addedNodes;
    for (const node of addedNodes) {
      node.style = `
      position: absolute;
      top: ${this.itemCount * this.itemSize}px;
      `
      this.indexNodeMap.set(this.itemCount, node);
      this.itemCount = this.itemCount + 1
    }
    this.contentNode.style.height = `${( this.itemCount) * this.itemSize + this.itemSize}px`; 
    this.tombstonesNode.style.top = `${this.itemCount * this.itemSize}px`;
  }
}
this.dispatchEvent(new CustomEvent('addItemFinish'))
}
setMutationObserver() {
const config = { attributes: true, childList: true, characterData: true };
this.mutationObserver = new MutationObserver(this.mutationCallback.bind(this));
this.mutationObserver.observe(this, config);
}

上一篇
web component 的實做- virtualized list
下一篇
web component 的實做- 實做組件庫或大量使用web component的建議
系列文
web component - 次世代網頁技術的重要拼圖30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言