前端JS总结(下)之DOM
目录
前言
DOM基础
DOM对象:
DOM结构:
节点类型:
nodeType属性:
解析:
注意:
获取元素:
获取方式:
getElementById():通过id获取元素
getElementsByTagName:通过标签名获取元素
补充:伪数组
getElementsByClassName():通过class获取元素
注意:
querySelector()和querySelectorAll():查找元素
getElementsByName():获取表单元素
补充:document.title和document.body
创建元素:
方法:
解析:
补充:创建文本节点
创建新元素属性:
插入元素:
方法:
解析:
例子:A.appendChild(B):
注意:插入元素都是在已有元素后面的。
补充:动态创建三层结构:
补充:A.insertBefore(new, ref)
删除元素:
前提:
方法:
场景:
DOM进阶
属性操作:
含义:
属性操作方式:
属性操作步骤:
注意区别:
获取HTML属性值:
语法:
解析:
步骤:
用途:
怎么做?
疑问:
解析:
注意:
补充:获取class属性
语法:
解析:
设置HTML属性值:
语法:
解析:
HTML属性操作:
对象方法:
getAttribute():获取属性值
语法:
等价于:
解析:
共同点:
区别:
补充:data属性
setAttribute():设置属性值
语法:
等价于:
区别:
removeAttribute():删除属性值
语法:
对象属性没有提供删除属性值的操作
总结:
场景:
hasAttribute():拥有属性值?
语法:
主要用途:
大总结
一句话总结:
CSS属性操作
CSS属性操作方式:
获取CSS属性值:
语法:
等价于:
解析:
补充:对象属性写法
等价于
设置CSS属性值:
方式:
style对象:
说明:
方式:
等价于:
CSSText属性:
语法:
解析:
注意:
小细节:
问题:
解析:
getComputedStyle():深度解析
补充:DOM遍历
解析:
理解:
注意:
查找父元素
语法:
查找子节点
子节点分类:
补充:
说明:
疑惑:
解释:
结论:
补充:其他两个
查找兄弟元素
兄弟元素分类:
解析:
补充:innerHTML、innerText
区别:
语法:
总结:
写在最后
前言
本篇还不是最后一篇,因为DOM操作写完都已经很多字,想想还是把事件操作放到下次吧。
为了行文流畅,这次换新的表现形式吧。
精读一本书,胜过看N个视频。
这次看的书是:
吴振杰的《Web前端开发精品课 HTML CSS JavaScript基础教程》
17年出版的。
不出意外的话,这估计就是压轴文章了~respect!
那么,咱们开始吧!
DOM基础
为了易于理解,这里先强调一下:
元素,也就是HTML标签,有人习惯称之为标签,但是也有人习惯称之为元素。
下面,统一将HTML标签称为元素,我也会反复提及的。
元素即HTML标签,元素即HTML标签,元素即HTML标签。
重要的事情说三遍!
DOM对象:
全称:Document Object Model,中文名即文档对象模型,W3C定义的
理解:DOM操作也就是元素操作;元素是啥?就是HTML标签。
DOM结构:
解析:DOM结构是一个树形结构,用树节点表示页面中的每一个HTML元素。
表现:html(head(title, meta), body(h1, p, div, span)),括号表示包含,head(title, meta)表示head标签包含title和meta标签的意思。
总结:这棵树也被称为DOM树,其中根元素是html标签;树形结构就像是家谱关系,每一个元素就是一个节点,每一个节点就是一个对象;我们在操作元素的时候,就是把这个元素看作是一个对象,然后使用对象的属性和方法进行操作。
为了易于理解,这里先强调一下:
下面提到的节点,就是DOM树上的一个树节点。
节点类型:
这里我们引入一个概念
nodeType属性:
元素节点的noteType属性值为1;
属性节点的noteType属性值为2;
文本节点的noteType属性值为3。
解析:
元素节点——也就是HTML标签,eg:div标签。
属性节点——也就是HTML标签中的属性,eg:input的type属性,就是一个属性节点。
文本节点——也就是HTML标签中的文本内容,eg:<p>这里就是文本节点</p>。
注意:
节点和元素是不一样的:节点包含元素,节点的范围不止元素。
nodeType属性值是一个数字,不是字符串。
一个元素就是一个节点,这个节点被称为“元素节点”。
属性节点和文本节点看似是元素节点的一部分,但实际上,它们之间的关系是兄弟关系,是各自独立的。
只有元素节点拥有子节点,属性和文本节点是没有子节点的——div下可以设置p标签,p标签就是这个div的子节点。
OK,铺垫到此为止,下面正式进入本文的第一个重点。
获取元素:
准确说,应该是获取元素节点,注意不是属性/文本节点,更不是节点。
获取方式:
getElementById():通过id获取元素
getElementsByTagName():通过标签名(如div、p这就是标签名)获取元素
getElementsByClassName():通过class获取元素
querySeletor()和querySeletorAll():查找元素,允许使用CSS选择器的语法获取元素
getElementByName():通过表单元素中的name属性获取元素
下面详细展开说说。
getElementById():通过id获取元素
通过id获取元素,这里的id就是CSS选择器中的id——这里在写的时候,无需带上#
使用:document.body.getElementById()
getElementsByTagName:通过标签名获取元素
通过标签名获取元素,类似于CSS选择器中的元素选择器。
获取所有同类标签元素,注意是Elements不是Element。
返回的结果是伪数组,我们可以通过数组下标的形式去单独控制某一个元素的操作。
获取方式:
精确获取、模糊获取。
前者会加上父元素,指定该父元素下面的所有同名标签;后者则是获取body元素下的所有同名标签。
或者,操作对象直接就是父元素,document.body/父元素.getElementsByTagName。
注意这里的document.父元素.getElementsByTagName。
补充:伪数组
也称为“类数组”。
顾名思义,并不是真正意义上的数组,只拥有数组中的length属性和下标index。
getElementsByClassName():通过class获取元素
通过class获取元素,类似于CSS中的class选择器——在这里写的时候,不需要加上.
获取所有具有同名class的元素,注意是Elements不是Element。
返回的结果是伪数组,我们可以通过数组下标的形式去单独控制某一个元素的操作。
注意:
以上三个方法,只有getElementsByTagName能够动态创建DOM元素,其他两个都不行。
因为你创建元素的时候,无法创建出带有id属性、class属性的元素。
querySelector()和querySelectorAll():查找元素
查找元素,允许使用CSS选择器的语法获取元素。
前者只能获取一个元素;后者能够获取多个符合条件的元素,返回一个伪数组。
跟id选择器的区别:
如果查找的元素是有id的,那么更加建议使用getElementById(),而不是querySelector()。
因为前者性能更好,效率更高。
getElementsByName():获取表单元素
一般来说,只有表单元素才有name属性,所以我们可以通过name属性来获取表单。
获取所有具有一致name属性值的表单元素,注意是Elements不是Element。
一般用于单选按钮和复选框。
补充:document.title和document.body
一个页面只有一个title,只有一个body,所以我们可以通过这种方式获取,性能更优。
我们后面会经常用到document.body的,这里需要注意。
学完如果获取元素之后,我们再来学习一下,如何创建元素。
创建元素:
方法:
document.createElement()
解析:
创建元素,又称动态DOM操作。
动态的意思:页面本身没有这个元素的,是我们后续使用JS创建并插入到页面中去的。
补充:创建文本节点
createTextNode()
用于创建一个文本节点,两者常用在一起创建一个有文本的HTML元素。
创建新元素属性:
前提:每一个元素节点都被看作是一个对象,所以我们可以使用对象属性的方式进行赋值。
eg:input.type = "text"
如果我们想要给这个标签添加class属性:input.className = "ipt",注意这里是className,而不是class。
学了创建元素,但是没学到插入元素,这怎么行呢?
插入元素:
在上文,我们学会了如何使用JS去创建一个元素。
但是实际上,如果我们只创建元素,页面上并不会将这个我们使用JS创建的元素表现出来,因为我们并没有把这个元素插入到页面中。
方法:
ele.appendChild(newEle)
解析:
ele指的是插入的HTML标签,所以我们要先获取该元素。
newEle指的是我们动态创建的新元素(新HTML标签)。
例子:A.appendChild(B):
表示往A元素标签内插入B元素,使得B成为A的末尾子节点。
注意:插入元素都是在已有元素后面的。
div标签已经有了一个p标签。
这个时候我再往div标签里面插入新创建的input标签,这个时候div的结构如下:
<div><p>这是一段话</p><input> // 新创建的标签是空标签,没有任何属性的 </div>
补充:动态创建三层结构:
如果有三层结构,则先完善最内层结构:先将孙元素添加到父元素中,然后父元素再添加到爷节点中。
补充:A.insertBefore(new, ref)
前提:A.appendChild()只能在A标签内的末尾位置添加。
情况:如果我想要在A标签内任意位置插入元素,该怎么办?
解决:A.insertBefor(new, ref)
解析:A表示要插入的HTML元素,需要我们事先获取;B表示我们动态创建的元素;ref表示参考元素,ref是A标签中的子元素,也需要我们事先获取。
删除元素:
我们有时候,需要某个元素隐藏,或者是从结构中删除,该怎么实现呢?
前者直接获取该元素,然后改变显示模式display: none即可。
前提:
该元素存在,元素不存在,你怎么删除?
获取到该元素的父元素,如果该元素没有父元素,那么它的父元素就是body。
方法:
A.removeChild(B)
使用:跟appendChild()方法一致
场景:
如果我全部删除A中的子元素,该怎么做?
直接删除A即可,这个时候就是:A的父元素.removeChild(A)
剩下还有两个不常用的方法:复制元素和替换元素,不讲。
这部分是DOM的基本操作,下面说一点高级玩法——DOM进阶。
DOM进阶
在DOM基础中,我们只说了元素操作,讲解了如何创建一个元素,如何插入一个元素,如何删除一个元素。
我们还提了一嘴文本节点、属性节点。
那么DOM进阶,我们开始吧。
属性操作:
含义:
HTML属性操作,指的是使用JS来操作一个元素的HTML属性。
上文说过:
我们在创建元素的时候,这个元素是没有任何属性的。
而上文给出的方法就是使用对象属性的方法给这些新创建的空白元素添加属性。
下面我们我们详细介绍一下。
属性操作方式:
对象属性
对象方法
属性操作步骤:
获取HTML属性值
设置HTML属性值
注意区别:
元素操作,操作的是“元素节点”;而属性操作,操作的是“属性节点”。
下面展开聊聊。
获取HTML属性值:
语法:
obj.attr
解析:
obj是元素名,是一个元素节点,是一个DOM对象,也就是div、p等标签。
attr是属性名,它是DOM对象的一个属性。
步骤:
先获取到元素节点,也就是DOM对象;
再使用对象属性的方法获取到该属性。
也就是说,属性操作离不开元素的获取。
用途:
用来获取输入框中的内容。
怎么做?
获取该输入框元素节点,比如A内容框;
A.value即可获取到输入框中的内容了。
疑问:
文本框中哪里存在value属性?怎么我们可以使用value属性来获取文本框中的内容?
解析:
在HTML中,对于单行文本框,都会默认给它加上value属性,所以我们就可以通过value属性去获取到输入框中的内容。
注意:
这个value是我们经常用到的,需要注意!
很多表单元素都有这个value值!!
补充:获取class属性
上文已经说过了,如果我们要设置class属性,用到的是className,而不是class。
这是因为class是一个保留字,保留字就不能当属性使用。
语法:
obj.className
解析:
跟上面如出一辙。
这个方法很少用。
设置HTML属性值:
其实上文的DOM基础也介绍过了。
语法:
obj.attr = "value"
解析:
obj和attr不解析了,直接回忆上面的即可。
value指的是你设置的新的属性值。
HTML属性操作:
上面说了这么多,都是使用对象属性的方式进行操作的,下面介绍一下使用对象方法的方式操作。
对象方法:
getAttribute()
setAttribute()
removeAttribute()
hasAttribute()
下面一个个展开细说。
getAttribute():获取属性值
语法:
obj.getAttribute("attr")
等价于:
obj.attr
解析:
obj获取到的DOM对象,attr指的是对象的属性值,下面都是一样的解析,就不再赘述了。
共同点:
两者都能够获取到静态HTML的属性值,也能够获取到动态DOM的属性值。
既然这两个获取到的结果是一致的,那么我直接使用obj.attr不就行了?
区别:
一些网页会使用到data自定义属性,这个时候,obj.attr就不起作用了。
补充:data属性
data属性是很常见的,比如说某米商城里面就巨多data属性。
data自定义属性一般是用来做CSS3动画开发的。
setAttribute():设置属性值
语法:
obj.setAttribute("attr", "值")
等价于:
obj.attr = "值"
区别:
只能使用setAttribute来设置自定义属性data的值。
removeAttribute():删除属性值
语法:
obj.removeAttribute("attr")
对象属性没有提供删除属性值的操作
只不过,我们可以使用点小手段:obj.attr="",让对象属性为空就行了(手动狗头)
总结:
很少用,基本都不怎么用。
场景:
结合class属性来“整体”控制元素的整体样式属性。
hasAttribute():拥有属性值?
注意这里的问号,说明这个是判断是否拥有某个属性值。
你就说,细不细吧?
语法:
obj.hasAttribute("attr")
主要用途:
在删除某个属性值之前,先看看是否拥有这个属性值;
存在,再去删除。
不存在,则不管。
大总结
DOM进阶主要介绍了属性操作的两种方式。
一种是对象属性方式,一种是对象方法方式。
一句话总结:
两者在大部分场景下都可以互相替代,只有在自定义属性data前,才只能使用对象方法操作。
既然都说了属性操作了,那怎么再说说CSS属性操作?
写到这里就已经5K多字了,所以说事件操作,咱们再单独开一篇吧。
CSS属性操作
跟属性操作几乎是一致的。
CSS属性操作方式:
获取CSS属性值
设置CSS属性值
下面,咱们一一展开细说。
获取CSS属性值:
语法:
getComputedStyle(obj).attr
等价于:
getComputedStyle(obj)["attr"]
解析:
obj是DOM对象,也就是元素节点,需要提前获取。
attr表示CSS属性名,这里的CSS属性名使用驼峰命名,因为很多CSS属性都是有-连接的。
下同,不再赘述。
补充:对象属性写法
obj.attr
等价于
obj[attr]
设置CSS属性值:
方式:
style对象
cssText()方法
这两个有什么区别呢?展开聊聊!
style对象:
说明:
使用style对象来设置,其实就是添加行内样式。
方式:
obj.style.attr = "值"
等价于:
obj.style["attr"] = "值"
如果值是复合属性,如:border、font等,就使用空格隔开即可。
这个方法好就好,就是有点麻烦:因为每次都只能设置一个属性。
那么有没有更好的方法?
CSSText属性:
语法:
obj.style.cssText = "值"
解析:
这里的值,可以是一连串的字符,也就是行内样式的写法。
也就是说,不需要驼峰命名了。
设置的依旧是行内样式。
注意:
很少使用cssText,更倾向于直接给DOM对象设置一个class属性值。
这样需要我们先写好class属性值。
小细节:
问题:
为什么不使用obj.style.attr或者obj.style.cssText来获取CSS属性值呢?
解析:
因为obj.style.attr只能获取DOM对象——即元素中的style属性的属性值。
是不是很绕?
细说一下:
obj.style是对象属性的方法,也就是获取obj对象里面的style属性。
这意味着这是行内样式,如果我用的是外部、头部style标签就不能获取了。
至于obj.cssText.attr就不存在这种写法了。
getComputedStyle():深度解析
get:获取
computed:计算后的
style:样式
合在一起就是获取计算后的样式,也就是不区分行内、头部、外部样式。
补充:DOM遍历
解析:
DOM遍历,就是查找元素。
理解:
获取到某个元素,我还想得到该元素的父元素、子元素,甚至是兄弟元素。
注意:
这里的兄弟元素是独立的,因为父和子元素都很好进行查找,但是兄弟元素就稍稍有点困难了。
下面展开说说。
查找父元素
语法:
obj.parentNode
解析:
obj就是获取到的DOM对象。
parentNode:parent——父/母的意思,Node——节点的意思;合起来就是父/母节点。
这里为了方便,所以就用了父元素一说。
查找子节点
子节点是比较难的,因为它有很多属性,这里将这些属性分开两组来说明。
子节点分类:
childNodes、firstChild、lastChild:获取所有子节点,包含元素节点和文本节点
children、firstElementChild、lastElementChild:获取所有子元素节点
这里又涉及到了节点了,如果忘记了,可以回去看看。
元素节点和文本节点区别是啥?nodeType是啥?还有记得的吗?
补充:
不管是哪种分类方式获取到的子节点总数(也就是每行的第一个),都是一个伪数组,我们可以.length属性来获得它们的长度。
说明:
为了更好地说明,举个例子。
步骤:
我们使用getElementById()的方式获取ul;
再使用上面的两种方式获取到childNode和children;
使用console.log()分别打印它们的长度。
ul代码:
<ul id="list"><li>HTML</li><li>CSS</li><li>JavaScript</li>
</ul>
猜一下,这里面childNode和children的长度是不是一样的?
实际上,这两个的值是不一样的。
childNode.length=7
children.length=3
疑惑:
这里不就是三个li吗?
为什么childNode是7个?而不是三个?
解释:
因为childNode包括文本节点、元素节点,而在这里,每一次换行都代表着一个空白的文本节点,JS会把这些空白节点当做是文本节点处理。
而children不包括文本节点,只包含元素节点。
结论:
应该使用下面那类,而不是上面那类。
补充:其他两个
firstElementChild和lastElementChild前者是用来获取第一个子元素的,后者是用来获取最后一个的。没啥好说的。
查找兄弟元素
同样地,分为两个分类:
兄弟元素分类:
previousSibling、nextSibling:可能查出来的是文本节点,所以不用
previousELementSibling、nextELementSibling:只会查出元素节点
解析:
previous:前者
next:后者
ELement:元素
不多说,跟上面理解是差不多的。
补充:innerHTML、innerText
在前面中,如果想要创建一个动态的DOM元素,都是将元素节点、属性节点、文本节点一个一个使用appendChild方法拼凑在一起,非常地不方便。
又没有更好的办法?
那就是innerHTML和innerText
区别:
innerHTML中HTML是全大写的,可以快速地获取、设置一个元素的内部元素;
innerText是符合驼峰命名的,可以快速地获取、设置一个元素的内部文本。
语法:
document.body/父元素.innerHTML = "值"
document.父元素.innerText = "文本内容"
总结:
父元素一般要提前获取。
innerText的父元素很少是body。
写在最后
谢天谢地,终于是写完这一章节了。
写完已经是很晚了,写完就已经是7.7K字了。
这一章真的很需要时间去理解,记忆是没有用的,理解了才能更好地掌握。
下期,咱们再说事件操作吧。
那么,我们下期再见!