资讯专栏INFORMATION COLUMN

《JavaScript高级程序设计》(第3版)读书笔记 第11章 DOM拓展

luck / 399人阅读

摘要:对的两个主要拓展是选择和。以下插入标记的拓展已经纳入了规范。在写模式下,会根据指定的字符串创建新的子树,然后用这个子树完全替换调用元素。在删除带有时间处理程序或引用了其他对象子树时,就有可能导致内存占用问题。

尽管DOM作为API已经非常完善了,但为了实现更多功能,仍然会有一些标准或专有的拓展。2008年之前,浏览器中几乎所有的拓展都是专有的,此后W3C着手将一些已经成为事实标准的专有拓展标准化,并写入规范中。

对DOM的两个主要拓展是Selectors API (选择API)和HTML5。这两个拓展都源自开发社区,而将某些常见做啊及API标准化,一直是众望所归。

选择符API

jQuery的核心就是通过CSS选择符查询DOM文档取得元素的引用,从而抛开了getElementById()gettElementByTagName()

Selectors API 是又W3C发起制定的一个标准,致力于让浏览器原生支持CSS查询。所有实现这一功能的JavaScript库都会写一个基础的CSS解析器,然后再使用已有的DOM方法查询文档并找到匹配的节点。而把这个功能变成原生API之后,解析和树查询操作可以在浏览器内部通过编译后的代码完成,极大改善了性能。

querySelector()方法

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

// 取得body元素
var body = document.querySelector("body");

// 取得ID为“myDiv”的元素
var myDiv = document.querySelector("#myDiv");

// 取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");

// 取得类为"button"的第一个图像元素
var selected = document.querySelector("img.button");

如果传入了不支持的选择符,会抛出错误。

querySelectorAll()方法

和上面类似,但返回的不仅仅是匹配的第一个元素,而是一个NodeList的实例,如果没有匹配,NodeList就是空的。

// 取得某div中所有em元素
var ems = document.getElementById("myDiv").querySelectorAll("em");

// 取得类为“selected”的所有元素
var selected = document.querySelectorAll(".selected");

// 取得所有p元素中的所有strong元素
var strongs = document.querySelectorAll("p strong");

取得返回NodeList中的每一个元素,可以使用item()方法,也可以使用方括号语法。

matchesSelector()方法

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

截止2011年年中,还没有浏览器支持该方法,不过有一些实验性的实现。因此如果你想使用这个方法,最好编写一个包装函数:

function matchesSelector(element, selector) {
  if (element.matchesSelecotr) {
    return element.matchesSelecotr(selector)
  } else if (element.msMatchesSelecotr) {
    return element.msMatchesSelecotr(selector)
  } else if (element.mozMatchesSelecotr) {
    return element.mozMatchesSelecotr(selector)
  } else if (element.webkitMatchesSelecotr) {
    return element.webkitMatchesSelecotr(selector)
  } else {
    throw new Error("Not supported.");
  }
}
元素遍历

对于元素间的空格,IE9及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。为了弥补这一差异,而又同时保持DOM规范不变,Element Traversal 规范新定义了一组属性。

childElementCount: 返回子元素(不包括文本节点和注释)的个数

firstElementChild: 指向第一个子元素;firstChild的元素版

lastElementChild: 指向最后一个子元素;lastChild的元素版

previousElementSibling: 指向前一个同辈元素;previousSibling的元素版

nextElementSibling: 指向前一个同辈元素;nextSibling的元素版

// 跨浏览器遍历某元素的所有子元素

// 老版的兼容性代码
var i,
    len,
    child = element.firstChild;
while(child != element.lastChild) {
  // 检查是不是元素
  if (child.nodeType == 1) {
    processChild(child);
  }
  child = child.nextSibling;
}

// 使用新版的方法
var i,
    len,
    child = element.firstChild;
while(child != element.lastElementChild) {
  processChild(child);
  child = child.nextElementSibling;
}

支持Element Traversal 规范的浏览器有 IE9+ Firefox3.5+ Safari4+ Chrome Opera10+

HTML5

HTML5所有之前的版本对JavaScript接口的描述不过三言两语,主要篇幅都用于定义标记,与JavaScript相关的内容一概交由DOM规范去定义。

HTML5规范则围绕如何使用新增标记定义了大量JavaScript API。其中一些API与DOM重叠,定义了浏览器应该支持的DOM拓展。

由于HTML5涉及的面非常广,本节只讨论与DOM节点相关的内容。

与类(class)相关的扩充

HTML5 新增了很多API,致力于简化CSS类的用法。

getElementsByClassName()方法
// 取得所有类中包含"username"和"current"的元素
// 类名的先后顺序无所谓
var allCurrentUsernames = docment.getElementsByClassName("username current");

// 取得ID为"myDiv"的元素中带有类名"selected"的所有元素
var selected = document.getElementById("myDiv").getElemenstByClassName("selected");

支持getElementsByClassName()方法的浏览器 IE9+ Firefox3+ Safari3.1+ Chrome Opera9.5+

classList属性

