34. DOM
什么是DOM?
- DOM 》 Document Object Model。
- DOM 定义了表示和修改文档所需的方法。DOM 对象即宿主对象,有浏览器厂商定义,用来操作 HTML 和 XML 功能的一类对象的集合。也有人称 DOM 是对 HTML 以及 XML 的标准接口。
- Document 代表整个文档。
DOM 的基本操
1. 对 DOM 节点的增删改查
查询
-
查看元素节点
document.getElementById();通过ID获取元素。(IE8及以下的浏览器是不区分大小写的)- .
getElementsByTagName();通过标签名获取元素的集合放在类数组中 .getElementsByName();需注意,只有部分标签 name 可生效(表单、表单元素,img,iframe),目前各浏览器对其他标签兼容情况得到了优化,但是不常用.getElementsByClassName();通过类名选取元素,IE8及以下IE版本中没有.querySelector('div > span > ...');css选择器,选择一个,在ie7及以下版本中没有.querySelectorAll('div > span > ...');css选择器,选择一组,在ie7及以下版本中没有
注意^: querySelector 和 querySelectorAll 都是不推荐使用的,因为他们选择出来的都是不是实时的,是静态的是副本。(例如:n 个 div 删除其中一个,用getElement……选中的元素是 n - 1 个, 但是用queryS……依然是 n 个。
-
遍历节点树
-
parentNode- 父节点(最顶端的parentNode 为 #document) -
childNodes- 子节点们,会选出所有节点(下面会有节点类型的介绍) -
firstChild- 第一个子节点 -
lastChild- 最后一个子节点 -
nextSiBling- 下一个兄弟元素节点 -
previousSiBling- 上一个兄弟元素节点
-
-
基于元素节点树的遍历(除去 children外,其它在IE9及以下不兼容)
parentElement- 父元素节点(IE9及以下不兼容)children- 子元素节点node.childElementCount === node.children.length当前元素节点的子元素个数(IE9及以下不兼容)firstElementChild- 第一个元素子节点(IE9及以下不兼容)lastElementChild- 最后一个元素子节点 (IE9及以下不兼容)nextElementSiBling- 下一个兄弟元素 (IE9及以下不兼容)previousElementSiBling- 上一个兄弟元素 (IE9及以下不兼容)
增加
document.createElement();创建元素节点document.createTextNode();创建文本节点document.createComment();创建注释节点document.createDocumentFragment();创建文档碎片节点
插入
-
PARENTNODE.appendChild();在哪个父节点插入节点。类似push操作,把已有的元素插入到另一个元素时,执行的是剪切操作 -
PARENTNODE.insertBefore(a, b);将 a 元素插入到父级元素 b 元素之前, insert a before b
删除
parent.removeChild();父节点删除子节点,返回剪切结果child.remove();子节点调用删除自身的方法,直接删除
替换
-
parent.replaceChild(newEl , originEl); 拿新元素替换旧元素
2. 节点类型 - 节点类型值
-
元素节点 - 1
-
属性节点 - 2
-
文本节点 - 3
-
注释节点 - 8
-
document - 9
-
documentFragment(文档碎片) - 11
3. 节点的四个属性
-
nodeName - 元素的标签名,以大写的形式表示,只读
-
nodeValue - Text 节点或 Comment 节点的文本内容,可读写
-
nodeType - 该节点的类型,只读
-
attributes - Element 节点的属性集合
4. 节点的一个方法
-
Node.hasChildNodes(); - 判断是否有子节点
5.DOM节点树
文档中元素的继承关系,如下图。

-
getElementById方法定义在 Document.prototype 上,即Element节点上不能使用。
-
getElementByName方法定义在HTMLDocument.prototype上,即非html中的document以外不能使用(xml document.Element)
-
getElementByTagName方法定义在Document.prototype 和 Element.prototype 上
-
HTMLDocument.prototype定义了一些常用的属性,body、head分别指代HTML文档中的
<html>和<body>标签document.body: body 标签document.head: head 标签
-
Document.prototype 上定义了 documentElement 属性,指代文档的根元素,在HTML文档中总是指代
<html>元素
-
getElementsByClassName、querySelector、querySelectorAll 在Document、Element类中均有定义。
6. Element 节点的一些属性
-
innerHTML: 写入、读取 HTML结构,默认会覆盖原有内容,可以使用 += 先取值再赋值
-
innerText:(老版本火狐不兼容) / textContent(老版本IE不好使) 写入、读取 文本
7. Element 节点的一些方法
- el.setAttribute('propName', 'propValue'); 写入行间属性,可以设置系统没有的
- el.getAttribute('propName') 读取行间属性
- 改变Class时还可以使用 dom.className
35. Date 日期对象
日期对象中使用的方法都是系统定义好的
语法:
var date = new Date()
Date 对象中的方法
-
date.getDate();返回今天是这个月的第几天 -
date.getDay();返回今天是这周的第几天,从零开始,0 表示 星期天 -
date.getMonth();返回月份,0 ~ 11 操作时需要加 1 -
date.getFullYear();返回四位数的年份 -
date.getHours();返回小时 -
date.getMinutes();返回分钟 -
date.getSeconds();返回秒 -
date.getMilliseconds();返回毫秒 -
date.getTime();返回自 1970 年 1 月 1 日至今的毫秒数(计算机的纪元时间)
时间戳
new Date().getTime(); 作为时间戳
36. 定时器&定时循环器
setInterval定时循环器, 精度不准确
setInterval(function() {
console.log('a');
}, 1000); // 间隔1000毫秒执行一次循环 1000毫秒 == 1秒
var timer = setInterval(function() { // setInterval 会返回一个唯一标识给 timer 用来清除定时循环器
console.log('a');
}, 1000);
clearInterval(timer); // 清除定时循环,可以在方法写在内部,到一定条件时清除
var timer = 1000;
setInterval(function() {
console.log('a');
}, timer); // 这里的 timer 只识别一次,后续修改不起作用
timer = 2000;
setTimeout定时器
var timer = setTimeout(function() {
console.log('时间到了');
}, 1000);
// 使用方法与 定时循环器 相同
37. 获取窗口属性和 dom元素尺寸
查看滚动条的滚动距离
-
window.pageXOffset/pageYOffset-
IE8及以下浏览器不兼容
-
IE8及以下浏览器查看滚动距离
document.body.scrollLeft/scrollTopdocument.documentElement.scorllLeft/Top: ie7/6
-
方法封装
function getScrollOffset () { if(window.pageXOffset) { return { x : window.pageXOffset, y : window.pageYOffset } } else { return { // 兼容IE8及以下浏览器 x : document.body.scrollLeft + document.documentElement.scrollLeft, y : document.body.scrollTop + document.documentElement.scrollTop } } }
-
查看视口尺寸
-
window.innerWidth/innerHeight- IE8 及以下浏览器不兼容
-
document.documentElement.clientWidth/clientHeight- 标准模式下,任意浏览器都兼容
-
document.body.clientWidth/clientHeight- 适用于怪异模式
-
代码封装
function getViewportOffset() { if(!window.innerWidth) { return { w : window.innerWidth, h : window.innerHeight } } else { if(document.compatMode === "BackCompat") { // BackCompat 为怪异模式 return { w : document.body.clientWidth, h : document.body.clientHeight } } else { return { w : document.documentElement.clientWidth, h : document.documentElement.clientHeight } } } }
查看元素的几何尺寸(基本报废)
documentElement.getBoundingClientRect()- 兼容性很好
- 该方法返回一个对象,对象里面有left、top、right、bottom等属性。left和top代表元素左上角的X和Y坐标,right和bottom代表元素右下角的x和y坐标
- height 和 width 属性老版本IE并未实现
- 返回的结果并不是 实时的
查看元素尺寸(视觉上的尺寸,包含padding、border 等宽高)
dom.offsetWidth - dom.offsetHeight
查看元素的位置
dom.offsetLeft - dom.offsetTop- 对于无定位父级的元素,返回相对于文档的坐标。对于定位的父级返回相对于最近的有定位的父级的坐标(只要父级有定位,不论这个距离是如何生成的,margin也可以)
dom.offsetParent- 返回最近的有定位打的父级,如无,返回 body,body.offsetParent 返回 null
设置滚动条滚动
window 上有三个方法
scroll(x, y): x / y 滚动条滚动设置距离的位置scrollTo(x, y): 同上scrollBy(x, y): 会在之前的数据基础之上做累加滚动距离
38. 脚本化CSS
读写元素 css 属性
dom.style.prop-
可读写行间样式(仅限于行间样式),没有任何兼容性问题,碰到 float 这样的关键字属性,前面应加 css
el.style.cssFloatLeft -
复合属性可以最好可以拆解
el.style.border = "1px solid red"; /* 复合属性 */ el.style.borderWidth = "1px"; el.style.borderStyle = "solid"; el.style.borderColor = "red"; -
组合单词变成小驼峰式命名法
// border-width borderWidth = "1px"; backgroundColor = "red"; -
写入的值必须是字符串
-
查询计算样式
-
window.getComputedStyle(elem, null);null: 可以获取指定伪元素样式表- 获取的是元素最后展示的值,计算样式只读,不区分是否是行间、内联和外部
- IE8及以下不兼容
elem.currentStyle.prop- 计算样式只读,IE独有,返回的计算样式的值不是经过转换的绝对值
window.getComputedStyle(elem, null).width; // 获取元素的宽 window.getComputedStyle(box, 'after').width; // 获取元素中伪元素的宽 -
脚本化样式表
document.styleSheets- 该属性存储了 HTML 文档里面所有 CSS 样式表的集合
39. 事件
绑定事件处理函数和执行环境
onclick
var div = document.getElementById('demo');
div.onclick = function() {
console.log('点击了div', this);
}
/*
这种方式是最原始、兼容性最好的绑定方式
缺点就是,只能为dom元素绑定一个 事件处理函数,因为属于赋值,后面的会覆盖之前的
这种编写方式等同于在行间编写事件处理
<div onclick = "console.log('点击了div')">点击了div</div> 这种书写方式被称为 句柄
执行环境:
this 指向元素本身
*/
addEventListener()
// addEventListener('事件类型',处理函数, false) {}
var div = document.getElementById('demo');
div.addEventListener('click', function() {
console.log('点击了div', this);
}, false);
/*
能够给一个事件绑定多个处理函数(不能给同一个函数绑定多个事件),先绑定先执行
IE9及以下不兼容
执行环境:
this 指向元素本身
*/
attachEvent()
// attachEvent('on' + 事件类型, 处理函数)
div.attachEvent('onclick', function() {});
/*
IE独有,和addEventListener类似,多个处理函数时先绑定后执行,同一个函数可以绑定多个事件
执行环境:
this 指向 window
*/
绑定事件方法封装
/**
* 添加事件 处理函数
* @param {elem} DOM元素
* @param {type} 事件类型
* @param {handle} 事件处理函数
*/
function addEvent(elem, type, handle) {
if (elem.addEventListener) {
elem.addEventListener(type, handle, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, handle);
} else {
elem['on' + type] = handle;
}
}
解除事件处理程序
onclick()
elem.onclick = function() { }
elem.onclick = null; // false / "" / null
addEventListener()
div.addEventListener('click', test, false);
div.removeEventListener('click', test, false); // 必须使用事件名来解除
function test() {
console.log('a');
}
attachEvent()
elem.detachEvent('on' + type, fn);
事件处理模型: 事件冒泡 / 捕获
1. 事件冒泡模型
结构上(非视觉上)嵌套关系的元素,会存在事件冒泡功能,即同一事件,自子元素冒泡向父元素。(自底向上)
示例
<style type="text/css">
.wrapper{
width: 300px;
height: 300px;
background-color: rgba(255, 0, 0, 0.8);
}
.content{
width: 200px;
height: 200px;
background-color: rgba(0, 255, 0, 0.8);
}
.aside{
width: 100px;
height: 100px;
background-color: rgba(0, 0, 255, 0.8);
}
</style>
<div class="wrapper">
<div class="content">
<div class="aside"></div>
</div>
</div>
<script type="text/javascript">
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var aside = document.getElementsByClassName('aside')[0];
wrapper.addEventListener('click', function() {
console.log('wrapper');
}, false)
content.addEventListener('click', function() {
console.log('content');
}, false)
aside.addEventListener('click', function() {
console.log('aside');
}, false)
</script>
2. 事件捕获模型
结构上(非视觉上)嵌套关系的元素,会存在事件捕获功能,即同一事件,自父元素捕获至子元素(事件源元素)。(自底向上) 对象上的一个事件类型,只能存在一个事件模型,例如:冒泡 or 捕获
示例
<style type="text/css">
.wrapper{
width: 300px;
height: 300px;
background-color: rgba(255, 0, 0, 0.8);
}
.content{
width: 200px;
height: 200px;
background-color: rgba(0, 255, 0, 0.8);
}
.aside{
width: 100px;
height: 100px;
background-color: rgba(0, 0, 255, 0.8);
}
</style>
<div class="wrapper">
<div class="content">
<div class="aside"></div>
</div>
</div>
<script type="text/javascript">
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var aside = document.getElementsByClassName('aside')[0];
wrapper.addEventListener('click', function() {
console.log('wrapper');
}, true)
content.addEventListener('click', function() {
console.log('content');
}, true)
aside.addEventListener('click', function() {
console.log('aside');
}, true) // 注意这里的第三个参数是 true
// 事件捕获正好是相反的,是由外而内依次触发事件
</script>
执行
- IE 上没有事件捕获
- 一个元素的一个事件类型一个事件处理函数只能存在一个模型
- 如果一个元素、一个事件类型、绑定了两个事件处理函数,则可以同时拥有事件冒泡模型和事件捕获模型,执行顺序是先捕获再冒泡
- 执行顺序: 捕获wrapper - 捕获content - 捕获中执行aside(这两个看代码的执行顺序)冒泡中执行aside - 冒泡content - 冒泡wrapper
- 不冒泡的事件类型
focusblurchangesubmitresetselect
取消冒泡和阻止默认事件
-
取消冒泡
-
W3C标准, IE9以下不支持
el.onclick = function(e) { // “e”: 事件源对象后续补充 e.stopPropagation(); // 取消冒泡事件 } -
IE浏览器独有
el.onclick = function(e) { e.cancelBubble = true; // IE独有 }
-
-
阻止默认事件
-
return false;只能以句柄的方式绑定或el.onclick = fn()的事件才能生效el.onclick = function() { return false; // 阻止默认事件 } -
event.preventDefault();W3C标准, IE9以下不兼容 -
event.returnValue = false;兼容IE -
协议限定符
// 阻止 a 标签的默认事件 <a href="javascript:void()">test</a>
-
事件对象 / 事件源对象
-
事件对象
-
处理函数可以传入一个形参 e,是系统传进来的 event对象
event: 普通浏览器window.event: IE!!!
-
兼容写法
el.onclick = function(e) { var event = e || window.event; // 获取事件对象方法的兼容 }
-
-
事件源对象
-
事件源对象是执行而非捕获
event.target: 火狐独有event.srcElement: IE独有- 以上 Chrome 都有
-
兼容写法
var target = event.target || event.srcElement;
-
事件委托
利用事件冒泡和事件源对象进行处理
- 优点
- 性能 - 不需要循环所有的元素一个个绑定事件
- 灵活 - 当有新的子元素时不需要重新绑定事件
示例
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<script type="text/javascript">
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e) {
var event = e || window.event;
var target = event.target || event.srcElement;
console.log(target.innerText);
}
</script>
事件分类
-
鼠标事件
事件名称 事件描述 事件类别 click在元素上按下并释放任意鼠标按键。 鼠标事件 contextmenu右键点击 鼠标事件 dblclick在元素上双击鼠标按钮 鼠标事件 mousedown在元素上按下任意鼠标按钮。 鼠标事件 mouseup在元素上释放任意鼠标按键。 鼠标事件 mouseenter指针移到有事件监听的元素内 鼠标事件 mouseleave指针移出元素范围外(不冒泡 ) 鼠标事件 mousemove指针移入元素。 鼠标事件 mouseover指针移出元素 鼠标事件 mouseout指针移出元素,或者移到它的子元素上。 鼠标事件 pointerlockchange鼠标被锁定或者解除锁定发生时。 鼠标事件 pointerlockerror可能因为一些技术的原因鼠标锁定被禁止时。 鼠标事件 select有文本被选中。 鼠标事件 wheel滚轮向任意方向滚动。 鼠标事件 -
区分鼠标的左右按键
-
mousedown -
mouseupdocument.onmousedown = function (e) { // 使用onmousedown || onmouseup console.log(e.button); // 使用事件对象中的button属性来判断,左:0 中:1 右:2 }> DOM3标准规定:click 事件只能监听左键,只能通过`mousedown`和`mouseup` 来判断鼠标键
-
-
键盘事件
-
keydown: 键盘按下事件 -
keyup: 键盘抬起事件 -
keypress: 键盘持续触发事件
-
keydown > keypress > keyup -
keydown和keypress的区别:-
keydown可以响应任意键盘按键, -
keypresskeypress只可以监听到字符类键盘按键,返回ASCII码,可以转换成相应字符
-
-
-
文本类事件
-
input: 内容改变监听事件 -
change: 聚焦和失焦时两个状态是否发生改变 -
focus: 元素聚焦 -
blur: 元素失焦
-
窗体类事件(window上的事件)
scroll: 滚动条滚动时触发load: 文档解析并加载完成之后执行脚本(性能低,不推荐)
-
40. 浏览器渲染机制
打开网页时浏览器首先识别 HTML 代码并行成 DOMTree,然后解析 CSS 形成 CSSTree,然后将 DOMTree 和 CSSTree 进行结合形成新的 RenderTree,然后浏览器会根据 RenderTree 绘制页面
-
浏览器资源加载顺序
-
识别 html 代码进行解析,然后形成DOM树
DOMTree/* DOMTree: <html> <head> <body> <div> <span> <stong> 根据节点的所在位置绘制成一个树结构 DOMTree 绘制机制符合**深度优先**原则,直到某一条枝干上没有其他元素,才会更改枝干再进行绘制(解析绘制时不必等元素引用的资源加载完成) */ -
CSSTree
/* DOMTree 创建完成之后,解析CSS并创建CSSTree */ -
RenderTree
/* 将 DOMTree 和 CSSTree 进行结合形成新的 RenderTree,然后浏览器会依据 RenderTree 进行绘制页面 */
-
-
重排 reflow
-
DOM节点的添加、删除,DOM节点的宽高变化、位置变化,display、offsetWidth、offsetHeight 都会致使 RenderTree 重构,极大损耗了性能
-
-
重绘 repaint
- 修改字体颜色、背景颜色等等会触发重绘,它会致使 renderTree 部分改变,想对 reflow 会好很多