提升对前端的认知,不得不了解Web API的DOM和BOM

了解Web API的DOM和BOM

  • 引言
  • 正文
  • 一、DOM操作
    • 1、DOM的本质
    • 2、DOM节点操作
      • (1)property形式
      • (2)attribute形式
    • 3、DOM结构操作
      • (1)新增/插入节点
      • (2)获取子元素列表,获取父元素
      • (3)删除子元素
    • 4、DOM性能
    • (1)对DOM查询做缓存
    • (2)将频繁操作改为一次性操作
    • 5、回顾
  • 二、BOM操作
    • 1、navigator
    • 2、screen
    • 3、location
    • 4、history
  • 结束语

引言

在现代的开发中,vue和react都是很流行的开发框架,框架虽好用,但是框架的原理还是基于 DOM 操作去实现。如果一个前端工程师只会框架,不会 DOM ,那基本上是很容易被淘汰的。因为框架的存活时间我们谁也说不准,且技术更新迭代也特别快,说不定三五年就会被淘汰了都有可能。所以,扎实的学会 js 的基础原理,不要被框架和一些外部事件所迷惑,对自己会有一个更好的竞争力提升。

本文将讲解 JS 中 Web API 的 DOM 和 BOM 操作。

正文

一、DOM操作

DOM操作,即Document Object Model文档对象模型。下面通过几个知识点来分析DOM的本质、节点操作、结构操作以及 DOM 的性能。

1、DOM的本质

DOM的本质就是一棵 树 ,是树结构。

我们从早起xml说起,xml是一种可扩展性的标准语言,早期基本是用xml来对DOM进行编写。具体形式如下:

<?xml version = "1.0" encoding = "UTF-8"?>
<note><to>Tony</to><from>Abby</from><heading>London</heading><body>Have a nice day!</body><other><a></a><b></b></other>
</note>

我们可以看到,一层一层的下来,层层递进,很像一棵树。


回到现在,我们基本上用的是 html 来进行编写。 html 也是一种特定的 xml ,只不过它是有自己的一套规范,比如说我们常见的 p 标签, span 标签, li 标签等等。引用百度来做一个解释:

DOM的本质

大家可以看到,上面一层一层递进的关系,把整个 html 渲染出来形成我们看到的页面,这一层层递进的关系,其实就是 DOM 树,所以也可以说, DOM 是从 html 文件解析出来的一棵树。

2、DOM节点操作

DOM节点主要有两种操作,一种是property操作,另一种是attribute操作。下面让我们来看看这两种操作。

(1)property形式

html代码:

<div id="div1" class="container"><p>文段 1</p><p>文段 2</p><p>文段 3</p>
</div>
<div id="div2"><img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
</div>

JS代码:

/*** property形式*/const pList = document.querySelectorAll('p');const p1 = pList[0];p1.style.width = '100px'; //修改样式console.log(p1.style.width); //获取样式p1.className = 'red'; //修改class名称console.log(p1.className); //获取class名称//  获取nodeName和nodeTypeconsole.log(p1.nodeName);  //打印节点名称,pconsole.log(p1.nodeType); //打印节点类型,普通的DOM节点元素为1,文本类型是3

控制台打印结果如下:

property

从上面的结果中可以看到,通过修改 DOM 的 JS 变量,从而操作 DOM ,最终得到我们想要的结果。

(2)attribute形式

html代码:

<div id="div1" class="container"><p>文段 1</p><p>文段 2</p><p>文段 3</p>
</div>
<div id="div2"><img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt="">
</div>

JS代码:

/*** attribute形式*/const pList = document.querySelectorAll('p');const p1 = pList[0];p1.setAttribute('data-name', 'immoc');console.log(p1.getAttribute('data-name'));p1.setAttribute('style', 'font-size:50px;');console.log(p1.getAttribute('style'));

控制台打印结果如下:

attribute

从上面的结果中可以看到,通过修改 DOM 结构的节点属性,最终得到我们想要的结果。

综上所述, porperty 和 attribute 这两种操作类型,主要有以下区别:

  • property:修改对象属性,不会体现到 html 结构中;
  • attribute:修改 html 属性,会改变 html 结构,即标签结构;
  • 两者都有可能引起DOM重新渲染,但 attribute 引起 DOM 重新渲染的可能性更大,因为它会改动 html 的结构。所以在实际开发中,可以选择的话尽量渲染 property 去操作 DOM 。

3、DOM结构操作

通过上面的了解,我们都明白了DOM是一种树结构。那既然是树结构,就应该可以对节点进行增删改的操作。

因此,DOM结构操作主要有以下三种类型:

  • 新增/插入节点;
  • 获取子元素列表,获取父元素;
  • 删除子元素。

接下来对这三种类型进行讲解。

(1)新增/插入节点

先附上html代码。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="div1" class="container"><p id = "p1">文段 1</p><p>文段 2</p><p>文段 3</p></div><div id="div2"> <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt=""></div><script src="./你的文件路径.js"></script>
</body>
</html>

此时执行状态如下。

新增子节点前

如果此时要给 div1 新增一个子节点 p ,代码如下。

// 新建节点
const newP = document.createElement('p');
newP.innerHTML = 'this is newP';
// 插入节点
div1.appendChild(newP);

此时浏览器执行状态如下。

新增子节点

大家可以看到, div1 成功新增了一个节点,这就是关于新增节点操作的具体流程。

那如果在此基础上,要移动一个节点呢?具体操作如下。

// 移动节点 -> 把p1移动到div2中来
const p1 = document.getElementById('p1');
div2.appendChild(p1);

此时浏览器执行状态如下。

移动节点

(2)获取子元素列表,获取父元素

在上面的基础上,我们来看看获取父元素和子元素列表的流程。

// 获取父元素
console.log(p1.parentNode);// 获取子元素列表
const div1ChildNodes = div1.childNodes;
console.log(div1ChildNodes); //此处获取的包含p标签以及p标签下面的文本,因此需要一层过滤
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child => {if(child.nodeType === 1){return true;}else{return false;}
});
console.log('div1ChildNodesP', div1ChildNodesP);

此时浏览器执行状态如下。

获取子元素和父元素

(3)删除子元素

在上面操作的基础上,我们现在对 div1 中的第一个 p 节点进行移除。

// 移除元素
div1.removeChild(div1ChildNodesP[0]);

此时浏览器执行状态如下。

移除节点

以上就是对DOM结构的新增、移动、获取子父元素以及删除子元素的一个操作,相信大家对DOM结构的增删改有了一个新的了解。接下来我们讲解DOM性能。

4、DOM性能

为什么要对DOM做性能优化呢,原因在于 DOM 操作是非常“昂贵”的,每一次操作都很有可能引发浏览器的重排和重绘,因此要避免频繁的 DOM 操作。那如何做到避免频繁的 DOM 操作,给 DOM 进行性能优化呢?主要有以下两个方面给 DOM 操作进行性能优化:

  • 对 DOM 查询做缓存
  • 将频繁操作改为一次性操作

接下来将对这两种方法进行讲解。

(1)对DOM查询做缓存

// 不缓存 DOM 查询结果
for(let i = 0; i < document.getElementsByTagName('p').length; i++){// 每次循环,都会计算length,频繁进行 DOM 查询
}
// 缓存 DOM 查询结果
const pList = document.getElementsByTagName('p');
const length = pList.length;
for(let i = 0; i < length; i++){// 缓存length,只进行一次 DOM 查询
}

分析以上两段代码,在第一段当中,每次循环的时候,都会计算 length ,频繁的对 DOM 进行查询,如此频繁的操作,可想对程序都不是不太好的。

因此,通过优化,我们写出第二段代码。在第二段代码中,把 length 放在外部进行缓存,等到每次循环的时候,只需要进行一次 DOM 查询,而不用像第一段一样频繁操作,极大提高了性能。

(2)将频繁操作改为一次性操作

我们来看一个例子。比如说,我们先在要通过操作 DOM 来一次性插入10个列表。

在正常情况下我们想象的操作如下:

html代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="div1" class="container"><p id = "p1">文段 1</p><p>文段 2</p><p>文段 3</p></div><div id="div2"> <img src="https://dss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=2144980717,2336175712&fm=58" alt=""></div><ul id="list"></ul><script src="./你的路径.js"></script>
</body>
</html>

js代码

const list = document.getElementById('list');for(let i = 0; i < 10; i++){const li = document.createElement('li');li.innerHTML = `List item ${i}`;list.appendChild(li);
}

