@Dale-Lin
2017-12-05T07:33:23.000000Z
字数 12128
阅读 1371
JavaScript
HTML页面内有四种节点:
访问并更新DOM树需要两个步骤:
1. 定位节点(访问元素)。
2. 使用其文本内容、子元素或属性(操作元素)。
| 选择单个元素节点 | 选择多个元素 | 在元素节点之间遍历 |
|---|---|---|
| getElementById() | getElementsByClassName() | parentNode |
| querySelector() | getElementsByTagName() | previousSibling/nextSibiling |
| querySelectorAll() | firstChild/lastChild |
选择单个节点元素
getElementById():使用元素唯一的id属性。
querySelector():使用CSS选择器,返回第一个匹配的子元素。
选择多个元素
getElementsByClassName('className1 className1'):选择所有具有特定一个或几个class属性的元素。可以通过 NodeList 调用,返回子元素树内的匹配项。
getElementsByTagName():选择所有使用了特定HTML标记的元素。
querySelectorAll():使用CSS选择器(定义CSS规则时用到的选择方法)来选择所有匹配的子元素的 NodeList。
在元素节点之间遍历
parentNode:选择当前元素的父节点。
previousSibling/nextSibiling:选择DOM树中的前一个或后一个兄弟节点。
firstChild/lastChild:返回当前元素的第一个或最后一个子节点。
访问document的根元素(如HTML文档的<html>元素)
document.documentElement:选择文档的根元素。
| 访问/更新文本节点 | 操作HTML内容 | 访问或更新属性值 |
|---|---|---|
| nodeValue | innerHTML | className/id |
| textContent | hasAttribute() | |
| createElement() | getAttribute() | |
| creatTextNode() | setAttribute() | |
| appendChild() | removeAttribute() | |
| removeChild() | ||
| insertBefore() | ||
| replaceChild() | ||
| cloneNode() | ||
| remove() |
访问/更新文本节点
nodeValue:let value = node.nodeValue value储存了当前 文本节点 的字符串;如果是文档节点,则返回null。
通过这个方法传入的字符串如果含有< 和 >等字符,会被编码成 < 和 >等形式再输出(即不能写入HTML)。
操作HTML内容
innerHtml:修改HTML内容。
createElement():document.createElement("tagName") 创建元素节点并返回。
createTextNode():var text = document.createTextNode('deta') 创建自定义内容的文本节点。
appendChild():向childNodes列表的末尾添加子元素节点,并返回这个节点。
remove():ChildNode.remove() 方法将它所属的DOM树中删除对象。
var el = document.getElementById('div-1');el.nextSibling.remove();//删除div-1元素的下一个兄弟元素节点
如果传入appendChild()的节点已经在文档中,则会将其从原有位置转移到新位置。(任一节点不能存在两次)
var returnedNode = someNode.appendChild(someNode.firstChild);console.log(returnedNode == someNode.firstChild); //falseconsole.log(returnedNode == someNode.lastChild); //true
removeChild():移除节点并返回该节点。
insertBefore():接受两个参数,要插入的节点和参照节点。如果参照节点为null,则插入到最后。
replaceChild():接受两个参数,要插入的节点和要替换的节点。
cloneNode():var dupNode = node.cloneNode(deep); 该方法返回调用该方法的节点的一个副本(包括onclick等绑定事件,但不会复制使用 addEventListener() 或 node.onclick = fn 这种js动态绑定的事件),由于复制后节点属性完全一致,往往需要重设 id 属性。 deep 使用布尔值表示是否进行深度克隆(克隆后代节点/只克隆该节点)。
/* appendChild()/removeChild()/replaceChild()/insertBefore() 都是从父级元素操作 childNodes 的方法,往往需要使用 parentNode 属性找到父元素。 */
访问并更新属性值
className/id:获取或更新元素的class和id属性。
hasAttribute():检查某属性是否存在。
getAttribute():let attribute = element.getAttribute(attributeName) 获取某属性的值,不存在则返回 null 或 " "。
在访问 style 时,返回CSS文本(通过属性访问 style 时返回一个对象);
在访问 onclick 这样的事件处理程序时,返回相应代码的字符串(通过属性访问时,返回一个 JS Function)。
setAttribute():element.setAttribute(name, value); 设置(属性不存在时)或更新属性值。
removeAttribute():element.removeAttribute(name); 移除属性。
当需要多次操作同一个元素的时候,应该使用一个变量来保存这个查询的结果,以节省浏览器再次查找的时间。
例如:
var itemOne = getElementById('one');
itemOne并不储存id属性值为one的元素节点,而是保存了DOM树中对这个元素的引用(元素节点的位置)。
DOM查询可能返回一个元素,也可能返回一个节点的集合NodeList。
如果使用的访问方法是能返回多个元素的方法,则无论返回多少个元素,这种方法都会返回一个NodeList,可以使用类似数组的索引编号来访问这个NodeList中的元素。
另外,在查询元素时应该选择查找的最快路径(经过最少节点的路径)。
注意:查询方法括号内的参数应使用单引号'',因为其是一个字符串。
查询由 对象+点操作符+方法 组成。
当DOM查询返回一个NodeList时,可以:
NodeList使用起来像一个数组,但其并不是一个数组实例。(有 length 属性,也可使用 方括号+索引 访问元素)
var arrayOfNodes = Array.prototype.slice.call(NodeList, 0);
NodeList 的原型链上只有一个 item() 方法:item(idx)
等价于 NodeList[idx]
该方法返回具有与字符串参数相同的 name/id 属性的节点
var images = document.getElementsByTagName("img");var myImage = images.namedItem("myImage");// equals to var myImage = images["myImage"]
即,当一个HTMLCollection方括号内接受数字时,调用的是 item() 方法;接受字符串时,调用的是 namedItem()。
当将一个NodeList保存在变量中时,要留意这个NodeList是动态还是静态的。
使用数组语法,如果需要多次访问同一个NodeList,应把DOM查询的结果保存在变量中:
var elements = document.getElementsByClassName('hot');if (elements.length >= 1){var firstItem = elements[0];}
如果查询结果没有任何元素,那么执行if语句内的代码是对资源的浪费,所以使用if语句判断这个NodeList中是否含有一个或以上的节点。
var elements = document.getElementsByClassName('hot');if (elements.length>2){ //If 3 or more are foundvar el = elements[2];el.className = 'cool'; //Change the value of its class attribute}
var elements = document.getElementsByTagName('li');if(elements.length > 0){var el = element[0];el.className = 'cool';}
var el = document.querySelector('li.hot');el.className = 'cool';var els = document.querySelectorAll('li.hot');els[1].className = 'cool';
var hotItems = document.querySelectorAll('li.hot');if (hotItems.length > 0){for (i=0; i<hotItems.length; i++){hotItems[i].className = 'cool';}}
访问到一个元素后,可以使用它的5个属性来找到与其相关的元素,这种方式成为遍历DOM。
parentNode:该属性在HTML中找到该元素的父元素节点。
previousSibling/nextSibling:这两个属性找到当前节点的前一个或后一个兄弟节点,若不存在,则返回结果null。
firstChild/lastChild:这两个属性找到当前元素的第一个或最后一个子节点,若不存在,返回结果null。
注意:
1. 这些是当前节点的JS属性,而不是一种方法。
2. 这些属性是只读的,而不能改变节点。
除了ie浏览器以外,其他浏览器会把HTML元素之间的空格、换行当做一个文本节点。所以在遍历DOM的时候浏览器可能会返回不同的节点。(使用jQuery可避开此问题),这时候可以使用循环来删除空白节点。
var nodes = document.getElementsByClassName('hot');function filterSpaceNode(nodes){for (var i = 0; i < nodes.length; i++){if (nodes[i].nodeType == 3 && /^\s+$/.test(node[i].nodeValue) ){node[i].parentNode.removeChild(node[i]); //得到空白节点后,从其父节点删除该空白节点}}return nodes;}
document.getElementById('one').firstChild.nextSibling.nodeValue;/* 使用nodeValue属性时,必须在文本节点上操作 */
var itemTwo = document.getElementById('two');//get second list itemvar elText = itemTwo.firstChild.nodeValue;//get its text contentelText = elText.replace("pine nuts","kale");//Change the saved text contentitemTwo.fitstChild.nodeValue = elText;//update the changed text content
<input>内的内容,可以使用value属性:
<input type="text" id="name" onchange="change()"><script>function change(){var x = document.getElementById('name');x.value = x.value.toUpperCase();}</script>
如果不知道节点周围是否有其他元素节点,使用包含元素会更容易:
<li id="one"><em>fresh</em> figs</li>
对于这段HTML代码,
document.getElementById('one').textContent;
会把em标签内的“fresh”和em标签的兄弟text节点内的“figs”都获取到。
var attr = element.attributes
Element.attributes 属性返回该元素所有属性节点的一个动态集合(一个NamedNodeMap),不是一个数组,但可以通过索引来访问,只是其中属性 名值对(Element.attributes[idx].name 和 Element.attributes[idx].value) 的排列顺序与浏览器有关。
表示属性节点 attr 对象的集合。
属性
只能通过 NamedNodeMap.length 读取长度。
方法
~.getNamedItem(name) 返回一个期望获取的属性节点的名值对。
<li id="one"><em>fresh</em> figs</li>
var elContent = document.getElementById('one').innerHTML;//get HTML contentdocument.getElementById('one').innerHTML = elContent;//set or update HTML content
下面这段代码更新文本和标签,注意使用HTML属性时,双引号在JS中的转义:
var firstChild = document.getElementById('one');//store the first list item in a variablevar itemContent = firstChild.innerHTML;//get item' s HTML contentfirstChild.innerHTML = '<a href=\"http://www.bilibili.com\">' + itemContent + '</a>';//update the content of the first item
如果操作一个节点及其后代“渲染”的文本内容,可以使用 innerText 。
DOM操作
DOM操作容易针对DOM树中的独立节点,但更新整个HTML片段更适合用innerHTML属性。DOM操作比使用innerHTML属性更安全,但需要更多代码,并且速度更慢。
var newEl = document.createElement('li');//create a new variable to store an elementvar newText = document.createTextNode('quinoa');//create a text node and storenewEl.appendChild(newText);//attach text node to the elementvar position = document.getElementByTagName('ul')[0];//find the positionposition.appendChild(newEl);//insert the element into the position
作为子元素插入的新元素会成为最后一个子元素。
var removeEl = document.getElementByTagName('li')[3];//the element to removevar newChild = document.getElementById('p2');//new nodevar containerEl = removeEl.parentNode;//its parent elementcontainerEl.removeChild(removeEl);//remove the elementcontainerEl.replaceChild(p2, p1);//replace the node
element.nodeName/element.tagName
element.tagName.toLowerCase();
除了id、className、title外,还能修改style、href、src等属性:
var changeStyle = document.getElementById('demo');x.style.color = "6600ff";x.style.width = "200px";x.style.backgroundColor = red;//changing style
注意style内有"-"号的样式,样式名要改成驼峰形式。
使用DOM操作修改元素的样式可以使元素移动:
容器的position应为relative,内容的position应为absolute
var pos = 0;var box = document.getElementById('box');var timer = setInterval(move, 10);function move(){if (pos >= 150){clearInterval(timer);}else{pos += 1;box.style.left = pos + "px";}}
如果对一个节点node使用 node.getElementByTagName('*') ;会将该节点下所有的子元素都获取。(*通配符的使用)
每个节点都有一个childNodes属性,保存了一个NodeList对象(只读)。
语法
var ndList = elementNodeReference.childNodes
空白节点 也会被视作一个子节点。
以上三种方法都返回一个Boolean值,可以用于判断。
合并节点下相邻的两个文本节点,并能消除空的文本节点。
语法
element.normalize()
适用情况:
根据偏移量将一个文本节点分割成两个相邻文本节点。
语法
replacementNode = textnode.splitText(offset)
replacementNode 会指向分割的后一个文本节点(作为返回值)offset 是后一个文本的起始位置。在<p>元素包含的文本节点中插入一个<span>元素
<body><p id="p">foobar</p><script type="text/javascript">var p = document.getElementById('p');var textnode = p.firstChild;// 将原文本节点分割成为内容分别为foo和bar的两个文本节点var replacementNode = textnode.splitText(3);// 创建一个包含了内容为' span contents '的文本节点的span元素var span = document.createElement('span');span.appendChild(document.createTextNode(' span contents '));// 将span元素插入到后一个文本节点('bar')的前面p.insertBefore(span, replacementNode);// 现在的HTML结构成了<p id="p">foo <span>span contents</span> bar</p></script></body>
完成后可以使用 node.normalize() 合并分开的文本节点。
语法
let fragment = docement.createDocumentFragment()
在 DocumentFragment 内可以包含和操作节点,且不会引起DOM树的重渲染。
所有node接口都适合用一个 DocumentFragment 作为参数(批量添加),避免每一个节点的插入都触发一次重渲染。
属性
继承了所有Node属性,且可以作为 parentNode 读取属性。
ParentNode.children 返回一个动态 HTMLCollection,包含所有 Element 类型的子对象。ParentNode.firstElementChild 返回第一个 Element 类型的子对象。ParentNode.lastElementChild 返回最后一个 Element 类型子对象。ParentNode.childElementCount 返回一个无符号长的 Element 类型子对象的数目。方法
继承了所有node方法,还有以下方法:
DocumentFragment.querySelector() 返回第一个符合选择器的元素节点。DocumentFragment.querySelectorAll() 返回所有符合选择器的元素节点。使用元素遍历能消除文本节点和注释的影响
ParentNode.childElementCount 返回子元素数目。ParentNode.firstElementChildParentNode.lastElementChildParentNode.previousElementSiblingParentNode.nextElementSibling元素的 classList 属性返回一个包含元素所有 class 的 DomTokenList(一组以空格分隔的tokens),可以通过索引访问内容。
方法
~.contains(String) 是否存在给定值,返回 true/false。~.add(String) 添加给定字符串,如果已经存在,则忽略此操作。~.remove(String) 移除一个字符串。~.toggle(String) 如果存在某字符串,移除该字符串,并返回 false;如果不存在某字符串,添加该字符串,并返回 true。document.activeElement 该属性返回正获得焦点的某个元素,如果没有,返回 body 元素。document.hasFocus() 用于确定文档是否获得了焦点(得知用户是否在与页面交互)。document.readyState 有两个可能值:
兼容模式查询 document.compatMode
CSS1Compat 标准模式(standards mode)返回值。BackCompat 兼容模式(quirks mode)返回值。innerHTML 属性outerHTML 属性insertAdjacentHTML(pos, HTML) 方法: beforebegin 在当前元素之前插入一个兄弟元素。afterbegin 在当前元素下插入第一个子元素。beforeend 在当前元素下插入最后一个子元素。afterend 在当前元素后插入一个兄弟元素。
<meta http-equiv="X-UA-Compatible" content="IE=IEVersion">
IEVersion 的值可以为下:
Edge:始终以最新文档模式来渲染页面。9/8/7/5:强制IE9/8/7/5标准模式渲染,忽略文档类型声明。EmulateIE9/8/7:若果有文档类型声明,以IE9/8/7标准模式渲染,否则以IE5渲染。测试一个包含元素是否含有某个子元素,返回 true/false 。
语法
node.contains( otherNode )
node 是包含元素。otherNode 是要确定的元素。语法
compareDocumentPosition
该方法根据传入节点和调用节点的关系返回一个掩码
| 掩码 | 节点关系 |
|---|---|
| 0 | 相同节点 |
| 1 | 不在同一文档 |
| 2 | 传入节点在前 |
| 4 | 传入节点在后 |
| 8 | 传入节点包含调用节点 |
| 16 | 传入节点被调用节点包含 |
例子:
var head = document.getElementsByTagName('head').[0];//&为按位与操作符if (head.compareDocumentPosition(document.body) & 4){console.log("well-fromed document");} else {console.log("<head> is before <body>");}
因为 compareDocumentPosition()返回的是一个位掩码,所以必须使用按位与运算符才能得到有意义的值。
innerText 属性。 innerText 和 textContent 返回的内容并不完全一样,innerText 会忽略行内的样式和脚本,而 textContent 会返回行内样式和脚本代码。
outerText 属性。 innerText 的范围扩大到调用标签。语法
let style = window.getComputedStyle(element, [pseudoElt]);
该方法返回一个实时的CSSStyleDeclaration
let elem1 = document.getElementById("elemId");let style = window.getComputedStyle(elem1, null);
在 IE 中,对元素的 style 属性使用 currentStyle 属性获得 CSSStyleDeclaration 实例
语法
element.style.cssText = 'str';
在通过element.style.cssText设置css属性的时候,0要加上单位px。