严格意义上讲,我们在 JavaScript 语法阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。
ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。
DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个对象下包含了许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态更新,为实现网页特效以及用户交互提供技术支撑。
简言之 DOM 是用来动态修改 HTML 的,其目的是开发网页特效及用户交互。
观察一个小例子:
上述的例子中当用户分分别点击【开始】或【结束】按钮后,通过右侧调试窗口可以观察到 html 标签的内容在不断的发生改变,这便是通过 DOM 实现的。
DOM是浏览器提供的专门用来操纵网页内容的功能。
我们有以下HTML代码,现在将其转化为DOM树结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标题</title>
</head>
<body>
文本
<a href="">链接名</a>
<div id="" class="">文本</div>
</body>
</html>
如下图所示:
在HTML文件中,我们的总文档为document,内部有HTML大标签,HTML内有head和body;在head内又有meta,title等等。
这样一直深入,我们就得到了标签之间的层级关系,最后用树状结构展示出来,我们就得到了DOM树。
将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。
节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。
- 【元素节点】其实就是 HTML 标签,如上图中
head
、div
、body
等都属于元素节点。- 【属性节点】是指 HTML 标签中的属性,如上图中
a
标签的href
属性、div
标签的class
属性。- 【文本节点】是指 HTML 标签的文字内容,如
title
标签中的文字。- 【根节点】特指
html
标签。- 其它…
要注意,节点是在DOM树中的概念,而对象是JavaScript中的概念,简单来说,这个DOM树转化为DOM对象后,我们就可以在JavaScript中操作对象,从而操纵网页标签。
DOM对象是浏览器根据HTML标签生成的JavaScript对象
我们在讲解DOM节点时已经大致提到,DOM是通过将HTML的标签转化为JavaScript对象从而实现的对HTML操控。
document
是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法。
我们先讲解一个熟悉的代码:
document.write('Hello World!');
这是一个在语法阶段就已经学习过的向浏览器页面写入的语句,观察其结构开头是document
,后面有一个点号.
紧接着的是一个函数write()
。这串代码的突破点在于这个点号.
,点号是JavaScript的对象访问使用的操作符,也就是说document
是一个对象,这串代码正在访问document
对象中的write()
方法(即函数)。
而这个 document.write('Hello World!');
过程,就是向网页中写入字符串’Hello World!',这是一个通过JavaScript操作网页的过程,我们使用了document
对象。
由此可见:
document
确实是一个对象
我们可以通过document
对象操控网页
刚刚的document
对象,我们是可以直接使用的,而在DOM树中,不止document
一个对象,对象的某个熟悉也可以是对象,比如document
下面的html,body,head都是一个对象。当我们想要操纵这些小对象时,我们要先获取到这个对象。
我们有两种方式可以通过CSS的选择器来获取对象分别是querySelector()和querySelectorAll():
这两个方式,都是函数,我们需要向函数传参,参数就是CSS的选择器。在此,选择器既可以是基本选择器,也可以是复合选择器。由于复合选择器可以非常精确的定位的元素,通过选择器获取到的DOM对象也会十分精准,这是获取对象最好用,最主要的方式。
querySelector
语法:
const/let/var 变量 = document.querySelector('CSS选择器')
既然是DOM树的内容,毫无疑问要通过document
对象出发,所以querySelector也是一个document
对象的方法。这个querySelector会返回一个对象,也就是符合我们条件的对象,比如document.querySelector('.box p')
这串代码就是获得了类名为box的父级元素里面的p标签。
但是要注意,querySelector只能获取第一个符合条件的元素。
当我们获取到对象后,就要用变量来接收它,此处用const/let/var
三种关键字来定义变量都可以,但是由于我们的对象一般是不修改的,最好使用const。
这个方式只能得到第一个符合条件的元素,我们第二种方法就是来获取所有符合条件的元素的:
querySelectorAll
语法:
const/let/var 变量 = document.querySelectorAll('CSS选择器')
和刚才的语法是一致的,但是它可以获取符合条件的所有对象,那么最后要如何让一个变量同时接收所有符合条件的对象?
我们先用一串代码观察一下:
<ul>
<li>元素</li>
<li>元素</li>
<li>元素</li>
<li>元素</li>
</ul>
<script>
const lis = document.querySelectorAll('li');
console.log(lis);
</script>
这串代码中const lis = document.querySelectorAll('li');
就是在获取所有的li标签,因为CSS中li这个选择器就是一个标签选择器,选择了所有li标签。
输出结果:
可以看到,我们的lis,即我们获取的返回值,其实是一个数组,数组里面有符合条件的所有对象。
结论:querySelectorAll可以获得所有符合条件的元素,将它们存放在一个数组中。
但是其实这个数组是一个伪数组,它有数组的形式,但是没有数组的所有功能,它不能使用数组的pop(),push()等方法。
getElementById
getElementsByTagName
getElementsByClassName
通过这三个名字就可以看出来,getElementById是通过id选择器来获取元素;getElementsByTagName是通过标签名来获得一类元素;getElementsByClassName是通过类选择器来获得元素。
对于这些方式,我们不过多讲解,querySelector()和querySelectorAll()已经足够好用,这些只做了解。
我们已经获得到了DOM对象,接下来就要学习如何通过DOM对象来操控页面内的元素了:
通过修改 DOM 的文本内容,动态改变网页的内容。
什么是DOM对象的内容?比如我们有一个li标签:<li>你好</li>
,其中“你好”这个字符串就是li标签的内容,当我们获取到li标签的对象,就可以修改其内部的内容了。
innerText
将文本内容添加/更新到任意标签,文本中包含的标签不会被解析。<script>
const intro = document.querySelector('.intro')
intro.innerText = '嗨~ 我叫李雷!'
intro.innerText = '<h4>嗨~ 我叫李雷!</h4>'
</script>
代码解析
首先,我们通过const intro = document.querySelector('.intro')
,获取到了类名为intro的元素对象,innerText
可以在对象内部写入文本,比如第二句代码中:intro.innerText = '嗨~ 我叫李雷!'
就是在我们对象对应的标签中写入“嗨~ 我叫李雷!”这个字符串。
但是这样写入的文本,不会被解析,这是什么意思?我们看到第三行代码:
我们通过innerText向网页写入了一个字符串,字符串被h4标签包裹,我们运行一下看看效果:
最后我们的<h4></h4>
没有被解析为标签,而是被解析为了文本。
innerHTML
将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。<script>
const intro = document.querySelector('.intro')
intro.innerHTML = '嗨~ 我叫韩梅梅!'
intro.innerHTML = '<h4>嗨~ 我叫韩梅梅!</h4>'
</script>
其与innerText
的唯一区别就是,它可以解析HTML标签,我们再运行一下试试看:
此时我们的<h4></h4>
就被解析为了四级标题标签。
在实际开发中,绝大部分场景都使用 innerHTML
,除非你真的想在网页内输出文本形式的标签。
对于一些简单的HTML属性,它们会被存放在相应的对象中,需要使用时直接调用即可:
语法:
对象.属性 = 值
示例:
<script>
// 1. 获取 img 对应的 DOM 元素
const pic = document.querySelector('.pic')
// 2. 修改属性
pic.src = './images/lion.webp'
pic.width = 400;
pic.alt = '图片不见了...'
</script>
src,alt,width都是HTML中的img自带的属性,它们本身就存在于img的对象中,需要时直接调用,对其赋值即可。
绝大部分在HTML中直接出现的标签属性值,都可以用这种方式来修改。
先前的属性,是直接在HTML中可以使用的属性,而绝大部分属性,是处于CSS中的,在这一类属性,会存在于DOM对象的style属性中。
语法:
对象.style.属性 = “属性值”
通过元素节点获得的 style
属性本身的数据类型也是对象,如 box.style.color
、box.style.width
分别用来获取元素节点 CSS 样式的 color
和 width
的值。
示例:
<div class="box">随便一些文本内容</div>
<script>
// 获取 DOM 节点
const box = document.querySelector('.intro')
box.style.color = 'red'
box.style.width = '300px'
box.style.backgroundColor = 'skyblue'
</script>
css 属性的 - 连接符与 JavaScript 的 减运算符冲突,此时要改为驼峰命名法。
任何标签都有 style
属性,通过 style
属性可以动态更改网页标签的样式,如要遇到 css
属性中包含字符 -
时,要将 -
去掉并将其后面的字母改成大写,如 background-color
要写成 box.style.backgroundColor
如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。
DOM对象有一个属性className,其存储这这个元素具有的CSS类,当我们想要修改的值太多,就可以把修改后的值都写到一个类选择器中,然后将对象的类修改为对于的类选择器。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>练习 - 修改样式</title>
<style>
.blue {
background: blue;
color: skyblue;
}
</style>
</head>
<body>
<div class="box">随便一些文本内容</div>
<script>
// 获取 DOM 节点
const box = document.querySelector('.intro')
box.className = 'blue'
</script>
</body>
</html>
解析:
在上述代码这种,我们提取了box这个对象,然后将字体颜色和背景封装在了一个名为blue的类中,当我们想要在JavaScript中改变box的字体颜色和背景,只需要将blue这个类给box即可。
注意:
className是使用新值换旧值, 我们在添加新的类后,原先的类会被覆盖
基于这个特性,这个方法其实并不好用,而下一个方法可以非常好的解决这个问题。
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
classList是DOM对象所具有的全部类的集合,classList下面有方法可以实现类的追加和删除:
追加:
对象.classList.add('类名')
删除:
对象.box.classList.remove('类名')
切换状态(有就删掉,没有就加上):
对象.box.classList.toggle('类名')
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
.active {
width: 300px;
height: 300px;
background-color: hotpink;
margin-left: 100px;
}
</style>
</head>
<body>
<div class="one"></div>
<script>
// 1.获取元素
let box = document.querySelector('div')
// add是个方法 添加 追加
box.classList.add('active')
// remove() 移除 类
box.classList.remove('one')
// 切换类
box.classList.toggle('one')
</script>
</body>
</html>
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
自定义属性:
在html5中推出来了专门的data-自定义属性,在标签上一律以data-
开头,在DOM对象上一律以dataset.对象
方式获取。
<body>
<div data-id="1"> </div>
<script>
// 1. 获取元素
let div = document.querySelector('div')
// 2. 获取自定义属性值
console.log(div.dataset.id)
</script>
</body>
这里的自定义属性值,可以存一些标识符号,用于后续JavaScript寻找符合条件的对象等等,在此不做展开了。