DOM
css文件的加载是与DOM的加载并行的,也就是说,css在加载时Dom还在继续加载构建,而过程中遇到的css样式或者img,则会向服务器发送一个请求,待资源返回后,将其添加到dom中的相对应位置中,由于js文件不会与DOM并行加载,因此需要等待js整个文件加载完之后才能继续DOM的加载,
js加载原理
浏览器内核可以分成两部分:渲染引擎(Layout Engine 或者 Rendering Engine)和 JS 引擎。早期渲染引擎和 JS 引擎并没有十分明确的区分,但随着 JS 引擎越来越独立,内核也成了渲染引擎的代称(下文我们将沿用这种叫法)。渲染引擎又包括了 HTML 解释器、CSS 解释器、布局、网络、存储、图形、音视频、图片解码器等等零部件。
JS 引擎是独立于渲染引擎存在的。我们的 JS 代码在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标签时,它会暂停渲染过程,将控制权交给 JS 引擎。JS 引擎对内联的 JS 代码会直接执行,对外部 JS 文件还要先获取到脚本、再进行执行。等 JS 引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续 CSSOM 和 DOM 的构建。 因此与其说是 JS 把 CSS 和 HTML 阻塞了,不如说是 JS 引擎抢走了渲染引擎的控制权
- 正常引入js,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行
- async,script.js会被异步加载,即加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步)。当 script.js加载完整立即执行script.js。执行script.js时,html解析暂停。从加载完成立即执行来看,async模式 执行顺序与写的顺序无关,不保证执行顺序。
<script async src="script.js"></script>
- defer,script.js会被异步加载,即加载和渲染后续文档元素的过程将和 script.js 的加载并行进行(异步)。这一点与
async
模式一致。不同的是当 script.js加载完成并不会立即执行,而是在所有元素解析完成之后
<script defer src="script.js"></script>
-
onload,你可以将你的js代码写在window.onload(()=>{……})中,会在页面渲染完执行js代码
-
使用时机
- 当你的js是个独立的模块且不依赖任何js,使用 async;
- 如果你的js依赖其他js或者被其他js 依赖,使用 defer;
- 如果你对js文件很小且被 async script 依赖,使用正常模式的script且放在async script 前面。
document
使用console.dir(e) 可以查看元素的解构信息
1. 相关属性
- title
- url => http://127.0.0.1:8848/My/index3.html
- domain => 127.0.0.1
- charset => UTF-8
- body,forms,head,links,images,documentElement(html节点),anchors(锚点)
2. 相关方法
2.1、HTMLCollection和NodeList
-
document.getElementsByTagName 返回匹配标签名的HTMLCollection类数组
-
document.querySelectorAll 返回匹配选择器的NodeList类数组
-
HTMLCollection提供通过name或id直接获取元素、item(index)通过下标选取元素以及namedItem(id/name)通过id或name获取元素的方法,
-
NodeList提供了同数组一样的entries,forEach,keys,values还有item,二者都可以通过Array.from或者三点符号转为数组
2.2、元素关系
- 此处所说的子元素指的是直接子元素
- 也有选取节点的方法,例如childNodes、parentNode、firstChild、lastChild、nextSibling、previousSibling,但获取的结果可能并不是我们想要的,因为会参杂文本注释等节点
节点属性 | 说明 | 返回值/不存在时候值 |
---|---|---|
parentElement | 获取父元素 | Element/null |
children | 获取所有子元素 | HTMLCollection/[] |
childElementCount | 子标签元素的数量 | n/0 |
firstElementChild | 第一个子标签 | Element/null |
lastElementChild | 最后一个子标签 | Element/null |
previousElementSibling | 上一个兄弟标签 | Element/null |
nextElementSibling | 下一个兄弟标签 | Element/null |
contains | 返回布尔值,判断传入的节点是否为该节点或其的后代节点 | true/false |
2.3、选取元素
- 拥有id的元素可以直接作为window的属性来访问,但不能和window属性重名,也不建议直接使用,语义不明确
- 以下前四种方法只支持document访问
- document获取的集合顺序为元素在文档中的顺序
- 使用getElement…返回的都是动态的集合,也就是说当添加或修改页面元素时,返回的集合也会同步改变,而使用querySelectorAll返回的是静态集合,页面修改返回集合也不会修改,相当于一个固定快照
方法名 | 说明 | 返回值 |
---|---|---|
getElementById | 根据id唯一获取元素 | Element |
getElementsByName | 获取设置了name属性的元素集合 | NodeList[] |
getElementsByTagName | 按标签名获取元素集合,传入* 获取全部 | HTMLCollection[] |
getElementsByClassName | 按class样式属性值获取元素集合 | HTMLCollection[] |
querySelectorAll | 根据CSS选择器获取Nodelist节点列表,支持元素获取 | NodeList[] |
querySelector | querySelector使用CSS选择器获取一个元素 | Element |
matchs | 用于检测元素是否是指定的样式选择器匹配 | boolean |
closest | 查找最近的符合选择器的祖先元素(包括自身) | element |
2.4、操作属性
-
常见内置属性:className(指向class)、hidden、src 、alt、href、checked、value等
-
遍历属性
for(let {name,value} of parent2.attributes){
console.log(name,value)
}
方法 | 说明 | |
---|---|---|
attributes | 获取全部属性列表 | NamedNodeMap(类数组) |
getAttribute(name) | 获取指定属性 | value |
setAttribute(name,value) | 设置属性 | undefined |
removeAttribute(name) | 删除属性 | undefined |
hasAttribute(name) | 检测属性 | boolean |
- 自定义属性
- 元素中以data-为前缀的属性会添加到属性集中
- 使用元素的dataset可获取属性集中的属性
- 改变dataset的值也会影响到元素上
<div data-wfl-img='myProp'>houdunren.com</div>
const el = document.querySelector('.houdunren')
console.log(el.dataset.wflImg) // myProp
console.log(el.getAttribute('data-wfl-img')) // myProp
el.dataset.wflImg = '666'
console.log(el.dataset.wflImg) // 666
- 特征和属性是记录元素属性的两个不同场所,大部分更改会进行同步操作,但有时侯不会,例如我们直接修改文本框的value后el.getAttribute(‘value’)并不会发生变化,反之亦然
<input type="text">
el.value = 100
console.log(el.getAttribute('value')) // null
2.5、操作节点
- createElement:可以新建标签节点对象,
document.createElement('div') //创建div
document.createElement('script') //创建script
document.createElement('link') // 创建外链css
- cloneNode:克隆节点,是节点方法,当参数为true递归复制子节点即深拷贝;与之对应的还有document.importNode
const el = document.querySelector('#app')
const newEl = el.cloneNode(true) //还可以替换为:document.importNode(app, true)
document.body.append(newEl)
-
insertAdjacent
- insertAdjacentText(location,text):插入文本,遇到html也不会解析
- insertAdjacentHTML(location,htmlText):插入html文本,会解析html
- insertAdjacentElement(location,element):插入节点元素
location 说明 beforebegin 元素本身前面 afterend 元素本身后面 afterbegin 元素内部前面 beforeend 元素内部后面 -
推荐方法
- 以下方法可以同时添加多个内容,包括字符串与元素标签
方法 | 说明 |
---|---|
append | 节点尾部添加新节点或字符串 |
prepend | 节点开始添加新节点或字符串 |
before | 节点前面添加新节点或字符串 |
after | 节点后面添加新节点或字符串 |
replaceWith | 将节点替换为新节点或字符串 |
remove | 将节点删除,午餐 |
// app内追加节点和文本,按照参数顺序添加
let app = document.querySelector('#app')
let h1 = document.createElement('h1')
h1.append('wfl')
app.append('@', h1,'com')
// 将h2移动到h1之前
<h1>@h1</h1>
<h2>@h2</h2>
let h1 = document.querySelector('h1')
let h2 = document.querySelector('h2')
h1.before(h2)
// 删除h1节点
h1.remove()
-
古老方法(不建议)
只能添加节点元素,不能添加文本,也不支持多个参数
方法 | 说明 |
---|---|
appendChild | 添加节点 |
insertBefore | 用于插入元素到另一个元素的前面 |
removeChild | 删除节点 |
replaceChild | 进行节点的替换操作 |
2.6 、节点内容
- innerHTML
- innerHTML中只解析HTML标签语法,所以其中的 script 不会做为JS处理
- 使用innertHTML操作会重绘元素,重绘之后原元素的设置例如事件绑定就没有了
- outerHTML
- outerHTML与innerHTML的区别是包含父标签
- outerHTML不会删除原来的旧元素,只是用新内容替换替换旧内容,旧内容(元素)依然存在,使用innerHTML内容是被删除然后使用新内容,而使用outerHTML是保留旧内容,页面中使用新内容
- innerText和textContent
- 获取时忽略所有标签,只获取文本内容
- 设置时将内容中的标签当文本对待不进行标签解析
- outerText
- 同outerHTML,包括自身元素和只替换页面内容,但自身保留旧内容
2.7、操作样式
两种方式:操作class和style
-
class
-
通过className或者通过setAttribute直接操作class属性,但建议使用classList
-
classList
方法 说明 classList 获取类列表,类数组 classList.add 添加类名 classList.remove 删除类名 classList.toggle 切换类名,boolean classList.contains 类名检测,boolean
-
-
style
-
style.样式名
- 可以直接得到或设置单个样式,但只能得到行内样式的值,无法获取style标签或者link设置的值
- 单个单词的属性通过驼峰进行命名,如backgroundColor
-
style.cssText
- 可以通过字符串的方式批量获取或设置操作样式,但也只能得到行内样式的值
-
setAttribute
- 通过setAttribute改变style特征来批量设置样式,只能行内样式
-
window.getComputedStyle
- 上面几种方式都无法获取link或者style标签内的元素,而getComputedStyle(name)可以
- 但不建议使用
- 尺寸设置auto时获取结果不可用
- 由于滚动条的存在,不同浏览器获取的返回结果也不同
- 当元素没有设置CSS尺寸时,获取不到相应的尺寸内容
-
#app{
color: red;
}
<div id="app" style="display: flex;">
app
</div>
const app = document.querySelector('#app')
console.log(window.getComputedStyle(app).color) // rgb(255, 0, 0)
console.log(window.getComputedStyle(app).height) // 21px
console.log(app.offsetHeight) // 21
空间坐标
- 视口尺寸即网页可见区域,不包括浏览器工具条、菜单、标签、状态栏等
1. 视口与文档尺寸
方法 | 说明 |
---|---|
window.screen.width/height | 屏幕分辨率的宽/高 |
window.innerWidth/innerHeight | 浏览器宽度/高度 ,包括滚动条 |
document.documentElement.clientWidth/clientHeight | 视口宽度/高度, 不包括滚动条 |
document.body.clientWidth/clientHeight | 网页正文宽度/高度 |
document.documentElement.scrollTop/scrollLeft | 视口距离顶部/左侧的距离,也是当前网页滚动的距离 |
2. 方法列表
方法 | 说明 |
---|---|
element.getBoundingClientRect | 返回元素在视口坐标及元素大小,包括外边距,width/height与offsetWidth/offsetHeight匹配 |
element.offsetWidth | 元素宽度尺寸,包括内边距与边框和滚动条 |
element.offsetHeight | 元素高度尺寸,包括内边距与边框和滚动条 |
element.offsetLeft | 元素到父元素左侧的距离 |
element.offsetTop | 元素到父元素顶部的距离 |
element.clientWidth | 元素宽度,不包含边框,只包含内容和内边距 |
element.clientHeight | 元素高度,不包含边框,只包含内容和内边距 |
element.clientLeft | 内容距离外部的距离,包含边框,滚动条在左侧时包括滚动条尺寸 |
element.clientTop | 内容距离顶部的距离,包含边框,滚动条在顶部时包括滚动条尺寸 |
element.scrollWidth | 元素宽度,内容+内边距+内容溢出的尺寸 |
element.scrollHeight | 元素高度,内容+内边距+内容溢出的尺寸 |
element.scrollLeft | 水平滚动条左侧已经滚动的宽度 |
element.scrollTop | 垂直滚动条顶部已经滚动的高度 |
window.pageXOffset | 获取当前页面距离文档左侧的位置 |
window.pageYOffset | 获取当前页面距离文档顶部的位置 |
// DOM元素到浏览器可视范围的距离
console.log(JSON.stringify(app.getBoundingClientRect(),null,4))
{
"x": 18 等价top
"y": 10, 等价left
"width": 301, 自身高度
"height": 4580, 自身宽度
"top": 10, 元素上边距离可视区域上边高度
"right": 319, 元素右边距离可视区域左边高度
"bottom": 4590, 元素下边距离可视区域上边高度
"left": 18 元素左边距离可视区域左边高度
}
图片懒加载
方式1:利用img到顶部的距离offsetTop与视口到顶部的距离scrollTop比较进行懒加载
lorem5000
<img src="" alt="1" data-src='./img/2.jpg'>
lorem5000
<script>
let imgs = document.querySelectorAll('img')
function lazyLoad(imgs){
let T = document.documentElement.scrollTop // 视口距离顶部的距离,也就是滚动条滚动的距离
let H = document.documentElement.clientHeight // 视口高度
imgs.forEach(i=>{
if(T+H>i.offsetTop){ // 只要视口距离顶部的距离+视口高度就加载
i.src = i.dataset.src
}
})
}
lazyLoad(imgs) // 初始加载
window.onscroll = function(){
lazyLoad(imgs)
}
</script>
方式2:可以直接使用getBoundingClientRect获取img距离视口的高度top,只要top小于视口高度就加载
function lazyLoad(imgs){
imgs.forEach(i=>{
if(i.getBoundingClientRect().top <= document.documentElement.clientHeight){
i.src = i.dataset.src
}
})
}
不仅可以获取元素的坐标,还可用通过elementsFromPoint获取坐标上的元素集合,通过elementFromPoint获取坐标上的最底层元素
3. 滚动控制
- scrollLeft、scrollTop可以获取水平和垂直滚动条所在的位置
监听窗口的滚动行为,并获得滚动的位置
window.addEventListener('scroll',function(){
console.clear()
console.log(document.documentElement.scrollTop)
console.log(document.documentElement.scrollLeft)
console.log(window.pageXOffset)
console.log(window.pageYOffset)
})
监听元素的滚动行为,并获得滚动的位置
#app{
height: 200px;
overflow: auto;
}
<div id="app">lorem1000</div>
let app = document.querySelector('#app')
app.onscroll = function(){
console.log(app.scrollTop)
console.log(app.scrollLeft)
}
网页顶部显示当前浏览的进度条
.line{
position: fixed;
background-color: #1E9FFF;
height: 1px;
box-shadow: 0px 0px 1px #1E9FFF;
top:0px
}
<div class="line"></div>
<main>lorem1000</main>
<script>
let line = document.querySelector('.line')
window.onscroll = function(){
let T = document.documentElement.scrollTop // 视口距离顶部的距离,也就是滚动条滚动的距离
let H = document.body.offsetHeight - document.documentElement.clientHeight // 网页总高度
line.style.width = parseInt(T/H*100)+'%'
}
</script>
- scrollBy(options):按偏移量进行滚动内容,options参数有
- top:垂直偏移量
- left:水平偏移量
- behavior:滚动方式,”smooth“为平滑滚动
- scroll()或scrollTo():滚动到指定的具体位置, scrollTo(left,top) 或者scrollTo(options),options参数同上
document.documentElement.scroll({ top: 30, behavior: 'smooth' })
也可以使用 scrollTo(20)
- scrollIntoView:参数为true元素定位到窗口顶部,为false定位到窗口底部
document.documentElement.scrollIntoView(true)
document.documentElement.scrollIntoView({ block: 'start', behavior: 'smooth' })
事件监听
捕获:从外道理,从上到下
冒泡:从里到外,从下到上,使用e.stopPropagation()可以阻止冒泡
- 使用e.preventDefault()可以组织事件得默认行为,例如href等
- 同一个元素的相同事件是可以叠加了,会按照顺序执行回调
1. 绑定事件
-
直接在标签上绑定οnclick=fn(arg1,event,this),必须写括号,this代表事件源对象,event代表事件对象
-
dom绑定,el.onclick = function(e){},e代表事件对象,可以直接在函数内使用this
-
事件监听,推荐
- el.addEventListener(‘listerenName’,function(e){})绑定事件
还可以指定第三个参数,当第三个参数设置true或false等价于{ capture: true/false}
选项 参数 说明 once boolean true时只执行一次事件,默认false capture boolean 事件是在捕获/冒泡哪个阶段执行,true:捕获阶段 false:冒泡阶段 passive boolean 声明事件里不会调用 preventDefault()
,可以减少系统默认行为的等待- el.removeEventListener(‘listerenName’,function(e){})移除事件,但必须保证删除的事件函数与监听的事件函数是同一函数才可以删除
2. 事件对象
执行事件处理程序时,会产生当前事件相关信息的对象,即为事件对象event。系统会自动做为参数传递给事件处理程序
属性 | 说明 |
---|---|
type | 事件类型 |
target | 事件目标对象 |
x,y | 相对窗口的X,Y坐标 |
clientX,clientY | 相对窗口的X,Y坐标 |
screenX,screenY | 相对计算机屏幕的X,Y坐标 |
pageX,pageY | 相对于文档的X,Y坐标 |
offsetX,offsetY | 相对于事件对象的X,Y坐标 |
layerX,layerY | 相对于父级定位的X,Y坐标 |
path | 冒泡的路径 |
altKey,shiftKey,metaKey | 是否按了alt,shift,mate键 |
3. 事件类型
事件名 | 说明 |
---|---|
window.onload | 文档解析及外部资源加载后 |
DOMContentLoaded | 文档解析后执行,不需要等待图片/样式文件等外部资源加载,该事件只能通过addEventListener设置 |
window.beforeunload | 文档刷新或关闭时 |
window.unload | 文档卸载时 |
scroll | 页面滚动时 |
// 需要等外部图片、样式文件、JS文件等资源加载
window.addEventListener('load', (event) => {
console.log('load')
})
// 文档标签解析后执行,不需要等外部图片、样式文件、JS文件等资源加载
window.addEventListener('DOMContentLoaded', (event) => {
console.log('DOMContentLoaded')
})
// 当浏览器窗口关闭或者刷新时,会触发beforeunload事件,可以取消关闭或刷新页面
window.onbeforeunload = function (e) {
return '真的要离开吗?'
}
4. 鼠标事件
- 通过which可以用户按的键,1左键,2中键,3右键
- element.click()会触发element的click事件,模拟单击效果
事件名 | 说明 |
---|---|
click | 鼠标单击事件,同时触发 mousedown/mouseup |
dblclick | 鼠标双击事件 |
contextmenu | 点击右键后显示的所在环境的菜单 |
mousedown | 鼠标按下 |
mouseup | 鼠标抬起时 |
mousemove | 鼠标移动时 |
mouseover | 鼠标移动时 |
mouseout | 鼠标从元素上离开时 |
mouseup | 鼠标抬起时 |
mouseenter | 鼠标移入时触发,不产生冒泡行为 |
mosueleave | 鼠标移出时触发,不产生冒泡行为 |
oncopy | 复制内容时触发 |
scroll | 元素滚动时,可以为元素设置overflow:auto; 产生滚动条来测试 |
5. 键盘事件
- 给那个元素绑定键盘事件必须要元素获取焦点才能触发
事件名 | 说明 |
---|---|
keydown | 键盘按下时,一直按键不松开时keydown事件会重复触发 |
keyup | 按键抬起时 |
属性 | 说明 |
---|---|
keyCode | 返回键盘的ASCII字符数字 |
code | 按键码,字符以Key开始,数字以Digit开始,特殊字符有专属名子。左右ALT键字符不同。 不同布局的键盘值会不同 |
key | 按键的字符含义表示,大小写不同。不能区分左右ALT等。不同语言操作系统下值会不同 |
altKey | 是否按了alt键 |
ctrlKey | 是否按了ctlr键 |
shiftKey | 是否按了shift键 |
metaKey | 是否按了媒体键 |
// 以shift+a举例
document.documentElement.addEventListener('keydown',e=>{
console.log(e.keyCode) // 65
console.log(e.code) // KeyA
console.log(e.key) // A
console.log(e.shiftKey) //true
})
6. 表单事件
事件类型 | 说明 |
---|---|
focus | 获取焦点事件 |
blur | 失去焦点事件 |
element.focus() | 让元素强制获取焦点 |
element.blur() | 让元素失去焦点 |
change | 文本框在内容发生改变并失去焦点时触发,select/checkbox/radio选项改变时触发事件 |
input | Input、textarea或 select 元素的 value 被修改时,会触发 input 事件。而 change 是鼠标离开后或选择一个不同的option时触发。 |
submit | 提交表单 |