第15章 DOM扩展

DOM扩展的两个标准:Selectors API和HTML5

Selectors API

querySelector()

接受CSS选择符参数,返回匹配该模式的第一个后代元素。

// 获取body元素
let body = document.querySelector('body');
// 获取ID为“myDiv”的元素
let myDiv = document.querySelector('#myDiv');
// 获取类名为“selected”的第一个元素
let selected = document.querySelector(".selected");
// 获取类名为button的图片
let img = document.body.querySelector("img.button");

querySelectorAll()

返回所有匹配的项,是个NodeList

// 取得 ID 为"myDiv"的<div>元素中的所有<em>元素
let ems = document.getElementById("myDiv").querySelectorAll("em");
// 取得所有是<p>元素子元素的<strong>元素
let strongs = document.querySelectorAll("p strong");

matches()

matches()方法接收一个 CSS 选择符参数,如果元素 匹配则该选择符返回 true,否则返回 false。

if (document.body.matches("body.page1"))&#123; 
 // true 
&#125; 

元素遍历

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

  • childElementCount,返回子元素数量(不包含文本节点和注释);
  • firstElementChild,指向第一个Element类型的子元素(Element版firstChild);
  • lastElementChild,指向最后一个Element类型的子元素(Element版lastChild);
  • previousElementSibling,指向前一个Element类型的同胞元素(Element版previousSibling);
  • nextElementSibling,指向后一个Element类型的同胞元素(Element版nextSibling)。

HTML5

HTML5 规范却包含了与标记相关的大量 JavaScript API 定义。其中有的 API 与 DOM 重合, 定义了浏览器应该提供的 DOM 扩展。

自 HTML4 被广泛采用以来,Web 开发中一个主要的变化是 class 属性用得越来越多,其用处是为 元素添加样式以及语义信息。自然地,JavaScript 与 CSS 类的交互就增多了,包括动态修改类名,以及 根据给定的一个或一组类名查询元素,等等。为了适应开发者和他们对 class 属性的认可,HTML5 增 加了一些特性以方便使用 CSS 类。

CSS类扩展

getElementsByClassName()

返回一个包含所有类名为className的元素的NodeList.

// 取得所有类名中包含"username"和"current"元素
// 这两个类名的顺序无关紧要
let allCurrentUsernames = document.getElementsByClassName("username current"); 
// 取得 ID 为"myDiv"的元素子树中所有包含"selected"类的元素
let selected = document.getElementById("myDiv").getElementsByClassName("selected"); 

classList

要操作类名,可以通过className属性实现添加、删除和替换。但是className是一个字符串,所以每次操作之后都需要重新设置这个值才能生效,即使只改动了部分字符串也一样。

<!-- 要删除div元素的3个类名中的user 需要把className拆开,删除不想要的哪个,再把包含剩余类的字符串设置回去 -->
<div class="bd user disabled">...</div>
<script>
    // 要删除"user"类
    let targetClass = "user"; 
    // 把类名拆成数组
    let classNames = div.className.split(/\s+/); 
    // 找到要删除类名的索引
    let idx = classNames.indexOf(targetClass); 
    // 如果有则删除
    if (idx > -1) &#123; 
     classNames.splice(i,1); 
    &#125; 
    // 重新设置类名
    div.className = classNames.join(" ");
</script>
<!-- 有了classList就可以将上述代码转换成为 -->
<script>
    // 删除user类
    div.classList.remove("user");
    // 添加current类型
    div.classList.add("current");
    // 迭代类名
    for (let class of div.classList)&#123; 
     doStuff(class); 
    &#125; 
</script>

classList 是一个新的集合类型 DOMTokenList 的实例。与其他 DOM 集合类型一样,DOMTokenList 也有 length 属性表示自己包含多少项,也可以通过 item()或中括号取得个别的元素。此外, DOMTokenList 还增加了以下方法。

add(value),向类名列表中添加指定的字符串值 value。如果这个值已经存在,则什么也不做。

contains(value),返回布尔值,表示给定的 value 是否存在。

remove(value),从类名列表中删除指定的字符串值 value。

toggle(value),如果类名列表中已经存在指定的 value,则删除;如果不存在,则添加

焦点管理

  • document.activeElement:始终包含当前拥有焦点的DOM元素
let button = document.getElementById("myButton"); 
button.focus(); 
console.log(document.activeElement === button); // true

默认情况下,document.activeElement 在页面刚加载完之后会设置为document.body,而在页面完全加载之前,document.activeElement的值为null.

  • document.hasFocus()方法,该方法返回布尔值,表示文档是否拥有焦点
let button = document.getElementById("myButton");
button.focus();
console.log(document.hasFocus()); // true

HTMLDocument扩展

document.readyState:loading,表示文档正在加载;complete,表示文档加载完成。

if (document.readyState == "complete")&#123; 
 // 执行操作
&#125; 

document.compatMode:CSS1Compat标准模式下;BackCompat混杂模式下

if (document.compatMode == "CSS1Compat")&#123; 
 console.log("Standards mode"); 
&#125; else &#123; 
 console.log("Quirks mode"); 
&#125; 

浏览器渲染模式:标准模式和怪异模式 (所谓的标准模式是指,浏览器按W3C标准解析执行代码;怪异模式则是使用浏览器自己的方式解析执行代码,因为不同浏览器解析执行的方式不一样,所以我们称之为怪异模式。)

