第十一章 DOM扩展

选择符 API

Selectors API Level 1的核心是两个方法:querySelector()和 querySelectorAll()。在兼容的浏 览器中,可以通过 Document 及 Element 类型的实例调用它们。

querySelector()方法

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

//get the body element
var body = document.querySelector("body");
alert(body.tagName);
                   
//get the element with the ID "myDiv"
var myDiv = document.querySelector("#myDiv");
alert(myDiv);
                   
//get first element with a class of "selected"
var selected = document.querySelector(".selected");
alert(selected.innerHTML);
                   
//get first image with class of "button"
var img = document.body.querySelector("img.button");
alert(img);

通过 Document 类型调用 querySelector()方法时,会在文档元素的范围内查找匹配的元素。而 通过 Element 类型调用 querySelector()方法时,只会在该元素后代元素的范围内查找匹配的元素。

querySelectorAll()方法

querySelectorAll()方法接收的参数与 querySelector()方法一样,都是一个 CSS选择符,但 返回的是所有匹配的元素而不仅仅是一个元素。这个方法返回的是一个 NodeList 的实例。

但这个NodeList 实例的底层实现则类似于一组元素 的快照,而非不断对文档进行搜索的动态查询。和childNodes 属性中的NodeList 对象不同,出于性能的考虑,这里的NodeList 实例不会进行动态更新。

只要传给 querySelectorAll()方法的 CSS选择符有效,该方法都会返回一个 NodeList 对象, 而不管找到多少匹配的元素。如果没有找到匹配的元素,NodeList 就是空的。

要取得返回的 NodeList 中的每一个元素,可以使用 item()方法,也可以使用方括号语法

元素遍历

Element Traversal API为 DOM元素添加了以下 5个属性。

  • childElementCount:返回子元素(不包括文本节点和注释)的个数。
  • firstElementChild:指向第一个子元素;firstChild 的元素版。
  • lastElementChild:指向后一个子元素;lastChild 的元素版。
  • previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
  • nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。

以上属性主要是为了解决在使用 childNodes 和 firstChild 等属性时由于文本节点和注释节点所导致的行为不一致的问题。

利用这些元素不必担心空白文本节点,从而可以更方便地查找 DOM元素了。

使用childNodes 返回的是所有的节点,使用children返回的是所有的元素节点

HTML5

与类相关的扩充

getElementsByClassName()方法

getElementsByClassName()方法接收一个参数,即一个包含一或多个类名的字符串,返回带有 指定类的所有元素的 NodeList。传入多个类名时,类名的先后顺序不重要。

//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓 
var allCurrentUsernames = document.getElementsByClassName("username current"); 

因为返回的对象是 NodeList,所以使用这个方法与使用 getElementsByTagName() 以及其他返回 NodeList 的 DOM方法都具有同样的性能问题。

classList 属性

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

<div class="bd user disabled">...</div>对于这种元素用className属性修改类名就很麻烦

classList 属性是新集合类型 DOMTokenList 的实例,有一个表示自己包含多少元素的 length 属性,而要取得每个元素可以使用 item()方 法,也可以使用方括号语法。

同时这个属性还有以下这些方法

  • add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
  • contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。
  • remove(value):从列表中删除给定的字符串。
  • toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。

这样,如果要删除上面div中的user类,可以使用div.classList.remove("user");

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

焦点管理

  • document.activeElement 属性

这个 属性始终会引用 DOM中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是 通过按 Tab 键)和在代码中调用 focus()方法。

文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元 素的引用。文档加载期间,document.activeElement 的值为 null。

  • document.hasFocus()方法

这个方法用于确定文档是否获得了焦点。

通过检测文档是否获得了焦点,可以知道用户是不是正在与页面交互。

HTMLDocument的变化

readyState 属性

Document 的 readyState 属性有两个可能的值:

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

head 属性

HTML5新增了 document.head 属性, 引用文档的元素。要引用文档的元素,可以结合使用这个属性和另一种后备方法。

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

字符集属性

charset 属性表示文档中实际使用的字符集, 也可以用来指定新字符集。默认情况下,这个属性的值为"UTF-16",但可以通过元素、响应头 部或直接设置 charset 属性修改这个值。

alert(document.charset); //"UTF-16" 
document.charset = "UTF-8"; 

defaultCharset属性表示根据默认浏览器及操作系统的设置,当前文档默认的字符集 应该是什么。如果文档没有使用默认的字符集,那 charset 和 defaultCharset 属性的值可能会不一 样

if (document.charset != document.defaultCharset){
      alert("Custom character set being used.");
}

通过这两个属性可以得到文档使用的字符编码的具体信息,也能对字符编码进行准确地控制。运行 适当的情况下,可以保证用户正常查看页面或使用应用。

