@Dale-Lin
2017-12-05T15:33:23.000000Z
字数 12128
阅读 1068
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); //false
console.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 found
var 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 item
var elText = itemTwo.firstChild.nodeValue;
//get its text content
elText = elText.replace("pine nuts","kale");
//Change the saved text content
itemTwo.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 content
document.getElementById('one').innerHTML = elContent;
//set or update HTML content
下面这段代码更新文本和标签,注意使用HTML属性时,双引号在JS中的转义:
var firstChild = document.getElementById('one');
//store the first list item in a variable
var itemContent = firstChild.innerHTML;
//get item' s HTML content
firstChild.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 element
var newText = document.createTextNode('quinoa');
//create a text node and store
newEl.appendChild(newText);
//attach text node to the element
var position = document.getElementByTagName('ul')[0];
//find the position
position.appendChild(newEl);
//insert the element into the position
作为子元素插入的新元素会成为最后一个子元素。
var removeEl = document.getElementByTagName('li')[3];
//the element to remove
var newChild = document.getElementById('p2');
//new node
var containerEl = removeEl.parentNode;
//its parent element
containerEl.removeChild(removeEl);
//remove the element
containerEl.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.firstElementChild
ParentNode.lastElementChild
ParentNode.previousElementSibling
ParentNode.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。