DOM——页面的渲染、style属性操作、预加载与懒加载、防抖与节流

2023-10-26

一、页面的渲染

(一)浏览器加载一份HTML文档的加载过程

  1、把标签、文本、注释、属性等html代码解析为节点树(DOM Tree)
  2、把所有样式(css代码和浏览器自带)解析为结构体
  3、把css样式结构体和节点树结合变成呈现树/渲染树(Render Tree)
  4、根据渲染树Render Tree绘制页面

 

(二)重绘与回流

 1)回流:

  • 当render tree中因为元素的数量、布局、隐藏等改变而需要重新构建的称为回流或者回档 
  • 每个页面至少需要一次回流即在页面第一次加载的时候

  2)重绘:

  • 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观、风格、样式等而不会影响布局的则称为重绘

  3)关系:

  • 任何对render tree中元素的操作都会引起回流或重绘
  • 回流必将引起重绘 重绘不一定引起回流

  4)常见的回流和重绘操作:

  1. 添加或删除元素(回流+重绘)
  2. 隐藏元素:如display:none(回流+重绘)    visibility:hidden(只重绘,不回流)
  3. 移动元素:如改变top,left(jquery的animate方法就是,改变top,left不一定会影响回流),或者移动元素到另外1个父元素中(重绘+回流)
  4. 对style的操作(对不同的属性操作,影响不一样)
  5. 用户的操作:如改变浏览器或浏览器字体的大小等(回流+重绘)

     示例:

 <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: red;
            /* visibility: hidden; */
            /* display: none; */
        }
    </style>
    <div class="box">hello</div>
    <button onclick="fn()">change</button>
    <script>
        function fn() {
            var box = document.querySelector(".box")
            box.innerHTML = "6666" //改变了文档树的结果回流
            box.style.visibility = "hidden"; //元素只是隐藏 位置保留 =>重绘不回流
            box.style.display = "none"; //元素会消失 文档树中不会保留它的位置 =>回流
        }
    </script>

  5)影响:

  • 频繁的重绘/回流会使 计算机消耗过大 导致页面的性能和用户体验不好 

  6)解决办法:

  • 尽量避免重绘
  • 创建一个fragment   

fragment:

将内容添加到fragment里面 它自身不会添加到文档树中用于渲染  将里面的内容添加到文档树后它就会消失 即要添加的子元素--->添加进fragment--->将fragment添加到目标父元素中--->子元素进父元素,fragment消失(仅重绘、回流一次) 

在dom中叫=> fragment  在微信小程序中=> block   在vue中=> template    在react中=> </>

   案例:添加1万个格子到页面上 每个格子显示时间(ms)

<style>
#box td {
      border: 1px gainsboro solid;
 }
</style>
<table id="box">
</table>
<script>
 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)      //多次添加元素到文档树中 导致频繁的回流和重绘
        	}
        }
      //多次回流重绘
</script>

   优化:

 <style>
        #box td {
            border: 1px gainsboro solid;
        }
</style>
<table id="box"></table>
<script>
     //创建一个fragment元素来承载即将渲染的元素
        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次
</script>

二、style的操作

(一)获取元素的问题

   引例:

<style>
    .box {
            width: 400px;
            height: 300px;
            background-color: aqua;
            cursor: pointer;
        }
</style>
<div class="box" style="color: red;">这是div1</div>
<script>
var body1 = document.body
var box2 = document.querySelector(".box2")
var color1 = document.querySelector(".box").style.color
var width1 = document.querySelector(".box").style.width
console.log(body1)   //body
console.log(box2)    //null 节点还没有加载完 访问不了后面的
console.log(color1)  //red
console.log(width1)  //    空的什么都没有
</script>
<div class="box2">这是div2</div>

 打印结果:

 

思考:为什么div2是null?

 原因:

  • 获取不了script脚本节点后面加载的元素节点,因为文档的加载是按照文档树的顺序加载的

 解决方案: 

  • 方案一:当页面加载完成的事件触发 再去执行获取节点的方式
    function fn() {
       var box2 = document.querySelector(".box2")
       console.log(box2)
    }
    window.onload = fn  
