DOM编程

DOM树


DOM: Document Object Model

使用对象的方式描述对应的html

API 规范,

浏览器中的JS 包含 DOM 通过JS 调用 API 操作浏览器操作 DOM树。

DOM 树:节点(node) 层次,文档节点(document)、元素节点、属性节点、文本节点。
DOM 把一个文档表示为一颗家谱树(父、子、兄弟)

  • 将一段html代码转换成DOM节点树

节点遍历

以p节点为参考对象node,

node.parentNode = body

node.firstChild = hello,

node.lastChild = img

node.previousSibling = null

node.nextSibling = div

节点类型

element_node 元素节点(body, p, div, span, img)

text_node 文本节点(hello, mooc, 为专业)

comment_node

document_type_node

元素遍历

p 的第一个元素节点是 em,p的最后一个元素节点是 a

em.nextElementSibling = a,em.previousElementSibling = null

如何兼容浏览器版本的element.children


element.children能够获取元素的元素子节点,但是低版本的ie不支持,如何在低版本的ie上兼容类似的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getElementChildren(element) {
// 如果支持element.children,直接返回
if (element.children) {
return element.children;
} else {
var list = []; // 声明一个数组用以存放之后获取的子节点
var nodelist = element.childNodes; // 初始化接受参数的子节点集合
for (var i = 0; i < nodelist.length; i++) {
if(nodelist[i].nodeType == 1){
// 若节点的元素类型属于1,即元素节点,存入数组
list.push(nodelist[i]);
}
}
return list;
}
}

节点操作

—

使用html表示页面的结构,浏览器读取html,并解析之后,能够生成页面的结构,然后再读取CSS,最后渲染出我们能看到的页面。页面渲染之后,如何修改页面结构。

2019-08-13 at 12.09 P

1
2
3
4
5
<div id="mydiv">
<span id="span1">1</span>
<span id="span2">2</span>
<span id="span3">3</span>
</div>
1
2
3
4
5
6
7
8
var mydiv = document.querySelector('#mydiv');

mydiv.firstChild; // #text
mydiv.firstElementChild; // span#span1
mydiv.lastChild; // #text
mydiv.lastElementChild; // span#span3
mydiv.childNodes; // 包括所有的 #text
mydiv.children; // span#span1, span#span2, span#span3

节点还包括 text 节点,标签与标签之间的文本信息,也算一个结点。
元素节点单指标签。同理:

1
2
3
4
5
var span1Ele = document.querySelector('#span1');

span1Ele.parentNode; // 父节点
span1Ele.nextSibling; // #text
span1Ele.nextElementSibling; // span#span2

节点方法名:

方法名 返回值类型 说明
hasChildNodes() Boolean 当前节点是否有子节点
appendChild(node) Node 往当前节点上添加子节点
removeChild(node) Node 删除子节点
replaceChild(oldNode, newNode) Node 替换子节点
insertBefore(newNode, refNode) Node 在指定节点前插入新结点

小练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<span id="span1">span1</span>
<span id="span2">span2</span>

<div id="div1" style="background: palegreen">1</div>
<div id="div2" style="background: palegoldenrod">2</div>
<div id="div3" style="background: palevioletred">3</div>

