回顾
DOM: Document Object Model
- 可以自定义的利用JS 操作页面
HTML 和 CSS 仅仅提供了一些基础的功能, 实际开发中 无法胜任复杂的需求
学习DOM: 定制化的为项目提供功能
重点:
-
document对象: 所有操作DOM有关的API都在这里
-
查找元素:
- 利用css选择器 -
全局- querySelector: 查询
首个满足条件的元素 - querySelectorAll: 查询
所有满足条件的元素
- querySelector: 查询
- 利用关系
- 父: parentElemnt
- 子: children
- 兄: previousElementSibling
- 弟: nextElementSibling
- 利用某些特征 -
全局- id: getElementById
- 利用css选择器 -
-
操作元素
-
样式操作
- class - 适合固定样式
- className: 直接操作 class 属性本体
- classList: 一个集合 - 包含一些快速操作class的相关方法
- style - 适合
动态样式
- class - 适合固定样式
-
事件: 都是 on 开头
- onclick: 点击
- onmouseover: 鼠标悬浮
-
零散的属性
-
图片的src: 决定 img 标签呈现的内容
-
自定义属性
- 随便写: 必须用
getAttribute读取 –不推荐, 旧方案 - 固定格式:
data-属性名, 读取通过 dataset.属性名
- 随便写: 必须用
-
内容
- innerHTML: 读取
文本+标签+换行符 - innerText: 读取
文本
如果标签中仅有文本, 则两者读取的内容无差别
- innerHTML: 写入的内容 作为HTML代码解析展示
- innerText: 写入的内容 作为文本展示
- innerHTML: 读取
-
-
作业
<!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>作业 09:13</title>
<link rel="stylesheet" href="./reset.css">
<style>
#box {
width: 604px;
cursor: pointer;
overflow: hidden;
}
#box>ul {
display: flex;
background-color: black;
user-select: none;
}
#box>ul>li {
color: #acaebb;
padding: 10px 0;
/* 每个li 在主轴上 都占1份 */
flex: 1;
text-align: center;
}
#box>ul>li.active {
background-color: #262626;
color: #e5bb54;
}
#box>div {
display: flex;
transition: 0.2s;
position: relative;
left: 0;
}
</style>
</head>
<body>
<div id="box">
<div>
<img src="./wzry/111.jpeg" alt="">
<img src="./wzry/222.jpeg" alt="">
<img src="./wzry/333.jpeg" alt="">
<img src="./wzry/444.jpeg" alt="">
<img src="./wzry/555.jpeg" alt="">
</div>
<ul>
<li data-i="0" class="active">世界冠军杯</li>
<li data-i="1">皮肤创作前瞻</li>
<li data-i="2">云中专线</li>
<li data-i="3">弈星滕王阁序</li>
<li data-i="4">峡谷搞事团</li>
</ul>
</div>
<script>
const lis = document.querySelectorAll('#box li')
lis.forEach(li => li.onmouseover = function () {
lis.forEach(li => li.classList.remove('active'))
this.classList.add('active')
// 获取当前激活项的序号, 计算出对应的偏移量 -?00%
// 设置给 图片所在div的 style.left
const all = this.parentElement.children
// 相当于 all.indexOf(this) : 从 all 数组里获取 当前this的序号
// const i = Array.prototype.indexOf.call(all, this)
for (let i = 0; i < all.length; i++) {
if (all[i].classList.contains('active')) {
const div = this.parentElement.previousElementSibling
div.style.left = `-${i}00%`
break
}
}
})
</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>输入框事件 10:19</title>
</head>
<body>
<input type="text" value="世宇">
<script>
// 相关事件:
const inp = document.querySelector('input')
// 获得焦点 focus
inp.onfocus = function () {
console.log('focus: 获得焦点')
}
// 失去焦点 blur
inp.onblur = function () {
console.log('blur: 失去焦点');
}
// 内容变化 change
// 触发条件: 内容有修改 - 按回车 或 失去焦点时
inp.onchange = function () {
console.log('change: 内容变化', this.value)
}
// 实时变化 input
inp.oninput = function () {
console.log('input:', this.value)
}
// 键盘按键抬起 keyup
// 事件对象: 当触发事件时, 系统会自动把 此次事件相关的信息作为参数传递给函数
inp.onkeyup = function (e) {
// 形参名: event事件, 偷懒则简写成 e
console.log('keyup')
// console.log(arguments)
console.log(e)
// keyCode 代表按键的编号, 每个按键都有自己的编号
// 回车是13
if (e.keyCode == 13) alert("回车!")
}
</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>练习 10:49</title>
</head>
<body>
<input type="text" placeholder="菜品名,食材名">
<ul>
<!-- 尝试用map 把请求到的数据中的 name 属性, 放到li标签里, 添加到 ul 里. 利用 join() 拼接, 用 innerHTML设置 -->
<!-- <li>Gemside捷赛自动烹饪锅多功能懒人锅P18</li> -->
</ul>
<script>
// 任务: 在输入框中按回车, 读取输入框中的值, 然后发请求获取数据
const inp = document.querySelector('input')
// 事件参数中: 编号13的按键 是回车
inp.onkeyup = function (e) {
if (e.keyCode == 13) {
console.log('搜索:', this.value)
// 从 xin666.vip 找到接口文档
const url = `https://serverms.xin88.top/mall/search?type=1&kw=${this.value}&page=1`
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onload = function () {
const data = JSON.parse(xhr.response)
console.log(data)
const ul = document.querySelector('ul')
ul.innerHTML = data.data.map((item) => {
return `<li>${item.name}</li>`
}).join('')
}
xhr.send()
}
}
</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>正则表达式 11:37</title>
<style>
ul>li {
display: none;
}
/* 对 ok */
ul.ok>li:first-child {
display: block;
}
/* 错 err */
ul.err>li:last-child {
display: block;
}
</style>
</head>
<body>
<!-- 正则表达式: regular expression 简称 RegExp -->
<!-- 是一套通用的 对字符串进行格式验证的方案 -->
<!-- 非JS专属, 使用正则 需要学习正则元字符 -->
<!-- 文档: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions -->
<input type="text">
<ul>
<li style="color: green;">手机号正确</li>
<li style="color: red;">手机号错误</li>
</ul>
<script>
// \d 代表 1个数字 0 ~ 9
// 任务: 让用户输入一些文字, 我们从其中找出所有的数字
const inp = document.querySelector('input')
const ul = inp.nextElementSibling //下一个兄弟
// 失去焦点
inp.onblur = function () {
// match: 匹配; 从字符串中匹配出 符合正则表达式要求的内容
// /\d/ : 字面量语法 /正则/ , 把正则符号 转为JS的正则对象
// g: 修饰符, global 全局匹配, 找到所有符合条件的
const res = this.value.match(/\d/g)
console.log(res)
// 手机号格式: 11位 1开头 第二位3-9
const reg_phone = /^1[3-9]\d{9}$/
// 正则.test(字符串): 用正则验证字符串是否符合格式要求
if (reg_phone.test(this.value)) {
console.log(this.value, '是手机号');
ul.className = 'ok'
// ul.classList.add('ok') //add是累加
// 思考题: 为什么这里不用 classList.add() ???
} else {
ul.className = 'err'
console.log(this.value, '非手机号');
}
}
</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>事件冒泡 14:05</title>
<style>
#red {
background-color: red;
width: 500px;
height: 500px;
}
#green {
background-color: green;
width: 300px;
height: 300px;
}
#blue {
background-color: blue;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="red">
<div id="green">
<div id="blue"></div>
</div>
</div>
<script>
// 事件冒泡: 子元素上触发事件, 会通知父元素, 触发相同的事件
// 触发事件的当事元素: target
red.onclick = function (e) {
// 事件参数中的target: 代表触发事件的当事元素
console.log('red clicked!');
console.log('当事元素:', e.target)
}
green.onclick = function (e) {
console.log('green clicked!');
console.log('当事元素:', e.target)
}
blue.onclick = function (e) {
console.log('blue clicked!');
console.log('当事元素:', e.target)
// 阻止冒泡
// stop:停止 propagation:传播
e.stopPropagation()
}
</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>事件委托 14:24</title>
<link rel="stylesheet" href="./reset.css">
<style>
li {
padding: 10px;
}
.active {
background-color: orange;
}
</style>
</head>
<body>
<!--
事件委托: 子元素把事件的处理操作, 委托给父元素
-->
<ul>
<li>亮亮</li>
<li>铭铭</li>
<p>世宇11</p>
<li>泡泡</li>
<p>世宇22</p>
<li>小新</li>
</ul>
<script>
// const lis = document.querySelectorAll('li')
// lis.forEach(li => li.onclick = function () {
// this.classList.add('active')
// })
const ul = document.querySelector('ul')
ul.onclick = function (e) {
console.log('ul clicked!', e.target)
// 注意: 委托模式下 所有子元素 及 自身 都会触发事件, 必须利用判断, 选择对哪些元素进行操作
console.dir(e.target)
// 判断标签名是 li , 再激活; 过滤掉不是 li 的元素
if (e.target.localName == 'li') {
// 当事元素 添加激活
e.target.classList.add('active')
}
}
// 取舍:
// 事件委托适合为动态新增的子元素添加事件
</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>动态新增 14:48</title>
<style>
li.active {
padding: 10px;
margin-bottom: 4px;
background-color: orange;
}
</style>
</head>
<body>
<button>新增元素</button>
<ul>
<li>泡泡</li>
</ul>
<script>
const btn = document.querySelector('button')
const ul = btn.nextElementSibling
btn.onclick = function () {
ul.innerHTML += '<li>亮亮</li>'
}
// 1. 用循环方式,给li加事件. 点击后激活. 试一试新增的元素是否有效果, 为什么?
// 此代码执行时, 只有泡泡被查询到 然后添加了事件
const lis = document.querySelectorAll('li')
lis.forEach(li => li.onclick = function () {
this.classList.add('active')
})
// 2. 再试试 用委托方式 实现 li的点击激活, 试试新增元素是否有效果, 为什么?
// 父元素不变, 所有的子元素 不分新旧, 触发的事件都会被父元素处理
ul.onclick = function (e) {
e.target.classList.add('active')
}
</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>练习 15:21</title>
<link rel="stylesheet" href="./reset.css">
<style>
ul {
display: flex;
}
ul>li {
margin: 4px;
padding: 5px;
user-select: none;
}
ul>li.active {
color: orange;
border-bottom: 2px solid orange;
}
</style>
</head>
<body>
<ul>
<li></li>
</ul>
<script>
const url = 'http://douyu.xin88.top/api/cate/recList'
const ul = document.querySelector('ul')
// 发请求. 获取数据. 转为 li 添加到 ul 标签里
const xhr = new XMLHttpRequest()
xhr.open('get', url)
xhr.onload = function () {
const data = JSON.parse(xhr.response)
console.log(data)
ul.innerHTML = data.data.map((item, index) => {
// 序号0 的, 添加激活样式, 其他的不加
return `<li class="${index == 0 ? 'active' : ''}">${item.name}</li>`
}).join('')
}
xhr.send()
// 书写css样式, 首个li要激活, 点击哪个li 哪个激活
// 问题: 网络请求是异步操作, 还没请求到 就开始找li 必然找不到
// const lis = document.querySelectorAll('li')
// lis.forEach(li => li.onclick = function () {
// this.classList.add('active')
// })
// 动态新增元素: 用委托
ul.onclick = function (e) {
// 当事元素不是 li , 则终止操作
if (e.target.localName != 'li') return
//查找激活项, 去掉其激活状态
const ac = document.querySelector('ul>li.active')
ac.classList.remove('active')
e.target.classList.add('active')
}
</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>事件监听器 16:20</title>
</head>
<body>
<button>点我</button>
<script>
const btn = document.querySelector('button')
// onclick属性: 同时只能赋1个值
btn.onclick = function () {
console.log(11)
}
// 后赋值的 覆盖 先赋值
btn.onclick = function () {
console.log(22)
}
// 实际开发时, 往往会团队合作 或 工期比较长
// 如何给1个元素绑定多个事件, 互不影响?
// 事件监听器:
// add添加 event事件 listener监听器
// 参数1: 事件名 -- 注意 没有on前缀
// 参数2: 触发时调用的函数
btn.addEventListener('click', function () {
console.log(33)
})
btn.addEventListener('click', function () {
console.log(44)
})
// 删除事件监听器
function show() {
alert("Hello")
// 只执行一次, 就删除
// remove:删除
btn.removeEventListener('click', show)
}
btn.addEventListener('click', show)
</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>页面滚动监听 16:36</title>
<style>
body,
ul {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<!--
滚动监听: 监听页面滚动的距离, 进行判断后, 执行一些操作
例如: 显示额外的组件, 触底加载更多数据...
-->
<ul></ul>
<script>
const ul = document.querySelector('ul')
for (let i = 0; i < 200; i++) {
ul.innerHTML += `<li>${i}</li>`
}
// 滚动事件: scroll
addEventListener('scroll', function () {
// 读取滚动距离顶部的偏移量
// 由于浏览器的 版本兼容性问题: 如果body中读不到, 就从后面的读
const offsetY = document.body.scrollTop || document.documentElement.scrollTop
console.log('滚动...', offsetY);
// 判断触底: 获取整个页面的高度 和 滚动的高度对比
console.log('页面可视区域高度:', innerHeight);
//页面内容
console.log('body高:', document.body.clientHeight);
// 设定距离底部50像素 算触底
const y = offsetY + innerHeight + 50
if (document.body.clientHeight < y) {
console.log("触底!")
}
})
// 逻辑短路语法:
// 逻辑或的设定: 从左到右第一个真值, 如果没有真的,则是最后一个
var a = '' || 0 || false || 44 || null || undefined
console.log(a)
var a = '' || 0 || false || null || undefined
console.log(a)
</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>封装DOM</title>
</head>
<body>
<ul>
<li>泡泡</li>
<li>铭铭</li>
<li>亮亮</li>
</ul>
<script>
// DOM是必须掌握的技能, 但是原生DOM书写太繁琐, 可以利用封装技巧, 把复杂的DOM操作进行简化
class Query {
constructor(sel) {
const els = document.querySelectorAll(sel)
console.log('els:', els)
// 把查询到的内容 移动到 构造的对象里
for (const key in els) {
// this[0] = li
// this[1] = li
// this[2] = li
// this['length'] = 3
this[key] = els[key]
}
}
css(name, value) {
for (let i = 0; i < this.length; i++) {
const li = this[i]
li.style[name] = value
}
}
}
// new: 触发构造方法 constructor
// const q = new Query('li')
// console.log('q:', q)
// q.css('color', 'red')
// new Query('li').css('color', 'red')
// $ :比较特别, 显眼, 换别的词也行
function $(sel) {
return new Query(sel)
}
// 期望效果:
// 查询到所有的 li 元素, 设置 style.color = 'red'
$('li').css('color', 'red')
$('li').css('border', '2px solid blue')
// 扩展: 仿造css, 制作一个click 方法, 自动遍历 为 li 添加onclick 事件
$('li').click(function () {
this.style.background = 'orange'
})
// 套路总结:
// 换原型: DOM默认查询出来的原型太简单, 提供的方法太少, 所以用起来不方便
// 解决: 自己做一套新的原型, 包含更多好用的方法
</script>
</body>
</html>
总结
DOM: 是程序员必须掌握的技能! 定制化开发网站全靠DOM
- 但是: 原生DOM操作书写过于复杂
高手帮你封装了框架 jQuery, 大大简化了 DOM操作的代码
本文介绍了DOM的基本概念,强调其在前端定制化开发中的重要性。内容涵盖查询和操作元素的方法,如querySelector、querySelectorAll、getElementById等,以及样式操作、事件监听器和事件冒泡。还讨论了动态内容、事件委托、滚动事件和封装理念,提醒读者DOM操作虽然复杂,但框架如jQuery能简化这一过程。
9536

被折叠的 条评论
为什么被折叠?



