哈喽elements 2019-11-19
getElementById()
和 gettElementByTagName()
。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()
方法matchesSelector()
方法。接收一个参数,即CSS选择符,如果调用元素与该选择符匹配返回true,否则返回false。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; }
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+className
属性添加、删除和替换类名。因为className
中是一个字符串,所以即使只是修改字符串一部分,也必须每次都设置整个字符串的值。classList
属性。classList
属性是新集合类型 DOMTokenList的实例。与其他DOM集合类似,DOMTokenList 有一个表示自己包含多少元素的length属性,而要去的每个元素可以使用item()
方法,或者方括号语法。此外,这个新类型还定义如下方法:
add(value)
: 将给定的字符串值添加到列表中。如果值已经存在,就不添加了。contains(value)
: 表示列表中是否存在给定的值,如果存在返回true,反之false。remove(value)
: 从列表中删除给定的字符串。toggle(value)
: 如果列表中已经存在给定的值,删除它;如果没有,添加它。classList
属性,除非你需要删除所有类名,或者完全重写元素的class属性,否则就用不到className
属性了。classList
的浏览器 Firefox3.6+ Chromedocument.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
Document.readyState
属性有两个可能的值:
loading
正在加载文档complete
已经加载完文档if (document.readyState == "complete") { ... }
compatMode
的属性,告诉开发人员浏览器采用了哪种渲染模式。document.compatMode
标准模式下等于"CSS1Compat",混杂模式下等于"BackCompat"。if (document.compatMode == "CSS1Compat") { console.log("Standards mode"); } else { console.log("Quirks mode"); }
document.head
属性,与docuemnt.body
对应var head = document.head || document.getElementsByTagName("head")[0];
charset
属性表示文档中实际使用的字符集,也可以用来指定新字符集。默认值是"UTF-16",可以通过<meta>
元素、响应头部或直接设置charset
属性修改这个值。document.Characterset
。console.log(document.charset); // "UTF-16" document.charset = "UTF-8";
defaultCharset
表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。如果文档没有使用默认的字符集,那charset
和defaultCharset
属性值可能会不一样。if (document.charset != document.defaultCharset) { console.log("Custom character set being used."); }
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); }
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
的值都会按照浏览器处理HTML的标注方式转换为元素(同样因浏览器而异)。如果设置的值仅是纯文本而没有HTML标签,那么结果就是设置纯文本。div.innerHTML = "Hello world!"; div.innerHTML = "Hello & welcom, <b>\"reader\"!</b>"; // 以上操作得到: <div id="content">Hello & welcome, <b>"reader"!</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>
<title>
元素也没有application/xhtml+xml
的XHTML文档中设置innerHTML
有严格的限制。在XHTML文档中使用innerHTML
时,XHTML代码必须完全符合要求。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本身 --> <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);
insertAdjacentHTML()
接收两个参数:插入位置和要插入的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>");
innerHTML
outerHTML
insertAdjacentHTML()
时,最好先手工删除要被替换的元素的所有事件处理程序和JavaScript对象属性(第13章将进一步讨论事件处理程序)。innerHTML
仍然还是可以为我们提供很多遍历的。插入大量HTML标记时,设置innerHTML
或outerHTML
时就会创建一个HTML解析器,这个解析器是在浏览器级别的代码(通常是C++编写的)基础上运行的,因此比执行JavaScript快的多。innerHTML
或outerHTML
的次数控制在合理的范围内。scrollIntoView()
作为标准方法。scrollIntoView()
可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。
IE8引入了一个新的概念叫“文档模式”(document mode)。页面的文档模式决定了可以使用什么功能。换言之,文档模式决定了你可以使用哪个级别的CSS,可以在JavaScript中使用哪些API,以及如何对待文档类型(doctype)。到了IE9总有有以下4种文档模式:
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
属性。这个属性是HTMLCollection的实例,只包含元素中同样还是元素的子节点。除此之外,children
与 childNodes
没有区别。children
属性中也会包含注释节点,但IE9之后的版本只返回元素节点。contains()
祖先节点调用这个方法,接收一个参数是要检查的后代节点。如果祖先节点包含传入的后代节点返回true,否则false。document.documentElement.contains(document.body); // true
compareDocumentPosition()
也能够确定节点间的关系。掩码 | 节点关系 |
---|---|
1 | 无关(给定的节点不在当前文档中) |
2 | 居前(给定的节点在DOM树中位于参考节点之前) |
4 | 居后(给定的节点在DOM树中位于参考节点之后) |
8 | 包含(给定的节点是参考节点的祖先) |
16 | 包含(给定的节点是参考节点的后代) |
contains()
方法,应该关注的是掩码16.可以对compareDocumentPosition()
的结果执行按位与。var result = document.documentElement.compareDocumentPosition(document.body); console.log(!!(result & 16));
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原来有innerHTML
和outerHTML
已被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()
是唯一一个所有浏览器都支持的方法,因此最常用。Vue和React是数据驱动视图,如何有效控制DOM操作?能不能把计算,更多的转移为js计算?因为js执行速度很快。patch函数-->patch,对比tag,对比tag与key,对比children