<button onclick="fun1()">把 span1 添加到 div1</button>
<button onclick="fun2()">把 span2 添加到 div2</button>
<button onclick="fun3()">创建span元素添加到div3</button>
<br/>
<select id="character" size="7">
<option id="item1">刘备</option>
<option id="item2">关羽</option>
<option id="item3">张飞</option>
<option id="item4">赵云</option>
</select>
<button onclick="fun4()">在关羽之前插入诸葛亮</button>
<button onclick="fun5()">把关羽换成魏延</button>
<button onclick="fun6()">删除张飞</button>
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function fun1() {
var span1 = document.querySelector('#span1');
var div1 = document.querySelector('#div1');
div1.appendChild(span1); // 将一个 ele node 拼接到另一个 ele node 后面
}
function fun2() {
var span2 = document.querySelector('#span2');
var div2 = document.querySelector('#div2');
div2.appendChild(span2); // 将一个 ele node 拼接到另一个 ele node 后面
}
function fun3() {
var span3 = document.createElement('span');
span3.innerHTML = "span3";
span3.id = 'span3'
var div3 = document.querySelector('#div3');
div3.appendChild(span3); // 将一个 ele node 拼接到另一个 ele node 后面
}
function fun4() {
var sels = document.querySelector('#character');
for (var childrenKey in sels.children) {
var op = sels.children[childrenKey];
if (op.innerHTML == '关羽') {
var option = document.createElement('option');
option.id = 'item' + childrenKey;
option.innerHTML = "诸葛亮";
sels.insertBefore(option, op);
break;
}
}
}
function fun5() {
var sels = document.querySelector('#character');
for (var childrenKey in sels.children) {
var op = sels.children[childrenKey];
if (op.innerHTML == '关羽') {
var option = document.createElement('option');
option.id = 'item' + (parseInt(childrenKey) + 1);
option.innerHTML = "魏延";
sels.replaceChild(option, op);
break;
}
}
}

function fun6() {
var sels = document.querySelector('#character');
for (var childrenKey in sels.children) {
var op = sels.children[childrenKey];
if (op.innerHTML == '张飞') {
sels.removeChild(op);
break;
}
}
}

获取节点


通过已获取到的节点的DOM数,可以获取节点

  • 父子关系:

    • parentNode
    • firstChild/lastChild/childNodes
    • childNodes/children
  • 兄弟关系:

    • previousSibling/nextSibling
    • previousElementSibling/nextElementSibling

这样获取节点的可维护性差


getElementById, 返回一个元素

getElementsByTagName, 返回集合

返回值是动态的,对节点操作,会影响其取值的变化。

通过这个方法可以获取指定元素下所有的后代元素。

getElementsByClassName, 返回集合

collection,返回的是集合,集合是动态的, 如果删除掉其中的一个值,则collection 中的值也会改变。

通过空格分隔,指定多个类名。和类名的顺序没什么关系。

ie 6 7 8 并不兼容 getElementsByClassName

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
33
function getElementByClassName(element, classNames) {
if (element.getElementByClassName) { // 侦测特性
return element.getElementByClassName(classNames);
// 优先使用W3C规范
} else {
// 如果没有W3C规范,先获取所有后代元素
var elements = element.getElementsByTagName('*');

var result = [];
var element, classNameStr, flag;
classNames = classNames.split(' ');

for(var i = 0; element = elements[i]; i++) {
classNameStr = ' ' + element.className + ' ';
flag = true;

for(var j = 0, className; className = classNames[j]; j++) {
if (classNameStr.indexOf(' ' + name + '') == -1) {
flag = false;
break;
}
}
if (flag) {
result.push(element);
}
}
return result;
}
}

function hasClassName(element, className) {
return element.className == className;
}

querySelector/ALL

list = element.querySelector/All(selector)

list 不是动态的,反映在如果获取了节点所有的元素,如果我们删掉了其中一个。list内部的值并未改变。

前者获取第一个符合条件的元素,后者获取所有符合条件的元素。

此时,users 获取到的是 div单个元素 div#users

