图片懒加载的三种实现方式及事件的防抖和节流

1.图片的懒加载

方案一:clientHeight、scrollTop 和 offsetTop

首先给图片一个占位资源:
<img src="default.jpg" data-src="http://www.xxx.com/target.jpg" />

接着,通过监听 scroll 事件来判断图片是否到达视口:
let img = document.getElementsByTagName("img");
let num = img.length;
let count = 0;//计数器,从第一张图片开始计

lazyload();//首次加载别忘了显示图片

window.addEventListener('scroll', lazyload);

function lazyload() {
  let viewHeight = document.documentElement.clientHeight;//视口高度
  let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;//滚动条卷去的高度
  for(let i = count; i <num; i++) {
    // 元素现在已经出现在视口中
    if(img[i].offsetTop < scrollHeight + viewHeight) {
      if(img[i].getAttribute("src") !== "default.jpg") continue;
      img[i].src = img[i].getAttribute("data-src");
      count ++;
    }
  }
}
当然,最好对 scroll 事件做节流处理,以免频繁触发:
// throttle函数我们上节已经实现
window.addEventListener('scroll', throttle(lazyload, 200));

方案二:getBoundingClientRect

现在我们用另外一种方式来判断图片是否出现在了当前视口,DOM 元素的 getBoundingClientRect API。

上述的 lazyload 函数改成下面这样:
function lazyload() {
  for(let i = count; i <num; i++) {
    // 元素现在已经出现在视口中
    if(img[i].getBoundingClientRect().top < document.documentElement.clientHeight) {
      if(img[i].getAttribute("src") !== "default.jpg") continue;
      img[i].src = img[i].getAttribute("data-src");
      count ++;
    }
  }
}

方案三: IntersectionObserver

这是浏览器内置的一个API,实现了监听window的scroll事件、判断是否在视口中以及节流三大功能。
我们来具体试一把:

let img = document.getElementsByTagName("img");

const observer = new IntersectionObserver(changes => {
  //changes 是被观察的元素集合
  for(let i = 0, len = changes.length; i < len; i++) {
    let change = changes[i];
    // 通过这个属性判断是否在视口中
    if(change.isIntersecting) {
      const imgElement = change.target;
      imgElement.src = imgElement.getAttribute("data-src");
      observer.unobserve(imgElement);
    }
  }
})
Array.from(img).forEach(item => observer.observe(item));
这样就很方便地实现了图片懒加载,当然这个IntersectionObserver也可以用作其他资源的预加载,功能非常强大。

2.事件的防抖和节流

节流

节流的核心思想: 如果在定时器的时间范围内再次触发,则不予理睬,等当前定时器完成,才能启动下一个定时器任务。
	这就好比公交车,10 分钟一趟,10 分钟内有多少人在公交站等我不管,10 分钟一到我就要发车走人!
代码如下:
function throttle(fn, interval) {
  let flag = true;
  return function(...args) {
    let context = this;
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(context, args);
      flag = true;
    }, interval);
  };
};
写成下面的方式也是表达一样的意思:
const throttle = function(fn, interval) {
  let last = 0;
  return function (...args) {
    let context = this;
    let now = +new Date();
    // 还没到时间
    if(now - last < interval) return;
    last = now;
    fn.apply(this, args)
  }
}

防抖

核心思想: 每次事件触发则删除原来的定时器,建立新的定时器。
	多次触发,那么只认最后一次,从最后一次触发开始计时。
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    let context = this;
    if(timer) clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}

合并版节流

现在我们可以把防抖和节流放到一起,为什么呢?因为防抖有时候触发的太频繁会导致一次响应都没有,我们希望到了固定的时间必须给用户一个响应,
function throttle(fn, delay) {
  let last = 0, timer = null;
  return function (...args) {
    let context = this;
    let now = new Date();
    if(now - last < delay){
      clearTimeout(timer);
      setTimeout(function() {
        last = now;
        fn.apply(context, args);
      }, delay);
    } else {
      // 这个时候表示时间到了,必须给响应
      last = now;
      fn.apply(context, args);
    }
  }
}

你可能感兴趣的:(图片懒加载的三种方式,事件防抖和节流)