浏览器环境,规格
平台可以是一个浏览器,一个 Web 服务器,或其他 主机(host),甚至可以是一个“智能”咖啡机,如果它能运行 JavaScript 的话。它们每个都提供了特定于平台的功能。JavaScript 规范将其称为 主机环境。
- 有一个叫做
window的“根”对象。它有两个角色:- 首先,它是 JavaScript 代码的全局对象,如 全局对象 一章所述。
- 其次,它代表“浏览器窗口”,并提供了控制它的方法。
- 文档对象模型(DOM)
- 文档对象模型(Document Object Model),简称 DOM,将所有页面内容表示为可以修改的对象。
document对象是页面的主要“入口点”。我们可以使用它来更改或创建页面上的任何内容。- DOM 规范解释了文档的结构,并提供了操作文档的对象。有的非浏览器设备也使用 DOM。
- 浏览器对象模型(BOM)
DOM 树
HTML 文档的主干是标签(tag)。 根据文档对象模型(DOM),每个 HTML 标签都是一个对象。嵌套的标签是闭合标签的“子标签(children)”。标签内的文本也是一个对象。 所有这些对象都可以通过 JavaScript 来访问,我们可以使用它们来修改页面。
- 一些标签
document.body是表示<body>标签的对象。innerHTML—— 节点的 HTML 内容。
offsetWidth—— 节点宽度(以像素度量)
- 等等
- DOM的一些知识点
- 标签被称为 元素节点(或者仅仅是元素),并形成了树状结构:
<html>在根节点,<head>和<body>是其子项,等。 - 元素内的文本形成 文本节点,被标记为
#text。一个文本节点只包含一个字符串。它没有子项,并且总是树的叶子。 - 字符串开头/结尾处的空格,以及只有空格的文本节点,通常会被工具隐藏
- 标签被称为 元素节点(或者仅仅是元素),并形成了树状结构:
- 自动修正
- 如果浏览器遇到格式不正确的 HTML,它会在形成 DOM 时自动更正它。
- 表格永远有
<tbody>
- 其他节点类型
- 除了元素和文本节点外,还有一些其他的节点类型。,例如注释
- HTML 中的所有内容,甚至注释,都会成为 DOM 的一部分。
- 甚至 HTML 开头的
<!DOCTYPE...>指令也是一个 DOM 节点 document—— DOM 的“入口点”。- 元素节点 —— HTML 标签,树构建块。
- 本节点 —— 包含文本。
- 注释 —— 有时我们可以将一些信息放入其中,它不会显示,但 JS 可以从 DOM 中读取它。
- 查看DOM结构
- 可以打开这个网页 elks.html,然后打开浏览器开发工具,并切换到元素(Elements)选项卡。
- Styles —— 我们可以看到按规则应用于当前元素的 CSS 规则,包括内建规则(灰色)。几乎所有内容都可以就地编辑,包括下面的方框的 dimension/margin/padding。
- Computed —— 按属性查看应用于元素的 CSS:对于每个属性,我们可以都可以看到赋予它的规则(包括 CSS 继承等)。
- Event Listeners —— 查看附加到 DOM 元素的事件侦听器(我们将在本教程的下一部分介绍它们)。
- 与控制台交互
- 在我们处理 DOM 时,我们可能还希望对其应用 JavaScript。例如:获取一个节点并运行一些代码来修改它,以查看结果。以下是在元素(Elements)选项卡和控制台(Console)之间切换的一些技巧。
- 在元素(Elements)选项卡中选择第一个
<li>。
- 在元素(Elements)选项卡中选择第一个
- 按下 Esc —— 它将在元素(Elements)选项卡下方打开控制台(Console)。
- 在我们处理 DOM 时,我们可能还希望对其应用 JavaScript。例如:获取一个节点并运行一些代码来修改它,以查看结果。以下是在元素(Elements)选项卡和控制台(Console)之间切换的一些技巧。
遍历 DOM
DOM 让我们可以对元素和它们中的内容做任何事,但是首先我们需要获取到对应的 DOM 对象。 对 DOM 的所有操作都是以 document 对象开始。它是 DOM 的主“入口点”。从它我们可以访问任何节点。
- 在最顶层:documentElement 和 body
- 最顶层的树节点可以直接作为
document的属性来使用:<html>=document.documentElement- 最顶层的 document 节点是
document.documentElement。这是对应<html>标签的 DOM 节点。 <body>=document.body- 另一个被广泛使用的 DOM 节点是
<body>元素 ——document.body。 <head>=document.head<head>标签可以通过document.head访问。
- 这里有个问题:
document.body的值可能是null- 脚本无法访问在运行时不存在的元素。
- 尤其是,如果一个脚本是在
<head>中,那么脚本是访问不到document.body元素的,因为浏览器还没有读到它。
- 最顶层的树节点可以直接作为
- 子节点:childNodes,firstChild,lastChild
- 子节点(或者叫作子) —— 对应的是直系的子元素。换句话说,它们被完全嵌套在给定的元素中。例如,
<head>和<body>就是<html>元素的子元素。
- 子节点(或者叫作子) —— 对应的是直系的子元素。换句话说,它们被完全嵌套在给定的元素中。例如,
- 子孙元素 —— 嵌套在给定元素中的所有元素,包括子元素,以及子元素的子元素等。
childNodes集合列出了所有子节点,包括文本节点。firstChild和lastChild属性是访问第一个和最后一个子元素的快捷方式。
- DOM 集合
- 正如我们看到的那样,
childNodes看起来就像一个数组。但实际上它并不是一个数组,而是一个 集合 —— 一个类数组的可迭代对象。- 我们可以使用
for..of来迭代它 - 无法使用数组的方法,因为它不是一个数组
- 我们可以使用
- DOM 集合是只读的
- DOM 集合,甚至可以说本章中列出的 所有 导航(navigation)属性都是只读的。
- 我们不能通过类似
childNodes[i] = ...的操作来替换一个子节点。
- DOM 集合是实时的
- 除小部分例外,几乎所有的 DOM 集合都是 实时 的。换句话说,它们反映了 DOM 的当前状态。
- 如果我们保留一个对
elem.childNodes的引用,然后向 DOM 中添加/移除节点,那么这些节点的更新会自动出现在集合中。
- 不要使用
for..in来遍历集合- 可以使用
for..of对集合进行迭代。但有时候人们会尝试使用for..in来迭代集合。
- 可以使用
- 正如我们看到的那样,
- 兄弟节点和父节点
- 兄弟节点(sibling) 是指有同一个父节点的节点。
<head>和<body>就是兄弟节点。
- 兄弟节点(sibling) 是指有同一个父节点的节点。
- 纯元素导航
- 上面列出的导航(navigation)属性引用 所有 节点。例如,在
childNodes中我们可以看到文本节点,元素节点,甚至包括注释节点(如果它们存在的话)。 parentElement属性返回的是“元素类型”的父节点,而parentNode返回的是“任何类型”的父节点。这些属性通常来说是一样的:它们都是用于获取父节点。
- 上面列出的导航(navigation)属性引用 所有 节点。例如,在
- 更多链接:表格
<table>元素支持 (除了上面给出的,之外) 以下属性 -table.rows——<tr>元素的集合。table.caption/tHead/tFoot—— 引用元素<caption>,<thead>,<tfoot>。table.tBodies——<tbody>元素的集合(根据标准还有很多元素,但是这里至少会有一个 —— 即使没有被写在 HTML 源文件中,浏览器也会将其放入 DOM 中)。
<thead>,<tfoot>,<tbody>元素提供了rows属性:tbody.rows—— 表格内部<tr>元素的集合。
<tr>:tr.cells—— 在给定<tr>中的<td>和<th>单元格的集合。tr.sectionRowIndex—— 给定的<tr>在封闭的<thead>/<tbody>/<tfoot>中的位置(索引)。tr.rowIndex—— 在整个表格中<tr>的编号(包括表格的所有行)。
<td>和<th>:td.cellIndex—— 在封闭的<tr>中单元格的编号。
搜索:getElement,querySelector
- document.getElementById 或者只使用 id
- 如果一个元素有
id特性(attribute),那我们就可以使用document.getElementById(id)方法获取该元素,无论它在哪里 - 请不要使用以 id 命名的全局变量来访问元素
id必须是唯一的
- 如果一个元素有
- querySelectorAll
- 到目前为止,最通用的方法是
elem.querySelectorAll(css),它返回elem中与给定 CSS 选择器匹配的所有元素。 - CSS 选择器的伪类,例如
:hover和:active也都是被支持的。
- 到目前为止,最通用的方法是
- querySelector
elem.querySelector(css)调用会返回给定 CSS 选择器的第一个元素。- 换句话说,结果与
elem.querySelectorAll(css)[0]相同,但是后者会查找 所有 元素,并从中选取一个,而elem.querySelector只会查找一个。因此它在速度上更快,并且写起来更短。
- matches
- elem.matches(css) 不会查找任何内容,它只会检查
elem是否与给定的 CSS 选择器匹配。它返回true或false。 - 当我们遍历元素(例如数组或其他内容)并试图过滤那些我们感兴趣的元素时,这个方法会很有用。
- elem.matches(css) 不会查找任何内容,它只会检查
- closest
- 元素的祖先(ancestor)是:父级,父级的父级,它的父级等。祖先们一起组成了从元素到顶端的父级链。
elem.closest(css)方法会查找与 CSS 选择器匹配的最近的祖先。elem自己也会被搜索。- 换句话说,方法
closest在元素中得到了提升,并检查每个父级。如果它与选择器匹配,则停止搜索并返回该祖先。
- getElementsBy
*elem.getElementsByTagName(tag)查找具有给定标签的元素,并返回它们的集合。tag参数也可以是对于“任何标签”的星号"*"。elem.getElementsByClassName(className)返回具有给定CSS类的元素。document.getElementsByName(name)返回在文档范围内具有给定name特性的元素。很少使用。
- 不要忘记字母
"s"!- 调用
getElementByTagName而不是getElement**s**ByTagName。
- 调用
- 它返回的是一个集合,不是一个元素!
- 实时的集合
- 所有的
"getElementsBy*"方法都会返回一个 实时的(live) 集合。这样的集合始终反映的是文档的当前状态,并且在文档发生更改时会“自动更新”。 querySelectorAll返回的是一个 静态的 集合。就像元素的固定数组。
- 所有的
节点属性:type,tag 和 content
- DOM 节点类
- 不同的 DOM 节点可能有不同的属性。例如,标签
<a>相对应的元素节点具有链接相关的(link-related)属性,标签<input>相对应的元素节点具有与输入相关的属性,等。 - 每个 DOM 节点都属于相应的内建类。
- 层次结构(hierarchy)的根节点是 EventTarget,Node 继承自它,其他 DOM 节点继承自 Node。
- 类:
- EventTarget —— 是一切的根“抽象(abstract)”类。 该类的对象从未被创建。它作为一个基础,以便让所有 DOM 节点都支持所谓的“事件(event)”,我们会在之后学习它。
- Node —— 也是一个“抽象”类,充当 DOM 节点的基础。 它提供了树的核心功能:
parentNode,nextSibling,childNodes等(它们都是 getter)。Node类的对象从未被创建。但是还有一些继承自它的其他类(因此继承了Node的功能)。 - Document 由于历史原因通常被
HTMLDocument继承(尽管最新的规范没有规定)—— 是一个整体的文档。 全局变量document就是属于这个类。它作为 DOM 的入口。 - CharacterData —— 一个“抽象”类,被下述类继承:
- Element —— 是 DOM 元素的基础类。 它提供了元素级导航(navigation),如
nextElementSibling,children,以及搜索方法,如getElementsByTagName和querySelector。 浏览器不仅支持 HTML,还支持 XML 和 SVG。因此,Element类充当的是更具体的类的基础:SVGElement,XMLElement(我们在这里不需要它)和HTMLElement。 - 最后,HTMLElement —— 是所有 HTML 元素的基础类。我们大部分时候都会用到它。 它会被更具体的 HTML 元素继承:
- HTMLInputElement ——
<input>元素的类, - HTMLBodyElement ——
<body>元素的类, - HTMLAnchorElement ——
<a>元素的类, - ……等。
- HTMLInputElement ——
console.dir(elem)与console.log(elem)- 它们是不同的:
console.log(elem)显示元素的 DOM 树。console.dir(elem)将元素显示为 DOM 对象,非常适合探索其属性。
- 它们是不同的:
- 规范中的 IDL
- 在规范中,DOM 类不是使用 JavaScript 来描述的,而是一种特殊的 接口描述语言(Interface description language),简写为 IDL,它通常很容易理解。
- 不同的 DOM 节点可能有不同的属性。例如,标签
- “nodeType” 属性
nodeType属性提供了另一种“过时的”用来获取 DOM 节点类型的方法。- 它有一个数值型值(numeric value):
- 对于元素节点
elem.nodeType == 1, - 对于文本节点
elem.nodeType == 3, - 对于 document 对象
elem.nodeType == 9, - 在 规范 中还有一些其他值。
- 对于元素节点
- 标签:nodeName 和 tagName
- 给定一个 DOM 节点,我们可以从
nodeName或者tagName属性中读取它的标签名 - tagName 和 nodeName 之间有什么不同吗?
tagName属性仅适用于Element节点。nodeName是为任意Node定义的:- 对于元素,它的意义与
tagName相同。 - 对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串。
tagName仅受元素节点支持(因为它起源于Element类),而nodeName则可以说明其他节点类型。
- 标签名称始终是大写的,除非是在 XML 模式下
- 给定一个 DOM 节点,我们可以从
- innerHTML:内容
- innerHTML 属性允许将元素中的 HTML 获取为字符串形式。
- 如果
innerHTML将一个<script>标签插入到 document 中 —— 它会成为 HTML 的一部分,但是不会执行。 - 小心:“innerHTML+=” 会进行完全重写
- 我们可以使用
elem.innerHTML+="more html"将 HTML 附加到元素上。 innerHTML+=的作用- 移除旧的内容。
- 然后写入新的
innerHTML(新旧结合)。 - 因为内容已“归零”并从头开始重写,因此所有的图片和其他资源都将重写加载。
- 我们可以使用
- outerHTML:元素的完整 HTML
outerHTML属性包含了元素的完整 HTML。就像innerHTML加上元素本身一样。- 与
innerHTML不同,写入outerHTML不会改变元素。而是在 DOM 中替换它。
- nodeValue/data:文本节点内容
innerHTML属性仅对元素节点有效。- 其他节点类型,例如文本节点,具有它们的对应项:
nodeValue和data属性。这两者在实际使用中几乎相同,只有细微规范上的差异。因此,我们将使用data,因为它更短。
- textContent:纯文本
textContent提供了对元素内的 文本 的访问权限:仅文本,去掉所有<tags>。- 写入
textContent要有用得多,因为它允许以“安全方式”写入文本。 - 使用
textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理。
- 使用
- “hidden” 属性
- “hidden” 特性(attribute)和 DOM 属性(property)指定元素是否可见。
- 从技术上来说,
hidden与style="display:none"做的是相同的事。但hidden写法更简洁。
- 更多属性
- DOM 元素还有其他属性,特别是那些依赖于 class 的属性:
value——<input>,<select>和<textarea>(HTMLInputElement,HTMLSelectElement……)的 value。href——<a href="...">(HTMLAnchorElement)的 href。id—— 所有元素(HTMLElement)的 “id” 特性(attribute)的值。- ……以及更多其他内容……
- DOM 元素还有其他属性,特别是那些依赖于 class 的属性:
特性和属性(Attributes and properties)
当浏览器加载页面时,它会“读取”(或者称之为:“解析”)HTML 并从中生成 DOM 对象。对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)。
- DOM 属性
- DOM 节点是常规的 JavaScript 对象。我们可以更改它们。
- DOM 属性和方法的行为就像常规的 Javascript 对象一样
- 它们可以有很多值。
- 它们是大小写敏感的(要写成
elem.nodeType,而不是elem.NoDeTyPe)。
- 它们是大小写敏感的(要写成
- HTML 特性
- 在 HTML 中,标签可能拥有特性(attributes)。当浏览器解析 HTML 文本,并根据标签创建 DOM 对象时,浏览器会辨别 标准的 特性并以此创建 DOM 属性。
- 所以,当一个元素有
id或其他 标准的 特性,那么就会生成对应的 DOM 属性。但是非 标准的 特性则不会。 - 所有特性都可以通过使用以下方法进行访问
elem.hasAttribute(name)—— 检查特性是否存在。elem.getAttribute(name)—— 获取这个特性值。elem.setAttribute(name, value)—— 设置这个特性值。elem.removeAttribute(name)—— 移除这个特性。
- HTML 特性有以下几个特征
- 它们的名字是大小写不敏感的(
id与ID相同)。 - 它们的值总是字符串类型的。
- 它们的名字是大小写不敏感的(
- 属性—特性同步
- 当一个标准的特性被改变,对应的属性也会自动更新,(除了几个特例)反之亦然。
- DOM 属性是多类型的
- DOM 属性不总是字符串类型的。
- 非标准的特性,dataset
- 有时,非标准的特性常常用于将自定义的数据从 HTML 传递到 JavaScript,或者用于为 JavaScript “标记” HTML 元素。
- 所有以 “data-” 开头的特性均被保留供程序员使用。它们可在
dataset属性中使用。
修改文档(document)
DOM 修改是创建“实时”页面的关键。
例子:展示一条消息
JavaScript
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>创建一个元素
JavaScript
let div = document.createElement('div');
//用给定的文本创建一个 文本节点
let textNode = document.createTextNode('Here I am');创建一条消息
JavaScript
// 1. 创建 <div> 元素
let div = document.createElement('div');
// 2. 将元素的类设置为 "alert"
div.className = "alert";
// 3. 填充消息内容
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";- 插入方法
- 元素插入方法,指明了不同的插入位置
node.append(...nodes or strings)—— 在node末尾 插入节点或字符串,node.prepend(...nodes or strings)—— 在node开头 插入节点或字符串,node.before(...nodes or strings)—— 在node前面 插入节点或字符串,node.after(...nodes or strings)—— 在node后面 插入节点或字符串,node.replaceWith(...nodes or strings)—— 将node替换为给定的节点或字符串。
- 元素插入方法,指明了不同的插入位置
- insertAdjacentHTML/Text/Element
- 一个非常通用的方法:
elem.insertAdjacentHTML(where, html)。"beforebegin"—— 将html插入到elem之前,"afterbegin"—— 将html插入到elem开头,"beforeend"—— 将html插入到elem末尾,"afterend"—— 将html插入到elem之后。
- 这个方法有两个兄弟
elem.insertAdjacentText(where, text)—— 语法一样,但是将text字符串“作为文本”插入而不是作为 HTML,
elem.insertAdjacentElement(where, elem)—— 语法一样,但是插入的是一个元素。
- 一个非常通用的方法:
- 节点移除
- 想要移除一个节点,可以使用
node.remove()。 - 如果我们要将一个元素 移动 到另一个地方,则无需将其从原来的位置中删除。
- 所有插入方法都会自动从旧位置删除该节点。
- 想要移除一个节点,可以使用
- 克隆节点:cloneNode
- 调用
elem.cloneNode(true)来创建元素的一个“深”克隆 —— 具有所有特性(attribute)和子元素。如果我们调用elem.cloneNode(false),那克隆就不包括子元素。
- 调用
- DocumentFragment
DocumentFragment是一个特殊的 DOM 节点,用作来传递节点列表的包装器(wrapper)。
- 老式的 insert/remove 方法
- 聊一聊 “document.write”
- 还有一个非常古老的向网页添加内容的方法:
document.write。 - 调用
document.write(html)意味着将html“就地马上”写入页面。html字符串可以是动态生成的,所以它很灵活。我们可以使用 JavaScript 创建一个完整的页面并对其进行写入。 document.write调用只在页面加载时工作。- 运行起来出奇的快,因为它 不涉及 DOM 修改。
- 还有一个非常古老的向网页添加内容的方法:
样式和类
通常有两种设置元素样式的方式: 1. 在 CSS 中创建一个类,并添加它:<div class="..."> 2. 将属性直接写入 style:<div style="...">。
- className 和 classList
- 更改类是脚本中最常见的操作之一。
"className":elem.className对应于"class"特性(attribute)。elem.classList是一个特殊的对象,它具有add/remove/toggle单个类的方法。classList的方法elem.classList.add/remove(class)—— 添加/移除类。elem.classList.toggle(class)—— 如果类不存在就添加类,存在就移除它。elem.classList.contains(class)—— 检查给定类,返回true/false。
- 元素样式
elem.style属性是一个对象,它对应于"style"特性(attribute)中所写的内容。elem.style.width="100px"的效果等价于我们在style特性中有一个width:100px字符串。
- 重置样式属性
- 有时我们想要分配一个样式属性,稍后移除它。
- 这里不应该使用
delete elem.style.display,而应该使用elem.style.display = ""将其赋值为空。 - 用
style.cssText进行完全的重写- 我们使用
style.*来对各个样式属性进行赋值。我们不能像这样的div.style="color: red; width: 100px"设置完整的属性,因为div.style是一个对象,并且它是只读的。
- 我们使用
- 注意单位
- px,rem,vh等
- 计算样式:getComputedStyle
style属性仅对"style"特性(attribute)值起作用,而没有任何 CSS 级联(cascade)。- element
- 需要被读取样式值的元素。
- pseudo
- 伪元素(如果需要),例如
::before。空字符串或无参数则意味着元素本身。
- 伪元素(如果需要),例如
- 计算值和解析值
- 计算 (computed) 样式值是所有 CSS 规则和 CSS 继承都应用后的值,这是 CSS 级联(cascade)的结果。它看起来像
height:1em或font-size:125%。 - 解析 (resolved) 样式值是最终应用于元素的样式值。诸如
1em或125%这样的值是相对的。浏览器将使用计算(computed)值,并使所有单位均为固定的,且为绝对单位,例如:height:20px或font-size:16px。对于几何属性,解析(resolved)值可能具有浮点,例如:width:50.5px。
- 计算 (computed) 样式值是所有 CSS 规则和 CSS 继承都应用后的值,这是 CSS 级联(cascade)的结果。它看起来像
getComputedStyle需要完整的属性名- 要的确切的属性,例如
paddingLeft、marginTop或borderTopWidth。否则,就不能保证正确的结果。
- 要的确切的属性,例如
- 应用于
:visited链接的样式被隐藏了!- 可以使用 CSS 伪类
:visited对被访问过的链接进行着色。 - 但
getComputedStyle没有给出访问该颜色的方式,因为如果允许的话,任意页面都可以通过在页面上创建它,并通过检查样式来确定用户是否访问了某链接。 - JavaScript 看不到
:visited所应用的样式。
- 可以使用 CSS 伪类
元素大小和滚动
- offsetParent,offsetLeft/Top
offsetParent是最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标。- 最近的祖先为下列之一:
- CSS 定位的(
position为absolute、relative、fixed或sticky),
- CSS 定位的(
- 或
<td>,<th>,<table>, - 或
<body>。 - 属性
offsetLeft/offsetTop提供相对于offsetParent左上角的 x/y 坐标。 - 有以下几种情况下,
offsetParent的值为null:- 对于未显示的元素(
display:none或者不在文档中)。 - 对于
<body>与<html>。 - 对于带有
position:fixed的元素。
- 对于未显示的元素(
- offsetWidth/Height
- 提供了元素的“外部” width/height
offsetWidth = 390—— 外部宽度(width),可以计算为内部 CSS-width(300px)加上 padding(2 * 20px)和 border(2 * 25px)。
offsetHeight = 290—— 外部高度(height)。
- 对于未显示的元素,几何属性为 0/null
- 如果一个元素(或其任何祖先)具有
display:none或不在文档中,则所有几何属性均为零(或offsetParent为null)。 - 当我们创建了一个元素,但尚未将其插入文档中,或者它(或它的祖先)具有
display:none时,offsetParent为null,并且offsetWidth和offsetHeight为0。
- 如果一个元素(或其任何祖先)具有
- 提供了元素的“外部” width/height
- clientTop/Left
- 为了测量它们,可以使用
clientTop和clientLeft。clientLeft = 25—— 左边框宽度
clientTop = 25—— 上边框宽度
- 为了测量它们,可以使用
- clientWidth/Height
- “content width” 和 “padding”,但不包括滚动条宽度(scrollbar)
- scrollWidth/Height
- 属性就像
clientWidth/clientHeight,但它们还包括滚动出(隐藏)的部分 - 是内容区域的完整内部高度,包括滚动出的部分。
- 是完整的内部宽度,这里我们没有水平滚动,因此它等于
clientWidth。
- 属性就像
- scrollLeft/scrollTop
- 属性
scrollLeft/scrollTop是元素的隐藏、滚动部分的 width/height。 - scrollLeft/scrollTop 是可修改的
- 大多数几何属性是只读的,但是
scrollLeft/scrollTop是可修改的,并且浏览器会滚动该元素。
- 属性
- 不要从 CSS 中获取 width/height
- 首先,CSS
width/height取决于另一个属性:box-sizing,它定义了“什么是” CSS 宽度和高度。出于 CSS 的目的而对box-sizing进行的更改可能会破坏此类 JavaScript 操作。
- 首先,CSS
- 其次,CSS 的
width/height可能是auto,例如内联(inline)元素
Window 大小和滚动
- 窗口的 width/height
- 为了获取窗口(window)的宽度和高度,我们可以使用
document.documentElement的clientWidth/clientHeight - 不是
window.innerWidth/innerHeight- 如果这里存在一个滚动条,并且滚动条占用了一些空间,那么
clientWidth/clientHeight会提供没有滚动条(减去它)的 width/height。换句话说,它们返回的是可用于内容的文档的可见部分的 width/height。window.innerWidth/innerHeight包括了滚动条。
- 如果这里存在一个滚动条,并且滚动条占用了一些空间,那么
DOCTYPE很重要- 当 HTML 中没有
<!DOCTYPE HTML>时,顶层级(top-level)几何属性的工作方式可能就会有所不同。可能会出现一些稀奇古怪的情况。
- 当 HTML 中没有
- 为了获取窗口(window)的宽度和高度,我们可以使用
- 文档的 width/height
- 由于根文档元素是
document.documentElement,并且它包围了所有内容,因此我们可以通过使用documentElement.scrollWidth/scrollHeight来测量文档的完整大小。
- 由于根文档元素是
- 获得当前滚动
- DOM 元素的当前滚动状态在其
scrollLeft/scrollTop属性中。 - 对于文档滚动,在大多数浏览器中,我们可以使用
document.documentElement.scrollLeft/scrollTop,但在较旧的基于 WebKit 的浏览器中则不行,例如在 Safari(bug 5991)中,我们应该使用document.body而不是document.documentElement。
- DOM 元素的当前滚动状态在其
- 我们也可以从
window的scrollX和scrollY属性中获取滚动信息window.pageXOffset是window.scrollX的别名。
window.pageYOffset是window.scrollY的别名。
- 滚动:scrollTo,scrollBy,scrollIntoView
- 可以通过更改
scrollTop/scrollLeft来滚动常规元素- 方法
scrollBy(x,y)将页面滚动至 相对于当前位置的(x, y)位置。例如,scrollBy(0,10)会将页面向下滚动10px。 - 方法
scrollTo(pageX,pageY)将页面滚动至 绝对坐标,使得可见部分的左上角具有相对于文档左上角的坐标(pageX, pageY)。就像设置了scrollLeft/scrollTop一样。
- 方法
- 可以通过更改
- scrollIntoView
elem.scrollIntoView(top)的调用将滚动页面以使elem可见。它有一个参数- 如果
top=true(默认值),页面滚动,使elem出现在窗口顶部。元素的上边缘将与窗口顶部对齐。
- 如果
- 如果
top=false,页面滚动,使elem出现在窗口底部。元素的底部边缘将与窗口底部对齐。
- 如果
- 禁止滚动
- 要使文档不可滚动,只需要设置
document.body.style.overflow = "hidden"。
- 要使文档不可滚动,只需要设置
坐标
移动页面的元素,我们应该先熟悉坐标
- 大多数 JavaScript 方法处理的是以下两种坐标系中的一个
- 相对于窗口 —— 类似于
position:fixed,从窗口的顶部/左侧边缘计算得出。- 我们将这些坐标表示为
clientX/clientY,当我们研究事件属性时,就会明白为什么使用这种名称来表示坐标。
- 我们将这些坐标表示为
- 相对于文档 —— 与文档根(document root)中的
position:absolute类似,从文档的顶部/左侧边缘计算得出。- 我们将它们表示为
pageX/pageY。
- 我们将它们表示为
- 相对于窗口 —— 类似于
- 元素坐标:getBoundingClientRect
- 方法
elem.getBoundingClientRect()返回最小矩形的窗口坐标,该矩形将elem作为内建 DOMRect 类的对象。 - 主要的
DOMRect属性x/y—— 矩形原点相对于窗口的 X/Y 坐标,
width/height—— 矩形的 width/height(可以为负)。
- 派生(derived)属性
top/bottom—— 顶部/底部矩形边缘的 Y 坐标,
left/right—— 左/右矩形边缘的 X 坐标。
- 为什么需要派生(derived)属性?如果有了
x/y,为什么还要还会存在top/left?- 从技术上讲,
width/height可能为负数,从而允许“定向(directed)”矩形,例如代表带有正确标记的开始和结束的鼠标选择。 - 负的
width/height值表示矩形从其右下角开始,然后向左上方“增长”。 elem.getBoundingClientRect()总是返回正数的 width/height,这里我们提及负的width/height只是为了帮助你理解,为什么这些看起来重复的属性,实际上并不是重复的。
- 从技术上讲,
- IE 浏览器不支持
x/y- 由于历史原因,IE 浏览器不支持
x/y属性。
- 由于历史原因,IE 浏览器不支持
- 坐标的 right/bottom 与 CSS position 属性不同
- 相对于窗口(window)的坐标和 CSS
position:fixed之间有明显的相似之处。 - 在 CSS 定位中,
right属性表示距右边缘的距离,而bottom属性表示距下边缘的距离
- 相对于窗口(window)的坐标和 CSS
- elementFromPoint(x, y)
- 对
document.elementFromPoint(x, y)的调用会返回在窗口坐标(x, y)处嵌套最多(the most nested)的元素。
- 对
- 对于在窗口之外的坐标,
elementFromPoint返回null- 方法
document.elementFromPoint(x,y)只对在可见区域内的坐标(x,y)起作用。 - 如果任何坐标为负或者超过了窗口的 width/height,那么该方法就会返回
null。
- 方法
- 用于 “fixed” 定位
- 想要在某元素附近展示内容,我们可以使用
getBoundingClientRect来获取这个元素的坐标,然后使用 CSSposition以及left/top(或right/bottom)。
- 想要在某元素附近展示内容,我们可以使用
- 文档坐标
- 在 CSS 中,窗口坐标对应于
position:fixed,而文档坐标与顶部的position:absolute类似。 - 我们可以使用
position:absolute和top/left来把某些内容放到文档中的某个位置,以便在页面滚动时,元素仍能保留在该位置。但是我们首先需要正确的坐标。 pageY=clientY+ 文档的垂直滚动出的部分的高度。
pageX=clientX+ 文档的水平滚动出的部分的宽度。
- 在 CSS 中,窗口坐标对应于
- 方法