1
2
3
4
users.querySelectorAll(".user")
// 获取所有的 class 里面有 user 的 节点。 [li.user li.user li.user.last]
document.querySelectorAll(#users .user");
// 具有 id = user 中的 class = user 的 所有元素

querySelector 返回的列表是静态的!

live: 动态集合

sole: 单一的

only document: 节点

创建节点


创建节点:

createElement,

element = document.createElement(tagName)

创建指定标签的元素。

设置节点内容


element.textContent 节点及其后代节点的文本内容。ie9 不支持

element.innerText 节点及其后代节点的文本内容。 常用(不规范, ff 不支持)

平台兼容方案:

插入节点


appendChild

var achild = element.appendChild(achild);

在指定元素的最末尾,追加元素

insertBefore,

var achild = element.insertBefore(achild, referenceChild);

将节点插入到指定元素的前面。

ul.insertBefore(li, ul.firstChild);

删除节点


removeChild,

child = element.removeChild(child)

删除element 指定的子元素

user2.parentNode.removeChild(user2);

innerHTML 提高效率


节点的 HTML 内容,建议仅用于新节点。内容可控,不是用户填写的内容,如果是用户填写的内容也要确保内部没有标签

低版本的ie 内存泄露,

安全问题:

修改属性


每一个html属性对应相应的 DOM 对象属性

HTML attribute -> DOM property

input.id input.type input.className

label.htmlFor = username

属性访问器 来操作对象


读取属性

input.className input[“id”]

属性写

input.value = ‘wwq@163.com

其实是在 标签内加入了 value 属性 并且赋值为 wwq@163.com

input.disabled = true;

input.className = “u-txt” String

input.maxLenth = 10 Number

input.disabled = true Boolean

input.onclick = function onclick(event){} Function

通过属性访问修改的属性,会转会进行类型转换,从字符串 转换到实用对象

用属性访问器来操作对象

通用性比较差, 名字异常

扩展性 比较差

取到的是实用对象,可以直接实用。

用 get / setAttribute 来操作对象


var attribute = element.getAttribute(attributeName);

input.getAttribute(“class”) // “u-txt”

element.setAttribute(name, value);

input.setAttribute(“value”, “wwq@163.com“);

input.setAttribute(“disabled”, “”);

get / setAttribute 来操作对象 纯粹是字符串的操作

get / setAttribute 来操作对象 特点

仅字符串 通用性好

案例:

为了防止重复点击登录按钮,重复发送请求,要在点击之后,改变登录按钮的样式

两种方式都能进行属性改变,如果是纯字符串操作,可以使用 get/setAttribute 如果是 实用类型,则实用属性访问器

dataset


HTMLElement.dataset

data-* 属性集

用途: 元素上保存数据

例子:

如何实现浏览器兼容版的 element.dataset

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
/**
* 兼容实现 dataset 属性
* @param {[Element]]} element [HTML元素]
* @return {[Object]} dataset [dataset对象]
*/
function getDataset(element) {
// if (element.dataset) return element.dataset;

element.dataset = {};
var attrs = element.attributes; // 获取属性集合

for(var i = 0; i < attrs.length; i++) {

if (/^data-/.test(attrs[i].name)) { // 如果属性名满足 data-*
// 捕获*的内容
var key = attrs[i].name.match(/^data-(.+)/)[1];
// 获取属性值
var value = attrs[i].value;

// 将-后面的第一个字母大写
key = key.replace(/-\w/g,function(match) {
return match.substring(1).toUpperCase();
});
element.dataset[key] = value;
}
}
return element.dataset;

}

样式操作


js 动态修改样式

CSS 的外部样式链接表:获取元素

获取内部样式:element.sheet

以上两种样式链接表可以通过 document.styleSheets 获取

CSSStyleDeclaration 是CSS属性名和属性值键值对的列表。

获取标签内部样式:element.style

更新样式


标准做法一:style.cssText(这种方式不够好)

标准做法二:更新class

换肤(更换样式表)

一次性更新很多元素

更换样式表:$(‘skin’).href = “skin.summer.css”;

1
2
3
function $(id) {
return document.getElementById(id);
}

获取样式


element.style 对应元素内嵌样式表 这样没办法获取到实际样式

只有设置了内嵌 style 属性的元素,才能通过element.style获取到元素的样式。

通过 window.getComputedStyle()

var style = window.getComputedStyle(element, pseudoElt]);

返回 style 是一个只读的 CSSStyleDeclaration 类型的属性

window.getComputedStyle(element).color // “rgb(0, 0, 0)”

获取到实际的颜色

ie9 不支持, ie9 会用element.currentStyle 做兼容

CSS DOM overview(概览)


StyleSheetList[0] 获取到具体样式表 -> CSSStyleSheet

如何实现兼容版的 window-getComputedStyle


