jQuery滚动加载图片实现原理


Posted in Javascript onDecember 14, 2015

本文主要通过以下几方面来说明懒加载技术的原理,个人前端小菜,有错误请多多指出

一、什么是图片滚动加载?

通俗的讲就是:当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。

二、为什要使用这个技术?

比如一个页面中有很多图片,如淘宝、京东首页等等,如果一上来就发送这么多请求,页面加载就会很漫长,如果js文件都放在了文档的底部,恰巧页面的头部又依赖这个js文件,那就不好办了。更为要命的是:一上来就发送百八十个请求,服务器可能就吃不消了(又不是只有一两个人在访问这个页面)。

因此优点就很明显了:不仅可以减轻服务器的压力,而且可以让加载好的页面更快地呈现在用户面前(用户体验好)。

三、怎么实现?

关键点如下:

      1、页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片(也就没有请求咯,也就提高性能咯),一旦通过javascript设置了图片路径,浏览器才会送请求。有点按需分配的意思,你不想看,就不给你看,你想看了就给你看~

2、如何获取正真的路径,这个简单,现在正真的路径存在元素的“data-url”(这个名字起个自己认识好记的就行)属性里,要用的时候就取出来,再设置;

3、开始比较之前,先了解一些基本的知识,比如说如何获取某个元素的尺寸大小、滚动条滚动距离及偏移位置距离; 

1)屏幕可视窗口大小:对应于图中1、2位置处

原生方法:window.innerHeight 标准浏览器及IE9+ || document.documentElement.clientHeight 标准浏览器及低版本IE标准模式 || document.body.clientHeight 低版本混杂模式

    

jQuery方法: $(window).height()

2)浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离:也就是图中3、4处对应的位置;

原生方法:window.pagYoffset——IE9+及标准浏览器 || document.documentElement.scrollTop 兼容ie低版本的标准模式 || document.body.scrollTop 兼容混杂模式;

    

jQuery方法:$(document).scrollTop();

3)获取元素的尺寸:对应于图中5、6位置处;左边jquery方法,右边原生方法

$(o).width() = o.style.width;

$(o).innerWidth() = o.style.width+o.style.padding;

$(o).outerWidth() = o.offsetWidth = o.style.width+o.style.padding+o.style.border;

$(o).outerWidth(true) = o.style.width+o.style.padding+o.style.border+o.style.margin;

注意:要使用原生的style.xxx方法获取属性,这个元素必须已经有内嵌的样式,如<div style="...."></div>;

如果原先是通过外部或内部样式表定义css样式,必须使用o.currentStyle[xxx] ||document.defaultView.getComputedStyle(0)[xxx]来获取样式值

4)获取元素的位置信息:对应与图中7、8位置处

1)返回元素相对于文档document顶部、左边的距离;

jQuery:$(o).offset().top元素距离文档顶的距离,$(o).offset().left元素距离文档左边缘的距离

原生:getoffsetTop(),高程上有具体说明,这边就忽略了;

 

  顺便提一下返回元素相对于第一个以定位的父元素的偏移距离,注意与上面偏移距的区别;

   

  jQuery:position()返回一个对象,$(o).position().left = style.left,$(o).position().top = style.top;

4、知道如何获取元素尺寸、偏移距离后,接下来一个问题就是:如何判断某个元素进入或者即将进入可视窗口区域?下面也通过一张图来说明问题。

jQuery滚动加载图片实现原理

1)外面最大的框为实际页面的大小,中间浅蓝色的框代表父元素的大小,对象1~8代表元素位于页面上的实际位置;以水平方向来做如下说明!

2)对象8左边界相对于页面左边界的偏移距离(offsetLeft)大于父元素右边界相对于页面左边界的距离,此时可判读元素位于父元素之外;

3)对象7左边界跨过了父元素右边界,此时:对象7左边界相对于页面左边界的偏移距离(offsetLeft)小于 父元素右边界相对于页面左边界的距离,因此对象7就进入了父元素可视区;

4)在对象6的位置处,对象5的右边界与页面左边界的距离 大于 父元素左边界与页面左边界的距离;

