上一节中,我们分析了 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个属性:innerWidth
、innerHeight
、outerWidth
、outerHeight
。在IE9+、Safari和Firefox中,outerWidth
和outerHeight
返回浏览器窗口本身的尺寸(无论是最外层的window对象还是从某个框架访问)。在Opera中,这两个属性的值表示页面视图容器(这里所谓的“页面视图容器”指的是Opera中单个标签页对应的浏览器窗口)的大小。而innerWidth
和innerHeight
则表示该容器中页面视图区的大小(减去边框宽度)。在Chrome中,outerWIdth
和outerHeight
与innerWidth
、innerHeight
返回相同的值,即视口(viewport)大小而非浏览器窗口大小。
IE8及更早版本没有提供取得当前浏览器窗口尺寸的属性;不过,它通过DOM提供了页面可见区域的相关信息。
在IE、Firefox、Safari、Opera和Chrome中,document.documentElement.clientWidth
和document.documentElement.clientHeight
中保存了页面视口的信息。在IE6中,这些属性必须在标准模式下才有效;如果是混杂模式,就必须通过document.body.clientWidth
和document.body.clientHeight
取得相同信息。而对于混在模式下的Chrome,则无论通过document.documentElement
还是document.body
中的clientWidth
和clientHeight
属性,都可以取得取得视口的大小。
(跑题了?? )
虽然最终无法确定浏览器窗口本身的大小,但却可以取得页面视口的大小,如下所示:
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.scrollTop
或document.body.scrollTop
的问题是,它们并不是总是都被定义了滚动。例如,Chrome和Safari将滚动定义在<body>
元素,而Firefox则定义在了document.documentElement
返回的<html>
元素上。这不是标准化的,并且在未来版本的浏览器中可能会发生变化。然而,如果scrollY
和pageYOffset
不存在,则这是获取滚动位置的唯一方法。遂总结如下:
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/Left
或document.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就变得轻而易举。