1
2
3
4
5
6
7
8
function getStyle(element) {
//特性侦测
if(window.getComputedStyle) {
return window.getComputedStyle(element);
} else if(element.currentStyle) {
return element.currentStyle;
}
}

实现getStyle函数


getStyle函数用于获取元素的实际样式,语法如下:

var cssPropertyValue = getStyle (element, cssPropertyName);

使用示例如下:
getStyle(element, “color”) 返回element元素的显示颜色,如:”rgb(0, 0, 0)”
getStyle(element, “line-height”) 返回element元素的实际行高,如:”30px”
请实现getStyle函数,要求浏览器兼容。

1
2
3
4
5
6
7
function getStyle(element,attr) {
if(window.getComputedStyle) {
return getComputedStyle(element)[attr];
} else {
return element.currentStyle[attr];
}
}

DOM 事件


  • 事件流
  • 事件注册
  • 事件对象
  • 事件分类
  • 事件代理

什么是 DOM 事件?

点击一个 DOM 事件、键盘按下一个键等等

需要记住的事件属性


  • onblur 元素失去焦点
  • onclick 鼠标点击
  • onchange 文本域改变、用户改变域的内容

JS事件参考手册

小练习:

1
<img id="img" src="lady.jpg" width="400" height="300"/>
1
2
3
4
var imgEle = document.querySelector('#img');
imgEle.onmouseover = function () {
this.src = 'supperman.png';
}

练习1

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
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function checkAll(flag) {
var all = document.getElementsByName('hobby')
var checkAll = document.querySelector('#checkAll')
checkAll.checked = flag;
for (var allKey in all) {
all[allKey].checked = flag;
}
}

function checkUnAll() {
var all = document.getElementsByName('hobby')
var checkAll = document.querySelector('#checkAll')
var flag = true;
for (var allKey in all) {
all[allKey].checked = !all[allKey].checked
if (all[allKey].checked == false) {
flag = false;
}
}
checkAll.checked = flag
}
</script>
</head>
<body>
请选择你的爱好:
<input type="checkbox" onchange="checkChange()" id="checkAll"/>全选/全不选 <br/>
<div>
<input type="checkbox" name="hobby"/>JAVA&nbsp;
<input type="checkbox" name="hobby"/>打篮球&nbsp;
<input type="checkbox" name="hobby"/>上网&nbsp;
<input type="checkbox" name="hobby"/>撩妹&nbsp;
</div>
<div>
<input type="button" id="btn_checkAll" onclick="checkAll(true)" value="全选"/>
<input type="button" onclick="javascript:checkAll(false)" value="全不选"/>
<input type="button" onclick="checkUnAll()" value="反选"/>
</div>
</body>
</html>

练习2:

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
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function moveSelected(id1, id2) {
var selectSrc = document.querySelector('#' + id1);
var selectDir = document.querySelector('#' + id2);
var options = selectSrc.selectedOptions;
while (options.length > 0) {
selectDir.appendChild(options[0])
}
}

function moveAll(id1, id2) {
var selectSrc = document.querySelector('#' + id1);
var selectDir = document.querySelector('#' + id2);
var options = selectSrc.options;
while (options.length > 0) {
var ele = selectSrc.options[0]
selectDir.appendChild(ele)
}
}
</script>
</head>
<body>
<table border="1">
<tr>
<td width="50">
<select id="select1" size="10" multiple="multiple">
<option value="选项1">选项1</option>
<option value="选项2">选项2</option>
<option value="选项3">选项3</option>
<option value="选项4">选项4</option>
<option value="选项5">选项5</option>
<option value="选项6">选项6</option>
<option value="选项7">选项7</option>
<option value="选项8">选项8</option>
<option value="选项9">选项9</option>
</select>
</td>
<td align="center">
<input type="button" onclick="moveSelected('select1', 'select2')" value="-->"/><br/>
<input type="button" onclick="moveAll('select1', 'select2')" value="==>"/><br/>
<input type="button" onclick="moveSelected('select2', 'select1')" value="<--"/><br/>
<input type="button" onclick="moveAll('select2', 'select1')" value="<=="/>
</td>
<td width="50">
<select id="select2" size="10" multiple="multiple">

