[关闭]
@Dale-Lin 2017-12-05T15:33:23.000000Z 字数 12128 阅读 1068

JS Dom

JavaScript


HTML页面内有四种节点:

  1. 文档节点(Document)
  2. 元素节点(HTML标记元素)
  3. 属性节点(HTML标记元素的属性)
  4. 文本节点(元素节点下的文本)

访问并更新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()

访问/更新文本节点
nodeValuelet value = node.nodeValue value储存了当前 文本节点 的字符串;如果是文档节点,则返回null。

通过这个方法传入的字符串如果含有< 和 >等字符,会被编码成 &lt; 和 &gt;等形式再输出(即不能写入HTML)。

操作HTML内容
innerHtml:修改HTML内容。
createElement()document.createElement("tagName") 创建元素节点并返回。
createTextNode()var text = document.createTextNode('deta') 创建自定义内容的文本节点。
appendChild():向childNodes列表的末尾添加子元素节点,并返回这个节点。
remove()ChildNode.remove() 方法将它所属的DOM树中删除对象。

  1. var el = document.getElementById('div-1');
  2. el.nextSibling.remove();
  3. //删除div-1元素的下一个兄弟元素节点

如果传入appendChild()的节点已经在文档中,则会将其从原有位置转移到新位置。(任一节点不能存在两次)

  1. var returnedNode = someNode.appendChild(someNode.firstChild);
  2. console.log(returnedNode == someNode.firstChild); //false
  3. console.log(returnedNode == someNode.lastChild); //true

