1. 事件类型
1.1 鼠标事件
1.1.1 click 鼠标点击
1.1.2 mouseenter 鼠标进入
1.1.3 mouseleave 鼠标离开
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div class="box">div 盒子</div>
<!--
鼠标事件
click 鼠标点击
mouseenter 鼠标进入
mouseleave 鼠标离开
-->
<script>
const box = document.querySelector('.box')
box.addEventListener('mouseenter', function () {
console.log('mouseenter')
})
box.addEventListener('mouseleave', function () {
console.log('mouseleave')
})
</script>
</body>
</html>
1.2 焦点事件
1.2.1 focus 获得焦点
1.2.2 blur 失去焦点
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* css选择器 → 属性选择器 */
[type=text] {
width: 245px;
height: 50px;
padding-left: 20px;
border: 1px solid #ccc;
font-size: 17px;
outline: none;
}
</style>
</head>
<body>
<input type="text" class="inp1" value="原本value">
<input type="text" class="inp2">
<!--
焦点事件
focus 获得焦点
blur 失去焦点
-->
<script>
const inp1 = document.querySelector('.inp1')
const inp2 = document.querySelector('.inp2')
inp1.addEventListener('focus', function () {
console.log('focus')
})
inp1.addEventListener('blur', function () {
console.log('blur')
console.log(inp1.value)
})
</script>
</body>
</html>
案例1_小米搜索框显示隐藏案例
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>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul {
list-style: none;
}
.mi {
position: relative;
width: 223px;
margin: 100px auto;
}
.mi .search-text {
width: 223px;
height: 48px;
padding: 0 10px;
font-size: 14px;
line-height: 48px;
border: 1px solid #e0e0e0;
outline: none;
}
/* 搜索框边框颜色 */
.mi .search {
border: 1px solid #ff6700;
}
/* 下拉菜单 */
.result-list {
/* 先隐藏下拉菜单 */
display: none;
position: absolute;
left: 0;
top: 48px;
width: 223px;
border: 1px solid #ff6700;
border-top: 0;
background: #fff;
}
.result-list a {
display: block;
padding: 6px 15px;
font-size: 12px;
color: #424242;
text-decoration: none;
}
.result-list a:hover {
background-color: #eee;
}
</style>
</head>
<body>
<div class="mi">
<input type="search" placeholder="小米笔记本" class="search-text">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
const searchText = document.querySelector('.search-text')
const list = document.querySelector('.result-list')
// 获得焦点:输入框的边框颜色变橙色search类;子菜单显示出来
// display:none;none是隐藏;block(块级)
searchText.addEventListener('focus', function () {
this.classList.add('search')
list.style.display = 'block'
})
// 失去焦点:输入框的边框颜色变灰色;子菜单隐藏起来
searchText.addEventListener('blur', function () {
this.classList.remove('search')
list.style.display = 'none'
})
</script>
</body>
</html>
1.3 键盘事件
1.3.1 keydown 键盘按下
1.3.2 keyup 键盘抬起
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
textarea {
width: 300px;
height: 30px;
padding: 10px;
border-color: transparent;
outline: none;
resize: none;
background: #f5f5f5;
border-radius: 4px;
}
</style>
</head>
<body>
<!-- input 不换行 -->
<textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
<!--
键盘事件
keydown 键盘按下
keyup 键盘抬起
文本事件 input 当表单value 被修改时触发
执行顺序:keydown → input → keyup
-->
<script>
const tx = document.querySelector('#tx')
tx.addEventListener('keydown', function () {
console.log('键盘按下')
})
tx.addEventListener('input', function () {
console.log('input')
})
tx.addEventListener('keyup', function () {
console.log('键盘抬起')
})
</script>
</body>
</html>
案例2_统计用户输入字数
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>
<style>...</style>
</head>
<body>
<div class="wrapper">
<i class="avatar"></i>
<!-- maxlength 输入的最大字符数 -->
<textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
<button>发布</button>
</div>
<div class="wrapper">
<span class="total">0/200字</span>
</div>
<div class="list">
<div class="item" style="display: none;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
<p class="time">2099-10-10 20:29:21</p>
</div>
</div>
</div>
<!--
统计用户输入字数
需求:用户输入文字,可以计算用户输入的字数
分析:
①:文本域获得焦点则显示统计 total 盒子,失去焦点则隐藏统计 total 盒子
②:文本域输入内容,使用input事件,不断取得字符长度(文本域.value.length )
③:把获得字符长度赋值给 total 字数统计盒子
-->
<script>
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
tx.addEventListener('focus', function () {
total.style.opacity = 1
})
tx.addEventListener('input', function () {
let num = tx.value.length
total.innerHTML = `${num}/200字`
})
tx.addEventListener('blur', function () {
total.style.opacity = 0
})
</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>页面滚动事件</title>
<style>
body {
height: 3000px;
}
a {
position: fixed;
right: 10px;
bottom: 10px;
}
</style>
</head>
<body>
<a href="#">返回顶部</a>
<!-- 滚动事件scroll 当元素或页面滚动时触发 -->
<script>
// 监听整个页面滚动,window.addEventListener('scroll', function () { })
// (监听某个元素的内部滚动直接给某个元素加即可)
window.addEventListener('scroll', function () {
// document.documentElement 获取html元素
// document.body 获取body元素
// scrollTop 获取被卷入的头部 scrollTop和scrollLeft可读写
console.log(document.documentElement.scrollTop)
})
const btn = document.querySelector('a')
btn.addEventListener('click', function () {
// 页面滚动是 html元素 滚动
document.documentElement.scrollTop = 0
})
</script>
</body>
</html>
案例3_xtx返回顶部
html
复制代码
<!-- 返回顶部 -->
<div class="xtx-elevator">
<ul class="xtx-elevator-list">
<li><a href="javascript:;" id="backTop"><i class="sprites"></i>顶部</a></li>
</ul>
</div>
<script>
const elevator = document.querySelector('.xtx-elevator')
html = document.documentElement
window.addEventListener('scroll', function () {
if (html.scrollTop >= 1000) {
elevator.style.display = 'block'
} else {
elevator.style.display = 'none'
}
})
elevator.addEventListener('click', function () {
html.scrollTop = 0
})
</script>
2. 事件对象
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
margin-bottom: 20px;
background-color: pink;
}
textarea {
width: 300px;
height: 30px;
padding: 10px;
border-color: transparent;
outline: none;
resize: none;
background: #f5f5f5;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="box"></div>
<textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
<!--
事件对象:包含事件触发时的相关信息,包含属性和方法
注册事件中,回调函数的第一个参数就是事件对象
一般命名为event、ev、e
-->
<!--
事件对象-常见属性
offsetX(number):事件发生时,鼠标相对于事件源的x坐标
offsetY(number):事件发生时,鼠标相对于事件源的y坐标
target(object):事件源对象
key(string):如果是键盘相关事件,则事件对象中包含该属性,表示键盘事件发生时,按下的是什么键。'Enter'回车键
-->
<script>
const box = document.querySelector('.box')
box.addEventListener('click', function (event) {
console.log(event)
})
const tx = document.querySelector('#tx')
tx.addEventListener('keydown', function (event) {
console.log(event.key)
})
</script>
</body>
</html>
案例4_回车发布评论
html
复制代码
<body>
<div class="wrapper">
<i class="avatar"></i>
<textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
<button>发布</button>
</div>
<div class="wrapper">
<span class="total">0/200字</span>
</div>
<div class="list">
<div class="item" style="display: none;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
<p class="time">2099-10-10 20:29:21</p>
</div>
</div>
</div>
<!--
需求:按下回车键,可以发布评论
功能:
①:按下回车,可以显示评论信息,并且评论内容显示到对应位置
②:输入完毕,文本域清空内容,并且字数复原为 0
-->
<script>
const tx = document.querySelector('#tx')
const list = document.querySelector('.list')
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
list.innerHTML = `
<div class="item" style="display: flex;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">${tx.value}</p>
<p class="time">2099-10-10 20:29:21</p>
</div>
</div>
`
tx.value = ''
})
tx.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
btn.click()
}
})
</script>
</body>
3. 事件流
3.1 事件捕获
3.2 事件冒泡
3.3 阻止冒泡
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
margin: 100px auto;
width: 300px;
height: 300px;
background-color: skyblue;
}
.son {
margin: 80px auto;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div class="father">
父盒子
<div class="son">子盒子</div>
</div>
<!--
事件流指的是事件完整执行过程中的流动路径,
当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
事件捕获:当一个元素的事件被触发时,会从DOM的根元素开始依次调用同名事件 (从外到里)
addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用),若传入false代表冒泡阶段触发,默认就是 false
事件冒泡: 当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。
事件冒泡是默认存在的,实际工作都是使用事件冒泡为主(子到父)
阻止冒泡:若想把事件就限制在当前元素内,就需要阻止事件冒泡
事件对象.stopPropagation()
-->
<script>
const father = document.querySelector('.father')
const son = document.querySelector('.son')
document.body.addEventListener('click', function (event) {
console.log('body')
})
father.addEventListener('click', function (event) {
console.log('father')
})
son.addEventListener('click', function (event) {
console.log('son')
event.stopPropagation()
})
</script>
</body>
</html>
3.4 鼠标经过/离开事件的区别
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
margin: 100px auto;
width: 300px;
height: 300px;
background-color: skyblue;
}
.son {
margin: 80px auto;
width: 100px;
height: 100px;
background-color: pink;
}
</style>
</head>
<body>
<div class="father">
父盒子
<div class="son">子盒子</div>
</div>
<!--
mouseover 和 mouseout 会有冒泡
mouseenter 和 mouseleave 没有冒泡 (常用)
-->
<script>
const father = document.querySelector('.father')
const son = document.querySelector('.son')
father.addEventListener('mouseenter', function (event) {
console.log('father')
})
son.addEventListener('mouseenter', function (event) {
console.log('son')
})
/* father.addEventListener('mouseover', function (event) {
console.log('father')
})
son.addEventListener('mouseover', function (event) {
console.log('son')
}) */
</script>
</body>
</html>
3.5 阻止默认行为
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- form 表单区域 可以提交数据 action提交地址 -->
<form action="">
姓名: <input type="text" name="username">
<button>提交</button>
</form>
<a href="http://www.baidu.com">点击跳转</a>
<!--
阻止默认行为:阻止元素发生默认的行为
例如:当点击提交按钮时阻止对表单的提交,阻止链接的跳转等等
事件对象.preventDefault()
-->
<script>
const a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.preventDefault()
})
// 表单的提交事件 → form submit
const form = document.querySelector('form')
form.addEventListener('submit', function (e) {
console.log('触发form')
// 阻止默认行为
e.preventDefault()
})
</script>
</body>
</html>
3.6 事件委托
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>
<link rel="stylesheet" href="./index.css">
<script src="./data.js"></script>
</head>
<body>
<div class="container">
<button type="button" class="btn add" id="btn-add">
<span>添加员工</span>
</button>
<table class="order">
<thead>
<tr>
<th>头像</th>
<th>姓名</th>
<th>工号</th>
<th>入职时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><span class="username">管</span></td>
<td>管理员</td>
<td>10000</td>
<td>2022-10-24</td>
<td>
<button class="btn edit">编辑</button>
<button class="btn del">删除</button>
</td>
</tr>
<tr>
<td><span class="username">孙</span></td>
<td>孙彩</td>
<td>10000</td>
<td>2022-09-24</td>
<td>
<button class="btn edit">编辑</button>
<button class="btn del">删除</button>
</td>
</tr>
<tr>
<td><span class="username">罗</span></td>
<td>罗晓晓</td>
<td>10002</td>
<td>2022-08-24</td>
<td>
<button class="btn edit">编辑</button>
<button class="btn del">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<!--
事件委托(Event Delegation):也称为事件委派、事件代理
将原本需要注册在子元素的事件委托给父元素,让父元素担当事件监听的职务
优点:减少注册次数,可以提高程序性能; 动态生成的元素也能触发事件
事件委托其实是利用事件冒泡的特点
给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
-->
<script>
// 需求: 点击每个删除按钮,都会弹出询问框确认是否删除
const tbody = document.querySelector('tbody')
tbody.addEventListener('click', function (e) {
// 事件对象.target.classList.contains() 可以判断真正触发事件的元素是否包含指定类名
if (e.target.classList.contains('del')) {
console.log('del')
}
if (e.target.classList.contains('edit')) {
console.log('edit')
}
})
// 新增功能(素材代码,课上无需实现,课上只需讲解事件委托代码即可)
document.querySelector('#btn-add').addEventListener('click', function () {
// 随机用户名
const uname = createRandomName()
// 随机工号
const workNum = createWorkNum()
// 新增一行内容
tbody.innerHTML += `
<tr>
<td><span class="username">${uname[0]}</span></td>
<td>${uname}</td>
<td>${workNum}</td>
<td>2021-03-13</td>
<td>
<button class="btn edit">编辑</button>
<button class="btn del">删除</button>
</td>
</tr>
`
})
</script>
</body>
</html>
案例5_价格筛选
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>
<style>...</style>
</head>
<body>
<!-- 筛选链接 -->
<div class="filter">
<a class="tab" data-index="1" href="javascript:;">0-100元</a>
<a class="tab" data-index="2" href="javascript:;">100-300元</a>
<a class="tab" data-index="3" href="javascript:;">300元以上</a>
<a class="tab" href="javascript:;">全部区间</a>
</div>
<!-- 渲染盒子 -->
<div class="list">
<!-- <div class="item">
<img src="" alt="">
<p class="name"></p>
<p class="price"></p>
</div> -->
</div>
<script>
// 初始化数据
const goodsList = [
{
id: '4001172',
name: '称心如意手摇咖啡磨豆机咖啡豆研磨机',
price: '289.00',
picture: 'https://yanxuan-item.nosdn.127.net/84a59ff9c58a77032564e61f716846d6.jpg',
},
]
// 渲染商品列表
function render(arr) {
const str = arr.map(item => {
const { name, price, picture } = item
return `
<div class="item">
<img src="${picture}" alt="">
<p class="name">${name}</p>
<p class="price">${price}</p>
</div>
`
}).join('')
document.querySelector('.list').innerHTML = str
}
render(goodsList)
// 筛选功能
const filter = document.querySelector('.filter')
filter.addEventListener('click', function (e) {
// console.log(e)
// console.log(e.target)
// console.log(e.target.dataset)
let index = e.target.dataset.index
// 0-100元
if (index == 1) {
// console.log(11)
const newArr = goodsList.filter(el => {
return el.price <= 100
})
render(newArr)
} else if (index == 2) {
const newArr = goodsList.filter(el => {
return el.price <= 300
})
render(newArr)
} else if (index == 3) {
const newArr = goodsList.filter(el => {
return el.price > 300
})
render(newArr)
} else {
render(goodsList)
}
})
</script>
</body>
</html>
4. 移除事件监听
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="L2">L2事件</button>
<button class="L0">L0事件</button>
<!--
移除事件监听, 移除事件处理函数,也称为解绑事件
-->
<!--
两种注册事件的区别
⚫ 传统on注册(L0)
➢ 同一个对象,后面注册的事件会覆盖前面注册(同名事件)
➢ 直接使用null覆盖偶就可以实现事件的解绑
➢ 只有冒泡阶段,没有捕获阶段
⚫ 事件监听注册(L2)
➢ 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
➢ 后面注册的事件不会覆盖前面注册的事件(同名事件)
➢ 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)实现事件解绑
➢ 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
➢ 匿名函数无法被解绑
-->
<script>
const L2 = document.querySelector('.L2')
const L0 = document.querySelector('.L0')
/* L2.addEventListener('click', function () {
console.log('L2')
}) */
/*
移除L2事件监听
addEventListener方式注册,必须使用:removeEventListener移除
注意:匿名函数无法解绑
*/
function fn() {
console.log('L2')
}
L2.addEventListener('click', fn)
L2.removeEventListener('click', fn)
/*
移除L0事件监听
on事件方式,直接使用null覆盖偶就可以实现事件的解绑
*/
L0.onclick = function () {
console.log('L0')
}
L0.onclick = null
</script>
</body>
</html>
5. 作业
5.1 顺丰快递单号查询
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.search {
position: relative;
width: 178px;
margin: 100px;
}
.sf {
height: 30px;
padding-left: 10px;
outline: none;
}
.con {
display: none;
position: absolute;
top: -43px;
min-width: 178px;
max-width: 440px;
/* 让连续的数字自动换行 */
word-wrap: break-word;
border: 1px solid rgba(0, 0, 0, .2);
box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
padding: 5px;
font-size: 18px;
line-height: 20px;
color: #333;
}
.con::before {
content: '';
width: 10px;
height: 10px;
position: absolute;
top: 25px;
left: 18px;
border-left: 1px solid rgba(0, 0, 0, .2);
border-bottom: 1px solid rgba(0, 0, 0, .2);
background-color: #fff;
transform: rotate(-45deg);
}
</style>
</head>
<body>
<div class="search">
<div class="con"></div>
<input type="text" placeholder="请输入您的快递单号" class="sf">
</div>
<!--
需求如下:
输入框获得焦点,如果没有值需要隐藏放大框
输入框焦去焦点,隐藏放大框
输入框中输入内容,没有值就隐藏放大框,有值就显示放大框,并将输入内容显示在放大框中
-->
<script>
const ipt = document.querySelector('.sf')
const con = document.querySelector('.con')
let num = 0
ipt.addEventListener('focus', function () {
// console.log(ipt.value)
// if (this.value !== '') {
if (num > 0) {
con.style.display = 'block'
}
})
ipt.addEventListener('blur', function () {
con.style.display = 'none'
})
ipt.addEventListener('input', function () {
num = ipt.value.length
if (num > 0) {
con.style.display = 'block'
con.innerText = ipt.value
} else {
con.style.display = 'none'
}
})
</script>
</body>
5.2 小米密码框
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>
<style>
.mi-form {
display: table;
width: 356px;
height: 60px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0);
background-color: #f9f9f9;
}
.mi-control {
position: relative;
display: table-cell;
width: 294px;
}
.mi-input {
box-sizing: border-box;
width: 100%;
height: 60px;
border: 0;
padding: 30px 20px 10px;
outline: none;
background: none;
appearance: none;
font-size: 17px;
font-family: "Noto Color Emoji";
color: #333;
line-height: 20px;
}
.mi-control label {
user-select: none;
position: absolute;
top: 20px;
left: 20px;
height: 20px;
font-weight: 400;
font-size: 17px;
color: rgba(0, 0, 0, .4);
line-height: 20px;
transition: top .15s cubic-bezier(.4, 0, .2, 1), font-size .15s cubic-bezier(.4, 0, .2, 1), color .15s cubic-bezier(.4, 0, .2, 1);
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.mi-control label.active {
top: 6px;
font-size: 12px;
color: #aaa;
}
.mi-password {
display: table-cell;
width: 60px;
color: rgba(0, 0, 0, .85);
font-size: 14px;
vertical-align: middle;
background: url(./images/close.png) center/30px 30px no-repeat;
cursor: pointer;
/* 防止选中文字 防止点击选中文本框的文字 */
user-select: none;
}
.mi-password.active {
background-image: url(./images/open.png);
}
</style>
</head>
<body>
<div class="mi-form">
<div class="mi-control">
<input type="password" class="mi-input">
<label>密码</label>
</div>
<div class="mi-password"></div>
</div>
<!--
输入框获得焦点,文字移动到输入框上面
输入框失焦且输入框无内容,文字跑下来
点击图标动态切换图标以及输入框的类型
-->
<script>
const ipt = document.querySelector('.mi-input')
const label = document.querySelector('label')
const password = document.querySelector('.mi-password')
let pwClick = false
ipt.addEventListener('focus', function () {
label.classList.add('active')
})
ipt.addEventListener('blur', function () {
if (ipt.value == '') {
label.classList.remove('active')
}
})
password.addEventListener('click', function () {
pwClick = !pwClick
/* if (pwClick) {
ipt.type = 'text'
this.style.backgroundImage = url('./images/close.png')
} else {
ipt.type = 'password'
this.style.backgroundImage = url('./images/open.png')
} */
password.classList.toggle('active')
ipt.type = pwClick ? 'text' : 'password'
})
</script>
</body>
</html>
5.3 掘金快捷导航
html
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./fonts/iconfont.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #f4f5f5;
}
ul {
list-style: none;
}
nav {
padding-top: 16px;
padding-bottom: 24px;
background: #3f7ef7;
display: flex;
align-items: center;
justify-content: center;
}
nav .txt {
background-color: #588ff8;
padding: 0px 11px 0 4px;
color: white;
height: 26px;
font-size: 14px;
border-radius: 13px;
margin: 0 5px;
display: flex;
align-items: center;
justify-content: center;
}
.search {
background-color: #3f7ef7;
text-align: center;
padding-top: 24px;
margin: 0 auto;
}
.search-wrapper {
margin: 0 auto;
width: 40%;
position: relative;
}
input {
padding-left: 60px;
width: 100%;
height: 40px;
border: none;
outline: none;
border-radius: 4px;
}
header {
width: 1440px;
height: 54px;
margin: 0 auto;
}
header img {
width: 1440px;
height: 54px;
display: block;
}
.web-icon {
width: 28px;
height: 28px;
position: absolute;
top: 6px;
left: 6px;
}
.web-icon img {
width: 100%;
height: 100%;
display: block;
}
.triangle {
position: absolute;
top: 12px;
left: 38px;
width: 16px;
height: 16px;
line-height: 16px;
color: #c3c8d0;
font-size: 12px;
transition: all .3s;
cursor: pointer;
}
.triangle.active {
transform: rotate(180deg);
}
.txt .iconfont {
width: 20px;
height: 20px;
border-radius: 10px;
color: white;
background-color: black;
text-align: center;
line-height: 20px;
margin-right: 6px;
}
.icon-github {
color: white;
background-color: black;
}
.nav-list {
width: 100%;
margin-top: 10px;
background-color: #fff;
padding: 8px;
border-radius: 4px;
position: absolute;
left: 0;
top: 40px;
display: none;
}
.nav-list.show {
display: block;
backdrop-filter: blur(20px);
}
.item {
float: left;
width: 90px;
height: 90px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-right: 8px;
margin-bottom: 8px;
position: relative;
}
.item.checked::after {
content: '✓';
display: block;
position: absolute;
right: 2px;
top: 2px;
width: 14px;
height: 14px;
border-radius: 50%;
background-color: #3f7ef7;
color: white;
font-size: 12px;
}
.item:hover {
background-color: #eaf3fe;
}
.item:hover span {
color: #3f7ef7;
}
.item.checked {
background-color: #eaf3fe;
color: #3f7ef7;
}
.item img {
width: 36px;
height: 36px;
pointer-events: none;
}
.item span {
font-size: 12px;
margin-top: 8px;
color: rgb(29, 33, 41);
pointer-events: none;
}
</style>
</head>
<body>
<header>
<img src="./imgs/juejin.png" alt="">
</header>
<div class="search">
<div class="search-wrapper">
<input type="text" id="ipt" placeholder="输入关键词 | tab键切换搜索引擎">
<div class="web-icon">
<img src="./imgs/chrome.png" class="curr-nav" alt="">
</div>
<!-- 三角 -->
<span class="triangle iconfont icon-xiangxia"></span>
<ul class="nav-list">
<li class="item checked" data-src="./imgs/chrome.png">
<img src="./imgs/chrome.png" alt="">
<span>谷歌</span>
</li>
<li class="item" data-src="./imgs/toutiao.png">
<img src="./imgs/toutiao.png" alt="">
<span>头条</span>
</li>
<li class="item" data-src="./imgs/baidu.png">
<img src="./imgs/baidu.png" alt="">
<span>百度</span>
</li>
<li class="item" data-src="./imgs/bing.png">
<img src="./imgs/bing.png" alt="">
<span>必应</span>
</li>
<li class="item" data-src="./imgs/github.png">
<img src="./imgs/github.png" alt="">
<span>github</span>
</li>
<li class="item" data-src="./imgs/npm.png">
<img src="./imgs/npm.png" alt="">
<span>npm</span>
</li>
<li class="item" data-src="./imgs/stack.png">
<img src="./imgs/stack.png" alt="">
<span>stack overflow</span>
</li>
</ul>
</div>
</div>
<nav>
<span class="txt">
<span>+</span>
xxx
</span>
</nav>
<!--
新增需求:
点击某个导航选项,隐藏下拉导航列表,同时将当前展示的导航项切换成点击的导航项
按tab键也能够切换当前的导航项,同时,如果下拉导航列表是显示状态,能看到选中的样式切换
-->
<script>
const triangle = document.querySelector('.triangle')
const list = document.querySelector('.nav-list')
const webIcon = document.querySelector('.web-icon')
/*
在浏览器中,当HTML文档被加载时,所有带有id属性的元素会自动在全局作用域(通常是window对象)中创建一个对应的属性。
这意味着,如果你有一个id为ipt的元素,理论上你可以通过window.ipt来访问这个元素。
然而,这种做法并不推荐,因为它依赖于全局作用域,可能会导致命名冲突和代码难以维护。
*/
// const ipt = document.querySelector('#ipt')
const items = document.querySelectorAll('.item')
const currNav = document.querySelector('.curr-nav')
triangle.addEventListener('click', function () {
list.classList.toggle('show')
triangle.classList.toggle('active')
})
list.addEventListener('click', function (e) {
// 点到item才会触发界面变换
if (e.target.classList.contains('item')) {
// webIcon.innerHTML = `<img src="${e.target.dataset.src}" class="curr-nav" alt="">`
currNav.src = e.target.dataset.src
document.querySelector('.checked')?.classList.remove('checked')
e.target.classList.add('checked')
list.classList.toggle('show')
triangle.classList.toggle('active')
}
})
/*
按tab键切换导航项
核心思路:用一个变量记录导航索引位置并监听键盘按下事件,
当键盘按下时,索引变量加1;当索引变量超出范围时,重置索引变量的值为0
2.1 定义变量记录当前展示导航的索引
2.2 按键判断
2.3 让input聚焦
2.4 记录索引加1
2.5 边界判断
2.6 更新导航图片
2.7 修改导航列表中选中项的类名
*/
let num = 0
/* ipt.addEventListener('keydown', function (e) {
if (e.key == 'Enter') {
// console.log(e.key)
// console.log(items.length)
// console.log(`num ${num}`)
// num++ 先赋值再自增,增加数据未保存,num一直为0
// num = num < items.length - 1 ? num++ : 0
// ++num 可以成功实现
// num = num < items.length - 1 ? ++num : 0
num < items.length - 1 ? num += 1 : num = 0
webIcon.innerHTML = `<img src="${items[num].dataset.src}" class="curr-nav" alt="">`
document.querySelector('.checked')?.classList.remove('checked')
items[num].classList.add('checked')
// console.log(items[num])
}
}) */
document.addEventListener('keydown', function (e) {
if (e.key != 'Tab') return
// 阻止默认行为
e.preventDefault()
// input 聚焦
ipt.focus()
num < items.length - 1 ? num += 1 : num = 0
currNav.src = items[num].dataset.src
// webIcon.innerHTML = `<img src="${items[num].dataset.src}" class="curr-nav" alt="">`
document.querySelector('.checked')?.classList.remove('checked')
items[num].classList.add('checked')
})
</script>
</body>
</html>