DOM方面
1、DOM构成,Node类型
DOM是文档对象模型,是由多层节点构成的文档,开发者可以通过它对页面进行增删改查。
任何的HTML XML文档都可以用DOM表示为一个由节点构成的层级结构。
所有DOM节点都会实现Node接口,Node接口表现为Node类型,一共有12个,比较常用的就是元素节点和文本节点。
1、nodeName和nodeValue
nodeName:保存着节点的信息,如果节点是元素节点,则 nodeName 属性返回标签名。 如果节点是属性节点,则 nodeName 属性返回属性的名称
nodeType:nodeType 属性返回以数字值返回指定节点的节点类型
2、childNodes属性与NodeList对象
childNodes属性是节点的关系属性
每一个节点都有一个childNodes属性,包含着NodeList实例,NodeList是类数组对象,实质上是一个对 DOM 结构的查询,存储按位置存取的有序节点,有length属性,还可以用中括号和item()获取它的值。
childNodes :
//获取someNode的一个子节点的元素 let firstChild = someNode.childNodes[0]; let secondChild = someNode.childNodes.item(1); //获取节点的长度 let count = someNode.childNodes.length;
3、将NodeList对象转换成数组
//方法一:通过Array.prototype.slice() let arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0); //方法二:通过Array.from let arrOfNodes = Array.from(someNode.childNodes);
4、parentNode属性
(firstChild|lastChild)及同胞节点previousSibling\nextSibling
每个节点都有一个 parentNode 属性,指向其 DOM 树中的父元素,firstChild 和 lastChild 分别指向childNodes 中的第一个和最后一个子节点;
而使用 previousSibling 和 nextSibling 可以在列表的兄弟节点间导航,previousSibling指向上一个节点,nextSibling指向下一个节点,无上下节点兄弟,则值为空。
5、如何操作节点:节点的增删改
appendChild()/insertBefore()/replaceChild()
因为所有关系指针都是只读的,所以 DOM 又提供了一些操纵节点的方法。
appendChild()方法:用于在childNodes末尾添加新的节点。添加的新节点会更新相关的关系指针(比如lastChild)。appendChild()方法返回新添加的节点::
let returnedNode = someNode.appendChild(newNode); alert(returnedNode == newNode); // true alert(someNode.lastChild == newNode); // true //将div元素插入到body里面 document.body.appendChild(div)
注意的是,如果是添加已存在的节点,比如把firstNode传给appendChild(),则这个节点会变成最后一个节点
// 假设 someNode 有多个子节点 let returnedNode = someNode.appendChild(someNode.firstChild); alert(returnedNode == someNode.firstChild); // false alert(returnedNode == someNode.lastChild); // true
insertBefore(要插入的节点,参照节点)方法:插入节点到特定的位置,要插入的节点会变成参照节点的前一个同胞节点,并被返回。如果参照节点是 null,则 insertBefore()与 appendChild()效果相同
// 作为最后一个子节点插入 returnedNode = someNode.insertBefore(newNode, null); alert(newNode == someNode.lastChild); // true // 作为新的第一个子节点插入 returnedNode = someNode.insertBefore(newNode, someNode.firstChild); alert(returnedNode == newNode); // true alert(newNode == someNode.firstChild); // true // 插入最后一个子节点前面 returnedNode = someNode.insertBefore(newNode, someNode.lastChild); alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); // true
replaceChild(要插入的节点,替换的节点)方法:要替换的节点会被返回并从文档树中完全移除,要插入的节点会取而代之,所有关系指针都会从被替换的节点复制过来
// 替换第一个子节点 let returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
removeChild(需要移除的节点):删除节点
// 删除最后一个子节点 let formerLastChild = someNode.removeChild(someNode.lastChild);
cloneNode(布尔值):克隆节点:接收一个布尔值参数,表示是否深复制::true-深复制;false-浅复制。这个方法只复制 HTML 属性。了解即可。
2、Document类型
Document 类型是 JavaScript 中表示文档节点的类型。在浏览器中,文档对象 document 是HTMLDocument 的实例(HTMLDocument 继承 Document),表示整个 HTML 页面。document 是 window对象的属性,因此是一个全局对象。Document 类型的节点有以下特征:
-
nodeType 等于 9;
-
nodeName 值为"#document";
-
nodeValue 值为 null;
-
parentNode 值为 null;
-
ownerDocument 值为 null;
-
子节点可以是 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或 Comment 类型。
Document 类型可以表示 HTML 页面或其他 XML 文档,但最常用的还是通过 HTMLDocument 的实例取得 document 对象。document 对象可用于获取关于页面的信息以及操纵其外观和底层结构。
1、Document的文档子节点
documentElement/body属性
1.documentElement属性
documentElement 属性,始终指向 HTML 页面中的<html>
元素
document.childNodes 中始终有<html>
元素,但使用 documentElement 属性可以更快更直接地访问该元素
比如有下面这个页面:
<html>
<body>
</body>
</html>
浏览器解析完之后,文档只有一个子节点-<html>
元素,可以通过两种方式访问
let html = document.documentElement; // 取得对<html>的引用 alert(html === document.childNodes[0]); // true alert(html === document.firstChild); // true
2.body属性:指向<body>
元素
let body = document.body; // 取得对<body>的引用
3.docType:获取doctype属性
2、文档信息相关属性方法
tittle/url/domain/referrer
tittle属性:包含<title>元素中的文本,通过这个属性可以读写页面的标题,修改 title 属性并不会改变<title>元素
// 读取文档标题 let originalTitle = document.title; // 修改文档标题 document.title = "New page title";
URL:URL 包含当前页面的完整 URL(地址栏中的 URL)
domain:domain 包含页面的域名
referrer:referrer 包含链接到当前页面的那个页面的 URL。
在这些属性中,只有 domain 属性是可以设置的。
// 取得完整的 URL let url = document.URL; // 取得域名 let domain = document.domain; // 取得来源 let referrer = document.referrer;
domain用法(了解):当页面中包含来自某个不同子域的窗格(<frame>
)或内嵌窗格(<iframe>
)时,设置document.domain 是有用的。因为跨源通信存在安全隐患,所以不同子域的页面间无法通过 JavaScript通信。此时,在每个页面上把 document.domain 设置为相同的值,这些页面就可以访问对方的 JavaScript对象了。
3、定位元素,执行操作
getElementById()和 getElementsByTagName()
getElementById()
方法接收一个参数,即要获取元素的 ID,如果找到了则返回这个元素,如果没找到则返回 null。存在多个具有相同 ID 的元素,则 getElementById()返回在文档中出现的第一个元素。
getElementsByTagName()
方法接收一个参数,即要获取元素的标签名,返回包含零个或多个元素的 NodeList(实时列表)。 HTML 文档中,这个方法返回一个HTMLCollection 对象。HTMLCollection 还提供了除索引之外的另一种获取列表项的方式:namedItem()。
HTMLCollection 对象而言,中括号既可以接收数值索引,也可以接收字符串索引。而在后台,数值索引会调用 item(),字符串索引会调用 namedItem()。
<div id="myDiv">Some text</div> let div = document.getElementById("myDiv"); // 取得对这个<div>元素的引用 //取得页面中所有的<img>元素并返回包含它们的 HTMLCollection let images = document.getElementsByTagName("img"); alert(images.length); // 图片数量 alert(images[0].src); // 第一张图片的 src 属性 alert(images.item(0).src); // 同上 /*HTMLCollection 对象还有一个额外的方法 namedItem(),可通过标签的 name 属性取得某一项的引用,例如,假设页面中包含如下的<img>元素:*/ <img src="myimage.gif" name="myImage"> let myImage = images.namedItem("myImage"); //对于name属性的元素,还可以直接使用中括号来获取 let myImage = images["myImage"];
getElementsByName():
会返回具有给定 name 属性的所有元素,常用于单选按钮,因为同一字段的单选按钮必须具有相同的 name 属性才能确保把正确的值发送给服务器
其他特殊的集合:
document 对象上还暴露了几个特殊集合,这些集合也都是 HTMLCollection 的实例。这些集合是访问文档中公共部分的快捷方式,列举如下。
document.anchors 包含文档中所有带 name 属性的<a>元素。
document.forms 包含文档中所有<form>元素
document.images 包含文档中所有<img>元素(与document.getElementsByTagName ("img")
document.links 包含文档中所有带 href 属性的<a>元素。
文档写入:
write()、writeln()、open()和 close()
write()和 writeln()方法都接收一个字符串参数,可以将这个字符串写入网页中。write()简单地写入文本,而 writeln()还会在字符串末尾追加一个换行符(\n)。这两个方法可以用来在页面加载期间向页面中动态添加内容
<html> <head> <title>document.write() Example</title> </head> <body> <p>The current date and time is: <script type="text/javascript"> document.write("<strong>" + (new Date()).toString() + "</strong>"); </script> </p> </body> </html>
open()和 close()方法分别用于打开和关闭网页输出流。不太重要,了解即可。
3、Element类型
最常用的类型,表示元素,对外暴露出访问元素标签名、子节点和属性的能力。
特征:
nodeType 等于 1;
nodeName 值为元素的标签名;
nodeValue 值为 null;
parentNode 值为 Document 或 Element 对象;
子节点可以是 Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference 类型。
可以通过 nodeName 或 tagName 属性来获取元素的标签名
<div id="myDiv"></div> //可以像这样取得这个元素的标签名: let div = document.getElementById("myDiv"); alert(div.tagName); // "DIV" alert(div.tagName == div.nodeName); // true
1,HTML元素
id,元素在文档中的唯一标识符;
title,包含元素的额外信息,通常以提示条形式展示;
lang,元素内容的语言代码(很少用);
dir,语言的书写方向("ltr"表示从左到右,"rtl"表示从右到左,同样很少用);
className,相当于 class 属性,用于指定元素的 CSS 类(因为 class 是 ECMAScript 关键字,所以不能直接用这个名字)。
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div> //修改 div.id = "someOtherId"; div.className = "ft"; div.title = "Some other text"; div.lang = "fr"; div.dir ="rtl";
2,取得属性getAttribute()
getAttribute()主要用于取得自定义属性的值,其他时候一般使用对象属性(style属性和事件处理程序,这个函数表现不一样)
重点:id值.getAttribute("属性")
<body> <div id = 'm1' class = 'name'></div> <body> <script> let m2= m1.getAttribute("class") console.log(m2) //name </script>
3、设置属性setAttribute()
接收两个参数:要设置的属性名和属性的值。
div.setAttribute("id", "someOtherId");
4、删除属性 removeAttribute()
会把整个属性完全从元素中去掉
5、attributes属性(中高级看,初级了解)
attributes 属性包含一个 NamedNodeMap 实例。元素的每个属性都表示为一个 Attr 节点,并保存在这个 NamedNodeMap 对象中。NamedNodeMap 对象包含下列方法:
getNamedItem(name),返回 nodeName 属性等于 name 的节点;
removeNamedItem(name),删除 nodeName 属性等于 name 的节点;
setNamedItem(node),向列表中添加 node 节点,以其 nodeName 为索引;
item(pos),返回索引位置 pos 处的节点
6、创建元素 createElement()
document.createElement()方法创建新元素,这个方法接收一个参数,即要创建元素的标签名,同时也会把新元素的 ownerDocument 属性设置为 document,此时,可以再为其添加属性、添加更多子元素
//创建一个新的div元素 let div = document.createElement("div") //添加一些属性 div.id = "myNewDiv";
在新元素上设置这些属性只会附加信息。因为这个元素还没有添加到文档树,所以不会影响浏览器显示。要把元素添加到文档树,可以使用 appendChild()、insertBefore()或 replaceChild()。比如,以下代码会把刚才创建的元素添加到文档的<body>元素中:
document.body.appendChild(div);
7、选择合适的元素(元素后代
<ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>
在解析以上代码时,<ul>元素会包含 7 个子元素,其中 3 个是<li>元素,还有 4 个 Text 节点(表示<li>元素周围的空格)。因此在执行操作时,通常会检测一下节点的nodeType,是元素节点或者某一类型节点再进行操作
for(let i = 0,len = element.childNodes.length;i<len;++i){ if(element.childNodes[i].nodeType ==1){ //执行某个操作 } }
以上代码会遍历某个元素的子节点,并且只在 nodeType 等于 1(即 Element 节点)时执行某个操作。
取得某个元素的子节点
let ul = document.getElementById("myList"); let items = ul.getElementByTagName("li");
4、Text类型
Text 节点由 Text 类型表示,包含按字面解释的纯文本,也可能包含转义后的 HTML 字符,但不含 HTML 代码。Text 类型的节点具有以下特征:
nodeType 等于 3;
nodeName 值为"#text";
nodeValue 值为节点中包含的文本;
parentNode 值为 Element 对象;
不支持子节点。
Text 节点中包含的文本可以通过 nodeValue 属性访问,也可以通过 data 属性访问,这两个属性包含相同的值。修改 nodeValue 或 data 的值,也会在另一个属性反映出来。
文本节点暴露了以下操作文本的方法:
appendData(text),向节点末尾添加文本 text;
deleteData(offset, count),从位置 offset 开始删除 count 个字符;
insertData(offset, text),在位置 offset 插入 text;
replaceData(offset, count, text),用 text 替换从位置 offset 到 offset + count 的
文本;
splitText(offset),在位置 offset 将当前文本节点拆分为两个文本节点;
substringData(offset, count),提取从位置 offset 到 offset + count 的文本。
除了这些方法,还可以通过 length 属性获取文本节点中包含的字符数量。这个值等于 nodeValue. length 和 data.length。
<!-- 有内容,因此有一个文本节点 --> <div>Hello World!</div> //获取文本节点 let textNode = div.firstChild;//或div.childNode[0] //修改文本节点 div.firstChild.NodeValue = "change text" /*修改文本节点还有一点要注意,就是HTML 或 XML 代码(取决于文档类型)会被转换成实体编码,即小于号、大于号或引号会被转义*/
创建文本节点
document.createTextNode(插入节点的文本):注意:这些要插入的文本也会应用 HTML 或 XML 编码
let textNode = document.createTextNode("<strong>Hello</strong> world!");
创建新文本节点后,其 ownerDocument 属性会被设置为 document。但在把这个节点添加到文档树之前,我们不会在浏览器中看到它。
以下代码创建了一个<div>
元素并给它添加了一段文本消息:
//第一步:创建一个div元素/节点 let element = document.createElement("div"); //第二步:创建一个文本节点 let textNode = document.CreateTextNode("Hello World!"); //第三步:给类为message的元素增加文本节点 element.appendChild(textNode); //第四步:将元素添加到文档主体,才会显示 document.body.appendChild(element);
规范文本节点
合并相邻文本节点:normalize()
该方法是在node节点中定义的,因此所有类型的节点都可以使用。在包含两个或多个相邻文本节点的父节点上调用 normalize()时,所有同胞文本节点会被合并为一个文本节点,这个文本节点的 nodeValue 就等于之前所有同胞节点 nodeValue 拼接在一起得到的字符串
拆分文本节点
splitText():从指定的位置拆分nodeValue,将一个文本节点拆分成两部分,拆分之后,原文本节点包含从开始到拆分点的文本,新文本节点包含后面的文本。这个方法返回新闻本。
let element = document.createElement("div"); element.className = "message"; let textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); let newNode = element.firstChild.splitText(5); alert(element.firstChild.nodeValue); // "Hello" alert(newNode.nodeValue); // " world!" alert(element.childNodes.length); // 2
5、DMO编程
通过 HTML 代码能实现的,也一样能通过 JavaScript 实现。
1、动态脚本
有两种方式通过<script>动态为网页添加脚本:引入外部文件和直接插入源代码。
方法一:外部文件通过DOM编程动态创建节点
//方法一:外部文件通过DOM编程动态创建节点 //这是一个外部文件 <script src="foo.js"></script> //创建一个元素节点,赋值给script对象 let script = document.createElement("script"); //给script对象加上src属性 script.src = "foo.js"; //将script节点加载到文档数,才会下载外部js文件 document.body.appendChild(srcipt); //用函数封装 function loadScript(url){ let srcipt = document.creatElement("script"); srcipt.src = url; document.body.appendChild(srcipt); } //动态加载 loadSrcipt("bar.js")
方法二:1-动态插入源代码
//这是一段srcipt代码 <script> function sayHi() { alert("hi"); }</srcipt> //使用DOM操作,依然可以实现: //第一步,创建一个srcipt节点 let script = document.createElement("script"); //第二步 给srcipt增加一个新的子文本节点,用于存放源码 //源码以text的类型被增加到srcipt内部的节点中 srcipt.appendChild(document.createTextNode("function sayHi(){alert("Hi!");}")) //展示 document.body.appendChild(srcipt); //以上代码不支持旧版IE
2-通过<script>
元素上有一个 text 属性,可以用来添加 JavaScript 代码
var script = document.createElement("script"); script.text = "function sayHi(){alert('hi');}"; document.body.appendChild(script); //老版safari不支持,哎呦其他方式实现,了解这么多差不多啦
2、动态样式
CSS可以通过link外部引入 也可以写style嵌入样式
跟动态脚本一样,添加动态样式也可以从两方面入手
方法一:通过DOM操作动态引入CSS外部样式
//外部文件通过DOM编程动态创建节点 //第一步:创建一个link节点 let link = document.createElement("link"); //给link添加rel/type/href属性 link.rel = "stylesheet"; link.type = "text/css"; link.href = "style.css"; //获取head中的任意一元素 let head = document.getElementByTagName("head")[0]; //将link元素添加至获取的head元素内部 head.appendChild(link);
函数表示为
function loadCss(url){ let link = document.creatElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = url; let head = document.getElementByTagName("head")[0]; head.appendChild(link); } loadCss("style.css")
方法二:动态插入源码,部分老版IE不支持
//这是一段CSS <style type="text/css"> body { background-color: red; } </style> //通过DOM操作实现 let style = document.createElement("style"); style.rel = "stylesheet"; style.type = "text/css"; style.appendChild(document.createTextNode("body{background-color:red}")); let head = document.getElementsByTagName("head")[0]; head.appendChild(style);
3、操作表格
为了方便创建表格,HTML DOM 给<table>
、<tbody>
和<tr>
元素添加了一些属性和方法。
<table>
元素添加了以下属性和方法:(表元指单元格)
caption,指向<caption>元素的指针(如果存在); tBodies,包含<tbody>元素的 HTMLCollection; tFoot,指向<tfoot>元素(如果存在); tHead,指向<thead>元素(如果存在); rows,包含表示所有行的 HTMLCollection; createTHead(),创建<thead>元素,放到表格中,返回引用; createTFoot(),创建<tfoot>元素,放到表格中,返回引用; createCaption(),创建<caption>元素,放到表格中,返回引用; deleteTHead(),删除<thead>元素; deleteTFoot(),删除<tfoot>元素; deleteCaption(),删除<caption>元素; deleteRow(*pos*),删除给定位置的行; insertRow(*pos*),在行集合中给定位置插入一行。 <tbody>元素添加了以下属性和方法: rows,包含<tbody>元素中所有行的 HTMLCollection; deleteRow(*pos*),删除给定位置的行; insertRow(*pos*),在行集合中给定位置插入一行,返回该行的引用。 <tr>元素添加了以下属性和方法: cells,包含<tr>元素所有表元的 HTMLCollection; deleteCell(*pos*),删除给定位置的表元; insertCell(*pos*),在表元集合给定位置插入一个表元,返回该表元的引用。 这些属性和方法极大地减少了创建表格所需的代码量。
案例:
// 创建表格 let table = document.createElement("table"); table.border = 1; table.width = "100%"; // 创建表体 let tbody = document.createElement("tbody"); table.appendChild(tbody); // 创建第一行 tbody.insertRow(0); tbody.rows[0].insertCell(0); tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1")); tbody.rows[0].insertCell(1); tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1")); // 创建第二行 tbody.insertRow(1); tbody.rows[1].insertCell(0); tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2")); tbody.rows[1].insertCell(1); tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2")); // 把表格添加到文档主体 document.body.appendChild(table);
这里创建<table>和<tbody>元素的代码没有变。变化的是创建两行的部分,这次使用了 HTML DOM 表格的属性和方法。创建第一行时,在<tbody>元素上调用了 insertRow()方法。传入参数 0,表示把这一行放在什么位置。然后,使用 tbody.rows[0]来引用这一行,因为这一行刚刚创建并被添加到了<tbody>的位置 0。创建表元的方式也与之类似。在<tr>元素上调用 insertCell()方法,传入参数 0,表示把这个表元放在什么位置上。然后,使用 tbody.rows[0].cells[0]来引用这个表元,因为这个表元刚刚创建并被添加到了<tr>的位置 0。
6、使用NodeList
理解 NodeList 对象和相关的 NamedNodeMap、HTMLCollection,是理解 DOM 编程的关键。这3 个集合类型都是“实时的”,意味着文档结构的变化会实时地在它们身上反映出来,因此它们的值始终代表最新的状态。实际上,NodeList 就是基于 DOM 文档的实时查询。
任何时候要迭代 NodeList,最好再初始化一个变量保存当时查询时的长度,然后用循环变量与这个变量进行比较,不然容易造成无限循环
//错误演示-无限循环-因为NodeList是实时更新的,不断的增加div,长度也会不断地增加 let divs = document.getElementsByTagName("div"); for (let i = 0; i < divs.length; ++i){ let div = document.createElement("div"); document.body.appendChild(div); } //建议: let divs = document.getElementsByTagName("div"); //初始化一个长度 for (let i = 0, len = divs.length; i < len; ++i) { let div = document.createElement("div"); document.body.appendChild(div); }