🏆从零开始学习JS进阶2️⃣🏆
三、DOM
3.1、DOM简介
文档对象模型(Document Object Model,简称DOM),是 W3C 组织推荐的处理 可扩展标记语言(html或xml文档) 的标准 编程接口。它是一种与平台和语言无关的 API,它可以动态操作HTML文档,如 对html标签作增删改查操作。DOM 是一种基于树的 API 文档,在处理html文档的过程中,DOM以对象的形式存储在内存中。因为DOM是基于树结构存储在内存中的,所以DOM又称为文档树模型。
3.2、DOM树
DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。他有三个组成部分:
- 文档:一个页面就是一个文档,DOM中使用document表示
- 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
- 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示
3.4、获取元素
我们获取元素的目的是,比如我们想要操作页面上的某部分(显示/隐藏,动画),需要先获取到该部分对应的元素,再对其进行操作。
3.4.1、根据ID获取
- 语法:
document.getElementById(id)
。 - 参数:id的值,是区分大小写的字符串。
- 返回值:元素对象或null。
- 作用:根据ID获取元素对象。
<body>
<div id="name">XiaoLin</div>
<script>
// 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
var timer = document.getElementById('name');
console.log(timer);
console.log(typeof timer);
// console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
console.dir(timer);
</script>
</body>
3.4.2、根据标签名获取元素
- 语法:
document.getElementsByTagName('标签名')
或者element.getElementsByTagName('标签名')
。 - 参数:标签名。
- 返回值:元素对象集合(伪数组,数组元素是元素对象)。
- 作用:根据标签名获取元素对象。
<body>
<ul>
<li>知否知否,应是等你好久11</li>
<li>知否知否,应是等你好久22</li>
<li>知否知否,应是等你好久33</li>
<li>知否知否,应是等你好久44</li>
<li>知否知否,应是等你好久55</li>
</ul>
<ul id="nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
// 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3. element.getElementsByTagName() 可以得到这个元素里面的某些标签
var nav = document.getElementById('nav'); // 这个获得nav 元素
var navLis = nav.getElementsByTagName('li');
console.log(navLis);
</script>
</body>
对于根据标签名获取到的元素,我们需要注意:
- 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要先遍历。
- 得到的元素对象是动态的,即:当页面增加了标签,这个集合中也就增加了元素。
3.4.3、通过其他方式获取
H5还有其他的几种方式获取:
- 通过类名返回元素对象集合:
document.getElementByClassName('类名')
。 - 通过指定选择器返回第一个元素对象:
document.querySelector('选择器')
。 - 通过指定选择器返回:
document.querySelectorAll('选择器')
<body>
<div class="box">XiaoLin_Java</div>
<div class="box">XiaoLin_Python</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
// 1. getElementsByClassName 根据类名获得某些元素集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
// 2. querySelector 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box #nav
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelectorAll()返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
</body>
3.4.4、获取特殊元素
3.4.4.1、获取Body
我们可以获取到整个Body元素。
document.body // 返回一个body元素对象
3.4.4.2、获取Html元素
document.documentElement // 返回Html元素对象
3.5、事件基础
3.5.1、事件概述
JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个 事件,然后去执行某些操作。
简单来说就是点击了某个按钮,发生了某件事情。
3.5.2、事件三要素
-
事件源(谁):触发事件的元素。
-
事件类型(什么事件): 例如 click 点击事件。
-
事件处理程序(做了啥):事件触发后要执行的代码(函数形式),事件处理函数。
<body>
<button id="btn">XiaoLin</button>
<script>
// 点击一个按钮,弹出对话框
// 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素
//(1) 事件源 事件被触发的对象 谁 按钮
var btn = document.getElementById('btn');
//(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
//(3) 事件处理程序 通过一个函数赋值的方式 完成
btn.onclick = function() {
alert('点秋香');
}
</script>
</body>
3.5.3、执行事件的步骤
- 获取事件源。
- 注册事件(绑定事件)。
- 才有函数赋值形式来添加事件处理程序。
<body>
<div>123</div>
<script>
// 执行事件步骤
// 点击div 控制台输出 我被选中了
// 1. 获取事件源
var div = document.querySelector('div');
// 2.绑定事件 注册事件
// div.onclick
// 3.添加事件处理程序
div.onclick = function() {
console.log('我是XiaoLin,我被选中了');
}
</script>
</body>
3.5.4、常见的鼠标事件
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点时触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
3.5.5、事件的书写位置
3.5.5.1、行内式
所谓的行内是指的就是直接把事件写在html代码中,不建议使用这种方式,虽然写起来简单,但是这种方式仅仅适合写一些简单的事件,比如说alert
之类的,如果写一些复杂的就看起来很冗余,而且后期维护起来及其困难。
<button onclick="alert('hello,我是XiaoLin')">行内式</button>
3.5.5.2、内嵌式
内嵌式指的是在html最下面写JavaScript代码,这样不仅仅可以写一些复杂的事件,同时还便于维护,但是还是不推荐这种方式,因为JavaScript代码和html代码没有完全解耦分离
<button onclick="fun()">内嵌式</button>
<script type="text/javascript">
function fun(){
alert("hello,我是XiaoLin");
}
3.5.5.3、外部JavaScript文件
这种是目前最主流也是推崇的方式,他是将所有的JavaScript都抽离出去,放在了一个单独的文件里面,然后在页面里面引用即可。
3.6、操作元素
JavaScript的DOM操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。
3.6.1、改变元素内容
方法 | 描述 |
---|---|
element.innerText | 从起始位置到终止位置的内容,但他去除html标签,同时空格和换行也会被去掉。 |
element.innerHTML | 从起始位置到终止位置的内容,包括html标签,同时空格和换行也会被去掉。 |
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<p id="demo">my first demo</p>
</body>
<script>
document.getElementById("demo").innerHTML="<b>hello XiaoLin_Java</b>"
</script>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p id="demo">my first demo</p>
</body>
<script>
document.getElementById("demo").innerText="<h1>My First XiaoLin_Java</h1>";
</script>
</html>
通过运行的结果,我们可以很好的看出这两者的区别:
- innerText会去除空格和换行,而innerHTML会保留空格和换行。
- innerText不会识别html,而innerHTML会识别。
3.6.2、获取属性的值
我们可以使用JavaScript来获取属性的值,他的基本语法为元素对象.属性名
。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<img src="imges/ldh.jpg" alt="" id="imgBtn" title="刘德华" >
</body>
</html>
<script>
// 获取元素
var img = document.getElementById('imgBtn');
// 2. 注册事件 处理程序
img.onclick = function() {
// 获取元素的值
alert(img.title)
}
</script>
3.6.3、设置属性的值
设置属性的值的语法为:元素对象.属性名 = 值
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<img src="imges/ldh.jpg" alt="" id="imgBtn" title="刘德华" >
</body>
</html>
<script>
// 获取元素
var img = document.getElementById('imgBtn');
// 2. 注册事件 处理程序
img.onclick = function() {
// 给元素设置值
alert("设置之前:"+img.title)
img.title = 'XiaoLin';
alert("设置之后:"+img.title)
}
</script>
3.6.4、布尔类型的值设值
<body>
<button>按钮</button>
<input type="text" value="输入内容">
<script>
// 1. 获取元素
var btn = document.querySelector('button');
var input = document.querySelector('input');
// 2. 注册事件 处理程序
btn.onclick = function() {
// 表单里面的值 文字内容是通过 value 来修改的
input.value = '被点击了';
// 如果想要某个表单被禁用 不能再点击 disabled 我们想要这个按钮 button禁用
// btn.disabled = true;
this.disabled = true;
// this 指向的是事件函数的调用者 btn
}
</script>
</body>
3.6.5、排他操作
如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:
- 所有元素全部清除样式(干掉其他人)
- 给当前元素设置样式 (留下我自己)
- 注意顺序不能颠倒,首先干掉其他人,再设置自己,如果先设值自己再干掉其他人会导致所有都没有样式了。
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 1. 获取所有按钮元素
var btns = document.getElementsByTagName('button');
// btns得到的是伪数组 里面的每一个元素 btns[i]
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
// (1) 我们先把所有的按钮背景颜色去掉 干掉所有人
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
// (2) 然后才让当前的元素背景颜色为pink 留下我自己
this.style.backgroundColor = 'pink';
}
}
</script>
3.6.6、一键换肤
<body>
<ul class="baidu">
<li><img src="images/1.jpg"></li>
<li><img src="images/2.jpg"></li>
<li><img src="images/3.jpg"></li>
<li><img src="images/4.jpg"></li>
</ul>
<script>
// 1. 获取元素
var imgs = document.querySelector('.baidu').querySelectorAll('img');
// console.log(imgs);
// 2. 循环注册事件
for (var i = 0; i < imgs.length; i++) {
imgs[i].onclick = function() {
// this.src 就是我们点击图片的路径 images/2.jpg
// console.log(this.src);
// 把这个路径 this.src 给body 就可以了
document.body.style.backgroundImage = 'url(' + this.src + ')';
}
}
</script>
</body>
3.6.7、表格隔行变色
<script>
// 1.获取元素 获取的是 tbody 里面所有的行
var trs = document.querySelector('tbody').querySelectorAll('tr');
// 2. 利用循环绑定注册事件
for (var i = 0; i < trs.length; i++) {
// 3. 鼠标经过事件 onmouseover
trs[i].onmouseover = function() {
// console.log(11);
this.className = 'bg';
}
// 4. 鼠标离开事件 onmouseout
trs[i].onmouseout = function() {
this.className = '';
}
}
</script>
3.6.8、全选
<script>
// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
// 获取元素
var j_cbAll = document.getElementById('j_cbAll');
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
// 全选按钮注册事件
j_cbAll.onclick = function() {
// this.checked 当前复选框的选中状态
console.log(this.checked);
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
// 给所有的子复选框注册单击事件
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function() {
// flag 控制全选按钮是否选中
var flag = true;
// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
for (var i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
flag = false;
break;
}
}
// 设置全选按钮的状态
j_cbAll.checked = flag;
}
}
</script>
3.7、自定义属性操作
3.7.1、设置属性值
我们有两种方式来设置属性值:
- element.属性 = ‘值’;
- element.setAttribute (‘属性’,‘值’);
他们俩的区别是:
- 方式一为内置属性设置值。
- 方式二主要是给自定义属性设置值。
3.7.2、移除属性
移除属性的语法格式为:
element.removeAttribute('属性');
div.removeAttribute('index');
3.8、节点操作
3.8.1、节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。在日常开发中,我们最常使用的是元素节点。
一般一个节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点的节点类型为1
- 属性节点的节点类型为2
- 文本节点的节点类型为3
3.8.2、节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
3.8.3、获取父级节点
我们可以通过parentNode来获取某节点的父节点,他返回的是最近的一父节点,如果没有就返回null。
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父节点 parentNode
var erweima = document.querySelector('.erweima');
// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
console.log(erweima.parentNode);
</script>
3.8.4、子节点
获取子节点的语法为:
parentNode.childNodes
这种写法是标准写法,返回包含指定节点的所有子节点,包括元素节点、文本节点,如果只想获得里面的元素节点,就需要专门处理,所以我们一般是不会用的,我们一般使用下面这个:
parentNode.childrent
parentNode.childrent是一个只读属性,返回所有的子元素节点,他只返回子元素节点,其他节点不返回。
<ul>
<li>我是XiaoLin</li>
<li>我是XiaoLin</li>
<li>我是XiaoLin</li>
<li>我是XiaoLin</li>
</ul>
<script>
// DOM 提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children);
</script>
还有几个获取常见节点的方法。
获取第一个节点
parentNode.firestChild
他返回的的是第一个子节点的所有节点,如果找不到就返回null。
获取第一子元素节点
parentNode.firstElementChild
他返回的是第一个子元素节点,找不到就返回NULL。这个方法有兼容性问题,IE9以上才支持。
获取最后一个节点
parentNode.lastChild
获取最后一个子元素节点
parentNode.lastElementChild
他返回的是最后一个子元素节点,找不到就返回null。这个方法有兼容性问题,IE9以上才支持。
总结
在实际开发中,firstChild和lastChild会包含其他节点,对于我们的操作是十分不方便的,而firstElementChild 和 lastElementChild又有兼容性问题,如果我们想要获取第一子元素节点和最后一个子元素节点,我们可以采用的办法是:
- 获取第一个子元素节点:
parentNode.childrent[0]
- 获取最后一个子元素节点:
parentNode.children[parentNode.children.length-1]
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一个子节点 不管是文本节点还是元素节点
console.log(ol.firstChild);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
3.8.5、创建节点
如果元素原先不存在,我们可以使用document.createElement('标签名称')
,来创建指定的标签的HTML元素,这些元素原先是不存在的,是根据我们的需求动态生成的,所以称为动态创建节点。
3.8.6、添加节点
我们有两种添加节点的方式:
- 尾插法
- 头插法
3.8.6.1、头插法
node.insertBefore(chile,指定元素)
方法将以节点添加到父节点指定子节点的前面,类似于CSS里面的before伪元素。
<html>
<body>
<ul>
<li>123</li>
</ul>
</body>
</html>
<script>
// 添加节点 node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
</script>
3.8.6.2、尾插法
node.appendChind(child)
方法将一个节点添加到指定的父节点的子节点列表的末尾,类似于CSS里面的after伪元素。
<html>
<body>
<ul>
<li>123</li>
</ul>
</body>
</html>
<script>
// 创建节点元素节点
var li = document.createElement('li');
// 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素
var ul = document.querySelector('ul');
ul.appendChild(li);
</script>
3.8.7、删除节点
node.removeChild()
方法从 node节点中删除一个子节点,返回删除的节点。
<html>
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 删除元素 node.removeChild(child)
// ul.removeChild(ul.children[0]);
// 3. 点击按钮依次删除里面的孩子
btn.onclick = function() {
if (ul.children.length == 0) {
this.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
</html>
3.8.8、复制节点
node.cloneNode()
方法返回调用该方法的节点的一个副本,成为复制节点。他里面可以传参数:
- 如果参数为空或者是false的话,就只是浅拷贝,即只复制节点本身,不会复制里面的子节点。
- 如果参数为true的话,就是深拷贝,会复制该节点以及该节点的所有子节点。
- 注意和Java中的深/浅拷贝区分。
<html>
<body>
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
// 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
var lili = ul.children[0].cloneNode(true);
ul.appendChild(lili);
</script>
</body>
</html>
3.9、元素操作
3.9.1、创建元素
我们创建元素主要有三种方式:
- document.write
- inner.innerHTML
- document.createElement
document.write
document.write
是直接将内容写入页面的内容流。
document.write('<div>123</div>');
innerHTML
innerHTML是将内容写入某个DOM节点,inner.innerHTML += '<a href="#">百度</a>'
如果我们需要创建多个元素可以使用innerHTML
效率更高,但是这个时候不要去拼接字符串,可以采用数组的结构拼接。
createElement
createElement
可以直接创建一个元素,虽然他在创建多个元素的效率稍微低一点点,但是他的结构更清晰明了。
var create = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
3.9.2、效率比较
效率低:innerHTML字符串拼接方式
innerHTML
的方式是效率最低的,而且他需要去拼接字符串,较为繁琐。
<script>
function fn() {
var d1 = +new Date();
var str = '';
for (var i = 0; i < 1000; i++) {
document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
效率一般:createElement
<script>
function fn() {
var d1 = +new Date();
for (var i = 0; i < 1000; i++) {
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '2px';
div.style.border = '1px solid red';
document.body.appendChild(div);
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
效率高:innerHTML数组方式
<script>
function fn() {
var d1 = +new Date();
var array = [];
for (var i = 0; i < 1000; i++) {
array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
}
document.body.innerHTML = array.join('');
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
3.10、事件高级
3.10.1、addEventListener()
addEventListener()
方法将指定的监听器注册到目标对象上,一般是目标对象直接调用该方法,当该对象触发指定的事件时,就会执行事件处理函数。该方法有三个参数:
- type:事件的类型,他是一个字符串,常见的有click、mouseover。
- listener:事件处理函数,事件发生时,会调用该监听函数。
- useCapture:是一个可选参数,类型为布尔值,默认为false。
<html>
<body>
<button>addEventListener方法测试</button>
</body>
<script>
var btns = document.querySelectorAll('button');
btns[0].addEventListener('click', function() {
alert(22);
})
</script>
</html>
3.10.2、attacheEvent()
attachEvent()
方法将指定的监听器注册到目标对象上,由目标对象调用该方法,当该对象触发指定的事件时,指定的回调函数就会被执行。该方法有两个参数:
- eventNameWithOn:事件类型的类型,他是一个字符串,常见的有onclick、onmouseover。
- callback:事件处理函数,当目标事件触发时,回调函数被调用。
<html>
<body>
<button>attacheEvent方法测试</button>
</body>
<script>
var btns = document.querySelectorAll('button');
btns[0].attachEvent('onclick', function() {
alert(11);
})
</script>
</html>
3.10.3、DOM事件流
事件流描述的是从页面接受事件的顺序。当事件发生时,会在元素节点之间按照特定的顺序传播,这个传播过程就叫做DOM事件流。
那么事件流的顺序是怎么样的呢?互联网两大巨头公司有了不同的纷争:
- 事件冒泡:由IE提出,事件开始时是由最具体的元素接受,然后逐渐向上传播到DOM最顶层节点的过程。
- 事件捕获:由网景公司提出,他表示的是由DOM最顶层节点开始,然后逐渐向下传播到最具体的元素接受的过程。
我们可以发现,这两家公司好像是天生的仇人一样,他们连提出的标准的方向都不同。IE 提出从目标元素开始,然后一层一层向外接收事件并响应,也就是冒泡型事件流。而Netscape(网景公司)提出从最外层开始,然后一层一层向内接收事件并响应,也就是捕获型事件流。他们俩谁也不服谁,为了一统江湖,W3C采用了这种的方式,制定了一个新的标准:先捕获再冒泡。当事件发生的时候,会经历3个阶段:
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
他有点类似于我们往水里抛一个石头:
- 首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程。
- 之后会产生泡泡,会在最低点( 最具体元素),有点类似于处于当前目标阶段。
- 最后会漂浮到水面上,这个过程相当于事件冒泡。
他有几个注意的点:
- JavaScript代码只能执行捕获或者冒泡其中的一个阶段。
- onclick和attachEven只能到冒泡阶段。
- addEventListener(type、listener、useCapture)中的第三个参数如果是true,则表示在事件捕获阶段调用事件处理程序,如果使用默认的false,表示事件在冒泡阶段调用事件处理函数。
- 实际的开发中我们很少使用事件捕获,更多的是关注事件冒泡。
- 有一些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave。
3.10.4、事件对象
3.10.4.1、概述
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。这一系列信息的数据集合包括:
- 谁绑定了这个事件。
- 鼠标触发事件的话,还可以拿到鼠标的相关信息,比如鼠标的位置。
- 键盘触发事件话,会得到键盘相关的信息,比如我按了哪个按键。
3.10.4.2、属性和方法
事件对象 | |
---|---|
e.target | 返回触发事件的对象(标准) |
e.srcElement | 返回触发事件的对象(非标准) |
e.type | 返回事件的类型,例如click、mouseover |
e.cancelBubble | 阻止冒泡(非标准) |
e.stopPropagation() | 阻止冒泡(标准) |
e.returnValue | 阻止默认事件(非标准) |
e.preventDefault | 阻止默认事件(标准) |
3.10.5、阻止默认行为
html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。我们如果不想让a标签跳转的话可以阻止他的默认行为。
<html>
<body>
<a href="http://www.baidu.com">百度</a>
</body>
<script>
// 2. 阻止默认行为 让链接不跳转
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
});
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
e.returnValue = false;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题
return false;
}
</script>
</html>
3.10.6、阻止事件冒泡
事件冒泡可能会带来好处也有可能会带来坏处,如果我们想阻止事件冒泡的话有两种写法:
- 标准写法:e.stopPropagation()
- 非标准写法:e.cancelBubble = true;
3.10.7、事件委托
事件委托也成为了事件代理,把事情委托给别人,代为处理。简单来说就是不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。
举个简单的例子,有10个班,1000名学生,每个学生要领一个swtich手柄,如果一个个由配送员来送的话,不仅仅花费的时间很长,配送员会累死,学生还要大排场龙,如果使用了代理的话,配送员只需要将每一个switch分给每个班的班主任,同学找班主任领取即可。
我们来看下面这个需求,我们点击每一个li的时候都会有一个弹窗,按照普通的思想,我们需要给每一个li单独注册事件,是非常麻烦的,不仅麻烦,而且页面加载的时候,由于访问DOM的次数变多了,会延长整个页面的响应时间。
<html>
<body>
<ul>
<li>switch1</li>
<li>switch2</li>
<li>switch3</li>
<li>switch4</li>
</body>
</html>
这个时候我们就需要采用事件委托,我们将这个事件委托给他的父元素。利用事件泡沫,当子元素的事件触发时,会冒泡到父元素,然后再去控制响应的子元素。
采用了事件委托的优点很明显:
- 只操作了一次DOM,显著提升了性能。
- 如果我们新加或者动态创建一个子元素,新加的子元素也拥有这个事件。
<html>
<body>
<ul>
<li>switch1</li>
<li>switch2</li>
<li>switch3</li>
<li>switch4</li>
</body>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'pink';
})
</script>
</html>
3.10.8、常见鼠标事件
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左侧触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfoucs | 获得鼠标焦点时触发 |
onblur | 失去鼠标焦点时触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
我们来做一个禁止选中文字和禁止右键菜单的联系。
<body>
我是一段不愿意分享的文字
<script>
// 1. contextmenu 我们可以禁用右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
// 2. 禁止选中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
3.10.9、鼠标事件对象
event事件对象是事件相关的一系列信息的集合,我们常用的是鼠标事件对象MouseEvent和键盘事件对象KeyboardEvent。
鼠标事件对象 | 作用 |
---|---|
e.clientX | 返回鼠标相对于浏览器可视区的X坐标 |
e.clientY | 返回鼠标相对于浏览器可视区的Y坐标 |
e.pageX | 返回鼠标相对于文档页面的X坐标 |
e.pageY | 返回鼠标相对于文档页面的Y坐标 |
e.screenX | 返回鼠标相对于电脑屏幕的X坐标 |
e.screenY | 返回鼠标相对于电脑屏幕的Y坐标 |
<script>
// 鼠标事件对象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠标在可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠标在页面文档的x和y坐标
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠标在电脑屏幕的x和y坐标
console.log(e.screenX);
console.log(e.screenY);
})
</script>
我们来做一个练手的案例:跟随鼠标的猫。大致的思路是这样:
- 鼠标不断地移动,我们可以使用鼠标移动事件:mousemove。
- 鼠标在页面中移动的时候,给document注册事件。
- 图片要移动距离。而且不能占位置,所以我们使用绝对定位。
- 每次鼠标移动,我们都获得一个新的鼠标坐标,然后将这个x、y值作为图片的top和left偏移就可以移动图片了。
<html>
<body>
<img src="images/cat.gif" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
// 1. mousemove只要我们鼠标移动1px 就会触发这个事件
// 2.核心原理: 每次鼠标移动,我们都会获得最新的鼠标坐标,
// 把这个x和y坐标做为图片的top和left 值就可以移动图片
var x = e.pageX;
var y = e.pageY;
console.log('x坐标是' + x, 'y坐标是' + y);
//3 . 千万不要忘记给left 和top 添加px 单位
pic.style.left = x - 50 + 'px';
pic.style.top = y - 40 + 'px';
});
</script>
</body>
</html>
3.10.11、常见的键盘事件
3.10.11.1、键盘事件
键盘事件 | 触发条件 |
---|---|
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个键盘按键被按下时触发 |
onkeypress | 某个键盘被按下时触发,但是他不能识别功能键,如ctrl、shift等 |
需要注意的是如果使用addEventListener
方法时,前面不需要加on。
<script>
// 常用的键盘事件
//1. keyup 按键弹起的时候触发
document.addEventListener('keyup', function() {
console.log('我弹起了');
})
//3. keypress 按键按下的时候触发 不能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keypress', function() {
console.log('我按下了press');
})
//2. keydown 按键按下的时候触发 能识别功能键 比如 ctrl shift 左右箭头啊
document.addEventListener('keydown', function() {
console.log('我按下了down');
})
</script>
3.10.11.1、键盘事件对象
键盘事件对象属性 | 说明 |
---|---|
keyCode | 返回该对象的ASCII码 |
需要注意的是:
- onkeydown和onkeyup不区分字母大小写,但是onkeypress区分字母大小写。
- 在实际开发中,用的更多的还是keydown和keyup,因为他可以识别键盘上所有的按键,包含了功能键。
- keypress虽然不能识别功能键,但是keyCode属性可以区分大小写,返回不能的ASCII码。
比如我们写一个小demo,判断用户按下的是哪一个按键?
<script>
// 键盘事件对象中的keyCode属性可以得到相应键的ASCII码值
document.addEventListener('keyup', function(e) {
console.log('up:' + e.keyCode);
// 我们可以利用keycode返回的ASCII码值来判断用户按下了那个键
if (e.keyCode === 65) {
alert('您按下的a键');
} else {
alert('您没有按下a键')
}
})
document.addEventListener('keypress', function(e) {
// console.log(e);
console.log('press:' + e.keyCode);
})
</script>
- 点赞
- 收藏
- 关注作者
评论(0)