</select>
</td>
</tr>
</table>
</body>
</html>

jqury 版本:

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
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
76
77
78
79
80
81
82
83
84
85
86
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="static/jquery/jquery-1.11.3.min.js"></script>
<script>
$(function () {
var checkHobbyState = function() {
// 只有4个爱好选中时 修改全选的标识
var sum = true;
$('input[name=hobby]').each(function (index, domEle) {
sum = sum && $(domEle).prop('checked')
})
$('#checkAll').prop('checked', sum)
}

$('#checkAll').change(function () {
$('input[name=hobby]').prop('checked', $(this).prop('checked'))
})

$('#btn_checkAll').click(function () {
$('input[name=hobby]').prop('checked', true)
checkHobbyState()
})

$('#btn_checkUnAll').click(function () {
$('input[name=hobby]').prop('checked', false)
checkHobbyState()
})

$('#btn_checkReverse').click(function () {
$('input[name=hobby]').prop('checked', function (index, checkedValue) {
return !checkedValue
})
checkHobbyState()
})
})

// foreach 底层原理
Array.prototype.foreach2 = function (fn) {
for (var i = 0; i < this.length; i++) {
fn(this[i], i, this)
}
}

var arr = ['1', '2', '3']
arr.forEach(function (value, index) {
console.log(value);
console.log(index);
})

// map 底层原理
Array.prototype.map2 = function (fn) {
var newArr = []
for (var i = 0; i < this.length; i++) {
var result = fn(this[i], i, this)
newArr.push(result)
}
return newArr
}

var newA2 = arr.map2(function (value, index, array) {
return '1' + value
})

console.log(newA2);

</script>
</head>
<body>
请选择你的爱好:
<input type="checkbox" id="checkAll"/>全选/全不选 <br/>
<div>
<input type="checkbox" name="hobby"/>JAVA&nbsp;
<input type="checkbox" name="hobby"/>打篮球&nbsp;
<input type="checkbox" name="hobby"/>上网&nbsp;
<input type="checkbox" name="hobby"/>撩妹&nbsp;
</div>
<div>
<input type="button" id="btn_checkAll" value="全选"/>
<input type="button" id="btn_checkUnAll" value="全不选"/>
<input type="button" id="btn_checkReverse" value="反选"/>
</div>
</body>
</html>

练习3:

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
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
76
77
78
79
80
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
window.onload = function () {
var removeAll = document.querySelector('#btn_removeAll');
removeAll.onclick = function () {
var ele = document.querySelector('#userTbody')
ele.innerHTML = ''
}
var trId = 1;
var btn_submit = document.querySelector('#btn_submit')
btn_submit.onclick = function () {
// 找到所有 input 输入文本值
var username = document.querySelector('#username').value
var email = document.querySelector('#email').value
var tel = document.querySelector('#tel').value

// 创建 tr td 并 td 加到 tr 中
var usernameTdEle = document.createElement('td')
usernameTdEle.innerHTML = username
var emailTdEle = document.createElement('td')
emailTdEle.innerHTML = email
var telTdEle = document.createElement('td')
telTdEle.innerHTML = tel
var optionTdEle = document.createElement('td')
optionTdEle.innerHTML = '<a href="javascript:delRow(\'tr' + trId + '\')">删除</a>'
var trEle = document.createElement('tr')
trEle.id = 'tr' + trId;
trId++
trEle.appendChild(usernameTdEle)
trEle.appendChild(emailTdEle)
trEle.appendChild(telTdEle)
trEle.appendChild(optionTdEle)

// 把 tr 加到 tbody 中
var tbody = document.querySelector('#userTbody')
tbody.appendChild(trEle)
}
}
function delRow(id) {
var removeAll = document.querySelector('#' + id);
removeAll.parentNode.removeChild(removeAll);
}
</script>
</head>
<body>
<form name="userForm">
<center>
用户录入<br/>
用户名:<input id="username" name="username" type="text" size="15"/>
E-mail:<input id="email" name="email" type="text" size="15">
电话:<input id="tel" name="tel" type="text" size="15">
<input type="button" value="添加" id="btn_submit">
<input type="button" value="删除所有" id="btn_removeAll">
</center>
</form>
<hr/>
<table border="1" align="center" cellpadding="0" cellspacing="0" width="400">
<thead>
<tr>
<th>用户名</th>
<th>E-mail</th>
<th>电话</th>
<th>操作</th>
</tr>
</thead>
<tbody id="userTbody">
<tr id="tr1">
<td>张无忌</td>
<td>wujizhang@163.com</td>
<td>18212345678</td>
<td><a href="javascript:delRow('tr1')">删除</a></td>
</tr>
</tbody>
</table>
</body>
</html>