此时浏览器执行状态如下。

DOM性能优化1

按照预想的,呈现出了我们想要的结果,且看着好像也没什么问题。但是呢,这就违反了我们所说的一次性操作 DOM 的原则,因为它在频繁的操作 DOM ,一直在频繁操作中修改。因此,我们可以通过以下方法来对性能做一个优化:

/*** 频繁操作改为一次性操作*/
const list = document.getElementById('list');// 创建一个文档片段,此时还没有插入到 DOM 结构中
const frag = document.createDocumentFragment();for(let i = 0; i < 15; i++){const li = document.createElement('li');li.innerHTML = `List item ${i}`;//先插入文档片段frag.appendChild(li);
}// 都完成之后,再统一插入到 DOM 结构中
list.appendChild(frag);

执行后,浏览器也同样效果呈现出来了。

DOM性能优化2

通过代码我们可以发现,通过创建一个文档片段,来对节点进行一个缓存,等到全部节点操作都完成以后,再统一插入到 DOM 结构中。这种方法,也极大提高了程序中的性能。

5、回顾

最后,我们用几个题目来回顾DOM的知识点。

(1)DOM是哪一种数据结构?

DOM是一种树的数据结构,DOM也常被称为DOM树。

(2)DOM操作的常用API

DOM操作的常用API

(3)attribute和property的区别

两者主要有以下区别:

  • property:修改对象属性,不会体现到 html 结构中;
  • attribute:修改 html 属性,会改变 html 结构,即标签结构;
  • 两者都有可能引起DOM重新渲染,但 attribute 引起 DOM 重新渲染的可能性更大,因为它会改动 html 的结构。所以在实际开发中,可以选择的话尽量渲染 property 去操作 DOM 。

(4)一次性插入多个DOM节点,需考虑性能,怎么操作?

可以通过创建一个Fragment的文档片段,来对节点进行一个缓存,等到 全部节点操作 都完成以后,再统一插入到 DOM 结构中,从频繁执行改为一次执行。

二、BOM操作

BOM,即Brouse Object Model浏览器对象模型。下面通过几个知识点来了解浏览器的BOM操作。

1、navigator

navigator 主要用到 userAgent 属性, navigator.userAgent 表示获取浏览器的用户代理字符串。如以下代码操作:

//navigator
const ua = navigator.userAgent;
console.log(ua);
const isChrome = ua.indexOf('Chrome');
console.log(isChrome);

此时浏览器打印如下。

浏览器内核信息

从上图中可以看到,通过 userAgent 可以获取到当前所使用浏览器的内核信息。

2、screen

screen 主要用到width和height属性,screen.width表示获取当前屏幕的宽度,sceen.height表示获取当前屏幕的高度。如以下代码操作:

// screen
console.log(screen.width); //获取屏幕宽度
console.log(screen.height); //获取屏幕高度

此时浏览器打印如下。

screen

从上图中可以看到,通过 screen.width 和 screen.height 可以获取到当前屏幕的宽度和高度。

3、location

location 主要用到 href/protocol/pathname/search/hash 属性,具体含义如下代码所示。

// location
console.log(location.href); //获取整个网址
console.log(location.protocol); //获取网址协议
console.log(location.pathname); //获取网址域名
console.log(location.search); //获取网址中的一些参数
console.log(location.hash); //获取哈希值,即#号后面的值

此时浏览器打印如下。

location

相信从上图的演示之后,大家对 location 属性的应用有了有一定的了解。

4、history

history 主要用到 back 和 forward 属性,当网页执行history.back()代码时,会将当前网页向后退一页;当网页执行history.forward()代码时,会将当前网页向前进一页。如以下代码操作:

// history
history.back(); //对网页进行后退
history.forward(); //对网页进行前进

此时浏览器打印如下。

history

从上图中可以看到,通过 history.back() 和 history.forward() 可以让当前浏览页面进行前进或者后退操作。

结束语

JS 的基础知识规定了 ECMA 的语法标准,而 Web API 则是网页操作的 API ,是 W3C 的标准。如果要说两者的关系,那自然是 ES 标准是 Web API 的基础。在实际应用开发中,只有两者结合才能真正做到实际应用。所以,不管是 ES 标准还是 Web API 中的 DOM 和 BOM 操作,在实际开发中都是至关重要的内容。

