1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>Document</title>
  8. <style>
  9. * {
  10. margin: 0;
  11. padding: 0;
  12. box-sizing: border-box;
  13. }
  14. ul {
  15. height: 600px;
  16. overflow: auto;
  17. }
  18. li {
  19. height: 60px;
  20. }
  21. </style>
  22. </head>
  23. <body>
  24. <ul>
  25. <div class="wrap"></div>
  26. </ul>
  27. </body>
  28. <script>
  29. let arr = [...new Array(100).keys()];
  30. const wrap = document.querySelector(".wrap");
  31. const ul = document.querySelector("ul");
  32. const li = document.createElement("li");
  33. ul.append(li);
  34. // 获取 li 高度
  35. const liHeight = li.clientHeight;
  36. // 根据数组长度,手动设置wrap高度,通过动态修改wrap的paddingTop实现虚拟滚动
  37. wrap.style.height = arr.length * liHeight + "px";
  38. // 一屏展示多少个li 向上取整,防止出现比如一屏展示5.5个li,用户手动滚动整屏时缺失第11个
  39. const screenLiIndex = Math.ceil(ul.clientHeight / liHeight);
  40. const vDom = () => {
  41. // 比如一屏展示10个 start 取前10个 end 取后20个 paddingTop 设置为第11个的位置
  42. // scrollListLength代表 paddingTop替代的li的长度
  43. scrollListLength = Math.round(ul.scrollTop / liHeight);
  44. let start = scrollListLength > screenLiIndex ? scrollListLength - screenLiIndex : 0;
  45. let end =
  46. scrollListLength > arr.length - screenLiIndex * 2 ? arr.length : scrollListLength + screenLiIndex * 2;
  47. wrap.innerHTML = "";
  48. for (let i = start; i < end; i++) {
  49. wrap.innerHTML += `<li>${arr[i]}</li>`;
  50. }
  51. wrap.style.paddingTop = start * liHeight + "px";
  52. };
  53. vDom();
  54. let touchStatus = "end";
  55. // touchStart时的位置,通过和touchEnd的位置比较,执行渲染,防止出现点击就渲染
  56. let touchStartY;
  57. let touchEndY;
  58. ul.ontouchstart = (e) => {
  59. touchStartY = e.changedTouches[0].screenY;
  60. touchStatus = "start";
  61. };
  62. ul.ontouchend = (e) => {
  63. touchStatus = "end";
  64. touchEndY = e.changedTouches[0].screenY;
  65. if (Math.abs(touchEndY - touchStartY) > liHeight / 2) {
  66. vDom();
  67. }
  68. };
  69. let scrollTimer;
  70. ul.onscroll = () => {
  71. if (touchStatus === "end") {
  72. clearTimeout(scrollTimer);
  73. scrollTimer = setTimeout(() => {
  74. vDom();
  75. }, 50);
  76. }
  77. };
  78. </script>
  79. </html>