removeChild():移除节点并返回该节点。
insertBefore():接受两个参数,要插入的节点和参照节点。如果参照节点为null,则插入到最后。
replaceChild():接受两个参数,要插入的节点和要替换的节点。
cloneNode()var dupNode = node.cloneNode(deep); 该方法返回调用该方法的节点的一个副本(包括onclick等绑定事件,但不会复制使用 addEventListener()node.onclick = fn 这种js动态绑定的事件),由于复制后节点属性完全一致,往往需要重设 id 属性。 deep 使用布尔值表示是否进行深度克隆(克隆后代节点/只克隆该节点)。

  1. /* 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); 移除属性。


缓存DOM查询

当需要多次操作同一个元素的时候,应该使用一个变量来保存这个查询的结果,以节省浏览器再次查找的时间。

例如:

  1. var itemOne = getElementById('one');

itemOne并不储存id属性值为one的元素节点,而是保存了DOM树中对这个元素的引用(元素节点的位置)。


访问元素

DOM查询可能返回一个元素,也可能返回一个节点的集合NodeList。

如果使用的访问方法是能返回多个元素的方法,则无论返回多少个元素,这种方法都会返回一个NodeList,可以使用类似数组的索引编号来访问这个NodeList中的元素。

另外,在查询元素时应该选择查找的最快路径(经过最少节点的路径)。

注意:查询方法括号内的参数应使用单引号'',因为其是一个字符串。

查询由 对象+点操作符+方法 组成。

NodeList

当DOM查询返回一个NodeList时,可以:

NodeList使用起来像一个数组,但其并不是一个数组实例。(有 length 属性,也可使用 方括号+索引 访问元素)

将NodeList对象转化为数组实例:

  1. var arrayOfNodes = Array.prototype.slice.call(NodeList, 0);

NodeList 的原型链上只有一个 item() 方法:item(idx)
等价于 NodeList[idx]

NodeList[str]/NodeList.namedItem(str)

该方法返回具有与字符串参数相同的 name/id 属性的节点

  1. var images = document.getElementsByTagName("img");
  2. var myImage = images.namedItem("myImage");
  3. // equals to var myImage = images["myImage"]

即,当一个HTMLCollection方括号内接受数字时,调用的是 item() 方法;接受字符串时,调用的是 namedItem()

动态和静态NodeList

当将一个NodeList保存在变量中时,要留意这个NodeList是动态还是静态的。

  1. 以getElementBy/getElementsBy开头的方法返回的是动态nodelist。它们的获取比静态的快。当脚本更新页面以后,NodeList同样会进行更新。
  2. 以querySelector(CSS选择器语法)开头的方法会返回静态NodeList,它们反应查询时的文档。当脚本更新页面之后,静态NodeList不会更新。

从NodeList中选择元素

使用数组语法,如果需要多次访问同一个NodeList,应把DOM查询的结果保存在变量中:

  1. var elements = document.getElementsByClassName('hot');
  2. if (elements.length >= 1){
  3. var firstItem = elements[0];
  4. }

如果查询结果没有任何元素,那么执行if语句内的代码是对资源的浪费,所以使用if语句判断这个NodeList中是否含有一个或以上的节点。

使用class属性选择元素

  1. var elements = document.getElementsByClassName('hot');
  2. if (elements.length>2){ //If 3 or more are found
  3. var el = elements[2];
  4. el.className = 'cool'; //Change the value of its class attribute
  5. }

使用标签名选择元素

  1. var elements = document.getElementsByTagName('li');
  2. if(elements.length > 0){
  3. var el = element[0];
  4. el.className = 'cool';
  5. }

使用CSS选择器选择元素

  1. var el = document.querySelector('li.hot');
  2. el.className = 'cool';
  3. var els = document.querySelectorAll('li.hot');
  4. els[1].className = 'cool';

循环遍历NodeList

  1. var hotItems = document.querySelectorAll('li.hot');
  2. if (hotItems.length > 0){
  3. for (i=0; i<hotItems.length; i++){
  4. hotItems[i].className = 'cool';
  5. }
  6. }

遍历DOM

访问到一个元素后,可以使用它的5个属性来找到与其相关的元素,这种方式成为遍历DOM。

parentNode:该属性在HTML中找到该元素的父元素节点。
previousSibling/nextSibling:这两个属性找到当前节点的前一个或后一个兄弟节点,若不存在,则返回结果null。
firstChild/lastChild:这两个属性找到当前元素的第一个或最后一个子节点,若不存在,返回结果null。

注意:
1. 这些是当前节点的JS属性,而不是一种方法。
2. 这些属性是只读的,而不能改变节点。

空白节点

除了ie浏览器以外,其他浏览器会把HTML元素之间的空格、换行当做一个文本节点。所以在遍历DOM的时候浏览器可能会返回不同的节点。(使用jQuery可避开此问题),这时候可以使用循环来删除空白节点。

  1. var nodes = document.getElementsByClassName('hot');
  2. function filterSpaceNode(nodes){
  3. for (var i = 0; i < nodes.length; i++){
  4. if (nodes[i].nodeType == 3 && /^\s+$/.test(node[i].nodeValue) ){
  5. node[i].parentNode.removeChild(node[i]); //得到空白节点后,从其父节点删除该空白节点
  6. }
  7. }
  8. return nodes;
  9. }

获取/更新Element类型

  1. document.getElementById('one').firstChild.nextSibling.nodeValue;
  2. /* 使用nodeValue属性时,必须在文本节点上操作 */
  1. var itemTwo = document.getElementById('two');
  2. //get second list item
  3. var elText = itemTwo.firstChild.nodeValue;
  4. //get its text content
  5. elText = elText.replace("pine nuts","kale");
  6. //Change the saved text content
  7. itemTwo.fitstChild.nodeValue = elText;
  8. //update the changed text content
  1. <input type="text" id="name" onchange="change()">
  2. <script>
  3. function change(){
  4. var x = document.getElementById('name');
  5. x.value = x.value.toUpperCase();
  6. }
  7. </script>

如果不知道节点周围是否有其他元素节点,使用包含元素会更容易:

  1. <li id="one"><em>fresh</em> figs</li>

对于这段HTML代码,

  1. document.getElementById('one').textContent;

会把em标签内的“fresh”和em标签的兄弟text节点内的“figs”都获取到。

Element类型的 attributes 属性

  1. var attr = element.attributes