文档声明:<!DOCTYPE>声明必须是HTML文档的第一行,它是指示web浏览器关于页面使用哪个HTML版本进行编写的指令。DTD:文档类型定义(Document Type Definition)是一套关于标记符的语法规则。DTD 在 <!DOCTYPE> 声明中引用。

没有文档声明的话大多数浏览器都将会转换到为怪异模式(quirk mode),有些地方会称为混杂模式或兼容模式 如果在文档开头有文档声明,浏览器就会遵循声明中DTD文档标准进入标准模式CSS1Compat

标准模式与怪异模式之间的区别:浏览器使用怪异模式解析渲染页面(目的是向后兼容),会按照自己的方式来解析渲染页面,在不同的浏览器就会显示不同的样式,而且,由于不遵循文档标准,所以对不严格的语法会产生兼容。标准模式/严格模式的话,浏览器使用W3C的标准解析渲染页面。

document.head:指向文档的元素。可以像下面这样直接取得元素

let head = document.head; 

document.characterSet :表示文档实际使用的字符集,也可以用来指定新的字符集,默认值是“UTF-16”,可以通过<meta>或者响应头来修改

document.characterSet = "UTF-8";

自定义数据属性

HTML5 允许给元素指定非标准的属性,但要使用前缀 data-以便告诉浏览器,这些属性既不包含 与渲染有关的信息,也不包含元素的语义信息。除了前缀,自定义属性对命名是没有限制的,data-后 面跟什么都可以。

<!-- 在html中行内元素中添加 -->
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
<!-- 有了自定义属性之后,可以通过元素的dataset属性来访问 -->
<script>
    let div = document.getElementById("myDiv");
    // 取得自定义数据属性的值
    let appId = div.dataset.appId; 
    let myName = div.dataset.myname; 
    // 设置自定义数据属性的值
    div.dataset.appId = 23456; 
    div.dataset.myname = "Michael"; 
    // 有"myname"吗?
    if (div.dataset.myname)&#123; 
     console.log(`Hello, $&#123;div.dataset.myname&#125;`); 
    &#125;
</script>

插入标记

innerHTML

<div id="content"> 
 <p>This is a <strong>paragraph</strong> with a list following it.</p> 
 <ul> 
 <li>Item 1</li> 
 <li>Item 2</li> 
 <li>Item 3</li> 
 </ul> 
</div> 

在读取 innerHTML 属性时,会返回元素所有后代的 HTML 字符串,包括元素、注释和文本节点。 而在写入 innerHTML 时,则会根据提供的字符串值以新的 DOM 子树替代元素中原来包含的所有节点。

div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>"; 

这里会把原来div里面的所有元素都替换成为现在的设置

outerHTML

读取 outerHTML 属性时,会返回调用它的元素(及所有后代元素)的 HTML 字符串。在写入 outerHTML 属性时,调用它的元素会被传入的 HTML 字符串经解释之后生成的 DOM 子树取代。

对于上述的那一段html代码

console.log("div.innerHTML:", div.innerHTML)
// 下面显示控制台中的结果
div.innerHTML: 
    <p>This is a <strong>paragraph</strong> with a list following it.</p> 
    <ul> 
    <li>Item 1</li> 
    <li>Item 2</li> 
    <li>Item 3</li> 
    </ul> 

console.log("div.outerHtml:", div.outerHTML)
// 下面显示控制台中的结果
div.outerHtml: <div id="content">
    <p>This is a <strong>paragraph</strong> with a list following it.</p> 
    <ul> 
    <li>Item 1</li> 
    <li>Item 2</li> 
    <li>Item 3</li> 
    </ul> 
  </div>
// 这里新的p元素会取代DOM树中原来的div元素
div.outerHTML = "<p>This is a paragraph.</p>";

insertAdjacentHTML和insertAdjacentText

这两个方法接受两个参数:要插入标记的位置和要插入的 HTML 或文本。第一个参数 必须是下列值中的一个

 “beforebegin”,插入当前元素前面,作为前一个同胞节点;

 “afterbegin”,插入当前元素内部,作为新的子节点或放在第一个子节点前面;

 “beforeend”,插入当前元素内部,作为新的子节点或放在最后一个子节点后面;

 “afterend”,插入当前元素后面,作为下一个同胞节点。

// 作为前一个同胞节点插入
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>"); 
element.insertAdjacentText("beforebegin", "Hello world!"); 
// 作为第一个子节点插入
element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>"); 
element.insertAdjacentText("afterbegin", "Hello world!"); 
// 作为最后一个子节点插入
element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>"); 
element.insertAdjacentText("beforeend", "Hello world!"); 
// 作为下一个同胞节点插入
element.insertAdjacentHTML("afterend", "<p>Hello world!</p>"); element. 
insertAdjacentText("afterend", "Hello world!"); 

专有扩展

children

children 属性是一个 HTMLCollection,只包含元素的 Element 类型的子节点。

contains

DOM 编程中经常需要确定一个元素是不是另一个元素的后代。

插入标记

  • innerText

    在用于读取值时, innerText 会按照深度优先的顺序将子树中所有文本节点的值拼接起来。在用于写入值时,innerText 会移除元素的所有后代并插入一个包含该值的文本节点

  • outerText

    outerText 与 innerText 是类似的,只不过作用范围包含调用它的节点。要读取文本值时, outerText 与 innerText 实际上会返回同样的内容。但在写入文本值时,outerText 就大不相同了。 写入文本值时,outerText 不止会移除所有后代节点,而是会替换整个元素。