第一部分:JavaScript基础语法
1.1 什么是JavaScript
⭐ 老师强调:纯HTML和CSS只能做静态页面,就像一张漂亮的宣传单,只能看不能互动。而实际网页需要动态交互------轮播图自动切换、下拉菜单响应鼠标悬停、点击按钮执行操作,这些都需要JavaScript来实现。
HTML/CSS/JS 分工:
| 技术 | 作用 | 比喻 |
|---|---|---|
| HTML | 页面结构 | 骨架 |
| CSS | 页面样式 | 衣服 |
| JavaScript | 交互逻辑 | 大脑 |
1.2 变量声明
JavaScript是弱类型语言 ,使用var或let声明变量,无需预先声明类型。
// 弱类型:同一个变量可以存储不同类型的数据
var a = 10; // 数字
var b = 20.5; // 小数
var c = 'aaa'; // 字符串
var d = true; // 布尔值
var e; // undefined(未定义)
var f = null; // null(空值)
var vs let:
| 关键字 | 变量提升 | 初始化 | 说明 |
|---|---|---|---|
var |
✅ 会提升 | 初始化为undefined |
可以先使用后定义 |
let |
✅ 会提升 | 不初始化(暂时性死区) | 定义前使用会报错 |
⭐ 老师强调 :JavaScript会把所有变量声明自动"提升"到作用域顶部。
var就像先把空盒子放好(盒子里是undefined),let则是盒子位置定了但没放东西前不让碰。
1.3 数据类型
1.3.1 基本类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 数字类型 | 不区分整数和小数 | 10、20.5 |
| 字符串 | 单引号或双引号均可 | 'aaa'、"hello" |
| 布尔类型 | 只有两个值 | true、false |
| undefined | 未定义(未赋值) | var e; |
| null | 空值 | var f = null; |
⭐ 老师强调 :JavaScript中所有数值统一为数字类型,不像C/Java那样细分为
int、short、float。字符串用单引号或双引号都行,前后匹配即可。
1.3.2 复合类型
| 类型 | 表示方式 | 说明 |
|---|---|---|
| 数组 | [] |
元素类型不固定,可嵌套 |
| 对象 | {} |
键值对(属性:值) |
| 函数 | function |
函数本身也是一种"值" |
数组示例:
var arr = [
10.5, // 数字
20, // 数字
"demo", // 字符串
true, // 布尔值
undefined, // undefined
null, // null
[10, 20.5], // 嵌套数组
{ name: "zhangsan" }, // 嵌套对象
function(a, b) { // 嵌套函数
console.log(a);
console.log(b);
}
];
console.log(arr[8]); // 输出函数体
arr[8](); // 调用函数
对象示例:
var obj = {
name: "demo",
age: 20,
friend: {
name: "lisi",
age: 21
},
play: ["足球", "篮球"],
fly: function(a, b) {
console.log(a);
console.log(b);
}
};
// 访问方式
console.log(obj.name); // 点语法
console.log(obj['name']); // 中括号语法
console.log(obj.friend.name); // 嵌套访问
console.log(obj.play[0]); // 数组元素
obj.fly(10, 20); // 调用函数
⭐ 老师强调 :访问数据时要分清当前是对象还是数组------对象用点(
.) ,数组用中括号([索引])。就像剥洋葱一样,一层层往里找。
1.3.3 强类型 vs 弱类型数组
| 语言类型 | 数组存储方式 | 访问速度 | 原因 |
|---|---|---|---|
| 强类型(C/Java) | 内存连续存储 | 快 | 每个元素固定大小 |
| 弱类型(JS) | 分散存储 | 慢 | 元素类型不同,需额外查找 |
⭐ 老师强调:强类型语言数组在内存中是连续存储的,访问效率高;弱类型语言数组可以混合存放不同类型的数据,虽然灵活但内存分配不连续,访问速度会慢一些。
1.4 函数
1.4.1 函数的两种定义方式
// 1. 命名函数
function change(a, b) {
console.log(a);
console.log(b);
}
// 2. 匿名函数(赋值给变量)
var run = function(a, b) {
console.log(a);
console.log(b);
};
// 3. 命名函数赋值给变量(函数名失效)
var jump = function change(a, b) {
console.log(a);
console.log(b);
};
// 调用方式
change(10, 20); // 命名函数直接调用
run(10, 20); // 匿名函数通过变量调用
jump(10, 20); // 此时只能通过jump调用,change失效
⭐ 老师强调 :把函数逻辑赋给变量后,原函数名就失效了。这就像给一个人起了外号后,本名反而没人叫了。匿名函数从定义到调用都只有一种固定方式,不会出现这种混淆,更推荐使用。
1.4.2 函数作为值
// 函数可以存储在数组或对象中
var arr = [
function(a, b) { console.log(a + b); }
];
var obj = {
add: function(a, b) { console.log(a + b); }
};
arr; // 输出30
obj.add(10, 20); // 输出30
1.5 window 与 document
// window ------ 浏览器窗口对象("老大")
console.log(window);
// document ------ window下的属性,代表整个HTML文档
console.log(document);
console.dir(document); // 以对象形式展示
// alert() 和 console.log() 都是window的方法(可省略window)
window.alert("hello");
alert("hello"); // 等价
| 对象 | 说明 |
|---|---|
window |
代表整个浏览器窗口,所有全局对象的"老大" |
document |
window的属性,代表整个HTML文档 |
⭐ 老师强调 :
console.log()以HTML标签形式输出,console.dir()以对象属性形式展示,能更清晰地呈现文档的底层数据结构。
第二部分:DOM操作基础
2.1 什么是DOM
⭐ 老师强调 :DOM(Document Object Model)是JavaScript对象与HTML元素之间的桥梁。JavaScript对象和HTML元素之间存在一一映射关系------操作JS对象就能控制页面元素,就像操纵提线木偶。
DOM = Document(文档)+ Object(对象)+ Model(模型)
2.2 查找元素
2.2.1 五种查找方法
| 方法 | 说明 | 返回值 |
|---|---|---|
getElementById('id') |
根据ID查找 | 单个元素对象 |
getElementsByClassName('class') |
根据class查找 | 元素数组 |
getElementsByTagName('标签名') |
根据标签名查找 | 元素数组 |
querySelector('选择器') |
根据CSS选择器查找 | 第一个匹配元素 |
querySelectorAll('选择器') |
根据CSS选择器查找 | 所有匹配元素数组 |
// 1. 根据ID查找(返回单个对象)
var obj = document.getElementById("aa");
// 2. 根据class查找(返回数组)
var arr = document.getElementsByClassName("demo");
// 3. 根据标签名查找(返回数组)
var arr = document.getElementsByTagName("div");
// 4. 根据选择器查找(返回第一个)
var obj = document.querySelector(".demo");
// 5. 根据选择器查找(返回所有)
var arr = document.querySelectorAll(".demo");
⭐ 老师强调 :根据ID查找时不需要加
#号 ,因为方法名已说明是按ID查询。即使页面上有多个相同ID,该方法也只会返回第一个匹配项,找不到返回null。
2.2.2 通过关系查找
利用元素自身属性遍历父子、兄弟关系:
var aa = document.getElementById("aa");
// 父子关系
console.log(aa.parentNode); // 父节点
console.log(aa.parentElement); // 父元素
console.log(aa.children); // 所有子元素(仅标签)
console.log(aa.childNodes); // 所有子节点(含文本)
console.log(aa.firstChild); // 第一个子节点(含文本)
console.log(aa.firstElementChild); // 第一个子元素(仅标签)
console.log(aa.lastChild); // 最后一个子节点(含文本)
console.log(aa.lastElementChild); // 最后一个子元素(仅标签)
// 兄弟关系
console.log(aa.previousSibling); // 前一个兄弟节点(含文本)
console.log(aa.previousElementSibling); // 前一个兄弟元素(仅标签)
console.log(aa.nextSibling); // 后一个兄弟节点(含文本)
console.log(aa.nextElementSibling); // 后一个兄弟元素(仅标签)
childNodes vs children:
| 属性 | 包含内容 | 示例 |
|---|---|---|
childNodes |
所有子节点(含文本节点) | 长度可能为7 |
children |
仅子元素节点(标签) | 长度可能为3 |
⭐ 老师强调 :
childNodes会包含文本节点(如换行、空格),而children只包含标签元素。firstChild可能是文本节点,而firstElementChild一定是标签元素。
2.3 DOM加载顺序问题
// ❌ 错误:元素还未加载
var obj = document.getElementById("aa"); // 返回null
obj.style.background = "red"; // 报错
// ✅ 解决方案1:将script放到body底部
// ✅ 解决方案2:使用defer属性延迟加载
<script src="js/demo.js" defer></script>
// ✅ 解决方案3:使用window.onload
window.onload = function() {
var obj = document.getElementById("aa");
obj.style.background = "red";
};
⭐ 老师强调 :必须等待整个窗口加载完成后再执行DOM操作,否则代码可能找不到目标元素。
window.onload确保所有内容加载完毕后再执行。
第三部分:DOM修改操作
3.1 修改内容
| 属性 | 说明 | 适用元素 |
|---|---|---|
innerText |
纯文本内容(不解析HTML) | 普通标签 |
innerHTML |
HTML内容(解析标签) | 普通标签 |
value |
表单控件的值 | input、textarea等 |
var aa = document.getElementById("aa");
// innerText vs innerHTML
aa.innerText = "<h1>hello</h1>"; // 显示为文本 "<h1>hello</h1>"
aa.innerHTML = "<h1>hello</h1>"; // 显示为加粗标题 "hello"
// input使用value
var bb = document.getElementById("bb");
console.log(bb.value); // 获取输入框的值
bb.value = "新内容"; // 设置输入框的值
⭐ 老师强调 :
innerHTML会解析HTML标签,innerText只会作为纯文本处理。input的值存在value属性里,用innerHTML是无效的。
3.2 修改样式
var aa = document.getElementById("aa");
// CSS属性名 → JS驼峰命名
aa.style.backgroundColor = "pink"; // background-color → backgroundColor
aa.style.fontSize = "50px"; // font-size → fontSize
aa.style.color = "red";
CSS属性名转换规则:
| CSS | JavaScript |
|---|---|
background-color |
backgroundColor |
font-size |
fontSize |
text-decoration |
textDecoration |
⭐ 老师强调 :
style本身是个包含多个CSS属性的对象,所有连字符命名的CSS属性必须转为小驼峰命名。
3.3 修改属性
var aa = document.getElementById("aa");
// 原生属性:直接用点语法
console.log(aa.className); // class属性在JS中叫className
aa.className = "ppppp";
console.log(aa.id);
aa.id = "newId";
// 自定义属性:使用getAttribute/setAttribute
console.log(aa.getAttribute("run")); // 获取自定义属性
aa.setAttribute("index", "xxxxxx"); // 设置自定义属性
aa.setAttribute("run", "yyyyy"); // 修改已有属性
| 属性类型 | 获取方式 | 设置方式 |
|---|---|---|
| 原生属性(id、class等) | 对象.属性名 |
对象.属性名 = 值 |
| 自定义属性 | getAttribute('属性名') |
setAttribute('属性名', '值') |
第四部分:DOM增删改查操作
4.1 创建元素
// 方法1:createElement() 创建新元素
var newNode = document.createElement("h1");
newNode.innerText = "新元素";
newNode.style.color = "gold";
newNode.className = "pppp";
console.log(newNode);
// 方法2:cloneNode() 复制已有元素
var oldNode = document.querySelector("h1");
var newNode = oldNode.cloneNode(true); // true=深拷贝(含子元素)
var newNode = oldNode.cloneNode(false); // false=浅拷贝(仅标签本身)
4.2 添加元素
// 获取容器
var container = document.getElementById("aa");
var target = document.querySelector("p");
// appendChild():末尾添加
container.appendChild(newNode);
// insertBefore():在指定元素前插入
container.insertBefore(newNode, target);
// replaceChild():替换指定元素
container.replaceChild(newNode, target);
⭐ 老师强调 :
appendChild把新节点作为最后一个子元素添加。insertBefore需要两个参数:第一个是要添加的新节点,第二个是作为参照的现有节点。
4.3 删除元素
var target = document.querySelector("p");
// 方法1:先找父容器再删除
var container = document.getElementById("aa");
container.removeChild(target);
// 方法2:通过parentElement向上查找删除
target.parentElement.removeChild(target);
⭐ 老师强调 :删除元素必须通过父节点 调用
removeChild()。可以通过parentElement属性向上查找父节点。
4.4 DOM操作总结
| 操作 | 方法 | 说明 |
|---|---|---|
| 查 | getElementById、querySelector等 |
查找元素 |
| 增 | createElement + appendChild |
创建并添加元素 |
| 删 | parent.removeChild(child) |
删除子元素 |
| 改 | 修改innerText、style、className等 |
修改元素内容/样式/属性 |
第五部分:事件绑定
5.1 事件绑定方式
// 方式1:直接在元素上绑定
document.onclick = function() {
document.body.style.background = "red";
};
// 方式2:获取元素后绑定
var obj = document.getElementById("aa");
obj.onclick = function() {
obj.style.background = "yellow";
};
5.2 常用事件
| 事件 | 触发时机 |
|---|---|
onclick |
鼠标单击 |
ondblclick |
鼠标双击 |
onmouseenter |
鼠标移入 |
onmouseleave |
鼠标移出 |
onfocus |
获得焦点 |
onblur |
失去焦点 |
onload |
页面加载完成 |
5.3 this关键字
var arr = document.getElementsByClassName("demo");
for (var i = 0; i < arr.length; i++) {
arr[i].onclick = function() {
// this 指向当前被点击的元素
this.style.background = "yellow";
};
}
⭐ 老师强调 :
this在事件处理函数中指向触发该事件的元素本身 ,比用数组索引更可靠。点击哪个元素,this就代表哪个元素。
5.4 事件绑定常见陷阱
问题:循环绑定事件时变量i的值不对
var arr = document.getElementsByClassName("demo");
// ❌ 错误:所有点击都输出3
for (var i = 0; i < arr.length; i++) {
arr[i].onclick = function() {
console.log(i); // i总是3
};
}
// ✅ 正确:使用this
for (var i = 0; i < arr.length; i++) {
arr[i].onclick = function() {
this.style.background = "yellow";
};
}
⭐ 老师强调 :事件绑定时,循环中的
i在点击时才取值,此时循环已结束,i变成最终值。使用this可以避免这个问题。
第六部分:To-Do List 综合实战
6.1 功能概述
这是一个完整的待办事项管理应用,包含增、删、改、查四个核心操作。
| 功能 | 操作 | DOM操作 |
|---|---|---|
| 添加 | 输入内容,点击"添加"按钮 | createElement + appendChild |
| 完成 | 点击"完成"按钮 | 修改style.textDecoration |
| 修改 | 点击"修改"按钮(待实现) | 替换内容为输入框 |
| 删除 | 点击"删除"按钮 | removeChild |
6.2 HTML结构
<div class="todolist">
<!-- 添加区域 -->
<div class="addbox">
<input type="text" class="info">
<input type="button" class="btn" value="添加">
</div>
<!-- 表格展示区域 -->
<table border="1">
<thead>
<tr>
<th>事项</th>
<th width="200px">操作</th>
</tr>
</thead>
<tbody>
<!-- 动态生成的行 -->
</tbody>
</table>
</div>
6.3 CSS样式核心代码
/* 重置 */
* { padding: 0; margin: 0; }
/* 容器居中 */
.todolist {
width: 400px;
margin: 200px auto;
}
/* 添加区域 - 弹性盒子 */
.todolist .addbox {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.todolist .addbox .info {
width: 80%;
padding: 5px;
outline: none;
}
.todolist .addbox .btn {
width: 15%;
padding: 5px;
background: #feca57;
border: none;
}
/* 悬停效果:内阴影 */
.todolist .addbox .btn:hover {
box-shadow: -2px -2px #ff9f43 inset;
}
/* 表格边框合并 */
.todolist table {
width: 100%;
border-collapse: collapse;
}
/* 表头 */
.todolist table thead tr {
background: #feca57;
}
.todolist table tr th {
padding: 5px;
font-size: 14px;
}
.todolist table tr td {
padding: 4px;
font-size: 13px;
}
/* 第二列居中 */
.todolist table tr td:nth-child(2) {
text-align: center;
}
/* 三个按钮样式 */
.todolist table tr td .finish {
padding: 3px;
width: 58px;
border: none;
background: #ff9ff3;
}
.todolist table tr td .update {
padding: 3px;
width: 58px;
border: none;
background: #48dbfb;
}
.todolist table tr td .delete {
padding: 3px;
width: 58px;
border: none;
background: #ff6b6b;
}
/* 悬停内阴影 */
.todolist table tr td .finish:hover {
box-shadow: -2px -2px #f368e0 inset;
}
.todolist table tr td .update:hover {
box-shadow: -2px -2px #0abde3 inset;
}
.todolist table tr td .delete:hover {
box-shadow: -2px -2px #ee5253 inset;
}
6.4 JavaScript完整逻辑
javascript
window.onload = function() {
// 1. 查找元素
var addBtn = document.querySelector('.btn');
var infoInput = document.querySelector(".info");
var tbody = document.querySelector("tbody");
// 2. 添加功能
addBtn.onclick = function() {
//之所以所有功能都在添加的inclick里
//因为"完成"和"删除"按钮是在"添加"按钮的点击事件里动态创建的,所以它们的点击事件(onclick)必
//在这个动态创建的过程中进行绑定。
//拿出去 = 页面加载时绑定一次,之后新增的按钮都没有事件,所以完全失效。
// 获取输入内容,去除首尾空格
var text = infoInput.value.trim();
// 空内容不添加
if (text) {
// 创建行
var tr = document.createElement("tr");
// 第一列:事项内容
var tdOne = document.createElement("td");
tdOne.innerText = text;
// 第二列:三个按钮
var tdTwo = document.createElement("td");
tdTwo.innerHTML =
'<input type="button" class="finish" value="完成">' +
'<input type="button" class="update" value="修改">' +
'<input type="button" class="delete" value="删除">';
// 组装行
tr.appendChild(tdOne);
tr.appendChild(tdTwo);
tbody.appendChild(tr);
// 清空输入框
infoInput.value = "";
}
// 3. 完成功能(每次添加后重新绑定)
var finishList = document.getElementsByClassName("finish");
for (var i = 0; i < finishList.length; i++) {
finishList[i].onclick = function() {
// 找到当前行的事项单元格
var target = this.parentElement.previousElementSibling;
target.style.textDecoration = "line-through";
target.style.color = "#888";
this.style.background = "#aaa";
};
}
// 4. 删除功能(每次添加后重新绑定)
var deleteList = document.getElementsByClassName("delete");
for (var i = 0; i < deleteList.length; i++) {
deleteList[i].onclick = function() {
// 找到当前行
var targetNode = this.parentElement.parentElement;
// 二次确认
if (confirm("确定要删除吗")) {
tbody.removeChild(targetNode);
}
};
}
};
};
6.5 功能实现详解
6.5.1 添加功能
// 核心步骤
var text = infoInput.value.trim(); // 1. 获取输入(去除首尾空格)
if (text) { // 2. 空值判断
var tr = document.createElement("tr"); // 3. 创建行
var tdOne = document.createElement("td");
tdOne.innerText = text; // 4. 设置内容
var tdTwo = document.createElement("td");
tdTwo.innerHTML = '三个按钮的HTML'; // 5. 创建操作列
tr.appendChild(tdOne); // 6. 组装
tr.appendChild(tdTwo);
tbody.appendChild(tr); // 7. 添加到表格
infoInput.value = ""; // 8. 清空输入框
}
⭐ 老师强调 :
trim()方法能自动去除字符串首尾空格,但不会处理中间空格。获取输入值必须在点击事件触发后执行,因为初始时输入框是空的。
6.5.2 完成功能
// 通过class查找所有"完成"按钮
var finishList = document.getElementsByClassName("finish");
for (var i = 0; i < finishList.length; i++) {
finishList[i].onclick = function() {
// this → 当前点击的"完成"按钮
// parentElement → td → previousElementSibling → 事项单元格
var target = this.parentElement.previousElementSibling;
target.style.textDecoration = "line-through"; // 中划线
target.style.color = "#888"; // 变灰
this.style.background = "#aaa"; // 按钮置灰
};
}
6.5.3 删除功能
var deleteList = document.getElementsByClassName("delete");
for (var i = 0; i < deleteList.length; i++) {
deleteList[i].onclick = function() {
// this → 当前"删除"按钮 → parentElement(td) → parentElement(tr)
var targetNode = this.parentElement.parentElement;
if (confirm("确定要删除吗")) { // 二次确认
tbody.removeChild(targetNode);
}
};
}
⭐ 老师强调 :
alert()是普通提示框,confirm()是带确定/取消的询问框。点击"确定"返回true,点击"取消"返回false。
6.5.4 修改功能(课后思考)
⭐ 老师强调:技术实现没有唯一标准答案,修改功能可以有多种实现方式:
- 就地编辑 :点击"修改",当前行的
td内容替换为<input>,默认显示原文本,修改完成后更新- 弹窗编辑 :使用
prompt()弹出输入框,获取新值后更新- 下方独立编辑区:在表格下方设置独立的编辑输入框
// 思路1:就地编辑(伪代码)
updateBtn.onclick = function() {
var td = this.parentElement.previousElementSibling;
var oldText = td.innerText;
td.innerHTML = '<input type="text" value="' + oldText + '">' +
'<input type="button" value="确认修改">';
// 确认按钮绑定事件,获取新值并更新
};
效果如图:
附录一:DOM查找方法速查表
| 方法 | 语法 | 返回值 | 示例 |
|---|---|---|---|
| 按ID | getElementById('id') |
单个元素 | document.getElementById('aa') |
| 按Class | getElementsByClassName('class') |
数组 | document.getElementsByClassName('demo') |
| 按标签 | getElementsByTagName('标签') |
数组 | document.getElementsByTagName('div') |
| CSS选择器(单个) | querySelector('选择器') |
单个元素 | document.querySelector('.demo') |
| CSS选择器(全部) | querySelectorAll('选择器') |
数组 | document.querySelectorAll('.demo') |
关系查找属性速查
| 属性 | 说明 | 是否含文本节点 |
|---|---|---|
parentNode |
父节点 | - |
parentElement |
父元素 | - |
children |
所有子元素 | ❌ 仅标签 |
childNodes |
所有子节点 | ✅ 含文本 |
firstChild |
第一个子节点 | ✅ 含文本 |
firstElementChild |
第一个子元素 | ❌ 仅标签 |
lastChild |
最后一个子节点 | ✅ 含文本 |
lastElementChild |
最后一个子元素 | ❌ 仅标签 |
previousSibling |
前一个兄弟节点 | ✅ 含文本 |
previousElementSibling |
前一个兄弟元素 | ❌ 仅标签 |
nextSibling |
后一个兄弟节点 | ✅ 含文本 |
nextElementSibling |
后一个兄弟元素 | ❌ 仅标签 |
附录二:事件类型速查表
| 事件 | 触发时机 | 适用元素 |
|---|---|---|
onclick |
鼠标单击 | 所有元素 |
ondblclick |
鼠标双击 | 所有元素 |
onmouseenter |
鼠标移入 | 所有元素 |
onmouseleave |
鼠标移出 | 所有元素 |
onfocus |
获得焦点 | input、textarea |
onblur |
失去焦点 | input、textarea |
onload |
页面加载完成 | window |
附录三:核心易错点速查表
| 类别 | 易错点 | 正确理解 |
|---|---|---|
| 变量声明 | 以为JS需要声明类型 | 用var/let声明,不区分类型 |
| 变量提升 | 以为先使用后定义会报错 | var会提升,值为undefined |
| 数据类型 | 混淆undefined和null |
undefined=未赋值,null=空值 |
| 数组类型 | 以为JS数组元素类型统一 | JS数组可混合存放多种类型 |
| 对象访问 | 用中括号访问对象属性 | 对象用.,数组用[索引] |
| innerText | 以为能解析HTML标签 | innerText是纯文本,innerHTML解析标签 |
| input取值 | 用innerHTML获取input值 |
input用value属性 |
| className | 直接使用class属性 |
JS中class→className |
| 样式属性名 | 写CSS连字符属性名 | 转驼峰:background-color→backgroundColor |
| getElementById | 加#号 |
不需要,直接传id值 |
| getElementsByClassName | 以为返回单个元素 | 返回数组,需用索引访问 |
| 循环绑定事件 | 循环内用i访问元素 |
使用this获取当前元素 |
| 动态元素事件 | 页面加载时一次性绑定 | 每次添加后需重新查找并绑定 |
| 删除元素 | 直接删除元素自身 | 必须通过父节点调用removeChild() |