文章目录
- 一. 什么是DOM
- 二. 最常用的DOM API
- 1. 选中页面元素
- 2. 操作元素的属性
- 2.1 事件概念
- 2.2 获取/修改元素内容
- 计数器
- 2.4 获取/修改元素属性
- 点击图片切换
- 2.5 获取/修改表单元素属性
- 表单计数器
- 全选/取消全选按钮
- 2.6 获取修改样式属性
- 点击文字放大
- 实现夜间/日间模式的切换
- 3. 操作页面节点
- 3.1 新增节点
- 3.2 删除节点
- 三. 综合案例
- 1. 猜数字
- 2. 表白墙
一. 什么是DOM
JavaScript主要由ECMAScript
, DOM
, BOM
这三部分组成; 我们要知道要完成一个程序, 光有语法是远远不够的, 对于JS来说, 除JS语法(ES)外, 还需要掌握一些WebAPI, 这些API是浏览器给JS代码提供的功能, 即DOM和BOM; DOM是页面文档对象模型, 可以对页面中的元素进行操作, 而BOM是浏览器对象模型, 可以对浏览器窗口进行操作; 本篇博客中主要介绍的就是DOM
.
DOM 全称为 Document Object Model, 是页面文档对象模型, html中的每个标签都是可以映射到JS中的一个对象中的, 标签中的内容都可以通过JS对象感知到, JS对象修改对应的属性能够影响到标签的展示, 通过这样的DOM API就可以让JS代码来操作页面元素.
二. 最常用的DOM API
1. 选中页面元素
在DOM中, 任何一个页面, 都会有一个document
对象, 是页面的一个全局对象, 所有的DOM API都是通过document
对象类展开的, 其中document对象中的querySelector
和querySelectorAll
函数可以用来选择元素, 通过传入CSS选择器来达到目的, 选择的范围是位于该函数之前所存在的选择器, 没找到返回值为null
.
let obj = document.querySelector('选择器');
要注意使用querySelector
函数如果符合选择的标签在页面中有多个, 就只会选择在页面中第一次出现的标签.
如果想把符合选择的元素都选中就需要使用querySelectorAll
函数了, 在使用上和和querySelector 是一样的.
let elem = document.querySelectorAll('选择器');
使用该函数会返回一个类似于数组的对象, 用法和数组相同.
将得到的数组对象里面的元素展开查看, 会发现有很多的属性, 这些属性都是DOM原生的一些属性.
2. 操作元素的属性
2.1 事件概念
JS要构建动态页面, 就需要感知到用户的行为, 而 “事件” 就是针对用户的操作进行的一些响应, 比如鼠标点击, 鼠标移动, 键盘输入, 调整浏览器窗口这些都是用户的操作, 而代码就需要根据这些事件做出相应的响应.
事件的三要素:
- 事件源, 哪个HTML元素产生的事件.
- 事件类型, 比如鼠标点击, 鼠标移动, 键盘输入等.
- 事件处理程序, 当事件发生之后, 要执行哪个代码.
前端页面中, 针对不同的事件也是有不同的处理方式的, 而处理方式都是再最开始的时候就设定好的(事件绑定).
看下面的这一行代码就是直接在元素标签中使用onXXX
这样的属性来绑定一个事件处理程序, onclick
是一个描述处理鼠标点击的事件.
<button onclick="alert('hello')">点一下试试</button>
如果事件处理的代码比较多的话使用上面这种写法就不太合适了, 所以更推荐的是下面这种写法, 可以直接使用onXXX
这样的方法进行绑定, 效果和上面是一样的.
<script !src="">
let button = document.querySelector("button");
button.onclick = function (){
alert("hello");
}
</script>
<button onclick="alert('hello')">点一下试试</button>
当然,这里的JS代码如果是写在一个单独的JS文件中就更好了, 但本篇中的示范代码都比较简单, 就使用内嵌式的写法来引入JS代码了.
2.2 获取/修改元素内容
在选中元素后, 就可以使用innerHTML
属性来获取/修改一个标签里面的内容了.
//1.选中标签
let var_name = document.querySelector();
//2.获取内容
let content = var_name.innnerHTML;
//3.修改内容
var_name.innerHTML = '修改值';
代码示例, 将点击按钮和输出div
中的内容绑定.
<div> hello world</div>
<button>点一下试试</button>
<script>
let but = document.querySelector('button');
but.onclick = function () {
let div = document.querySelector('div');
console.log(div.innerHTML);
}
</script>
观察控制台的结果, 控制台中多次点击按钮的结果都被折叠输出了, 这是因为chrome控制台会默认把相邻且相同的日志进行合并, 另外再显示一个数字表示输出的次数, 我们可以点击开发者工具的设置栏, 设置一下让它不合并就行了.
还要注意的是, 通过innerHTML
是可以获取到div内部的html结构的, 比如下面的的代码, 会将div标签中的html代码也获取到, 而innerText
只能获取文本内容, 并不能获取html结构, innerHTML用的场景是要比innerText更多的.
<div>
<span>hello world</span>
<span>hello world</span>
</div>
<button class="but">点一下试试</button>
<script>
let but = document.querySelector('.but');
but.onclick = function () {
let div = document.querySelector('div');
console.log(div.innerHTML);
}
</script>
修改元素也是一样的操作, 比如将上面代码中的div
中的内容修改为一个标题.
<div>
<span>hello world</span>
<span>hello world</span>
</div>
<button class="but">点一下试试</button>
<script>
let but = document.querySelector('.but');
but.onclick = function () {
let div = document.querySelector('div');
div.innerHTML = '<h1>这是一个一级标题</h1>>';
console.log(div.innerHTML)
}
</script>
计数器
有了上面的获取和修改元素的知识, 我们这里实现一个简单的计数器, 就是点击一下按钮, 计数器就+1
.
实现思路也很简单, 用户的点击操作, 就会触发点击事件, 就是先获取到计数元素中的内容, 然后将元素内容进行加一操作再写回元素中, 代码如下:
<div id='count'>0</div>
<button id='countAdd'>计数器:点击+1</button>
<script !src="">
let counterAdd = document.querySelector('#countAdd');
counterAdd.onclick = function () {
let num = document.querySelector('#count');
numAdd = parseInt(num.innerHTML) + 1
num.innerHTML = numAdd;
};
</script>
这个代码要注意的是, num.innerHTML
这里拿到的元素内容是字符串类型的, 直接进行加法运算就是字符串拼接的效果了, 而要完成算数相加的效果就需要将字符串转换为整数, 和Java中类似, 可以使用parseInt
方法将字符串转换为整数, 而如果是浮点数就使用parseFloat
方法
2.4 获取/修改元素属性
点击图片切换
上面介绍的是修改元素(标签)中的内容, 我们还可以在代码中使用DOM直接获取元素的属性并修改元素的属性, 比如这里实现一个效果, 就是点击一个图片就可以切换到另一张图片, 然后再点击就能再切换回来, 这个案例我们只需要设置点击事件为修改图片的路径即可, 也就是修改src
属性.
代码如下:
这里要注意图片的路径尽量不要设置成中文的, 中文路径在浏览器中涉及转义重新编码的问题, 转义后路径就可能没有中文字符了, 判断就可能出问题了.
<style>
img {
height: 500px;
}
</style>
<img src="./man.jpg" alt="">
<script !src>
let img = document.querySelector("img");
console.dir(img);
img.onclick = function() {
if (img.src.indexOf('wo') !== -1) {
img.src = './man.jpg';
} else {
img.src = './woman.jpg';
}
}
</script>
一个标签中具体有哪些属性可以修改, 可以使用console.dir()
函数来获取DOM API能够操作的全部属性, 比如img
元素.
2.5 获取/修改表单元素属性
这里把表单元素单拎出来是因为, 是因为, 表单元素中有一些特别的属性是普通标签没有的, 主要需要区别的是一些表单元素想要获取其中用户输入的内容的话是不能通过innerHTML
拿到的, 这是因为input
, textarea
… 这些标签元素都是单标签, 是没有内容的, 正确的获取/修改的方式应该是通过这些标签的value
属性来进行.
表单计数器
比如input
标签, 还是在文本框中实现一下计数功能, 同样的要注意拿到的value
属性的值也是字符串类型的, 要注意进行类型转换.
<input type="text" value="0" id='count'>
<button id='countAdd'>计数器: 点击+1</button>
<script !src="">
let counterAdd = document.querySelector('#countAdd');
counterAdd.onclick = function () {
let num = document.querySelector('#count');
numAdd = parseInt(num.value) + 1
num.value = numAdd;
};
</script>
再举例一个比较常见的场景, 我们平常见的登录功能中密码框中的文本可以选择是否显示密码, 这个实现起来其实也很简单, 通过DOM来修改type
属性的值即可.
<input type="text" id="pw">
<button>隐藏密码</button>
<script !src="">
let pw = document.querySelector('#pw');
let button = document.querySelector('button');
button.onclick = function() {
if (pw.type == 'text') {
pw.type = 'password';
button.innerHTML = '显示密码';
} else {
pw.type = 'text';
button.innerHTML = '隐藏密码';
}
}
</script>
全选/取消全选按钮
这里再实现一下全选功能, 也就是有若干个复选框, 点击全选按钮, 就会选中所有的选项, 但只要某个选项取消, 则自动取消全选按钮的勾选状态.
实现的基本思路如下:
- 获取全选按钮元素, 获取其按钮元素
- 注册全选按钮的点击事件, 点击全选按钮时, 全选按钮的
checked
属性发生改变, 将checked属性值赋值给其他按钮的checked属性即完成了全选. - 对每一个其他复选按钮设置点击事件, 并将状态与全选按钮关联, 即检查其他复选按钮是否选中, 如果选中, 则全选按钮也选中(修改checked值为true), 否则全选按钮不选中(修改checked值为false).
- 每次点击其他框都要检测其他框是否都选中, 以确定全选按钮的状态.
<input type="checkbox" id="all">我全都要 <br>
<input type="checkbox" class="girl">貂蝉 <br>
<input type="checkbox" class="girl">小乔 <br>
<input type="checkbox" class="girl">安琪拉 <br>
<input type="checkbox" class="girl">妲己 <br>
<script>
//checked属性为checked/true表示选中状态,为空字符串/false表示未选中
//获取到元素
let all = document.querySelector('#all');
let girls = document.querySelectorAll('.girl');
//给all注册点击事件,选中/取消所有选项
all.onclick = function () {
for (let i = 0; i < girls.length; i++) {
//使girl元素状态与all相同
girls[i].checked = all.checked;
}
}
//给所有girl元素注册点击事件
for (let i = 0; i < girls.length; i++) {
girls[i].onclick = function () {
//检测当前是不是所有的girl元素都被选中了,确定all的状态
all.checked = checkGirls(girls);
}
}
//实现checkGirls,检测当前是不是所有的girl元素都被选中了
function checkGirls(girls) {
for (let i = 0; i < girls.length; i++) {
if (!girls[i].checked) {
//只要一个 girl 没被选中,就认为结果是 false(找到了反例)
return false;
}
}
// 所有 girl 中都没找到反例, 结果就是全选中
return true;
}
</script>
2.6 获取修改样式属性
第一种方式是通过修改元素的内联样式, 即修改元素的style
属性的值来指定样式, 这种方式的优先级很高, 适用于该样式比较少的情况.
点击文字放大
文字放大效果的实现, 我们可以给文本所在的标签注册一个点击事件, 每点击一次就将字体大小增大, 即修改CSS的font-size
属性; 思路很简单, 但JS代码的写法上有一些细节需要注意, 在CSS中是不区分大小写的, 且在CSS中不需要进行算数运算, 属性和变量名是采用的是脊柱式命名法, 但-
在JS中就不能用于命名了, 所以JS中采用驼峰命名的形式来表示CSS的属性, 比如font-size
属性在对应JS的中就为fontSize
了.
<div style="font-size:20px;">
这是一段文本,点击之后字体放大
</div>
<script !src="">
let div = document.querySelector("div");
div.onclick = function() {
//1.获取文字大小属性
let size = parseInt(div.style.fontSize);
console.log("修改前" + size);
//2.修改文字大小
size += 5;
//3.写回到属性
div.style.fontSize = size + "px";
console.log("修改后" + size);
}
</script>
还有一种修改样式的方式可以通过修改类属性来达到修改样式的效果, 可以通过元素.className
来获取/修改类属性的值, 由于class
是 JS 的保留字, 所以这里获取类属性的名字叫做className
.
实现夜间/日间模式的切换
实现点击界面, 切换日间模式和夜间模式
<div class="container light">
这是一大段话. <br>
这是一大段话. <br>
这是一大段话. <br>
这是一大段话. <br>
</div>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
.container {
width: 100%;
height: 100%;
}
.light {
background-color: #f3f3f3;
color: #333;
}
.dark {
background-color: #333;
color: #f3f3f3;
}
</style>
<script !src="">
let div = document.querySelector('div');
div.onclick = function () {
console.log(div.className);
if (div.className.indexOf('light') != -1) {
div.className = 'container dark';
} else {
div.className = 'container light';
}
}
</script>
3. 操作页面节点
上述涉及的操作都是针对页面已有的元素进行操作的, 利用DOM API还可以完成添加/删除元素的操作.
3.1 新增节点
添加元素主要有两个步骤, 首先需要创建一个新的创建元素节点, 然后把这个元素节点插入到父元素中就能完成元素的的添加(依赖于DOM树).
可以使用creatElement
方法来创建一个新的元素:
let element = document.createElement('元素标签名');
插入到DOM树中:
父元素.appendChild(创建的子元素);
代码示例:
关于插入节点还可以使用insertBefore
将节点插入到指定节点之前.
新插入节点的父元素.insertBefore(新插入的元素, 指定节点(将要插在这个节点之前));
<div class="container">
<div>11</div>
<div>22</div>
<div>33</div>
<div>44</div>
</div>
<script>
var newDiv = document.createElement('div');
newDiv.innerHTML = '我是新的节点';
var container = document.querySelector('.container');
console.log(container.children);
//children可以返回父元素所有的直系子节点的集合
container.insertBefore(newDiv, container.children[0]);
</script>
3.2 删除节点
删除节点操作就很简单了, 语法如下:
父元素.removeChild(需要删除的子元素);
代码示例, 比如删除上面代码无序列表中的33
这个元素.
三. 综合案例
1. 猜数字
目标页面如下, 系统可以自动生成[1, 100]
之间的随机数, 用户输入猜测数字, 能够提示用户数字是猜大了还是猜小了.
这里的逻辑很简单就不做过多的赘述了, 最关键的是JS中随机数的获取, 我们可以通过Math.random()
函数来获取随机数, 该函数生成随机数的范围是[0,1)
区间内的一个小数, 我们需要的是[1,100]
之间的整数, 我们可以乘上100
后再拿到整数部分+1
就能得到目标区间的数了.
<button id="flash">重开一局</button>
<div>请输入要猜的数字</div>
<input type="text">
<button id="submit">提交</button>
<!--使用这个div来显示猜测结果-->
<div class="result"></div>
<script>
//1.生成一个1-100随机的整数
let toGuess = parseInt(100 * Math.random()) + 1;
console.log(toGuess);//用于测试,方便查看猜测结果
//2.进行猜数字操作,比较用户输入的数和生成的随机数
let input = document.querySelector('input');
let resultDiv = document.querySelector('.result');
let submit = document.querySelector('#submit');
submit.onclick = function() {
//取出输入框中的内容
if (input.value == '') {
//输入框没有值,用户啥都没输入,直接返回
return;
}
let inputNum = parseInt(input.value);
//比较大小关系
if (inputNum < toGuess) {
//小了
resultDiv.innerHTML = '猜小了';
} else if (inputNum > toGuess) {
//大了
resultDiv.innerHTML = '猜大了';
} else {
//猜对了
resultDiv.innerHTML = '猜对了';
}
}
//3.刷新页面
let flash = document.querySelector('#flash');
flash.onclick = function() {
location.reload();
}
</script>
实际效果:
2. 表白墙
目标页面如下, 点击提交, 能够把用户输入的话, 显示在页面中, 点击撤销, 能够撤回最后一条显示在页面的话.
这个案例也没有难点, 只是的的揉合东西更多了一些, 前端页面是通过HTML加上CSS弹性布局实现的, 交互通过JavaScript DOM实现.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表白墙</title>
<style>
* {
/* 消除浏览器的默认样式 */
margin: 0;
padding: 0;
/* 保证盒子不被撑大 */
box-sizing: border-box;
}
.container {
width: 600px;
margin: 20px auto;
}
h1 {
text-align: center;
}
p {
text-align: center;
color: #666;
margin: 20px 0;
}
.row {
/* 开启弹性布局 */
display: flex;
height: 40px;
/* 水平方向居中 */
justify-content: center;
/* 垂直方向居中 */
align-items: center;
}
.row span {
width: 80px;
}
.row input {
width: 200px;
height: 30px;
}
.row button {
width: 280px;
height: 30px;
color: white;
background-color: orange;
/* 去掉边框 */
border: none;
border-radius: 5px;
}
/* 点击的时候有个反馈 */
.row button:active {
background-color: grey;
}
</style>
</head>
<body>
<div class="container">
<h1>表白墙</h1>
<p>输入内容后点击提交, 信息会显示到下方表格中</p>
<div class="row">
<span>是谁: </span>
<input type="text">
</div>
<div class="row">
<span>对谁: </span>
<input type="text">
</div>
<div class="row">
<span>说: </span>
<input type="text">
</div>
<div class="row">
<button id="submit">提交</button>
</div>
<div class="row">
<button id="revert">撤销</button>
</div>
<!-- <div class="row">
xxx 对 xx 说 xxxx
</div> -->
</div>
<script>
// 实现提交操作. 点击提交按钮, 就能够把用户输入的内容提交到页面上显示.
// 点击的时候, 获取到三个输入框中的文本内容
// 创建一个新的 div.row 把内容构造到这个 div 中即可.
let containerDiv = document.querySelector('.container');
let inputs = document.querySelectorAll('input');
let button = document.querySelector('#submit');
button.onclick = function() {
// 1. 获取到三个输入框的内容
let from = inputs[0].value;
let to = inputs[1].value;
let msg = inputs[2].value;
if (from == '' || to == '' || msg == '') {
return;
}
// 2. 构造新 div
let rowDiv = document.createElement('div');
rowDiv.className = 'row message';
rowDiv.innerHTML = from + ' 对 ' + to + ' 说: ' + msg;
containerDiv.appendChild(rowDiv);
// 3. 清空之前的输入框内容
for (let input of inputs) {
input.value = '';
}
}
let revertButton = document.querySelector('#revert');
revertButton.onclick = function() {
// 删除最后一条消息.
// 选中所有的 row, 找出最后一个 row, 然后进行删除
let rows = document.querySelectorAll('.message');
if (rows == null || rows.length == 0) {
return;
}
containerDiv.removeChild(rows[rows.length - 1]);
}
</script>
</body>
</html>
实际效果: