未分类

JavaScript-DOM扩展

一、选择符API扩展

querySelector()

该方法接受一个CSS选择符,返回与该模式匹配的第一个元素。没有匹配的元素则返回null。在Document类型调用该方法会在文档元素的范围内查找匹配元素,在Element类型调用该方法会在该元素的后代范围内查找匹配元素。传入非法的选择符时会抛出错误。

1
2
3
var div = document.querySelector("div");
var myDiv = document.querySelector("#myDiv");
var bth = myDiv.querySelector(".btn");

querySelectorAll()

该方法参数与querySelector()方法一样,但返回的是一个NodeList实例,包含所有匹配的元素。如果没有匹配的元素,则返回一个空的NodeList。返回的NodeList带有属性方法,其底层实现类似于一组元素的快照,而非不断对文档进行搜索的动态查询,从而避免了使用NodeList通常会引起的大多数性能问题。

1
2
3
var div = document.querySelectorAll("div");
var .btn = document.querySelectorAll(".btn");
var firstDiv = div.item(0); // 或者div[0]

matchesSelector()

该方法接受一个CSS选择符,如果调用元素与该选择符匹配则返回true,否则返回false

1
2
var div = document.querySelector("div.aha");
console.log(div.matchesSelector(".aha")); // true

二、元素遍历扩展

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

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

过去,要跨浏览器遍历某元素的所有子元素,需要像下面的示例1一样去写代码,childNodes列表中包含元素节点、文本节点、注释节点等信息,因此需要检测元素节点。而有了上面5个拓展属性之后就可使得代码更加简洁,而且循环次数更少,如示例2。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 示例1
var child = element.firstChild;
while (child != element.lastChild) {
if (child.nodeType == 1) { // 需要检查是否为元素节点
processChild(child);
}
child = child.nextSibling;
}

// 示例2
child = element.firstElementChild;
while (child != element.lastElementChild) {
processChild(child);
child = child.nextElementSibling;
}

三、HTML5扩展

与类相关的扩充

getElementsByClassName(): 该方法接受一个参数,即包含一个或多个类型的字符串,返回一个NodeList,包含所有带有指定类型的元素。

classList属性: 在操作类名时,需要通过className属性添加、删除和替换类名,由于className的值是一个字符串,所以每次修改都需要替换整个字符串的值,如果元素有多个类名,仅修改其中一个类名都需要写不少代码,非常麻烦。classList的出现解决了这些麻烦,使得操作类名更简单更安全。classList属性是新集合类型DOMTokenList的实例,有length属性表示包含的元素数量,通过item()方法或方括号语法可以取得每个元素。DOMTokenList还有以下方法:

  • add(value): 将给定的字符串添加到列表中
  • contains(value): 如果列表中存在给定的值,返回true,否则返回false
  • remove(value): 从列表中删除给定字符串
  • toggle(value): 如果列表中存在给定值,删除它,否则添加它

下面给出代码示例,示例1展示了使用className修改类名的过程,示例2则是使用classList的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 示例1,删除"xxx"类
var classNames = div.className.split(/\s+/);
var pos = -1;
for (var i = 0; i < classNames.length; ++i) {
if (className[i] == "xxx") {
pos = i;
break;
}
}
classNames.splice(i, 1);
div.className = classNames.join(" ");

// 示例2
div.classList.remove("xxx"); // 删除"xxx"类
div.classList.add("yyy"); // 添加"yyy"类

焦点管理

document.activeElement: 引用DOM中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入和代码中调用focus()方法。默认情况下,文档刚刚加载完时,document.activeElement中保存的是document.body元素的引用。文档加载期间,document.activeElement的值是null

document.hasFocus(): 该方法用于确定文档是否获取了焦点,便于检测用户是否正在与页面交互。

1
2
3
4
var btn = document.getElementById("#btn");
btn.focus();
console.log(document.activeElement == btn); // true
console.log(document.hasFocus()); // true

HTMLDocument

document.readyState: DocumentreadyState属性有两个可能值,"loading"表示正在加载文档,"complete"表示已经加载完文档。document.readyState的基本用法如下:

1
2
3
if (document.readyState == "complete") {
// do something
}

