Before the introduction

Hello, everyone. I am D student from tui Front End Team. Today I will briefly share with you the related content of the virtual long list. rendering

1. Design idea

Virtual list is a technology of on-demand display. In a scene with many similar items, only a few elements in and around the visual area are rendered, and then Dom elements are reused according to the user’s scrolling, which is a means of performance optimization.

The main design ideas are as follows:

  • Utilize reuse of Dom elements outside of the viewable area
  • Add Padding to the container
  • Make the area outside of the viewable area just a padding blank, and only need to render the viewable area and the upper and lower content areas (to avoid too much data and lag rendering of the corresponding data).

Simple Dom structure

<div class="container">
    <ul>
      <li>2020</li>
      <li>2019</li>
      <li>2018</li>
      <li>2017</li>
      <li>2016</li>
      <li>2015</li>
      <li>2014</li>
    </ul>
</div>
Copy the code

Next, before using JavaScript to control behavior, you need to first think about criticality, and the general behavior behind it.

As shown in figure

  • Drop down to the threshold, append the first element to the container, then add poddingTop to the container, and the threshold changes.
  • After the pull-up to set the threshold, insert the last element before the first element to reduce the container paddingTop, threshold change.

In this example, the initial threshold of the pull-down is the height of the three lI’s, and the initial threshold of the pull-up is 0.

Meanwhile, the padding value at the top of the container is calculated dynamically by changing li, so we need to save a variable to calculate the paddingTop of the container.

2. Main code implementation

let ul = document.getElementsByTagName('ul') [0];
let item = document.getElementsByTagName('li') [0];
// The height of a single item
const ItemWidth = parseInt(getComputedStyle(item).height)
let obj = {
  initx: 0.// Controls whether mousemove moves
  isMove: false.// The initial maximum value is used to determine the limit of the pull-up
  max: 0.// Initial minimum value, used to determine the limit of the pull-down
  min: -3 * ItemWidth,
  topIndex: 0
}
// Unscrupulously get the Y offset of the element
function getTransY(el) {
  if(! el.style.transform) {return 0
  }
  let str = el.style.transform.replace(/. *? , /.' ')
  return parseInt(str)
}
Copy the code

After that, step by step on the container for the mouse event monitoring, to achieve the drag effect.

ul.addEventListener('mousedown'.(e) = > {
  // Record the mouse coordinates here to calculate the offset
  // Record the initial offset of ul to add the offset to calculate the total offset
  obj.isMove = true,
  obj.initY = e.clientY
  obj.initTrans = getTransY(ul)
})
ul.addEventListener('mousemove'.(e) = > {
    if(obj.isMove){
      // Original offset + mouse sliding distance
      let abs = obj.initTrans + (e.clientY - obj.initY) 
      // The value cannot be smaller than 0
      abs = abs > 0 ? 0 : abs
      ul.style.transform = `translate3d(0,${abs}px,0)`
      if (abs < obj.min) {
        obj.max -=  ItemWidth
        obj.min -=  ItemWidth
        // changeStatus(0)
        console.log('append')}else if (abs > obj.max) {
        obj.max +=  ItemWidth
        obj.min +=  ItemWidth
        // changeStatus(1)
        console.log('insertBefore')
    }
  }
})
ul.addEventListener('mouseup'.() = > {
  obj.isMove = false
})
Copy the code

Let’s put down the append and insert code and test the logic.

  • additional

  • insert

As you can clearly see, the logic makes sense, and the next thing to do is to operate on the elements in ul.

We extract this complicated logic and pass parameters to achieve both append and insert.

function changeStatus(flag) {
  let lgh = ul.childElementCount
  // Get the first element
  let fir = ul.children[0]
  // Get the last element
  let las = ul.children[lgh - 1]
  if (flag) {
    // insertBefore
    // How does the content change
    las.innerText = +fir.innerText + 1
    // The index used to calculate paddingTop is subtracted by one
    obj.topIndex--
    // Ul's paddingTop placeholder value is subtracted by the height of an item for each element inserted
    ul.style.paddingTop = obj.topIndex * ItemWidth + 'px'
    ul.insertBefore(las, fir)
  } else {
    // append
    // Ul's paddingTop placeholder adds the height of one item each time the first element is appended to the last position
    obj.topIndex++
    ul.style.paddingTop = obj.topIndex * ItemWidth + 'px'
    fir.innerText = +las.innerText - 1
    ul.appendChild(fir)
  }
}
Copy the code

When this is done, add overflow: Hidden to the ul container and you can easily simulate the visual effect of a virtual infinite list.

Based on this idea, we can simply implement a virtual long list date selector.

conclusion

This article is just a simple introduction to some virtual long list of simple implementation, is really to complete a more complete virtual long list, there are many details need to explore the implementation. But the key is to reuse the Dom to populate new data.

For example, expose custom configuration items and implement vertical or horizontal scrolling.

Or in the non-infinite scrolling scene, given a dummy scroll bar, used to simulate the real scroll bar effect, and can let the user roughly understand the length of the content.

Contributions from “Virtual Long List”