5)在对象5位置处时,对象5的右边界与页面左边界的距离 小于 父元素左边界与页面左边界的距离;此时,可判断元素处于父元素可视区外;

6)因此水平方向必须买足两个条件,才能说明元素位于父元素的可视区内;同理垂直方向也必须满足两个条件;具体见下文的源码;

四、扩展为jquery插件
使用方法:$("selector").scrollLoad({ 参数在代码中有说明 })

(function($) {
 $.fn.scrollLoading = function(options) {
 var defaults = {
  // 在html标签中存放的属性名称;
  attr: "data-url",
  // 父元素默认为window
  container: window,
  callback: $.noop
 };
 // 不管有没有传入参数,先合并再说;
 var params = $.extend({}, defaults, options || {});
 // 把父元素转为jquery对象;
 var container = $(params.container);
 // 新建一个数组,然后调用each方法,用于存储每个dom对象相关的数据;
 params.cache = [];
 $(this).each(function() {
  // 取出jquery对象中每个dom对象的节点类型,取出每个dom对象上设置的图片路径
  var node = this.nodeName.toLowerCase(), url = $(this).attr(params["attr"]);
  //重组,把每个dom对象上的属性存为一个对象;
  var data = {
  obj: $(this),
  tag: node,
  url: url
  };
  // 把这个对象加到一个数组中;
  params.cache.push(data);
 });

 var callback = function(call) {
  if ($.isFunction(params.callback)) {
  params.callback.call(call);
  }
 };
 
 //每次触发滚动事件时,对每个dom元素与container元素进行位置判断,如果满足条件,就把路径赋予这个dom元素!
 var loading = function() {
  // 获取父元素的高度
  var contHeight = container.outerHeight();
  var contWidth = container.outerWidth();

  // 获取父元素相对于文档页顶部的距离,这边要注意了,分为以下两种情况;
  if (container.get(0) === window) {
  // 第一种情况父元素为window,获取浏览器滚动条已滚动的距离;$(window)没有offset()方法;
  var contop = $(window).scrollTop();
  var conleft = $(window).scrollLeft();
  } else {
  // 第二种情况父元素为非window元素,获取它的滚动条滚动的距离;
  var contop = container.offset().top;
  var conleft = container.offset().left;
  }

  $.each(params.cache, function(i, data) {
  var o = data.obj, tag = data.tag, url = data.url, post, posb, posl, posr;
  if (o) {
   //对象顶部与文档顶部之间的距离,如果它小于父元素底部与文档顶部的距离,则说明垂直方向上已经进入可视区域了;
   post = o.offset().top - (contop + contHeight);
   //对象底部与文档顶部之间的距离,如果它大于父元素顶部与文档顶部的距离,则说明垂直方向上已经进入可视区域了;
   posb = o.offset().top + o.height() - contop;

   // 水平方向上同理;
   posl = o.offset().left - (conleft + contWidth);
   posr = o.offset().left + o.width() - conleft;

   // 只有当这个对象是可视的,并且这四个条件都满足时,才能给这个对象赋予图片路径;
   if ( o.is(':visible') && (post < 0 && posb > 0) && (posl < 0 && posr > 0) ) {
   if (url) {
    //在浏览器窗口内
    if (tag === "img") {
    //设置图片src
    callback(o.attr("src", url));
    } else {
    // 设置除img之外元素的背景url
    callback(o.css("background-image", "url("+ url +")"));
    }
   } else {
    // 无地址,直接触发回调
    callback(o);
   }
   // 给对象设置完图片路径之后,把params.cache中的对象给清除掉;对象再进入可视区,就不再进行重复设置了;
   data.obj = null;
   }
  }
  });
 };
 //加载完毕即执行
 loading();
 //滚动执行
 container.bind("scroll", loading);
 };
})(jQuery);

本文已被整理到了《jquery图片加载方法汇总》 ,欢迎大家学习阅读。

以上就是滚动加载图片实现原理的详细内容介绍,希望对大家的学习有所帮助。

