本文写于2017-10-17,有点历史,就是想发布出来(`・ω・´)
前言
本文起源于一次同事向我反馈,在做页面回到顶部功能时获取 scrollTop 数值有问题。当时他实测在 360 浏览器可以获取 scrollTop,在 chrome 和 Firefox 不能正常使用。那时我的第一反应是浏览器兼容性问题,猜测同事有可能使用的是 document.body.scrollTop
来获取 scrollTop 并且使用了 360 兼容模式浏览,因为 360 兼容模式是 IE 模式,因此 chrome 和 Firefox 不能正常获取。在帮助同事解决了问题后,总结了本文。
先上结论:
- jQuery 版本回到顶部
$('html,body')
$('html,body').animate({ scrollTop: 0 }, 100);
- 原生版本 兼容版回到顶部
/**
* 设置页面滚动条距顶部高度
* @param {Number} top
*/
export function setPageScrollTop(top) {
document.documentElement.scrollTop /* 标准 */ = top;
window.pageYOffset /* Safari */ = top;
document.body.scrollTop /* IE6/7/8 */ = top;
}
/**
* 获取滚动条距顶部高度
*/
export function getPageScrollTop() {
let doc = document;
return (
doc.documentElement.scrollTop /* 标准 */ || window.pageYOffset /* Safari */ || doc.body.scrollTop /* IE6/7/8 */ || 0
);
}
var scrollTop =
document.documentElement.scrollTop /* 标准 */ ||
window.pageYOffset /* Safari */ ||
document.body.scrollTop; /* IE6/7/8 */ || 0
下面根据上述问题引出兼容性源头。在谈论 scrollTop 的浏览器差异前,先科普一下 DTD。
DTD (Document Type Definition)
文档类型定义(DTD)可定义合法的 XML 文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
DTD 用途
-
通过 DTD, XML 文件均可携带一个有关其自身格式的描述。
-
通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。
-
可以使用某个标准的 DTD 来验证从外部接收到的数据。
-
可以使用 DTD 来验证文件自身的数据。
DTD 的使用方式
内部的 DOCTYPE 声明
内部的 DOCTYPE 声明 指 在 xml 文档内部使用 DTD 声明。
- 语法结构:
<!DOCTYPE 根元素 [元素声明]>
案例:
- 熟悉的 html 声明
<!-- html5 声明 -->
<!DOCTYPE html>
<html></html>
<!-- html4 声明 -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"></html>
-
自定义 xml 解析
- 定义一个
note类型
的文档:<!DOCTYPE note []
- 定义
note类型
文档的元素:<!ELEMENT note (to,from,heading,body)>
- 定义
note类型
文档的元素的类型为"#PCDATA" 类型
:如:<!ELEMENT body (#PCDATA)>
- 定义一个
<!-- 内部声明DTD的自定义xml源文件 -->
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
外部文档声明
外部文档声明 指 在 xml 文档内部不直接声明 DTD 类型,转而通过引用外部 DTD 声明文件来实现。
- 语法结构:
<!DOCTYPE 根元素 SYSTEM "文件名">
案例:
对上述 内部的 DOCTYPE 声明 案例作改写
<!-- note.dtd源文件 -->
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
<!-- 外部声明DTD的自定义xml源文件 -->
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
特别说明
对于 xml 文档元素的类型,CDATA 与 PCDATA 的区别
- CDATA
- Character Data: 指字符数据。数据源是
不会被解析器解析的文本
,文本中的标签不被看作标记来对待,其中的实体也不会被展开。 - 用于属性声明。
- 属性值是字符数据。
- CDATA 是属性声明中的类型,就是字符串,&、<、“”和‘’ 等都具有特殊含义被解析,例如:"解析为双引号。
- Character Data: 指字符数据。数据源是
- PCDATA
- parsed character data:指被解析的字符数据。数据源可以是字符串、子元素、字符串和子元素,是
会被解析器解析的文本
(如,>
要写成>
才不会出错。 ),文本将被解析器检查实体以及标记,其中的标签会被当作标记来处理,实体会被展开。 - 用于元素声明。
- 该内容模型说明元素中可以同时出现文本和元素。
- PCDATA 是元素声明中的类型,指的是混合类型,即可以包含子元素也可包含字符串, &和<也是具有特殊含义被解析。被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们,否则会报错。
- parsed character data:指被解析的字符数据。数据源可以是字符串、子元素、字符串和子元素,是
HTML 的 DTD
对页面具有 DTD,或者说指定了 DOCTYPE 时,使用 document.documentElement(指整个节点树的根节点 root,即 标签) 获取文档对象
对页面不具有 DTD,或者说没有指定了 DOCTYPE 时,使用 document.body(即 标签) 获取文档对象
在 IE 和 Firefox 中均是如此。
因此不管 html 源文件有是否 DTD,兼容写法如下:
var scrollTop =
document.documentElement.scrollTop /* 标准 */ ||
window.pageYOffset /* Safari */ ||
document.body.scrollTop; /* IE6/7/8 */ ||0
各浏览器的差异
各浏览器文本都以 DOM 层次结构来描述。
DOM 把层次中的每一个对象都称之为节点,就是一个层次结构,你可以理解为一个树形结构,就像我们的目录一样,一个根目录,根目录下有子目录,子目录下还有子目录。
以 HTML 超文本标记语言为例:整个文档的就是一个根 root(标签),在 DOM 中可以使用 document.documentElement 来访问它,它就是整个节点树的根节点。而 body 是子节点,要访问到 body 标签,在脚本中应该写:document.body。
-
body 是 DOM 对象里的 body 子节点,即 标签;
-
documentElement 是整个节点树的根节点 root,即 标签;
IE6/7/8 的 scrollTop
对于没有 doctype 声明的页面里可以使用 document.body.scrollTop 获取
对于有 doctype 声明的页面则可以使用 document.documentElement.scrollTop 获取
当页面滚动条刚好在最顶端,window.pageYOffset 返回为 undefined
Safari 的 scrollTop
Safari 比较特别,有自己获取 scrollTop 的函数 window.pageYOffset 来获取 scrollTop 的值
Firefox 的 scrollTop
Firefox 等相对标准些的浏览器,可直接使用 W3C 标准方法获取 document.documentElement.scrollTop
兼容获取 scrollTop
对于各浏览器的情况,可以使用逻辑与来获取兼容值
var scrollTop =
document.documentElement.scrollTop /* 标准 */ ||
window.pageYOffset /* Safari */ ||
document.body.scrollTop; /* IE6/7/8 */ || 0
总结
由上述总结可知:
- IE6/7/8 通过 document.body 获取 scrollTop
- Safari 通过 window.pageYOffset 获取 scrollTop
- 标准浏览器 通过 document.documentElement 获取 scrollTop
jQuery 版本回到顶部$('html,body')
$('html,body').animate({ scrollTop: 0 }, 100);
原生版本 兼容版回到顶部
/**
* 设置页面滚动条距顶部高度
* @param {Number} top
*/
export function setPageScrollTop(top) {
document.documentElement.scrollTop /* 标准 */ = top;
window.pageYOffset /* Safari */ = top;
document.body.scrollTop /* IE6/7/8 */ = top;
}
/**
* 获取滚动条距顶部高度
*/
export function getPageScrollTop() {
let doc = document;
return (
doc.documentElement.scrollTop /* 标准 */ || window.pageYOffset /* Safari */ || doc.body.scrollTop /* IE6/7/8 */ || 0
);
}
var scrollTop =
document.documentElement.scrollTop /* 标准 */ ||
window.pageYOffset /* Safari */ ||
document.body.scrollTop; /* IE6/7/8 */ || 0
直接使用$('html,body')来获取文档