Implementation code of infinite scrolling with n container elements

Time:2020-1-12

scene

How to correctly render a list of up to 10000 elements.

Infinite pull-down loading technology enables users to scroll and view a large number of block contents all the time. This approach is to keep loading new content as you scroll down.

When you use scrolling as your primary method of discovering data, it may allow your users to stay longer on the web and increase user engagement. With the popularity of social media, a lot of data is consumed by users. Wireless scrolling provides an efficient way for users to browse massive information without waiting for the page to be preloaded.

If we change the way of thinking, if there are 10000 components on a page, how can we use five components to dynamically render the whole page?

Solutions

In the long list optimization, we have many solutions, such as paging, lazy loading and so on. There is very good interaction (Chrysanthemum chart), let the user wait. Such a program is also very successful.

How to find another way?

1. In the single page application, can we analyze the paging scheme once? There are 10 elements on each page, and each paging renders 10 elements according to the

2. Can we use a bracket to support the length of the whole list? When the screen scrolls to the corresponding position, it will render the corresponding 10 elements

Flicker problem

When we implement according to this idea, there will be flickering problems. Because of frequent rolling events, we find that the elements we see are constantly redrawing, because we find which ten elements need to be rendered each time we cross the field, and then we directly replace them

The idea here is that when I see 10 screen position renderings and 10 upward and downward derivative renderings, a total of 30. When I control the rendering, I always replace the top or bottom elements, so that the part we can see in the middle is not redrawn. Just redraw the pre rendered data.

principle

Implement a component to display a list of fixed window size n items with 5 elements: that is, at any time, there are only 5 DOM containers on infinite scrolling n elements.


 <ul>
        <li style="transform: translate3d(0px, 0px, 0px);">……</li>
        <li style="transform: translate3d(0px, 60px, 0px);">……</li>
    </ul>
  • The content of a long list generally has rules. The height of this list can be calculated directly by the number of < li >. For example, 1000 elements, each element is 60px high, so we can quickly calculate the height of a real list is 60000px
  • After having the container, absolute positioning of the internal < li > relative to the < UL > is performed. In JS, the ‘Transform: translate3d (0px, 0px, 0px);’ attribute of each < li > is calculated directly
  • Through monitoring the scroll time, find out the < li > that needs to be rendered at the current location. Compare with the previous group of rendered data, if there is the same < li > then skip, find out the different elements of the previous group of rendered data, and then make corresponding replacement

For example, if there are 100 elements, the home page will display 5, and initialization [0,1,2,3,4] 5 < li > need to be rendered. When I scroll down a bit, I will see [1,2,3,4,5] these < li > need to be rendered. At this time, do not directly replace the whole, only replace the difference item, and the structure should be [5,1,2,3,4]. Because it is absolute positioning, it will break away from the standard flow, and a single redraw will not affect the other four To improve performance.

How to achieve

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
  <title>Document</title>
  <style>
    body,
    ul,
    li {
      margin: 0;
      padding: 0;
      list-style: none;
    }

    ul {
      position: relative;
    }

    ul li {
      position: absolute;
      top: 0;
      width: 100%;
      height: 31px;
      line-height: 32px;
      border-bottom: 1px solid #ccc;
    }
  </style>
</head>

<body>
  <ul>
  </ul>
</body>
<script>
  // total container
  var list = [];
  //Element container in visual range
  var showList = [];
  //Render container
  var renderList = [];
  //Height of each container
  var lineHeight = 32

  //Initialize 1000 elements
  for (var i = 0; i < 1000; i++) {
    List. Push ({ID: I, text: 'the' + (I + 1) + 'elements', top: I * lineheight, bottom: (I + 1) * lineheight})
  }
  //Initialize container height
  $('ul').attr('style', 'height:' + 1000 * lineHeight + 'px')
  //Find the
  function render(top, bottom) {
    showList = []
    //What data is marked and what has been rendered are duplicate, this part will not be rendered repeatedly
    var sameIndex = []
    //Find the scroll position on which other element
    var currentIndex = 0
    for (var i = 0; i < list.length; i++) {
      var item = list[i]
      if (item.top <= window.scrollY && item.bottom > window.scrollY) {
        currentIndex = i;
        break;
      }
    }
    var range = 0
    //Find the elements to be displayed up and down in the currently found elements until the total number reaches 35
    while (range < 100 && showList.length + sameIndex.length < 35) {
      if (currentIndex - range >= 0) {
        //Compare whether the elements that meet the conditions are in the already rendered list, mark them when they are not, and replace the elements that are not marked when they are not in the showlist
        if (renderList.includes(list[currentIndex - range].id)) {
          // sameIndex.push(currentIndex-range)
          sameIndex.unshift(renderList.indexOf(list[currentIndex - range].id))
        } else {
          showList.unshift(list[currentIndex - range])
        }
      }

      if (currentIndex + range < list.length && range != 0) {
        if (renderList.includes(list[currentIndex + range].id)) {
          sameIndex.push(renderList.indexOf(list[currentIndex + range].id))
        } else {
          showList.push(list[currentIndex + range])
        }
      }
      range++
    }
    //Replace the new elements that need to be rendered with the ones that are not marked
    if (renderList.length > 0) {
      for (var i = 0; i < renderList.length; i++) {
        if (!sameIndex.includes(i) && showList.length) {
          renderList[i] = showList.shift().id
          $('ul li').eq(i).html(list[renderList[i]].id + list[renderList[i]].text).attr('style', 'transform: translate3d(0px, ' + list[renderList[i]].top + 'px, 0px);')
        }
      }
    } else {
      //Initial list for the first time
      renderList = showList.map(function (val) { return val.id })
      renderList.map(function (key) {
        $('ul').append($('<li style="transform: translate3d(0px, ' + list[key].top + 'px, 0px);"">#' + list[key].id + list[key].text + '</li>'))
      })
    }
    console.log(renderList)
  }
  //First render
  render()
  $(window).scroll(function (e) {
    render()
  });

</script>

</html>

TODO

  1. Compared with the method of replacing container elements, it always feels like it can be optimized, so as to improve the operation efficiency and optimize the white screen with fast scrolling
  2. Here is also a thinking question [0,1 , 10000], take 5 elements out of the array each time, compare the new one with the old one, keep the intersection part of the two arrays, replace the non intersection part of the old array with the new one, for example, the first one is [0,1,2,3,4], the second one is [2,3,4,5,6], then generate [5,6,2,3,4] after comparison, and the third one is [4,5,6,7,8], then generate [5,6,7,8,4]. Get the result array with the minimum number of codes.

Summary

  1. Layout and initialization by data
  2. Separate the container from the standard flow by feeling the orientation
  3. Through data comparison, we can find out different container elements and redraw as few container elements as possible each time.

Next issue – long list of components
 

Key points

  1. Components are different from regular lists. The height of components may be uncontrollable, and different heights will appear according to different mobile devices
  2. Due to the same height of components, the number of several components in the rendering area is different, which results in different number of containers
  3. For the height is not easy to calculate directly through the data, if you need a whole rendering and then calculate the location and height through DOM, it is very expensive to load the performance for the first time.

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.