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


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

     (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.性能消耗更高  业务却相同

    1. <div class="box1">
    2. <div class="box2">hello1div>
    3. <div class="box2">hello2div>
    4. <div class="box2">hello3div>
    5. <div class="box2">hello4div>
    6. div>
    7. <button onclick="load1()">网络请求了新的新闻button>
    8. var box2s=document.querySelectorAll(".box2")
    9. box2s.forEach(el=>{
    10. el.addEventListener("click",function(e){
    11. console.log(this.innerHTML)
    12. })
    13. })
    14. //案例事件代理的缺点的举例:
    15. function load1(){
    16. // var box1=document.querySelector(".box1")
    17. // box1.innerHTML+='
      hello5
      '
    18. var box1=document.querySelector(".box1")
    19. var box2=document.createElement("div")
    20. box2.innerHTML="hello5"
    21. box2.className="box2"
    22. box1.appendChild(box2)
    23. }

    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重绘、回流/回档

    自己理解:    

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

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

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

    案例例如:

    1. <style>
    2. .box{
    3. width: 200px;
    4. height: 200px;
    5. background-color: red;
    6. /* visibility: hidden; */
    7. /* display: none; */
    8. }
    9. style>
    10. <div class="box">hellodiv>
    11. <h1>1231243254h1>
    12. <button onclick="change1()">changebutton>
    13. function change1 () {
    14. var box=document.querySelector(".box")
    15. // box.innerHTML="6666" //回档
    16. // box.style.visibility= "hidden"; //重绘不回流
    17. // box.style.display= "none"; //回流
    18. }

    2.回流与重绘

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

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

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

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

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

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

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

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

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

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

    案例例如:

    1. <table id="box">table>
    2. 我们的程序执行时 常常会操作页面 常常引起重绘/回流
    3. 频繁的重绘/回流会造成页面性能不好==>页面卡顿 迟缓 手机发烫
    4. 因此操作页面时: 尽量避免高频重绘 使用框架(MVVM)
    5. 尽量避免高频重绘案例:
    6. 添加1万个格子到页面上 每个格子显示时间(ms)
    7. let tb=document.querySelector(".box")
    8. for(let i=0;i<100;i++){
    9. let tr=document.createElement("tr")
    10. tb.appendChild(tr)
    11. for(let j=0;j<100;j++){
    12. let dt=new Date().getTime()
    13. let td=document.createElement("td")
    14. td.innerHTML=dt
    15. tr.appendChild(td)
    16. }
    17. }
    18. 回档了1万零100
    19. 1658297335582-1658297335446 136ms
    1. 创建一个"冰"元素来承载即将渲染的元素
    2. dom=>fragment
    3. wx=>block
    4. vue=>template
    5. react=>
    6. let tb=document.querySelector("#box")
    7. let fra1=document.createDocumentFragment()//它在内存中还不在文档中
    8. for(let i=0;i<100;i++){
    9. let tr=document.createElement("tr")
    10. fra1.appendChild(tr)
    11. for(let j=0;j<100;j++){
    12. let dt=new Date().getTime()
    13. let td=document.createElement("td")
    14. td.innerHTML=dt
    15. tr.appendChild(td)
    16. }
    17. }
    18. tb.appendChild(fra1)//它自己不会添加到文档树中用于渲染 但是它的子代元素都会添加进去
    19. 回档了1
    20. 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 秒内高频事件再次被触 发,则重新计算时间。
    思路:每次触发事件时都取消之前的延时调用方法。
    /*
    fn: 事件处理函数
    delay :定时器延时
    rest :传参给事件处理函数
    */
    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.预加载与懒加载

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

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

  • 相关阅读:
    网络安全(黑客)——自学2024
    即时通讯Web端通信方式的演进
    IPTABLES问题:DNAT下如何解决内网访问内部服务器问题
    几个适合Java开发者的免费IDEA插件
    内燃机可变气门驱动研究进展
    变量常用函数
    Kotlin 协程二三事:入门
    二叉树 | 对称二叉树、相同的树、子树相同 | leecode刷题笔记
    什么是智慧校园?
    个人博客类网站为什么更适合虚拟主机?
  • 原文地址:https://blog.csdn.net/m0_63774574/article/details/125899999