自定义数据属性

HTML5规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的 信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以 data-开头即可。

<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div> 

添加了自定义属性之后,可以通过元素的 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"; 

插入标记

innerHTML 属性

在读模式下,innerHTML 属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应 的 HTML标记。在写模式下,innerHTML 会根据指定的值创建新的 DOM树,然后用这个 DOM树完全 替换调用元素原先的所有子节点。

其实就是在元素中插入一段解析后的html字符串

script标签的字符串不会进行解析执行

div.innerHTML = "<script defer>alert('hi');<\/script>"; //无效 

并不是所有元素都支持 innerHTML 属性。不支持 innerHTML 的元素有:、、 、、、

  • 安全问题

只要使用 innerHTML 从外部插入 HTML,都应该首先以可靠的方式处理 HTML。 IE8为此提供了 window.toStaticHTML()方法,这个方法接收一个参数,即一个 HTML字符串;返回 一个经过无害处理后的版本——从源 HTML 中删除所有脚本节点和事件处理程序属性。只有 IE8原生支持这个方法

outerHTML 属性

在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML标签。在写模式下,outerHTML 会根据指定的 HTML字符串创建新的 DOM子树,然后用这个 DOM子树完全替换调用元素。

和innerHTML 的区别是一个包括节点本身,一个只操作子节点

div.outerHTML = "<p>This is a paragraph.</p>"; 
//等价于
var p = document.createElement("p"); 
p.appendChild(document.createTextNode("This is a paragraph.")); 
div.parentNode.replaceChild(p, div); 

insertAdjacentHTML()方法

它接收两个参数:插入位置和要插入的 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对象属性

在插入大量新 HTML标记时,使用 innerHTML 属性与通过多次 DOM操作先创建节点再指定它 们之间的关系相比,效率要高得多。这是因为在设置 innerHTML 或 outerHTML 时,就会创建一个HTML 解析器。这个解析器是在浏览器级别的代码(通常是 C++编写的)基础上运行的,因此比执行 JavaScript 快得多。不可避免地,创建和销毁 HTML解析器也会带来性能损失,所以好能够将设置 innerHTML 或 outerHTML 的次数控制在合理的范围内。

将要添加的多个dom组成一个html字符串,然后一次性使用innerHTML插入到页面中

scrollIntoView()方法

scrollIntoView()可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用 元素就可以出现在视口中。如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动 之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部 出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐

专有扩展

children属性

这个属性是 HTMLCollection 的实例,只包含元素中同样还是元素的子节点。除此之外, children 属性与 childNodes 没有什么区别,即在元素只包含元素子节点时,这两个属性的值相同。

contains()方法

这个方法用来检查某个节点是不是另一个节点的后代。调用 contains()方法的应该是祖先节点, 也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点, 该方法返回 true;否则,返回 false。

alert(document.documentElement.contains(document.body));    //true 

插入文本

innerText 属性

通过 innertText 属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过 innerText 读取值时,它会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。在通过 innerText 写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。

通过 innerText 读取值时,会将所有的子文本节点拼接在一起输出,空白和换行根据浏览器不同,会有不同的处理方式

设置innerText 属性的同时,也对文本中存在的 HTML 语法字符(小于号、大于号、引号及和号)进行了编码。

div.innerText = "Hello & welcome, <b>\"reader\"!</b>"; 
//运行结果
<div>Hello &amp; welcome, &lt;b&gt;&quot;reader&quot;!&lt;/b&gt;</div> 

利用这一点,可以通过 innerText 属性过滤掉 HTML标签。方法是 将 innerText 设置为等于 innerText,这样就可以去掉所有 HTML标签

outerText 属性

除了作用范围扩大到了包含调用它的节点之外,outerText 与 innerText 基本上没有多大区别。 在读取文本值时,outerText 与 innerText 的结果完全一样。但在写模式下,outerText 就完全不 同了:outerText 不只是替换调用它的元素的子节点,而是会替换整个元素(包括子节点)。

新的文本节点会完全取代调用 outerText 的元素。此后,该元素就从文档中被删除,无 法访问。

滚动

  • scrollIntoViewIfNeeded(alignCenter):只在当前元素在视口中不可见的情况下,才滚 动浏览器窗口或容器元素,终让它可见。如果当前元素在视口中可见,这个方法什么也不做。 如果将可选的 alignCenter 参数设置为 true,则表示尽量将元素显示在视口中部(垂直方向)。
  • scrollByLines(lineCount):将元素的内容滚动指定的行高,lineCount 值可以是正值, 也可以是负值。
  • scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决 定。

scrollIntoView()和 scrollIntoViewIfNeeded()的作用对象是元素的 容器,而 scrollByLines()和 scrollByPages()影响的则是元素自身。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值