《JavaScript高等程序设计》(第3版)读书笔记 第11章 DOM拓展

  • 只管DOM作为API已异常完美了,但为了完成更多功用,依然会有一些范例或专有的拓展。2008年之前,浏览器中险些一切的拓展都是专有的,今后W3C动手将一些已成为事实范例的专有拓展范例化,并写入范例中。
  • 对DOM的两个主要拓展是Selectors API (挑选API)和HTML5。这两个拓展都源自开辟社区,而将某些罕见做啊及API范例化,一直是众望所归。

挑选符API

  • jQuery的中心就是经由过程CSS挑选符查询DOM文档取得元素的援用,从而抛开了getElementById()gettElementByTagName()
  • Selectors API 是又W3C提议制订的一个范例,致力于让浏览器原生支撑CSS查询。一切完成这一功用的JavaScript库都邑写一个基本的CSS剖析器,然后再运用已有的DOM要领查询文档并找到婚配的节点。而把这个功用变成原生API以后,剖析和树查询操纵可以在浏览器内部经由过程编译后的代码完成,极大改良了机能。

querySelector()要领

  • querySelector()要领吸收一个CSS挑选符,返回与该情势婚配的第一个元素,假如没有找到婚配的元素,返回null
// 取得body元素
var body = document.querySelector("body");

// 取得ID为“myDiv”的元素
var myDiv = document.querySelector("#myDiv");

// 取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");

// 取得类为"button"的第一个图象元素
var selected = document.querySelector("img.button");
  • 假如传入了不支撑的挑选符,会抛出毛病。

querySelectorAll()要领

  • 和上面相似,但返回的不仅仅是婚配的第一个元素,而是一个NodeList的实例,假如没有婚配,NodeList就是空的。
// 取得某div中一切em元素
var ems = document.getElementById("myDiv").querySelectorAll("em");

// 取得类为“selected”的一切元素
var selected = document.querySelectorAll(".selected");

// 取得一切p元素中的一切strong元素
var strongs = document.querySelectorAll("p strong");
  • 取得返回NodeList中的每一个元素,可以运用item()要领,也可以运用方括号语法。

matchesSelector()要领

  • Selector API Level2 范例为Element范例新增了一个matchesSelector()要领。吸收一个参数,即CSS挑选符,假如挪用元素与该挑选符婚配返回true,不然返回false。
  • 停止2011年年中,还没有浏览器支撑该要领,不过有一些实验性的完成。因而假如你想运用这个要领,最好编写一个包装函数:
function matchesSelector(element, selector) {
  if (element.matchesSelecotr) {
    return element.matchesSelecotr(selector)
  } else if (element.msMatchesSelecotr) {
    return element.msMatchesSelecotr(selector)
  } else if (element.mozMatchesSelecotr) {
    return element.mozMatchesSelecotr(selector)
  } else if (element.webkitMatchesSelecotr) {
    return element.webkitMatchesSelecotr(selector)
  } else {
    throw new Error("Not supported.");
  }
}

元素遍历

  • 关于元素间的空格,IE9及之前版本不会返回文本节点,而其他一切浏览器都邑返回文本节点。为了填补这一差别,而又同时对峙DOM范例稳定,Element Traversal 范例新定义了一组属性。

    • childElementCount: 返回子元素(不包含文本节点和诠释)的个数
    • firstElementChild: 指向第一个子元素;firstChild的元素版
    • lastElementChild: 指向末了一个子元素;lastChild的元素版
    • previousElementSibling: 指向前一个平辈元素;previousSibling的元素版
    • nextElementSibling: 指向前一个平辈元素;nextSibling的元素版
// 跨浏览器遍历某元素的一切子元素

// 老版的兼容性代码
var i,
    len,
    child = element.firstChild;
while(child != element.lastChild) {
  // 搜检是不是是元素
  if (child.nodeType == 1) {
    processChild(child);
  }
  child = child.nextSibling;
}

// 运用新版的要领
var i,
    len,
    child = element.firstChild;
while(child != element.lastElementChild) {
  processChild(child);
  child = child.nextElementSibling;
}
  • 支撑Element Traversal 范例的浏览器有 IE9+ Firefox3.5+ Safari4+ Chrome Opera10+

HTML5

  • HTML5一切之前的版本对JavaScript接口的形貌不过一言半语,主要篇幅都用于定义标记,与JavaScript相干的内容一概交由DOM范例去定义。
  • HTML5范例则缭绕怎样运用新增标记定义了大批JavaScript API。个中一些API与DOM堆叠,定义了浏览器应当支撑的DOM拓展。
  • 由于HTML5触及的面异常广,本节只议论与DOM节点相干的内容。

