栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

事件链、事件代理、页面的渲染过程、style的操作、防抖与节流【DOM(四)】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

事件链、事件代理、页面的渲染过程、style的操作、防抖与节流【DOM(四)】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

目录

文章目录

1.事件链(冒泡目标捕获)

 (1)事件链原理

(2)阻止冒泡和默认事件

2.事件代理(面试笔试题重点)

3.页面的渲染流程(高频面试题)

3.1 浏览器加载一份html文档是怎么加载的?

3.2重绘、回流/回档

4.style的操作

(1)行内样式

(2)获取最终绘制样式

5.防抖与节流(重点)

6.预加载与懒加载


1.事件链(冒泡目标捕获)

 (1)事件链原理

(1)事件的三个阶段: 先捕获,后目标,再冒泡,只能有一个阶段触发程序执行,比如捕获阶段触发了到了冒泡阶段就不再触发

(2)事件经过所有元素都没有被处理,这个事件消失

(3)事件传递的过程 只跟文档树的结构有关系 跟界面显示的层叠效果没有任何关系

  

事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素。(自底向上)

事件捕获:结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素)。(自顶向下)

2.默认在冒泡的时候执行事件:onclick/attach和 addEventListener传false的时候

3.addEventListener绑定事件,如果把第三个参数设置为true,则在捕捉的时候执行事件

3.整个事件处理过程,会有个event事件对象在整个事件过程传播(W3C标准,ie8及其以下没有)

4.ie8以下不支持addEventListener

5.focus,blur,change,submit,reset,select等事件不冒泡

      addEventListener第三个参数  true捕获阶段触发  false冒泡阶段触发

(2)阻止冒泡和默认事件

1、阻止事件冒泡: W3C标准 event.stopPropagation();但不支持 ie9以下版本

        stopImmediatePropagation() 支持stopPropagation的浏览器中也可以用                  stopImmediatePropagation()方法,这个不仅会阻止事件向祖元素的冒泡,也会阻止同一个节点上同一事件的其他的事件处理程序(优先级比它低的,同元素同事件多处理程序时)

        event.cancelBubble=true; ie8及ie8以下可用

2、阻止默认事件: 默认事件——表单提交,a标签跳转,右键菜单等等

        return false; 以对象属性的方式注册的事件才生效,用addEventListener/attachEvent这种是不生效的

        event.preventDefault(); W3C标准,IE9以下不兼容

        event.returnValue=false;兼容IE

案例如下:

2.事件代理(面试笔试题重点)

(1)事件代理: 网页设计中一种设计思想 利用事件对象中引用的目标对象这个技术来实现的

(2)无论事件触发时 是不是目标对象的监听器 在监听器内部的事件对象event中都可以访问这个事件的目标对象,利用这个特点去绑定事件给父级元素 来代理子级元素的业务,这种设计就是事件代理

 事件代理设计有两个缺点:

            1.静态的事件绑定:如果动态的添加元素进去  添加进去的元素没有这个事件

            2.性能消耗更高  业务却相同

        
		
			hello1
			hello2
			hello3
			hello4
		
		

        var box2s=document.querySelectorAll(".box2")
			box2s.forEach(el=>{
				el.addEventListener("click",function(e){
					console.log(this.innerHTML)
				})
			})


    //案例事件代理的缺点的举例:
    function load1(){
				// var box1=document.querySelector(".box1")
				// box1.innerHTML+='hello5'
				var box1=document.querySelector(".box1")
				var box2=document.createElement("div")
				box2.innerHTML="hello5"
				box2.className="box2"
				box1.appendChild(box2)			
			}
			

3.页面的渲染流程(高频面试题)

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来绘制页面。

用户在浏览器地址栏输入url(网址)后,到页面渲染出来,背后发生了什么?

==>请求网址对应的资源,得到资源,然后运行文档(简单的过程,不是标准答案)

      答案:前端=>网络=>后端=>网络=>前端  这4步都得分析

3.1 浏览器加载一份html文档是怎么加载的?

 浏览器加载一份html文档是怎么加载的?

 ==> (1)把标签、文本、注释、属性等等解析为节点树(DOM Tree)

        (2)解析DOM tree中的节点时遇到了不太的元素会有不同的操作:

                (2.1) style标签或者link-css,遇到css代码就会把css解析为css样式结构体

                (2.2) 遇到了src会去加载(网络请求)资源

        (3)把(2.1)css样式结构体和(1)DOM Tree结合变成呈现树/渲染树(Render tree)

        (4)按照Render tree绘制页面

呈现树有的文档树中一定有,呈现树中没有的文档树中可能也有

3.2重绘、回流/回档