关于 DOM 和 BOM 的操作就讲到这里啦!如有疑问欢迎评论区评论或私信我交流~

  • 关注公众号 星期一研究室 ,不定期分享学习干货

  • 如果这篇文章对你有用,记得点个赞加个关注哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/308034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dapr微服务应用开发系列1:环境配置

题记&#xff1a;上篇Dapr系列文章简要介绍了Dapr&#xff0c;这篇来谈一下开发和运行环境配置本机开发环境配置安装Docker为了方便进行Dapr开发&#xff0c;最好&#xff08;其实不一定必须&#xff09;首先在本机&#xff08;开发机器&#xff09;上安装Docker。安装方式可以…

leetcode704二分法:(左闭右闭+左闭右开)

前言 又重温了一遍<肖生客的救赎> 其中安迪的一句话一直回荡我的脑中&#xff1a;“人生可以归结为一种简单的选择&#xff1a;不是忙着活&#xff0c;就是忙着死。” 多深刻&#xff0c;多简单&#xff0c;又多令人深省&#xff0c; 哪有那么多选择 哪有那么多时间去花…

你真的理解事件绑定、事件冒泡和事件委托吗?

一文了解Web API中的事件绑定、事件冒泡、事件委托引言正文一、事件绑定1、事件和事件绑定时什么&#xff1f;2、事件是如何实现的&#xff1f;二、事件冒泡1、事件模型2、事件模型解析&#xff08;1&#xff09;捕获阶段&#xff08;2&#xff09;目标阶段&#xff08;3&#…

欢迎来到 C# 9.0(Welcome to C# 9.0)

翻译自 Mads Torgersen 2020年5月20日的博文《Welcome to C# 9.0》&#xff0c;Mads Torgersen 是微软 C# 语言的首席设计师&#xff0c;也是微软 .NET 团队的项目群经理。C# 9.0 正在成形&#xff0c;我想和大家分享一下我们对下一版本语言中添加的一些主要特性的想法。对于 C…

367. 有效的完全平方数(二分法)

一&#xff1a;题目 二:思路 完全平方数:若一个数能表示成某个整数的平方的形式&#xff0c;则称这个数为完全平方数 思路:1.我们将num先折半,因为它是某个整数的平方&#xff0c;而这个数的范围肯定不会超过num的一半 2.那么这就相当于在[left,num/2]中查找某个数&#xff0c…

跨越跨域大山,前端不得不知道的Ajax

AJAX和跨域引言正文一、AJAX请求1、模拟get和post请求2、封装一个简易的AJAX二、状态码1、xhr.readyState2、xhr.status三、跨域1、同源策略&#xff08;1&#xff09;同源策略是什么&#xff08;2&#xff09;为什么浏览器会有同源策略&#xff1f;&#xff08;3&#xff09;同…

译 | Azure 应用服务中的程序崩溃监控

点击上方蓝字关注“汪宇杰博客”原文&#xff1a;Yun Jung Choi, Puneet Gupta翻译&#xff1a;汪宇杰应用程序崩溃经常发生。崩溃是指代码中的异常未得到处理并终止进程。这些未处理的异常也称为二次机会异常&#xff08;second chance exceptions&#xff09;。当您的应用程序…

解决浏览器存储问题,不得不了解的cookie,localStorage和sessionStorage

浏览器存储方式一、浏览器存储的方式二、cookie、localStorage和sessionStorage1、cookie、localStorage和sessionStorage是什么&#xff1f;&#xff08;1&#xff09;cookie&#xff08;2&#xff09;localStorage&#xff08;3&#xff09;sessionStorage2、cookie、localSt…

使用Seq搭建免费的日志服务

Seq简介Seq是老外开发的一个针对.NET平台非常友好的日志服务。支持容器部署&#xff0c;提供一个单用户免费的开发版本。官网&#xff1a;https://datalust.co/seq使用文档&#xff1a;https://docs.datalust.co/docsSeq主体功能如下所示&#xff1a;支持主流的编程语言&#x…

leetcode27:移除元素(暴力+双指针)

