DOM03
复习
DOM: Document Object Model
文档对象模型
HTML文本代码 -> 转化成DOM -> 显示在浏览器上
浏览器展示的是DOM, 通过操作DOM 就可以实时修改浏览器的效果
查找元素
:
- 固定元素读取
document.head
document.body
document.documentElement
: 整个html
- 按照与已知元素的关系
- 父:
parentElement
- 子:
- 第一个:
firstElementChild
- 最后一个:
lastElementChild
- 所有孩子:
children
利用下标读取具体的某个
- 第一个:
- 兄弟
- 上一个:
previousElementSibling
- 下一个:
nextElementSibling
- 上一个:
- 父:
- 按照特征读取
- id:
getElementById
– 结果是元素本身 - class:
getElementsByClassName
– 结果是伪数组-遍历/下标取值 - name:
getElementsByName
– 结果是伪数组-遍历/下标取值 - tag:
getElementsByTagName
– 结果是伪数组-遍历/下标取值
- id:
- 利用 css 选择器读取
querySelector(css选择器)
: 读取单个元素querySelectorAll
: 读取多个元素, 原型中有forEach
方法可用
操作元素
:
-
style属性: 操作内联样式
-
class
- className: 字符串的值, 简单粗暴的修改 class的值
- classList: 优雅的修改
- add: 添加
- remove: 移除
- toggle: 切换
-
系统属性读取
- 新
- 元素.属性名
- 元素.属性名 = 值
元素.属性名==''
- 旧
元素.getAttribute(属性名)
元素.setAttribute(属性名, 值)
元素.hasAttribute(属性名)
- 新
-
自定义属性
- 要求格式:
data-属性名=值
- 读取
- 新:
元素.dataset.属性名
, 自定义属性固定存储在dataset中 - 旧:
元素.getAttribute('data-xxx')
- 新:
- 要求格式:
-
冒泡机制
- 子元素触发事件之后, 会通过所有的祖先元素, 触发相同的事件, 其中事件参数的
e.target
代表了事件具体是谁触发的 事件委托
: 让父元素代为管理 子元素的事件
- 子元素触发事件之后, 会通过所有的祖先元素, 触发相同的事件, 其中事件参数的
-
常见事件
- click: 点击
- mouseover: 鼠标悬浮
- focus: 获得焦点
- blur: 失去焦点
- change: 发生变化
- 输入框的值:
value
属性读取 - 勾选框的值:
checked
属性读取
- 输入框的值:
- input: 输入框值的实时变化监听
- keyup: 按键抬起, 利用
e.keyCode == 13
代表回车
本阶段的各种资源
https://pan.baidu.com/s/10oVRMBaEDL9uQSB1Jrrc3w
提取码:6666
双标签内容
<!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>
</head>
<body>
<div id="box">
<p>Hello</p>
<b>World</b>
</div>
<script>
const box = document.getElementById('box')
console.dir(box) //查看其中的 innerHTML 和 innerText
// innerHTML: 读取标签内容中的 所有HTML代码
console.log('html:', box.innerHTML)
// innerText: 读取标签内容中的 文本部分, 不含HTML代码
console.log('text:', box.innerText)
// 修改: 1.替换标签原有内容 2.HTML标签被解析
box.innerHTML = '<h1>打亮亮</h1>'
// 累加:
box.innerHTML += '<h1>打高达</h1>'
// 差异: 把内容当做文本进行展示, HTML标签不解析
box.innerText = '<h1>打亮亮</h1>'
</script>
</body>
</html>
计数器
<!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>计数器</title>
</head>
<body>
<button>1</button>
<script>
/*
1. 找到按钮
2. 加点击事件
3. 获取按钮上的文字
4. 给文字 +1
5.赋值回按钮上, 替换之前的值
*/
// const:常量, 不可修改, 安全
// let: 可修改, 适合值需要修改的场景
// 点击按钮, 让上方的文字+1
const btn = document.getElementsByTagName('button')[0]
btn.onclick = function () {
// this.innerHTML++
// return
let num = this.innerHTML //标签内容都是 字符串类型
// num++ ; ++:自增, 转数字然后自身+1
// 利用隐式转换, *1 把任意类型转成数字
num = num * 1 + 1
// 覆盖之前的值
this.innerHTML = num
}
</script>
</body>
</html>
计数器升级
<!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>
</head>
<body>
<div>
<button disabled>-</button>
<span>1</span>
<button>+</button>
</div>
<script>
// 思路1: 分别为 -按钮 和 +按钮添加事件
const num = document.querySelector('span')
const jian = num.previousElementSibling //上一个兄弟
const jia = num.nextElementSibling //下一个兄弟
jia.onclick = function () {
num.innerHTML++
// 减按钮的不可用变为假 -> 不不可用->可用
jian.disabled = false
}
jian.onclick = function () {
num.innerHTML--
// 当 ==1 让不可用属性生效
console.dir(this)
// disabled: 不可用
if (num.innerHTML == 1) this.disabled = true
console.log(this.disabled)
// 语法糖: if(条件) {一行代码} 可以省略{}
}
// 思路2: 利用事件委托 给父元素添加事件监听
</script>
</body>
</html>
计数器委托
<!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>
</head>
<body>
<div id="counter">
<button disabled>-</button>
<span>1</span>
<button>+</button>
</div>
<script>
// 事件委托: 依赖冒泡机制-子元素的事件就会传到给父元素
const counter = document.getElementById('counter')
counter.onclick = function (e) {
// 事件委托: 必须通过target过滤出你需要的元素
if (e.target.localName == 'button') {
console.dir(e.target)
const num = this.children[1]
const jian = this.children[0]
//const [jian, num] = this.children //数组解构
// 根据按钮上的文本,判断出是什么按钮
if (e.target.innerHTML == '+') {
num.innerHTML++
jian.disabled = false // 不 不可用 -> 可用
}
if (e.target.innerHTML == '-') {
num.innerHTML--
if (num.innerHTML == 1) jian.disabled = true
}
}
}
</script>
</body>
</html>
小计
<!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>
.cell {
border: 1px solid lightgray;
width: 400px;
display: flex;
justify-content: space-between;
padding: 10px;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="cell">
<span>¥99</span>
<div>
<button disabled>-</button>
<span>1</span>
<button>+</button>
</div>
<span>¥99</span>
</div>
<script>
const [jian, num, jia] = document.querySelector('.cell>div').children
// 实现+1
jia.onclick = function () {
num.innerHTML++
jian.disabled = false
updateSubtotal() //更新小计
}
// 实现-1
jian.onclick = function () {
num.innerHTML--
if (num.innerHTML == 1) this.disabled = true
updateSubtotal() //更新小计
}
// +1 和 -1 都需要: 获取单价 x 数量, 结果赋值给小计
// 制作函数, 更新小计 -> 目的复用. +1 -1 都触发
function updateSubtotal() {
const [price, , subtotal] = document.querySelector('.cell').children
// slice(i): 从字符串的序号i位置 截取
console.log('price:', price.innerHTML.slice(1))
const price_value = price.innerHTML.slice(1)
const num_value = num.innerHTML
subtotal.innerHTML = '¥' + price_value * num_value
}
</script>
</body>
</html>
数组转HTML
<!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>
</head>
<body>
<!--
实际工作中, 数据都是通过网络接口请求的, 通常是数组类型
我们需要把数组中的数据转化成HTML代码, 最终展现到页面上
-->
<div id="box"></div>
<script>
// 假数组 -- 以后学习网络请求ajax,再用真数组
const names = ['高达', '海浪', '启航', '泽权']
// 期望: 把每个元素放到 按钮标签里, 最后显示到页面上
// 例如: '高达' -> '<button>高达</button>'
// 数组高阶函数: map, 把数组的每个元素处理之后, 返回值形成新的数组
const btns = names.map(value => {
// ``:模板字符串, 特别适合 HTML代码的拼接
return `<button>${value}</button>`
})
console.log('btns:', btns)
// 数组的元素拼接到一起, 形成字符串用什么方法? join
// join(','): 默认拼接时, 用逗号间隔
console.log('join:', btns.join(''))
// 放到 id='box' 的元素中
const box = document.getElementById('box')
// 一定要用 innerHTML, 才能解析文本中的 html标签
box.innerHTML = btns.join('')
</script>
</body>
</html>
练习
<!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>
</head>
<body>
<ul id="box"></ul>
<script>
const products = ['APPLE', 'OPPO', 'HUAWEI', 'XIAOMI']
// 期望: 把每个元素放到 li 标签里, 然后显示到页面
// 用map转化 -> 用innerHTML添加到 id=box 元素中
const lis = products.map(value => `<li>${value}</li>`)
box.innerHTML = lis.join('')
</script>
</body>
</html>
练习 - 升级
<!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>
</head>
<body>
<div id="box">
<!-- 由于在模板字符串中书写HTML代码, 没有代码提示; 推荐如果HTML复杂, 就好在 HTML中先写好, 然后复制粘贴改一改 -->
<!-- <div><a href="地址">标题</a></div> -->
</div>
<script>
const news = [
{ title: '百度一下', url: 'http://www.baidu.com' },
{ title: 'TMOOC', url: 'http://tmooc.cn' },
{ title: '哔哩哔哩', url: 'http://www.bilibili.com' },
{ title: '斗鱼', url: 'http://www.douyu.com' },
]
// 转换映射: el 是element缩写, 代表元素
const news_el = news.map(value => {
//先解构 再使用
const { title, url } = value
return `<div><a href="${url}">${title}</a></div>`
})
console.log(news_el)
const box = document.getElementById('box')
box.innerHTML = news_el.join('')
</script>
</body>
</html>
练习
<!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>
.cell {
width: 400px;
display: flex;
justify-content: space-between;
padding: 10px;
border: 1px solid gray;
}
</style>
</head>
<body>
<div id="box">
<!-- <div class="cell">
<div>姓名: xxx</div>
<div>年龄: xxx</div>
<div>手机号: xxx</div>
</div> -->
</div>
<script>
const emps = [
{ name: '亮亮1', age: 31, phone: '1878787777' },
{ name: '亮亮2', age: 30, phone: '1878787577' },
{ name: '亮亮3', age: 32, phone: '1878787477' },
{ name: '亮亮4', age: 34, phone: '1878227777' },
{ name: '亮亮5', age: 35, phone: '1878447777' },
]
const emps_el = emps.map(value => {
// 先写 const {} = value; 然后在补充{}的值, 有提示
const { name, age, phone } = value
return `<div class="cell">
<div>姓名: ${name}</div>
<div>年龄: ${age}</div>
<div>手机号: ${phone}</div>
</div>`
})
document.getElementById('box').innerHTML = emps_el.join('')
</script>
</body>
</html>
练习 - 将数据显示在表格中
<!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>
.cell {
width: 500px;
display: flex;
justify-content: space-between;
padding: 10px;
border: 1px solid gray;
}
.cell:first-child {
background-color: beige;
}
</style>
</head>
<body>
<div id="box">
<div class="cell">
<span>序号</span>
<span>名字</span>
<span>单价</span>
<span>数量</span>
<span>小计</span>
</div>
</div>
<script>
const products = [
{ name: 'iPhone1', price: 4999, count: 4 },
{ name: 'iPhone2', price: 5999, count: 1 },
{ name: 'iPhone3', price: 6999, count: 7 },
{ name: 'iPhone4', price: 7999, count: 10 },
]
const products_el = products.map((value, index) => {
// ctrl+i: 弹出提示
const { name, count, price } = value
return `<div class="cell">
<span>${index + 1}</span>
<span>${name}</span>
<span>¥${price}</span>
<span>${count}</span>
<span>¥${price * count}</span>
</div>`
})
// 累加拼接:保留默认内容
const box = document.getElementById('box')
box.innerHTML += products_el.join('')
</script>
</body>
</html>
购物车 - 1.7pm 108分
<!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>
table {
border-collapse: collapse;
}
td {
border: 1px solid gray;
padding: 10px;
text-align: center;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<td><input type="checkbox" />全选</td>
<td>序号</td>
<td>商品名</td>
<td>单价</td>
<td>数量</td>
<td>小计</td>
</tr>
</thead>
<tbody>
<tr>
<td>xxx</td>
<td>xxx</td>
<td>xxx</td>
<td>
<button>-</button>
<span>xxx</span>
<button>+</button>
</td>
<td>xxx</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="6">合计:xxx</td>
</tr>
</tfoot>
</table>
<script>
const products = [
{ name: '苹果', price: 10, count: 30, checked: true },
{ name: '香蕉', price: 6, count: 3, checked: true },
{ name: '柿子', price: 26, count: 1, checked: true },
{ name: '哈密瓜', price: 19, count: 45, checked: true },
]
const products_el = products.map((value, index) => {
const { count, name, price, checked } = value
// 添加自定义属性: index, 保存序号
return `<tr data-index='${index}'>
<td><input type="checkbox" ${checked ? 'checked' : ''} /></td>
<td>${index + 1}</td>
<td>${name}</td>
<td>¥${price}</td>
<td>
<button ${count == 1 ? 'disabled' : ''}>-</button>
<span>${count}</span>
<button>+</button>
</td>
<td>¥${price * count}</td>
</tr>`
})
document.querySelector('tbody').innerHTML = products_el.join('')
// 难点1: + 和 -
// 思路1: 查询所有的按钮, 遍历挨个添加 事件
// 思路2: 委托 -> 只需要给按钮共同的父元素添加事件
const tbody = document.querySelector('tbody')
tbody.onclick = function (e) {
// 过滤出按钮的点击
if (e.target.localName == 'button') {
console.log(e.target)
// 按钮的 父元素 的 父元素的 自定义属性.index
const index = e.target.parentElement.parentElement.dataset.index
console.log('序号:', index)
// 现在场景: 页面是通过数据数组生成的, 他们应该联动
if (e.target.innerHTML == '+') {
products[index].count++
}
if (e.target.innerHTML == '-') {
products[index].count--
}
console.log(products)
//更新对应序号的界面
updateCell(index)
}
}
// 数据变化后, 要同步更新对应的栏目
function updateCell(index) {
// 通过序号, 从tbody孩子中, 找到对应的一条
const cell = tbody.children[index]
const subtotal = cell.lastElementChild //小计元素
// 数量: 小计上方元素的孩子中的序号1
const count_el = subtotal.previousElementSibling.children[1]
// 从数据数组中, 读取 单价 和 数量
const { count, price } = products[index]
// 把数据 赋值给 元素内容
count_el.innerHTML = count
subtotal.innerHTML = '¥' + price * count
// -按钮
const jian_el = count_el.previousElementSibling
// 数量==1 是true, 则说明 不可用是 true
// jian_el.disabled = count == 1 ? true : false
jian_el.disabled = count == 1
// 更新合计
updateTotal()
}
//合计
function updateTotal() {
// 数组高阶函数: reduce, 合并归纳数组数据
// sum是总和, 0是初始值, value是每次遍历的元素
const total = products.reduce((sum, value) => {
const { price, count, checked } = value
// 只累加 勾选的元素的价格, 此处利用 true1 false0
// 乘以 checked, 如果是false, 不勾选,就是0, 不累加
return sum + price * count * checked
}, 0)
// 获取合计元素
const total_el = document.querySelector('tfoot td')
total_el.innerHTML = '合计: ¥' + total
}
// 初始时: 触发一次总和
updateTotal()
// 全选按钮初始状态: 数组中的每一个都是勾选,则全选真的
const cha = document.querySelector('thead input')
// 忘记的人: 回顾 JSCORE 的day03
cha.checked = products.every(value => value.checked)
// 全选按钮变化时, 让其他勾选都变化
cha.onchange = function () {
//找到其他的所有按钮
const chs = document.querySelectorAll('tbody input')
// 遍历每一项, 让其勾选状态 和 全选按钮的一致
chs.forEach(value => (value.checked = cha.checked))
// 同步更新数据中的 checked
products.forEach(value => (value.checked = cha.checked))
// 重新算总和
updateTotal()
}
// 为每一个单选按钮, 添加change事件:
const chs = document.querySelectorAll('tbody input')
chs.forEach(value => {
value.onchange = function () {
// 获取序号
const index = this.parentElement.parentElement.dataset.index
// 修改对应数据项的checked属性, 与当前选框的 选中状态一样
products[index].checked = this.checked
// 更新总价格
updateTotal()
// 全选按钮要跟随单选变化: 数据中的每一个元素, 都是勾选才是全选
cha.checked = products.every(value => value.checked)
}
})
</script>
</body>
</html>