BOM和DOM

202 阅读7分钟

1、BOM介绍

BOM是三个单词的首拼--Browser Object Model,即浏览器对象模型。

  • navigator 存储浏览器信息

  • history

    • 操作浏览器的历史记录

      • back()

        • 后退一个页面
        history.back(); # 返回到上一个页面,相当于浏览器的后退按钮
        
      • forward()

        • 前进一个页面
        history.forward(); # 前进到下一个页面(下一个页面必须是点击以后的页面),相当于浏览器的前进按钮
        
      • go()

        • 前进或后退多个页面
        history.go()
        
        -   正数是前进
        -   负数是后退
        -   前进后退几个页面,就看数字是几
        
  • location

    • 操作地址栏url

      • href

        • 设置完整的地址

          • location.href = 新地址
        • 获取完整的地址

          • location.href
      • search

        • 设置地址栏参数

          • location.search = ?键=值&键=值
        • 获取地址栏参数

          • location.search
      • hash

        • 设置锚点

          • location.hash = #单词
        • 获取锚点

          • location.hash
  • 窗口大小

    • innerWidth
    • innerHeight
      var w = window.innerWidth;
      console.log(w);
      var h = window.innerHeight;
      console.log(h);
    
  • 事件

    • window.onload

      • 当所有资源加载完成后,触发执行
      window.onload = function(){
      console.log("当网页中所有资源加载完成才打印");
        }
      
    • window.onresize

      • 当浏览器窗口大小发生改变,触发执行
      window.onresize = function(){
      // 当浏览器的窗口大小发生改变的时候触发这个事件
      }
      
    • window.onscroll

      • 当浏览器卷去的距离发生改变,触发执行
      window.onscroll = function () {
      console.log('浏览器滚动了')
        }
      
  • 方法

    • 弹出层

      • alert
      • confirm
      • prompt
    • 全局定义的函数,都属于window的方法

    • 全局定义的变量,都属于window的属性

    • 定时器

      • setInterval(函数, 毫秒数)

        • 让函数每隔毫秒数,就执行一次,会不停的执行下去
           var timerId = setTimeout(function () {
           console.log('我执行了')
         }, 1000)
         console.log(timerId) // 1
        
      • setTimeout(函数, 毫秒数)

        • 让函数延迟毫秒数执行一次,就这一次
        var timerId = setInterval(function () {
           console.log('我执行了')
         }, 1000)                 
        
      • 关闭定时器

        • clearTimeout(timerId); // 关闭延迟执行的定时器
        • clearInterval(timerId); // 关闭间隔执行的定时器
        • (可以互用)
    • 打开新的标签页

      • window.open(url)
    • 设置卷去的距离

      • window.scrollTo(x, y)
    • 关闭窗口

      • window.close()

