JS高级程序设计——第11章 DOM扩展 11.3 HTML5
11.3 HTML5
因为 HTML5 涉及的面非常广,本节只讨论与 DOM 节点相关的内容。
HTML5 新增了很多 API,致力于简化 CSS 类的用法。
一、与类相关的扩充
1、getElementsByClassName()方法
- 可以通过 document 对象及所有 HTML 元素调用该方法。
getElementsByClassName()方法
接收一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的 NodeList。- 传入多个类名时,类名的先后顺序不重要。来看下面的例子。
- 在 document 对象上调用 getElementsByClassName()始终会返回与类名匹配的所有元素
//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓 var allCurrentUsernames = document.getElementsByClassName("username current");
- 在元素上调用该方法就只会返回后代元素中匹配的元素。
//取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素 var selected = document.getElementById("myDiv").getElementsByClassName("selected");
注意:因为返回的对象是 NodeList,所以使用这个方法与使用 getElementsByTagName() 以及其他返回 NodeList 的 DOM 方法都具有同样的性能问题。【NodeList实时更新的问题】
2、classList 属性
操作类名时的两个方法:DOM1使用
className属性,HTML5使用
classList属性比如,以下面的 HTML 代码为例。
<div class="bd user disabled">...</div>
- 使用className属性
- 在操作类名时,需要通过 className 属性添加、删除和替换类名。因为 className 中是一个字符串,所以即使只修改字符串一部分,也必须每次都设置整个字符串的值。(疑问:直接改成新的值覆盖不可以吗?)
- 这个
<div>
元素一共有三个类名。要从中删除一个类名,需要把这三个类名拆开,删除不想要的那个,然后再把其他类名拼成一个新字符串。请看下面的例子。
- 使用classList属性
-
HTML5 新增了一种操作类名的方式,可以让操作更简单也更安全,那就是为所有元素添加 classList 属性。
-
这个
classList 属性
是新集合类型 DOMTokenList 的实例。与其他 DOM 集合类似,DOMTokenList 有一个表示自己包含多少元素的length 属性
,而要取得每个元素可以使用item()方法
,也可以使用方括号语法
。此外,这个新类型还定义如下方法。 add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。
remove(value):从列表中删除给定的字符串。
toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。 -
这样,前面那么多行代码用下面这一行代码就可以代替了:
div.classList.remove("user");
- 以上代码能够确保其他类名不受此次修改的影响。其他方法也能极大地减少类似基本操作的复杂 性,如下面的例子所示。
二、焦点管理
HTML5 也添加了辅助管理 DOM 焦点的功能。
1、 document.activeElement 属性
- 这个属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是 通过按 Tab 键)和在代码中**调用 focus()**方法。来看几个例子。
var button = document.getElementById("myButton"); button.focus(); alert(document.activeElement === button);
- 默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元 素的引用。文档加载期间,document.activeElement 的值为 null。
2、document.hasFocus()方法
另外就是新增了
document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。
var button = document.getElementById("myButton"); button.focus(); alert(document.hasFocus()); //true
通过检测文档是否获得了焦点,可以知道用户是不是正在与页面交互。
3、HTMLDocument的变化
1、readyState属性
- Document 的
readyState 属性
有两个可能的值:
loading,正在加载文档;
complete,已经加载完文档。 - readyState属性的作用:
使用 document.readyState
的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器
。【在这个属性得到广泛支持之前,要实现这样一个指示器,必须借助 onload 事件处理程序设置一 个标签】 - document.readyState 属性的基本用法如下。
if (document.readyState == "complete"){ //执行操作 }
2、兼容模式
compatMode
的属性:这个属性就是为了告诉开发人员浏览器采用了哪种渲染模式。- 在标准模式下,
document.compatMode
的 值等于"CSS1Compat
",而在混杂模式下,document.compatMode
的值等于"BackCompat
"。
if (document.compatMode == "CSS1Compat"){ alert("Standards mode"); } else { alert("Quirks mode"); }
3、head属性
HTML5 新增了 document.head 属性, 引用文档的元素。要引用文档的元素,可以结合使用这个属性和另一种后备方法。
var head = document.head || document.getElementsByTagName("head")[0];
4、字符集属性
charset
属性:表示文档中实际使用的字符集, 也可以用来指定新字符集。默认情况下,这个属性的值为"UTF-16",但可以通过<meta>
元素、响应头部或直接设置charset 属性
修改这个值。
alert(document.charset); //"UTF-16" document.charset = "UTF-8";
-
defaultCharset
属性:表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。如果文档没有使用默认的字符集,那 charset 和 defaultCharset 属性的值可能会不一 样,例如:
if (document.charset != document.defaultCharset){ alert("Custom character set being used."); }
通过这两个属性可以得到文档使用的字符编码的具体信息,也能对字符编码进行准确地控制。运行适当的情况下,可以保证用户正常查看页面或使用应用。
5、自定义数据属性
- HTML5 规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以data-开头即可。来看一个例子。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
dataset 属性
:添加了自定义属性之后,可以通过元素的dataset
属性来访问自定义属性的值。
- dataset 属性的值是 DOMStringMap 的一个实例,也就是一个名值对儿的映射。在这个映射中,每个 data-name 形式的属性都会有一个对应的属性**,只不过属性名没有 data-前缀(**比如,自定义属性是 data-myname, 那映射中对应的属性就是 myname)。还是看一个例子吧。
//本例中使用的方法仅用于演示 var div = document.getElementById("myDiv"); //取得自定义属性的值 var appId = div.dataset.appId; var myName = div.dataset.myname; //设置值 div.dataset.appId = 23456; div.dataset.myname = "Michael"; //有没有"myname"值呢? if (div.dataset.myname){ alert("Hello, " + div.dataset.myname); }
如果需要给元素添加一些不可见的数据以便进行其他处理,那就要用到自定义数据属性。在跟踪链接或混搭应用中,通过自定义数据属性能方便地知道点击来自页面中的哪个部分。
6、插入标记
innerHTML属性
(不包括本身)
-
在读模式下,innerHTML 属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的 HTML 标记。
在写模式下,innerHTML 会根据指定的值创建新的 DOM 树,然后用这个 DOM 树
完全替换
调用元素原先的所有子节点。下面是一个例子。
<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 属性会返回
如下字符串。
<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>
但是,不同浏览器返回的文本格式会有所不同。
-
在写模式下,innerHTML 的值会被解析为 DOM 子树,替换调用元素原来的所有子节点。因为它的值被认为是 HTML,所以其中的所有标签都会按照浏览器处理 HTML 的标准方式转换为元素(同样, 这里的转换结果也因浏览器而异)。
如果设置的值仅是文本而没有 HTML 标签,那么结果就是设置纯文本,如下所示。
div.innerHTML = "Hello world!";
- 为 innerHTML 设置的包含 HTML 的字符串值与解析后 innerHTML 的值大不相同。来看下面的例子。
div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
以上操作得到的结果如下:
<div id="content">Hello & welcome, <b>"reader"!</b></div>
-
使用 innerHTML 属性也有一些限制:比如,在大多数浏览器中,通过 innerHTML 插入
<script>
元素并不会执行其中的脚本。IE8 及更早版本是唯一能在这种情况下执行脚本的浏览器,但必须满足一些条件。一是必须为
<script>
元素指定 defer 属性,二是<script>
元素必须位于(微软所谓的)“有作用域的元素”(scoped element)之后。
<script>
元素被认为是“无作用域的元素”(NoScope element), 也就是在页面中看不到的元素,与<style>
元素或注释类似。如果通过 innerHTML 插入的字符串开头 就是一个“无作用域的元素”,那么 IE 会在解析这个字符串前先删除该元素。换句话说,以下代码达不到目的:
div.innerHTML = "<script defer>alert('hi');<\/script>"; //无效
此时,innerHTML 字符串一开始(而且整个)就是一个“无作用域的元素”,所以这个字符串会变成空字符串。如果想插入这段脚本,必须在前面添加一个“有作用域的元素”,可以是一个文本节点, 也可以是一个没有结束标签的元素如
<input>(首选的方法)。例如,下面这几行代码都可以正常执行:
//事后,为了不影响页面显示,你可能需要移 除这个文本节点。 div.innerHTML = "_<script defer>alert('hi');<\/script>"; //第二行代码采用的方法类似,只不过使用的是一个包含非换行空格的<div>元素。 //如果仅仅插入一个空的<div>元素,还是不行;必须要包含一点儿内容,浏览器才会创建文本节点。 //同样,为了不影响页面布局,恐怕还得移除这个节点 div.innerHTML = "<div> </div><script defer>alert('hi');<\/script>"; //由于隐藏的<input>域不影响页面布局,因此这种方式在大多数情况下都是首选。 div.innerHTML = "<input type=\"hidden\"><script defer>alert('hi');<\/script>";
- 大多数浏览器都支持以直观的方式通过 innerHTML 插入
<style>
元素,例如:
div.innerHTML = "<style type=\"text/css\">body {background-color: red; }</style>";
但在 IE8 及更早版本中,
<style>也是一个“没有作用域的元素”,因此必须像下面这样给它前置 一个“有作用域的元素”:
div.innerHTML = "_<style type=\"text/css\">body {background-color: red; }</style>"; div.removeChild(div.firstChild);
- 并不是所有元素都支持 innerHTML 属性。不支持 innerHTML 的元素有:
<col>
、<colgroup>
、<frameset>
、<head>
、<html>
、<style>
、<table>
、<tbody>
、<thead>
、<tfoot>
和<tr>
。此 外,在 IE8 及更早版本中,<title>
元素也没有 innerHTML 属性。 - IE8 为此提供了
window.toStaticHTML()
方法,只有ie8支持。这个方法接收一个参数,即一个 HTML 字符串;返回 一个经过无害处理后的版本——从源 HTML 中删除所有脚本节点和事件处理程序属性。下面就是一个 例子:
var text = "<a href=\"#\" οnclick=\"alert('hi')\">Click Me</a>"; var sanitized = window.toStaticHTML(text); //Internet Explorer 8 only alert(sanitized); //"<a href=\"#\">Click Me</a>"
还是建议在通过 innerHTML 插入代码之前,尽可能先手工检查一下其中的文本内容。
outerHTML
属性(包括本身)
-
在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML 标签。
在写模式下,outerHTML 会根据指定的 HTML 字符串创建新的 DOM 子树,然后用这个 DOM 子树完全替换调用元素。下面是一 个例子。
<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>元素上调用 outerHTML,会返回与上面相同的代码,包括
<div>本身。不过,由于浏览器解析和解释 HTML 标记的不同,结果也可能会有所不同。(这里的不同与使用 innerHTML 属性时 存在的差异性质是一样的。)
- 使用 outerHTML 属性以下面这种方式设置值:
div.outerHTML = "<p>This is a paragraph.</p>"
这行代码完成的操作与下面这些 DOM 脚本代码一样:
var p = document.createElement("p"); p.appendChild(document.createTextNode("This is a paragraph.")); div.parentNode.replaceChild(p, div);
结果,就是新创建的
<p>元素会取代 DOM 树中的
<div>元素。
insertAdjacentHTML()方法
插入标记的最后一个新增方式是 insertAdjacentHTML()方法。这个方法最早也是在 IE 中出现的, 它接收两个参数:插入位置和要插入的 HTML 文本。
- 第一个参数必须是下列值之一:
“beforebegin”,在当前元素之前插入一个紧邻的同辈元素;
“afterbegin”,在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
“beforeend”,在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
“afterend”,在当前元素之后插入一个紧邻的同辈元素。
注意,这些值都必须是小写形式。
- 第二个参数是一个 HTML 字符串(与 innerHTML 和 outerHTML 的值相同),如果浏览器无法解析该字符串,就会抛出错误。以下是这个方法的基本用法示例。
//作为前一个同辈元素插入 element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>"); //作为第一个子元素插入 element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>"); //作为最后一个子元素插入 element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>"); //作为后一个同辈元素插入 element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");
- 内存与性能问题
- 例子:假设某个元素有一个事件处理程序(或者引用了一个 JavaScript 对象作为属性),在使用前述某个属性将该元素从文档树中删除后,元素与事件处理程序(或 JavaScript 对象)之间的绑定关系在内存中并没有一并删除。如果这种情况频繁出现,页面占用的内存数量就会明显增加。因此,在使用 innerHTML、 outerHTML 属性和 insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的所有事件处理程序和 JavaScript 对象属性。
- 在设置 innerHTML 或 outerHTML 时,就会创建一个 HTML 解析器。这个解析器是在浏览器级别的代码(通常是 C++编写的)基础上运行的,因此比执行 JavaScript 快得多。不可避免地,创建和销毁 HTML 解析器也会带来性能损失,所以最好能够将设置 innerHTML 或 outerHTML 的次数控制在合理的范围内。
- 例如,下列代码使用 innerHTML 创建了很多列表项:
for (var i=0, len=values.length; i < len; i++){ ul.innerHTML += "<li>" + values[i] + "</li>"; //要避免这种频繁操作!! }
这种每次循环都设置一次 innerHTML 的做法效率很低。而且,每次循环还要从 innerHTML 中读 取一次信息,就意味着每次循环要访问两次 innerHTML。最好的做法是单独构建字符串,然后再一次性地将结果字符串赋值给 innerHTML,像下面这样:
var itemsHtml = ""; for (var i=0, len=values.length; i < len; i++){ itemsHtml += "<li>" + values[i] + "</li>"; } ul.innerHTML = itemsHtml;
这个例子的效率要高得多,因为它只对 innerHTML 执行了一次赋值操作。
7、scrollIntoView()方法
- scrollIntoView()可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。
- 如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动 之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐,例如:
//让元素可见 document.forms[0].scrollIntoView();
- 当页面发生变化时,一般会用这个方法来吸引用户的注意力。实际上,为某个元素设置焦点也会导致浏览器滚动并显示出获得焦点的元素。
- 点赞
- 收藏
- 分享
- 文章举报
- JS高级程序设计——第11章 DOM扩展 11.4 专有扩展
- JS高级程序设计11-DOM扩展
- JavaScript高级程序设计之DOM 扩展之HTML5之HTMLDocument的变化第11.3.3讲
- JavaScript高级程序设计之DOM 扩展之HTML5之字符集属性第11.3.4讲
- 读书笔记 - js高级程序设计 - 第十一章 DOM扩展
- javascript高级程序设计第三版 第11章 DOM扩展
- JavaScript高级程序设计之DOM 扩展之HTML5之自定义数据属性第11.3.5讲
- 《JS高程(3)》DOM扩展-HTML5-第11章笔记(20)
- JavaScript高级程序设计之DOM 扩展之HTML5之与类相关的扩充第11.3.1讲
- JavaScript高级程序设计之DOM 扩展之HTML5之插入标记第11.3.6讲
- JavaScript高级程序设计之DOM 扩展之HTML5之焦点管理第11.3.2讲
- JavaScript高级程序设计之DOM 扩展之HTML5之scrollIntoView()方法第11.3.7讲
- js学习小结(十六)--javascript 高级程序设计-DOM扩展
- JS高级程序设计——第12章 DOM2和DOM3 12.1 DOM变化
- js高级程序设计--DOM
- JS高级程序设计10-DOM
- JavaScript高级程序设计之DOM 扩展之专有扩展之滚动第11.4.5讲
- JavaScript高级程序设计9--DOM扩展
- JavaScript高级程序设计:11.12章-DOM扩展
- JavaScript高级程序设计之DOM 扩展之选择符API第11.1讲