//window.onload会在页面加载完后执行 即fn也在页面加载完后执行 就可以在原位置访问到div2
  • 方案二:通过引入script标签的defer、async(修饰src的加载外部js资源的)异步属性

                      将代码写到外部js文件中

//index.js 文件中:
<script>
var box2 = document.querySelector(".box2")
console.log(box2)
</script>
//html文件中:
<script defer src="index.js"></script>
<div class="box2">这是div2</div>

async 和 defer
脚本的异步加载
1.<script src="script.js"></script>
没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

2.<script async src="script.js"></script>
有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

3.<script defer src="myscript.js"></script>
有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

从实用角度来说:首先把所有脚本都丢到 </body> 之前是最佳实践,因为对于旧浏览器来说这是唯一的优化选择,此法可保证非脚本的其他一切元素能够以最快的速度得到加载和解析。
 

 

(二)style属性问题

   1.行内样式 :el.style.xx    ele.currentStyle (IE的低版本)

  • 只能操作行内样式 没有兼容问题  
  • 只能设置为字符串  样式必须用驼峰命名法 
  • 遇到与保留字一样的样式,前面应加“css”(eg:float—>el.style.cssFloat)
  • 其实是获取的文档树中的元素的属性值

  2.最终绘制样式:window.getComputedStyle(el)

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

  3.拓展:window.getComputedStyle(ele,"after")

  • 第二个参数解决的是获取伪元素样式

三、防抖与节流

1) 防抖:

  •   触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
  •  思路:每次触发事件时都取消之前的延时调用方法

  案例:

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)
    	}
    }

 优化:

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)
        }
    }


2)节流:

  • 高频事件触发,但在n秒内只执行一次,所以节流会稀释函数的执行频率
  • 思路:每次触发事件时都判断当前是否有等待执行的延时函数

案例:

document.onmousemove=jieliu(function(){console.log(666)},20)
    function jieliu(cb,daley){
    	var timer=null;
    	return function(){
    		if(!timer){
    		  timer=setTimeout(()=>{
    			  cb();						  
    			  timer=null
    		  },daley)						
    		}
    	}
    }

优化:

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但是希望是cb					  
                  timer=null
              },daley)						
            }
        }
    }

四、预加载与懒加载

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

  案例:

  • 相同的图片加载一次后就保存在浏览器的本地中,不用多次加载
<!-- 同时加载三张相同的图片-->
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">		
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">		
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">

  效果: 

2)懒加载: 先不加载 等待某个条件成立时再加载资源

  案例

  • 当条件成立才加载页面之下的图片 保证在滑到图片位置之时就已经加载好了 能看到图片
