带型带秀专题之 Lazy Load (三)

lzxy 2019-06-25

博客地址:https://guitong.github.io/blo...

上一节中,我们分析了 jQuery lazyload 源码,其中有这么一段:

/* 在jQuery命名空间内定义了便捷的方法,判断图片是否在容器视口范围内 */
  $.belowthefold = function (element, settings) {...}
  $.rightoffold = function (element, settings) {...}
  $.abovethetop = function (element, settings) {...}
  $.leftofbegin = function (element, settings) {...}
  $.inviewport = function (element, settings) {...}

说实话,这才是我最感兴趣的内容。那么实现一个lazyload,应该怎样判断图片与浏览器可见区域的相对位置呢,现在以$.belowthefold方法为例,看一下其实现方式:

$.belowthefold = function(element, settings) {
        var fold;

        if (settings.container === undefined || settings.container === window) {
            fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
        } else {
            fold = $(settings.container).offset().top + $(settings.container).height();
        }

        return fold <= $(element).offset().top - settings.threshold;
    };

首先这段代码的目的是判断当前元素(图片)是否在浏览器视口的下方。

变量fold我理解为当前文档已“折叠”的高度,也就是当前浏览器视口的最底部至文档最顶部的距离。与fold变量值相比较的值是$(element).offset().top - settings.threshold,那么这个值代表什么呢?

seetings.threshold是我们上节提到过的临界值,默认为0,这里我们可以先将其忽略。jQuery对象的offset()方法定义如下:

Get the current coordinates of the first element in the set of matched elements, relative to the documents.

也就是当前元素相对于文档的坐标。它的top值即为元素到文档顶部的距离。

那么也就不难理解,如果这个值大于等于文档已“折叠”的高度值,它的位置就在浏览器视口的下方。

然而这些jQuery方法也是封装过的方法,我们还需要探究它们的实现方式。

一、如何得到浏览器视口(即可见区域)的大小

以下内容参考自大红本 — 《JavaScript高级程序设计》,第三版,第八章

跨浏览器确定一个窗口的大小不是一件简单的事。IE9+、Firefox、Safari、Opera和Chrome均为此提供了4个属性:innerWidthinnerHeightouterWidthouterHeight。在IE9+、Safari和Firefox中,outerWidthouterHeight返回浏览器窗口本身的尺寸(无论是最外层的window对象还是从某个框架访问)。在Opera中,这两个属性的值表示页面视图容器(这里所谓的“页面视图容器”指的是Opera中单个标签页对应的浏览器窗口)的大小。而innerWidthinnerHeight则表示该容器中页面视图区的大小(减去边框宽度)。在Chrome中,outerWidthouterHeightinnerWidthinnerHeight返回相同的值,即视口(viewport)大小而非浏览器窗口大小。

IE8及更早版本没有提供取得当前浏览器窗口尺寸的属性;不过,它通过DOM提供了页面可见区域的相关信息。

在IE、Firefox、Safari、Opera和Chrome中,document.documentElement.clientWidthdocument.documentElement.clientHeight中保存了页面视口的信息。在IE6中,这些属性必须在标准模式下才有效;如果是混杂模式,就必须通过document.body.clientWidthdocument.body.clientHeight取得相同信息。而对于混在模式下的Chrome,则无论通过document.documentElement还是document.body中的clientWidthclientHeight属性,都可以取得取得视口的大小。

(跑题了?? )

虽然最终无法确定浏览器窗口本身的大小,但却可以取得页面视口的大小,如下所示:

var pageWidth = window.innerWidth,
    pageHeight = window.innerHeight;
if (typeof pageWidth != 'number') {
    if (document.compatMode == 'CSS1Compat') {
          pageWidth = document.documentElement.clientWidth;
          pageHeight = document.documentElement.clientHeight;
    } else {
          pageWidth = document.body.clientWidth;
          pageHeight = document.body.clientHeight;
    }
}

