天天看点

DOM扩展

下述内容主要讲述了《JavaScript高级程序设计(第3版)》第11章关于“DOM扩展”。

尽管DOM作为API已经非常完善了,但是为了实现更多的功能,仍然会有一些标准或专有的扩展。对DOM的两个主要扩展是Selectors API(选择符API)和HTML5。

一、选择符API

Selectors API是由W3C发起制定的一个标准,致力于让浏览器原生支持CSS查询。其核心的两个方法:

querySelector()

querySelectorAll()

1. querySelector()方法

querySelector()

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

示例:

document.querySelector("body");
document.querySelector("#content");
document.querySelector(".bd");           

复制

2. querySelectorAll()方法

querySelectorAll()接受一个CSS选择符,返回与该模式匹配的NodeList实例,如果没有找到匹配的元素返回null。

示例:

var lis = document.querySelectorAll("ul li");
// 获取第一个li
lis.item(0); // 等价于:lis[0];           

复制

注意:上述两个函数如果传入不支持的选择符,会抛出错误。

3. matchesSelector()方法

matchesSelector()

方法大部分浏览器未实现,可使用matches()或者使用各个浏览器实验性的实现。

function matchesSelector(element, selec)           

复制

如果调用元素与该选择符匹配,返回true;否则,返回false。

function matchesSelector(element, selector){
    if (element.matchesSelector){
        return element.matchesSelector(selector);
    } else if (element.msMatchesSelector){
        return element.msMatchesSelector(selector);
    } else if (element.mozMatchesSelector){
        return element.mozMatchesSelector(selector);
    } else if (element.webkitMatchesSelector){
        return element.webkitMatchesSelector(selector);
    } else if(ele.matches){
        return ele.matches(selector);
    } else{
        throw new Error("Not supported.");
    }
}
matchesSelector(document.body, "body");     // true
matchesSelector(document.body, "body div"); // false           

复制

DOM扩展

二、元素遍历

对于元素间的空格,各浏览器对于childNodes和firstChild等属性处理行为不一致,为了弥补差异,Element Traversal新定义了一组属性。

示例:

<p>李刚</p>   
<ul>
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
    <li>item4</li>
    <li>item5</li>
</ul>
<p id="blog">http://blog.csdn.net/ligang2585116</p>           

复制

属性 说明 示例
childElementCount 子元素(不包含文本节点和注释)的个数 5
firstElementChild 指向第一个子元素,firstChild的元素版 <li>​Item 1​</li>
lastElementChild 指向最后一个子元素,lastChild的元素版 <li>​Item 5</li>​
previousElementSibling 指向前一个同辈元素,previousSibling的元素版 <p>李刚</p>
nextElementSibling 指向后一个同辈元素,nextSibling的元素版 <p id="blog">http://blog.csdn.net/ligang2585116</p>
var ul = document.querySelector("ul");
ul.firstChild;  // #text
ul.lastChild;   // <li>​Item 5​</li>​
ul.previousSibling; // #text
ul.nextSibling;     // #text           

复制

三、HTML5

1. 与类相关的扩充

(1)

getElementsByClassName("包含一或多个类名的字符串")

document.getElementsByClassName("bj");      // 取得类中包含“bj”的元素
document.getElementsByClassName("bj bd");   // 取得类中同时包含“bj”和“bd”的元素           

复制

(2)classList属性

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

<div class="bd user">ligang</div>           

复制

示例:className方式

var div = document.getElementsByClassName("bd")[0];
var classNames = div.className.split(/\s+/);
// 查找要删除的类名"user"
var index = classNames.indexOf("user");
// 删除类名"user"
classNames.splice(index, 1);
// 重新设置className
div.className = classNames;           

复制

示例:classList方式

var div = document.getElementsByClassName("bd")[0];
div.classList.remove("user");           

复制

classList属性是DOMTokenList的实例,其含有length属性。

属性 说明
add(value) 将给定的字符串添加到列表中。如果已存在,就不添加了
contains(value) 表示列表中是否存在给定的值
remove(value) 从列表中删除给定的字符串
toggle(value) 如果列表中已存在给定的值,删除它;如果不存在,添加它

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

2. 焦点管理

document.activeElement

属性始终会引用DOM中当前获得了焦点的元素。

示例:

var btn = document.getElementById("myBtn");
btn.focus();
console.log(btn.hasFocus());    // true
console.log(document.activeElement === btn); // true           

复制

默认情况下,文档刚刚加载完成时,

document.activeElement

中保存的是

document.body

元素的引用。文档加载期间,

document.activeElement

为null。

查询文档获知哪个元素获得了焦点,以及确定文档是否获得了焦点,这两个功能最重要的用途是提高Web应用的无障碍性。无障碍Web应用的一个重要标志就是恰当的焦点管理,而确切地知道哪个元素获得了焦点是一个极大的进步。

3. HTMLDocument的变化

(1)readyState属性

  • loading:正在加载文档;
  • complete:已经加载完文档。

    使用document.readyState的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器。

(2)兼容模式

document.compatMode

告知开发人员浏览器采用了哪种渲染模式。

  • CSS1Compat:标准模式;
  • BackCompat:混杂模式。

(3)head属性

HTML5新增了

document.head

属性,引用文档的

<head>

元素。

var head = document.head || document.getElementsByTagName("head")[0];           

复制

