n个容器元素实现无限滚动的实现代码

所属分类: HTML/Xhtml / 网页制作 阅读数: 1456
收藏 0 赞 0 分享

场景

如何正确渲染多达10000个元素的列表。

无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加载新内容。

当你使用滚动作为发现数据的主要方法时,它可能使你的用户在网页上停留更长时间并提升用户参与度。随着社交媒体的流行,大量的数据被用户消费。无线滚动提供了一个高效的方法让用户浏览海量信息,而不必等待页面的预加载。

如果我们换一种思维方式,如果一个页面上面有10000个通栏组件构成,我们如何使用5个组件动态去渲染整个页面呢?

解决思路

在对长列表的优化中我们有很多的方案,例如分页,懒加载等等。 有非常好的交互(菊花图),让用户去等待一下。这样的方案也是非常的成功。

如何另辟蹊径?

1. 在单页运用中我们是否可以对分页方案做一次分析,每页10个元素,分页每次渲染10个元素根据

2. 我们能否用一个支架撑起整个列表的长度 当屏幕滚动到对应的位置就渲染对应的10个元素

闪烁问题

当我们按这个思路实现后会出现闪烁的问题,由于滚动事件比较频繁,我们发现在我们看见的元素在不断的重新绘制,因为我们每次的过场是找到哪十个元素是需要渲染的,然后就直接替换了

这里的思路是我在看的到屏幕位置渲染10个再向上和向下各衍生渲染10个,总共30个,控制渲染的时候总是替换最上面或者最下面的元素,这样处于中间我们可以看见的部分就没有做重新重绘渲染。只是在预渲染的数据在做重绘。

原理

实现一个组件,可以显示具有5个元素的固定窗口大小的n个项目的列表: 即在任何时候,无限滚动n元素上也仅存在5个DOM容器。

 <ul>
        <li style="transform: translate3d(0px, 0px, 0px);">……</li>
        <li style="transform: translate3d(0px, 60px, 0px);">……</li>
    </ul>
  • 长列表内容一般都有规律,这个列表的高度是可以直接通过<li>的个数直接算出来的,比如1000个元素,每个元素的高是60px,那么我们很快就能算出真个列表的高度是60000px
  • 有了容器后将内部的<li>相对于<ul>进行绝对定位,在用js直接算出每一个<li> 对应的'transform: translate3d(0px, 0px, 0px);'属性
  • 通过监听scroll时间不断的去找到当前位置需要渲染的<li>的, 和上一组渲染数据进行对比,有相同的<li>就跳过,找出和上一组渲染数据的不同元素,然后做对应的替换

例如: 100 个元素,首页就显示5个,初始化[0,1,2,3,4] 这5个<li>需要渲染,当我向下滚动一点的时候出现 [1,2,3,4,5] 这几个<li>需要渲染,这时候不要直接做整体替换,只应该替换差异项,结构应为[5,1,2,3,4],由于是绝对定位,会脱离标准流,单个的重绘不会影响其他四个,从而提升性能。

如何实现

<!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>
  //总容器  var list = [];
  // 可视范围内元素容器  var showList = [];
  // 渲染容器  var renderList = [];
  // 每个容器的高度  var lineHeight = 32  // 初始化1000 个元素  for (var i = 0; i < 1000; i++) {
    list.push({
 id: i, text: '第' + (i + 1) + '个元素', top: i * lineHeight, bottom: (i + 1) * lineHeight 
}
)  
}
  // 初始化容器高度  $('ul').attr('style', 'height:' + 1000 * lineHeight + 'px')  // 寻找初始化容器的  function render(top, bottom) {
    showList = []    // 标记哪些数据和已经渲染后的是重复的,这部分将不会重复渲染    var sameIndex = []    // 找出滚动位置再哪一个元素上    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    // 在当前找到的元素分别向上和向下找需要显示的元素,总共的个数达到35个为止    while (range < 100 && showList.length + sameIndex.length < 35) {
      if (currentIndex - range >= 0) {
        // 对比满足条件的元素是否在已经渲染列表中,在就做标记,不在就放在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++    
}
    // 将对比出来的新的需要渲染的元素和没有被标记的渲染列表元素做替换    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 {
      // 第一次进行初始化列表      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)  
}
  // 第一次渲染  render()  $(window).scroll(function (e) {
    render()  
});
</script></html>

TODO

  1. 对比替换容器元素的方法总是感觉还能优化,这样就能提升运行效率,从而优化快速滚动出现的白屏
  2. 这里也出一个思考题[0,1……,10000], 每次从中取出5个元素,新选出来的和旧的进行对比,保留两个数组的交集部分,用新数组里面的新元素替换老数组里面的非交集部分, 例如第一次是[0,1,2,3,4],第二次是[2,3,4,5,6] 那么对比后生成[5,6,2,3,4],第三次如果是[4,5,6,7,8],生成的就为[5,6,7,8,4]。用最少的代码数得到这个结果数组。

