1.复习
-
DOM操作,内容的设定
写入
标签对象.innerHTML = ‘内容’ 支持解析标签
标签对象.innerText = ‘内容’ 不支持,不解析标签
获取
var 变量 = 标签对象.innerHTML 获取标签的所有内容,包括标签
var 变量 = 标签对象.innerText 获取标签的所有文本内容,没有标签 -
事件
鼠标事件:
左键单击 click
左键双击 dblclick
右键单击 contentmenu
鼠标移入
mouseover 当前标签,子级标签边界线,都会触发
mouseenter 只有当前标签边界线,会触发
鼠标移出
mouseout 当前标签,子级标签边界线,都会触发
mouseleave 只有当前标签边界线,会触发
鼠标移动
mousemove
表单事件
获取焦点 focus
失去焦点数值改变 change
输入事件 input
提交事件 submit -
事件对象
标签对象.绑定的事件类型 = function(e){ e = e || window.event }
2.作业
<!-- 表单提交 -->
<form action="./01_复习.html">
注册账号 : <input type="text" maxlength="16"> <span></span> <br>
注册密码 : <input type="password" maxlength="16"> <span></span> <br>
确认密码 : <input type="password" maxlength="16"> <span></span> <br>
验 证 码 : <input type="text" maxlength="6"> <span></span> <br>
<span></span> <button type="button">看不清楚</button><br>
<button>提交</button>
</form>
<script>
// 一般写js程序的基本步骤
// 1,获取标签对象
// 2,定义执行程序
// 3,将封装的函数,定义在所有程序最后
// 获取标签对象
// 可以定义不同的属性等,来获取标签对象
// 可以获取所有的标签对象的集合,是伪数组,通过索引下标,获取每个标签
// 偷懒的写法,如果标签顺序有变化,就完蛋了
var userName = document.querySelectorAll('input')[0];
var userPwd1 = document.querySelectorAll('input')[1];
var userPwd2 = document.querySelectorAll('input')[2];
var userVc = document.querySelectorAll('input')[3];
var spanName = document.querySelectorAll('span')[0];
var spanPwd1 = document.querySelectorAll('span')[1];
var spanPwd2 = document.querySelectorAll('span')[2];
var spanVc = document.querySelectorAll('span')[3];
var oVc = document.querySelectorAll('span')[4];
var oBtn = document.querySelectorAll('button')[0];
var oSub = document.querySelectorAll('button')[1];
var oForm = document.querySelector('form');
var str = '0123456789abcdefghijklmnopqrstuvwxyzABCEDEFGHIJKLMNOPQRSTUVWXYZ';
//2,定义程序,操作标签对象
// 定义验证码
oVc.innerHTML = setVc(str);
oBtn.onclick = function(){
oVc.innerHTML = setVc(str);
}
// 账号验证
// 获取焦点
userName.onfocus = function(){
spanName.innerHTML = '输入8-16位账号';
spanName.style.color = 'black';
}
// 输入内容时
userName.oninput = function(){
if(userName.value.length < 8){
spanName.innerHTML = `您输入了${userName.value.length}位数值,至少输入8位,您还必须输入${8-userName.value.length}位`;
spanName.style.color = 'red';
}else if( userName.value.length >= 8 && userName.value.length <= 16 ){
spanName.innerHTML = `您输入了${userName.value.length}位数值,符合规范`;
spanName.style.color = 'black';
}
}
// 失去焦点时
userName.onchange = function(){
if(userName.value.length < 8){
spanName.innerHTML = `您输入了${userName.value.length}位数值,至少输入8位,现在输入的不符合规范,您还必须输入${8-userName.value.length}位`;
spanName.style.color = 'red';
}else if( userName.value.length >= 8 && userName.value.length <= 16 ){
spanName.innerHTML = `您输入了${userName.value.length}位数值,符合规范,是OK的`;
spanName.style.color = 'black';
}
}
// 密码验证
// 获取焦点
userPwd1.onfocus = function(){
spanPwd1.innerHTML = '输入8-16位密码';
spanPwd1.style.color = 'black';
}
// 输入内容时
userPwd1.oninput = function(){
if(userPwd1.value.length < 8){
spanPwd1.innerHTML = `您输入了${userPwd1.value.length}位数值,至少输入8位,您还必须输入${8-userPwd1.value.length}位`;
spanPwd1.style.color = 'red';
}else if( userPwd1.value.length >= 8 && userPwd1.value.length <= 16 ){
spanPwd1.innerHTML = `您输入了${userPwd1.value.length}位数值,符合规范`;
spanPwd1.style.color = 'black';
}
}
// 失去焦点时
userPwd1.onchange = function(){
if(userPwd1.value.length < 8){
spanPwd1.innerHTML = `您输入了${userPwd1.value.length}位数值,至少输入8位,现在输入的不符合规范,您还必须输入${8-userName.value.length}位`;
spanPwd1.style.color = 'red';
}else if( userPwd1.value.length >= 8 && userPwd1.value.length <= 16 ){
spanPwd1.innerHTML = `您输入了${userPwd1.value.length}位数值,符合规范,是OK的`;
spanPwd1.style.color = 'black';
}
}
// 确认密码
// 获取焦点
userPwd2.onfocus = function(){
spanPwd2.innerHTML = '请您输入确认密码,必须与注册密码一致';
spanPwd2.style.color = 'black';
}
// 输入内容
userPwd2.oninput = function(){
if( userPwd1.value === userPwd2.value ){
spanPwd2.innerHTML = '请您输入的确认密码与注册密码相同';
spanPwd2.style.color = 'black';
}else{
spanPwd2.innerHTML = '请您输入的确认密码与注册密码不相同';
spanPwd2.style.color = 'red';
}
}
// 失去焦点
userPwd2.onchange = function(){
if( userPwd1.value === userPwd2.value ){
spanPwd2.innerHTML = '请您输入的确认密码与注册密码相同!!!!';
spanPwd2.style.color = 'black';
}else{
spanPwd2.innerHTML = '请您输入的确认密码与注册密码不相同!!!!';
spanPwd2.style.color = 'red';
}
}
// 验证码
// 获取焦点
userVc.onfocus = function(){
spanVc.innerHTML = '请您输入6位验证码';
spanVc.style.color = 'black';
}
// 输入内容
userVc.oninput = function(){
if(userVc.value.length < 6){
spanVc.innerHTML = `您输入了${userVc.value.length}位数值,必须输入6位,您还必须输入${6-userVc.value.length}位`;
spanVc.style.color = 'red';
}else if(userVc.value.length == 6){
spanVc.innerHTML = `您输入了${userVc.value.length}位数值,符合规范`;
spanVc.style.color = 'black';
}
}
// 点击提交,对所有的数据进行验证
// 将所有的验证都写在一个提交事件中
// 给form标签,添加提交事件
oForm.onsubmit = function(e){
// 获取事件对象
e = e || window.event;
// 1,非空判断,输入内容不能是空
if(userName.value == ''){
spanName.innerHTML = '账号不能为空';
spanName.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}else if(userName.value.length < 8){
spanName.innerHTML = '账号长度少于8位,请继续输入';
spanName.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
if(userPwd1.value == ''){
spanPwd1.innerHTML = '密码不能为空';
spanPwd1.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}else if(userPwd1.value.length < 8){
spanPwd1.innerHTML = '密码长度小于8位';
spanPwd1.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
if(userPwd2.value == ''){
spanPwd2.innerHTML = '确认密码不能为空';
spanPwd2.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}else if(userPwd2.value.length < 8){
spanPwd2.innerHTML = '确认密码长度小于8位';
spanPwd2.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
if(userVc.value == ''){
spanVc.innerHTML = '验证码不能为空';
spanVc.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}else if(userVc.value.length < 6){
spanVc.innerHTML = '验证码长度小于6位';
spanVc.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
// 2,输入内容要符合规范 长度符合要求
// 方法1,单独再写独立的if判断
// if( !(userName.value.length >= 8 && userName.value.length <= 16)){
// spanName.innerHTML = '账号长度不符合规范';
// spanName.style.color = 'orange';
// // 阻止默认提交效果的执行
// e.preventDefault();
// return;
// }
// 方法2,继续在非空判断之后写
// 3,判断 密码和确认密码 必须一致,如果不同不能提交
if(userPwd1.value !== userPwd2.value){
spanPwd2.innerHTML = '确认密码与密码不同';
spanPwd2.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
// 4,验证码的判断
// 你输入的内容,必须入,span中写入的内容相同
// userVc.value 验证码 input 标签,输入的内容
// oVc.innerText 验证码 span 标签,写入的内容
// 如果不需要区分大小写
// 将字符串内容,统一为都是大写,或者都是小写
// 字符串.toUpperCase() 大写
// 字符串.toLowerCase() 小写
// 都统一为小写字符,就不区分小写了
// console.log(oVc.innerText) // 就是字
// console.log(oVc.innerHTML) // 有标签的
if(userVc.value.toLocaleLowerCase() !== oVc.innerText.toLocaleLowerCase() ){
spanVc.innerHTML = '验证码不正确,请重新输入';
spanVc.style.color = 'orange';
// 阻止默认提交效果的执行
e.preventDefault();
return;
}
}
// 定义生成随机验证码的函数
function setVc(str){
var vc = '';
while(vc.length < 6){
var num = parseInt(Math.random() * str.length);
if(vc.indexOf( str[num] ) === -1 ){
vc += str[num];
}
}
return `<b>${vc}</b>`;
}
</script>
3.button标签介绍
button按钮的说明
<form action="">
<!-- input标签 3种标签-->
<!-- 提交按钮 点击提交数据-->
<input type="submit" style="color:red;font-size: 50px;">
<!-- 刷新按钮 点击恢复默认数据-->
<input type="reset">
<!-- 普通按钮 点击啥用没有-->
<input type="button" value="普通标签">
<br>
<!-- button标签 在 form标签中 3种type效果-->
<!-- button标签,默认或者定义也是submit,提交标签效果 -->
<button>提交</button>
<button type="submit">提交</button>
<!-- button标签,type,reset,重置标签效果 -->
<button type="reset">重置</button>
<!-- button标签,type,button,普通标签效果 -->
<button type="button">普通标签</button>
button标签和input标签的区别
- 样式设定方便
button按钮中的内容,是标签的文字内容,可以直接按照css样式来设定
input标签,字体样式等可以设定,但是其他的样式设定,盒子模型,弹性盒模型等,无法支持的 - H5中,给button标签,新增很多的功能,
H5中,对button标签做了专门的强化
实际项目中,推荐使用button标签,代替input按钮标签
总结:
input 能实现的 button都能实现
button 能实现的 input不一定能实现
推荐使用button
4.键盘事件
键盘事件
与 按键 相关的事件
键盘事件,不能绑定给某个标签
必须是, window 或者 document 两个使用效果完全一致
或者是, 可以被选中的,可以获取焦点的标签
input select textarea button a …
keydown 按键按下事件 , 如果按住不会,会一直触发
keyup 按键抬起事件
<div>123</div>
<input type="text">
<script>
// window.onkeydown = function(){
// console.log(123);
// }
// document.onkeydown = function(){
// console.log(456);
// }
// var oDiv = document.querySelector('div');
// oDiv.onkeydown = function(){
// console.log(789);
// }
// var oIpt = document.querySelector('input');
// oIpt.onkeydown = function(){
// console.log(11111);
// }
// 按键的事件对象
// e.key 存储你按下的按键的名称
// 键盘区域的数值和小键盘区域的数值,不做区分,都是相同的
// e.keyCode 每个按键对应一个数值,绝对不会重复
// 使用这个属性来区分每个按键
// 按键编码
// 需要做兼容处理,兼容的是低版本的火狐浏览器
// var 变量 = e.keyCode || e.which
// 现在用的很少了
window.onkeydown = function(e){
console.log(e);
}
// 按键组合
// ctrl alt shift + 其他组合按键
// 事件对象e中有3个属性
// ctrlKey 按下 ctrl 结果是 true 没有按结果是false
// altKey 按下 alt 结果是 true 没有按结果是false
// shiftKey 按下 shift 结果是 true 没有按结果是false
// 判断按下按键是 ctrl + e (69)
// window.onkeydown = function(e){
// // 按下 ctrl键 按下,按键编码是69的按键
// if(e.ctrlKey == true && e.keyCode == 69){
// console.log('同时按下ctrl和e键');
// }
// }
5.触摸事件和特殊事件
- 触摸事件
用于移动端事件
touchstart 触摸开始
touchend 触摸结束
touchmove 触摸移动
其他事件实际就是这三个事件的组合使用
长按 : 触摸开始和触摸结束之间的时间差 大于 1秒 0.5秒
计算 两个 触发时 时间戳 的 差
: 或者触发 触摸开始事件 超过 1秒 或者 0.5秒
轻触 : 触摸时间差小于 100毫秒
左移 : 触摸开始,触摸结束,坐标差 - 特殊事件
当过度结束时 transitionend 当过度结束时执行
当动画结束时 animationend 当动画结束时执行
有几个执行属性,程序就执行几次操作
<style>
div{
width: 100px;
height: 100px;
background: blue;
transition: all 2s;
}
div:hover{
width: 400px;
height: 400px;
background: red;
}
</style>
</head>
<body>
<div>123</div>
<script>
var oDiv = document.querySelector('div');
oDiv.ontransitionend = function(){
console.log('过度结束了')
}
6.事件对象的目标
事件对象的目标
-
事件对象
触发事件时,JavaScript自动向参数中存储的触发事件的标签的信息
兼容语法 e = e || window.event -
事件对象的目标
e.target
绑定事件标签的子级标签,也会触发父级绑定的事件
此时触发事件的对象是子级标签,不是绑定事件的标签
<div>123
<button>按钮</button>
<span>456</span>
</div>
<script>
// 低版本的IE浏览器,没有 事件对象.target
// 有 srcElement
// 兼容语法 : var eTar = e.target || e.srcElement
var oDiv = document.querySelector('div');
oDiv.onclick = function(e){
e = e || window.event;
console.log(e);
}
7.冒泡事件
-
冒泡事件 / 事件的传播
什么是冒泡事件:
子级标签触发事件,JavaScript中,父级程序,默认也会触发相同类型的事件
这样的效果,称为冒泡事件
只与标签在HTML中的层级关系有关,与css和显示效果无关 -
执行顺序,都是从当前标签向父级元素,执行
实际的标签的捕获顺序是有区别的
IE浏览器是 从 子级向父级 获取标签,称为 冒泡机制
其他浏览器 是 从 父级向子级 获取标签,称为 捕获机制
虽然获取标签的方式不一样
但是现在所有的执行顺序都是从当前标签向父级标签执行 -
阻止事件的传播 / 阻止冒泡事件
事件对象.stopPropagation()
哪个标题有阻止冒泡事件代码,冒泡事件,执行就到这个标签为止
只触发自己身上的事件,不会触发父级标签,相同类型的事件
只管自己,谁写,谁不触发,子级父级,都不管,就自己触发自己的事件 -
兼容问题
低版本IE浏览器
事件对象.cancelBubble = true; 阻止冒泡事件,阻止事件的传播
<style>
.box{
width: 400px;
height: 400px;
background: blue;
}
.middle{
width: 300px;
height: 300px;
background: green;
}
.inner{
width: 200px;
height: 200px;
background: orange;
}
</style>
</head>
<body>
<div class="box">
<div class="middle">
<div class="inner"></div>
</div>
</div>
<script>
// 兼容语法
/*
if(事件对象.stopPropagation){
事件对象.stopPropagation();
}else{
事件对象.cancelBubble = true;
}
*/
var oDivBox = document.querySelectorAll('div')[0];
var oDivMiddle = document.querySelectorAll('div')[1];
var oDivInner = document.querySelectorAll('div')[2];
oDivBox.onclick = function(e){
e = e || window.event;
console.log('我是box-div触发的事件');
// 阻止冒泡事件 / 阻止事件的传播
// 给最外层加载,只是阻止当前标签,触发父级标签的事件
// 不影响,子级标签,触发父级标签事件
// 阻止当前div,触发父级标签的事件
// 但是不影响子级的两个div标签
// e.stopPropagation();
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;
}
}
oDivMiddle.onclick = function(e){
e = e || window.event;
console.log('我是middle-div触发的事件');
// e.stopPropagation();
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;
}
}
oDivInner.onclick = function(e){
e = e || window.event;
console.log('我是inner-div触发的事件');
// e.stopPropagation();
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;
}
}
document.onclick = function(){
console.log('我是document');
}
// 总结
// 外 中 内
// 都写,就都只触发自己
// 外写,就管自己,外,不触发父级
// 中,内,没有变化,中触发自己和外 内触发自己和中外
// 中写,就管自己,外,会触发父级 内触发只到中
// 内写,就管自己,外,会触发父级 中触发,自己和外
8.事件的委托
事件的委托
给当前标签添加事件
通过判断触发事件的标签,是子级元素的标签
根据不同的标签,执行不同的代码,称为事件的委托
给标签添加事件,子级标签触发,不同标签,触发不同程序
<div id="div1">
123
<button>点击</button>
<span class="span1">我是span1</span>
<span class="span2">我是span2</span>
<strong name="abc">我是strong标签</strong>
</div>
<script>
var oDiv = document.querySelector('div')
oDiv.onclick = function(e){
// e 事件对象 和 e.target 事件对象目标 都应该有兼容
// console.log(e)
// 通过,判断 e.target 不同的属性值
// 判断,点击的是不同的标签,执行不同的程序
// e.target 中 存储的是 一个标签对象 不是字符串
// e.target == 'div' 是绝对绝对绝对错误的写法
// 使用 e.target 对象的属性来进行判断
// e.target.id 触发事件标签对象的id属性值
// e.target.className 触发事件标签对象的class属性值
// e.target.TagName 触发事件标签对象的标签名称
// 都是大写字母
// e.target.getAttribute('属性') 通过getAttribute,获取属性
// e.target.innerHTML 触发事件标签对象的内容,包括标签
// e.target.innerText 触发事件标签对象的内容,不包括标签
// 只会触发符合条件时执行程序,点击不同的标签,触发不同的事件
// id属性值
if(e.target.id == 'div1'){
console.log('触发事件的标签是div')
}
// 标签名称 必须都是大写字符
if(e.target.tagName == 'BUTTON'){
console.log('触发事件的标签是button')
}
// class属性值
if(e.target.className == 'span1'){
console.log('触发事件的标签是第一个span标签')
}
// class属性值
if(e.target.className == 'span2'){
console.log('触发事件的标签是第二个span标签')
}
// 通过 getAttribute() 获取属性的属性值
if(e.target.getAttribute('name') == 'abc'){
console.log('触发事件的标签是strong标签')
}
}
9.事件的委托用法
<style>
table{
border-collapse: collapse;
}
table td{
width: 100px;
line-height: 30px;
border: 1px solid #000;
text-align: center;
}
</style>
</head>
<body>
姓名: <input type="text"><br>
年龄: <input type="number" min="18" max="250"><br>
姓名: 男<input type="radio" name="sex" value="男">
女<input type="radio" name="sex" value="女">
保密<input type="radio" name="sex" value="保密"> <br>
城市: <select>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="重庆">重庆</option>
<option value="天津">天津</option>
</select><br>
<button name="add">添加</button>
<table>
<thead>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
<td>城市</td>
<td>删除</td>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
// 事件委托,多用于 动态 添加的标签
// table表格中 所有的tbody 中的内容,都是动态,根据arr数组内容,生成的
// 此时使用事件委托,就特别使用
var arr = [
{name:'张三',age:18,sex:'男',city:'北京'},
{name:'李四',age:19,sex:'女',city:'上海'},
{name:'王五',age:20,sex:'男',city:'广州'},
{name:'赵六',age:21,sex:'女',city:'重庆'},
{name:'刘七',age:22,sex:'保密',city:'天津'},
];
// 获取tbody标签对象
var oTb = document.querySelector('tbody');
// 获取添加button按钮对象
var oBtnAdd = document.querySelector('[name="add"]');
// 调用生成页面函数
setTab();
// 新增数据操作
oBtnAdd.onclick = function(){
// 1,获取数据
var oIptName = document.querySelector('[type="text"]').value;
var oIptAge = document.querySelector('[type="number"]').value;
var oIptCity = document.querySelector('select').value;
var oIptSex = document.querySelectorAll('[type="radio"]');
var oIptSexValue = 0;
oIptSex.forEach(function(item){
if(item.checked === true){
oIptSexValue = item.value;
}
})
// 2,创建对象
var obj = {
name:oIptName,
age:oIptAge,
sex:oIptSexValue,
city:oIptCity,
}
// 3,将对象,写入数据
arr.push(obj);
// 4,根据新的数据,重新渲染生成新的table表格
setTab();
}
// 渲染页面函数
function setTab(){
// 定义空字符串
var str = '';
// 循环遍历数组,生成页面内容字符串
arr.forEach(function(item , key){
// 拼接tr起始标签
str += '<tr>';
// 拼接序号单元格
str += `<td>${key+1}</td>`;
// 循环对象,拼接生成内容单元格
for(var k in item){
str += `<td>${item[k]}</td>`;
}
// 拼接删除单元格
str += `<td><button index="${key}">删除</button></td>`;
// 拼接tr结束标签
str += '</tr>';
})
// 将生成的字符串内容,写入tbody标签
oTb.innerHTML = str;
}
// 给动态生成的标签中,给button按钮,添加事件
// 使用事件委托的形式,给button按钮的父级标签添加事件
// 给一直存在的标签,不是动态生成父级标签,添加事件 ---> tbody
oTb.onclick = function(e){
// 获取事件对象,兼容
e = e || window.event;
// 获取事件对象触发目标,兼容
// eTag就是我们点击的标签
var eTag = e.target || e.srcElement;
// 判断点击的对象是button按钮 -- 标签名称就行,注意必须都大写
if(eTag.tagName == 'BUTTON'){
// 这里的思路和步骤,与 之前 del() 函数中定义的步骤思路完全一致
// 只是之前的标签对象是存储在item中,现在是eTag中
// 获取点击的标签对象中,index属性的属性值
var index = eTag.getAttribute('index');
// 根据属性值,也就是索引下标,执行删除数组单元程序
arr.splice(index , 1);
// 根据新的数组,重新渲染页面,生成新的tbody内容
setTab();
}
}
事件委托的优点
- 不用循环遍历,执行效率就调高了
只是给父级添加事件,判断触发事件的目标 - 不同考虑代码的执行顺序
只要给一个一直存在的,不是动态生成的父级标签,添加事件
判断触发事件的目标 即可 如果有执行,没有不执行
不同考虑添加事件的时候,标签是否已经存在
一般是选择层级关系最近的,一直存在的标签,添加事件
例如我们demo中,使用的是 tbody 如果非要使用table 也是可以的
只要判断准确,不冲突即可 - 今后我们生成的页面,基本都是动态渲染的,事件委托的方式,会经常使用