与类(class)相干的扩大

  • HTML5 新增了许多API,致力于简化CSS类的用法。

getElementsByClassName()要领

// 取得一切类中包含"username"和"current"的元素
// 类名的先后递次无所谓
var allCurrentUsernames = docment.getElementsByClassName("username current");

// 取得ID为"myDiv"的元素中带有类名"selected"的一切元素
var selected = document.getElementById("myDiv").getElemenstByClassName("selected");
  • 支撑getElementsByClassName()要领的浏览器 IE9+ Firefox3+ Safari3.1+ Chrome Opera9.5+

classList属性

  • 在操纵类名时,须要经由过程className属性增加、删除和替代类名。由于className中是一个字符串,所以纵然只是修正字符串一部分,也必需每次都设置悉数字符串的值。
  • HTML5 新增了一种操纵类名的体式格局,可以让操纵更简朴也更平安,那就是为一切元素增加了classList属性。
  • classList属性是新鸠合范例 DOMTokenList的实例。与其他DOM鸠合相似,DOMTokenList 有一个示意本身包含若干元素的length属性,而要去的每一个元素可以运用item()要领,或许方括号语法。别的,这个新范例还定义以下要领:

    • add(value): 将给定的字符串值增加到列表中。假如值已存在,就不增加了。
    • contains(value): 示意列表中是不是存在给定的值,假如存在返回true,反之false。
    • remove(value): 从列表中删除给定的字符串。
    • toggle(value): 假如列表中已存在给定的值,删除它;假如没有,增加它。
  • 有了classList属性,除非你须要删除一切类名,或许完全重写元素的class属性,不然就用不到className属性了。
  • 支撑classList的浏览器 Firefox3.6+ Chrome

核心治理

  • HTML5增加了辅佐DOM核心的功用。
  • document.activeElement属性,一直会援用DOM中当前取得了核心的元素。元素取得核心的体式格局有页面加载、用户输入(通常是经由过程Tab键)和在代码中挪用focus()要领。
var button = document.getElementById("myButton");
button.focus();
console.log(document.activeElement === button);     // true
  • 默许状况下,文档方才加载完成时,document.activeElement中保留的是document.body元素的援用。文档加载时期,document.activeElement的值为null
  • document.hasFocus()要领,这个要领用于肯定文档是不是取得了核心。
var button = document.getElementById("myButton");
button.focus();
console.log(document.hasFocus());        // true
  • 支撑的浏览器 IE4+ Firefox3+ Safari4+ Chrome Opera8+

HTMLDocument的变化

readyState属性

  • Document.readyState属性有两个可以的值:

    • loading正在加载文档
    • complete已加载完文档
  • 支撑的浏览器 IE4+ Firefox3.6+ Safari Chrome Opera9+
if (document.readyState == "complete") {
  ...
}

兼容情势

  • 自从IE6最先辨别衬着页面的情势是范例的照样混淆的,检测页面的兼容性就成为浏览器的必要功用。IE为此给document增加了一个名为compatMode的属性,通知开辟人员浏览器采用了哪一种衬着情势。
  • document.compatMode范例情势下即是”CSS1Compat”,混淆情势下即是”BackCompat”。
  • 终究HTML5将这个属性归入范例
  • 支撑的浏览器 IE Firefox Safari3.1 Chrome Opera
if (document.compatMode == "CSS1Compat") {
  console.log("Standards mode");
} else {
  console.log("Quirks mode");
}

head属性

  • HTML5新增了document.head属性,与docuemnt.body对应
  • 支撑的浏览器 Chrome Safari 5+
var head = document.head || document.getElementsByTagName("head")[0];

字符集属性

  • HTML5新增了几个与文档字符集有关的属性
  • charset属性示意文档中实际运用的字符集,也可以用来指定新字符集。默许值是”UTF-16″,可以经由过程<meta>元素、相应头部或直接设置charset属性修正这个值。
  • 支撑的浏览器 IE Safari Opera Chrome 。 Firefox 支撑 document.Characterset
console.log(document.charset);     // "UTF-16"
document.charset = "UTF-8";
  • defaultCharset示意依据默许浏览器及操纵系统的设置,当前文档默许的字符集应当是什么。假如文档没有运用默许的字符集,那charsetdefaultCharset属性值可以会不一样。
if (document.charset != document.defaultCharset) {
  console.log("Custom character set being used.");
}
  • 支撑的浏览器 IE Safari Chrome 。