小结

  1. 通过数据完成布局和初始化
  2. 通过觉得定位让容器脱离标准流
  3. 通过数据对比,找出差异容器元素,每次重绘尽量少的容器元素。

下期-----组件搭配实现长列表
 

关键点

  1. 组件不同于有规律的list, 组件的高度有可能不可控,会根据不同的手机设备而出现不用的高度
  2. 由于组件高度的同,渲染区域内有几个组件的数量也是不一样的,这就造成了容器个数不一样
  3. 对于高度不好通过数据直接进行计算的,如果需要一次整体渲染然后通过dom去计算位置和高度是非常耗费第一次加载性能的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Html屏蔽右键菜单和左键划字功能的示例

禁止右键菜单<body oncontextmenu=self.event.returnValue=false>禁止左键划字复制<body onselectstart="return false">采用CSS来控制是否可以选择文字.uns... 查看详情
收藏 0 赞 0 分享

html+css+js 实现拍照预览上传图片功能

前言:我们在做网页时经常会需要有上传图片的需求,可能是选择图片或者拍照上传,如果简单的使用<input type="file"/>这种方式虽然也能实现功能,但用户体验上可能会差了一些,所以本文记录了使用css+js实现图片选中后的预览及压缩上传功能... 查看详情
收藏 0 赞 0 分享

HTML阻止iframe跳转页面并使用iframe在页面内嵌微信网页版的实现方法

就想弄一个winform结合html5的一个小东西,突有兴致,想在里面嵌套一个微信网页版。好了,想法一出来,就行动吧,最终效果如下图:一开始就打算在页面里面嵌套一个iframe指向https://wx.qq.com就OK了,但是我还是太天真,微信网页版会自动跳转。结果如下图:于是... 查看详情
收藏 0 赞 0 分享

详解HTML常用的标签中行内元素和块级元素

块元素(block element) HTML标签分类明细     * address - 地址     * blockquote - 块引用     * center - 举中对... 查看详情
收藏 0 赞 0 分享

解决移动端跳转问题(CSS过渡、target伪类)

前言很多刚刚接触移动端的小伙伴都可能对于点击跳转路由这方面有些疑惑,特别是运用了Vue路由,因此这篇文章就带领小伙伴一起尝试用css进行页面跳转效果如图,由于是移动端,所以选择预览的手机模拟:HTML <body> <main> ... 查看详情
收藏 0 赞 0 分享

浅谈HTML中src和href之间的区别

简单来说 src 就是 “我想加载这个资源”,而 href 就是 “我想和这个资源建立关联”src 主要用于元素替换,href 用于和相关文档和外部资源建立相关链接。href 属性说明本地Web资源和定义的资源建立了链接。如:<... 查看详情
收藏 0 赞 0 分享

HTML中的if判断用法

在django的web开发过程中,编写html时,从后端传入同名列表变量,但是内容格式有所区别,需要分别判断,查阅了很多文章试了好几种方法,格式似乎都不太对(本人没有系统学习过前端,基本都是照葫芦画瓢)后来找到django框架下html的if用法形式大致如下  ... 查看详情
收藏 0 赞 0 分享

详解html的几种水平垂直居中的方式(基础)

前言我们在编写马过程中,想必大家对水平垂直居中的方法了解并不多。所以我给大家总结式的列出几种常用的水平垂直居中的方法。第一种方法<!--html盒子代码--><!--水平垂直居中--><div class="Centered1"&... 查看详情
收藏 0 赞 0 分享

纯css实现(无脚本)Html指令式tooltip文字提示效果

 分析执行流程 鼠标移入节点 检测是该节点是否存在开启实现 tooltip 实现的标识(类名,属性等) 检测主题、位置(类名,属性等) 生成 / 显示气泡 借鉴他人让我们先来看看 element-ui的tooltip 样式很明显, 气泡的位置 是通过 javascrip... 查看详情
收藏 0 赞 0 分享

Markodwn 标题对齐的同步滚动实现思路详解

前言需要给正在写的Markodwn编辑器加上同步滚动的功能,百度了一通,没找到比较好的思路。就自己写了一个。Github上是写好的库,和更直观的Demo。Github这篇文章主要讲的是实现的思路。介绍同步滚动的实现方式有很多种。简单粗暴的就直接让 HTMLElement.scro... 查看详情
收藏 0 赞 0 分享
查看更多