一&#xff1a;题目 二&#xff1a;暴力双指针 1&#xff1a;暴力解法 (1):思路 1.在数组当中 我们想要删除一个元素 得靠覆盖也就是后面的元素往前覆盖其想要删除的元素 但是注意的是我们真实的数组中的元素个数是不变的 因为我们只是将后面的元素移到起前面 并未真正的删除…

三分钟Docker-推送本地镜像到仓库

在上篇文章中&#xff0c;我们完成了应用程序容器化&#xff0c;把webapi项目构建镜像并容器化运行。本文将会演示如何把自己构建的镜像上传到docker官网的仓库和自己私有仓库本地镜像推送到官网的registry1.创建仓库点击Docker Desktop图标->Repositories-》create 跳转到…

你知道304吗?图解强缓存和协商缓存

http协议—常见状态码&#xff0c;请求方法&#xff0c;http头部&#xff0c;http缓存一、http状态码1、引例阐述2、状态码分类3、常见状态码4、关于协议和规范二、http 方法1、传统的methods2、现在的methods3、Restful API&#xff08;1&#xff09;Restful API是什么&#x…

leetcode844. 比较含退格的字符串(栈+双指针)

一:题目 二:思路代码 1:利用栈 (1):思路 1.利用栈 我们将字符串中的单个元素都入栈 当遇到’#的时候将将栈顶元素弹出 (2):上码&#xff08;方法一&#xff09; class Solution { public:/**思路:1.利用栈 我们将字符串中的单个元素都入栈 当遇到#的时候将将栈顶元素弹出*…

efcore技巧贴-也许有你不知道的使用技巧

前言.net 环境近些年也算是稳步发展。在开发的过程中&#xff0c;与数据库打交道是必不可少的。早期的开发者都是DbHelper一撸到底&#xff0c;到现在的各种各样的ORM框架大行其道。孰优孰劣谁也说不清楚&#xff0c;文无第一武无第二说的就是这个理。没有什么最好的&#xff0…

关于前端性能优化问题,认识网页加载过程和防抖节流

前端性能优化—网页加载过程、性能优化方法、防抖和节流一、网页加载过程1、加载资源的形式2、加载资源的过程3、渲染页面的过程4、关于window.onload 和 DOMContentLoaded二、性能优化1、性能优化原则2、性能优化的方法3、让加载更快4、让渲染更快三、防抖和节流1、防抖 debou…

javax.net.ssl.SSLHandshakeException: No appropriate protocol

一:报错 二:解决 找到jdk 1.8安装目录&#xff0c;找到C:\Program Files\Java\jre里面的lib\security 下面有个java.security将jdk.tls.disabledAlgorithms后面的SSLv3, TLSv1, TLSv1.1都删除掉.&#xff08;大概位置是在700多行&#xff09; 三:上方并未解决的 我的jdk是这…

『软件工程9』结构化系统分析——解决软件“做什么”问题

结构化系统分析——解决软件“做什么”问题一、系统分析的任务和过程1、系统分析的任务2、系统分析的过程&#xff08;1&#xff09;问题识别&#xff08;2&#xff09;分析与综合&#xff08;3&#xff09;编制文档&#xff08;4&#xff09;系统分析评审二、结构化分析方法1、…

.NET5.0 Preview 8 开箱教程

.NET5.0 Preview 8 开箱教程前言首先&#xff0c;看到 .NET5.0 Preview 8 发布后&#xff0c;作为一枚基层应用开发人员&#xff0c;很想要体验一下新版本的魅力&#xff1b;这可能就是程序员对新技术的一种执着吧。其实从官方宣布 .NETCore 将更名为 .NET5 开始&#xff0c;我…

leetcode977. 有序数组的平方(暴力+双指针)

一:题目 二:暴力双指针 1:暴力 class Solution { public:vector<int> sortedSquares(vector<int>& nums) {vector<int> v;for(int num : nums){int temp pow(num,2);v.push_back(temp);} sort(v.begin(),v.end());return v;} };2:双指针 思路:1.利…

『软件工程10』结构化系统分析:数据流图和字典案例分析

结构化系统分析——数据流图和数据字典案例分析一、数据流图案例分析1、案例1&#xff1a;商店业务管理系统2、案例2&#xff1a;学籍管理系统3、案例3&#xff1a;大型企业数据中心二、数据字典案例分析1、案例1&#xff1a;学籍管理系统三、写在最后接 上一篇文章的内容&…