这段代码很好理解,值得注意的是document.compatMode这个属性。该值表明当前文档的渲染模式为“混杂模式”还是“标准模式”。当值为'CSS1Compat'代表标准规范模式;当值为'BackCompat'代表混杂模式。

二、如果得到文档在在垂直/水平方向滚动的距离

这里以垂直方向为例。

先来看一个stackoverflow上的回答,翻译如下:

获取滚动距离的标准方法为window.scrollY。Chrome、Firefox、Opera、Safari及IE Edge(或更高版本)均支持此方法。如果您仅需要支持这些浏览器,使用这个属性即可。

IE >= 9 支持一个类似的属性window.pageYOffset,为了保证兼容性,在现代浏览器中会返回与window.scrollY相同的值,尽管它可能在某些时候被弃用。

使用document.documentElement.scrollTopdocument.body.scrollTop的问题是,它们并不是总是都被定义了滚动。例如,Chrome和Safari将滚动定义在<body>元素,而Firefox则定义在了document.documentElement返回的<html>元素上。这不是标准化的,并且在未来版本的浏览器中可能会发生变化。然而,如果scrollYpageYOffset不存在,则这是获取滚动位置的唯一方法。


遂总结如下:

window.scrollY || window.pageYOffset || document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0)

经过测试,这个方法是可行的。不过,正如文中所说,果然在未来版本的浏览器中发生了变化。在最新的Chrome、Safari、Firefox中测试发现,Chrome与Firefox表现相同,document.documentElement.scrollTop返回滚动值,而document.body.scrollTop返回0,Safari则与它们相反。

再来看一下MDN上对window.scrollY的解释

window接口的只读属性值scrollY返回文档当前垂直滚动距离的像素值。这个值在现在浏览器中是亚像素精准的,这意味着它不一定是一个整数。您可以从scrollY属性获取文档水平滚动的像素值。

# 语法

var y = window.scrollY

实际上,返回的值是一个双精度浮点值,指示文档当前从原点垂直滚动的像素数,其中正值表示向上滚动。如果文档在子像素精准的设备上呈现,则返回的值也是子像素精准的,并且可能包含一个小数分量。如果文档没有向上或向下滚动,则滚动值是 0 。

如果你需要一个整型值,可以使用Math.round()方法

用更技术的话说,scrollY返回当前视口顶边的Y坐标,如果没有视口,则返回 0 。

# 示例

// make sure and go down to the second page
if (window.scrollY) {
  window.scroll(0, 0); // 重置滚动条位置
}

window.scrollByPages(1);

(这个示例 出现在这里感觉怪怪的)

# 注意事项

使用此属性来检查使用相对滚动方法时(如;scrollBy()scrollByLines()scrollByPages())文档是否尚未滚动。

pageYOffset属性是scrollY属性的别名:

window.pageYOffset == window.scrollY // always true

考虑到跨浏览器兼容性,使用window.pageYOffset替代window.scrollY。除此之外,旧版本 IE(< 9)不支持这些属性,必须通过检查其他非标准属性来解决。完全兼容的的例子如下:

var supportPageOffset = window.pageXOffset !== undefied;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");

var scrollX = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft;
var scrollY = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.document.scrollTop : document.body.scrollTop;

# 规范

CSS Object Model (CSSOM) View ModuleThe definition of 'window.scrollY' in that specification.

==================== 太平洋分割线=======================

那么可以总结一下了。

要获得页面当前的滚动值,window.scrollY是基于标准的方法。然而考虑到跨浏览器兼容性,应该使用window.pageYOffset,该属性是window.scrollY的别名,被绝大多数现代浏览器所支持。对于低版本IE浏览器(< 9),可以判断渲染模式(标准or混杂)来选择使用document.documentElement.scrollTop/Leftdocument.body.scrollTop/Left方法。

推荐:

var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");

var scrollX = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft;
var scrollY = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;

三、???

发现跑题了,不是要看一下jQuery方法的源码吗???不过应该大同小异。

明白了上面两个重要的方法,实现一个兼容性良好lazyload就变得轻而易举。

相关推荐