<p>hello</p><p>hello</p><p>hello</p><p>hello</p><p>hello</p>
<p>hello</p><p>hello</p><p>hello</p><p>hello</p><p>hello</p>
<!--此处省略很多个p标签-->
<p>hello</p><p>hello</p><p>hello</p><p>hello</p><p>hello</p>
<p>hello</p><p>hello</p><p>hello</p><p>hello</p><p>hello</p>
<script>
window.onload=function(){
  document.onscroll=function(e){
	let top=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop
	let h=cHeight =window.innerHeight||document.body.clientHeight;
	console.log(top,img2.offsetTop-h-100)
	if(top>=(img2.offsetTop-h-100)){
			img2.src=img2.dataset.src1  //条件成立加载图片
	}
  }
}			
</script>
<img  id="img2" data-src1="https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=f99225a791b634226dcd5ee47c8b5f3f">

  效果:

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DOM——页面的渲染、style属性操作、预加载与懒加载、防抖与节流 的相关文章

  • 使用 Node.js 构建网站的最佳实践

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我想知道如何使用 Node js 从头开始 开发一个网站 我明白我怎么能possibly
  • 为什么 window 与 Internet Explorer 中的 window.self 不同?

    关于我如何遇到这个问题有一个复杂的背景故事 但为什么self属性不完全等于窗口本身 在 Safari 和 Firefox 及其朋友中 结果如我所料 gt window window self true gt window window se
  • 如何在React Native Android中获取响应头?

    您好 我想在获取 POST 请求后获取响应标头 我尝试调试看看里面有什么response with console log response 我可以从以下位置获取响应机构responseData但我不知道如何获取标题 我想同时获得标题和正文
  • 如何在网站上使用 svg 元素制作块的屏幕截图?

    我在网站上创建了一个构造函数 其本质是将所选元素及其颜色 svg中的元素 添加到访问者选择的背景和背景颜色 png中的背景 中 然后必须单击 保存 结果 按钮并仅执行工作区的屏幕截图 我写了这个脚本 但它需要屏幕截图 但只有背景 并忽略选定
  • IntersectionObserver是否支持水平滚动观察?

    我制作了几个垂直滚动 IntersectionObserver 模块 但我对水平滚动感兴趣 根将是 div 观察目标将是 img 我想观察当 img 放大但 div 保持视口宽度时的变化 我什至不确定移动 Safari 是否会将缩放后的图片
  • 按下回车键时不刷新页面

    我遇到了一些问题 只要表单中有输入 回车键就会触发页面刷新 下面的代码 如果按下回车并且文本区域 input 中没有输入任何文本 则不会刷新页面 但是如果按下回车并且 input中有输入或者光标位于文本区域 我不确定是什么触发了它 因为 s
  • 删除 IE9 边缘周围的 2px 灰色边框

    我正在尝试对这个网站进行编码 尝试关键字 并且我正在尝试找出如何删除这个阴影2px灰色边框延伸到 IE9 窗口的内部 至少顶部 左侧和底部 我的边距设置为零 因此所有页面元素都到达页面的最边缘 但使用 IE9 它们会停在这个灰色边框处 我没
  • 如何使用 Greasemonkey 监视静态 HTML 页面的更改?使用哈希?

    我希望我的 Greasemonkey 脚本仅在其访问的静态页面具有与以前完全相同的内容时运行 现在我可以设置一个包含该页面哈希的变量 我正在寻找一种动态散列页面的方法 以便我可以将我的散列与生成的散列进行比较 关于如何即时实现散列的任何想法
  • javascript 选择自定义光标 (svg)

    我正在动态地将光标更改为悬停时的本地 svg element on mouseover function this css cursor url svgs pointer svg 9 30 auto 工作正常 但我想选择该 svg 来操纵其
  • React autoFocus 将光标设置为输入值的开头

    我有一个受控输入 最初显示一个值 我已将该输入设置为自动聚焦 但当我希望它出现在末尾时 光标出现在输入的开头 我知道这可能是因为自动对焦是在值之前添加的 但我不能 100 确定 在输入字段末尾完成光标初始化的最佳方法是什么 var Test
  • 如何始终将焦点保持在文本框中

    我创建了一个包含两个 div 的 HTML 页面 左侧的 div 页面的 90 是 ajax 结果的目标 右侧的 div 页面的 10 包含一个文本框 该页面的想法是在文本框中输入零件编号 通过条形码扫描仪 并显示与该零件编号匹配的绘图 显
  • 如何停止TinyMCE删除span标签?

    在我的工作中 前一位程序员决定使用公司网站上精彩的TinyMCE 我遇到的数千个问题之一是 如果原文有的话span标签 当我按下退格键删除一行 p仅标签 全部span标签已从文本中删除 这个错误比另一个错误更具体 我可以删除anything
  • 具有 100% 高度行和 Internet Explorer 9 的表格

    我有以下示例 div style height 150px background color AAAAFF div
  • 用于选择特定 div 中具有特定类的锚元素的 jQuery 选择器是什么

    我有一些这样的代码 我想选择每个 a 带有类的标签status在 div 中foo div a class status a div 你可以这样做 foo find status a
  • Rails 3.1+ 的 Jasmine 与 Mocha JavaScript 测试 [已关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我对茉莉花有经验并且非常喜欢它 有谁有 Jasmine 和 Mocha 的经验 特别是 Rails 的经验吗 我想知道是否值得转用 我已经在 J
  • 从 PHP 数组生成 HTML 表

    我不明白这一点 我需要解决看似简单的问题 但这超出了我的逻辑 我需要编写一个函数 table columns input cols 它将输出一个表 示例 input array apple orange monkey potato chee
  • 在 iOS 7 Safari 中,如何区分通过边缘滑动与后退/前进按钮的 popstate 事件?

    在 iOS 7 Safari 中 现在有两种后退 前进导航方式 使用底部的传统后退 前进按钮箭头或从屏幕边缘滑动 我正在使用动画在 ajax 应用程序中的页面之间进行转换 但如果用户通过边缘滑动进行导航 我不想触发该转换 因为这本身就是一个
  • 滚动顶部不符合预期

    Note 由于上次忘记奖励而重新开放赏金 A Woff 大师已经给出答案 我想在用户展开某一行时到达该行 这样当最后一个可见行展开时 用户不必向下滚动即可查看内容 I used example tbody on click td green
  • Flot 库将 y 轴设置为最小值 0 和最大值 24

    如何将 y 轴设置在 0 到 24 的范围内 这是我的代码 j plot j placeholder d1 xaxis mode time min new Date 2010 11 01 getTime max new Date 2011
  • 将数组从 jquery ajax 传递到代码后面

    我必须将二维数组传递给在asp net网页代码后面编写的页面方法我有一个变量objList作为二维数组 我使用以下代码来实现此目的 但没有成功 并且未调用页面方法 脚本语言 function BindTable objList ajax u

随机推荐

  • 数据库应用:Mycat+HAProxy+Keepalived实现高可用

    目录 一 理论 1 高可用 2 部署高可用 二 实验 1 Mycat HAProxy Keepalived实现高可用 三 问题 1 开启HA Proxy失败 2 VMware 克隆linux 网卡UUID重复问题 3 keepalived状
  • 从零开始搭建Kafka集群遇到的问题

    文章目录 安装kafka 下载kafka 2 12 3 1 0 tgz 使用Xshell将文件传入虚拟机 解压 启动kafka 使用kakfa自带的zookeeper 修改kafka配置 启动kafka 使用kafka 创建主题 查看主题
  • 考试系统服务器考试机,网络考试系统——服务器考试管理系统

    内容介绍 原文档由会员 快乐浪子 发布 网络考试系统 服务器考试管理系统 内容丰富 建议下载阅览 页数 32 字数 12157 摘要 在网络技术逐渐渗入社会生活各个层面的今天 传统的考试方式也面临着变革 而网络考试则是一个很重要的方向 基于
  • 对称加密算法之RC4介绍及OpenSSL中RC4常用函数使用举例

    RC4是一种对称密码算法 它属于对称密码算法中的序列密码 streamcipher 也称为流密码 它是可变密钥长度 面向字节操作的流密码 RC4是流密码streamcipher中的一种 为序列密码 RC4加密算法是Ron Rivest在19
  • 开发要点-Vue3.0通用知识学习

    开发要点 Vue3 0通用知识学习 2020 09 18 发布的Vue3 0正式版 建议先学ts 因为Vue3 0框架多数都是用ts重写的 API文档仔细阅读 Vue3亮点 Performance 性能更快 Tree shaking sup
  • linux下查看磁盘分区,文件系统,磁盘文件系统的命令

    http www linuxsir org bbs thread214738 html 一 df 命令 df 是来自于coreutils 软件包 系统安装时 就自带的 我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置 举例 S
  • 国内网络安全公司、社区简介

    国内网络安全公司简介 1 http www symantec com region cn 赛门铁克中国一家从事网络安全服务的公司 挺供网络安全产品下载 整体网络安全解决方案 2 http www nsfocus com 绿盟科技有限公司 一
  • 解决数据修改,但是视图未更新

    例一 在uniapp或者vue中 有时候会遇到数据修改 但实际上视图的数据未更新的情况 例如 直接赋值就不会改变视图 用this set 后 就可以更新视图 但是当this list time有设置默认值时 不管直接是直接赋值还是用set
  • (Z)复杂繁琐的芯片设计流程

    芯片制造的过程就如同用乐高盖房子一样 先有晶圆作为地基 再层层往上叠的芯片制造流程后 就可产出必要的 IC 芯片 这些会在后面介绍 然而 没有设计图 拥有再强制造能力都没有用 因此 建筑师的角色相当重要 但是IC 设计中的建筑师究竟是谁呢
  • uniapp连接蓝牙相关问题

    设备蓝牙连接成功 获取不到设备蓝牙服务列表 这种情况一般会接收到uniapp的错误码 10004 我们可以前往uniapp官网API模块查看错误码信息 查看后可以得知错误码10004是没有找到指定服务 我们可以详细看下获取所有服务的api
  • 单例模式实现

    单例模式 一个类在任何情况下都绝对只有一个实例 并提供一个全局访问点 单例模式是创建型模式 单例模式的应用场景 某类只要求生成一个对象的时候 如一个国家主席 班级班长等 单例模式的好处 由于单例模式只允许创建一个对象 共享该对象可以节省内存
  • 关于高通AR摄像机聚焦和调用前后摄像头的研究

    我们在做高通AR的时候会有一些设置的图片无法识别的情况 怎么解决呢 Vuforia有个Target Manager 这里主要负责处理待识别的Target 在最新的4 0版本中 可识别的标志包括以下四种类型 一般使用较多的是Single Im
  • k8s 读书笔记 - 深入掌握 Pod 扩缩容

    Pod 扩缩容的应用场景 在实际生产环境中通常会遇到某个服务需要扩缩容的场景 某个 Service 服务资源紧张 需要对其进行扩容 比如服务请求的负载突然增加 原本一个 Pod 副本开始吃不消 此时需要多扩展几个 Pod 副本来分担突发的负
  • dnsmasq搭建dns

    dnsmasq服务端 安装 yum y install dnsmasq 改配置文件 root yum nfs egrep v etc dnsmasq conf resolv file etc resolv dnsmasq conf stri
  • 示例:统计IP地址对应的省份,并把结果存入到mysql

    ip txt access log import java io BufferedReader FileInputStream InputStreamReader import java sql Connection DriverManag
  • vscode利用ssh远程连接linux虚拟机

    1 vscode是利用ssh远程连接linux的 所以首先确保vscode已经安装了这两个插件 2 点击左下角的连接 3 选择Connect to Host 4 选择添加新的主机 5 按格式输入 ssh 主机名 ip 比如我的 ssh du
  • python list列表操作进阶

    1 list基础介绍 在Python中 list是一种非常重要并且使用也非常广泛的数据类型 可以存储任意数量的有序元素 list可以包含不同数据类型的元素 如整数 浮点数 字符串等 并且可以随时添加或删除其中的元素 使得它在处理动态变化数据
  • Windows小技巧12--永久关闭开机弹出的今日热点

    Windows小技巧12 永久关闭开机弹出的今日热点 1 异常概述 2 解决方法 3 说明 1 异常概述 笔者最近打开Windows 发现总会自动弹出一个未知的今日热点 如下图 即便设置了永久不再弹出 下次开机的时候也会弹出 并且会随机发各
  • windows 安装Python2.7

    Win7 Python2 7 安装教程 Win7 Python2 7 安装过程如下 1 到 官方网站 下载安装文件包Python 2 7 10 amd64 msi 根据自己的电脑配置选择相应的安装包 点击安装即可 默认安装在C Python
  • DOM——页面的渲染、style属性操作、预加载与懒加载、防抖与节流

    一 页面的渲染 一 浏览器加载一份HTML文档的加载过程 1 把标签 文本 注释 属性等html代码解析为节点树 DOM Tree 2 把所有样式 css代码和浏览器自带 解析为结构体 3 把css样式结构体和节点树结合变成呈现树 渲染树