自定义数据属性

  • HTML5 划定可以为元素增加非范例的属性,但要增加前缀data- ,目标是为元素供应与衬着无关的信息,或许供应语义信息。这些属机可以恣意增加、随意显著,只要以data-开首即可。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
  • 增加了自定义属性以后,可以经由过程元素的dataset属性来访问自定义属性的值。dataset属性的值时DOMStringMap的实例,也就是一个名值对的映照。在这个映照中,每一个data-name情势的属性都邑有一个对应的属性,只不过属性没有data-前缀。
var div = document.getElementById("myDiv");

// 取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;

// 设置值
div.dataset.appId = 2345;
div.dataset.myname = "Michael";

// 有无"myname"值呢?
if (div.dataset.myname) {
  console.log("Hello, " + div.dataset.myname);
}

插进去标记

  • 虽然DOM为操纵节点供应了细致入微的掌握手腕,但在须要给文档插进去大批HTML标记的状况下,经由过程DOM操纵依然异常贫苦,由于不仅要建立一系列DOM节点,还要警惕根据准确的递次把它们连接起来。
  • 相对而言,直接插进去HTML字符串不仅更简朴,速率也更快。以下插进去标记的DOM拓展已归入了HTML5范例。

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>
  • 差别浏览器返回的文本花样不会有所差别。IE和Opera会将一切标签转换为大小写情势,而Safari、Chrome、Firefox则会原原本本的根据本来文档中(或指定这些标签时)的花样返回HTML,包含空格和缩进。
  • 在写情势下,传入innerHTML的值都邑根据浏览器处置惩罚HTML的标注体式格局转换为元素(一样因浏览器而异)。假如设置的值仅是纯文本而没有HTML标签,那末效果就是设置纯文本。
div.innerHTML = "Hello world!";
div.innerHTML = "Hello & welcom, <b>\"reader\"!</b>";
// 以上操纵获得: <div id="content">Hello &amp; welcome, <b>&quot;reader&quot;!</b></div>
  • 运用innerHTML属性也有一些限定,大多数浏览器中经由过程innerHTML插进去<script>元素并不会实行个中的剧本。IE8及更早的版本是唯一可以在这类状况下实行剧本的浏览器,但必需满足2个前提:

    • <script>元素指定defer属性
    • <script>元素必需位于(微软所谓的)“有作用域的元素”(scoped element)以后。<script>元素被认为是“无作用域的元素”(NoScoped element),也就是在页面中看不到的元素,与<style>元素或诠释相似。
  • 没有(不支撑)innerHTML属性的元素有:

    • <col>
    • <colgroup>
    • <frameset>
    • <head>
    • <html>
    • <style>
    • <table>
    • <tbody>
    • <thead>
    • <tfoot>
    • <tr>
    • IE8及更早版本 <title>元素也没有
  • Firefox在内容范例为application/xhtml+xml的XHTML文档中设置innerHTML有严厉的限定。在XHTML文档中运用innerHTML时,XHTML代码必需完全符合要求。

outerHTML属性

  • 在读情势下,outerHTML返回挪用它的元素及一切子节点的HTML标签。
  • 在写情势下,outerHTML会依据指定的HTML字符串建立新的DOM子树,然后用这个DOM子树完全替代挪用元素。
  • 在元素上挪用outerHTML会返回雷同的代码,包含元素本身。由于浏览器剖析和诠释HTML标记的差别,效果可以会有所差别。
<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本身 -->
<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>
  • 运用outerHTML属性,会替代挪用的元素本身
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);
  • 支撑的浏览器 IE4+ Safari4+ Chrome Opera8+ Firefox8+

insertAdjacentHTML() 要领

  • insertAdjacentHTML()吸收两个参数:插进去位置和要插进去的HTML文本。
  • 第一个参数必需是以下之一:

    • “beforebegin” 在当前元素之前插进去一个紧邻的平辈元素
    • “afterbegin” 在当前元素之下插进去一个新的子元素或在第一个子元素之前再插进去新的子元素
    • “beforeend” 在当前元素之下插进去一个新的子元素或在末了一个子元素以后再插进去新的子元素
    • “afterend” 在当前元素以后插进去一个紧邻的平辈元素
  • 第二个参数是HTML字符串,假如浏览器没法剖析该字符串,就会抛出毛病。
// 作为前一个平辈元素插进去
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>");
  • 支撑的浏览器 IE Firefox8+ Safari Opera Chrome