在操作类名时,需要通过className属性添加、删除和替换类名。因为className中是一个字符串,所以即使只是修改字符串一部分,也必须每次都设置整个字符串的值。

HTML5 新增了一种操作类名的方式,可以让操作更简单也更安全,那就是为所有元素添加了classList属性。

classList属性是新集合类型 DOMTokenList的实例。与其他DOM集合类似,DOMTokenList 有一个表示自己包含多少元素的length属性,而要去的每个元素可以使用item()方法,或者方括号语法。此外,这个新类型还定义如下方法:

add(value): 将给定的字符串值添加到列表中。如果值已经存在,就不添加了。

contains(value): 表示列表中是否存在给定的值,如果存在返回true,反之false。

remove(value): 从列表中删除给定的字符串。

toggle(value): 如果列表中已经存在给定的值,删除它;如果没有,添加它。

有了classList属性,除非你需要删除所有类名,或者完全重写元素的class属性,否则就用不到className属性了。

支持classList的浏览器 Firefox3.6+ Chrome

焦点管理

HTML5添加了辅助DOM焦点的功能。

document.activeElement属性,始终会引用DOM中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是通过Tab键)和在代码中调用focus()方法。

var button = document.getElementById("myButton");
button.focus();
console.log(document.activeElement === button);     // true

默认情况下,文档刚刚加载完成时,document.activeElement中保存的是document.body元素的引用。文档加载期间,document.activeElement的值为null

document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。

var button = document.getElementById("myButton");
button.focus();
console.log(document.hasFocus());        // true

支持的浏览器 IE4+ Firefox3+ Safari4+ Chrome Opera8+

HTMLDocument的变化 readyState属性

Document.readyState属性有两个可能的值:

loading正在加载文档

complete已经加载完文档

支持的浏览器 IE4+ Firefox3.6+ Safari Chrome Opera9+

if (document.readyState == "complete") {
  ...
}
兼容模式

自从IE6开始区分渲染页面的模式是标准的还是混杂的,检测页面的兼容性就成为浏览器的必要功能。IE为此给document添加了一个名为compatMode的属性,告诉开发人员浏览器采用了哪种渲染模式。

document.compatMode标准模式下等于"CSS1Compat",混杂模式下等于"BackCompat"。

最终HTML5将这个属性纳入标准

支持的浏览器 IE Firefox Safari3.1 Chrome Opera

if (document.compatMode == "CSS1Compat") {
  console.log("Standards mode");
} else {
  console.log("Quirks mode");
}
head属性

HTML5新增了document.head属性,与docuemnt.body对应

支持的浏览器 Chrome Safari 5+

var head = document.head || document.getElementsByTagName("head")[0];
字符集属性

HTML5新增了几个与文档字符集有关的属性

charset属性表示文档中实际使用的字符集,也可以用来指定新字符集。默认值是"UTF-16",可以通过元素、响应头部或直接设置charset属性修改这个值。

支持的浏览器 IE Safari Opera Chrome 。 Firefox 支持 document.Characterset

console.log(document.charset);     // "UTF-16"
document.charset = "UTF-8";

defaultCharset表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。如果文档没有使用默认的字符集,那charsetdefaultCharset属性值可能会不一样。

if (document.charset != document.defaultCharset) {
  console.log("Custom character set being used.");
}

支持的浏览器 IE Safari Chrome 。

自定义数据属性

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

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

var div = document.getElementById("myDiv");

// 取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;

// 设置值
div.dataset.appId = 2345;
div.dataset.myname = "Michael";

// 有没有"myname"值呢?
if (div.dataset.myname) {
  console.log("Hello, " + div.dataset.myname);
}
插入标记

虽然DOM为操作节点提供了细致入微的控制手段,但在需要给文档插入大量HTML标记的情况下,通过DOM操作仍然非常麻烦,因为不仅要创建一系列DOM节点,还要小心按照正确的顺序把它们连接起来。

相对而言,直接插入HTML字符串不仅更简单,速度也更快。以下插入标记的DOM拓展已经纳入了HTML5规范。

innerHTML属性

在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的HTML标记。

在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点。

This is a paragraph with a list following it.

  • Item 1
  • Item 2
  • Item 3

This is a paragraph with a list following it.

  • Item 1
  • Item 2
  • Item 3

不同浏览器返回的文本格式不会有所不同。IE和Opera会将所有标签转换为大小写形式,而Safari、Chrome、Firefox则会原原本本的按照原先文档中(或指定这些标签时)的格式返回HTML,包括空格和缩进。

在写模式下,传入innerHTML的值都会按照浏览器处理HTML的标注方式转换为元素(同样因浏览器而异)。如果设置的值仅是纯文本而没有HTML标签,那么结果就是设置纯文本。

div.innerHTML = "Hello world!";
div.innerHTML = "Hello & welcom, "reader"!";
// 以上操作得到: 
Hello & welcome, "reader"!

使用innerHTML属性也有一些限制,大多数浏览器中通过innerHTML插入

阅读需要支付1元查看
<