浏览器:文档,事件,接口

DOM 树

HTML 文档的主干是标签(tag)。

根据文档对象模型(DOM),每个 HTML 标签都是一个对象。嵌套的标签是闭合标签的“子标签(children)”。标签内的文本也是一个对象。

所有这些对象都可以通过 JavaScript 来访问,我们可以使用它们来修改页面。

一共有 12 种节点类型。实际上,我们通常用到的是其中的 4 种:

  1. document — DOM 的“入口点”。
  2. 元素节点 — HTML 标签,树构建块。
  3. 文本节点 — 包含文本。
  4. 注释 — 有时我们可以将一些信息放入其中,它不会显示,但 JS 可以从 DOM 中读取它。

DOM 集合是只读的

DOM 集合,甚至可以说本章中列出的 所有 导航(navigation)属性都是只读的。

我们不能通过类似 childNodes[i] = ... 的操作来替换一个子节点。

修改子节点需要使用其它方法。我们将会在下一章中看到它们。

DOM 集合是实时的

除小部分例外,几乎所有的 DOM 集合都是 实时 的。换句话说,它们反映了 DOM 的当前状态。

如果我们保留一个对 elem.childNodes 的引用,然后向 DOM 中添加/移除节点,那么这些节点的更新会自动出现在集合中。

总结

HTML/XML 文档在浏览器内均被表示为 DOM 树。

  • 标签(tag)成为元素节点,并形成文档结构。
  • 文本(text)成为文本节点。
  • ……等,HTML 中的所有东西在 DOM 中都有它的位置,甚至对注释也是如此。

遍历 DOM

DOM 让我们可以对元素和它们中的内容做任何事,但是首先我们需要获取到对应的 DOM 对象。

对 DOM 的所有操作都是以 document 对象开始。它是 DOM 的主“入口点”。从它我们可以访问任何节点。

在 DOM 的世界中,null 就意味着“不存在”

在 DOM 中,null 值就意味着“不存在”或者“没有这个节点”。

DOM 集合是只读的

DOM 集合是实时的

不要使用 for..in 来遍历集合

更多链接:表格

元素支持 (除了上面给出的,之外) 以下这些属性: table.rows — 元素的集合。table.caption/tHead/tFoot — 引用元素 ,。table.tBodies — 元素的集合(根据标准还有很多元素,但是这里至少会有一个 — 即使没有被写在 HTML 源文件中,浏览器也会将其放入 DOM 中)。 ,, 元素提供了 rows 属性: tbody.rows — 表格内部 元素的集合。 : tr.cells — 在给定 中的 在封闭的 // 中的位置(索引)。tr.rowIndex — 在整个表格中 的编号(包括表格的所有行)。 中单元格的编号。

总结

给定一个 DOM 节点,我们可以使用导航(navigation)属性访问其直接的邻居。

这些属性主要分为两组:

  • 对于所有节点:parentNodechildNodesfirstChildlastChildpreviousSiblingnextSibling
  • 仅对于元素节点:parentElementchildrenfirstElementChildlastElementChildpreviousElementSiblingnextElementSibling

某些类型的 DOM 元素,例如 table,提供了用于访问其内容的其他属性和集合。

搜索:getElement,querySelector

document.getElementById 或者只使用 id

如果一个元素有 id 特性(attribute),那我们就可以使用 document.getElementById(id) 方法获取该元素,无论它在哪里。

id 必须是唯一的

id 必须是唯一的。在文档中,只能有一个元素带有给定的 id

如果有多个元素都带有同一个 id,那么使用它的方法的行为是不可预测的,例如 document.getElementById 可能会随机返回其中一个元素。因此,请遵守规则,保持 id 的唯一性。

只有 document.getElementById,没有 anyElem.getElementById

getElementById 方法只能被在 document 对象上调用。它会在整个文档中查找给定的 id

querySelectorAll

到目前为止,最通用的方法是 elem.querySelectorAll(css),它返回 elem 中与给定 CSS 选择器匹配的所有元素。

matches

之前的方法是搜索 DOM。

elem.matches(css) 不会查找任何内容,它只会检查 elem 是否与给定的 CSS 选择器匹配。它返回 truefalse

closest

元素的祖先(ancestor)是:父级,父级的父级,它的父级等。祖先们一起组成了从元素到顶端的父级链。

elem.closest(css) 方法会查找与 CSS 选择器匹配的最近的祖先。elem 自己也会被搜索。

换句话说,方法 closest 在元素中得到了提升,并检查每个父级。如果它与选择器匹配,则停止搜索并返回该祖先。

总结

目前为止,最常用的是 querySelectorquerySelectorAll,但是 getElement(s)By* 可能会偶尔有用,或者可以在旧脚本中找到。

此外:

  • elem.matches(css) 用于检查 elem 与给定的 CSS 选择器是否匹配。
  • elem.closest(css) 用于查找与给定 CSS 选择器相匹配的最近的祖先。elem 本身也会被检查。

让我们在这里提一下另一种用来检查子级与父级之间关系的方法,因为它有时很有用:

  • 如果 elemBelemA 内(elemA 的后代)或者 elemA==elemBelemA.contains(elemB) 将返回 true。

节点属性:type,tag 和 content

DOM 节点类

不同的 DOM 节点可能有不同的属性。例如,标签 <a> 相对应的元素节点具有链接相关的(link-related)属性,标签 <input> 相对应的元素节点具有与输入相关的属性,等。文本节点与元素节点不同。但是所有这些标签对应的 DOM 节点之间也存在共有的属性和方法,因为所有类型的 DOM 节点都形成了一个单一层次的结构(single hierarchy)。

每个 DOM 节点都属于相应的内建类。

