HTML5

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

一、CSS类扩展

1、getElementByClassName()

  • 该方法暴露在document对象和所有HTML元素上。

    • 方法接收一个参数:包含一个或多个类名的字符串;
    • 返回类名中包含相应类的元素的NodeList;
    • 如果提供了多个类名,则顺序无关紧要。
      1
      2
      3
      4
      5
      // 取得所有类名中包含"username"和"current"元素
      // 这两个类名的顺序无关紧要
      let allCurrentUsernames = document.getElementsByClassName("username current");
      // 取得 ID 为"myDiv"的元素子树中所有包含"selected"类的元素
      let selected = document.getElementById("myDiv").getElementsByClassName("selected");
  • 在 document上调用getElementsByClassName()返回文档中所有匹配的元素,而在特定元素上调用getElementsByClassName()则返回该元素后代中匹配的元素。

2、classList属性

  • classList属性是为了更加方便的对于类名进行增删改查操作。
    • classList是一个新的集合类型DOMTokenList的实例。
    • 除开length属性,item()和中括号取值等操作外,还增加了以下方法:
      • add(value):向类名列表中添加指定的字符串值value。如果这个值已经存在,则什么也不做。
      • contains(value):返回布尔值,表示给定的value是否存在。
      • remove(value):从类名列表中删除指定的字符串值value。
      • toggle(value):如果类名列表中已经存在指定的value,则删除;如果不存在,则添加。
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        // 删除"disabled"类
        div.classList.remove("disabled");
        // 添加"current"类
        div.classList.add("current");
        // 切换"user"类
        div.classList.toggle("user");
        // 检测类名
        if (div.classList.contains("bd") && !div.classList.contains("disabled")){
        // 执行操作
        )
        // 迭代类名
        for (let class of div.classList){
        doStuff(class);
        }
  • 添加了classList属性之后,除非是完全删除或完全重写元素的class属性,否则className属性就用不到了。

二、焦点管理

  • HTML5增加了辅助DOM焦点管理的功能。首先是document.activeElement,始终包含当前拥有焦点的 DOM元素。
  • 页面加载时,可以通过用户输人(按Tab键或代码中使用focus()方法)让某个元素自动获得焦点。
  • 默认情况下,document.activeElement在页面刚加载完之后会设置为document.body。而在页面完全加载之前,document.activeElement的值为null。
1
2
3
let button = document.getElementById("myButton"); 
button.focus();
console.log(document.activeElement === button); // true
  • document.hasFocus()方法,该方法返回布尔值,表示文档是否拥有焦点。
  • 确定文档是否获得了焦点,就可以帮助确定用户是否在操作页面。
1
2
3
let button = document.getElementById("myButton"); 
button.focus();
console.log(document.hasFocus()); // true
  • 这对于保证 Web应用程序的无障碍使用是非常重要的。无障碍Web应用程序的一个重要方面就是焦点管理,而能够确定哪个元素当前拥有焦点(相比于之前的猜测)是一个很大的进步。

三、HTMLDocument扩展

1、readyState属性

  • document.readyState属性有两个可能的值:
    • loading:表示文档正在加载;
    • complete:表示文档加载完成。
  • 实际开发中,最好是把 document.readstate当成一个指示器,以判断文档是否加载完毕。
  • 通常要依赖onload事件处理程序设置一个标记,表示文档加载完了。
1
2
3
if (document.readyState == "complete"){ 
// 执行操作
}

2、compatMode属性

  • 这个属性唯一的任务是指示浏览器当前处于什么渲染模式。
  • 标准模式下document.compatMode的值是“CSSlCompat”。
  • 混杂模式下document.compatMode的值是“BlackCompat”。
1
2
3
4
5
if (document.compatMode == "CSS1Compat"){ 
console.log("Standards mode");
} else {
console.log("Quirks mode");
}

3、head属性

  • document.head属性指向文档的<head>元素。
1
let head = document.head;

四、字符集属性

  • characterSet属性表示文档实际使用的字符集,也可以用来指定新字符集。
  • 属性的默认值是“UTF-16”,可以通过<meta>元素或响应头以及新增的characterSeet属性来修改。
1
2
console.log(document.characterSet); // "UTF-16" 
document.characterSet = "UTF-8";

五、自定义数据属性

  • HTML5允许给元素指定非标准的属性,但要使用前缀data-以便告诉浏览器,这些属性既不包含与渲染有关的信息,也不包含元素的语义信息。
  • 除了前缀,自定义属性对命名是没有限制的,data-后面跟什么都可以。
1
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
  • 定义了自定义数据属性后,可以通过元素的dataset属性来访问。dataset属性是一个DOMStringMap的实例,包含一组键/值对映射。
  • 元素的每个data-name 属性在dataset 中都可以通过data-后面的字符串作为键来访问(例如,属性data-myname,data-myName可以通过myname访问,但要注意data-my-name、data-My-Name要通过myName 来访问)。