jquery 版本:

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
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="static/jquery/jquery-1.11.3.min.js"></script>
<script>
$(function () {
var trId = 2
$('#btn_removeAll').click(function () {
$('#userTbody').empty()
})
$('#btn_submit').click(function () {
// 找到所有 input 输入文本值
var username = $('#username').val()
var email = $('#email').val()
var tel = $('#tel').val()
// 模板字符串
var a = `
<tr id="tr${trId}">
<td>${username}</td>
<td>${email}</td>
<td>${tel}</td>
<td><a href="javascript:delRow('tr${trId}')">删除</a></td>
</tr>
`;
$('#userTbody').append(a)
trId ++;
})
})

function delRow(id) {
$('#' + id).remove();
}
</script>
</head>
<body>
<form name="userForm">
<center>
用户录入<br/>
用户名:<input id="username" name="username" type="text" size="15"/>
E-mail:<input id="email" name="email" type="text" size="15">
电话:<input id="tel" name="tel" type="text" size="15">
<input type="button" value="添加" id="btn_submit">
<input type="button" value="删除所有" id="btn_removeAll">
</center>
</form>
<hr/>
<table border="1" align="center" cellpadding="0" cellspacing="0" width="400">
<thead>
<tr>
<th>用户名</th>
<th>E-mail</th>
<th>电话</th>
<th>操作</th>
</tr>
</thead>
<tbody id="userTbody">
<tr id="tr1">
<td>张无忌</td>
<td>wujizhang@163.com</td>
<td>18212345678</td>
<td><a href="javascript:delRow('tr1')">删除</a></td>
</tr>
</tbody>
</table>
</body>
</html>

事件流


  • capture phase 捕获过程

会从事件的最顶端,window 开始往下 捕获到触发事件节点的父元素,即 p 元素

  • target phase 触发过程

事件触发过程。

  • bubble phase 冒泡过程

从事件响应的起 冒泡的 window 对象。

有些事件没有冒泡过程, 一些低版本浏览器没有 capture 过程。

事件注册与触发


事件注册 、事件触发、 事件取消注册

事件注册

eventTarget.addEventListener(type, listener[, useCapture])

使用第二种事件注册方法,有个弊端,只能注册一个函数。实际上可以有多个函数响应该事件。

取消事件注册

eventTarget.removeEventListener(type, listener[, useCapture])

事件触发

eventTarget.dispatchEvent(type)

浏览器兼容 ie6、7、8 没有采用该标准

  • 事件的注册于取消:

attchEvent/detachEvent

  • 事件触发

fireEvent(e)

  • no capture

js 的 addEventListener 和 removeEventListener 的兼容

事件对象


ie 的 低版本:不是通过函数传入 event对象,而是将事件对象放在 window.event中,兼容方式:

属性

  • type
  • target(sourceElement)
  • currentTarget(当前处理事件节点),有可能处理函数是注册在 target 父节点上

方法

  • stopPropagation 阻止传播,不想继续冒泡了(w3c)
    • event.cancelBubble = true(IE)
  • stopImmediatePropagation 阻止冒泡
    • 阻止事件传播到父节点
    • 阻止当前节点的后续事件(在我后面注册的事件不会传播)
  • preventDefault 阻止默认行为(w3c)
    • event.returnValue = false(IE)