4. 字符集属性

  • document.charset:文档中实际使用的字符集,也可以用来指定新字符集;
  • document.defaultCharset:表示根据浏览器及操作系统的设置,当前文档默认的字符集应该是什么,注意兼容性。

5. 自定义数据属性

HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。

<div id="div" data-status="1" data-appId="123" data-customName="ligang">哈哈</div>           

复制

添加了自定义属性后,可以通过元素的dataset属性来访问自定义属性的值。

var div = document.getElementById("div");
var dataset = div.dataset;
console.log(dataset.appid);     // 123
console.log(dataset.customname);    // "ligang" 注意,都是小写           

复制

6. 插入标记

DOM操作文档插入HTML标记非常复杂,因为其不仅要创建一系列DOM节点,而且还要小心地按照正确顺序进行连接。详见:DOM

示例:创建a标签并插入到body

var a = document.createElement("a");
a.href = "http://blog.csdn.net/ligang2585116";
a.text = "http://blog.csdn.net/ligang2585116";
document.body.appendChild(a);           

复制

使用插入标记的技术,直接插入HTML字符串不仅简单而且速度更快。

(1)innerHTML属性

读模式:返回调用元素的所有节点(包括元素、注释和文本节点)对应的HTML标记;

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

示例:创建a标签并插入到body

document.body.innerHTML += '<a href="http://blog.csdn.net/ligang2585116">http://blog.csdn.net/ligang2585116</a>'           

复制

注意:上述使用“+=”,因为写模式下,innerHTML的值会被解析为DOM子树,替换调用元素原来的所有子节点。

说明:使用innerHTML插入的字符串开头是一个无作用域的元素(

<script>、<style>

)时,IE会在解析这个字符串前先删除该元素。

div.innerHTML = "<script>console.logt('hi')</script>";  // 无效
div.innerHTML = "<div>&nbsp;</div><script>console.logt('hi')</script>"; // 有效
div.innerHTML = "<input type=\"hidden\"><script>console.logt('hi')</script>"; // 有效           

复制

(2)outerHTML属性

读模式:返回调用它的元素及所有节点(包括元素、注释和文本节点)对应的HTML标记;

写模式:根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素。

示例:

<div id="blog">
    <!-- blog address -->
    <a href="http://blog.csdn.net/ligang2585116">blog</a>
</div>           

复制

var div = document.getElementById("blog");
div.outerHTML;  // 整个innerHTML内容+div本身
div.outerHTML = "<a href=\"http://blog.csdn.net/ligang2585116\">blog</a>";  // 整个div会被a标签替换掉

// 等价于
var a = document.createElement("a");
a.href = "http://blog.csdn.net/ligang2585116";
a.text = "blog";
div.parentNode.replaceChild(a, div);           

复制

(3)内存与性能问题

使用上述节点方法可能会导致浏览器的内存占用问题。在删除带有事件处理程序或引用了其他JavaScript对象子树时,就会导致内存占用问题。在使用innerHTML、outerHTML等方法时,最好先删除要替换的元素的所有事件处理程序和JavaScript对象属性。

7. scrollIntoView()方法

如何滚动页面也是DOM规范没有解决的一个问题,HTML5最终选择了

scrollIntoView

作为标准。

scrollIntoView()

可以在任何元素上调用。默认或传入true,窗口会滚动让调用元素与视口顶部尽可能齐平;传入false,调用元素尽可能会全部出现在窗口中。

document.forms[0].scrollIntoView();           

复制

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

四、专有扩展

1. children属性

children属性与childNodes没有什么区别。

2. contains()方法

判断某个节点是不是另一个节点的后代。

document.body.contains(div); // true           

复制

掩码 节点关系
1 无关(给定的节点不再当前文档中)
2 居前(给定的节点在DOM树中位于参考节点之前)
4 居后(给定的节点在DOM树中位于参考节点之后)
8 包含(给定的节点是参考节点的祖先)
16 被包含(给定的节点是参考节点的后代)

示例:通用的contains函数

function contains(refNode, otherNode){
    if (typeof refNode.contains == "function" && 
            (!client.engine.webkit || client.engine.webkit >= 522)){
        return refNode.contains(otherNode);
    } else if (typeof refNode.compareDocumentPosition == "function"){
        return !!(refNode.compareDocumentPosition(otherNode) & 16);
    } else {
        var node = otherNode.parentNode;
        do {
            if (node === refNode){
                return true;
            } else {
                node = node.parentNode;
            }
        } while (node !== null);
        return false;
    }
}
console.log(contains(document.documentElement, document.body)); // true           

复制

3. 插入文本

innerText

outerText

并没有被纳入HTML5。

其对文本进行操作,使用方式类似于

innerHTM

outerHTML

4. 滚动

HTML5将scrollIntoView()方法纳入规范之后,仍还有其他几个专有方法可以在不同的浏览器中使用。

(1)

scrollIntoViewIfNeeded(alignCenter)

:只在当前元素视口中不可见的情况下,才滚动浏览器窗口或容器元素,最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。true,尽量将元素在显示视口中部(垂直方向)。

DOM扩展

(2)

scrollByLines(lineCount)

:将元素的内容滚动指定的行高。

(3)

scrollByPages(pageCount)

:将元素的内容滚动指定的页面高度,具体高度由元素的高度决定。

注意:

scrollIntoView()

scrollIntoViewIfNeeded()

的作用对象是元素的容器;

scrollByLines()

scrollByPages()

影响的则是元素自身。