Element.attributes 属性返回该元素所有属性节点的一个动态集合(一个NamedNodeMap),不是一个数组,但可以通过索引来访问,只是其中属性 名值对(Element.attributes[idx].name 和 Element.attributes[idx].value) 的排列顺序与浏览器有关。

NamedNodeMap 是啥?

表示属性节点 attr 对象的集合。

属性

只能通过 NamedNodeMap.length 读取长度。

方法

~.getNamedItem(name) 返回一个期望获取的属性节点的名值对。


添加或移除HTML内容

  1. <li id="one"><em>fresh</em> figs</li>
  1. var elContent = document.getElementById('one').innerHTML;
  2. //get HTML content
  3. document.getElementById('one').innerHTML = elContent;
  4. //set or update HTML content

下面这段代码更新文本和标签,注意使用HTML属性时,双引号在JS中的转义:

  1. var firstChild = document.getElementById('one');
  2. //store the first list item in a variable
  3. var itemContent = firstChild.innerHTML;
  4. //get item' s HTML content
  5. firstChild.innerHTML = '<a href=\"http://www.bilibili.com\">' + itemContent + '</a>';
  6. //update the content of the first item

如果操作一个节点及其后代“渲染”的文本内容,可以使用 innerText

  1. var newEl = document.createElement('li');
  2. //create a new variable to store an element
  3. var newText = document.createTextNode('quinoa');
  4. //create a text node and store
  5. newEl.appendChild(newText);
  6. //attach text node to the element
  7. var position = document.getElementByTagName('ul')[0];
  8. //find the position
  9. position.appendChild(newEl);
  10. //insert the element into the position

作为子元素插入的新元素会成为最后一个子元素。

  1. 将需要移除的元素保存在一个变量中。
  2. parentNode:使用该属性将该元素的父元素保存在一个变量中。
  3. removeChild():该方法用于移除一个子元素(及其所有后代元素)。
  1. var removeEl = document.getElementByTagName('li')[3];
  2. //the element to remove
  3. var newChild = document.getElementById('p2');
  4. //new node
  5. var containerEl = removeEl.parentNode;
  6. //its parent element
  7. containerEl.removeChild(removeEl);
  8. //remove the element
  9. containerEl.replaceChild(p2, p1);
  10. //replace the node
  1. element.tagName.toLowerCase();

修改HTML标签内的属性

除了id、className、title外,还能修改style、href、src等属性:

  1. var changeStyle = document.getElementById('demo');
  2. x.style.color = "6600ff";
  3. x.style.width = "200px";
  4. x.style.backgroundColor = red;
  5. //changing style

注意style内有"-"号的样式,样式名要改成驼峰形式。


制作动画

使用DOM操作修改元素的样式可以使元素移动:

容器的position应为relative,内容的position应为absolute

  1. setInterval():接受两个参数,一个重复被调用的函数或语句、调用的时间间隔。
  2. clearInterval():接受一个setInterval函数,用于停止该函数。
  1. var pos = 0;
  2. var box = document.getElementById('box');
  3. var timer = setInterval(move, 10);
  4. function move(){
  5. if (pos >= 150){
  6. clearInterval(timer);
  7. }
  8. else{
  9. pos += 1;
  10. box.style.left = pos + "px";
  11. }
  12. }

如果对一个节点node使用 node.getElementByTagName('*') ;会将该节点下所有的子元素都获取。(*通配符的使用)


Node.childNodes

每个节点都有一个childNodes属性,保存了一个NodeList对象(只读)。

语法

var ndList = elementNodeReference.childNodes

空白节点 也会被视作一个子节点。

检查当前节点是否有子节点

以上三种方法都返回一个Boolean值,可以用于判断。


Node.normalize()

合并节点下相邻的两个文本节点,并能消除空的文本节点。

语法

element.normalize()

适用情况:


Text.splitText()

根据偏移量将一个文本节点分割成两个相邻文本节点。

语法

replacementNode = textnode.splitText(offset)

