- 1.事件注册:
事件源.addEventListener(‘事件类型’,事件处理函数) //不要on, 例如 click 、 mouseenter
- 2.移除事件:
事件源.removeEventListener(‘事件类型’,事件处理函数) /* 细节:移除事件只能移除具名事件,无法移除匿名事件 */
<script>
/* 学习目标: 注册事件两种方式
1.点语法注册: 事件源.事件类型 = 事件处理函数
* box.onclick = function(){ }
1.1 点语法移除事件:事件源.事件类型 = null
2.事件源.addEventListener('事件类型',事件处理函数)
2.1 移除事件: 事件源.removeEventListener('事件类型',事件处理函数)
3.点语法与addEventListener()区别 :
3.1 点语法不可以注册同名事件,后者会覆盖前者。(变量赋值会先销毁旧值)
3.2 addEventListener()可以注册同名事件,依次触发
*/
let box = document.querySelector('#box')
//1.点语法注册事件: 事件源.事件类型 = 事件处理函数
box.onclick = function(){
alert('1-我是点语法注册的事件')
}
//1.1 点语法移除事件 : 事件源.事件类型 = null
box.onclick = null
//2.addEventListener()
/**
* @description: 注册事件
* @param {string} 事件类型 不要on, 例如 click 、 mouseenter
* @param {function} 事件处理函数
* @return:
*/
let fn = function(){
alert('1-好好学习')
}
/*
fn : 变量取值。 函数是一种数据类型,也可以像其他数据类型一样,进行赋值
fn() : 调用函数,得到函数返回值
*/
box.addEventListener('click', fn )
box.addEventListener('click',function(){
alert('2-天天向上')
})
//移除事件
/* 细节:移除事件只能移除具名事件,无法移除匿名事件 */
box.removeEventListener('click',fn)
</script>
- 3.阻止默认事件
1.默认事件(行为) : 在HTML中,有一些标签自带点击事件(a标签 form表单)
2.如何阻止默认事件 : e.preventDefault()
* 阻止默认事件,就可以正常触发我们自己注册的事件
<body>
<a href="http://www.baidu.com">跳转到百度</a>
<form action="">
<input class="username" type="text" placeholder="请输入账号" />
<br />
<input class="password" type="text" placeholder="请输入密码" />
<br />
<button class="btn">点我登录</button>
</form>
<script>
/*学习目标: 阻止默认事件(默认行为)
1.默认事件(行为) : 在HTML中,有一些标签自带点击事件(a标签 form表单)
2.如何阻止默认事件 : e.preventDefault()
* 阻止默认事件,就可以正常触发我们自己注册的事件
*/
let a = document.querySelector('a')
a.onclick = function (e) {
//阻止默认事件
e.preventDefault()
console.log('我是a标签点击事件')
}
//表单中的按钮
document.querySelector('.btn').onclick = function (e) {
//阻止表单默认行为
e.preventDefault()
let username = document.querySelector('.username')
let password = document.querySelector('.password')
console.log(username, password)
/* 了解:只要是表单中的按钮,点击的时候你的网页就会像a标签href一样默认跳转到其他页面。页面的网址就是表单元素的action属性值 */
}
</script>
- 4. 事件对象
1.事件对象 : 存储与事件触发相关的数据
* 当一个事件在触发的时候,浏览器会捕捉事件触发相关的数据(鼠标坐标点、键盘按键),存入一个对象中,称之为事件对象
2.如何获取事件对象 : 给事件处理函数添加一个形参即可 event/ev/e
3.事件对象常用属性 :
2.1 e.pageX/e.pageY : 获取鼠标触发点位置
2.2 e.key : 获取具体按键字符串 ‘Enter’
2.3 e.keyCode : 获取键盘ASCII码
ASCII码 : 键盘每一个按键对应一个数字编码,称之为ASCII码
/*
1.事件对象 : 存储与事件触发相关的数据
* 当一个事件在触发的时候,浏览器会捕捉事件触发相关的数据(鼠标坐标点、键盘按键),存入一个对象中,称之为事件对象
2.如何获取事件对象 : 给事件处理函数添加一个形参即可 event/ev/e
3.事件对象常用属性 :
3.1 e.pageX/e.pageY : 获取鼠标触发点到页面左上角距离
*/
let box = document.querySelector('#box')
box.onclick = function (e) {
console.log(e)
console.log(e.pageX, e.pageY)
console.log('有人点我了')
}
4.1案例:鼠标跟随移动
<style>
img {
/* 要想修改元素的位置,必须要有定位属性 */
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<img src="./tianshi.gif" alt="" />
<script>
/*
1.复习学习过的鼠标事件
onclick : 鼠标单击
ondblclick : 鼠标双击
onmouseover/onmouseout : 鼠标移入移出
onmouseenter/onmouseleave : 鼠标移入移出
onmousemove : 鼠标移动
*/
//1.页面鼠标注册
window.onmousemove = function (e) {
console.log(e.pageX, e.pageY)
//定位 宽高这些都是有单位的, 我们需要自己加上单位
document.querySelector('img').style.left = e.pageX + 'px'
document.querySelector('img').style.top = e.pageY + 'px'
}
4.2键盘事件与获取按键
<body>
<input type="text" placeholder="请输入内容" />
<script>
/*
1.复习键盘事件
onfocus : 键盘成为输入框焦点
onblur : 键盘失去输入框焦点
oninput : 键盘输入
* 场景: 实时获取输入框文本
onkeydown : 键盘按下
* 场景: 搜索框监听enter键
onkeyup : 键盘松开
2.获取键盘按键 : 通过事件对象来获取
2.1 e.pageX/e.pageY : 获取鼠标触发点位置
2.2 e.key : 获取具体按键字符串 'Enter'
2.3 e.keyCode : 获取键盘ASCII码
ASCII码 : 键盘每一个按键对应一个数字编码,称之为ASCII码
*/
let input = document.querySelector('input')
//键盘按下
input.onkeydown = function (e) {
console.log(e)
//enter键: if(e.key == 'Enter' )
console.log(e.key)//获取具体按键字符串
//enter键: if(e.keyCode == 13)
console.log(e.keyCode)//获取键盘ASCII码
console.log('1-键盘被按下')
}
//键盘松开
input.onkeyup = function () {
console.log('2-键盘被松开')
}
</script>
</body>
- 5.事件流与事件委托
5.1事件冒泡
当子元素事件被触发的时候,子元素所有的父级元素‘同名事件’会被依次触发
<!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>
.father {
width: 300px;
height: 300px;
background-color: red;
}
.son {
width: 100px;
height: 100px;
background-color: cyan;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
/*
1.事件冒泡 : 当子元素事件被触发的时候,子元素所有的父级元素‘同名事件’会被依次触发
* 子元素 -> 父元素 -> body -> html -> document -> window
* 事件冒泡是浏览器特点,一直都存在。 以前没有是因为以前并没有给父级元素注册同名事件
2.事件委托 :
*/
//子元素
document.querySelector('.son').onclick = function(){
alert('1-我是子元素')
}
//父元素
document.querySelector('.father').onclick = function(){
alert('2-我是父元素')
}
//body
document.body.onclick = function(){
alert('3-我是body')
}
//html
document.documentElement.onclick = function(){
alert('4-我是html')
}
//document
document.onclick = function(){
alert('5-我是document')
}
//window
window.onclick = function(){
alert('6-我是window')
}
</script>
</body>
</html>
5.2事件委托(重点)
事件委托: 给父元素注册,委托子元素处理 ul.οnclick=fuction(e){
alert(e.target.innerText)
}
e.target : 事件触发源。 真正触发这个事件的子元素
事件委托原理:事件冒泡
事件委托应用场景:
实际开发最多:给动态新增的子元素注册事件
性能优化:如果所有的子元素都需要注册同名事件,只需要给父元素注册
事件委托注意点:
委托事件不能通过this找到子元素.(this指向父元素)
事件委托需要通过什么属性找到子元素: e.target
<!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>
</head>
<body>
<ul>
<li>我是班长1</li>
<li>我是班长2</li>
<li>我是班长3</li>
<li>我是班长4</li>
<li>我是班长5</li>
<li>我是班长6</li>
</ul>
<script>
/*
1.事件冒泡 : 当子元素事件被触发的时候,子元素所有的父级元素‘同名事件’会被依次触发
* 子元素 -> 父元素 -> body -> html -> document -> window
* 事件冒泡是浏览器特点,一直都存在。 以前没有是因为以前并没有给父级元素注册同名事件
2.事件委托 : 给父元素注册事件,委托给子元素处理
2.1 事件委托原理 : 事件冒泡
2.2 事件委托注意点 : 不能使用this
this : 指向父元素
e.target: 指向真正点击的子元素(事件触发源:触发冒泡的源头)
*/
//事件委托:给父元素注册,委托给子元素处理
document.querySelector('ul').onclick = function (e) {
/*
this : ul (事件源:这个事件给谁注册的)
e.target : 事件触发源。 真正触发这个事件的子元素
*/
alert(e.target.innerText)
}
//需求:给页面每一个li元素注册事件事件
//1.以前写法: 获取所有li元素数组, 遍历数组给每一个li元素注册
// //(1)获取li元素
// let liList = document.querySelectorAll('li')
// //(2)遍历li元素
// for (let i = 0; i < liList.length; i++) {
// //(3)给每一个li元素注册
// liList[i].onclick = function () {
// // this : 当前点击的li元素
// alert(this.innerText)
// }
// }
// //如果一个元素是动态新增的,则无法直接注册事件
// let newLi = document.createElement('li')
// newLi.innerText = '我是新来的'
// document.querySelector('ul').appendChild(newLi)
</script>
</body>
</html>
5.3事件捕获(了解即可)
1.事件捕获 : 当子元素事件被触发的时候,会先从最顶级的父元素一级一级往下触发 * window->document->html->body->父元素->子元素
2.如何注册捕获事件
(1)点语法注册的事件一定是冒泡,无法注册捕获
(2)只有一种语法可以注册捕获事件
元素.addEventListener(‘事件类型’,事件处理函数,true)
3.addEventListener第三个参数是一个布尔类型,默认值是false(冒泡) true(捕获)
5.4事件流与阻止事件流
1.事件流三个阶段e.eventPhase: 给元素和父级元素都注册同名事件
1.1事件捕获
1.2事件目标
1.3事件冒泡
2.阻止事件流动 : 阻止冒泡 + 捕获
e.stopPropagation()
<!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>
.father {
width: 300px;
height: 300px;
background-color: red;
}
.son {
width: 100px;
height: 100px;
background-color: cyan;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
/*
1.事件冒泡 : 当子元素事件被触发的时候,子元素所有的父级元素‘同名事件’会被依次触发
* 子元素 -> 父元素 -> body -> html -> document -> window
* 事件冒泡是浏览器特点,一直都存在。 以前没有是因为以前并没有给父级元素注册同名事件
2.事件捕获 : 当子元素事件被触发的时候,会先从最顶级的父元素一级一级往下触发
* window->document->html->body->父元素->子元素
3.如何注册捕获事件
(1)点语法注册的事件一定是冒泡,无法注册捕获
(2)只有一种语法可以注册捕获事件
元素.addEventListener('事件类型',事件处理函数,true)
4.事件流三个阶段e.eventPhase: 给元素和父级元素都注册同名事件
1-事件捕获
2-事件目标
3-事件冒泡
5.阻止事件流动 : 阻止冒泡 + 捕获
e.stopPropagation()
*/
//子元素
//addEventListener第三个参数是一个布尔类型,默认值是false(冒泡) true(捕获)
document.querySelector('.son').addEventListener('click',function(e){
alert('1-我是子元素' + e.eventPhase)
},false)
//父元素
document.querySelector('.father').addEventListener('click',function(e){
alert('2-我是父元素' + e.eventPhase)
e.stopPropagation()
},false)
//body
document.body.addEventListener('click',function(e){
alert('3-我是body' + e.eventPhase)
},false)
//html
document.documentElement.addEventListener('click',function(e){
alert('4-我是html' + e.eventPhase)
},false)
//document
document.addEventListener('click',function(e){
alert('5-我是document' + e.eventPhase)
e.stopPropagation()
},false)
//window
window.addEventListener('click',function(e){
alert('6-我是window' + e.eventPhase)
},false)
</script>
</body>
</html>
6.额外记录部分
/*
Js 所有的BUG分为两种
1.1.语法错误 :Syntax Error
2.2.数据错误 :Type Error :
Type Error:一般是你某个数据是undefined或者null导致
null:获取元素选择器写错
nudefined:
Reference Error:声明变量和使用不是同一个单词(变量声明)
原因:Cpu解析代码做两件事: 识别语法,处理(运算和存储)数据
2.不报错:
主要原因是:js中的引用类型可以动态新增数据
给自己心理大方向案例:绝壁是单词写错
*/
/*
classList.add( newClassName );
添加新的类名,如已经存在,取消添加
classList.contains( oldClassName );
确定元素中是否包含指定的类名,返回值为true 、false;
classList.remove( oldClassName );
移除已经存在的类名;
classList.toggle( className );
如果classList中存在给定的值,删除它,否则,添加它;
classList.replace( oldClassName,newClassName );
*/
// 提交 清空表单
document.querySelector('form').reset()
// 阻止默认事件 如默认跳转
e.preventDefault()
//阻止事件流动(冒泡 + 捕获)
e.stopPropagation()
//去除字符串空白 trim( )
//判断输入是否为空
if(textarea.value.trim( ) = '')
//获取当前时间
new Date().toLocaleString( )
7.案例:DOM综合表格处理
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
<style>
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
color: #721c24;
}
h1 {
text-align: center;
color: #333;
margin: 20px 0;
}
table {
margin: 0 auto;
width: 800px;
border-collapse: collapse;
color: #004085;
}
th {
padding: 10px;
background: #cfe5ff;
font-size: 20px;
font-weight: 400;
}
td,
th {
border: 1px solid #b8daff;
}
td {
padding: 10px;
color: #666;
text-align: center;
font-size: 16px;
}
tbody tr {
background: #fff;
}
tbody tr:hover {
background: #e1ecf8;
}
.info {
width: 900px;
margin: 50px auto;
text-align: center;
}
.info input {
width: 80px;
height: 25px;
outline: none;
border-radius: 5px;
border: 1px solid #b8daff;
padding-left: 5px;
}
.info button {
width: 60px;
height: 25px;
background-color: #004085;
outline: none;
border: 0;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
.info .age {
width: 50px;
}
</style>
</head>
<body>
<h1>新增学员</h1>
<form action="">
<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>
</form>
<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>19</td>
<td>男</td>
<td>15000</td>
<td>上海</td>
<td>
<a href="javascript:" class="delete">删除</a>
</td>
</tr> -->
</tbody>
<script>
/* 思路分析
1.点击录入 :(注意点form表单中的按钮需要阻止默认事件)
1.1 非空判断 : 姓名、年龄、薪资不能为空
1.2 新增tr元素
* (1)创建tr (2)设置内容 (3)添加到table>tbody
1.3 清空form表单
2.点击删除 : 使用事件委托技术
2.1 点击删除, 删除tr标签 ( 删除按钮的父元素的td, td的父元素是tr)
3.上移
4.下移
*/
//1.获取元素
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')
let add = document.querySelector('.add')
//2.1 点击录入
add.onclick = function(e){
/* 注意点:form表单元素中的按钮需要阻止默认条件 */
e.preventDefault()
//3.1 非空判断 : 姓名、年龄、薪资不能为空
if( uname.value.trim() == '' || age.value.trim() == '' || salary.value.trim() == ''){
alert('输入框不能为空')
}else{
//3.2 新增元素
//(1)创建空tr标签
let tr = document.createElement('tr')
//(2)设置内容
tr.innerHTML = `
<td>1001</td>
<td>${ uname.value }</td>
<td>${ age.value }</td>
<td>${ gender.value }</td>
<td>${ salary.value }</td>
<td>${ city.value }</td>
<td>
<a href="javascript:" class="up">上移</a>
<a href="javascript:" class="down">下移</a>
<a href="javascript:" class="delete">删除</a>
</td>
`
//(3)添加到tbody
document.querySelector('tbody').appendChild(tr)
}
//3.3 表单清空 form.reset()
document.querySelector('form').reset()
}
//2.2 使用事件委托给删除按钮注册点击事件
document.querySelector('tbody').onclick = function(e){
//点击tbody任意区域都会触发点击事件, 真正委托的是删除按钮
let tr = e.target.parentNode.parentNode
//3.1 多分支判断到底是哪一个按钮: 删除、上移、下移
if( e.target.classList.contains('delete') ){
//(1)删除
this.removeChild( tr )
}else if( e.target.classList.contains('up') ){
//(2)上移
//判断tr是不是第一个儿子
if( tr.previousElementSibling ){//有哥哥可以上移
//上移:移到tr哥哥的前面
this.insertBefore(tr,tr.previousElementSibling)
}else{
alert('已经是第一个了')
}
}else if( e.target.classList.contains('down') ){
//(3)下移
//判断tr是不是最后一个儿子
if( tr.nextElementSibling ){
//下移: 移到 弟弟的弟弟的前面
this.insertBefore( tr, tr.nextElementSibling.nextElementSibling )
}else{
alert('已经是最后一个了')
}
}
}
</script>
</table>
</body>
</html>