最近在重新过一遍前端的基础知识,WebAPI、 DOM和BOM是前端中非常重要的概念,但是很多刚刚接触前端的小伙伴不知道WebAPI、 DOM和BOM是什么,以及它们之间的关系,本文就把笔者对WebAPI、 DOM和BOM相关知识的总结,分享给各位小伙伴,希望能对大家有所启发。
JS的三大组成部分
说到JS中的WebAPI、DOM和BOM,就不得不先说js的基本组成了。JS由ECMAScript基本语法(简称ES)、DOM(文档对象模型)和BOM(浏览器对象模型),图示如下:
WebAPI
什么是API和WebAPI ?
👉:API——Application Programming Interface(应用程序接口),简单来说,就是一些程序(比如操作系统)提前封装好了一些函数和方法,提供给我们,我们不需要了解它是怎么封装的,只要调用,就可以实现相应的功能。
👉:WebAPI——就是浏览器用js语法提前封装好了一些函数和方法,给我们来调用。主要是针对BOM 和DOM 封装的一些方法。
为什么要使用WebAPI?
👉:最主要是可以提高开发效率
BOM和DOM的关系
👉:BOM——Browser Object Model (浏览器对象模型),其实就是把浏览器当做一个对象来进行操作,比如前进、后退、页面跳转、刷新等
👉:DOM——Document Object Model(文档对象模型),Document是文档,即整个WEB页面,所有的Dom元素都在Document整个文档里。简单来说,DOM就是把整个文档页面当做一个对象进行操作,其核心思路就是把网页上的任何内容都当做一个对象来处理
👉:二者的关系简单理解,就是BOM包含DOM,图示如下:
- 浏览器打开一个页面就会 自动 创建 一套 BOM 对象(window/document/.......)
- 其中 document 下 包含了 根据 html 创建 的
Dom 对象,这个DOM对象,以树形结构展示,即DOM树
DOM对象的核心
作为一个对象,DOM对象内,有数百个属性和方法。DOM对象处理的核心,就是对DOM中的元素节点(即标签)进行一些增、删、改、查、事件操作、属性操作等一系列操作。
DOM核心操作思维导图:
DOM之创建元素(三种方法的对比)
先引入一个概念:文档流
👉文档流:
浏览器在打开页面时会开启一个文档流,来渲染每个标签,从上到下执行完了后会关闭文档流。
-
document.write:
- 特点是,如果document.write是在文档流关闭后才来调用,那么浏览器会开启一个新的文档流,新的文档流就会把原来的内容全部覆
- 此外,document.write创建元素时,只能创建到body里
- 因此, 在实际工作中,创建元素时不会用document.write,因为它不好用,而且可能会把原来网页内所有的内容都覆盖掉
-
innerHTML:
- 如果直接用等号(=),也会覆盖原来的值
- 如果用加等(+=),从视觉角度没有覆盖,就是增加新元素
- 但是从原理角度:本质上是把原来的元素取出来,再和新的元素一起添加进去,其实也是覆盖了原来的内容,它会让
原来的元素事件失效 - 就像正品和高仿,就算再像,也不是同一个
-
createElement:- 就是单纯的新增一个元素,不会影响原来的内容,最好用
DOM之增:就是新增元素节点
简单到令人发指:
1、利用appendChild和insertBefore添加
-
appendChild增到到最后面,即追加:父元素.appendChild(标签名)
注意:这个标签名是变量,不能加引号 -
insertBefore增加到某一子元素前面,即插入:父元素.insertBefore(标签名,在哪个元素之前插入)
-
细节:- 以上两种方式,如果添加的是新创建的元素,就是
添加; - 如果添加的是已经存在的元素,那么都相当于是
移动;
- 以上两种方式,如果添加的是新创建的元素,就是
2、以克隆方式新增:cloneNode(),分浅克隆和深克隆
-
浅克隆:cloneNode(),就像山寨,只克隆样式(即标签),连内容都没有,一看就是假的
-
深克隆:cloneNode(true),就像高仿,虽然克隆了样式和内容,但是没有方法,仔细看还是能看出来是假的
-
两种方式的克隆,都不包含事件
DOM之删:就是删除元素节点
- 父元素.removeChild(要被删除的标签名)
DOM之查:就是获取页面的元素节点
常见的方法有2个:
1、传统方法获取页面元素:
就是DOM提供的API方法,现在不常用
- 获取单个DOM对象:document.getElementById('id')
- 要传入的是字符串,直接写id名不用加#
- document不能换成其他标签,因为ID具有唯一性
- 获取DOM
伪数组:- document.getElementsByTagName('标签名') // 要传入的是字符串
- document.getElementsByClassName('类名') // 直接写类名不用加.
- 这两个document都可以换成其他的标签
2、H5新方法: 推荐使用
-
获取单个DOM对象:
- document.querySelector('选择器名')// ID和类选择器要加#或. 如果有多个,只能找到第一个
- document.body //获取body
- document.head //获取head
- document.documentElement //获取html
-
获取DOM伪数组:
- document.quertSelectorAll ('选择器名') // ID和类选择器要加#或.
3、利用节点操作获取元素
-
获取父节点(父元素):子元素.parentNode
- 只能得到1个元素节点,一是因为,文本节点不能做父亲,二是因为,亲爸爸只有一个,因此只能获取到一个父元素
-
获取子元素:父元素.children
- 默认得到
伪数组 - 如果想找第几个孩子,可以像找数组元素一样,用下标找,即父元素.children[下标]
- 默认得到
DOM之改:就是修改页面的元素属性
1、访问和修改的方法
由于获取的页面元素,本质上都是对象,因此用访问和修改对象属性的方法,来访问和修改页面元素的属性
-
通过点语法:对象.属性名=‘属性值’
img.src = './images/lyf.jpg' -
通过中括号: 对象['属性名']=‘属性值’
img['title'] = '我是李易峰'
2、常被修改的元素属性:src、href、tittle等
3、修改普通元素内容:innerText和innerHTML
- 相同点:
- 都能获取
双标签元素的内容 - 都能给
双标签元素添加内容
- 都能获取
- 不同点:
- 获取内容时:innerText只能获取文本内容,innerHTML既能获取文本,也能获取标签
- 添加内容时:如果内容中有标签,innerText会把标签当做文本一起添加进去,而innerHTML会把标签
解析出来,比如h3,innerHTML会把标签内容解析为h3的格式
4、修改表单的属性:
- 获取表单元素(单标签)的文本:对象名.value
- 一些表单属性:disabled(是否禁用)、checked(单复选框是否选中)、selected(下拉菜单是否选中),这些属性,在js中是写上就代表使用了,因此修改的时候,赋值为true 或false即可
5、修改元素样式
- document.style.样式名,如box.style.width='300px'
6、修改元素的类名
6.1 传统方法修改类名
- className,如box.className=‘.red’
6.2 H5新增的操作元素类的方法——classList
-
为什么有这个新增的方法?
-
传统的className方法,添加和删除的操作都非常不方便
-
添加的时候,如果直接用等号,会覆盖原来的样式;如果用加等,可以一直赋值相同的类名,但样式没有任何变化,如下图:
-
删除的时候,如果用赋值为原值的方法进行删除,会把后来添加的所有样式,都删除;如果用replaca替换的方法进行删除,在行内样式中会产生很多空格,而且,如果有重复的样式名,只能删除一个,如图:
-
因此,传统的className操作元素类的方法,非常之不方便
-
-
classList的一些方法:classList是一个伪数组,它保存当前这个元素所有的类
- add()
- 添加一个类
- 如果要添加多个类,用逗号隔开,每个类都是独立的一个参数
- remove()
- 删除一个类
- 如果要删除多个类,用逗号隔开,每个类都是独立的一个参数
- contains()
- 判断是否有某个类
- 如果有得到true,如果没有得到false
- toggle()
- 切换一个类,原来有这个类就删除,原来没有这个类就添加
- replace()
- 替换一个类
- 参数1:被替换的类
- 参数2:要替换的新类
- add()
DOM之属性操作:主要是对自定义的属性进行操作
1、传统操作自定义属性的方法
- 新增或设置DOM的属性值:setAttribute()
- 获取DOM的属性值:getAttibute()
- 移除DOM的属性值(自定义和非自定义都可以移除):removeAttibute()
点语法只能拿到html中自带的属性,不能拿到自定义属性- 以上方法,只能操作单个属性,如果想将自定义属性,全部取出,就很麻烦,因此,H5引入了新的操作自定义属性的方法,即dateset()
2、H5新增的操作自定义属性的方法
- 自定义属性写法规范:以用data-开头,方便识别哪些是自定义的属性
- 方法:元素.dataset
- 它是一个对象,可以获取所有以data-开头的自定义的属性
- 获取单个属性,用元素.dataset.属性名
- 设置元素属性,元素.dataset.属性名 = 数据
事件操作
👉事件:就是用户与页面的交互,用户做了什么,页面做了什么回应
1、什么是事件对象?
- 其实就是一个对象。里面包含了
事件触发时的一些信息,比如,是否按了alt键、鼠标点击的位置等
2、事件分类:0级事件和2级事件
- 0级事件:
- 用on开头,比如onclick;0级事件是DOM初稿中的方法
- 删除事件:对象.onclick=null
- 2级事件:第二版DOM中的新方法
- 添加事件:对象.addEventListener('事件名',function(){})
- 删除事件:对象.removeEventListener('事件名', function(){})
- 删除时,用哪种方法添加的事件,就用哪种方式删除
- 匿名函数添加的事件,不能删除原因详见《为什么匿名函数添加的事件,不能进行删除操作?》
3、事件三要素
-
事件源:真正触发事件的元素
- 获取事件源:e.target
-
事件类型
-
鼠标事件:
- 鼠标点击:click
- 鼠标双击:dblclick
- 鼠标移入:mouseover
- 鼠标移出:mouseout
-
鼠标按下:mousedown
- 鼠标弹起:mouseup
-
H5中的鼠标拖拽事件- 元素默认不可拖拽,要加拖拽属性,才能拖拽,即draggable='true'
- 拖拽事件:
- 给被拖拽的元素加的事件:dragstart(拖拽开始)、drag(拖拽中)、dragend(拖拽结束事件)
- 给容器检测添加的事件:dragenter(拖拽进入事件)、dragleave(拖拽离开事件)
- 拖拽悬停事件:dragover
- 在容器范围内,鼠标拖拽并悬停,就会一直触发
- 它的默认行为是禁止被拖放进来
- 拖放事件:drop
- 在容器范围内,且松手
- 默认不触发,想触发,要先用dragover,阻止拖拽悬停的默认行为
-
焦点事件:
- 获得焦点:focus
- 失去焦点:blur
-
键盘事件:
- 键盘按下:keydown和keypress,常用keydown
- 键盘弹起:keyup,与keydown是一对,不区分大小写,不过滤功能键
-
-
响应程序:就是做了什么回应,一般是个函数
4、事件流
👉定义:事件触发时会经历从上到下,再从下到上的流动过程
- 事件流的三个阶段:
- 捕获阶段:从上到下,需要写代码调用
- 目标阶段:当前被触发的事件
- 冒泡阶段:从下往上,默认存在
5、事件流的应用——事件委托
- 什么是事件委托?
👉就是把事件交给其他元素来处理,即把事件绑定给其他元素,一般是父元素
- 为什么要委托?
👉主要是为了减少事件的绑定的次数,提高运行性能。
比如说,一个ul中有很多li,如果想点击每个li,就弹出这个li的内容,那传统的方法是,要遍历这个ul,找到每个li,给每个li添加点击事件,这样一方面会绑定很多事件,另一方面会降低运行性能;另外,如果后续这个ul中添加了很多新的li,那新增的li,是不会有点击事件的,因为,在给每个li添加事件时,获取的是当时ul中所有的li,后来新增的,不包含在内。
这种情况,就可以采用事件委托的方法,把这个点击事件,绑定给li的父亲ul。借用事件冒泡的优势,点击li的时候,li没有事件,会冒泡给父亲ul,ul再执行事件,显示每个事件源的innerText即可。
-
事件委托的好处?
由此,可以看出,事件委托主要有两大好处:
- 减少事件的绑定的次数
- 让不管是老元素,还是后续新增的元素,同样具有事件
6、事件中的常用方法:详见思维导图
7、关于坐标系的使用场景
很多小伙伴在学习JS的时候,经常将e.pageX、e.screenX、e.clientX等概念,与三大家族混淆,不知道它们的区别及具体使用场景,在此做一个简要小结:
7.1、事件里的坐标系:e.pageX、e.screenX、e.clientX
顾名思义,这些坐标系是用在事件里的,获取的是事件中鼠标的位置(坐标)。我们都知道,事件是用户在页面上做的一些操作(如鼠标点击、键盘按下等)后,页面给出的一些响应程序。事件中包含事件对象,即保存了事件触发时的一些信息。
想要获取事件里的信息,需要在事件触发的函数(如onclick)内写一个形参,一般用e、ev、event等,这样,再触发的事件里,就可以用e.来获取事件触发时的一些信息。
因此,e.pageX、e.screenX、e.clientX这些,其实都是事件对象中的方法, 主要作用是,获取事件内,鼠标的一些位置信息,如点击的时候,鼠标距离【页面】的距离(e.pageX、e.pageY)、鼠标距离【可视区】的距离(e.clientX、e.clientY)、鼠标距离【屏幕】的距离(e.screenX、e.screenY)
7.2、给元素使用的坐标系:三大家族
与上述事件内的坐标系不同,三大家族系列,不是给事件对象用的,而是给元素自己用的,获取的是元素自己相对于页面(父盒子)、可视区和被卷去的距离,具体区别详见下图:
BOM对象的核心
前文提到过,DOM是把整个文档页面当做一个对象进行操作,而BOM是,把整个浏览器当做一个对象来进行操作,比如前进、后退、页面跳转、刷新等。因此,BOM的核心操作,其实是对BOM中除Document以外的其他Window、Location、History等对象的操作。
Window
什么是Window?
👉 Window: 代表整个浏览器,它是浏览器的顶级事件,具体含义包括两个方面:
- 页面上在
全局作用域中用var声明的变量以及函数,都是window的属性和方法,window可以省略 - BOM和DOM都在window里
Window中的一些事件
window本身,可以被看成一个对象,因此,它也有属性和方法,也可以添加事件
1. 加载相关事件
-
load加载事件
- 页面加载完成之后(DOM树创建完毕,内外部资源如图片等加载完毕),里面的代码才执行
- 用法:window.onload 或者 window.addEventListener('load', 回调函数)
-
beforeunload :页面关闭之前执行的事件
-
unload:页面关闭时执行的事件,可以保存用户信息
2. resize尺寸改变事件
- 用法:window.onresize
- 动态监听/获取浏览器的【可视区】的尺寸
- 应用场景:可实现响应式布局
window的一些属性
1. name属性
- window.name
- 特点是:不管给name赋什么值,最后都会转成【字符串】
2. innerWidth和innerHeight属性
- window.innerWidth,获取整个浏览器【能显示内容】的宽
- window.innerHeight,获取整个浏览器【能显示内容】的高
3. pageXOffset和pageYOffset属性
- window.pageXOffset,获取浏览器【往左】滚出去的距离
- window.pageYOffset,获取浏览器【往上】滚出去的距离
window的一些方法
- open():打开一个新页面
- close():关闭当前页面
- scroll(x,y):设置页面滚动距离
- 定时器
- 开启:setInterval(回调函数, 间隔时间)、setTimeout(回调函数, 间隔时间)
- 关闭:clearInterval( 定时器的id )、clearTimeout(( 定时器的id )
- setInterval和setTimeout的用法一样,只不过setInterval是每隔一段时间,调用一次,有循环的作用,只要不手动关闭,会一直执行;而setTimeout执行一次就结束,因此主要用来设置延迟执行事件
Location对象
什么是Location对象?
👉 : 保存了浏览器的一些地址相关信息的对象,本质上也是一个对象,有自己的属性和方法
👉 : 对Location对象的操作,可以获得浏览器地址相关的一些信息
Location对象的一些属性
- location.href, 获取完整网址,可设置,设置就是跳转
- location.search ,获取
问号及之后的信息,即用户输入或提交的信息 - location.hash, 获取#号及之后的信息
Location对象的一些方法
-
assign(),等同于给location.href赋值,实现跳转功能
-
reload(),刷新
-
replace(),替换当前页面
History对象
什么是History对象?
👉 : 保存了浏览器的一些历史相关信息的对象,本质上也是一个对象,有自己的属性和方法
History对象的一些方法
- history.back() 去上一个页面
- history.go() 去后退几个数字的页面
- history.forward() 前进一个页面
Navigator对象
👉 : 保存了浏览器关于的定位相关信息的对象,获取你所在位置的经纬度
浏览器本地存储
- 本地存储、浏览器存储、浏览器缓存,都是一个意思,都是只讲数据存储在浏览器本地。
- 浏览器本地存储有三种方法,分别是:localStorage、sessionStorage和cookie
1、LocalStorage
- 特点:默认看不到数据,要想看,去控制台的Application——localStorage找
- 方法:
- localStorage.setItem('键','值')——提交设置、修改存储数据,参数1是属性名,参数2是属性值
- localStorage.setItem('键')——获取存储的数据,只有一个参数,即属性名,得到结果是属性值
- localStorage.removeItem('键')——删除存储的数据,只有一个参数,即通过属性名,一个一个删除整条数据
- localStorage.clear()——清除存储的数据,没有参数,调用后立即清空所有存储的数据
2、SessionStorage
-
特点:默认看不到数据,要想看,去控制台的Application——sessionStorage找
-
方法:跟localStorage一模一样
- sessionStorage.setItem('键','值')——提交设置、修改存储数据,参数1是属性名,参数2是属性值
- sessionStorage.setItem('键')——获取存储的数据,只有一个参数,即属性名,得到结果是属性值
- sessionStorage.removeItem('键')——删除存储的数据,只有一个参数,即通过属性名,一个一个删除整条数据
- sessionStorage.clear()——清除存储的数据,没有参数,调用后立即清空所有存储的数据
-
localStorage和sessionStorage应用实例代码:
<body>
<button id="btn1">保存</button>
<button id="btn2">获取</button>
<button id="btn3">删除</button>
<button id="btn4">清除</button>
<script>
// 保存数据到浏览器
document.querySelector('#btn1').onclick = function () {
// 参数1:key,相当于给数据起一个别名(以后就通过这个别名找到数据)
// 参数2:value(真正存储的数据)
localStorage.setItem('name', 'andy')
localStorage.setItem('age', 16)
localStorage.setItem('sex', '男')
// 只能存字符串,如果你强行传入别的数据,它会调用toString方法转成字符串
// 再来存储,这个时候再取出来无法恢复成原来的数据
localStorage.setItem('array', [10, 20, 30])
localStorage.setItem('obj', {name:'jack', age:16} )
console.log([10,20,30].toString())
console.log({name:'jack', age:16}.toString())
}
// 获取
document.querySelector('#btn2').onclick = function () {
// 传入key,它就根据这个key取出对应的值
let res = localStorage.getItem('name')
alert(res)
console.log(localStorage.getItem('age'))
console.log(localStorage.getItem('sex'))
// 如果访问不存在的数据,得到null
console.log(localStorage.getItem('shengao'))
console.log(localStorage.getItem('array'))
console.log(localStorage.getItem('obj'))
}
// 删除
document.querySelector('#btn3').onclick = function () {
// 删除name对应的数据
localStorage.removeItem('name')
localStorage.removeItem('age')
localStorage.removeItem('sex')
}
// 清除
document.querySelector('#btn4').onclick = function () {
localStorage.clear()
}
</script>
3、localStorage和sessionStorage的异同点
-
相同点:
- 都是按域名保存起来的,如果多个网页,保存在同一个域名内,那网页之间,可以访问这些数据;但不同域名内的网页,不能访问
- 保存数据默认都是
字符串的形式,所以,默认取出来的也是字符串 - 因此,如果要保存非字符串的数据,可以先把数据用JSON.stringify转换成JSON字符串,然后进行保存
- 取出时,再用JSON.parse转成JS相应的数据
-
不同点:
- localStorage:只要不手动删除,会永久保存
- sessionStorage:只要关闭当前的网页,存储的内容就会被清空,因此也可以叫
临时存储或会话存储