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 事件做节流处理,以免频繁触发:
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 => {
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) ;
}
}
}