事件语法-DOM
1、事件绑定与事件解绑
1.1绑定
1.1.1行内绑定方式
<div class="rwx" onclick="javaScript:alert(666)"></div>
<div class="rwx" onclick="fn1()"></div>
<script>
function fn1() {
console.log(111);
}
<input type="text" onChange="alert(this.value)">
</script>
1.1.2元素属性绑定方式
- 兼容性很好,但是一个元素的同一个时间上只能绑定一个处理程序
<div class="rwx">点我</div>
<script>
var rwx2 = document.querySelector(".rwx")
rwx2.onclick = function () {
console.log(222);
}
</script>
1.1.3同元素多处理程序绑定方式1
<div class="rwx">点我</div>
<script>
var rwx3 = document.querySelector(".rwx")
function fn() {
console.log(333);
}
function fn1() {
console.log(444);
}
rwx3.addEventListener("click", fn)
rwx3.addEventListener("click", fn1)
</script>
1.1.4同元素多处理程序绑定方式2
obj.attachEvent('on'+type,fn);
//IE专有( ie11例外),一个事件同样可以绑定多个处理程序
// <meta http-equiv="X-UA-Compatible" content="IE=10" />,解决 IE11向后兼容 IE10的问题
1.1.5解决注册事件兼容性
function addEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相当于 element.onclick = fn;
element['on' + eventName] = fn;
}
1.2解绑
- ele.οnclick=false/‘’/null;
- ele.removeEventListener(type,fn,false);
- ele.detachEvent(“on”+type,fn);
ps:2,3若绑定的是匿名函数,则永远无法解除
<div id="xiake" onclick="console.log('下课1')">
下课
</div>
<script>
//1.行内和属性绑定的事件 解绑:xiake.οnclick=null
xiake.onclick=function(){
xiake.onclick=null;
console.log(6666)
}
//2.移除对应的元素的对应的监听程序
function fn1 () {
console.log("真下课1")
}
function fn2 () {
console.log("真下课2")
}
function fn3 () {
console.log("真下课3")
}
xiake.addEventListener("click",fn1)
xiake.addEventListener("click",fn2)
xiake.addEventListener("click",fn3)
xiake.removeEventListener("click",fn1)
// 总结:
// box.οnclick=函数 box.οnclick=null
// box.addEventListener box.removeEventListener
</script>
1.2.1解决删除事件兼容性
function removeEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 removeEventListener 方法
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName] = null;
}
1.2.2页面HTML滚动的距离兼容写法
let pY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop
2、事件类型
2.1鼠标事件
1、click、 mousedown、mousemove、mouseup、dblclick、 contextmenu、mouseover、mouseout、mouseenter(html5标准)、mouseleave(html5标准)
<div class='box'>
<div class="son">
</div>
</div>
<script>
//鼠标事件
//11 click
var box = document.querySelector(".box")
box.addEventListener("click", () => {
console.log("鼠标按下和松开时,鼠标指针都在被选元素内部区域");
})
//22 dblclick
var box = document.querySelector(".box")
box.addEventListener("dblclick", () => {
console.log("鼠标按下和松开的时间间隔不能太长,鼠标指针都在被选元素内部区域");
})
//33 mousedown
var box = document.querySelector(".box")
box.addEventListener("mousedown", () => {
console.log("鼠标在被选元素内按下");
})
//44 mouseup
var box = document.querySelector(".box")
box.addEventListener("mouseup", () => {
console.log("鼠标在被选元素内松开");
})
//55 mouseout
var box = document.querySelector(".box")
box.addEventListener("mouseout", () => {
console.log("鼠标从被选元素出去");
//父元素是被选元素,父到子,子到父 从父移出去都会触发
})
//66 mouseover
var box = document.querySelector(".box")
box.addEventListener("mouseover", () => {
console.log("鼠标进入被选元素");
//父元素是被选元素,进入父,父到子,子到父都会触发
})
//77 mouseleave
var box = document.querySelector(".box")
box.addEventListener("mouseleave", () => {
console.log("鼠标从被选元素出去");
//父元素是被选元素 只有移除父才会触发
})
//88 mouseenter
var box = document.querySelector(".box")
box.addEventListener("mouseenter", () => {
console.log("鼠标从被选元素进去");
父元素是被选元素 只有进入父才会触发
})
//99 scroll
var box = document.querySelector(".box")
box.addEventListener("scroll", () => {
console.log("元素自己的滚动条 滚动就触发:单位时间内滚动条的位置发生变化");
})
</script>
2.2键盘事件
keydown、keyup、keypress
<input type="text" id="box2">
<script>
// 键盘事件
//11 keydown
var box2 = document.querySelector("#box2")
box2.addEventListener("keydown", function () {
console.log("输入框的键盘按下就会触发")
})
//22 keyup
var box2 = document.querySelector("#box2")
box2.addEventListener("keyup", function () {
console.log("输入框的键盘松开才会触发")
})
//33 keypress
var box2 = document.querySelector("#box2")
box2.addEventListener("keypress", function () {
console.log("输入框的键盘按下就会触发")
// 某个键盘按键被按下时触发,但是它不识别功能键和中文打字,比如 ctrl shift 箭头等
})
</script>
2.3输入框操作事件
<input type="text" id="box2">
<script>
// 输入框操作事件
//11 input
var box2 = document.querySelector("#box2")
box2.addEventListener("input", function () {
console.log("输入框在输入字符就触发")
})
//22 change
var box2 = document.querySelector("#box2")
box2.addEventListener("change", function () {
console.log("输入框失焦并且value改变")
})
//33 focus
var box2 = document.querySelector("#box2")
box2.addEventListener("focus", function () {
console.log("输入框获取焦时触发")
})
//44 blur
var box2 = document.querySelector("#box2")
box2.addEventListener("blur", function () {
console.log("输入框失焦时触发")
})
</script>
3、事件对象
3.1事件对象
事件对象上存储着事件发生时的相关信息(例如:event.which)
a) 事件处理函数形参ev(event),W3C制定的标准,IE9以下不行
b) 全局对象 window.event用于IE9以下
// 兼容性写法 var event= ev|| window.event
3.2事件源对象
event.target 火狐只有这个
event.srcElement IE6/78只有这个
这两个chrome都有
兼容性写法 var ele=event.target|| event.srcElement
3.3鼠标事件触发时:
altKey 鼠标事件发生时,是否按下alt键,返回一个布尔
ctrlKey 鼠标事件发生时,是否按下ctrl键,返回一个布尔
metaKey 鼠标事件发生时,是否按下windows/commond键,返回一个布尔
shiftKey 鼠标事件发生时,是否按下shift键,返回一个布尔
pageX 鼠标点击的 X 坐标;(包含body隐藏的)
pageY 鼠标点击的 Y 坐标;(包含body隐藏的)
clientX clientY返回鼠标位置相对于浏览器窗口左上角的坐标,单位为像素(不包括body隐藏的)
screenX screenY返回鼠标位置相对于屏幕左上角的坐标,单位为像素
movementX,movementY返回一个位移值,单位为像素,表示当前位置与上一个mousemove事件之间的距离
offsetX/offsetY 相对于元素自己的x/y 跟它是否是定位的元素无关
3.4键盘事件触发时
charCode/keyCode 键码值 key 键码
37左
38上
39右
40下
13enter
4、元素盒子模型
el.offsetWidth:本身宽度+边框线+左右内边距;
el.offsetHeight:本身高度+边框线+上下内边距;
el.offsetTop:相对第一个父级节点有定位属性的上偏移量;
el.offsetLeft:相对有定位属性的父节点左偏移量;
el.clientWidth:本身的宽度+左右内边距;
el.clientHeight:本身的高度+上下内边距;
el.clientTop:上边框线的宽度
el.clientLeft:左边框线的宽度
el.scrollWidth:盒子的实际宽度(包括滚动条不可见部分,不包括边线)
el.scrollHeight:盒子的实际高度(包括滚动条不可见部分,不包括边线)
el.scrollTop:滚动条向下滚动的距离;
el.scrollLeft:滚动条向右滚动的距离;
window.innerHeight:浏览器窗口可见区域高度;
window.innerWidth:浏览器窗口可见区域宽度;
详解:
clientTop:元素上边框的厚度,当没有指定边框厚底时,一般为0。
scrollTop:位于对象最顶端和窗口中可见内容的最顶端之间的距离,简单地说就是滚动后被隐藏的高度。
offsetTop:获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)距离顶端的高度。
clientHeight:内容可视区域的高度,也就是说页面浏览器中可以看到内容的这个区域的高度,一般是最后一个工具条以下到状态栏以上的这个区域,与页面内容无关。
scrollHeight:IE、Opera 认为 scrollHeight 是网页内容实际高度,可以小于 clientHeight。FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。
offsetHeight:获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)的高度。IE、Opera 认为 offsetHeight = clientHeight + 滚动条 + 边框。FF 认为 offsetHeight 是网页内容实际高度,可以小于 clientHeight。offsetHeight在新版本的FF和IE中是一样的,表示网页的高度,与滚动条无关,chrome中不包括滚动条。
clientX、clientY:相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性。
pageX、pageY:类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性。
offsetX、offsetY:相对于事件源元素(target或srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性。
screenX、screenY:相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性
5、事件链(捕获目标冒泡)
1.事件的三个阶段:
- 先捕获,后目标,再冒泡,只能有一个阶段触发程序执行,比如捕获阶段触发了到了冒泡阶段就不再触发
- 事件经过所有元素都没有被处理,这个事件消失
- 事件传递的过程 只跟文档树的结构有关系 跟界面显示的层叠效果没有任何关系
事件捕获:结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素)。(自顶向下)
事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(自底向上)
2.onclick 和 attachEvent只能得到冒泡阶段
3.addEventListener(type,listener[,useCapture])第三个参数如果是 true,表示在事件捕获阶段调用事件处理程序;如果是 false (不写默认就是false),表示在事件冒泡阶段调用事件处理程序
4.整个事件处理过程,会有个event事件对象在整个事件过程传播(W3C标准,ie8及其以下没有)
5.ie8以下不支持addEventListener
6.focus,blur,change,submit,reset,select等事件不冒泡
5.1捕获阶段
- 两个盒子嵌套,一个父盒子一个子盒子,我们的需求是当点击父盒子时弹出 father ,当点击子盒子时弹出 son
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三个阶段
// 1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
// 2. onclick 和 attachEvent(ie) 只能得到冒泡阶段。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段 document -> html -> body -> father -> son
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
</script>
</body>
- addEventListener绑定事件,如果把第三个参数设置为true,则在捕捉的时候执行事件
- document -> html -> body -> father -> son
- 但是因为DOM流的影响,我们点击子盒子,会先弹出 father,之后再弹出 son
- 先看 document 的事件,没有;再看 html 的事件,没有;再看 body 的事件,没有;再看 father 的事件,有就先执行;再看 son 的事件,再执行。
5.2冒泡阶段
- son -> father ->body -> html -> document
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段 son -> father ->body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
- 我们点击子盒子,会弹出 son、father、document
5.3阻止冒泡事件和默认事件
- 事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点
- 标准写法
e.stopPropagation();
- 非标准写法,ie8及ie8以下可用
e.cancelBubble = true;
<body>
<div class="father">
<div class="son">son儿子</div>
</div>
<script>
// 常见事件对象的属性和方法
// 阻止冒泡 dom 推荐的标准 stopPropagation()
var son = document.querySelector('.son');
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 传播
e.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
点击son就只alertson,不再alert father和document
5.4解决阻止事件冒泡的兼容性
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
5.5事件对象阻止默认行为
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 常见事件对象的属性和方法
// 1. 返回事件类型
var div = document.querySelector('div');
div.addEventListener('click', fn);
div.addEventListener('mouseover', fn);
div.addEventListener('mouseout', fn);
function fn(e) {
console.log(e.type);
}
// 2. 阻止默认行为(事件) 让链接不跳转 或者让提交按钮不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
})
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
// e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
// e.returnValue;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题 特点: return 后面的代码不执行了, 而且只限于传统的注册方式
return false;
alert(11);
}
</script>
</body>
6、e.target 与 this
- this是事件绑定的元素,这个函数的调用者(绑定这个事件的元素)
- e.target是事件触发的元素,例如是点击的那个元素
<body>
<div>123</div>
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 常见事件对象的属性和方法
// 1. e.target 返回的是触发事件的对象(元素) this 返回的是绑定事件的对象(元素)
// 区别 : e.target 点击了那个元素,就返回那个元素 this 那个元素绑定了这个点击事件,那么就返回谁
var div = document.querySelector('div');
div.addEventListener('click', function(e) {
console.log(e.target);
console.log(this);
})
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我们给ul 绑定了事件 那么this 就指向ul
console.log(this);
console.log(e.currentTarget);
// e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
console.log(e.target);
})
// 了解兼容性
// div.onclick = function(e) {
// e = e || window.event;
// var target = e.target || e.srcElement;
// console.log(target);
// }
// 2. 了解 跟 this 有个非常相似的属性 currentTarget ie678不认识
</script>
</body>
7、事件代理
7.1事件代理的原理:
不是给每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒
泡原理影响设置每个子节点
7.2事件代理案例 ul-li
案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器。
<body>
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件代理的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// alert('知否知否,点我应有弹框在手!');
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'pink';
// 点了谁,就让谁的style里面的backgroundColor颜色变为pink
})
</script>
</body>
8、页面渲染流程
8.1页面渲染流程
8.1.1页面呈现过程
不同的浏览器略微会有些不同。但基本上都是类似的
①.浏览器把html代码解析成1个Dom树,html中的每个tag都是Dom树中的1个节点,根节
点就是我们常用的document对象。dom树就是html结构,里面包含了所有的html tag,包
括用JS添加的元素。
②浏览器把所有样式(主要包括css和浏览器自带的样式设置)解析成样式结构体,在解析的
过程中会去掉浏览器不能识别的样式。
③dom tree和样式结构体结合后构建呈现树(render tree),render tree有点类似于dom tree,
但有区别,render tree能识别样式,render tree中每个node都有自己的style,而且render
tree不包含隐藏的节点(比如display:none的节点,还有head节点),因为这些节点不会用于
呈现,而且不会影响呈现的,所以就不会包含到render tree中。但是visibility:hidden隐藏的
元素还是会包含到render tree中的,因为visibility:hidden 会影响布局,会占有位置。
④一旦render tree构建完毕后,浏览器就根据render tree来绘制页面。
8.1.2回流与重绘
① 当render tree中因为元素的数量,尺寸,布局,隐藏等改变而需要重新构建。这就称为回
流或者回档(其实我觉得叫重新布局更简单明了些)。每个页面至少需要一次回流,就是在
页面第一次加载的时候。
②当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而
不会影响布局的,比如background-color。则就叫称为重绘。
③(面试,选择题,问答题就答上面)从上面可以看出,回流必将引起重绘,而重绘不一定会引
起回流。
④重绘与回流操作的注意点
//重绘与回流操作的次数越多,计算机的性能消耗越大
进行dom操作的时候,就要考虑
重绘 和 回流/回档
重绘 就是按照文档树的结构 重新绘制页面 渲染
回流/回档 就是页面的元素排版布局数量和节点在树中位置等发生了改变 就是回流
回流必然引起重绘 但是重绘不一定引起回流
8.1.3常见的回流和重绘操作
重绘:当渲染树中的元素外观(如:颜色)发生改变,不影响布局时,产生重绘
回流:当渲染树中的元素的布局(如:尺寸、位置、隐藏/状态状态)发生改变时,
产生重绘回流
回流必将引起重绘,而重绘不一定会引起回流
任何对render tree中元素的操作都会引起回流或者重绘
①添加、删除元素(回流+重绘)
② 隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
③ 移动元素,比如改变top,left(jquery的animate方法就是,改变top,left不一定会影响回流),
或者移动元素到另外1个父元素中。(重绘+回流)
④ 对style的操作(对不同的属性操作,影响不一样)
⑤ 还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小等(回流+重绘)
function change1 () {
var box=document.querySelector(".box")
// box.innerHTML="6666"//回档
// box.style.visibility= "hidden";//重绘不回流
// box.style.display= "none";//回流
}
8.2避免高频重绘案例
- 添加1万个格子到页面上 每个格子显示时间(ms)
不推荐方法:
let tb=document.querySelector(".box")
for(let i=0;i<100;i++){
let tr=document.createElement("tr")
tb.appendChild(tr)
for(let j=0;j<100;j++){
let dt=new Date().getTime()
let td=document.createElement("td")
td.innerHTML=dt
tr.appendChild(td)
}
}
// 回档了1万零100次
推荐方法:
创建一个元素来承载即将渲染的元素
let tb = document.querySelector("#box")
let fra1 = document.createDocumentFragment() //它在内存中还不在文档中
for (let i = 0; i < 100; i++) {
let tr = document.createElement("tr")
fra1.appendChild(tr)
for (let j = 0; j < 100; j++) {
let dt = new Date().getTime()
let td = document.createElement("td")
td.innerHTML = dt
tr.appendChild(td)
}
}
tb.appendChild(fra1) //它自己不会添加到文档树中用于渲染 但是它的子代元素都会添加进去
// 回档了1次
9、获取元素节点和元素样式属性及修改
9.1不能获取script脚本后的元素节点
解决方案一:
- 当页面加载完成的事件触发 再去执行获取节点的方式
- window.onload
function fn(){
var box2=document.querySelector(".box2")
console.log(box2)
}
window.onload=fn
解决方案二
script-- defer async 修改src如何加载外部js资源的异步属性
代码写到js文件中:
9.2获取元素样式属性及修改
9.2.1用element.style获取
- 原生js操作样式只能操作元素的行内样式
- 非行内样式只能获取不能设置。
- 用.style获取width(行内样式)可以,但是获取height等其他样式(非行内样式)时结果为空
box.style.width = "200px"
设置的是行内样式而不是非行内样式
9.2.2window.getComputedStyle() 方法的使用
获取:
console.log(window.getComputedStyle(btn));//CSSStyleDeclaration
console.log(window.getComputedStyle(btn).backgroundColor);
设置会报错
window.getComputedStyle(btn).width = "200px";
box.style.width = parseInt(box.style.width) * 2 + 'px'//不会生效
box.style.width = parseInt(window.getComputedStyle(box).width) * 2 + 'px'
// 会生效
9.2.3getComputedStyle() 和 style 的异同
getComputedStyle 和 element.style 的相同点就是二者返回的都是
CSSStyleDeclaration 对象,取相应属性值得时候都是采用的 CSS 驼峰式写法,均需要注
意 float 属性。
而不同点就是:
element.style 读取的只是元素的内联样式,即写在元素的 style 属性上的样式;而
getComputedStyle 读取的样式是最终样式,包括了内联样式、嵌入样式和外部样式。
element.style 既支持读也支持写,我们通过 element.style 即可改写元素的样式。而
getComputedStyle 仅支持读并不支持写入。我们可以通过使用 getComputedStyle 读取样
式,通过 element.style 修改样式。
box.style.width = parseInt(window.getComputedStyle(box).width) * 2 + 'px'
10、防抖与节流
高频率触发的业务: 如抽奖 登录 动画 网络加载等等需要做防抖或者是节流操作
10.1防抖
10.1.1防抖思想
- 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
- 电梯第一个人进来后,15秒后准时运送一次,这是节流。
- 电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖。
- 防抖就是当持续触发事件时,会等到停止后一段时间才开始执行。为什么需要这样呢?比如,要是我们点击一个按键,我们是不是只需要提交一次,要是我们疯狂点击,是不是就会疯狂触发。还有输入搜索,我们是不是应该输入结束才开始调用接口搜索,而不是每输入一个字就调用一次。使用防抖函数后,就使得只会触发一次,避免了无意义的触发
。
10.1.2防抖方案
document.onclick = fangdou(function(e) {
console.log(6666)
}, 1000)
function fangdou(cb, delay) {
var timer = null;
return function() {
//return这个函数频率很高 想办法让cb()执行慢下来:防抖
clearTimeout(timer)
timer = setTimeout(function() {
cb()
}, delay)
}
}
10.1.3防抖方案优化
document.onclick = fangdou2(function (e) {
console.log(6666, e, this)
}, 1000)
function fangdou2(cb, delay) {
var timer = null;
return function () {
//return这个函数频率很高 想办法让cb()执行慢下来:防抖
let arg = arguments
clearTimeout(timer)
timer = setTimeout(() => {
cb.apply(this, arg)
}, delay)
}
}
10.2节流
10.2.1节流思想
- 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
- 电梯第一个人进来后,15秒后准时运送一次,这是节流。
- 电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖。
- 节流是持续触发事件,会每隔一段时间,才执行执行一次,比如在DOM元素的拖拽功能中,射击游戏,计算鼠标移动距离中。当我们计算鼠标的移动距离,要是没有使用节流函数,那么每移动1px,就会调用计算。我们可想而知,当我们轻轻滑动。就会调用触发无数次了,会给服务器带来极大的压力。但我们使用后,比如设置间隔时间,持续触发那么他就会每隔这个时间段触发一次。大大减少服务端压力。
10.2.2节流方案
document.onmousemove=jieliu(function(){console.log(666)},20)
function jieliu(cb,daley){
var timer=null;
return function(){
// console.log(66666,timer)
if(!timer){
timer=setTimeout(()=>{
cb();
timer=null
},daley)
}
}
}
10.2.3节流方案优化
function jieliu2(cb, daley) {
var timer = null;
return function () {
// console.log(66666,timer)
// this是事件函数的this
let arg = arguments
if (!timer) {
timer = setTimeout(() => {
cb.apply(this, arg);//本来是window但是希望
timer = null
}, daley)
}
}
}
11、缓动动画
11.1定义
缓动动画: 匀速改变元素的样式 就是匀速动画 非匀速的改变元素css样式的动画效果就
是缓动动画
设定值=当前+(目标值-当前)*百分比
目标是让div的高度为800 div.style.heiht=当前h+(800-当前w)*0.5
11.2缓动案例
11.2.1下拉菜单缓动动画
<style>
.djxl {
width: 200px;
height: 40px;
background-color: #007ACC;
border-radius: 5px;
}
.caidan {
width: 150px;
height: 0px;
background-color: #cc0047;
border-radius: 5px;
overflow: hidden;
position: absolute;
left: 32px;
}
.box {
text-align: center;
height: 30px;
line-height: 30px;
margin: 2px;
background-color: bisque;
}
</style>
<body>
<button class="djxl">点击出现菜单</button>
<div class="caidan">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
</div>
<script>
// 设定值=当前值+(目标值-当前值)*百分比
window.onload = function () {
var djxl = document.querySelector(".djxl")
var flag = true
var timer;
var result;
djxl.addEventListener("click", function () {
var caidan = document.querySelector(".caidan")
clearInterval(timer)
if (flag = !flag) {
result = 0
} else {
result = 166
}
timer = setInterval(() => {
var h = window.getComputedStyle(caidan).height
caidan.style.height = parseInt(h) + (result - parseInt(h)) * 0.3 + "px"
}, 20)
})
}
</script>
11.2.2信息滑入缓动动画
<style>
.dahezi {
position: relative;
width: 200px;
height: 200px;
overflow: hidden;
}
.huaruk {
width: 200px;
height: 60px;
line-height: 60px;
text-align: center;
position: absolute;
background-color: rgba(0, 122, 204, 0.5);
top: 200px;
}
</style>
<body>
<div class="dahezi">
<img src="./tupian/images (2).jpg" alt="">
<div class="huaruk">大帅哥</div>
</div>
<script>
// 设定值=当前值+(目标值-当前值)*百分比
var dahezi = document.querySelector(".dahezi")
var timer;
var flag = true;
dahezi.addEventListener("mouseenter", () => {
var huaruk = document.querySelector(".huaruk")
var mubiaozhi = 140
clearInterval(timer)
timer = setInterval(() => {
huaruk.style.top = huaruk.offsetTop + (mubiaozhi - huaruk.offsetTop) * 0.4 + "px"
}, 20)
})
dahezi.addEventListener("mouseleave", () => {
var mubiaozhi = 200
clearInterval(timer)
var huaruk = document.querySelector(".huaruk")
timer = setInterval(() => {
huaruk.style.top = huaruk.offsetTop + (mubiaozhi - huaruk.offsetTop) * 0.4 + "px"
}, 20)
})
</script>
11.2.2缓动模态窗口+拖拽效果
<style>
.dahezi {
position: relative;
width: 200px;
height: 200px;
overflow: hidden;
}
.huaruk {
width: 200px;
height: 60px;
line-height: 60px;
text-align: center;
position: absolute;
background-color: rgba(0, 122, 204, 0.5);
top: 200px;
}
</style>
<body>
<div class="dahezi">
<img src="./tupian/images (2).jpg" alt="">
<div class="huaruk">大帅哥</div>
</div>
<script>
// 设定值=当前值+(目标值-当前值)*百分比
var dahezi = document.querySelector(".dahezi")
var timer;
var flag = true;
dahezi.addEventListener("mouseenter", () => {
var huaruk = document.querySelector(".huaruk")
var mubiaozhi = 140
clearInterval(timer)
timer = setInterval(() => {
huaruk.style.top = huaruk.offsetTop + (mubiaozhi - huaruk.offsetTop) * 0.4 + "px"
}, 20)
})
dahezi.addEventListener("mouseleave", () => {
var mubiaozhi = 200
clearInterval(timer)
var huaruk = document.querySelector(".huaruk")
timer = setInterval(() => {
huaruk.style.top = huaruk.offsetTop + (mubiaozhi - huaruk.offsetTop) * 0.4 + "px"
}, 20)
})
</script>
12、预加载与懒加载
- 预加载: 提前加载资源–同源加载的优化
- 懒加载: 先不加载 等条件成立时再加载