写在前面:本文仅包含DOM内容,JavaScript传送门在这里,BOM传送门在这里。
本文内容是假期中刷的黑马Pink老师视频(十分感谢Pink老师),原文保存在个人的GitLab中,如果需要写的网页内容信息等可以评论联系我,若是编辑博文中出现了忘记上传的图片或者错位的图片欢迎评论区指正。写作不易,欢迎点赞、收藏+关注。
DOM
DOM 专有名词
DOM树
- 文档:一个页面就是一个文档,DOM中使用Document表示
- 元素:页面中所有的标签都是元素,DOM中使用element表示
- 节点:网页中所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示
DOM 把以上内容都看做事对象。
DOM 获取元素
- 通过ID获取 |
getElementById()
- 通过标签名获取
- 通过HTML5新增方法获取
- 特殊元素获取
- 通过ID获取 |
getElementById()
返回的是一个
<body>
<div id="time">
2019-9-9
</div>
<script>
var time = document.getElementById('time');
console.log(time);
console.log(typeof time); // 返回的是一个元素对象
console.log(time.id);
console.dir(time); // div#time
</script>
</body>
- 根据标签名获取 |
getElementsByTagName()
| 返回的是对象集合
<body>
<ul>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
</ul>
<script>
var eles = document.getElementsByTagName('li');
console.log(eles); // HTMLCollections(5) [li,li,li,li,li]
// 逐个输出一下
for( var i = 0; i < eles.length ; i ++) {
console.log(eles[i]);
}
</script>
</body>
得到元素对象是动态的,如果元素的内容发生了变化,JS获取到的内容跟着一起变化
注意事项
- 如果页面中只有一个元素,返回的也是一个伪数组
- 如果页面中没有元素,返回的是一个空的伪数组
通过父元素获取其全部子元素
<body>
<ul>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
<li>知否知否,应是绿肥红瘦。</li>
</ul>
<ol>
<li>哈哈哈</li>
<li>哈哈哈</li>
<li>哈哈哈</li>
<li>哈哈哈</li>
</ol>
<script>
var ol = document.getElementsByTagName('ol');
var lis = ol[0].getElementsByTagName('li');
console.log(lis);
</script>
</body>
- HTML5新增的方法 | 通过类名获取 |
getElementsByClassName()
<body>
<div class="box">123</div>
<div class="box">123</div>
<div class="box">123</div>
<script>
var lis = document.getElementsByClassName('box'); // 长度为3的伪数组
</script>
</body>
querySelectir()
querySelector()
:获取指定选择器的第一个元素querySelectorAll()
:获取页面上所有元素
<body>
<div class="box">hahaha</div>
<div class="box">hahaha</div>
<div class="box">hahaha</div>
<script>
var firstBox1 = document.querySelector('.box'); // 通过.告诉选择器这是一个class | <div class="box">hahaha</div>
var firstBox2 = document.querySelector('#box'); // 通过#告诉选择器这是一个id | null
console.log(firstBox1);
console.log(firstBox2);
</script>
</body>
querySelectirAll()
| 获取所有元素
<body>
<div class="box">hahaha</div>
<div class="box">hahaha</div>
<div class="box">hahaha</div>
<script>
var firstBox1 = document.querySelectorAll('.box'); // 通过.告诉选择器这是一个class | <div class="box">hahaha</div>
var firstBox2 = document.querySelectorAll('#box'); // 通过#告诉选择器这是一个id | null
console.log(firstBox1); // 长度为3的伪数组
console.log(firstBox2); // 空的伪数组
</script>
</body>
- 获取特殊标签 | body、htlm
一般来说只有一个body标签或者一个htlm标签
- 获取 BODY
<body>
<script>
var bodyEle = document.body;
console.log(bodyEle); // 输出Body对象
</script>
</body>
- 获取 HTML
<body>
<script>
var htmlEle = document.documentElement;
console.log(htmlEle); // HTML 对象
</script>
</body>
DOM | 事件
JavaScript 使我们有能力创建动态页面,而时间是可以被JavaScript侦测到的行为
简单理解:出发—响应机制
网页中的每个元素都可以产生某些可以出发JavaScript的事件,例如,我们可以在用户点击某个按钮的时候产生一个事件,然后去执行某些操作。
事件是由三部分组成:事件源、事件类型、事件处理程序(一般称为事件三要素)
- 事件源:时间的触发的对象,是由谁来触发,比如按钮
- 事件类型:如何出发,比如鼠标点击出发、鼠标经过出发、键盘按下触发
- 事件处理程序:通过一个函数赋值的方式完成
点击按钮事件示例
<body>
<button id="btn">点击一下</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
alert('点击了一下~');
}
</script>
</body>
执行事件过程
- 获取事件源
- 注册时间(绑定事件)
- 添加事件处理程序(采用函数赋值形式)
鼠标到Div上跳出弹框示例
<body>
<div style="background: antiquewhite;text-align: center;width: 200px;border-radius: 5px;box-shadow: 0 0 3px gray">123</div>
<script>
var div = document.querySelector('div');
div.onmouseover = function (){
alert('Hello World~');
}
</script>
</body>
鼠标事件 | 描述 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseenter | 鼠标进入触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获取鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmousedown | 鼠标按下触发 |
onmouseup | 鼠标弹起触发 |
contextmenu | 右键的时候触发 |
selectstart | 选择文字的时候触发 |
注意:
mouseover
存在冒泡事件,经过自身盒子会触发事件,子盒子也会触发。mouseenter
不存在冒泡事件,只有经过自身时才会触发。
操作元素
修改元素内容
- element.innerText | 起始位置稻种植未知的内容,但它去除Html标签,同时空格和空行也会去掉
- element.innerHTML | 起始位置到终止位置的全部内容,包括html,同时保留空格和换行
示例 | 鼠标移动进去显示中文,离开显示英文
<body>
<button>显示中文</button>
<div style="background: antiquewhite;border: .1px solid gray">Hello World</div>
<script>
//1. 获取元素
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onmouseover = function () {
div.innerText = '你好,世界!'
}
btn.onmouseleave = function () {
div.innerText = 'Hello World'
}
</script>
</body>
鼠标移动上去显示中文,鼠标移出之后显示英文。
区别 | innerText 与 innerHTML 的区别
innetText:它会原封不动的将我们给它的字符串显示到页面上(无法识别HTML标签)
innerHTML:如果发现是html标签,它会解析html标签并显示
尽量使用innerHTML,这是W3C发布的标准
<body>
<button>点击加粗名字</button>
<div>我是Jim.kk</div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function () {
div.innerHTML = '<b>我是Jim.kk</b>';
}
</script>
</body>
如果是使用innerText
的方式的话,则会原封不动的显示双引号的内容,下面看示例:
<body>
<button>点击加粗名字</button>
<div>我是Jim.kk</div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function () {
div.innerText = '<b>我是Jim.kk</b>';
}
</script>
</body>
通过innetText 和 innerHTML 获取文字内容
两者的区别在于:innerText只会获取文字
innerText
<body>
<p>
一段文字:
<span>我是Jim.kk</span>
</p>
<script>
var p = document.querySelector('p');
console.log(p.innerText); // 一段文字: 我是Jim.kk
</script>
</body>
innerHTML
<body>
<p>
一段文字:
<span>我是Jim.kk</span>
</p>
<script>
var p = document.querySelector('p');
console.log(p.innerHTML); // 一段文字:(换行)<span>我是Jim.kk</span>
</script>
</body>
修改元素属性
- src、href
- id、alt、title
<body>
<button id="btn1">图片1</button>
<button id="btn2">图片2</button>
<div style="margin-top: 20px">
<img src="img/img1.png" alt="" style="width: 500px" title="IMG1">
</div>
<script>
// 修改图片属性
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var img = document.querySelector('img');
btn1.onclick = function () {
img.src = 'img/img1.png';
img.title = 'IMG1';
}
btn2.onclick = function () {
img.src = 'img/img2.png';
img.title = 'IMG2';
}
</script>
</body>
以上代码点击按钮2显示一张图片,点击按钮1又会变回来
表单属性操作
修改表单文字
type、value、checked、selected、disabled
<body>
<input type="text" value="请输入内容">
<button id="btn1">按钮</button>
<button id="btn2">禁用</button>
<script>
// 1. 获取元素
var btn1 = document.getElementById('btn1');
var btn2 = document.getElementById('btn2');
var input = document.querySelector('input');
btn1.onclick = function () {
input.value = '123';
}
btn2.onclick = function () {
input.disabled = true;
this.disabled = true; // this指向的是btn,谁调用函数就指向谁,这里是btn2
}
</script>
</body>
示例 | 输入密码
点击小眼睛显示密码,同时小眼睛睁开
再次点击隐藏密码,同时小眼睛关闭
<body>
<div class="box">
<label for="">
<img src="img/icons/close.png" alt="" id="eye">
</label>
<input type="password" name="" id="pwd">
</div>
<script>
var btn = document.getElementById('eye');
var pwd = document.getElementById('pwd');
var flag = 0;
btn.onclick = function () {
if(flag === 0) {
this.src = 'img/icons/open.png'
pwd.type='text';
flag = 1;
} else {
this.src = 'img/icons/close.png'
pwd.type='password';
flag = 0;
}
}
</script>
</body>
表单样式修改
可以用以下两种方式修改:
element.style='background: red';
element.style.background='red';
区别在于,第一种方式可以在引号内写入很多样式,第二种需要一个样式定义一行
示例 | 修改背景颜色
设置一个DIV,鼠标移入之后改变背景颜色,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>样式操作1</title>
<style>
div {
width: 200px;
height: 200px;
background: antiquewhite;
}
</style>
</head>
<body>
<div>
</div>
<script>
var div = document.querySelector('div');
div.onmouseover = function () {
div.style = 'background: red';
}
div.onmouseleave = function () {
div.style = 'background: antiquewhite';
}
</script>
</body>
</html>
或者像下面这么写:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>样式操作2</title>
<style>
div {
width: 200px;
height: 200px;
background: antiquewhite;
}
</style>
</head>
<body>
<div></div>
<script>
var div = document.querySelector('div');
div.onmouseover = function () {
div.style.background = 'red';
}
div.onmouseleave = function () {
div.style.background = 'antiquewhite';
}
</script>
</body>
</html>
示例 | 淘宝精灵图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>淘宝精灵图案例</title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
}
.box {;
width: 250px;
margin: 100px auto;
}
.box li {
float: left;
width: 24px;
height: 24px;
background: pink;
margin: 15px;
background: url(img/icons/sprite.png) no-repeat;
}
</style>
</head>
<body>
<div class="box">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
// 1. 获取所有li
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length ; i++){
// 让索引号*44获取每个li的背景y坐标
var index = i * 44;
lis[i].style.backgroundPosition = '0 -' + index + 'px'
}
</script>
</body>
</html>
示例 | 得到焦点与失去焦点
以下示例有一个磨人的输入框,若是输入框的默认值是’手机’,则在得到焦点的时候清空输入框,若是失去焦点的时候输入框内容是空的,则显示手机。
<body>
<input type="text" value="手机"></input>
<script>
var input = document.querySelector('input');
input.onfocus = function () {
// console.log('得到焦点');
if ( input.value === '手机' ) {
this.value = '';
}
}
input.onblur = function () {
if ( input.value === '' ) {
input.value = '手机';
}
}
</script>
</body>
使用className
修改样式
上述方法是一条一条的style属性,除了以上方法外,我们还可以同时定义多个div,然后通过
element.className='divName'
的方式修改它的样式
如果想要即保留原来的类名,有来个新的类名,只需要
element.className='oldDivName newDivName'
下面源码是个很好的示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>className</title>
<style>
.div1{
height: 50px;
background: antiquewhite;
box-shadow: 0 0 5px gray;
border: 3px solid gray;
border-radius: 5px;
}
.div2{
height: 100px;
background: red;
box-shadow: 0 0 20px gray inset;
border: .1px solid gray;
border-radius: 50px;
}
</style>
</head>
<body>
<div class="div1"></div>
<script>
var div = document.querySelector('div');
div.onmouseenter = function () {
div.className = 'div2';
}
div.onmouseleave = function () {
div.className = 'div1';
}
</script>
</body>
</html>
自定义属性值
- 通过
element.getAttribute('')
可以获取属性- 这种方式可以获取自定义属性
- 通过
element.setAttribute('属性名','属性值'')
可以为元素自定义属性
- 通过
getAttribute
获取自定义属性值
div中本来是不存在
index
这个内置属性的,但是我们设置之后可以通过getAttribute
来获取:
<body>
<div index="1" id="div1"></div>
<script>
var div = document.querySelector('div');
console.log(div.getAttribute('id')); // div1
console.log(div.getAttribute('index')); // 1
</script>
</body>
- 通过
setAttribute
设置自定义属性
除了直接将自定义属性写在标签里面以外,还可以通过
setAttribute
的方式设置自定义属性
<body>
<div id="div1"></div>
<script>
var div = document.querySelector('div');
div.setAttribute('xx','123');
console.log(div.getAttribute('xx')); // 123
// 修改内置属性值
div.setAttribute('id','divx');
console.log(div.getAttribute('id')); // divx
</script>
</body>
- H5 新标准
H5新标准中,规定所有的自定义属性都得以
data-
开头,所以又新增了dataset
方法让我们获取自定义属性这种方法只能获取
data-
开头的自定义属性
<div data-index="1" data-list-name="Jim.kk"></div>
<script>
var div = document.querySelector('div');
console.log(div.dataset.index); // 1
console.log(div.dataset['index']) // 1
console.log(div.dataset.listName); // Jim.kk
console.log(div.dataset['listName']) // Jim.kk
</script>
节点操作
什么是节点?网页中所有的内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。
HTML DOM树种的所有节点均可通过JavaScript进行访问,所有的HTML元素(节点)均可被修改,也可以被创建或者删除。
节点一般由:nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
以上截图对应以下代码(注意,由于没有规定编码,在浏览器中打开可能存在乱码):
<html lang="en">
<head>
<title>节点操作</title>
</head>
<body>
<a href="www.baidu.com">我的链接</a>
<h1>我的标题</h1>
</body>
</html>
利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
node.parentNode | 获取父节点
<body>
<div>
<span>新年快乐!龙年行大运!!!</span>
</div>
<script>
var span = document.querySelector('span');
var div = span.parentNode;
console.log(div);
</script>
</body>
node.childNodes | 获取子节点
childNode
默认会获得所有的子节点,包括文字和标签,所以以下代码输出的node长度为9(有五个换行)如果不想要换行(毕竟也没什么用),可以使用
children
来代替childNode
children
并不是官方的,但是获得了各浏览器的支持,所以放心使用。
<body>
<ul>
<li>新年快乐!龙年行大运!!!</li>
<li>新年快乐!龙年行大运!!!</li>
<li>新年快乐!龙年行大运!!!</li>
<li>新年快乐!龙年行大运!!!</li>
</ul>
<ul>
<li>我是Jim.kk!!!</li>
<li>我是Jim.kk!!!</li>
<li>我是Jim.kk!!!</li>
<li>我是Jim.kk!!!</li>
</ul>
<script>
var ul = document.querySelector('ul');
console.log(ul.childNodes); // NodeList(9) [text, li, text, li, text, li, text, li, text]
console.log(ul.children); // HTMLCollection(4)
</script>
</body>
获取特定子节点
可以使用
firstChild
来获取第一个节点,但是这种获取方式仍然会获取换行等文本节点
方法 | 说明 | 备注 |
---|---|---|
element.firstChild | 获取第一个元素,但是会包含文本信息,也就是会获取你的换行等信息 | |
element.lastChild | 获取最后一个元素,但是会包含文本信息,也就是会获取你的换行等信息 | |
element.firstElementChild | 获取第一个元素,不包含文本信息,也就是只获取标签元素 | IE9以上才支持 |
element.lastElementChild | 获取最后一个元素,不包含文本信息,也就是只获取标签元素 | IE9以上才支持 |
element.children[0] | 用下标的方式获取某个指定的元素 | 实际开发中的用法 |
获取兄弟节点
获取下一个兄弟节点
方法 | 说明 | 备注 | 注意 |
---|---|---|---|
nextSibling | 获取下一个兄弟节点 | 也会获取text节点 | |
nextElementSibling | 获取下一个兄弟节点 | 只会获取标签节点 | IE9以上才支持 |
<body>
<div>新年快乐!龙年行大运</div>
<span>我是Jim.kk</span>
<script>
// 下一个兄弟节点存在
var div = document.querySelector('div');
console.log(div.nextSibling) // #text
console.log(div.nextElementSibling) // span
// 下一个兄弟节点是Script
var span = document.querySelector('span');
console.log(span.nextSibling) // #text
console.log(span.nextElementSibling) // <script>
// 下一个兄弟节点不存在
var script = document.querySelector('script');
console.log(script.nextSibling) // <link type="text/css" rel="stylesheet" id="dark-mode-custom-link">
console.log(script.nextElementSibling) // <link type="text/css" rel="stylesheet" id="dark-mode-custom-link">
</script>
</body>
获取上一个兄弟节点
方法 | 说明 | 备注 | 注意 |
---|---|---|---|
previousSibling | 获取上一个兄弟节点 | 也会获取text节点 | |
previousElementSibling | 获取上一个兄弟节点 | 只会获取标签节点,如果不存在则会返回null | IE9以上才支持 |
<body>
<div>新年快乐!龙年行大运</div>
<span>我是Jim.kk</span>
<script>
var span = document.querySelector('span');
console.log(span.previousSibling); // #text
console.log(span.previousElementSibling); // <div>新年快乐!龙年行大运</div>
// 当前节点是第一个节点,会获取一个空
var div = document.querySelector('div');
console.log(div.previousSibling) // #text
console.log(div.previousElementSibling) // null
</script>
</body>
创建节点
- 创建一个节点 |
document.createElement();
- 添加到某个地方去
2.1fatherElement.appendChild(childElement);
| 在父节点内部追加节点
2.2fatherElement.insertBefore(childElement,fatherElement.children[0]);
| 在父节点内部的某个子节点前面插入这个元素
<body>
<ul></ul>
<script>
var li1 = document.createElement('li');
var ul = document.querySelector('ul');
ul.appendChild(li1); // 在ul的子节点中追加这个元素
var li2 = document.createElement('li');
ul.insertBefore(li2,ul.children[0]); // 在ul的0号元素前面插入一个li2
</script>
</body>
删除节点
node.removeChild()
| 删除父亲中的某个孩子
<body>
<ul>
<li>兔年</li> <!-- 打错了,删除掉 -->
<li>龙年</li>
<li>行大运</li>
</ul>
<script>
// 1. 获取元素
var ul = document.querySelector('ul');
// 2. 删除元素
ul.removeChild(ul.children[0]);
</script>
</body>
拷贝节点
我们可以使用
node.cloneNode()
来拷贝节点,但是如果括号中为空或者是false,则只会克隆节点本身,而不会克隆里面的子节点,如果想要克隆里面的子节点,我们在括号内写入true
即可。
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode();
var li1 = ul.children[0].cloneNode();
ul.appendChild(li1); // 节点为空 | 因为上面的括号参数为空或者false,是浅拷贝,则只克隆节点本身,不克隆里面的子节点
var li2 = ul.children[0].cloneNode(true);
ul.appendChild(li2); // 节点为空 | 因为上面的括号参数为空或者false,是浅拷贝,则只克隆节点本身,不克隆里面的子节点
</script>
</body>
document.write()
| 不推荐
document.write
是直接将内容写入页面的内容流,但是文档流执行完毕,会导致页面全部重绘。简单解释下:若是我们在页面加载的时候就直接在script中写入
document.write
,那么里面的元素会追加在页面的最下面,但是若是通过页面中一个按钮点击后再加载的话,那么整个页面会只剩下这一个元素。示例如下:
- 直接加载元素
<body>
<p>abc</p>
<script>
var btn = document.querySelector('button');
document.write('<div>我是Jim.kk</div>');
</script>
</body>
可以看到,由于页面加载的时候就已经执行了这个方法,所以该方法中的内容是被追加到页面最下面的。
<body>
<button>点击</button>
<p>abc</p>
<script>
var btn = document.querySelector('button');
btn.onclick = function () {
document.write('<div>我是Jim.kk</div>')
}
</script>
</body>
由于页面已经渲染完毕,这时候我们点击按钮写入这个元素,页面中的内容直接被覆盖了,只留下了我们写入的元素(建议自己试验一下试试效果)。
监听事件
给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式。
方式比较:
传统方式
- 传统方式利用on开头的事件,比如
onclick
、onmouseenter
- 特点:注册事件的唯一性
- 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数会覆盖前面注册的处理函数。
下面代码中,由于我们给btn先后注册了两个点击事件,所以后面的点击事件会覆盖掉前面的点击事件,点击之后会跳出弹框’Hello~’
<body>
<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.onclick = function () {
alert('Hi~')
}
btn.onclick = function () {
alert('Hello~')
}
</script>
</body>
监听事件
- W3C 标准 推荐方式
addEventListener()
是一个方法- IE9之前的IE浏览器不支持此方法,可以使用
attachEvent()
代替- 同一个元素,同一个事件可以添加多个
添加监听事件
书写方式:
eventTarget.addEventListener(type,listener[,useCapture])
eventTarget.addEventListener()
方法将制定的监听器注册到eventTarget
上(目标对象),当该对象出发指定的事件时,就会执行事件处理函数。
type
:事件类型字符串,比如click
、mouseover
,注意这里不带onlistener
:事件处理函数,事件发生时,会调用该监听函数useCapture
:可选参数,是一个布尔值,默认是false。
添加监听事件的两个方法:
方法 | 说明 | 备注 |
---|---|---|
addEventListener | IE9以后才支持 | 推荐 |
attachEvent | IE9以前才支持 |
<body>
<button>监听事件</button>
<script>
// 2. 监听事件
var btn = document.querySelector('button');
btn.addEventListener('click',function () {
alert('Hello World');
})
btn.addEventListener('click',function () {
alert('我是Jim.kk');
})
</script>
</body>
以上代码点击按钮之后,会连续跳出两次弹框,第一个弹框显示
Hello World
,当你关闭该弹框之后,会再次跳出一个弹框,显示我是Jim.kk
。
解绑事件
- 传统方式中,我们可以使用
element.onclick = null
的方式解绑事件- 监听事件中,我们要移除事件,就不能再用匿名函数了,而是要给函数一个名字,然后
removeEventListener
这个事件
<body>
<!-- 传统方式 -->
<button id="btn1">传统事件</button>
<script>
var btn = document.querySelector('#btn1');
btn.onclick = function () {
alert('我是Jim.kk');
btn.onclick = null; // 解绑事件,再次点击就没用了
}
</script>
<!-- 监听方式 -->
<button id="btn2">监听方式</button>
<script>
var btn = document.querySelector('#btn2');
function fun() {
alert('Jim.kk祝大家新年快乐!!');
btn.removeEventListener('click',fun)
}
btn.addEventListener('click',fun);
</script>
</body>
在IE9之前的浏览器中,我们只能使用
attachEvent
添加监听函数,在这种情况下我们要使用detachEvent
的方式来移除事件,用法与removeEventListener
无异,这里不做演示。
事件流
事件执行流程
事件流描述的是从页面中接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。
比如我们给DIV注册了点击事件
DOM事件流分为三个阶段:
- 补货阶段
- 当前目标节点
- 冒泡阶段
捕获阶段
- 虽然我是给Div添加的点击事件,但是点击事件的接收者是
Document
Document
并没有绑定这个点击事件,接下来向下找,找到html
html
也没有绑定,继续向下找找到body
body
也没有绑定,接下来找到div
,这时候发现div
绑定了这个事件
目标节点
- 找到
div
之后,进入到当前目标阶段,开始执行事件
冒泡阶段
div
执行事件结束,将事件向上传播,依次给body
-html
-Document
,这里就是冒泡阶段
事件冒泡:IE最早提出,事件开始时由最具体的元素接收,然后主机箱上传播到DOM最顶层节点的过程
事件捕获:网景公司最早提出,有DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收过程
验证
如果
addEventListener
第三个参数是true,那么则处于捕获阶段,执行顺序是由外向内,也就是document-html-body-target
如果addEventListener
第三个参数是false或者空,那么则处于冒泡阶段,执行顺序是由内到外,也就是target-body-html-document
以下代码点击之后先弹出
father
框,关闭后再弹出son
框
<head>
<meta charset="UTF-8">
<title>事件流</title>
<style>
.father {
width: 400px;
height: 400px;
margin: 200px auto;
padding: 50px;
background: antiquewhite;
}
.son {
background: gray;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流三阶段:
// 1. JS 代码中只能执行捕获或者冒泡其中一个阶段
// 2. onclick 和 attachEvent(ie)只能得到冒泡阶段
// 3. 捕获阶段,如果addEventListener第三个参数是true,那么则处于捕获阶段 document-html-body-father-son
var son = document.querySelector('.son');
son.addEventListener('click',function () {
alert('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click',function () {
alert('father');
}, true)
</script>
</body>
以下代码点击后先弹出
son
框,再弹出father
框,最后弹出document
框。
<head>
<meta charset="UTF-8">
<title>事件流</title>
<style>
.father {
width: 400px;
height: 400px;
margin: 200px auto;
padding: 50px;
background: antiquewhite;
}
.son {
background: gray;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 4. 冒泡阶段:如果addEventListener第三个参数是false,或者省略,那么就是冒泡阶段,执行顺序 son-father-body-html-document
var son = document.querySelector('.son');
son.addEventListener('click',function () {
alert('son');
});
var father = document.querySelector('.father');
father.addEventListener('click',function () {
alert('father');
});
document.addEventListener('click',function () {
alert('document');
})
</script>
</body>
事件对象
- event 就是一个事件对象,写到侦听函数的小括号中
- 事件对象只有有了事件才会存在,它是系统自动创建的,无需传递参数
- 事件对象是事件相关的一系列相关数据的集合,跟事件相关,比如鼠标点击里面就包含了鼠标的相关信息,如果是键盘事件里面就包含了键盘的信息,比如:判断用户按下了哪个键
- 事件对象不一定非要写成
event
,名字可以随便写- 事件对象也有兼容性问题,ie678通过window.event获取事件对象。
获取事件对象
传统方式获取事件对象
<body>
<div>一个盒子</div>
<script>
var div = document.querySelector('div');
div.onclick = function (event) {
// 1. event 就是一个事件对象,写到侦听函数的小括号中
// 2. 事件对象只有有了事件才会存在,它是系统自动创建的,无需传递参数
// 3. 事件对象是事件相关的一系列相关数据的集合,跟时间相关,比如鼠标点击里面就包含了鼠标的相关信息,如果是键盘事件里面就包含了键盘的信息,比如:判断用户按下了哪个键
console.log(event);
}
</script>
</body>
以上代码点击之后会在控制台输出鼠标的相关信息,如截图中所示:
监听方式获取事件对象
以下代码的执行顺序与上面代码无异,不做演示
<body>
<div>一个盒子</div>
<script>
var div = document.querySelector('div');
div.addEventListener('click',function (event) {
console.log(event);
})
</script>
</body>
在ie678中使用监听对象
<body>
<div>一个盒子</div>
<script>
var div = document.querySelector('div');
div.addEventListener('click',function (event) {
console.log(event); // undefined
console.log(window.event); // 事件对象
})
</script>
</body>
事件对象的常见属性和方法
事件对象属性方法 | 说明 | 备注 |
---|---|---|
e.target | 返回触发事件的对象 | 标准 |
e.srcElement | 返回触发事件的对象 | 非标准 ie678使用 |
e.type | 返回时间的类型,比如’click’、‘mouseover’ | 不带on |
e.cancelbubble | 该属性组织冒泡 | 非标准 ie678使用 |
e.returnValue | 该属性组织默认时间(默认行为) | 非标准 ie678使用,比如不让连接跳转 |
e.preventDefault() | 该方法阻止默认事件(默认行为) | 标准,比如不让链接跳转 |
e.stopPropagation() | 阻止冒泡 | 标准 |
this
:我们给谁绑定了事件,那么this就指向谁event.target
:指向我们点击的那个对象event.currentEvent
类似于this
,但是ie678不支持
阻止默认行为
什么是默认行为?比如a标签是跳转。
<body>
<a href="https://baidu.com">百度</a>
<script>
var a = document.querySelector('a');
// 1. 监听方式
a.addEventListener('click',function (e) {
e.preventDefault(); // DOM 标准写法
})
// 2. 传统方式
a.onclick = function (e) {
// 普通浏览器 e.preventDefault(); 方法
e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
e.returnValue;
// 也可以使用return false 阻止默认方法,没有兼容问题
return false;
}
</script>
</body>
其中监听浏览器只能使用
e.preventDefault()
的写法传统方式可以采用三种方式
e.preventDefault();
e.returnValue;
// 仅支持 ie678return false;
最推荐的写法,没有兼容问题
阻止冒泡事件
冒泡事件:开始时由最具体的元素接收,然后主机箱上传播到DOM最顶层节点。
事件冒泡本身的特定会带来一定的好处,也会带来一定的坏处,需要我们灵活掌握。
阻止冒泡排序
stopPropagation()
方法
在事件方法中对事件对象使用stopPropagation()
即可
在ie678中使用window.event.cancelBubble = true;
的写法
事件委托
事件委托也称为事件代理,在
jQuery
里面成为事件委派。不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
示例
给
ul
注册点击事件,然后利用时间对象的target来找到当前点击的li
,因为点击li
,事件会冒泡到ul
上,ul
有注册事件,就会触发事件监听器。
一起看一下以下代码:
- 我们给父元素
ul
添加点击事件,这样当我们点击子元素li
之后,就会通过冒泡的方式传递到ul
上 - 我们在点击事件中通过target获取我们点击的元素,然后给它一个背景颜色
- 完美
<body>
<ul>
<li>弹框咯</li>
<li>弹框咯</li>
<li>弹框咯</li>
<li>弹框咯</li>
<li>弹框咯</li>
<li>弹框咯</li>
<li>弹框咯</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click',function (e) {
e.target.style.backgroundColor = 'pink';
})
</script>
</body>
禁用鼠标右键
<script>
document.addEventListener('contextmenu',function (e) {
e.preventDefault();
})
</script>
注意:由于是监听事件,不支持return false;
的写法,必须要用e.preventDefault();
禁用文字选择
<body>
<span>我是Jim.kk</span>
<script>
document.addEventListener('selectstart',function (e) {
e.preventDefault();
})
</script>
</body>
获取鼠标坐标信息
鼠标事件对象 | 说明 | 备注 |
---|---|---|
e.clientX | 返回鼠标相对于浏览器窗口可视区的X坐标 | |
e.clientY | 返回鼠标相对于浏览器窗口可视区的Y坐标 | |
e.pageX | 返回鼠标相对于文档页面的X坐标 | IE9+ 支持 |
e.pageY | 返回鼠标相对于文档页面的Y坐标 | IE9+ 支持 |
e.screenX | 返回鼠标相对于电脑屏幕的X坐标 | |
e.screenY | 返回鼠标相对于电脑屏幕的Y坐标 |
<head>
<meta charset="UTF-8">
<title>获取鼠标坐标 </title>
<style>
body {
height: 3000px;
}
</style>
</head>
<body>
<script>
document.addEventListener('click',function (e) {
console.log(e.clientX); // 可视窗口的X坐标(纯网页部分)
console.log(e.clientY); // 可视窗口的Y坐标(纯网页部分)
console.log(e.pageX); // 页面上的X坐标(若是页面发生了滚动,则该值也会增大缩小,clientX/Y不会)
console.log(e.pageY); // 页面上的X坐标(若是页面发生了滚动,则该值也会增大缩小,clientX/Y不会)
console.log(e.screenX); // 当前屏幕的坐标,包含浏览器上册的操作栏部分
console.log(e.screenY); // 当前屏幕的坐标,包含浏览器上册的操作栏部分
})
</script>
</body>
以上代码点击后输出内容如下所示(页面已经滚动):
常用键盘事件
键盘事件 | 触发条件 | 备注 |
---|---|---|
onkeyup | 某个按键松开时触发 | |
onkeydown | 某个按键按下时触发 | |
onkeypress | 某个按键按下时被触发 | 可以区分字母大小写,但是不能识别功能键,比如:ctrl、shift、箭头等 |
keydown
| 键盘被按下
<body>
<script>
document.addEventListener('keydown',function (e) {
console.log(e.key+"被按下了");
})
</script>
</body>
以上代码,当我在页面上按住"s"键不放,效果如下:
keyup
| 键盘弹起事件
与键盘按下不太一样,当键盘持续被按着时,键盘按下事件会一直被触发,但是键盘弹起事件只有在弹起的时候会被触发一次,见以下代码。
<body>
<script>
document.addEventListener('keyup',function (e) {
console.log(e.key+"弹起了");
})
</script>
</body>
以上代码在我点击键盘后(长按弹起后才会生效),控制台输出如下:
keypress
| 键盘按下事件
与上面两个方法不同的是,keypress
按钮对功能键不生效。
<body>
<script>
document.addEventListener('keypress',function (e) {
console.log(e.key+"被按下了");
})
</script>
</body>
以上代码当我在键盘上分别点击a
、s
、d
、f
和ctrl
键后,控制台输出如下:
可以看到,ctrl
键并没有生效。
我们可以通过event.key
来获取键盘的值,或者event.keyCode
来获取ASCII码值。
按下s键输入框获取焦点案例
<body>
<input type="text">
<script>
var search = document.querySelector('input');
document.addEventListener('keyup',function (e) {
if ( e.key === 's') {
search.focus();
}
})
</script>
</body>
以上代码用户在页面中点击s
键,输入框就会获取焦点,用户就可以在输入框中输入内容了。