## 1. tab切换
```js
<style>
*{
margin: 0;
padding:0;
}
ul,ol,li{
list-style: none;
}
.cont{
width: 800px;
height: 600px;
border: 5px solid #333;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.cont ul{
width: 100%;
height: 60px;
display: flex;
}
.cont ul li{
flex:1;
font-size: 35px;
color: #fff;
border-left: 2px solid blue;
border-right: 2px solid blue;
background: hotpink;
display: flex;
justify-content: center;
align-items: center;
}
.cont ol{
flex:1;
position: relative;
}
.cont ol li{
width: 100%;
height: 100%;
font-size: 150px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top:0;
left:0;
background: burlywood;
display: none;
}
/* 按钮标签 哪个标签有这个属性,哪个就显示特殊背景颜色 */
.cont ul li.active{
background: skyblue;
color: black;
}
/* 内容标签 哪个标签有这个属性,哪个就显示 */
.cont ol li.active{
display: flex;
}
</style>
</head>
<body>
<div class="cont">
<ul>
<li class="active">按钮1</li>
<li>按钮2</li>
<li>按钮3</li>
</ul>
<ol>
<li class="active">内容1</li>
<li >内容2</li>
<li>内容3</li>
</ol>
</div>
<script>
// 1,核心思想:
// 利用 按钮标签 与 内容标签 标签数量是一一对应的关系
// 通过点击 按钮标签 同时操作 对应的内容标签
// 2,基本步骤:
// 点击 按钮标签 时
// 1,给所有的li标签,包括ul,ol中的所有li标签,清除class样式
// 2,给当前点击的 按钮标签 添加样式
// 3,给对应的 内容标签 添加样式
// 方法1: 两个forEach循环
// 获取标签对象
var ullis = document.querySelectorAll('ul li');
var ollis = document.querySelectorAll('ol li');
// 通过第一层循环,给所有的ul中的li,添加点击事件
ullis.forEach(function(item1 , key1){
// item1,是ul中的li标签,key1是这个ul标签,对应的索引下标
item1.onclick = function(){
// 1,点击时,给所有的li标签,清除class样式
// 需要点击时,循环遍历所有的li标签,设定class样式为 ''
// 可以通过两个循环完成
// ullis.forEach(function(item2,key2){
// item2.className = '';
// })
// ollis.forEach(function(item3,key3){
// item3.className = '';
// })
// 利用ul>li,ol>li索引下标完全下同的原理
// 利用循环ul时生成的索引下标,从ol中,获取li标签,进行操作
ullis.forEach(function(item2,key2){
item2.className = '';
ollis[key2].className = '';
})
// 2,给当前的点击标签,添加css样式
item1.className = 'active';
// 3,给索引相同的,ol中的li标签,添加样式
ollis[key1].className = 'active';
}
})
```
## 2. tab切换之事件的委托
```js
<style>
*{
margin: 0;
padding:0;
}
ul,ol,li{
list-style: none;
}
.cont{
width: 800px;
height: 600px;
border: 5px solid #333;
margin: 0 auto;
display: flex;
flex-direction: column;
}
.cont ul{
width: 100%;
height: 60px;
display: flex;
}
.cont ul li{
flex:1;
font-size: 35px;
color: #fff;
border-left: 2px solid blue;
border-right: 2px solid blue;
background: hotpink;
display: flex;
justify-content: center;
align-items: center;
}
.cont ol{
flex:1;
position: relative;
}
.cont ol li{
width: 100%;
height: 100%;
font-size: 150px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top:0;
left:0;
background: burlywood;
display: none;
}
/* 按钮标签 哪个标签有这个属性,哪个就显示特殊背景颜色 */
.cont ul li.active{
background: skyblue;
color: black;
}
/* 内容标签 哪个标签有这个属性,哪个就显示 */
.cont ol li.active{
display: flex;
}
</style>
</head>
<body>
<div class="cont">
<ul>
<li name="ulli" class="active">按钮1</li>
<li name="ulli" >按钮2</li>
<li name="ulli" >按钮3</li>
</ul>
<ol>
<li name="olli" class="active">内容1</li>
<li name="olli" >内容2</li>
<li name="olli" >内容3</li>
</ol>
</div>
<script>
// 1,核心思想:
// 利用 按钮标签 与 内容标签 标签数量是一一对应的关系
// 通过点击 按钮标签 同时操作 对应的内容标签
// 2,基本步骤:
// 点击 按钮标签 时
// 1,给所有的li标签,包括ul,ol中的所有li标签,清除class样式
// 2,给当前点击的 按钮标签 添加样式
// 3,给对应的 内容标签 添加样式
// 事件委托
// 1,给父级div,添加事件委托
// 2,判断点击 ul中的li时,执行程序
// 1,给所有的li清除class样式标签
// 2,给点击的li添加class样式
// 3,给点击的li对应的ol中的li,添加class样式
// 区别:
// 1,获取父级div标签对象,添加点击事件
// 2,给li添加属性,做区别
// 3,没有循环:当前标签是 e.target
// 4,没有循环,无法获取到当前标签的索引下标
// 必须要在标签中,定义属性来存储索引下标
// 方法1,直接写在标签中
// 方法2,通过循环遍历,给ul中的li,setAttribute
// 总结:事件委托方式,代码更麻烦但是,少了一层循环,执行效率更高
// 方法2: 事件委托
// 获取父级div标签对象
var oDiv = document.querySelector('div');
// 获取标签对象
var ullis = document.querySelectorAll('ul li');
var ollis = document.querySelectorAll('ol li');
// 给父级div添加点击事件
// 获取事件对象,我偷懒,不写兼容了
oDiv.onclick = function(e){
// 判断,点击的是ul中的li标签
// e.target,就是触发点击事件的标签对象
// 如果点击的标签对象,name是ulli,表示点击的是ul中的li标签
if(e.target.getAttribute('name') === 'ulli'){
// 1,给所有的li标签,清除样式
ullis.forEach(function(item,key){
item.className = '';
ollis[key].className = '';
// 给item,也就是ul中的li标签,定义属性
item.setAttribute('index',key);
})
// 2,给当前ul中的li,添加样式
// 没有循环 当前的li是 e.target
e.target.className = 'active';
// 3,给对应的ol中的li,添加样式
// 没有forEach循环,没有索引下标,获取标签中定义的属性的属性值
ollis[e.target.getAttribute('index')].className = 'active';
}
}
</script>
```
## 3. 拖拽效果边界值
```js
<style>
* {
padding: 0;
margin: 0;
}
body {
height: 5000px;
}
.box {
width: 800px;
height: 800px;
border: 10px solid #000;
margin: 40px auto;
position: relative;
background: skyblue;
}
.inner {
width: 100px;
height: 100px;
background: pink;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div class="box">
<div class="inner"></div>
</div>
<script>
// 1,获取标签对象
var oBox = document.querySelector('.box');
var oInner = document.querySelector('.inner');
// 2,获取相应的数据
// 获取父级标签的---占位---不包括边框线
var oBoxWidth = oBox.clientWidth;
var oBoxHeight = oBox.clientHeight;
// 获取子级标签的---占位---包括边框线
var oInnerWidth = oInner.offsetWidth;
var oInnerHeight = oInner.offsetHeight;
// 给父级标签添加事件
oInner.onmousedown = function () {
document.onmousemove = function (e) {
// 获取计算,设定子级标签定位的数值
// page坐标 - 外边距 - 边框线 - 子级宽度的一半
var x = e.pageX - oBox.offsetLeft - oBox.clientLeft - oInnerWidth / 2;
var y = e.pageY - oBox.offsetTop - oBox.clientTop - oInnerHeight / 2;
// 设定极限值
// 最小值是0
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
// 最大值
// 父级标签占位(没有border) - 移动标签占位(计算border)
if (x > oBoxWidth - oInnerWidth) {
x = oBoxWidth - oInnerWidth;
}
if (y > oBoxHeight - oInnerHeight) {
y = oBoxHeight - oInnerHeight
}
// 将设定好的数值,作为子级标签的定位
oInner.style.left = x + 'px';
oInner.style.top = y + 'px';
}
}
window.onmouseup = function () {
document.onmousemove = null;
}
</script>
```
### 思路步骤和问题
1. 事件,到底是绑定给 父级,子级,还是document,还是window
事件,取消,到底是通过谁取消
没有固定的写法,看你需要的效果
还是document和window效果相同,没有区别
鼠标按下
oInner.onmousedown 鼠标在粉色div上,按下鼠标,并且移动鼠标,才会有效果
oBox.onmousedown 鼠标在蓝色div上,按下鼠标,并且移动鼠标,才会有效果
window.onmousedown 鼠标在全屏上,按下鼠标,并且移动鼠标,都会有效果
鼠标移动
最好写成 document.onmousemove / window.onmousemove
鼠标可以快速移动,不怕移出粉色div
鼠标抬起
oInner.onmouseup 鼠标在粉色div上抬起,才会执行,标签不跟随移动
oBox.onmouseup 鼠标在蓝色div上抬起,才会执行,标签不跟随移动
window.onmouseup 鼠标在整个页面上抬起,都会执行,标签不跟随移动
2. 闪烁问题
原因: 相对于标签内容的左上角的定位
会因为鼠标经过不同的标签对象,获取不同的数据
而且JavaScript执行时,有时还会有bug产生
offsetX offsetY 我们是不推荐使用的
解决: 使用 pageX 和 pageY
定位: page距离 - 父级标签外边距 - 父级border - 移动标签占位/2(标签中心点和鼠标重合)
极值1: 左 上 都是 0
极值2: 右 下 父级标签占位(不算border) - 移动标签占位(计算border)
## 4. 事件的监听
### 事件的监听
作用:
如果是普通的事件绑定,相同事件类型,只能绑定一个事件处理函数
如果同一个类型,绑定多个事件处理函数,后绑定的事件会覆盖之前绑定的事件
语法:
标签对象.addEventListener(事件类型 , 事件处理函数)
第三个参数: true / false(默认值)
事件的获取方式
冒泡 : 从子级开始,向父级执行
捕获 : 从父级开始,向子级执行
浏览器默认都是冒泡的执行方式
可以通过 addEventListener 第三个参数来设定获取方式
默认值 false 是 冒泡方式 true是捕获方式
一般实际项目中,不定义第三个参数,就使用默认方式
低版本IE,只支持冒泡,不支持捕获,不会报错,只会按照冒泡的顺序执行
今后给标签,绑定事件,推荐都使用 事件监听方式
```js
var oDiv1 = document.querySelectorAll('div')[0];
var oDiv2 = document.querySelectorAll('div')[1];
var oDiv3 = document.querySelectorAll('div')[2];
// 普通事件绑定方式,只会执行最后一个绑定的事件处理函数
// oDiv1.onclick = function(){
// console.log(123)
// }
// oDiv1.onclick = function(){
// console.log(456)
// }
// oDiv1.onclick = function(){
// console.log(789)
// }
// 事件监听方式
// oDiv1.addEventListener('click' , function(){
// console.log(123);
// })
// oDiv1.addEventListener('click' , function(){
// console.log(456);
// })
// oDiv1.addEventListener('click' , function(){
// console.log(789);
// })
// 使用事件监听的方式,给父子级关系的标签,绑定事件
oDiv1.addEventListener('click' , function(){
console.log(1111)
} , true)
oDiv2.addEventListener('click' , function(){
console.log(2222)
} , true)
oDiv3.addEventListener('click' , function(){
console.log(3333)
} , true)
```
## 5. 事件监听的兼容方式
兼容方式
普通浏览器
标签对象.addEventListener('事件类型' , 事件处理函数)
事件类型必须是字符串形式,直接写事件类型名称,不能有on
低版本IE浏览器
标签对象.attachEvent('on事件类型' , 事件处理函数)
```js
<div id="div">123</div>
<script>
// 兼容方式
// 普通浏览器
// 标签对象.addEventListener('事件类型' , 事件处理函数)
// 事件类型必须是字符串形式,直接写事件类型名称,不能有on
// 低版本IE浏览器
// 标签对象.attachEvent('on事件类型' , 事件处理函数)
var oDiv = document.getElementById('div');
// 一般的监听方式
oDiv.addEventListener('click' , function(){
console.log(123);
})
// 低版本IE浏览器
oDiv.attachEvent('onclick' , function(){
console.log(456);
})
// 使用函数的方式,来做兼容处理
// 参数1,要绑定的事件对象
// 参数2,要绑定的事件类型
// 参数3,要绑定的事件处理函数
// 如果是特殊的事件类型,需要绑定,可以再单独写判断
var oDiv = document.getElementById('div');
myAddEvent(oDiv , 'click' , function(){console.log(123) } );
function myAddEvent(ele,type,fun){
if(ele.addEventListener){
ele.addEventListener(type,fun)
}else{
ele.attachEvent( 'on'+type , fun)
}
}
```
## 6.事件监听的删除
```js
<div>123</div>
<script>
var oDiv = document.querySelector('div');
var fun4 = function(){
console.log('abc');
}
oDiv.addEventListener('click' , fun1);
oDiv.addEventListener('click' , fun2);
oDiv.addEventListener('click' , fun3);
oDiv.addEventListener('click' , fun4);
oDiv.addEventListener('click' , function(){console.log('别想删我')});
// 可以删除
oDiv.removeEventListener('click' , fun1);
oDiv.removeEventListener('click' , fun4);
// 不能删除
oDiv.removeEventListener('click' , function(){console.log('别想删我')});
// 注意:
// 绑定时,必须是函数名称,或者存储函数的变量名称
// 才能删除
// 绑定时,如果直接定义函数,是不能删除的
// 原因: 两个函数,程序相同,但是地址不同
function fun1(){
console.log(123);
}
function fun2(){
console.log(456);
}
function fun3(){
console.log(789);
}
</script>
```
## 7.轮播图之运动函数1
运动函数
轮播图中,图片的切换,不是瞬间切换完成的
而是以类似于动画/运动的方式,逐步切换完成
将位移/定位效果,分多次,逐步完成
transition 虽然可以实现类似的效果
但是对于复杂的运动,复杂的设定,是不方便支持的
而且,transition过渡的兼容性,有待提高
通过JavaScript,配合 定时器 来逐步完成 运动效果
```js
<style>
*{
margin: 0;
padding: 0;
}
div{
width: 100px;
height: 100px;
background: pink;
position: fixed;
top: 100px;
left: 0px;
}
</style>
</head>
<body>
<button>点击</button>
<div></div>
<script>
var oBtn = document.querySelector('button');
var oDiv = document.querySelector('div');
oBtn.addEventListener('click' , function(){
// 定义的初始值
var speed = 0;
// 定时器
var time = setInterval(function(){
// 每次初始值,增加一个数值
speed += 10;
// 将这个数值定义为定位的数值
oDiv.style.left = speed + 'px';
// 当数值达到规定的数值时,清除定时器,终止执行
// 运动也就终止了
if(speed == 500){
clearInterval(time);
}
} , 30)
})
</script>
```
## 8.轮播图之事件函数2
```js
<style>
* {
margin: 0;
padding: 0;
}
div {
width: 100px;
height: 100px;
background: pink;
position: fixed;
top: 100px;
left: 200px;
transition: all 3s;
}
</style>
</head>
<body>
<button>点击</button>
<div></div>
<script>
// 解决第一个问题
// 封装成一个函数的形式
// 如果有初始位置,怎么办?
// 参数1,要运动的标签
// 参数2,要运动的方向/方式 以及 目标位置
// 一般,目标位置与运动方式是相关的,定义对象的形式
// { 方式 : 位置 } {left : 500}
// 获取标签对象
var oDiv = document.querySelector('div');
var oBtn = document.querySelector('button');
// 不愿意写点击事件,直接执行也可以
// move(oDiv,{left:500});
// 执行函数
// 给按钮绑定点击事件
// 执行的函数,是调用move()函数,并且赋值参数
// 注意:不能在 addEventListener 参数2的位置上,直接定义move()
oBtn.addEventListener('click', function(){ move(oDiv,{left:500}) });
// 直接写move函数,是直接调用move函数,不是绑定move函数
// oBtn.addEventListener('click', move() );
function move(ele, obj) {
// 先通过for...in循环,来调用参数2,获取其中的执行属性和目标位置的数值
// 目前是有obj中,有一个单元,会循环一次,生成left对应的定时器
// 如果有obj中,有多个单元,会循环多次,生成每一个属性对应的定时器
// 这样,每一个属性都有自己对应的定时器,便于控制和观察运动状态
// type中存储的是,obj对象的键名,也就是left
for(var type in obj) {
// 初始位置,需要获取标签原始定位的数值
// 参数2中,定义的运动属性,是谁,现在对应的就获取谁的初始数值
// {left:500},就获取left属性对应的初始值
// window.getComputedStyle 获取标签的css样式的属性值
// 语法: window.getComputedStyle(标签对象).属性
// 这里的属性是存储在type自定义变量中的,必须要用[]语法来解析
var speed = parseInt(window.getComputedStyle(ele)[type]) ;
// 定时器,每次循环都会生成一个新的定时器
var time = setInterval(function(){
// 初始值,加上数值
speed += 20;
// 将数值作为定位的数据
// 点语法,不能解析变量,必须使用[]语法,解析存储的变量
// 设定 标签 css , 语法是 标签对象.style[属性] = 属性值
// type是for...in循环中,自定义的变量,用来存储对象的键名
// 要使用时,type是变量, 对象.变量,不能解析,只能使用对象[变量]才能解析变量
ele.style[type] = speed + 'px';
// 当数值,达到目标值的时候,来停止定时器
// obj是参数2,{left:500}
// 这个500使我们的目标数值
// 获取500这个数值,语法是 对象[属性]
// 属性存储在变量type中,必须要[]语法才能及解析
if (speed == obj[type]) {
clearInterval(time);
}
}, 100);
}
}
// 对象的操作
// 点语法只能直接写属性
// var obj = {name:'张三'};
// console.log(obj.name);
// []语法,可以写属性,要有引号
// 也可以写变量,能解析变量,不用写引号
// var str = 'name'
// console.log(obj[str]);
```