DOM

  • 概念

    • document object model

      • 文档对象模型

        • 由对象组成的结构操作html文档
  • 获取标签

    • document.querySelector(css选择器)

      • 获取满足css选择器的第一个 标签

        • 返回一个标签
    • document.querySelectorAll(css选择器)

      • 获取所有满足css选择器的标签

        • 返回一个集合
    • document.getElementById()

    • document.getElementsByClassName()

    • document.getElementsByTagName()

    • document.getElementsByName()

    document.getElementById("标签id名"); // 通过标签的id名获取标签
    document.getElementsByTagName("标签名"); // 通过标签名获取标签
    document.getElementsByClassName("标签类名"); // 通类名获取标签
    document.getElementsByName("标签的name属性的值"); // 通过标签的name属性获取标签
    // 上述4种获取标签的方法,除了通过id可以准确获取到元素,别的方法都是只能获取到元素的集合(类数组)
    
  • 获取html基本结构标签

    • HTML

      • document.documentElement 可以获取html元素及其所有内容
    • body

      • document.body body比较常用, 并且在页面中是唯一的, 因此可以使用document.body直接获取
    • head

      • document.head 可以直接获取head元素
    • 网页标题

      • document.title

        • 可获取标题
        • 可设置标题
  • 获取标签名

    • 标签.tagName
  • 操作内容

    • 双标签

      • 文本内容

        • 设置

          • 标签.innerText = 值
        • 获取

          • 标签.innerText
      • 带标签的内容

        • 设置

          • 标签.innerHTML = 值
        • 获取

          • 标签.innerHTML
    • 表单

      • 设置

        • 标签.value = 值
      • 获取

        • 标签.value
        <body>
        <div id="box" introduce="区域"><p>我是盒子</p></div>
        </body>
        <script type="text/javascript">
            document.getElementById("box").innerText = "今天你要嫁给我";
            var text = document.getElementById("box").innerText;
            document.write(text);
            document.getElementById("box").innerHTML = "<a                                     href='http://www.baidu.com'>百度</a>";
            var content = document.getElementById("box").innerHTML;
            console.log(content);
        
  • 操作属性

    • 设置属性

      • 标签.setAttribute(属性名, 值)
    • 获取属性

      • 标签.getAttribute(属性名)
    • 删除属性

      • 标签.removeAttribute(属性名)
  • 样式操作

    • 获取样式

      • getComputedStyle(标签)

        • 返回由所有css键值对组成的对象
    • 设置样式

      • 标签.style.css键 = 值

        • 设置到行内
  • 类名操作

    • 直接操作class属性值

      • 标签.className = 值
      • 标签.className
    • classList

      • add()

        • 添加一个类名
         list.add('border')
        
      • remove()

        • 删除一个类名
        list.remove('green')
        
      • contains()

        • 判断是否有这个类名
        console.log(list.contains('green'))
        
      • toggle()

        • 让类名在删除和添加之间切换
  • 获取卷去的距离

    • 有文档声明

      • document.documentElement.scrollTop
    • 没有文档声明

      • document.body.scrollTop
  • 短路运算

    • 使用逻辑运算符 && 和 || 给变量进行赋值

      • var 变量 = 值1 && 值2
      
          -   如果左边为true,就会将右边的值赋值给变量
          -   如果左边为false,就会将左边的值赋值给变量
      
      • var 变量 = 值1 || 值
            -   如果左边为true,就会将左边的值赋值给变量
            -   如果左边为false,就会将右边的值赋值给变量
    
  • this在事件函数中,代表触发事件的标签

  • 节点操作

    • 创建标签

      • document.createElement(标签名)
    • 插入标签

      • 父.appendChild(子)

      • 父.insertBefore(新, 旧)

        • 将新的子标签放在旧的子标签前面
    • 替换标签

      • 父.replaceChild(新, 旧)
    • 复制标签

      • 标签.cloneNode(true)
    • 删除标签

      • 父.removeChild(子)

        • 子.parentElement.removeChild(子)
    • 获取标签

      • 所有子

        • 父.children
      • 第一个子

        • 父.firstElementChild
      • 最后一个子

        • 父.lastElementChild
        • 子.parentElement
      • 上一个兄弟

        • 标签.previousElementSibling
      • 下一个兄弟

        • 标签.nextElementSibling
  • 标签大小

    • 带边框

      • 标签.offsetWidth
      • 标签.offsetHeight
    • 不带边框

      • 标签.clientWidth
      • 标签.clientHeight
  • 标签位置

    • 标签.offsetLeft
    • 标签.offsetTop

    获取标签的位置 --- 数字

    参照设置过定位的直系祖宗标签

  • 边框厚度

    • 标签.clientLeft
    • 标签.clientTop
  • 不包含滚动条的窗口大小

    • document.documentElement.clientWidth
    • document.documentElement.clientHeight
  • 回流和重绘

    • 浏览器的渲染过程

      • 解析html成DOM树,同时解析css成样式规则

      • 合并成渲染树

      • 布局

        • 计算标签大小、关系、位置
      • 喷绘

        • 绘制颜色
      • 浏览器显示

    • 回流:英文叫reflow,指的是当渲染树中的节点信息发生了大小、边距等问题,需要重新计算各节点和css具体的大小和位置。例:在css中对一个div修饰的样式中,修饰了宽度、高度等样式,浏览器需要重新计算标签大小,这个计算的过程,就是回流的过程。

      容易造成回流的操作:

    • 布局流相关操作

      • 盒模型的相关操作会触发重新布局
      • 定位相关操作会触发重新布局
      • 浮动相关操作会触发重新布局
    • 节点操作

      改变节点的结构或其中的文本结构会触发重新布局。

      对标签进行下面这些属性或方法操作的时候,会强行回流:

      • offsetTop
      • offsetLeft
      • offsetWidth
      • offsetHeight
      • scrollTop
      • scrollLeft
      • scrollWidth
      • scrollHeight
      • clientTop
      • clientLeft
      • clientWidth
      • clientHeight
      • getComputedStyle
    • css

      • width
      • height
      • padding
      • border
      • margin
      • position
      • top
      • left
      • bottom
      • right
      • float
      • clear
      • text-align
      • vertical-align
      • line-height
      • font-weight
      • font-size
      • font-family
      • overflow
      • white-space
  • 重绘:英文叫repaint,当节点的部分属性发生变化,但不影响布局,只需要重新计算节点 在屏幕中的绝对位置并渲染的过程,就叫重绘。比如:改变元素的背景颜色、字体颜色等操作会造成重绘。 回流的过程在重绘的过程前面,所以回流一定会重绘,但重绘不一定会引起回流。

    容易造成重绘操作的css:

    -   color
    -   border-style
    -   border-radius
    -   text-decoration
    -   box-shadow
    -   outline
    -   background
    

