尽管 DOM 作为 API 已经非常完善了,但为了实现更多的功能,仍然会有一些标准或专有的扩 展。2008 年之前,浏览器中几乎所有的 DOM 扩展都是专有的。此后,W3C 着手将一些已经 成为事实标准的专有扩展标准化并写入规范当中。 对 DOM 的两个主要的扩展是 Selectors API(选择符 API)和 HTML5。这两个扩展都源自开发社区, 而将某些常见做法及 API 标准化一直是众望所归。此外,还有一个不那么引人瞩目的 Element Traversal (元素遍历)规范,为 DOM 添加了一些属性。虽然前述两个主要规范(特别是 HTML5)已经涵盖了大 量的 DOM 扩展,但专有扩展依然存在。

选择符API

  • querySelector()方法

querySelector()方法接收一个 CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹
配的元素,返回 null。

  1. //取得 body 元素
  2. var body = document.querySelector("body");
  3. //取得 ID 为"myDiv"的元素
  4. var myDiv = document.querySelector("#myDiv");
  5. //取得类为"selected"的第一个元素
  6. var selected = document.querySelector(".selected");
  7. //取得类为"button"的第一个图像元素
  8. var img = document.body.querySelector("img.button");
  • querySelectorAll()方法

querySelectorAll()方法接收的参数与 querySelector()方法一样,都是一个 CSS 选择符,但
返回的是所有匹配的元素而不仅仅是一个元素。这个方法返回的是一个 NodeList 的实例。

  1. //取得某<div>中的所有<em>元素(类似于 getElementsByTagName("em"))
  2. var ems = document.getElementById("myDiv").querySelectorAll("em");
  3. //取得类为"selected"的所有元素
  4. var selecteds = document.querySelectorAll(".selected");
  5. //取得所有<p>元素中的所有<strong>元素
  6. var strongs = document.querySelectorAll("p strong");
  • matchesSelector()方法

Selectors API Level 2 规范为 Element 类型新增了一个方法 matchesSelector()。这个方法接收
一个参数,即 CSS 选择符,如果调用元素与该选择符匹配,返回 true;否则,返回 false

  1. if (document.body.matchesSelector("body.page1")){
  2. //true
  3. }

元素遍历API

这个作为了解:

对于元素间的空格,IE9 及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。这样,就导致了在使用 childNodes 和 firstChild 等属性时的行为不一致。为了弥补这一差异,而同时又保持 DOM 规范不变Element Traversal 规范(www.w3.org/TR/ElementTraversal/)新定义了一组属性。

Element Traversal API 为 DOM 元素添加了以下 5 个属性:

  • childElementCount:返回子元素(不包括文本节点和注释)的个数
  • firstElementChild:指向第一个子元素;firstChild 的元素版
  • lastElementChild:指向最后一个子元素;lastChild 的元素版
  • previousElementSibling:指向前一个同辈元素;previousSibling 的元素版
  • nextElementSibling:指向后一个同辈元素;nextSibling 的元素版

HTML5


对于传统 HTML 而言,HTML5 是一个叛逆。所有之前的版本对 JavaScript 接口的描述都不过三言 两语,主要篇幅都用于定义标记,与 JavaScript 相关的内容一概交由 DOM 规范去定义。而 HTML5 规范则围绕如何使用新增标记定义了大量 JavaScript API。其中一些 API 与 DOM 重叠,定义了浏览器应该支持的 DOM 扩展

与类相关的扩充
1、getElementsByClassName()方法

  1. //取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓
  2. var allCurrentUsernames = document.getElementsByClassName("username current");
  3. //取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素
  4. var selected = document.getElementById("myDiv").getElementsByClassName("selected");

2、classList 属性
在操作类名时,需要通过 className 属性添加、删除和替换类名。

  1. <div class="bd user disabled">...</div>
  2. 这个<div>元素一共有三个类名。要从中删除一个类名,需要把这三个类名拆开,删除不想要的那
  3. 个,然后再把其他类名拼成一个新字符串。请看下面的例子。
  4. //删除"user"类
  5. //首先,取得类名字符串并拆分成数组
  6. var classNames = div.className.split(/\s+/);
  7. //找到要删的类名
  8. var pos = -1, i, len;
  9. for (i=0, len=classNames.length; i < len; i++){
  10. if (classNames[i] == "user"){
  11. pos = i;
  12. break;
  13. }
  14. }
  15. //删除类名
  16. classNames.splice(i,1);
  17. //把剩下的类名拼成字符串并重新设置
  18. div.className = classNames.join(" ");

焦点管理

HTML5 也添加了辅助管理 DOM 焦点的功能。首先就是 document.activeElement 属性,这个
属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是
通过按 Tab 键)和在代码中调用 focus()方法。来看几个例子。

  1. var button = document.getElementById("myButton");
  2. button.focus();
  3. alert(document.activeElement === button);//true

默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元素的引用。文档加载期间,document.activeElement 的值为 null。另外就是新增了 document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。

  1. var button = document.getElementById("myButton");
  2. button.focus();
  3. alert(document.hasFocus()); //true

小结:

通过检测文档是否获得了焦点,可以知道用户是不是正在与页面交互。
查询文档获知哪个元素获得了焦点,以及确定文档是否获得了焦点,这两个功能最重要的用途是提高 Web 应用的无障碍性。无障碍 Web 应用的一个主要标志就是恰当的焦点管理,而确切地知道哪个元素获得了焦点是一个极大的进步,至少我们不用再像过去那样靠猜测了。

