0.介绍
0.1介绍
浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)
MDN-Web API
0.2组成结构
ECMAScript-JavaScript的核心
定义了javaScrip的语法规范
javaScrip的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关BOM浏览器对象模型
一套操作浏览器功能的API
通过BOM可以操作浏览器窗口,比如:弹出窗,控制浏览器跳转,获取分辨率等DOM文档对象模型
一套操作页面元素的API
DOM可以把HTML看作是文档树,通过DOM提供的API可以对树上的节点进行操作
DOM
1.1 DOM概念
文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。
DOM是一种基于树的API文档,他在处理过程中整个文档都加载到内存中。
DOM文档树模型如下
- 文档:一个网页可以成为文档
- 节点:网页中的所有内容都是节点(标签,属性,文本,注释等)
- 元素:网页中的标签
- 属性:标签的属性
DOM经常进行的操作
- 获取元素
- 对元素进行操作(设置其属性或调用其防范)
- 动态创建元素
- 事件(给元素绑定事件)
1.2 document获取标签
HTML是从上向下加载,如果把js写在head中,在HTML还没有加载完全就去获取dom元素会找不到
console.dir(box) 输出对象
1 | var box = document.getElementById('box'); |
获取对象类型两个方法
- typeof
typeof不能获取对象的具体类型,获取对象类型始终返回object construction
对象.construction可以获取到具体对象根据id获取元素
1
2
3var div = document.getElementById('main');
console.log(div)
// 获取到的数据类型HTMLDivElement,对象都是有类型的根据标签名称获取元素
返回的是节点集合1
2
3
4
5var divs = document.getElementsByTagName('div');
for(var i = 0;i < divs.length;i++){
var div = divs[i];
console.log(div)
}根据name获取元素
返回的是节点集合1
2
3
4
5var divs = document.getElementsByName('sex');
for(var i = 0;i < divs.length;i++){
var div = divs[i];
console.log(div)
}根据class获取元素
返回的是节点集合这个是IE9以后才支持的(class是关键字,所以采用classname)
1
2
3
4
5var divs = document.getElementsByClassName('sex');
for(var i = 0;i < divs.length;i++){
var div = divs[i];
console.log(div)
}HTML5中获取元素
在HTML5中新增的元素获取方法querySelector
查找一个元素,如果多个只返回第一个
querySelector1
2
3
4
5
6
7
8
9
10
11
12
13
14// class="lover"
var div = document.querySelector('.lover')
// 获取p元素
var node = document.querySelector("p");
// 通过id属性取得元素
var oBox = document.querySelector('#box');
//通过属性选择器取得元素
var oTest = body.querySelector('[title="test"]');
// 获取class=”lover” 的第一个p元素
var node = document.querySelector("p.lover");
// 获取第一个带target属性的a元素
var node = document.querySelector("a[target]");
// 获取第一个输入框
var node = document.querySelector("input[type=text]");
查找多个元素
querySelectorAll1
2
3
4
5
6
7
8//取得所有div元素
var div = document.querySelectorAll("div")
//取得所有class为"in"的元素
var oIn = document.querySelectorAll('.in');
//取得title属性为test的元素
var oTest = body.querySelectorAll('[title="test"]');
//取得body元素
var body = document.querySelectorAll("body")[0]
总而言之,这个方法相对于上面方法就是取多个
1.3 绑定事件
事件的三要素:
- 事件源(谁能触发事件)
- 事件名称(怎么能触发,点击还是经过等)
- 事件处理函数
1 | // 获取超链接 |
案例
1 | // 1.获取元素 |
1.4 this
函数中的this
window对象1
2
3function fn(){
console.log(this);
}方法中的this
调用该方法的对象1
2
3
4
5
6
7var obj = {
name:'zs',
say:function(){
console.log(this);
}
}
obj.say();构造函数中的this
当前对象事件处理函数中的this
触发事件的对象,事件源
案例
1 | // 1.获取所有a标签对应的元素 |
1.5 innerHTML和innerText
获取标签之间的内容
- innerHTML获取内容的时候,会把标签也获取到
- innerText获取内容的时候会把标签过滤掉
设置标签之间的内容
- innerHTML设置内容的时候,会把特殊字符进行标签化处理
- innerText不会进行标签化处理
案例
1
2
3box.innerHTML = "linjia<b>d</b><p>这是一个段落</p>"
box.innerHTML = "linjia<b>d</b><p>这是一个段落</p>"
box.innerText = "linjia<b>d</b><p>这是一个段落</p>"- < <
- < >
-   空格
- © @
innerHTML 不是标准的DOM属性
innerText 最早IE中增加的属性,老版本的firefox不支持此属性,老版本的firefox用的是textContent
- 案例
写一个函数,处理innerText和textContent的兼容性问题1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 获取元素之间的内容
function getInnerText(element) {
// 判断element是否支持innerText
if(typeof element.innerHTML === "string"){
return element.innerHTML;
}else{
return element.textContent;
}
}
// 设置元素之间的内容
function setInnerText(element,content) {
// 判断element是否支持innerText
if(typeof element.innerHTML === "string"){
element.innerHTML = content;
}else{
element.textContent = content;
}
}
1.6 表单元素属性
设置文本框中的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// 1. 设置文本框中的内容
var inputs = document.getElementsByTagName("input");
var i = 0,len = inputs.length;
for(;i <len;i++){
// 获取集合中的元素
var input = inputs[i];
// 判断当前的input是否是文本框
if(input.type === "text"){
input.value = i + 1;
}
}
// 2. 点击按钮获取文本框中的内容,并用|分割形成一个新的字符串
var btn = document.getElementById("btn");
btn.onclick = function() {
// 找到所有的文本框,获取文本框的值
var str = '';
// 定义数组,避免拼接字符串耗费性能
var arr = [];
for (i = 0;i<len;i++){
var input = inputs[i];
// 判断是否是文本框
if(input.type !== "text"){
continue;
}
// str += input.value + "|";
arr.push(input.value);
}
// str = str.substr(0,str.length - 1);
str = arr.join("|");
console.log(str);
}切换图片
1 | // 记录索引 默认是0 |
检测用户名密码
检验用户名是3-6位,密码是6-8位,如果不满足条件要求高亮显示1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 1 给登录按钮注册点击事件
var btnLogin = document.getElementById("btnLogin");
btnLogin.onclick = function() {
// 检测用户名是否是3-6位,不满足要求高亮显示文本框
// 文本框对应元素
var texName = document.getElementById("name");
if(texName.value.length < 3 || texName.value.length > 6){
// 文本框高亮显示(这里需要先定义css)
texName.className = 'bg';
// 阻止表单提交
return false;
}else{
texName.className = '';
}
// 3 密码是否是6-8位 不满足高亮显示
var texPwd = document.getElementById("pwd");
if(texPwd.value.length < 6 || texPwd.value.length > 8){
// 文本框高亮显示(这里需要先定义css)
texPwd.className = 'bg';
// 阻止表单提交
return false;
}else{
texPwd.className = '';
}
}输入框默认值
当文本框获得焦点的时候onfocus,如果文本框中的内容是《请输入搜索关键字》则清空文本框,设置黑色字体onfocus 获得焦点事件
onblur 失去焦点事件
1 | // 提取公共方法 |
- checkbox全选,反选
1 | // 1. 点击全员按钮(父的checkbox)的时候,让子的checkbox的选中状态跟其保持一致 |
写到这里会有问题,当反选时无法控制父checkbox
所以我们把父checkbox的方法单独拿出来,修改代码如下
1 | // 1. 点击全员按钮(父的checkbox)的时候,让子的checkbox的选中状态跟其保持一致 |
1.7 自定义属性
- getAttribute() 获取标签内属性
- setAttribute() 设置标签内属性
- removeAttribute() 删除标签内属性
- 与element.属性的区别:上诉三个方法用于获取任意的行内元素
案例
1 | <div id="box" stuId="1">张三</div> |
1 | var box = document.getElementById("box"); |
1.8 操作样式
设置style仅仅是操作style的样式并不能改变class写好的样式
- 设置样式类(需要提前写class)
- 设置style
- 设置style.cssText
1 | var box = document.getElementById("box"); |
- 案例:开关灯
document.body.style
1 | // 1. 给按钮注册事件 |
- 案例:鼠标经过显示二维码图片
鼠标经过的事件 onmouseover 鼠标离开事件 onmouseout
1 | // 获取nodeSmall注册事件 鼠标经过的事件 onmouseover 鼠标离开事件 onmouseout |
- 案例:移动方块
1 | document.getElementById("btn").onclick = function() { |
- 案例:输入框输入时高亮
.onfocus获得焦点 .onblur 失去焦点
1 | // 给所有的文本框注册事件 获取焦点的事件 |
- 案例:各行变色,鼠标放上变高亮
1 | // 隔行变色 奇数red 偶数 green |
- 案例:切换TAB
1 | // 1. 鼠标放到tab栏上高亮显示,其他tab取消高亮显示 |
1.9 节点操作
想要构建如图所示的结构
- 元素节点
- nodeName 标签的名称
- nodeType 节点的类型 1 元素节点
- nodeValue 当是元素节点的时候nodeValue始终是null
1 | // 元素节点 |
- 模拟DOM文档的结构
1 | // 模拟DOM文档的结构 |
- 获取子节点
- childNodes 所有子节点
- children 子元素
- parentNode 父元素
1 | var box = document.getElementById("box"); |
- 获取第一个/最后一个节点
- .firstChild 第一个子节点
- .firstElementChild 第一个子元素
- .lastChild 最后一个子节点
- .lastElementChild 最后一个子元素
1 | var ul = document.getElementById("ul"); |
由于firstElementChild存在兼容性问题,我们可以自己封装一个方法来解决兼容性
1 | function getFirstElementChild(parent) { |
- 获取兄弟节点
- .nextSibling 下一个兄弟节点
- .nextElementSibling 下一个兄弟元素
- .previousSibling 上一个兄弟节点
- .previousElementSibling 上一个兄弟元素
1 | // 获取下一个兄弟节点 |
2.0 a标签href
href=”javascript:void(0)”
这里有两部分首先是javascript表示我下面要说的都是用js来解析
还有其他如下
- javascript: 用js来解析
<a href="javascript:void(0)">首页</a>
- http:下面是连接,连接跳转
<a href="http://www.baidu.com">首页</a>
- mailto: 下面是邮箱
<a href="mailto://xxx@xx.com">首页</a>
- tel: 下面是手机号码拨打电话(在手机上使用)
<a href="tel://00000>首页</a>
void 关键字 void会执行()里面的语句表达式,总是返回undefined
就是什么都不做
1 | var r = void(1+2); |
案例:高亮显示列表,并屏蔽a标签的点击事件
1 | // 获取menu中所有的a |
1 | // 事件处理函数 |
2.1 document.write()
把整个页面都重写,只剩下write中的内容
- 性能问题
- innerHTML方法由于会对字符串进行解析,需要避免在循环内多次使用。
- 可以借助字符串或数组的方式进行替换,再设置给innerHTML
- 优化后与document.createElement性能相近
1 | // 点击按钮 |
innerText具有兼容性问题,这里我们下一个函数,处理兼容性问题
1 | // 获取元素之间的内容 |
对比性能
- 第一种方法(循环innerHTML)
console.time('flag')
console.timeEnd('flag')
查询开始到结束的时间
1 | console.time('flag') |
- 第二种方法(利用数组innerHTML)
1 | console.time('flag') |
- 第三种方法(利用document.createElement)
.removeChild() 移除元素
var r = confirm(“是否确定删除?”);
.appendChild() 添加子节点
document.createElement 创建节点
下面我们实现一个表格Table1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75// 数据行中的数据
var bodyData = [
{name:'zs',subject:'语文',score:100},
{name:'ls',subject:'数学',score:80},
{name:'sdb',subject:'英语',score:0},
{name:'gw',subject:'体育',score:19},
{name:'bzr',subject:'数学',score:50},
]
// 表头数据
var headData = ["姓名","科目","成绩","操作"];
// 1.表头
var table = document.createElement("table");
document.getElementById("box").appendChild(table);
// 设置table样式
table.border = "1px";
table.width = "500px";
table.cellSpacing = 0;
var tead = document.createElement("tead");
table.appendChild(tead);
var tr = document.createElement("tr");
tead.appendChild(tr);
// 设置tr样式
tr.style.height = "50px";
tr.style.backgroundColor = "lightgray"
// 生成表头中的列
headData.forEach(function(item) {
var th = document.createElement('th');
tr.appendChild(th);
th.innerText = item;
})
// 2. 数据行
var tbody = document.createElement("tbody");
table.appendChild(tbody);
// 设置数据行的内容居中
tbody.style.textAlign = "center";
// 遍历数据
bodyData.forEach(function(item) {
// 创建行
// 便利对象
for(var key in item){
var td = document.createElement("td");
tr.appendChild(td);
td.innerText = item[key];
}
// 操作的列
td = document.createElement("td");
tr.appendChild(td);
// 创建删除超链接
var link = document.createElement("a");
link.href = "javascript:void(0)";
td.appendChild(link);
link.innerText = "删除";
// 给link注册删除事件
link.onclick = linkClick;
})
// 事件处理函数
function linkClick() {
// 提示
var r = confirm("是否确定删除?");
if(r){
// 删除
// 获取点击按钮所在的行
var tr = this.parentNode.parentNode;
tbody.removeChild(tr);
}
}
- 测试3种方式
1 | var d1 = +new Date(); |
1 | var d1 = +new Date(); |
1 | var d1 = +new Date(); |
2.2 操作节点
insertBefore(div1,div2) 在div2前插入div1
removeChild(div) 删除div节点
replaceChild(div1,div2) 把div2替换成div1
在ul中第三个位置之前插入li1
2
3
4
5
6var ul = document.getElementById("ul");
var li = document.createElement("li");
li.innerText = "测试";
ul.insertBefore(li,ul.children[2])
替换元素1
2
3
4var ul = document.getElementById("ul");
var div = document.createElement("div");
div.innerText = "div";
ul.replaceChild(div,ul.children[0]);
删除ul中所有li
如果使用
innerHtml=""
,不会删除元素身上的事件对应的函数,容易造成内存泄漏
1 | var ul = document.getElementById("ul"); |
案例:权限选择
<select id="select" multiple="multiple">
这里还有第一点需要说明,循环document.get出list的时候,如果删除了其中一个。document.get的length会改变,里面的元素会重新创建索引,这时候在删除第二个(刚才删除第一个)实际上删除的是刚才的第三个
如果appendChild是其他地方的元素,在appendChild的同时原来的元素就没有了
1 | // 存放所有权限的列表 |
2.3 注册事件
注册事件的三种方式
- onclick
如果有一个元素两次onclick,第二次会覆盖第一次。
1 | var box = document.getElementById("box"); |
- addEventListenner(W3C标准)
box.addEventListener(“click”,eventCode,false)
- 第一个参数 事件名称(不带on)
- 第二个参数 事件处理函数
- 第三个参数 事件冒泡(false)/事件捕获(true)
1 | var box = document.getElementById("box"); |
- attachEvent(IE9以前)
1 | var box = document.getElementById("box"); |
注册和移除事件处理兼容性问题
1 | // 注册事件 |
2.4 事件流(冒泡/ 捕获)
DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。
事件的三个阶段/事件流
- 事件捕获阶段
- 目标阶段
- 冒泡阶段
冒泡型事件流:事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。【推荐】
捕获型事件流:事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子
阻止事件冒泡 e.stopPropagation();
1 |
|
在冒泡型事件流中click事件传播顺序为<div>—》<body>—》<html>—》document
在捕获型事件流中click事件传播顺序为document—》<html>—》<body>—》<div>
2.5 事件委托
把本来应该li做的事情,转交给父元素来做(事件冒泡)
1 | var ul = document.getElementById("ul"); |
2.6 事件对象属性和方法
- event.type 获取事件类型
- clientX/clientY 所有浏览器都支持,窗口位置
- pageX/pageY IE8以前不支持,页面未知
- event.target||event.srcElement用于获取触发事件的元素
获取是什么事件触发的方法
1 | var box = document.getElementById("box"); |
案例:根据鼠标位置改变图片位置
这里有个坑,
document.onmousemove
和document.body.onmousemove
的区别
body有效范围是bodyn内部元素的范围,这里就是图片那么大的范围
clientX和clientY互殴的是鼠标在可视区域中的位置
pagX和pagY获取的是在页面中的位置
1
2
3
4
5
6
7
8
9
10
11
12 var img = document.getElementById("img");
document.onmousemove = function(e) {
e = e || window.event;
img.style.position = "absolute";
// img.style.left = e.clientX + "px";
// img.style.top = e.clientY + "px";
// 获取鼠标在页面中的位置
img.style.left = e.pageX + "px";
img.style.top = e.pageY + "px";
}
如果上下左右有滚动条时1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var img = document.getElementById("img");
document.onmousemove = function(e) {
e = e || window.event;
img.style.position = "absolute";
img.style.left = getPage(e).pageX + "px";
img.style.top = getPage(e).pageY + "px";
}
// 获取鼠标在页面上的坐标
function getPage(e){
return{
pageX:e.clientX + getScroll().scrollLeft,
pageY:e.clientY + getScroll().scrollTop
}
}
// 获取页面滚出去的距离(处理兼容性)
function getScroll() {
return{
scrollTop:document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft:document.documentElement.scrollLeft || document.body.scrollLeft,
}
}
2.7 阻止事件传播方式
- 标准方式:event.stopPropagation();
- IE低版本:event.cancelBubble = true; 标准中已废弃
1 | var link = document.getElementById("link"); |
event.stopPropagation()用法介绍
- 该方法将停止事件的传播,阻止它被分派到其他 Document 节点。在事件传播的任何阶段都可以调用它。
event.preventDefault()用法介绍
- 将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。
2.7 常用的鼠标键盘事件
- onmouseup 鼠标按键放开时触发
- onmousedown 鼠标按键按下触发
- onmousemove 鼠标移动触发
- onkeyup 键盘按下触发(这个阻止方法执行,否则down来不及)
- onkeydown 键盘按键抬起触发
1 | var txt = document.getElementById("txt"); |
想让用户按下空格input光标换到下一个input
use strict
第一种思路把keyCode改成91
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a = 5;// 严格模式下必须先声明变量再使用
function f() {
console.log(this);// 严格模式下this是undefined
}
var texts = document.querySelectorAll("input[type = text]";)
var i=0,len = texts.length;
for(;i<len;i++){
var txt = texts[i];
txt.onkeydown = txtKeydown;
}
function txtKeydown() {
// 13回车 9 tab
if(e.keyCode === 13){
e.keyCode = 9;
}
}
如果加了严格模式会报错,因为keyCode是只读属性
当在文本框中按下回车,让下一个文本框获取焦点
nextElement.focus();
1 |
|
最后更新: 2019年01月14日 21:06