1
2
3
4
5
6
7
8
9
10
11
12
// 本例中使用的方法仅用于示范
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){
console.log(`Hello, ${div.dataset.myname}`);
}

六、插入标记

  • 在所有现代浏览器中,通过innerHTML插入的<script>标签是不会执行的。

1、innerHTML属性

  • 在读取innerHTML属性时,会返回元素所有后代的HTML字符串,包括元素、注释和文本节点

  • 在写入innerHTML时,则会根据提供的字符串值以新的DOM子树替代元素中原来包含的所有节点

  • 比如下面的 HTML 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    <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>
  • 对于这里的<div>元素而言,其 innerHTML 属性会返回以下字符串:

    1
    2
    3
    4
    5
    6
    <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>
  • 实际返回的文本内容会因浏览器而不同:

    • IE 和 Opera 会把所有元素标签转换为大写;
    • Safari、Chrome和 Firefox 会按照文档源代码的格式返回,包含空格和缩进。
  • 在写人模式下,赋给innerHTML属性的值会被解析为DOM子树,并替代元素之前的所有节点。如果赋值中不包含任何HTML标签,则直接生成一个文本节点。

    • 因为所赋的值默认为HTML,所以其中的所有标签都会以浏览器处理HTML的方式转换为元素(同样,转换结果也会因浏览器不同而不同)。
      1
      div.innerHTML = "Hello world!"; 
  • 因为浏览器会解析设置的值,所以给innerHTML, 设置包含HTML的字符串时,结果会大不一样。

    1
    div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
    1
    <div id="content">Hello &amp; welcome, <b>&quot;reader&quot;!</b></div>
  • 设置innerHTML会导致浏览器将HTML字符串解析为相应的DOM树。这意味着设置innerHTML属性后马上再读出来会得到不同的字符串。这是因为返回的字符串是将原始字符串对应的 DOM子树序列化之后的结果。

2、outerHTML属性

  • 读取 outerHTML属性时,会返回调用它的元素(及所有后代元素)的 HTML字符串。
  • 在写入outerHTML属性时,调用它的元素会被传入的 HTML字符串经解释之后生成的 DOM子树取代。
  • 如果使用outerHTML设置HTML,比如:
    1
    div.outerHTML = "<p>This is a paragraph.</p>";
    会得到与执行以下脚本相同的结果:
    1
    2
    3
    let p = document.createElement("p"); 
    p.appendChild(document.createTextNode("This is a paragraph."));
    div.parentNode.replaceChild(p, div);
    新的<p>元素会取代 DOM 树中原来的<div>元素。

3、insertAdjacentHTML()与insertAdjacentText()

  • 这两个方法都接收两个参数:
    • 要插入标记的位置;
    • 要插入的HTML或文本。
  • 第一个参数必须是下列值中的一个:
    • beforebegin:插入当前元素前面,作为前一个同胞节点;
    • afterbegin:插入当前元素内部,作为新的子节点或放在第一个子节点前面;
    • beforeend:插入当前元素内部,作为新的子节点或放在最后一个子节点后面;
    • afterend:插入当前元素后面,作为下一个同胞节点。
  • 这几个值是不区分大小写的。第二个参数会作为HTML字符串解析(与innerHTML 和outerHTML相同)或者作为纯文本解析(与innerText和outerText相同)。 如果是HTML,则会在解析出错时抛出错误。
1
2
3
4
5
6
7
8
9
10
11
12
// 作为前一个同胞节点插入
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!");

4、内存与性能问题

  • 使用以上方法替换子节点可能在浏览器(特别是IE)中导致内存问题。

七、scrollIntoView()

  • DOM规范中没有涉及的一个问题是如何滚动页面中的某个区域。为填充这方面的缺失,不同浏览器实现了不同的控制滚动的方式。在所有这些专有方法中,HTML5选择了标准化scrollIntoView()。
  • scrollIntoView()方法存在于所有HTML元素上,可以滚动浏览器窗口或容器元素以便包含元素进入视口。参数如下:
    • alignToTop是一个布尔值:
      • true:窗口滚动后元素的顶部与视口顶部对齐。
      • false:窗口滚动后元素的底部与视口底部对齐。
    • scrollIntoViewOptions是一个选项对象:
      • behavior:定义过渡动画,可取的值为” smooth”和”auto”,默认为”auto”。
      • block:定义垂直方向的对齐,可取的值为“start”、“center”、“end”和“nearest”,默认为“start”。
      • inline:定义水平方向的对齐,可取的值为“start”、“center”、“end”和“nearest”,默认为“nearest”。
    • 不传参数等同于alignToTop为true。
1
2
3
4
5
6
7
// 确保元素可见
document.forms[0].scrollIntoView();
// 同上
document.forms[0].scrollIntoView(true);
document.forms[0].scrollIntoView({block: 'start'});
// 尝试将元素平滑地滚入视口
document.forms[0].scrollIntoView({behavior: 'smooth', block: 'start'});