HTMLDocument的变化
  1. readyState 属性
    IE4 最早为 document 对象引入了 readyState 属性。然后,其他浏览器也都陆续添加这个属性,
    最终 HTML5 把这个属性纳入了标准当中。Document 的 readyState 属性有两个可能的值:
     loading,正在加载文档;
     complete,已经加载完文档。

  2. 兼容模式
    自从 IE6 开始区分渲染页面的模式是标准的还是混杂的,检测页面的兼容模式就成为浏览器的必要
    功能。IE 为此给 document 添加了一个名为 compatMode 的属性,这个属性就是为了告诉开发人员浏
    览器采用了哪种渲染模式。就像下面例子中所展示的那样,在标准模式下,document.compatMode 的
    值等于”CSS1Compat”,而在混杂模式下,document.compatMode 的值等于”BackCompat”。

    1. if (document.compatMode == "CSS1Compat"){
    2. alert("Standards mode");
    3. } else {
    4. alert("Quirks mode");
    5. }

    字符集属性

HTML5 新增了几个与文档字符集有关的属性。其中,charset 属性表示文档中实际使用的字符集,
也可以用来指定新字符集。默认情况下,这个属性的值为”UTF-16”,但可以通过元素、响应头
部或直接设置 charset 属性修改这个值。来看一个例子:

  1. alert(document.charset); //"UTF-16"
  2. document.charset = "UTF-8";

自定义数据属性

HTML5 规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的
信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以 data-开头即可。来看一个例子。

  1. <div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>

添加了自定义属性之后,可以通过元素的 dataset 属性来访问自定义属性的值。dataset 属性的
值是 DOMStringMap 的一个实例,也就是一个名值对儿的映射。在这个映射中,每个 data-name 形式
的属性都会有一个对应的属性,只不过属性名没有 data-前缀(比如,自定义属性是 data-myname,
那映射中对应的属性就是 myname)

  1. //本例中使用的方法仅用于演示
  2. var div = document.getElementById("myDiv");
  3. //取得自定义属性的值
  4. var appId = div.dataset.appId;
  5. var myName = div.dataset.myname;
  6. //设置值
  7. div.dataset.appId = 23456;
  8. div.dataset.myname = "Michael";
  9. //有没有"myname"值呢?
  10. if (div.dataset.myname){
  11. alert("Hello, " + div.dataset.myname);
  12. }

插入标记
  1. innerHTML 属性

    1. div.innerHTML = "Hello world!" div.innerText ="Hello world!"
  2. outerHTML 在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML 标签。在写模式下,

会根据指定的 HTML 字符串创建新的 DOM 子树,然后用这个 DOM 子树完全替换调用元素。下面是一
个例子:

  1. <div id="content">
  2. <p>This is a <strong>paragraph</strong> with a list following it.</p>
  3. <ul>
  4. <li>Item 1</li>
  5. <li>Item 2</li>
  6. <li>Item 3</li>
  7. </ul>
  8. </div>
  9. 使用 outerHTML 属性以下面这种方式设置值:
  10. div.outerHTML = "<p>This is a paragraph.</p>";

scrollIntoView()方法

如何滚动页面也是 DOM 规范没有解决的一个问题。为了解决这个问题,浏览器实现了一些方法,
以方便开发人员更好地控制页面滚动。在各种专有方法中,HTML5 最终选择了 scrollIntoView()作
为标准方法。
scrollIntoView()可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用
元素就可以出现在视口中。如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动
之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部
出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐,例如:

  1. //让元素可见
  2. document.forms[0].scrollIntoView();

当页面发生变化时,一般会用这个方法来吸引用户的注意力。实际上,为某个元素设置焦点也会导
致浏览器滚动并显示出获得焦点的元素。

专有扩展

1、文档模式 document mode

2、children 属性

3、contains()方法

在实际开发中,经常需要知道某个节点是不是另一个节点的后代。IE 为此率先引入了 contains()
方法,以便不通过在 DOM 文档树中查找即可获得这个信息。调用 contains()方法的应该是祖先节点,
也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点,
该方法返回 true;否则,返回 false。以下是一个例子:

  1. alert(document.documentElement.contains(document.body)); //true

4、插入文本

innerText 属性:
通过 innertText 属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过
innerText 读取值时,它会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。在通过
innerText 写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。

  1. <div id="content">
  2. <p>This is a <strong>paragraph</strong> with a list following it.</p>
  3. <ul>
  4. <li>Item 1</li>
  5. <li>Item 2</li>
  6. <li>Item 3</li>
  7. </ul>
  8. </div>
  9. div.innerText = "Hello world!";
  10. <div id="content">Hello world!</div>

5、滚动
  • scrollIntoViewIfNeeded(alignCenter):只在当前元素在视口中不可见的情况下,才滚

动浏览器窗口或容器元素,最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。
如果将可选的 alignCenter 参数设置为 true,则表示尽量将元素显示在视口中部(垂直方向)。
Safari 和 Chrome 实现了这个方法

  • scrollByLines(lineCount):将元素的内容滚动指定的行高,lineCount 值可以是正值,

也可以是负值。Safari 和 Chrome 实现了这个方法

  • scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决

定。Safari 和 Chrome 实现了这个方法

希望大家要注意的是,scrollIntoView()和 scrollIntoViewIfNeeded()的作用对象是元素的容器,而 scrollByLines()和 scrollByPages()影响的则是元素自身。下面还是来看几个示例吧。

  1. //将页面主体滚动 5 行
  2. document.body.scrollByLines(5);
  3. //在当前元素不可见的时候,让它进入浏览器的视口
  4. document.images[0].scrollIntoViewIfNeeded();
  5. //将页面主体往回滚动 1 页
  6. document.body.scrollByPages(-1);