内存与机能题目

  • 运用本节引见的要领替代子节点可以会致使浏览器的内存占用题目,尤其是IE,题目越发显著。
  • 在删除带有时刻处置惩罚顺序或援用了其他JavaScript对象子树时,就有可以致使内存占用题目。假定某个元素有一个时刻处置惩罚顺序(或许援用了一个JavaScript对象作为属性),在运用前述某个属性将该元素从文档树删除后,元素与事宜处置惩罚顺序(或JavaScript对象)之间的绑定关联在内存中并没有一并删除。假如这类状况频仍涌现,页面占用的内存数目就会显著增加。
  • 因而,在运用innerHTML outerHTML insertAdjacentHTML() 时,最好先手工删除要被替代的元素的一切事宜处置惩罚顺序和JavaScript对象属性(第13章将进一步议论事宜处置惩罚顺序)。
  • 不过运用这几个属性,特别是innerHTML依然照样可以为我们供应许多遍历的。插进去大批HTML标记时,设置innerHTMLouterHTML时就会建立一个HTML剖析器,这个剖析器是在浏览器级别的代码(通常是C++编写的)基本上运转的,因而比实行JavaScript快的多。
  • 建立和烧毁HTML剖析器也会带来机能丧失,所以最好可以将设置innerHTMLouterHTML的次数掌握在合理的范围内。

scrollIntoView()要领

  • 怎样转动页面也是DOM范例没有解决的一个题目。HTML5终究挑选了scrollIntoView()作为范例要领。
  • scrollIntoView()可以在一切HTML元素上挪用,经由过程转动浏览器窗口或某个容器元素,挪用元素就可以涌现在视口中。

    • 假如传入true作为参数,或许不传任何参数,那末窗口转动以后会让挪用元素的顶部与视口顶部只管的平齐。
    • 假如传入false作为参数,挪用元素会只管悉数涌现在视口中,(可以的话,挪用元素的底部会与视口顶部平齐)不过顶部不一定平齐。
  • 当页面发生变化时,平常会用这个要领来吸援用户的注重力。实际上,为某个元素设置核心也会致使浏览器转动并显现出取得核心的元素。
  • 支撑的浏览器 IE Firefox8+ Safari Opera

专有拓展

  • 虽然一切浏览器开辟商都晓得对峙范例的主要性,但在发明某项功用缺失机,这些开辟商都邑自始自终的向DOM中增加专有拓展,以填补功用上的不足。
  • 表面上看不太友爱,但实际上专有拓展为Web开辟范畴供应了许多主要的功用,这些功用终究都在HTML5范例中得以范例化。
  • 即便如此,依然另有大批专有的DOM拓展没有成为范例。编写本书时,它们照样专有功用,而且只获得了少数浏览器的支撑

文档情势

  • IE8引入了一个新的观点叫“文档情势”(document mode)。页面的文档情势决议了可以运用什么功用。换言之,文档情势决议了你可以运用哪一个级别的CSS,可以在JavaScript中运用哪些API,以及怎样看待文档范例(doctype)。到了IE9总有有以下4种文档情势:

    • IE5: 以混淆情势衬着页面(IE5的默许情势就是混淆情势)。IE8及更高版本中的新功用都没法运用。
    • IE7: 以IE7范例情势衬着页面。IE8及更高版本中的新功用都没法运用
    • IE8: 以IE8范例情势衬着页面。IE8新功用都能用,包含 Selectors API 、更多CSS2级挑选符和某些CSS3功用,另有一些HTML5功用。不能运用IE9中的新功用。
    • IE9: 以IE9范例情势衬着页面。IE9新功用都能用,包含 ECMAScript 5 、完全的CSS3级,更多HTML5功用。
  • 要强迫浏览器以某种情势衬着页面,可以运用HTTP头部信息X-UA-Compatible或等价的<meta>标签来设置。
<meta http-equiv="X-UA-Compatible" content="IE=IEVersion">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
<meta http-equiv="X-UA-Compatible" content="IE=7">
  • 这里的IE版本(IEVersion)有以下差别的值,不一定与上述4中文档情势对应。

    • Edge: 一直以最新的文档情势来衬着页面。疏忽文档范例声明。
    • EmulateIE9: 假如有文档范例声明,则以IE9范例情势衬着,不然将文档情势设置为IE5。
    • EmulateIE8: 假如有文档范例声明,则以IE8范例情势衬着,不然将文档情势设置为IE5。
    • EmulateIE7: 假如有文档范例声明,则以IE7范例情势衬着,不然将文档情势设置为IE5。
    • 9: 强迫以IE9范例情势衬着页面,疏忽文档范例声明
    • 8: 强迫以IE8范例情势衬着页面,疏忽文档范例声明
    • 7: 强迫以IE7范例情势衬着页面,疏忽文档范例声明
    • 5: 强迫将文档情势设置为IE5,疏忽文档范例声明
  • document.documentMode 属机可以晓得给定页面运用的是什么文档情势,它会返回文档情势的版本号(在IE9中,可以返回的版本号为5、7、8、9)

