原生JavaScript DOM编程技术
节点概览
DOM其实就是一个由JavaScript节点对象组成的层次结构/树。
节点对象类型
ELEMENT_NODE
(元素节点)ATTRIBUTE_NODE
(属性节点)TEXT_NODE
(文本节点,空白符和换行符也属于文本节点)DOCUMENT_NODE
(window.document)DOCUMENT_TYPE_NODE
(文档类型节点,<!DOCTYPE html>)DOCUMENT_FRAGMENT_NODE
(HTML片段节点)
在浏览器中,这些常量都是NODE对象的属性,他们的值是数值,这些数值是映射到某一个特定的节点的。节点对象类型数值映射
E A T,1 2 3,D D D,9 10 11特定的节点类型是由特定的JavaScript结口/构造函数构造出来的。
节点对象的继承,DOM中每个节点对象都从NODE继承属性和方法。
如: Object > Node > Elment > HTMLElement识别节点的类型和名称,使用nodeType和nodeName,注意nodeType返回的数值映射,nodeName返回节点的大写标记名称。
使用
nodeValue
获取Text和Comment节点实际文本字符串。使用JavaScript方法来创建元素节点和文本节点:
createElement(nodename) createTextNode("str")
使用以下属性来创建文本节点和元素节点并添加到DOM(获取同理):
innerHTML outerHTML(会把选取的元素也一起修改掉)
textContent(标准) innerText(已经是标准) outerText(已经是标准,会把选取的元素也一起修改掉)
使用
appendChild(Node) insertBefore(newNode,oldNode)
来想DOM中插入节点对象使用
parentNode.removeChild(Node) parentNode.replaceChild(newNode,oldNode)
来移除与替换节点。使用cloneNode(Deep?)复制节点,默认是false,浅克隆。浅克隆只会赋值当前节点而不会复制所有子节点。true,深克隆则会复制所有子节点。
节点集合:NodeList和HTMLCollection,这种集合都是类数组对象,可以是实时的,也可以是静态的。有length可以表明这个集合有多少个节点。
将节点集合转换为JS数组的方法:
1
2
3var linkList = document.querySelectAll("a");
// 只要是能返回一个新的数组的原型方法都可以,使用call
var linkArray = Array.prototype.slice.call(linkList);遍历DOM的节点,用以下属性能遍历DOM来获取其他节点引用。
parentNode firstChild lastChild nextSibling previousSibling childNodes
以上的属性不仅仅是遍历元素节点,还会遍历文本和注释节点。但是我们大多时候都是只需要元素节点,那么可以通过以下属性来只获取元素节点,同时忽略文本和注释节点。
firstElementChild lastELementChild nextElementChild previousElementChild children parentElement
使用
contains(Node)
验证节点在DOM中的位置。使用
isEqualNode()
判断两个节点是否相等,使用===
判断两个节点是否相同。
文档节点
文档节点就是
window.document
,表示当前的文档,是HTMLDocument,DOCUMENT_NODE
,9。获取HTML DOCUMENT的通用信息:
document.title document.URL[当前的连接] document.referrer[也就是上一个的连接] document.lastModified document.compatMode[CSS1Compat是标准模式,BackCompat是怪异模式]
快速访问属性:
document.doctype[<!DOCTYPE>] document.docmentElement[<html>] docuemnt.head[<head>] document.body[<body>]
获取文档当前聚焦/激活节点的引用
document.activeElement
document.defaultView
是个全局对象的引用,在浏览器环境中就是window
元素节点
获取元素的标签名
nodeName\tagName
返回的都是大写的标签名。获取元素的属性集合:
attributes
获取、设置、移除元素的属性值
getAttribute() setAttribute() removeAttribute()
验证元素是否有某一特定属性:
hasAttibute()
获取类属性值列表:
classList
添加和移除以及变换类属性中的部分值:(不要使用className了)
classList.add() classList.remove() classList.toggle()[有这类则删除,没有则添加的意思]
获取与设置data-的属性:
dataset
【注意:获取到的data-\属性时驼峰命名的。】
元素节点选取
最常用的方法:
querySelector(css选择器) getElementbyId()
选取一个元素节点集合列表:
querySelectorAll() getElementByTagName() queryElementsByClassName() children()
预定义的元素节点选取
document.all document.forms document.images document.link document.scripts document.styleSheets
元素几点几何量和滚动几何量
offsetLeft,offsetTop
,分别对应左,上的相对距离。offsetParent
,是offsetLeft,offsetTop
作为相对距离的对象。getBoundingClientRect()
,获取元素相对于视区的top,right,bottom及left
边沿偏移量以及通过获取元素的尺寸width、height
(或者offsetWidth\offsetHeight),包含边框。clientHeight、clientWidth
可以获得元素不包含边框的尺寸。elementFromPoint(px,px)
获取视口中某一特定点上最顶层(覆盖层)的元素。(z-index)scrollHeight
和scrollWidth
获取滚动元素的尺寸大小,指的就是那些可以有滚动条元素的尺寸大小scrollTop和scrollLeft
获取并设置上左边滚动的距离。(滚动整个文档是html元素)scrollIntoView()
滚动元素到视口。也就是类似超链接,点击就能到达元素的位置。
元素节点内联样式
每个HTML元素都有一个style属性,可以用来出入针对该元素的内联CSS属性。
通过操作style对象的属性(也就是css样式属性),可以设置、获取、以及移除单个内联CSS属性。
【style对象中的属性名不含CSS属性中常见的横线。而是使用了驼峰体,比如font-size=》fontSize,如果css属性名刚好是JavaScript关键字,那么JavaScript css属性名需要加css前缀,比如:float=cssFloat,另外对于任何需要度量单位的css属性都要记得添加上,否则浏览器无视该属性】style对象上还有三个对css属性进行操作的属性
setProperty(),getPropertyValue(),removeProperty()
通过
cssText
属性,和getAttribute,stAttribute,remoteAttribute
可以获取和设置以及移除单个元素的所有内联CSS属性style属性值只包含内联的CSS定义的属性,并不是计算后的样式(实际样式)。可以使用
getComputedStyle(ElementName)
来获取元素计算后的样式(就是实际的样式)。这个方法返回的对象是只读的。使用
setAttribute()和classList.add()
配合class和id属性应用css属性,使用remoteAttribute()和classList.remote()
就可以移除这些属性。
文本节点
当HTML文档被解析时,在HTML页面中与元素混杂在一起的文本就会被转换为文本节点。
记住一点:不管通过浏览器还是编程方式,空白符与文本字符都会创建文本节点。因为空白符也是字符(换行符同样)
创建和注入文本节点:
creatTextNode()、appendChild()
,还可以在创建一个元素的同时appendChild一个文本节点。使用.data或nodeValue获取文本节点值。两者都返回Text节点中的文本。length属性可以访问节点的文本长度。
对文本节点进行添加,删除,插入,替换,获取某一段的文本.
appendData() deleteData(start,deleteLength) insertData(start,string) replaceData(start,Length) subStringData(start,length)
【不包含start位置的字符】使用
textContent
移除文本标记并返回所有子文本节点也可以移除所有文本节点。textContent
和innerText
的区别:
innerText知道CSS,如果你有隐藏的内容,会忽略它。但textContent不会。
innerText是非标准,textContent是标准的。使用normalize合并兄弟文本节点成单个文本节点。【只是相当于减少了文本节点,并无实际变化】
使用splitTxt()分割文本节点。【只是相当于增多了文本节点,并无实际变化】
DocumentFragment节点
把
DocumentFragment
看做是一个空的文档模板,行为与实时DOM树相同,但它只在内存中存在,并且它的子节点可以很简单在内存中操作,而后附加到实时DOM中。【fragment的意思就是片段的意思】使用
createDocumentFragment()
方法创建DocumentFragment。使用文档片段在内存中创建节点结构,注入该文档片段到实时DOM中,是高效的。
【为什么是高效的:自身不会被添加;可以包含任意类型的节点。】同样直接appendChild,就可以添加DocumentFragment到实时DOM中。
CSS样式表和CSS规则
- 通过HTMLLinkElement节点引入外部样式表,通过HTMLStyleElment定义内联样式表。
2.通过document.styleSheets
访问DOM中所有的样式表(包含了style以及link),这是一个实时类数组对象。或者通过获取style元素,再使用.sheet获取该CSSstyle对象。
- 使用
cssRules
获取样式表的样式规则,这是一个类数组对象。使用cssText查看该条规则的内容。
DOM中的JavaScript
script三个可选属性:
async、defer、src
默认情况下,DOM在解析时遇到
script
元素的时候,它将停止解析文档。阻止任何进一步的渲染和下载,并执行JavaScript。因此这个行为是阻塞的,并且不允许并行执行DOM或者执行JavaScript,所以这个行为是同步的。如果是外部JS那么阻塞更加严重,需要先下载完再解析。比如1
2
3
4
5
6
7<!-- 停止文档解析,阻塞文档解析,加载JS,执行js,然后继续文档的解析 -->
<script src="./index.js">
</script>
<!-- 停止文档解析,阻塞文档解析,执行js,然后继续文档的解析 -->
<script>
console.log('hi');
</script>script
元素默认的阻塞同步天性让HTML网页的性能与视觉渲染的感知性能有显著的影响。比如,你如果在HTML的头部加入了大量的JavaScript代码,那么在JavaScript执行完毕之前,你的页面将会是一片空白!再比如,如果你在HTML头部需要获取一个元素会得到null
,如果直接输出它的属性则会错误,因为DOM结构已经被该JS代码阻塞了。使用defer推迟外部脚本的下载和执行,直到浏览器完成解析并关闭标签,注意只能是外部脚本,对内联JS是无效的。【按规范,设置了defer的外部js应该以在文档中的顺序去执行】
使用async异步下载并执行外部JavaScript文件,async属性可以覆盖script元素在web浏览器构造DOM时默认的顺序、阻塞加载的天性。通过使用这个属性,告诉浏览器不要阻塞页面的构建(包括DOM解析,下载其他图片、样式等资源)并且放弃顺序加载。【使用async属性,js文件是会被加载的也会被执行,只不过不会阻塞页面渲染,一边下载并执行js一遍渲染页面。js文件哪个先下载完就先执行】(async > defer)
动态script元素强制异步加载并解析外部JavaScript,其实就是动态创建script元素,并且添加src。
script元素支持一个加载时间处理程序,onload,可以在js加载并执行完成的时候写一个回调函数。
获取DOM的script列表。文档对象上可用document.scripts属性,返回一个当前DOM中所有脚本的列表。(包含内联js、外部js),使用src属性可以获得js的url。
DOM事件
事件,就是用户和页面进行交互的某些情景,比如UI状态、页面加载完毕、XHR请求完成。
对DOM添加事件的三种方法:HTML内联属性事件处理程序,属性事件处理程序,addEventListener().[注意,属性事件处理程序只能给事件一个处理程序,而add可以无数个。]
DOM事件类型:
用户界面事件(resize、scroll、context menu)、
聚焦事件(blur,focus,focusin,focusout)、
表单事件(change、reset、submit、select)、
鼠标事件(click、dbclick、mousedown、mouseenter【不冒泡类似mouseover】、mouseleave【不冒泡类似mouseout】、mouseout、mousemove、mouseup、mouseover)
滚轮事件(mousewheel)
键盘事件(keydown、keypress、keyup)
触控事件(touchstar、touchend、touchmove、touchenter、touchleave、touchcancel)
文档相关事件(readystatechange)
拖拽事件(drag、dragstart、dragend、dragenter、dragleave、dragover、drop)事件流程,当某个事件发生时,事件在DOM中流动和传播,在其他节点上也会触发相同的事件,这个事件流程可以被编写为捕捉阶段(从DOM树主干到分支,也就是从事件目标父元素到事件目标元素)还是冒泡阶段(从DOM树分支到主干,跟上相反)。一般而言,都假定事件是在冒泡阶段触发的。浏览器也能支持捕捉阶段。
addEventListener(event,handler,boolean)
,其中布尔值代表是捕捉事件还是冒泡事件,默认或者省略是flase,冒泡事件。true则是捕捉事件。移除事件监听函数,使用
removeEventListener(event,handler,boolean)
,注意,这里的handler函数必须是使用函数引用方式绑定的,否则是不能移除。因为使用匿名函数会导致两个不同的函数。使用
addEventListener
时的监听函数this指向事件监听函数所绑定的元素。【注意!这里的this总是引用事件处理附加到的元素,也就是event.currentTarget,而不是事件发生的元素,也就是event.target】使用
preventDefault()
来撤销浏览器默认事件,比如点击链接会跳转,但可以使用这个方法来取消这个跳转。事件监听函数体末尾提供return false;
有同样的效果。使用
stopPropagation()
来终止事件传播。无论是捕捉事件还是冒泡事件都可以终止事件的传播。使用
stopImmediatePropagation()
终止事件传播和相同目标上的相同其他事件。自定义事件:
CustomEvent(name,{}) addEventListener
1
2
3
4
5
6
7
8
9aElm.addEventListener("janro", function(e) {
alert("你成功地自定义事件!~");
});
var event = new CustomEvent("janro", {
bubbles: true,
cancelable: false
});
aElm.dispatchEvent(event);模拟/触发鼠标事件。略。
事件委托:利用事件流程的编程方法,利用单个事件监听处理多个事件目标,好处,减少暑假能处理程序,减少DOM操作。减少函数对象。提升性能。参考文章:事件委托或代理;