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

二、元素遍历

对于元素间的空格,各浏览器对于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. 插入文本

innerTextouterText并没有被纳入HTML5。 其对文本进行操作,使用方式类似于innerHTMouterHTML

4. 滚动

HTML5将scrollIntoView()方法纳入规范之后,仍还有其他几个专有方法可以在不同的浏览器中使用。 (1)scrollIntoViewIfNeeded(alignCenter):只在当前元素视口中不可见的情况下,才滚动浏览器窗口或容器元素,最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。true,尽量将元素在显示视口中部(垂直方向)。

(2)scrollByLines(lineCount):将元素的内容滚动指定的行高。 (3)scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决定。 注意scrollIntoView()scrollIntoViewIfNeeded()的作用对象是元素的容器;scrollByLines()scrollByPages()影响的则是元素自身。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券