层次结构(hierarchy)的根节点是 EventTargetNode 继承自它,其他 DOM 节点继承自 Node。

image-20210202215947121

特性和属性(Attributes and properties)

当浏览器加载页面时,它会“读取”(或者称之为:“解析”)HTML 并从中生成 DOM 对象。对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)。

HTML 特性

在 HTML 中,标签可能拥有特性(attributes)。当浏览器解析 HTML 文本,并根据标签创建 DOM 对象时,浏览器会辨别 标准的 特性并以此创建 DOM 属性。

所以,当一个元素有 id 或其他 标准的 特性,那么就会生成对应的 DOM 属性。但是非 标准的 特性则不会。

HTML 特性有以下几个特征:

  • 它们的名字是大小写不敏感的(idID 相同)。
  • 它们的值总是字符串类型的。

总结

  • 特性(attribute)— 写在 HTML 中的内容。
  • 属性(property)— DOM 对象中的内容。

简略的对比:

单元格的集合。tr.sectionRowIndex — 给定的
: td.cellIndex — 在封闭的
属性 特性
类型 任何值,标准的属性具有规范中描述的类型 字符串
名字 名字(name)是大小写敏感的 名字(name)是大小写不敏感的

Window 大小和滚动

窗口的 width/height

为了获取窗口(window)的宽度和高度,我们可以使用 document.documentElementclientWidth/clientHeight

滚动:scrollTo,scrollBy,scrollIntoView

可以通过更改 scrollTop/scrollLeft 来滚动常规元素。

我们可以使用 document.documentElement.scrollTop/scrollLeft 对页面进行相同的操作(Safari 除外,而应该使用 document.body.scrollTop/Left 代替)。

方法 scrollBy(x,y) 将页面滚动至 相对于当前位置的 (x, y) 位置。例如,scrollBy(0,10) 会将页面向下滚动 10px

方法 scrollTo(pageX,pageY) 将页面滚动至 绝对坐标,使得可见部分的左上角具有相对于文档左上角的坐标 (pageX, pageY)。就像设置了 scrollLeft/scrollTop 一样。

要滚动到最开始,我们可以使用 scrollTo(0,0)

scrollIntoView

为了完整起见,让我们再介绍一种方法:elem.scrollIntoView(top)

elem.scrollIntoView(top) 的调用将滚动页面以使 elem 可见。它有一个参数:

  • 如果 top=true(默认值),页面滚动,使 elem 出现在窗口顶部。元素的上边缘将与窗口顶部对齐。
  • 如果 top=false,页面滚动,使 elem 出现在窗口底部。元素的底部边缘将与窗口底部对齐。

下面这个按钮会滚动页面,以使其自身定位在窗口顶部:

this.scrollIntoView()

下面这个按钮会滚动页面,以使其自身定位在窗口底部:

this.scrollIntoView(false)

总结

几何:

  • 文档可见部分的 width/height(内容区域的 width/height):document.documentElement.clientWidth/clientHeight

  • 整个文档的 width/height,其中包括滚动出去的部分:

    let scrollHeight = Math.max(
      document.body.scrollHeight,
      document.documentElement.scrollHeight,
      document.body.offsetHeight,
      document.documentElement.offsetHeight,
      document.body.clientHeight,
      document.documentElement.clientHeight
    )

    滚动:

  • 读取当前的滚动:window.pageYOffset/pageXOffset

  • 更改当前的滚动:

    • window.scrollTo(pageX,pageY) — 绝对坐标,
    • window.scrollBy(x,y) — 相对当前位置进行滚动,
    • elem.scrollIntoView(top) — 滚动以使 elem 可见(elem 与窗口的顶部/底部对齐)。

创建自定义事件

我们不仅可以分配事件处理程序,还可以从 JavaScript 生成事件。

自定义事件可用于创建“图形组件”。

事件构造器

内建事件类形成一个层次结构(hierarchy),类似于 DOM 元素类。根是内建的 Event 类。

我们可以像这样创建 Event 对象:

let event = new Event(type[, options]);

参数:

  • type —— 事件类型,可以是像这样 "click" 的字符串,或者我们自己的像这样 "my-event" 的参数。

  • options —— 具有两个可选属性的对象:

    • bubbles: true/false —— 如果为 true,那么事件会冒泡。
    • cancelable: true/false —— 如果为 true,那么“默认行为”就会被阻止。稍后我们会看到对于自定义事件,它意味着什么。

    默认情况下,以上两者都为 false:{bubbles: false, cancelable: false}

dispatchEvent

事件对象被创建后,我们应该使用 elem.dispatchEvent(event) 调用在元素上“运行”它。

然后,处理程序会对它做出反应,就好像它是一个常规的浏览器事件一样。如果事件是用 bubbles 标志创建的,那么它会冒泡。

表单属性和方法

表单(form)以及例如 <input> 的控件(control)元素有许多特殊的属性和事件。

导航:表单和元素

文档中的表单是特殊集合 document.forms 的成员。

这就是所谓的“命名的集合”:既是被命名了的,也是有序的。我们既可以使用名字,也可以使用在文档中的编号来获取表单。

document.forms.my - name="my" 的表单
document.forms[0] - 文档中的第一个表单

当我们有了一个表单时,其中的任何元素都可以通过命名的集合 form.elements 来获取到。

反向引用:element.form

对于任何元素,其对应的表单都可以通过 element.form 访问到。因此,表单引用了所有元素,元素也引用了表单。

这是一张示意图:

image-20210221110537095

表单元素

让我们来谈谈表单控件。

input 和 textarea

我们可以通过 input.value(字符串)或 input.checked(布尔值)来访问复选框(checkbox)中的它们的 value