DOM+JavaScript操作DOM常用的API

1,915 阅读12分钟

DOM节点

  • 文档本身就是一个文档节点,nodeType=9
  • 所有html元素都是元素节点,nodeType=1
  • 所有HTML属性都是属性节点,nodeType=2
  • 插入到 HTML 元素文本是文本节点,nodeType=3
  • 注释是注释节点,nodeType=8

元素element节点

特性:

1、nodeType=1
2、nodeName和tagName都是返回标签名的大写形式
3、nodeValue=null
4、父节点可能是document / 元素节点
5、子节点可能是元素节点、注释节点、文本节点
6、获取元素的子节点列表是:element.childNodes
7、获取元素的子元素列表是:element.children(只包含元素节点)

element.childNodes属性

<body>
	
<p id="demo">单击“按钮”获取有关身体元素的子节点的信息</p>
<button onclick="myFunction()">点我</button>
<script>
function myFunction(){
	var txt="";
	var c=document.body.childNodes;
	for (i=0; i<c.length; i++){
		txt=txt + c[i].nodeName + "<br>";
	};
	var x=document.getElementById("demo");  
	x.innerHTML=txt;
}
</script>
<p><strong>注意:</strong> 空格内元素看作是文本,文本是节点。</p>

</body>

结果:

#text
P
#text
BUTTON
#text
SCRIPT
#text
P
#text

element.children属性

<body>

<p>点击按钮获取 body 元素子元素的标签名。</p>

<button onclick="myFunction()">点我</button>

<p id="demo"></p>

<script>
function myFunction() {
    var c = document.body.children;
    var txt = "";
    var i;
    for (i = 0; i < c.length; i++) {
        txt = txt + c[i].tagName + "<br>";
    }

    document.getElementById("demo").innerHTML = txt;
}
</script>

</body>

结果

P
BUTTON
P
SCRIPT

属性节点

特性:

1、nodeType=2
获取元素上的所有属性:element.attributes,返回的是一个集合,含length属性
获取元素上的某个属性:element.getAttribute('attributeName') / element.getAttributeNode('attributeName').value
获取元素上的某个属性节点:element.getAttributeNode('attributeName'),返回的是一个属性节点对象
判断元素有没有某个属性:element.hasAttribute(attributeName)
判断元素是否含有任何属性:element.hasAttributes()
设置或修改已有属性:element.setAttribute(attributeName, attributeValue)
移除节点的某个属性:element.removeAttribute(attributeName)

Text节点

特性:

1、nodeType=3
2、nodeName为#text
3、nodeValue为文本内容
4、父节点是元素节点
5、没有子节点

文档节点

特性:

1、nodeType=9
2、document.nodeName='#document'
3、父元素:document.parentElement=null
4、父节点:document.parentNode=null
5、nodeValue=null
6、document.documentElement.nodeName='HTML'

DocumentFragment文档碎片 -- 轻量级节点

特性

1、nodeType=11
2、nodeName的值为#document-fragment.
3、没有父节点
4、被作为一个轻量版的 Document 使用
5、不是真实DOM树的一部分,它的变化不会触发 DOM 树的(重新渲染) ,且不会导致性能等问题。
6、创建文档碎片:document.createDocumentFragment()
7、文档碎片的实例的属性都继承了Node 的属性
<body>
  <ul id="ul"></ul>
</body>
<script>
(function(){
    var ul=document.getElementById('ul');
      var fragment=document.createDocumentFragment();
      for(var i=0;i<5;i++){
          var li=document.createElement('li');
          li.textContent=`第${i+1}项`;    //文本内容
          /**
           * 给li节点添加文本内容也可以写成:
           * var text=document.createTextNode('我是文本节点');
           * li.appendChild(text)
           */
          fragment.appendChild(li)
      }
      ul.appendChild(fragment)
})
</script>

创建节点API

createElement(存在的标签名 / 自定义的标签名)

语法:document.createElement(存在的标签名 / 自定义的标签名);

例子:

var ele=document.createElement('div');
ele.id='div';
ele.style='color:red';
ele.textContent='新创建的节点';
document.body.appendChild(ele)      //将节点添加到文档中

createTextNode(文本内容)