document.compatMode: 该属性用于告诉开发人员浏览器采用了哪种渲染模式,标准模式下其值为"CSS1Compat",混杂模式下其值为"BackCompat"

document.head: 包含对<head>元素的引用。

字符集属性

document.charset: 表示文档中使用的字符集。基本用法如下:

1
2
console.log(document.charset);
document.charset = "utf-8";

document.defaultCharset: 表示根据默认浏览器及操作系统的设置,当前文档的默认字符集应该是什么。

自定义数据属性

HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息或者语义信息。这些属性可以任意添加、随便命名,但必须以data-开头。添加自定义属性之后在JS中可以通过元素的dataset属性来访问。dataset是一个键值对,每个data-name形式的属性都会有一个对应的属性,属性名会去掉data-前缀。

1
2
3
4
5
// <div id="myDiv" data-name="xxx" data-phone="123" data-email="yyy"></div>
var div = document.getElementById("myDiv");
console.log(div.dataset.name); // "xxx"
console.log(div.dataset.phone); // "123"
console.log(div.dataset.email); // "yyy"

插入标记

innerHTML属性:在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素、注释、文本)对应的HTML标记。在写模式下,会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点。

1
2
3
4
<div id="myDiv">
<p>some content</p>
<p>another content</p>
</div>

1
2
3
var div = document.getElementById("myDiv");
console.log(div.innerHTML); // 输出"<p>some content</p>\n<p>another content</p>"
div.innerHTML = "haha~"; // div的内容变成<div id="myDiv">haha~</div>

outerHTML属性:读模式下,outerHTML返回调用它的元素以及所有子节点的HTML标签。在写模式下会根据指定的HTML字符串创建新的DOM子树,然后用这个子树完全替换调用元素。

1
2
3
4
5
6
<div id="div-1">
<div id="div-2">
<p>some content</p>
<p>another content</p>
</div>
</div>

1
2
3
var div2 = document.getElementById("div-2");
console.log(div2.outerHTML); // 输出"<div id="div-2"><p>some content</p><p>another content</p></div>"
div2.outerHTML = "haha~"; // div的内容变成<div id="div-1">haha~</div>

insertAdjacentHTML()方法:该方法接受两个参数,要插入的位置和要插入的HTML文本。第一个参数必须是下列值之一:

  • "beforebegin": 在当前元素之前插入一个紧邻的同辈元素
  • "afterbegin": 在当前元素下插入一个新的子元素,新元素放在原有的第一个子元素之前
  • "beforeend": 在当前元素的最后一个子元素后面插入新的子元素
  • "afterend": 在当前元素后面插入一个紧邻的同辈元素

下面是代码示例:

1
2
3
4
element.insertAdjacentHTML("beforebegin", "<p>haha~</p>");
element.insertAdjacentHTML("afterbegin", "<p>haha~</p>");
element.insertAdjacentHTML("beforeend", "<p>haha~</p>");
element.insertAdjacentHTML("afterend", "<p>haha~</p>");

内存与性能问题:

  1. 假设某个元素有一个事件处理程序(或者引用了一个JS对象作为属性),在使用上述的某个属性将该元素从文档树中删除后,元素与事件处理程序(或JS对象)之间的绑定关系在内存中并没有一并删除。如果这种情况频繁出现,页面占用的内存数量就会明显增加。因此,在使用innerHTMLouterHTMLinsertAdjacentHTML()时最好先手动删除要被替换元素的所有事件处理程序和JS对象属性。

  2. 使用innerHTML属性与通过多次DOM操作先创建节点再指定它们之间的关系相比,效率要高很多。这是因为在设置innerHTMLouterHTML时,就会创建一个HTML解析器,这个解析器是在浏览器级别的代码(通常是C++)基础上运行,比执行JS要快得多。此外,创建和销毁这个HTML解析器也会带来性能损失,所以最好将设置innerHTMLouterHTML的次数控制在合理的范围之内。

scrollIntoView()方法

该方法可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。当页面发生变化时,一般会用这个方法来吸引用户的注意力。实际上,为某个元素设置焦点也会导致浏览器滚动并显示出获得焦点的元素。

分享到