自己理解:    

        重绘就是按照文档树的结构,重新绘制页面渲染

        回流/回档:页面的元素排版布局数量和节点在树中的位置等发生了改变,就是回流/回档

        回流必然引起重绘,但是重绘不一定引起回流

案例例如:

        
		hello
		1231243254
		

    	function  change1 () {
				var box=document.querySelector(".box")
				// box.innerHTML="6666"                //回档
				// box.style.visibility= "hidden";    //重绘不回流
				// box.style.display= "none";        //回流
			}

2.回流与重绘

① 当render tree中因为元素的数量,尺寸,布局,隐藏等改变而需要重新构建。这就称为回流或者回档(其实我觉得叫重新布局更简单明了些)。每个页面至少需要一次回流,就是在页面第一次加载的时候。

②当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

③(面试,选择题,问答题就答上面)从上面可以看出,回流必将引起重绘,而重绘不一定会引起回流。

3.常见的回流和重绘操作 任何对render tree中元素的操作都会引起回流或者重绘

①添加、删除元素(回流+重绘)

② 隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)

③ 移动元素,比如改变top,left(jquery的animate方法就是,改变top,left不一定会影响回流),或者移动元素到另外1个父元素中。(重绘+回流)

④ 对style的操作(对不同的属性操作,影响不一样)

⑤ 还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小等(回流+重绘)

④重绘与回流操作的注意点 //重绘与回流操作的次数越多,计算机的性能消耗越大 进行dom操作的时候,就要考虑

案例例如:

        

		
我们的程序执行时 常常会操作页面 常常引起重绘/回流 频繁的重绘/回流会造成页面性能不好==>页面卡顿 迟缓 手机发烫 因此操作页面时: 尽量避免高频重绘 使用框架(MVVM) 尽量避免高频重绘案例: 添加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次 1658297335582-1658297335446 136ms
	
			创建一个"冰"元素来承载即将渲染的元素
			dom=>fragment
			wx=>block
			vue=>template
			react=>
			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次
			1658297870113-1658297869968 145ms  

4.style的操作

1.获取script脚本节点后面加载的元素节点 是获取不了的

            因为文档的加载是按照文档树的顺序加载的

 解决方案一:当页面加载完成的事件触发 再去执行获取节点的方式

 解决方案二:script-- defer  async  修饰src如何加载外部js资源的异步属性

程序案例如下:

 

(1)行内样式

el.style.xx
设置/获取 ,只能操作行内样式
没有兼容问题,通吃
只能设置为字符串
样式必须用驼峰命名法 

遇到与保留字一样的样式,前面应加“css”(eg:float——>el.style.cssFloat)
==>其实是获取的文档树中的元素的属性值

(2)获取最终绘制样式

let styleobj=window.getComputedStyle(ele);

styleobj对象是一个最终绘制的样式结果对象  有with height等270多个样式值

只能获取样式,要设置使用el.style.xx

拓展:window.getComputedStyle(ele,"after")
//第二个参数解决的是获取伪元素样式

ele.currentStyle
计算样式,只读
IE独有的属性

==>其实是获取的呈现树中的元素的属性值

5.防抖与节流(重点) 防抖:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触 发,则重新计算时间。 思路:每次触发事件时都取消之前的延时调用方法。
function debounce (fn, delay,...rest) {         let timeout = null;         return function (e) {                 // e:事件对象         if (timeout) { clearTimeout(timeout) };//把前面那个 timeout 清除         timeout = setTimeout(()=>{                 console.log(e, rest);                 console.log(this);                 fn.apply(this, [e, ...rest]);// 绑定 this 指向         }, delay);      } } function sayHi(e, ...rest) {         console.log(e, rest);         console.log(this.value); } var inp = document.getElementById('inp'); inp.addEventListener('input', debounce(sayHi, 300, 100, 200));
节流:高频事件触发,但在 n 秒内只执行一次,所以节流会稀释函数的执行频率。 思路:每次触发事件时都判断当前是否有等待执行的延时函数。
function throttle(fn) {         let canRun = true; // 通过闭包保存一个标记         return function () {                 if (! canRun) return; // 在函数开头判断标记是否为 true ,不为 true 则 return         canRun = false; // 立即设置为 false         setTimeout(() => { // 将外部传入的函数的执行放在 setTimeout 中                 fn. apply (this, arguments);                 // 最后在 setTimeout 执行完毕后再把标记设置为 true( 关键 ) 表示可以执行下一次循环了。当定时器没有执行的时候标记永远是 false ,在开头被 return 掉         canRun = true;         }, 500);      }; } function sayHi(e) {         console.log (e. target. innerWidth, e. target. innerHeight); } window. addEventListener ('resize', throttle(sayHi));

6.预加载与懒加载

  预加载: 提前加载资源--同源加载的优化

  懒加载: 先不加载 等条件成立时再加载

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1023995.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号