DOM 和 BOM

139 阅读7分钟

DOM 指的是文档对象模型,它指的是把文档当做一个对象,这个对象主要定义了处理网页内容的方法和接口。

BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象。

思考:

console.log(window.window)//输出什么?

常见的DOM操作有哪些?

DOM 节点的获取

getElementById // 按照 id 查询
getElementsByTagName // 按照标签名查询
getElementsByClassName // 按照类名查询
querySelectorAll // 按照 css 选择器查询

// 按照 id 查询
var imooc = document.getElementById('imooc') // 查询到 id 为 imooc 的元素
// 按照标签名查询
var pList = document.getElementsByTagName('p')  // 查询到标签为 p 的集合
console.log(divList.length)
console.log(divList[0])
// 按照类名查询
var moocList = document.getElementsByClassName('mooc') // 查询到类名为 mooc 的集合
// 按照 css 选择器查询
var pList = document.querySelectorAll('.mooc') // 查询到类名为 mooc 的集合

DOM 节点的创建并把它添加到指定节点的后面。 已知的 HTML 结构如下:

<html>
  <head>
    <title>DEMO</title>
  </head>
  <body>
    <div id="container"> 
      <h1 id="title">我是标题</h1>
    </div>   
  </body>
</html>

要求添加一个有内容的 span 节点到 id 为 title 的节点后面,做法就是:

// 首先获取父节点
var container = document.getElementById('container')
// 创建新节点
var targetSpan = document.createElement('span')
// 设置 span 节点的内容
targetSpan.innerHTML = 'hello world'
// 把新创建的元素塞进父节点里去
container.appendChild(targetSpan)

DOM 节点删除已知的 HTML 结构如下:

<html>
  <head>
    <title>DEMO</title>
  </head>
  <body>
    <div id="container"> 
      <h1 id="title">我是标题</h1>
    </div>   
  </body>
</html>

需要删除 id 为 title 的元素,做法是:

// 获取目标元素的父元素
var container = document.getElementById('container')
// 获取目标元素
var targetNode = document.getElementById('title')
// 删除目标元素
container.removeChild(targetNode)

或者通过子节点数组来完成删除:

// 获取目标元素的父元素var container = document.getElementById('container')// 获取目标元素var targetNode = container.childNodes[1]// 删除目标元素container.removeChild(targetNode)

修改 DOM 元素这个动作可以分很多维度,比如说移动 DOM 元素的位置,修改 DOM 元素的属性等。

将指定的两个 DOM 元素交换位置, 已知的 HTML 结构如下:

<html>
  <head>
    <title>DEMO</title>
  </head>
  <body>
    <div id="container"> 
      <h1 id="title">我是标题</h1>
      <p id="content">我是内容</p>
    </div>   
  </body>
</html>

现在需要调换 title 和 content 的位置,可以考虑 insertBefore 或者 appendChild:

// 获取父元素
var container = document.getElementById('container')   
 
// 获取两个需要被交换的元素
var title = document.getElementById('title')
var content = document.getElementById('content')
// 交换两个元素,把 content 置于 title 前面
container.insertBefore(content, title)

常见的BOM操作有哪些?

screenLeft 返回浏览器窗口左上角相当于当前屏幕左上角的水平距离。

 screenTop 返回浏览器窗口左上角相当于当前屏幕左上角的垂直距离。

不兼容FF浏览器。

 screenX 功能同上。

screenY功能同上。

 这两种功能兼容FF 。

innerWidth 返回网页在当前窗口中可见部分的宽度,包含滚动条。

innerHeight 返回网页在当前窗口中可见部分的高度,包含滚动条。

 outerWidth 返回浏览器窗口宽度,包含浏览器菜单和边框 。

 outerHeight 返回浏览器窗口高度,包含浏览器菜单和边框。

window.open():打开一个新的浏览器窗口,接收4个参数(URL/打开方式/窗口参数/布尔值)。

 window.close():关闭新打开的窗口(仅限open()打开的窗口)。

screen功能:包含设备的信息 个别属性举例。

screen.height、screen.width返回设备的分辨率。

 location对象 功能:保存当前文档信息、将URL解析为独立片段 属性:href 返回当前页面完整的URL 修改这个属性,即可跳转新页面 hash:返回URL中的hash host:返回服务器名称和端口号 port:返回服务器端口号 pathname:返回URL中的目录和文件名 hostname:返回不带端口号的服务器名称 protocol:返回页面使用的协议 search:返回URL的查询字符串,字符串以问号开头。

 navigator对象 提供一系列属性用于检测浏览器 属性:onLine 检测是否联网 userAgent 浏览器嗅探 检测浏览器的类型。

 history对象 功能:保存用户上网的历史记录。 方法属性:go()在用户历史记录任意跳转,接收一个参数,标签前后跳转页数的整数值 back()后退 foward()前进。

为什么说操作DOM是昂贵的?

操作DOM会进行线程上的切换,JS引擎然切换到GUI渲染引擎然后GUI渲染引擎再切换回JS引擎这一些的操作就会造成性能损耗,最主要的是操作DOM还有可能引发回流与重绘,比如修改DOM的边距、大小,添加删除元素、改变窗口大小等就会引发重排,设置背景色、修改文字颜色、改变 visibility属性值就会引发重绘,回流必然引发重绘但是重绘不一定引发回流,回流的性能开销要大于重绘。

重绘和回流(Repaint & Reflow),以及如何进行优化

重绘

由于节点的集合属性发生改变或者由于样式改变而不会影响布局的,称为重绘,例如 outline、visibility、color、background-color 等,重绘的代价是高昂的,因此浏览器必须验证DOM 树上其他节点元素的可见性。 

回流

回流是布局或者几何属性需要改变就称为回流。回流是影响浏览器性能的关键因素,因为其变化涉及到部分页面(或是整个页面)的布局更新。一个元素的 回流可能会导致其素有子元素以及 DOM 中紧随其后的节点、祖先节点元素的随后 的回流。大部分的回流将导致页面的重新渲染。 

浏览器优化

现代浏览器大多是通过队列机制来批量更新布局,浏览器会把修改操作放在 队列中,至少一个浏览器刷新(即 16.6ms)才会清空队列,但当你获取布局信 息的时候,队列中可能会有影响这些属性或方法返回值的操作,即使没有,浏览器也会强制清空队列,触发回流和重绘来确保返回正确的值。 

但是会有一些属性会会强制渲染刷新队列我们应该避免这些属性:ffsetTop、clientTop、scrollTop、getComputedStyle()、width、height、getBoundingClientRect()。

代码优化

CSS

1.使用 transform 代替 top;

2.使用 visibility 替换 display: none,前者引起重绘,后者引发回流; 

3.避免使用 table 布局;

4.尽可能在 DOM 树的最末端改变 class;

5.避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多;

 6.将动画效果应用到 position 属性为 absolute 或 fixed 的元素上,避免影响其他元素的布局;避免使用 CSS 表达式,可能会引发回流;

7.CSS 硬件加速;

JS

1.避免频繁操作样式,修改 class 最好;

 2.避免频繁操作 DOM,合并多次修改为一次;

3.避免频繁读取会引发回流/重绘的属性,将结果缓存;

4.对具有复杂动画的元素使用绝对定位,使它脱离文档流; 

opacity: 0、visibility: hidden、display: none 优劣和适用场景

1. display: none - 不占空间,不能点击,会引起回流,子元素不影响;

2. visibility: hidden - 占据空间,不能点击,引起重绘,子元素可设置 visible 进行显示;

3. opacity: 0 - 占据空间,可以点击,引起重绘,子元素不影响;