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