Javascript 相关文章推荐
mysql输出数据赋给js变量报unterminated string literal错误原因
May 22 Javascript
用于节点操作的API,颠覆原生操作HTML DOM节点的API
Dec 11 Javascript
js 幻灯片的实现
Dec 06 Javascript
jQuery中:eq()选择器用法实例
Dec 29 Javascript
微信小程序 loading 详解及实例代码
Nov 09 Javascript
vue 如何添加全局函数或全局变量以及单页面的title设置总结
Jun 01 Javascript
在Vue组件化中利用axios处理ajax请求的使用方法
Aug 25 Javascript
Vue父子组建的简单通信之控制开关Switch的实现
Jun 04 Javascript
JS实现的Object数组去重功能示例【数组成员为Object对象】
Feb 01 Javascript
详解vue 自定义组件使用v-model 及探究其中原理
Oct 11 Javascript
webgl实现物体描边效果的方法介绍
Nov 27 Javascript
Vue 如何使用props、emit实现自定义双向绑定的实现
Jun 05 Javascript
jQuery点击按钮弹出遮罩层且内容居中特效
Dec 14 #Javascript
jquery实现倒计时效果
Dec 14 #Javascript
JavaScript 七大技巧(一)
Dec 13 #Javascript
JavaScript 七大技巧(二)
Dec 13 #Javascript
js自定义回调函数
Dec 13 #Javascript
由浅入深讲解Javascript继承机制与simple-inheritance源码分析
Dec 13 #Javascript
分享Javascript实用方法二
Dec 13 #Javascript
You might like
《Re:从零开始的异世界生活 冰结之绊》
2020/04/09 日漫
发一个php简单的伪原创程序,配合商城采集用的
2010/10/12 PHP
30 个很棒的PHP开源CMS内容管理系统小结
2011/10/14 PHP
php中的curl使用入门教程和常见用法实例
2014/04/10 PHP
php 把数字转换成汉字的代码
2015/07/21 PHP
JavaScript中清空数组的三种方法分享
2011/04/07 Javascript
做好七件事帮你提升jQuery的性能
2014/02/06 Javascript
js实现简单的可切换选项卡效果
2015/04/10 Javascript
jQuery蓝色风格滑动导航栏代码分享
2015/08/19 Javascript
AngularJS控制器controller正确的通信的方法
2016/01/25 Javascript
原生JavaScript实现Ajax的方法
2016/04/07 Javascript
jQuery获取浏览器类型和版本号的方法
2016/07/05 Javascript
浅谈js中对象的使用
2016/08/11 Javascript
Javascript中的async awai的用法
2017/05/17 Javascript
js实现首屏延迟加载实现方法 js实现多屏单张图片延迟加载效果
2017/07/17 Javascript
vue-cli axios请求方式及跨域处理问题
2018/03/28 Javascript
Angularjs之如何在跨域请求中传输Cookie的方法
2018/06/01 Javascript
vue实现类似淘宝商品评价页面星级评价及上传多张图片功能
2018/10/29 Javascript
微信小程序自定义tabBar在uni-app的适配详解
2019/09/30 Javascript
搞笑的程序猿:看看你是哪种Python程序员
2015/06/12 Python
Python实现的归并排序算法示例
2017/11/21 Python
树莓派使用python-librtmp实现rtmp推流h264的方法
2019/07/22 Python
Python批量处理csv并保存过程解析
2020/05/16 Python
python 根据列表批量下载网易云音乐的免费音乐
2020/12/03 Python
详解通过focusout事件解决IOS键盘收起时界面不归位的问题
2019/07/18 HTML / CSS
英国计算机产品零售商:Novatech(定制个人电脑、笔记本电脑、工作站和服务器)
2018/01/28 全球购物
C语言开发工程师测试题
2016/12/20 面试题
师范生自荐信范文
2013/10/06 职场文书
软件工程师岗位职责
2013/11/16 职场文书
先进德育工作者事迹材料
2014/01/24 职场文书
学习十八大坚定理想信念心得体会
2014/03/11 职场文书
法律顾问服务方案
2014/05/15 职场文书
医德医风个人总结
2015/02/28 职场文书
2015年检察院个人工作总结
2015/05/20 职场文书
创业计划书之餐饮
2019/09/02 职场文书
Python 使用 Frame tkraise() 方法在 Tkinter 应用程序中的Frame之间切换
2022/04/24 Python