事件对象
事件对象
我们设置了两个不同的点击事件监听器,分别绑定在按钮元素和整个文档上。当发生点击事件时,事件处理器函数接收到一个事件对象e作为参数。事件对象中包含了丰富的信息,如clientX、clientY、pageX、pageY和offsetX、offsetY,分别用于获取鼠标点击位置相对于浏览器视口、整个页面以及特定DOM元素的位置信息。
<button>点击</button>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click', function (e) {
console.log(e);
})
document.addEventListener('click', function (e) {
// 获取光标相对于游览器可见窗口左上角的位置
console.log('client ' + e.clientX, e.clientY);
// 获取光标相对于整个页面左上角的位置
console.log('page ' + e.pageX, e.pageY);
// 获取光标相对于DOM元素左上角的位置
console.log('offset ' + e.offsetX, e.offsetY);
})
</script>
案例
鼠标跟随
当鼠标在页面上移动时,mousemove事件被触发,事件处理器会读取事件对象e中的pageX和pageY属性,并据此更新图片元素的位置,使其始终跟随鼠标移动。
<!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>
img {
position: absolute;
top: 0;
left: 0;
width: 66px;
}
</style>
</head>
<body>
<img src="../../resources/image/1.png" alt="">
<script>
let img = document.querySelector('img')
document.addEventListener('mousemove', function (e) {
img.style.top = e.pageY - 33 + 'px'
img.style.left = e.pageX - 33 + 'px'
})
</script>
</body>
</html>
回车发布微博
当用户在文本区域中输入内容并按下回车键时,会触发keyup事件,进而调用发布微博的逻辑。该逻辑包括判断文本是否为空、随机选择一个用户头像和昵称插入到新创建的微博条目中、将新条目添加到列表顶端,并清空文本输入框。此外,代码中还实现了通过点击.the_del元素删除对应微博的功能。
<!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>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.w {
width: 900px;
margin: 0 auto;
}
.controls textarea {
width: 878px;
height: 100px;
resize: none;
border-radius: 10px;
outline: none;
padding-left: 20px;
padding-top: 10px;
font-size: 18px;
}
.controls {
overflow: hidden;
}
.controls div {
float: right;
}
.controls div span {
color: #666;
}
.controls div .useCount {
color: red;
}
.controls div button {
width: 100px;
outline: none;
border: none;
background-color: rbg(0, 132, 255);
height: 30px;
cursor: pointer;
columns: #fff;
font: bold 14px '宋体';
transition: all 0.5s;
}
.controls div button :hover {
background: rgb(0, 255, 255);
}
.controls div button :disabled {
background: rgba(0, 255, 255, 0.5);
}
.contentList li {
padding: 20px 0;
border-bottom: 1px dashed #ccc;
position: relative;
}
.contentList .info {
position: relative;
}
.contentList li .info span {
position: absolute;
top: 15px;
left: 100px;
font: bold 16px '宋体';
}
.contentList li .info p {
position: absolute;
top: 40px;
left: 100px;
color: #aaa;
font-size: 12px;
}
.contentList img {
width: 80px;
border-radius: 50%;
}
.contentList li .content {
padding-left: 100px;
color: #666;
word-break: break-all;
}
.contentList li .the_del {
position: absolute;
right: 0;
top: 0;
font-size: 28px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="w">
<div class="controls">
<img src="../../resources/image/bilibili/bilibanner.png" alt="" height="100px">
<textarea id="area" placeholder="说点什么把..." name="" id="" cols="30" rows="10" maxlength="200"></textarea>
<div>
<span class="useCount">0</span>
<span>/</span>
<span>200</span>
<button id="send">发布</button>
</div>
</div>
<div class="contentList">
<ul id="list"></ul>
</div>
</div>
<li hidden>
<div class="info">
<img class="userpic" src="../../resources/image/1.png" alt="">
<span class="username">可莉</span>
<p class="send-time">发布于</p>
</div>
<div class="content">111</div>
<span class="the_del">X</span>
</li>
<script>
// 数据
let dataArr = [
{ uname: '可莉', imgSrc: '../../resources/image/1.png' },
{ uname: '可莉', imgSrc: '../../resources/image/2.png' },
{ uname: '霄宫', imgSrc: '../../resources/image/3.png' },
{ uname: '刻晴', imgSrc: '../../resources/image/4.png' },
{ uname: '胡桃', imgSrc: '../../resources/image/5.png' },
{ uname: '胡桃', imgSrc: '../../resources/image/6.png' },
{ uname: '胡桃', imgSrc: '../../resources/image/7.png' },
]
// 文本域
let textarea = document.querySelector('#area')
let useCount = document.querySelector('.useCount')
// 发布按钮
let send = document.querySelector('#send')
// ul
let ul = document.querySelector('#list')
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}
// 1.检测用户输入长度
area.addEventListener('input', function () {
useCount.innerHTML = textarea.value.length
})
// 2.输入内容不能为空
send.addEventListener('click', function () {
if (textarea.value.trim() === '') {
textarea.value = ''
useCount.innerHTML = 0
return alert('内容不为空')
}
let random = getRandomIntInclusive(0, dataArr.length - 1)
// 3.新增留言
let li = document.createElement('li')
li.innerHTML = `
<div class="info">
<img class="userpic" src=${dataArr[random].imgSrc} alt="">
<span class="username">${dataArr[random].uname}</span>
<p class="send-time">发布于${new Date().toLocaleString()}</p>
</div>
<div class="content">${textarea.value}</div>
<span class="the_del">X</span>
`
// 4.删除留言
// 删除留言 放到追加的前面,这样创建元素的同时顺便绑定了时间
// 使用li.querySelector('.the_del')
let del = li.querySelector('.the_del')
del.addEventListener('click', function () {
ul.removeChild(li)
})
ul.insertBefore(li, ul.children[0])
// 5.重置文本域
textarea.value = ''
useCount.innerHTML = 0
})
// 回车发布信息
textarea.addEventListener('keyup', function (e) {
if (e.key === 'Enter') {
send.click()
}
})
</script>
</body>
</html>
冒泡事件
冒泡事件
当点击内部子元素(.son)时,点击事件会先在子元素上触发,接着向上冒泡至父元素(.father)以及最终到最外层的文档对象(document)。每个阶段都会触发相应的事件处理器函数,依次弹出son、baba和ye的警告消息。这体现了事件冒泡机制,在实际开发中可以通过阻止事件冒泡来精确控制事件的处理范围。
<!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>
.father{
margin: 100px auto;
width: 500px;
height: 500px;
background-color: pink;
}
.son{
width: 200px;
height: 200px;
background-color: skyblue;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
let fa =document.querySelector('.father')
let son =document.querySelector('.son')
fa.addEventListener('click',function(){
alert('baba')
})
son.addEventListener('click',function(){
alert('son')
})
document.addEventListener('click',function(){
alert('ye')
})
</script>
</body>
</html>
阻止冒泡
在这个例子中,当点击内嵌的".son"元素时,除了触发自身的点击事件,还会触发父级".father"和根元素"document"的点击事件。然而,由于在子元素的事件处理函数中调用了e.stopPropagation(),它阻止了事件继续向上冒泡,因此只有"son"的alert会被触发,不会触发父元素和文档的alert。
<!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>
.father {
margin: 100px auto;
width: 500px;
height: 500px;
background-color: pink;
}
.son {
width: 200px;
height: 200px;
background-color: skyblue;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
let fa = document.querySelector('.father')
let son = document.querySelector('.son')
fa.addEventListener('click', function () {
alert('baba')
})
son.addEventListener('click', function (e) {
alert('son')
e.stopPropagation()
})
document.addEventListener('click', function () {
alert('ye')
})
</script>
</body>
</html>
阻止默认行为
在此例中,给"a"标签添加了点击事件监听器,当点击链接时,通过调用e.preventDefault()阻止了浏览器对链接默认行为(跳转到href指定的URL)的执行。
<body>
<a href="https://www.bilibili.com/">bilibili</a>
<script>
let a = document.querySelector('a')
a.addEventListener('click', re)
function re(e) {
e.preventDefault()
}
// 移除事件
a.removeEventListener('click', re)
</script>
</body>
事件委托
在HTML的ul元素上添加了一个点击事件监听器,无论用户点击哪个子li元素,事件处理器都会被执行,这是因为事件是从父级元素传播下来的。在这里,事件处理器改变点击元素的颜色。
<body>
<ul>
<li>我是第1个</li>
<li>我是第2个</li>
<li>我是第3个</li>
<li>我是第4个</li>
<li>我是第5个</li>
</ul>
<script>
let ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
e.target.style.color = 'red'
})
</script>
</body>
案例
学生信息表
这个案例展示了如何创建一个学生信息表单,并能够动态添加和删除学生信息。通过JavaScript收集表单字段的输入值,并将其添加到数组中。当点击“录入”按钮时,新学生信息会被推送到数组中,并通过render函数重新渲染表格内容。同时,当点击表格中的删除链接时,会根据链接ID从数组中移除对应的学生信息,再次调用render函数刷新表格。
<!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>
h1,
.info {
margin: 0 atuo;
text-align: center;
}
table {
width: 900px;
text-align: center;
margin: 10px auto;
}
table,
thead th,
thead td {
border: 1px solid #ccc;
border-collapse: collapse;
}
thead tr {
height: 40px;
cursor: pointer;
}
table thead tr {
background-color: rgb(110, 253, 255);
}
</style>
</head>
<body>
<h1>新增学员</h1>
<div class="info">
姓名:<input type="text" class="uname">
年龄:<input type="text" class="age">
性别:<select name="gender" id="" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary">
就业城市:<select name="city" id="" class="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="武汉">武汉</option>
<option value="九江">九江</option>
<option value="厦门">厦门</option>
</select>
<button class="add">录入</button>
</div>
<h1>就业榜</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>1001</td>
<td>甘雨</td>
<td>20</td>
<td>女</td>
<td>0¥</td>
<td>璃月</td>
<td>
<a href="javascript:">删除</a>
</td>
</tr> -->
</tbody>
</table>
<script>
let arr = [
{ stuId: 1001, uname: '刻晴', age: 19, gender: '女', salary: 2000, city: '上海' },
{ stuId: 1002, uname: '凝光', age: 22, gender: '女', salary: 20000, city: '北京' }
]
let tbody = document.querySelector('tbody')
let add = document.querySelector('.add')
let uname = document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
render()
function render() {
// 清空表单
tbody.innerHTML = ''
for (let i = 0; i < arr.length; i++) {
let tr = document.createElement('tr')
tr.innerHTML = `
<td>${arr[i].stuId}</td>
<td>${arr[i].uname}</td>
<td>${arr[i].age}</td>
<td>${arr[i].gender}</td>
<td>${arr[i].salary}</td>
<td>${arr[i].city}</td>
<td>
<a href="javascript:" id=${i}>删除</a>
</td>
`
// 渲染表单
tbody.appendChild(tr)
// 复原数据
uname.value = age.value = salary.value = ``
gender.value = '男'
city.value = '北京'
}
}
add.addEventListener('click', function () {
arr.push({
stuId: arr[arr.length - 1].stuId + 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
})
render()
})
// 删除操作
tbody.addEventListener('click', function (e) {
if (e.target.tagName === 'A') {
arr.splice(e.target.id, 1)
}
render()
})
</script>
</body>
</html>
手风琴效果
这段代码实现了一个简单的CSS过渡动画和JavaScript配合的手风琴效果。当鼠标悬停在一个列表项(li元素)上时,该列表项会扩大宽度,其他列表项会缩小宽度,形成手风琴展开的效果。当鼠标离开当前列表项时,所有列表项恢复原始宽度。这种效果通过给每个列表项添加mouseenter和mouseleave事件监听器实现。
<!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>
ul {
list-style: none;
}
* {
margin: 0;
padding: 0;
}
div {
width: 1200px;
height: 400px;
margin: 50px auto;
border: 1px solid red;
overflow: hidden;
}
div li {
width: 240px;
height: 400px;
float: left;
transition: all 500ms;
}
div ul {
width: 1200px;
}
img {
height: 400px;
}
</style>
</head>
<body>
<div class="box">
<ul>
<li>
<a href="#">
<img src="../../resources/image/background/1.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="../../resources/image/background/2.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="../../resources/image/background/3.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="../../resources/image/background/4.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="../../resources/image/background/5.jpg" alt="">
</a>
</li>
</ul>
</div>
<script>
let lis = document.querySelectorAll('li')
for (let i = 0; i < lis.length; i++) {
lis[i].addEventListener('mouseenter', function () {
for (let j = 0; j < lis.length; j++) {
lis[j].style.width = '120px'
}
this.style.width = '711px'
})
lis[i].addEventListener('mouseleave', function () {
for (let j = 0; j < lis.length; j++) {
lis[j].style.width = '240px'
}
})
}
</script>
</body>
</html>