children 属性

  • IE9之前的版本处置惩罚空白符与其他浏览器有差别,因而涌现了children属性。这个属性是HTMLCollection的实例,只包含元素中一样照样元素的子节点。除此之外,childrenchildNodes没有区分。
  • IE8及更早版本的children属性中也会包含诠释节点,但IE9以后的版本只返回元素节点。
  • 支撑的浏览器 IE5 Firefox 3.5 Safari2(有bug) Safari3 Opera8 和Chrome

contains() 要领

  • contains() 先人节点挪用这个要领,吸收一个参数是要搜检的子女节点。假如先人节点包含传入的子女节点返回true,不然false。
  • 支撑的浏览器 IE9+ Firefox Safari Opera Chrome
document.documentElement.contains(document.body);  // true
  • 运用DOM Level 3 compareDocumentPosition()也可以肯定节点间的关联。
  • 这个要领肯定两个节点间的关联,返回示意该关联的位掩码(bitmask)
掩码节点关联
1无关(给定的节点不在当前文档中)
2居前(给定的节点在DOM树中位于参考节点之前)
4居后(给定的节点在DOM树中位于参考节点以后)
8包含(给定的节点是参考节点的先人)
16包含(给定的节点是参考节点的子女)
  • 支撑的浏览器 IE9+ Firefox Safari Opera9.5 Chrome
  • 为模拟contains()要领,应当关注的是掩码16.可以对compareDocumentPosition()的效果实行按位与。
var result = document.documentElement.compareDocumentPosition(document.body);
console.log(!!(result & 16));
  • 实行上面的代码后,效果会变成20(示意“居后”的4加上示意“被包含”的16)。对掩码16实行慰藉操纵会返回一个非零数值,而两个逻辑非操纵符会将该数值转换成布尔值。
  • 注重: 回值可所以值的组合。比方,返回 20 意味着在 p2 在 p1 内部(16),而且 p1 在 p2 之前(4)。
  • 运用一些浏览器及才能检测,就可以写出以下所示的一个通用的contains函数:
function contains(refNode, otherNode) {
  if (typeof refNode.contains == "function" &&
        (!client.engine.webkit || client.engine.webkit >= 522)) {
    // 浏览器支撑contains要领直接运用
    return refNode.contains(otherNode);
  } else if (typeof refNode.compareDocumentPosition == "function") {
    // 浏览器支撑compareDocumentPosition要领
    // 用返回效果和16举行按位与,再转换成布尔值返回
    return !!(refNode.compareDocumentPosition(otherNode) & 16);
  } else {
    // 针对Safari设想的考证要领
    // 在文档树中向上递归考证是不是有refNode
    // 抵达文档树顶端,parentNode 的值为null,轮回完毕
    var node = otherNode.parentNode;
    do {
      if (node === refNode) {
        return true;
      } else {
        node = node.parentNode;
      }
    } while (node !== null);
    return false;
  }
}

插进去文本

  • IE本来有innerHTMLouterHTML已被HTML5归入范例。但别的两个插进去文本的专有属性则没有这么好的命运运限。

    • innerText
    • outerText

转动

  • scrollIntoView()归入范例后,仍有几个专有要领可以在差别浏览器中运用。以下都是对HTMLElement范例的扩大,因而在一切元素中都可以挪用:

    • scrollIntoViewIfNeeded(alignCenter): 只在当前元素在视口中不可见的状况下,才转动浏览器窗口或容器元素,终究让它可见。假如当前元素可见,这个要领什么都不会做。假如将可选的alignCenter参数设置为true,则示意只管将元素显现在视口中部(垂直方向)。Safari和Chrome完成了这个要领
    • scrollByLines(lineCount): 将元素的内容转动到指定的行高,lineCount可所以正值也可所以负值。Safari和Chrome完成了这个要领
    • scrollByPages(lineCount): 将元素的内容转动到指定的页面高度,详细高度由元素的高度决议。Safari和Chrome完成了这个要领
  • 须要注重的是,scrollIntoView()scrollIntoViewIfNeeded()的作用对象是元素的容器,而scrollByLines()scrollByPages()影响的是元素本身
// 将页面主体转动5行
document.body.scrollByLines(5);

// 在当前元素不可见的时刻,让它进入浏览器的视口
document.images[0].scrollIntoViewIfNeeded();

// 将页面主体往回转动1页
document.body.scrollByPages(-1);
  • scrollIntoView()是唯一一个一切浏览器都支撑的要领,因而最经常使用。
    原文作者:陈思达
    原文地址: https://segmentfault.com/a/1190000019244388
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