语法:document.createTextNode(文本内容");

用来创建文本节点,参数是文本节点的nodeValue

var node = document.createTextNode("我是文本节点");
document.body.appendChild(node);

cloneNode()

语法:var clone=node.cloneNode(isDeep)

node是要被克隆的节点,clone是克隆生成的副本节点,isDeep参数可选,表示是否采用深度克隆,如果为true,则node的所有后代节点也会被克隆,否则只会克隆node本身。

注意:

1、如果复制的节点含有id,则其副本同样会包含该id,由于id具有唯一性,所以在复制节点后必须要修改其id
2、如果复制的节点通过js的addEventListener或者onclick绑定事件,则副本节点不会绑定该事件
3、如果复制的节点是通过内联方式绑定事件:<div onclick="showParent()"></div>,这样的话,副本节点同样会触发事件。
4、cloneNode创建的节点只是游离有HTML文档外的节点,要调用appendChild方法才能添加到文档树中

例子:

<body>
  <div id="parent">
    我是父元素的文本
    <br/>
    <span>
        我是子元素
    </span>
  </div>
  <button id="btnCopy">复制</button>
</body>
<script>
  var parent = document.getElementById("parent");
  document.getElementById("btnCopy").onclick = function(){
  	var parent2 = parent.cloneNode(true);
  	parent2.id = "parent2";
  	document.body.appendChild(parent2);
  }
</script>

修改页面节点API

appendChild()

语法:parent.appendChild(child)

说明:参数child将会作为父节点parent的最后一个子节点

注意:

如果被添加的节点已在页面存在,则会在原本所在的位置移除该节点,然后添加到新位置
如果child上绑定了事件,则被移动时,它依然绑定着该事件

insertBefore()

语法:parent.insertBefore(newNode,refNode)

newNode是要添加的节点,refNode是参照的节点,newNode将添加在refNode节点前面

注意:

refNode必传,否则出错
如果refNode传入null或undefined,则会将newNode插入父节点的子元素的末尾,相当于appendChild

removeChild()

语法:var deletedChild = parent.removeChild(node);

删除指定节点并返回,deletedChild指向被删除节点node的引用,
被删除的节点仍然存在于内存中,可以对其进行下一步操作。

如果node不是其子节点,则会报错

确保移除的是其子节点:

if(node.parentNode){
    node.parentNode.removeChild(node);
}

replaceChild()

语法:parent.replaceChild(newChild,oldChild)

newChild是替换的节点,可以是新的节点,也可以是页面上的节点,如果是页面上的节点,则其将被转移到新的位置 oldChild是被替换的节点

修改页面节点API总结

这四个接口api

  • 不管是新增还是替换节点,如果新增或替换的节点是原本存在页面上的,则其原来位置的节点将被移除,也就是说同一个节点不能存在于页面的多个位置
  • 节点本身绑定的事件会不会消失,会一直保留着。

节点关系

父关系

  • ele.parentNode:返回元素的父节点
  • ele.parentElement:返回元素的父节点,该节点必须是元素类型,如果没有则返回null

子关系

  • ele.childNodes:返回一个即时的NodeList,元素的子节点列表,可能包含文本节点、注释节点、元素节点
  • ele.children:返回一个即时的HTMLCollection集合,非数组。子节点都是元素节点。
  • ele.firstChild:返回第一个子节点,如果无子节点则返回null
  • ele.lastChild:返回最后一个子节点
  • ele.hasChildNodes:元素是否有子节点

兄弟关系

  • ele.previousSibling:返回节点的前一个兄弟节点,可能会得到一个空白符的文本节点
  • ele.previousElementSibling:前一个兄弟元素节点
  • ele.nextSibling:返回节点的下一个兄弟节点,可能会得到一个空白符的文本节点
  • ele.nextElementSibling:下一个兄弟元素节点

节点查询型API

document.getElementById()

语法: var element = document.getElementById(id);

getElementsByClassName()

语法: var elements = document.getElementsByClassName(names);

  • elements是一个实时集合,类数组,包含了找到的所有元素
  • 返回的集合是动态的,意味着它会自动更新来保持和DOM树的同步,而不用再次调用
  • names是字符串,表示匹配的类目列表。多个类名以空格隔开
  • getElementsByClassName()可以在任何元素调用,调用者将作为父节点,在其子节点中查找,前提是这个元素是一个对象而不是集合。
 var elements = document.getElementById('main').getElementsByClassName('test');
 //getElementById()获取到的是一个对象
  • 可以对任意的HTMLCollection 使用Array.prototype的方法,调用时传递HTMLCollection 作为方法的参数。这里我们将查找到所有class为'test'的div元素:
var testElements = document.getElementsByClassName('test');
  var testDivs = Array.prototype.filter.call(testElements, function(testElement){
    return testElement.nodeName === 'DIV';;
  });

getElementsByTagName()

语法:var elements = document.getElementsByTagName(name);

  • elements:包括所有给定标签名称的元素的HTML集合HTMLCollection
  • 返回的 HTML集合是动态的, 意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用document.getElementsByTagName()
  • 如果不存在指定的标签,该接口返回的不是null,而是一个空的HTMLCollection
  • name是一个代表元素的名称的字符串。特殊字符 "*" 代表了所有元素。
  • getElementsByTagName()和getElementsByClassName一样,可以被任何元素调用,前提是这个元素是一个对象而不是集合。

document.getElementsByName()

语法:var elements = document.getElementsByName(name)

注意:在HTML元素中,并不是所有元素都有name属性,比如div是没有name属性的,但是如果强制设置div的name属性,它也是可以被查找到的

document.querySelector()

语法:var element = document.querySelector(selectors);

  • document.querySelector返回第一个匹配到的元素,如果没有匹配的元素,则返回null
  • 采用深度优先搜索,在前面的优先被搜索到,即使是很深的级别
<body>
  <div>
    <div>
      <span class="test">第三级的span</span>	
    </div>
  </div>
  <div class="test">			
    同级的第二个div
  </div>
  <input type="button" id="btnGet" value="获取test元素" />
</body>
<script>
  document.getElementById("btnGet").addEventListener("click",function(){
    var element = document.querySelector(".test");
    alert(element.textContent);     //第三级的span
  })
</script>

document.querySelectorAll()

语法:var elementList = document.querySelectorAll(selectors);

  • elementList是一个静态的NodeList类型的对象,即结果不会随着文档树的变化而变化
  • selectors是一个由逗号连接的包含一个或多个CSS选择器的字符串
  • querySelectorAll也是通过深度优先搜索,搜索的元素顺序和选择器的顺序无关

例子:

var matches = document.querySelectorAll("div.note, div.alert");

返回一个文档中所有的class为"note"或者"alert"的div元素

元素样式型

window.getComputedStyle()

语法:var CSSStyleDeclaration=window.getComputedStyle(ele [,pseudo-element])

  • 第一个元素就是想要获取样式的目标元素
  • 第二个元素不是必须的,当不查询伪类元素的时候可以忽略或者传入 null。(比如a:hover)
  • IE8使用document.currentStyle
<body id="myid" class="mystyle">
<div id="elem-container">dummy</div>
<div id="output"></div>

<script>
    function getTheStyle(){
        let elem = document.getElementById("elem-container");
        let theCSSprop = window.getComputedStyle(elem,null).height;//或者['height'],得到100px
        document.getElementById("output").innerHTML = theCSSprop;
    }
    getTheStyle();
</script>

window.getComputedStyle()与element.style的区别

  • getComputedStyle是函数类型
  • getComputedStyle()获取的是指定元素的最终样式,包括内联样式、嵌入样式、外部样式
  • style获取的是元素的内联样式,即在标签上的style属性
  • getComputedStyle()是只读的,style返回的CSSStyleDeclaration对象是只读的,但style属性的属性能够用来设置样式。所以用getComputedStyle()来读取样式,通过 element.style.属性名 修改样式

直接修改元素样式

例子:

//设置一个样式
ele.style.color='red';
//在单个语句设置多个样式
ele.style.cssText='color:blue; font-size:50px'
//或者
ele.seAttribute('style','color:blue; font-size:50px')

动态添加样式规则

例子:

var style=document.createElement('style');
style.textContent="body{color:red} div{width:200px; height:200px; border:1px solid #000}"

document.head.appendChild(style)

getBoundingClientRect

语法: var clientRect = element.getBoundingClientRect();

用于获取某个元素相对于视窗的位置的集合,集合有top、left、right、bottom、width、height、x、y属性

  • getBoundingClientRect().top:获取元素的上边距离视窗顶部的距离
  • getBoundingClientRect().bottom:获取元素的下边距离视窗底部的距离
  • getBoundingClientRect().left:获取元素的左边距离视窗的左边的距离
  • getBoundingClientRect().right:获取元素的右边距离视窗的左边的距离
  • getBoundingClientRect().width:获取元素的offsetWidth(width+左右padding+border),无单位
  • getBoundingClientRect().height:获取元素的offsetHeight
  • getBoundingClientRect().x:获取元素左上角的x坐标,无单位
  • getBoundingClientRect().y:获取元素左上角的y坐标,无单位

offsetWidth、clientWidth、scrollWidth属性

元素的属性

element.clientWidth:width+左右padding
element.clientHeight:height+上下padding

element.offsetWidth:width+左右padding+左右border-width
element.offsetHeight:height+上下padding+上下border-width

element.offsetTop:当前元素的上边框外边缘到最近的有定位的父元素(除了static)的内壁的距离,即padding
element.offsetLeft:同理

element.clientTop:元素顶部边框的宽度,border-width
element.clientLeft:同理

element.scrollWidth:元素内容真实的宽度,内容不超出盒子高度时为盒子的clientWidth
element.scrollHeight:同理

Window的属性

window.innerHeight:获取浏览器可视区的高度,随着浏览器窗口大小的变化而变化
window.innerWidth:同理

document

document.documentElement.offsetWidth:整个文档的宽度(包含body的margin)
document.body.offsetWidth:整个文档的宽度(不包含body的margin)

都是无单位,通过element.style获取的就有单位

DOM中property和attributes的区别

<input type="text" id="input" self="name">
let dom=document.getElementById('input');
   console.log(dom.self);   //undefined
   console.log(dom.attributes.self);    //self='name'
   console.log(typeof dom.attributes);  //‘object’
   console.log(dom.attributes.id);  //id='input'
   console.log(typeof dom.attributes.id);   //"object"
  • property:每一个DOM对象都会有它默认的基本属性,当创建dom对象时,就会含有默认的基本属性,比如input有id、class、value、type、attributes、nodeName、nodeType、className等基本属性,这些就是dom的property
  • attributes:是dom对象的一个属性,是一个property,所以attributes是property的子集。它是对象类型,它保存了html标签里定义的属性,每个属性都是attr对象类型,当访问attributes中的属性时,返回 属性名=属性值
console.log(dom.value); //没有出错,只是返回空字符串
  • 访问property:就算没有在html标签上定义的属性,也能访问
dom.setAttribute('id','new id');
console.log(dom.attributes.id);      //id='new id'
console.log(dom.getAttribute('id')); //new id
  • 访问attribute
    • 通过attributes访问属性会得到attr类型的对象
    • 通过getAttribute函数会得到属性值
dom.value='new val';
   console.log(dom.value);  //new val
   console.log(dom.attributes.value);   //value='val'

   dom.id='dom';
   console.log(dom.id); //dom
   console.log(dom.attributes.id);  //id='dom' 
  • 修改property:
    • 对于input的value,修改后的值是无法同步到attributes的同名属性上
    • 对于input的id,修改后的值能同步到attributes的同名属性上,即双向数据绑定
dom.attributes.value.value='new val'    //修改的是value的属性值,也可以value.nodeValue
console.log(dom.value); //new val

dom.setAttribute('id','new id');        //也可以这样修改属性值
console.log(dom.id);    //new id

*****注意不能这样修改attributes的属性******
dom.attributes.value='new val'
console.log(dom.attributes.value);   //'new val'
console.log(dom.value);  //val,没有变化

dom.attributes.id='new id'
console.log(dom.attributes.id);  //'new id'
console.log(dom.id);     //input,没有变化
  • 修改attributes的属性
    • 修改后的属性值会同步到同名的property,所以attribute --> property

总结:

  • property指的是dom对象上的基本默认属性,包括attributes
  • attributes是一个对象,保存了html标签上对于的属性
  • attributes上的属性会同步到同名的property,反过来不一定
  • 对于value、class是单项数据绑定,attribute --> property
  • 对于id是双向数据绑定,property <--> attribute
  • 资料

参考

原文