一、事件捕获/冒泡
事件捕获:事件捕获会由最先接收到事件的元素然后传向最里边(我们可以将元素想象成一个盒子装一个盒子,而不是一个积木堆积)
事件冒泡:事件冒泡即由最具体的元素(文档嵌套最深节点)接收,然后逐步上传至document
示例:
我们点击一个span,我可能就想点击一个span,事实上他是先点击document,然后点击事件传递到span的,而且并不会在span停下,span有子元素就会继续往下,最后会依次回传至document。
DOM事件:web端Dom事件分为三类:一类:dom节点通过onclick绑定事件,通过dom.onclick = null来解绑事件;二类:dom节点通过addEventListener()方法注册事件,通过removeEventListener()来注销事件;三类:css中设置ui事件,比如:hover事件,焦点事件(input框)等;
二、DOM2级事件流
1、事件捕获阶段:事件从window对象自上而下向目标节点传播的阶段
2、处于目标阶段:真正的目标节点正在处理事件的阶段
3、事件冒泡阶段:事件从目标节点自下而上向window对象传播的阶段
示例:
addEventlistener的第三个参数为ture是阻止事件捕获传递还是false? 都不会阻止事件传递,因为ture捕获阶段触发,false冒泡阶段触发,要阻止事件传递,唯一的办法就是阻止事件冒泡:事件对象调用stopPropagation()
//addEventListener第三个参数 true捕获阶段触发 false冒泡阶段触发
var box1=document.querySelector(".box1")
var box2=document.querySelector(".box2")
var box3=document.querySelector(".box3")
// box1.addEventListener("click",(e)=>{
// console.log("box111111111a",e)
// },true)
// box2.addEventListener("click",(e)=>{
// console.log("box22222")
// })
// box3.addEventListener("click",(e)=>{
// // 阻止事件冒泡
// // e.stopPropagation()
// console.log("box33333")
// })
//addEventListener的第三个参数为true是阻止事件传递还是false?
//都不会阻止事件传递,因为true捕获阶段触发 false冒泡阶段触发
//要阻止事件传递 唯一的方式就是阻止事件冒泡:事件对象调用stopPropagation()
//优化:
//兼容写法
// box1.addEventListener("click",(e)=>{
// console.log("box111111111a",e)
// })
// box3.addEventListener("click",(e)=>{
// console.log("box3333333333a",e)
// //智能浏览器:
// //阻止向父级元素冒泡
// // e.stopPropagation()
// //阻止程序传递执行冒泡
// // e.stopImmediatePropagation()
// //ie8以下
// // e.cancelBubble=false
// })
// box3.addEventListener("click",(e)=>{
// console.log("box3333333333b",e)
// })
考试题 也是未来企业的常见的笔试面试题
事件代理: 网页设计中一种设计思想 利用事件对象中引用的目标对象这个技术来实现的
-
无论事件触发时 是不是目标对象的监听器 在监听器内部的事件对象event中都可以访问这个事件的目标对象,利用这个特点去绑定事件给父级元素 来代理子级元素的业务,这种设计就是事件代理
// var box2s=document.querySelectorAll(".box2") // box2s.forEach(el=>{ // el.addEventListener("click",function(e){ // console.log(this.innerHTML) // }) // }) // //这样设计有两个缺点 // //1.静态的事件绑定:如果动态的添加元素进去 添加进去的元素没有这个事件 // //2.性能消耗更高 业务却相同 function load1(){ // var box1=document.querySelector(".box1") // box1.innerHTML+='<div class="box2">hello5</div>' var box1=document.querySelector(".box1") var box2=document.createElement("div") box2.innerHTML="hello5" box2.className="box2" box1.appendChild(box2) } // 代理 // var box1=document.querySelector(".box1") // box1.addEventListener("click",function(e){ // console.log(e.target.innerHTML) // }) </script> //额外的一些技术 <div id="box5" data-hqyj1="200" data-src1="http://xxxx"> 666 </div> <script> // document.onclick=function(){ // } // var box1=document.querySelector(".box1") // box1.addEventListener("click",function(e){ // console.log(e.target)//事件的目标对象 // console.log(e.srcElement)//事件的目标对象 // console.log(document.documentElement)//html元素 // console.log(document.body)//body元素 // console.log(document)//根节点 // }) // console.log(box5.dataset) // document.write("hello") </script>
重绘与回流
什么是重绘?
重绘: 当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格,而不会影响布局的操作,比如 background-color,我们将这样的操作称为重绘。
什么是回流?
回流:当渲染树中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新构建的操作,会影响到布局的操作,这样的操作我们称为回流。
常见引起回流属性和方法:
- 任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发回流。
(1)添加或者删除可见的 DOM 元素; (2)元素尺寸改变——边距、填充、边框、宽度和高度 (3)内容变化,比如用户在 input 框中输入文字 (4)浏览器窗口尺寸改变——resize事件发生时 (5)计算 offsetWidth 和 offsetHeight 属性 (6)设置 style 属性的值 (7)当你修改网页的默认字体时。
// 重绘 和 回流/回档
//重绘 就是按照文档树的结构 重新绘制页面 渲染
//回流/回档 就是页面的元素排版布局数量和节点在树中位置等发生了改变 就是回流
//回流必然引起重绘 但是重绘不一定引起回流
function change1 () {
var box=document.querySelector(".box")
// box.innerHTML="6666"//回档
// box.style.visibility= "hidden";//重绘不回流
// box.style.display= "none";//回流
}
//我们的程序执行时 常常会操作页面 常常引起重绘/回流
//频繁的重绘/回流会造成页面性能不好==>页面卡顿 迟缓 手机发烫
//因此操作页面时: 尽量避免高频重绘 使用框架(MVVM)
小结 :
回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。
前端js元素style的样式操作
1.获取script脚本节点后面加载的元素节点 是获取不了的
//因为文档的加载是按照文档树的顺序加载的
// var box2=document.querySelector(".box2")
// console.log(box2)
//解决方案一:当页面加载完成的事件触发 再去执行获取节点的方式
// function fn(){
// var box2=document.querySelector(".box2")
// console.log(box2)
// }
// window.onload=fn
// 解决方案二:
//script-- defer async 修饰src如何加载外部js资源的异步属性
// 代码写到js文件中:
// var box2 = document.querySelector(".box2")
// console.log(box2)
案列:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
div{
width: 300px;
height: 300px;
text-align: center;
background-color: yellow;
line-height: 150px;
margin: 60px auto;
font-size: 40px;
}
</style>
</head>
<body>
<div>哈哈哈哈哈哈哈哈中午吃啥</div>
</body>
</html>
<script type="text/javascript">
var div=document.querySelector("div");
//我们一般通过标签.style.属性值=值;进行赋值操作
//那我们该如何取值呢
var a=div.style.width;
//这样取不可行
//此操作正能对行间引用的css样式起作用 本质还是对象的.属性
//对于外部引用的css样式或者head引用的css无法取出css样式的值
console.log(a);
//这是正确的去css样式值的方式
//getComputedStyle(获取想要的标签样式)
var obj=getComputedStyle(div);
console.log(obj);
console.log(obj.width);
console.log(obj.height);
console.log(obj["font-size"]);
//取整操作
var a="1782px";
console.log(parseInt(a));
var a1="哈哈哈1782px";
console.log(parseInt(a1));
var a2="1782哈哈哈px";
console.log(parseInt(a2));
var b=89.6767;
var c=parseInt(b);
console.log(c);
//去小数操作
a=10;
console.log(parseFloat(a));
a="241.31哈哈";
console.log(parseFloat(a));
</script>
getComputedStyle (标签,null ) .属性用于获取标签的当前样式,第一个参数是标签,第二个参数是伪元素,第二个参数不填空。 getComputedStyle与ie8及以下浏览器不兼容。
标记. currentStyle用于获取标记的当前样式,但currentStyle仅在ie浏览器中可用。
- 综上所述,如果我们在style .属性中获取内联样式,而在css中也写入了内联样式,则必须将getComputedStyle和currentStyle组合起来。 getComputedStyle和currentStyle可以编写在兼容浏览器中获取样式值的方法
防抖与节流
防抖
- 防抖:在一段时间内允许多次触发函数,但是只在最后一次有效(执行)
做法:
每次触发函数清除掉原来的定时器,重新开始计时。
如果在规定时间内不再触发函数,则执行,否则,清除掉之前的定时器,重新计时。
<!-- 防抖:防抖和节流不同点在于,函数在一段时间内可以多次调用,尽仅在最后一次调用有效 -->
<!-- 每一次的点击都要清除掉之前的定时器,重新定时 -->
<input type="text">
<button>提交</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', debounce(submit, 2000));
function submit() {
console.log('点击了');
}
// 为submit函数添加防抖
function debounce(func, delay) {
var timeout;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, arguments);
}, delay)
}
}
</script>
</body>
节流
节流:在一段时间内,只能触发一次函数。
做法:触发函数时判断是否到达了指定时间,如果到达了指定时间,执行;否则不执行弹出警告
- 时间戳法
<input type="text">
<button>提交</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', throttle(submit, 2000));
function thorttle(func,delay){ //传入需要节流的函数,延迟时间
var last = 0; // 上一次触发的时间
return function(){
var now = Date.now(); //获取当前时间
if(now >= delay + last){
//当前时间已经符合再次触发条件了
func(this,arguments);
// console.log(this); //this指向window
last = now; //当前的时间就变为了上次的
}else{
console.log('时间未到');
}
}
}
</script>
- 定时器法
<body>
<input type="text">
<button>提交</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', throttle(submit, 2000));
function thorttle(func, delay) {
//设置一个空的定时器
var timer = null;
return function () {
if (!timer) {
// 如果定时间为空,执行操作
func.apply(this, arguments);
// 设置定时器,并且在delays后定时器变为空时,可再次操作
timer = setTimeout(() => {
timer = null;
}, delay);
} else {
console.log('时间未到');
}
}
}
</script>
- 时间戳加定时器
// 节流throttle代码(时间戳+定时器):
var throttle = function(func, delay) {
var timer = null;
var startTime = Date.now();
return function() {
var curTime = Date.now();
var remaining = delay - (curTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
总结
-
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
-
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
预加载和懒加载
前端页面进行浏览页面时都会发生加载的行为,其中又可分为预加载和懒加载。
-
1.预加载:顾名思义,预加载就是在你还没有看到的地方就已经开始加载,好处就是极大的提高了用户的使用体验,但是不好的是会增加流量的消耗,而且会增加前端的压力等。
-
2.懒加载:懒加载的主要目的是优化前端性能,减少请求数或延迟请求数。可以延迟加载,可视区域加载,或者说利用定时器进行延迟加载。但是可能会加载缓慢,页面显示延迟等。
接下来让大家更直观地看到这个区别
预加载
<script type="text/javascript">
var images = new Array()
function preload() {
for (i = 0; i < preload.arguments.length; i++) {
images[i] = new Image()
images[i].src = preload.arguments[i]
}
}
// 图片文件路径,也可以是网络文件
preload(
"../image/image-001.jpg",
"../image/image-002.jpg",
"../image/image-003.jpg"
)
</script>
懒加载
实现懒加载思路就会更加清晰,咱们只需要对看见的地方进行加载,看不见的不进行加载,所以就需要获取到当前页面的位置。从而进行下一步的操作。
//页面加载时先调用一次loadPage函数
loadPage()
window.onscroll = function () {
loadPage()
}
//封装 加载方法
function loadPage() {
var viewHeight = document.documentElement.clientHeight;
var viewTop = document.documentElement.scrollTop || document.body.scrollTop;
//这里我获取的是类名,可以根据页面改为直接获取img
//注意获取的元素要有自己的宽高,不然会直接加载整个页面的所有图片,功能也就失效了
var img = document.getElementsByClassName("img_pdf");
for (var i = 0; i < img.length; i++) {
if (offsetTop1(img[i]) < (viewHeight + viewTop)) {
var src = img[i].getAttribute("data-url");
//赋值
img[i].src = src;
}
}
}
//封装获取元素离上方的高度的函数
function offsetTop1(obj) {
var t = obj.offsetTop;
while (obj.offsetparent) {
obj = obj.offsetparent;
t = t + obj.offsetTop;
}
return t;
}
这样页面向下滚动下面会开始加载,当停止滚动的时候页面也就不加载了,极大的节省了流量的消耗。
懒加载和预加载的对比
1)概念 懒加载也叫延迟加载:JS图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片。 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
2)技术 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一 定的缓解压力作用,预加载则会增加服务器前端压力。
3)实现方式
懒加载:
1.第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
2.第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
3.第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,一般会在距用户看到
某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。(推荐写法)
预加载:
1.用CSS和JavaScript实现预加载
如果用CSS实现预加载,如果中间某一张图太大,可能会导致后面加载不进来的情况,在前端渲染可能就渲染出来。
2.仅使用JavaScript实现预加载(推荐写法)
3.使用Ajax实现预加载(推荐写法)