阻止默认行为,比如点击链接,会默认打开链接。可以使用该方法阻止事件响应。

事件分类


MouseEvent

mouseover、mouseout 与 mouseenter、mouseleave 的区别

加入 div 有子元素, 我们鼠标离开 div 元素 进入到 div 中的子元素上,都会触发 mouseover 、 mouseout

mouseenter、mouseleave 只有进入这个元素就会触发,进入该元素的子元素不会触发。而出这个 div 元素。将 div 看成一个整体,进入或移出这个整体会触发。

属性

  • clientX, clientY
  • screenX, screenY

  • ctrlKey, shiftKey, altKey, metaKey

如果事件触发时,这几个键是按下去的,则这些属性为 true 否则为false

  • button(0,1,2)分别代表按下鼠标的键是左键还是右键

MouseEvent 顺序

  • 从元素A上方移过:
    • mousemove -> moseover(A) -> mouseenter(A) -> mousemove(A)(多次) -> mouseout(A) -> mouseleave(A)
  • 点击元素
    • mousedown -> [mousemove] -> mouseup -> click

例子 拖拽div

WheelEvent对象

事件类型: wheel 是否冒泡: YES 元素:Element
默认事件:scroll or zoom document
元素: div

属性:

  • deltaMode(delta值的单位)
  • deltaX
  • deltaY
  • deltaZ

x y z 轴上的偏移量

FocusEvent

属性:

  • relatedTarget 当一个元素失去焦点时,获得焦点的元素就是 relatedTarget,当一个元素获得焦点时,失去焦点的元素就是 relatedTarget。

InputEvent 输入事件

ie 兼容:

onpropertychange

KeyboardEvent 处理键盘事件

比如回车键,等

属性:

  • key: 你按下的什么键 字符串
  • code:按键码
  • ctrlKey、shiftKey、altKey、metaKey 用来标识这些键有没有按下去
  • repeat: 持续按一个键,值是true
  • keyCode:获取键盘的键的ASK码
  • charCode:
  • which

Event 其他事件类型

依赖网络加载的事件,加载完成时会触发,

unload 指 页面退出的时候出发

error 出现在加载错误

selected 通常 input textarea 被选择了 会触发

abort 退出事件,Window image上,比如图片加载时,按了 esc 会触发

window对象

  • load 当页面上所有的请求都完成时,会触发 load
  • unload 当文档退出的时候,比如关闭当前页面,离开当前页面
  • error 浏览器加载异常
  • abort 离开页面

image

  • load 图片加载完成
  • error 图片加载异常
  • abort 当图片加载时,按esc 会触发

一般这样通用:

如果指定图片加载不到,会显示默认图片。

UIEvent

resize 通常在修改窗体大小的时候,修改浏览器窗体大小,iframe窗体大小

scroll 指页面发生滚动时触发。元素上触发会冒泡

元素上触发scroll会冒泡,document上触发Scroll 不会冒泡

事件代理


将事件注册到元素的父节点上,对于ul li 一组标签来说,将事件注册到父元素上,这样简化事件注册。

触发事件的方式


为什么 JavaScript 中的函数需要用 window.onload 包起来?

window.onload 方法的作用是在页面的所有元素都加载完毕后才会触发,包括图片。当连接质量不好的时候,会导致该方法触发延迟。当然你也可以在 body 后写 script 代码,但是这样不符合习惯。

1
<button id="btn">点击</button>
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
window.onload = function () {
var btn = document.querySelector('#btn');
// 绑定一个
btn.onclick = function () {
console.log(1);
};

// 绑定多个
btn.addEventListener('click', function () {
console.log(2);
})


var listener = function () {
console.log(3);
}
btn.addEventListener('click', listener);
btn.removeEventListener('click', listener);

// 浏览器兼容做法 IE
function addEvent(btn, eventName, func) {
// IE
if (btn.attachEvent) {
btn.attachEvent('on', eventName, func);
} else {
btn.addEventListener(eventName, func);
}
}
}
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2016/08/10/DOM%E7%BC%96%E7%A8%8B/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论