优化

不管是回流还是重绘,都会对浏览器的渲染造成影响,所以我们在项目中,尽量避免回流。

1、合并样式修改

减少造成回流的次数,如果要给一个节点操作多个css属性,而每一个都会造成回流的话,尽量将多次操作合并成一个,例:

var oDiv = document.querySelector('.box');
oDiv.style.padding = '5px';
oDiv.style.border = '1px solid #000';
oDiv.style.margin = '5px';

操作div的3个css属性,分别是padding、border、margin,此时就可以考虑将多次操作合并为一次。

方法1:使用style的cssText

oDiv.style.cssText = 'padding:5px; border:1px solid #000; margin:5px;';

方法二:将这几个样式定义给一个类名,然后给标签添加类名:

<style>
    .pbm{
        padding:5px; 
        border:1px solid #000; 
        margin:5px;
    }
</style>
<script>
    var oDiv = document.querySelector('.box');
    oDiv.classList.add('pbm');
</script>

2、批量操作DOM

当对DOM有多次操作的时候,需要使用一些特殊处理减少触发回流,其实就是对DOM的多次操作,在脱离标准流后,对元素进行的多次操作,不会触发回流,等操作完成后,再将元素放回标准流。

例:

var data = [
    {
        id:1,
        name:"商品1",
    },
    {
        id:2,
        name:"商品1",
    },
    {
        id:3,
        name:"商品1",
    },
    {
        id:4,
        name:"商品1",
    },
    // 假设后面还有很多
];
var oUl = document.querySelector("ul");
for(var i=0;i<data.length;i++){
    var oLi = document.createElement("li");
    oLi.innerText = data[i].name;
    oUl.appendChild(oLi);
}

这样每次给ul中新增一个li的操作,每次都会触发回流。

方法1:方法一:隐藏ul后,给ul添加节点,添加完成后再将ul显示

oUl.style.display = 'none';
for(var i=0;i<data.length;i++){
    var oLi = document.createElement("li");
    oLi.innerText = data[i].name;
    oUl.appendChild(oLi);
}
oUl.style.display = 'block';

此时,在隐藏ul和显示ul的时候,触发了两次回流,给ul添加每个li的时候没有触发回流。

方法二:创建文档碎片,将所有li先放在文档碎片中,等都放进去以后,再将文档碎片放在ul中

var fragment = document.createDocumentFragment();
for(var i=0;i<data.length;i++){
    var oLi = document.createElement("li");
    oLi.innerText = data[i].name;
    fragment.appendChild(oLi);
}
oUl.appendChild(fragment);

文档碎片就是一个虚拟的DOM节点。对文档碎片操作不会造成回流。

方法三:将ul拷贝一份,将所有li放在拷贝中,等都放进去以后,使用拷贝替换掉ul

var newUL = oUl.cloneNode(true);
for(var i=0;i<data.length;i++){
    var oLi = document.createElement("li");
    oLi.innerText = data[i].name;
    newUL.appendChild(oLi);
}
oUl.parentElement.replaceChild(newUl, oUl);

3、避免多次触发布局

如下回到顶部的操作:

goBack.onclick = function(){
    setInterval(function(){
        var t = document.documentElement.scrollTop || document.body.scrollTop;
        t += 10;
        document.documentElement.scrollTop = document.body.scrollTop = t;
    },20)
}

每隔20毫秒都会重新获取滚动过的距离,每次都会触发回流,代码优化如下:

goBack.onclick = function(){
    var t = document.documentElement.scrollTop || document.body.scrollTop;
    setInterval(function(){
        t += 10;
        document.documentElement.scrollTop = document.body.scrollTop = t;
    },20)
}

只获取一次,每次都让数字递增,避免每次都获取滚动过的距离。

对于页面中比较复杂的动画,尽量将元素设置为绝对定位,操作元素的定位属性,这样只有这一个元素会回流,如果不是定位的话,容易引起其父元素以及子元素的回流。

4、修改批量设置样式函数

function setStyle(ele, styleObj) {
    var cssText = ''
    for(var key in styleObj) {
        var cssProp = key
        for(var a = 0; a < cssProp.length; a++) {
            var charCode = cssProp.charCodeAt(a)
            if(charCode >= 65 && charCode <= 90) {
                cssProp = cssProp.slice(0, a) + '-' + cssProp[a].toLowerCase() + cssProp.slice(a+1)
            }
        }
        cssText += cssProp + ':' + styleObj[key] + ';'
    }
    ele.style.cssText = cssText
}