<p>元素包含的文本节点中插入一个<span>元素

  1. <body>
  2. <p id="p">foobar</p>
  3. <script type="text/javascript">
  4. var p = document.getElementById('p');
  5. var textnode = p.firstChild;
  6. // 将原文本节点分割成为内容分别为foo和bar的两个文本节点
  7. var replacementNode = textnode.splitText(3);
  8. // 创建一个包含了内容为' span contents '的文本节点的span元素
  9. var span = document.createElement('span');
  10. span.appendChild(document.createTextNode(' span contents '));
  11. // 将span元素插入到后一个文本节点('bar')的前面
  12. p.insertBefore(span, replacementNode);
  13. // 现在的HTML结构成了<p id="p">foo <span>span contents</span> bar</p>
  14. </script>
  15. </body>

完成后可以使用 node.normalize() 合并分开的文本节点。


DocumentFragment 类型 (nodeType == 11)

语法
let fragment = docement.createDocumentFragment()

DocumentFragment 内可以包含和操作节点,且不会引起DOM树的重渲染。
所有node接口都适合用一个 DocumentFragment 作为参数(批量添加),避免每一个节点的插入都触发一次重渲染。

属性

继承了所有Node属性,且可以作为 parentNode 读取属性。

方法

继承了所有node方法,还有以下方法:


元素(Element) 遍历

使用元素遍历能消除文本节点和注释的影响


Element.classList

元素的 classList 属性返回一个包含元素所有 class 的 DomTokenList(一组以空格分隔的tokens),可以通过索引访问内容。

方法


焦点管理


HTMLDocument 变化

  1. document.readyState 有两个可能值:

    • loading,正在加载文档。
    • complete,文档加载完成。
  2. 兼容模式查询 document.compatMode

    • CSS1Compat 标准模式(standards mode)返回值。
    • BackCompat 兼容模式(quirks mode)返回值。

插入标记

  1. innerHTML 属性
  2. outerHTML 属性
  3. insertAdjacentHTML(pos, HTML) 方法:
    • beforebegin 在当前元素之前插入一个兄弟元素。
    • afterbegin 在当前元素下插入第一个子元素。
    • beforeend 在当前元素下插入最后一个子元素。
    • afterend 在当前元素后插入一个兄弟元素。

文档模式

  1. <meta http-equiv="X-UA-Compatible" content="IE=IEVersion">

IEVersion 的值可以为下:


contains() 方法

测试一个包含元素是否含有某个子元素,返回 true/false

语法

node.contains( otherNode )

确定节点之间的关系

语法

compareDocumentPosition
该方法根据传入节点和调用节点的关系返回一个掩码

掩码 节点关系
0 相同节点
1 不在同一文档
2 传入节点在前
4 传入节点在后
8 传入节点包含调用节点
16 传入节点被调用节点包含

例子:

  1. var head = document.getElementsByTagName('head').[0];
  2. //&为按位与操作符
  3. if (head.compareDocumentPosition(document.body) & 4){
  4. console.log("well-fromed document");
  5. } else {
  6. console.log("<head> is before <body>");
  7. }

因为 compareDocumentPosition()返回的是一个位掩码,所以必须使用按位与运算符才能得到有意义的值。


插入文本

  1. innerText 属性。
    该属性会返回节点内所有(包括子元素)的文本内容,并且忽略行内的样式;写入时,将过滤掉HTML标签(被转义)。

innerText 和 textContent 返回的内容并不完全一样,innerText 会忽略行内的样式和脚本,而 textContent 会返回行内样式和脚本代码。

  1. outerText 属性。
    innerText 的范围扩大到调用标签。

访问元素的样式

语法

let style = window.getComputedStyle(element, [pseudoElt]);

该方法返回一个实时的CSSStyleDeclaration

  1. let elem1 = document.getElementById("elemId");
  2. let style = window.getComputedStyle(elem1, null);

在 IE 中,对元素的 style 属性使用 currentStyle 属性获得 CSSStyleDeclaration 实例


添加样式(CSS)

语法

element.style.cssText = 'str';

在通过element.style.cssText设置css属性的时候,0要加上单位px。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注