该系列文章是博主学习前端入门课程的笔记,同时也为了方便查阅,有任何错漏或疑问都欢迎在评论区提出。
第一章 节点操作
1.1 为什么需要节点操作?
前面介绍了利用DOM提供的方法获取元素
- document.getelementById()
- document.getelementByClassName()
- document.querySelector等
这种方法很繁琐,且不能体现页面的层级关系,使用节点操作获取元素会更简单且能体现页面的层级关系
1.2 节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。
HTML DOM树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。
节点至少有 nodeType , nodeName , nodeValue 这三个基本属性,在实际开发中通常操作的是元素节点。
- 元素节点 nodeType 为 1
- 属性节点 nodeType 为 2
- 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
<div class="box">
<span class="erweima">x</span>
</div>
<script>
var box = document.querySelector('.box');
console.dir(box);
1.3 节点层级
利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
1. 父节点
node.parentNode:返回距离最近的父节点
<div class="demo">
<div class="box">
<span class="erweima">x</span>
</div>
</div>
<script>
// 父节点 parentNode
var erweima = document.querySelector('.erweima');
// 得到的是离得最近的父级节点,如果找不到父节点就返回null
console.log(erweima.parentNode);
var demo = document.querySelector('.demo');
console.log(demo.parentNode);
console.log(document.parentNode);
</script>
输出
2. 子节点
- node.childNodes(标准):返回指定节点的所有子节点(包括元素节点,文本节点等)。
代码示例
<ul>
<li>
<div class="test">我是li</div>
</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// 1.DOM提供的方法(API)获取ul中的li
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 2.子级节点 childNodes 返回所有的子节点,包含元素节点和文本节点
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 如果只需要元素节点,需要特别操作,所有不提倡使用childNode
for (var i = 0; i < ul.childNodes.length; i++) {
// console.log(i);
if (ul.childNodes[i].nodeType == 1) {
console.log(ul.childNodes[i]);
}
}
</script>
控制台输出
- node.children(非标准但得到各个浏览器的支持,更常用):返回指定节点的所有子元素节点
代码示例
<ul>
<li>
<div class="test">我是li</div>
</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// 1.DOM提供的方法(API)获取ul中的li
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 3.children 获取所有的子元素节点,开发常用
console.log(ul.children);
</script>
控制台输出
- node.firstChild:返回第一个子节点,找不到则返回null。
- node.lastChild: 返回最后一个子节点,找不到则返回null。
- node.firstElementChild:返回第一个子元素节点,找不到则返回null。
- node.lastElementChild:返回最后一个子元素节点,找不到则返回null。
代码示例
<ul>
<li>第1个li</li>
<li>第2个li</li>
<li>第3个li</li>
<li>第4个li</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1.firstChild 返回第一个子节点,无论是元素节点还是文本节点
console.log(ul.firstChild);
console.log(ul.lastChild);
// 2.firstElementChile 返回第一个子元素节点,有兼容性问题,IE9以上才支持
console.log(ul.firstElementChild);
console.log(ul.lastElementChild);
// 3.实际开发写法
console.log(ul.children[0]);
console.log(ul.children[ul.children.length - 1]);
</script>
控制台输出
、
案例:仿新浪下拉菜单
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
font-size: 14px;
}
.nav {
margin: 100px;
}
.nav>li {
position: relative;
float: left;
width: 80px;
height: 41px;
text-align: center;
}
.nav li a {
display: block;
width: 100%;
height: 100%;
line-height: 41px;
color: #333;
}
.nav>li>a:hover {
background-color: #eee;
}
.nav ul {
display: none;
position: absolute;
top: 41px;
left: 0;
width: 100%;
border-left: 1px solid #FECC5B;
border-right: 1px solid #FECC5B;
}
.nav ul li {
border-bottom: 1px solid #FECC5B;
}
.nav ul li a:hover {
background-color: #FFF5DA;
}
</style>
<ul class="nav">
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
</ul>
<script>
// 1.获取元素
var nav = document.querySelector('.nav');
var lis = nav.querySelectorAll('li');
// 2.循环注册事件
for (var i = 0; i < lis.length; i++) {
// 相较于之前自定义属性的方式,节点操作更简洁
lis[i].onmouseenter = function () {
this.children[1].style.display = 'block';
}
lis[i].onmouseleave = function () {
this.children[1].style.display = 'none';
}
}
</script>
3. 兄弟节点
- node.nextSibling:返回当前元素的下一个兄弟节点,找不到则返回null。
- node.previousSibling:返回当前元素的上一个兄弟节点,找不到则返回null。
- node.nextElementSibling:返回当前元素下一个兄弟元素节点,找不到则返回null。有兼容性问题, IE9 以上才支持。
- node.previousElementSibling:返回当前元素上一个兄弟元素节点,找不到则返回null。有兼容性问题, IE9 以上才支持。
代码示例
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一个兄弟节点:包含元素节点和文本节点
console.log(div.nextSibling);
console.log(div.previousSibling);
// 2.nextElementSibling 下一个兄弟元素节点
console.log(div.nextElementSibling);
console.log(div.previousElementSibling); // 找不到时输出null
</script>
控制台输出
兼容性写法:既能只返回兄弟元素节点,又不产生兼容性问题。了解即可,实际开发通常不要求
function getNextElementSibling(element) {
var el = element;
// 如果存在兄弟节点则循环继续
while (el = el.nextSibling) {
// 兄弟节点是元素类型时返回
if (el.nodeType === 1) {
return el;
}
}
return null;
}
了解到如何获取节点后,接下来可以尝试对节点进行操作了
1.4 创建和添加节点
document.createElement(‘tagName’) :创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,是根据需求动态生成的,所以也称为动态创建元素节点
<ul>
<li>123</li>
</ul>
<script>
// 1.创建元素节点
var li = document.createElement('li');
</script>
利用createElement创建节点后发现并没有在页面中看到节点,还需要将节点添加到指定位置
添加节点有两种方式
- parentNode.appendChild(child):将一个节点添加到指定节点的子节点列表末尾。类似于 CSS 里面的 after 伪元素。
- parentNode.insertBefore(child,指定元素):将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 伪元素。
<ul>
<li>123</li>
</ul>
<script>
// 1.创建元素节点
var li = document.createElement('li');
// 2.添加节点 node.appendChild(child) 在元素末尾追加元素,类似数组的push()方法
var ul = document.querySelector('ul');
li.innerHTML = 'appendChild';
ul.appendChild(li);
// 3.添加节点 node.insertBefore(child,指定元素) 添加到指定元素的前面
var lili = document.createElement('li');
lili.innerHTML = 'insertBefore';
ul.insertBefore(lili, ul.children[0]);
</script>
效果图
1.5 删除节点
parentNode.removeChild(child):从 DOM 中删除一个子节点,返回删除的节点。
案例:点击按钮后会删除1个li,但li全被删除时按钮被禁用
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector('ul');
// 2.删除元素
// ul.removeChild(ul.children[0]);
// 3.点击按钮依次删除子元素
var btn = document.querySelector('button');
btn.onclick = function () {
console.log(ul.removeChild(ul.children[0]));
if (ul.children.length == 0) {
btn.disabled = true;
}
}
</script>
效果图
案例:评论区案例
<style>
body {
padding: 100px;
}
li {
width: 300px;
background-color: pink;
color: red;
margin: 5px 0;
}
li a {
float: right;
}
</style>
<textarea name="" id="" cols="30" rows="10"></textarea>
<button>发布</button>
<ul></ul>
<script>
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
btn.onclick = function () {
if (text.value == '') {
alert('您没有输入内容');
return false;
} else {
// a.创建节点
var li = document.createElement('li');
// 表单的值是通过value获得的
// href="javascript:; 阻止链接跳转 javascript:void(0)也同理
li.innerHTML = text.value + '<a href="javascript:;">删除</a>';
// b.添加节点
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
// c.删除元素
var as = document.querySelectorAll('a');
// 每次点击都要给所有评论添加函数,好像不太合理
for (var i = 0; i < as.length; i++) {
as[i].onclick = function () {
// node.removeChild(child);
ul.removeChild(this.parentNode);
}
}
}
}
</script>
效果图
1.6 复制节点
node.cloneNode(boolean):返回调用该方法的节点的一个副本。
- 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点
代码示例
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1.node.cloneNode() 括号为空或false是浅拷贝,只复制标签不复制子节点
var liFalse = ul.children[0].cloneNode();
ul.appendChild(liFalse);
// 2.node.cloneNode() 括号为true是深拷贝,会复制节点本身和里面所有的子节点
var liTrue = ul.children[0].cloneNode(true);
ul.appendChild(liTrue);
</script>
效果图
1.7 替换节点
parentNode.replaceNode(newChild,oldChild):用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点
1.8 三种创建元素的方式
- document.write():如果页面文档流加载完毕,再调用这句话会导致页面重绘
代码示例
<button>点击</button>
<p>123</p>
<div class="inner"></div>
<div class="create"></div>
<script>
// 1.document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
var btn = document.querySelector('button');
btn.onclick = function () {
document.write('<div>123</div>');
}
document.write('<div>123</div>');
</script>
点击按钮后页面变为
2. innerHTML:将内容写入某个 DOM 节点,不会导致页面全部重绘,但是复制节点的时候,不会复制原先节点的事件,存在内存泄露问题
3. document.createAttribute():注意与innerHTML效率上的区别:innerHTML字符串拼接 < createAttribute < innerHTML数组
<button>点击</button>
<p>123</p>
<div class="inner"></div>
<div class="create"></div>
<script>
// 2.innerHTML 创建元素
var inner = document.querySelector('.inner');
// 采取字符换拼接的形式效率低
inner.innerHTML = '<a href="#">百度</a>';
// 利用数组能极大提升效率
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3.document.createElement() 创建元素
var create = document.querySelector('.create');
var a = document.createElement('a');
create.appendChild(a);
</script>
1.9 DOM总结
文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
- 对于JavaScript,为了能够使JavaScript操作HTML,JavaScript就有了一套自己的dom编程接口。
- 对于HTML,dom使得html形成一棵dom树. 包含 文档、元素、节点
获取过来的DOM元素是一个对象(object),所以称为文档对象模型
dom操作主要是针对于元素的操作。主要有创建、添加、删除、修改、获取、属性操作、事件操作。
1. 创建元素
- document.write()
- innerHTML
- createElement(‘标签名’)
2. 添加元素
- appendChild(chile)
- insertBefore(child,指定元素)
3. 删除元素
- parentNode.removeChild(child)
4. 修改元素(属性、内容、元素样式)
主要修改dom的元素属性,dom元素的内容、属性, 表单的值等
- 修改元素属性: src、href、title等
- 修改普通元素内容: innerHTML 、innerText
- 修改表单元素: value、type、disabled等
- 修改元素样式: style、className
5. 获取元素
主要获取查询dom的元素
- DOM提供的API 方法(古老用法,不推荐): getElementById、getElementsByTagName
- H5提供的新方法(提倡):querySelector、querySelectorAll
- 利用节点操作获取元素(提倡): 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)
6. 属性操作
- setAttribute:设置dom的属性值
- getAttribute:得到dom的属性值
- element.属性名:设置或获取dom的属性值
- removeAttribute:移除属性
7. 事件操作
给元素注册事件:事件源.事件类型 = 事件处理程序
第二章 事件高级
2.1 注册事件(绑定事件)
1. 注册事件概述
给元素添加事件,称为注册事件或者绑定事件
注册事件有两种方式:传统注册方式和方法监听注册方式
2. addEventListener 事件监听方式
eventTarget.addEventListener(type, listener[, useCapture]) :将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。这种方法存在兼容性问题,适用于ie9+
该方法有三个参数:
- type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
- listener:事件处理函数,事件发生时,会调用该监听函数
- useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,再进一步讲解
<button>传统注册事件</button>
<button>方法监听注册事件</button>
<button>ie9attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// 1.传统注册事件
btns[0].onclick = function () {
console.log('hi');
}
// 最后一个事件(同一个元素同一个注册事件)会覆盖前面的事件
btns[0].onclick = function () {
console.log('hello');
}
// 2.方法监听注册事件
// a.事件类型是字符串,且不带on
btns[1].addEventListener('click', function () {
console.log(22);
});
// b.同一个元素同一个事件可以添加多个监听器,会依次执行
btns[1].addEventListener('click', function () {
console.log(33);
})
</script>
控制台输出
3. attachEvent 事件监听方式
eventTarget.attachEvent(eventNameWithOn, callback)
ie9以前的版本进行方法监听注册的方式。作用与addEventListener相同:将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
该方法接收两个参数:
- eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,注意这里要带 on
- callback: 事件处理函数,当目标触发事件时回调函数被调用
代码示例
// 3.attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function () {
alert(11);
})
console.log(btns[2].attachEvent); // undefined,因为chrome浏览器不支持这种写法,所以输出是undefined
4. 注册事件兼容性解决方案(了解)
兼容性处理的原则: 首先照顾大多数浏览器,再处理特殊浏览器
<script>
function addEventListener(element, eventName, fn) {
// 判断当前浏览器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三个参数默认是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相当于 element.onclick = fn;
element['on' + eventName] = fn;
}
}
</script>
2. 删除事件(解绑事件)
1. 删除事件的方式
- 传统注册方式:eventTarget.onclick = null;
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySelectorAll('div');
divs[0].onclick = function () {
alert(11);
// 1.传统方式删除事件 对方法监听注册事件不生效
divs[0].onclick = null;
}
</script>
- 方法监听注册方式
① eventTarget.removeEventListener(type, listener[, useCapture]);
② eventTarget.detachEvent(eventNameWithOn, callback);
// 2.removeEventListener 删除事件
divs[1].addEventListener('click', fn) // 里面的函数不加()调用
function fn() {
alert(22);
divs[1].removeEventListener('click', fn);
}
// 3.detachEvent 删除事件
divs[2].attachEvent('onclick', fn1);
function fn1() {
alert(33);
div[2].detachEvent('click', fn1);
}
2. 删除事件兼容性解决方案(了解)
function removeEventListener(element, eventName, fn) {
// removeEventListener 和 detachEvent 两种方法不同时兼容
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三个参数 默认是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
}
element['on' + eventName] = null;
}
2.3 DOM事件流
事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。
DOM 事件流分为3个阶段:
- 捕获阶段:网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
- 当前目标阶段
- 冒泡阶段:IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
由此可以看出:当我们某个元素触发事件时,也可能对其他节点造成影响
代码示例:捕获阶段
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
text-align: center;
background-color: pink;
}
.son {
width: 100px;
height: 100px;
line-height: 100px;
margin: 100px;
background-color: skyblue;
}
</style>
// dom事件流 三个阶段:捕获 当前目标 冒泡
// 1.JS代码只能执行捕获或冒泡其中的一个阶段
// 2.onclick 和 attachEvent只能得到冒泡阶段
// 3.捕获阶段 如果addEventListener的第三个参数是true 那么处于捕获阶段
// document -> html -> body -> father -> son
var son = document.querySelector('.son');
son.addEventListener('click', function () {
console.log('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click', function () {
console.log('father');
}, true);
</script>
控制台输出
点击子盒子:捕获阶段是自上而下传递事件的,father先接收到click,所以先输出father在输出son
冒泡阶段
// 4.冒泡阶段 如果addEventListener的第三个参数是false或省略(默认是false) 那么处于冒泡阶段
// son -> father -> body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click', function () {
alert('son');
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function () {
alert('father');
}, false);
var document = document.querySelector('.document');
document.addEventListener('click', function () {
alert('document');
}, false);
控制台输出
点击子盒子:冒泡阶段是自下而上传递事件的,son -> father -> body -> html -> document依次接收click事件。实际开发对冒泡阶段的关注和使用会更多
2.4 事件对象
1. 什么是事件对象
官方解释:事件(event)对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。
简单理解:事件发生后,跟事件相关的一系列信息数据的集合,包含这个集合的对象就是事件对象event,它有很多属性和方法。
比如:
- 谁绑定了这个事件。
- 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
- 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
2. 事件对象的用法
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
</style>
<div>123</div>
<script>
var div = document.querySelector('div');
div.onclick = function (event) {
// 兼容性写法,ie6-8需要通过window.event获取
event = event || window.event;
console.log(event);
}
div.addEventListener('click', function (e) {
console.log(e);
})
</script>
总结:
- event就是一个事件对象,写到监听函数的小括号里面,当形参来看
- 事件对象在事件发生后才会存在,它是系统自动创建,不需要传参
- 事件对象是事件的一系列相关数据的集合(与事件相关) 比如鼠标点击click里面包含了鼠标点击的相关信息,例如鼠标坐标,如果是键盘事件就包含键盘事件的信息,例如用户按下的键
- 事件对象可以自行命名,如e、evt、event等等
- 事件对象存在兼容性问题,ie6-8需要通过window.event获取。兼容性写法 event = event || window.event;
2.5 事件对象的常见属性和方法
1. 常用属性 target
作用:返回触发事件的的对象
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 1.e.target 返回触发事件的对象 this 返回的是绑定事件的对象(元素)
var ul = document.querySelector('ul');
ul.addEventListener('click', function (e) {
// ul绑定事件,this指向ul
console.log(this);
// 与this非常相似的属性(作用相同),但存在兼容性问题 ie6-8不可用
console.log(e.currentTarget);
// e.target返回的是被点击的对象,谁触发这个事件 被点击的是li,所以返回li
console.log(e.target);
})
</script>
控制台输出(点击abc后):
前两个分别输出的是this和currentTarget,是调用绑定事件的对象ul;第三个输出的是target,是触发事件的对象li
target的兼容性写法
div.onclick = function (e) {
e = e || window.event;
// srcElement是非标准写法,是为了兼容ie678
var target = e.target || e.srcElement;
console.log(target);
}
2. 阻止事件冒泡
事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点( document )。事件冒泡本身的特性,会带来的坏处,也会带来好处,需要灵活掌握。
- 标准写法:利用事件对象里面的 stopPropagation()方法(适用于IE 9+)
<style>
.father {
overflow: hidden;
width: 300px;
height: 300px;
margin: 100px auto;
text-align: center;
background-color: pink;
}
.son {
width: 100px;
height: 100px;
line-height: 100px;
margin: 100px;
background-color: skyblue;
}
</style>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
<script>
// 阻止事件冒泡 dom标准写法 stopPropagation()
var son = document.querySelector('.son');
son.addEventListener('click', function (e) {
console.log('son');
e.stopPropagation();
e.cancelable = true; // 非标准写法,ie678
// 兼容性写法,了解即可。实际开发通常不要求
/* if (e && e.stopPropagation()) {
e.stopPropagation();
}else {
window.event.cancelBubble = true;
} */
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function () {
console.log('father');
}, false);
document.addEventListener('click', function () {
console.log('document');
}, false);
</script>
点击son盒子后,控制台输出
- 非标准写法: 利用事件对象的 cancelBubble 属性(适用于IE 6-8)
e.cancelBubble = true;
- 兼容性写法(了解)
if(e && e.stopPropagation){
e.stopPropagation();
} else{
window.event.cancelBubble = true;
}
3. 阻止事件默认行为
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 1.返回事件类型,
var div = document.querySelector('div');
div.addEventListener('click', fn);
div.addEventListener('mouseover', fn);
div.addEventListener('mouseout', fn);
function fn(e) {
console.log(e.type);
}
// 2.阻止默认行为(事件) 让链接不跳转或按钮不提交
var a = document.querySelector('a');
a.addEventListener('click', function (e) {
e.preventDefault(); // dom标准写法
});
// 3.传统注册事件
a.onclick = function (e) {
// 普通浏览器
e.preventDefault();
// ie678
e.returnValue;
// return 没有兼容性问题,也能阻止默认行为,但是return后面的代码不执行了。且只在传统注册方式生效
return false;
alert(11);
}
</script>
发现点击链接不跳转,点击div控制台有输出
2.6 事件委托
事件委托也称为事件代理, 在 jQuery 里面称为事件委派。
原理:把事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
优点:减少操作 DOM 的次数,能够提高程序的性能。
案例:点击每个 li 之后背景颜色都会改变且在控制台输出‘知否知否,点我应是弹框在手’
<ul>
<li>知否知否,点我应是弹框在手</li>
<li>知否知否,点我应是弹框在手</li>
<li>知否知否,点我应是弹框在手</li>
<li>知否知否,点我应是弹框在手</li>
<li>知否知否,点我应是弹框在手</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器,利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
var lis = document.querySelectorAll('li');
ul.addEventListener('click', function (e) {
// 子节点被点击后背景颜色改变
for (var i = 0; i < lis.length; i++) {
lis[i].style.background = 'none';
}
e.target.style.background = 'pink';
console.log('知否知否,点我应是弹框在手');
})
</script>
控制台输出
2.7 常用的鼠标事件
1. 常用鼠标事件
当我们不希望页面的内容被用户直接获取时,可以使用以下代码
<p>我是不希望被分享的文字</p>
<script>
// 1.禁用右键菜单 contextmenu
document.addEventListener('contextmenu', function (e) {
e.preventDefault();
})
// 2.禁止鼠标选中 selectstart
document.addEventListener('selectstart', function (e) {
e.preventDefault();
})
</script>
2. 鼠标事件对象
代码验证
<style>
body {
height: 3000px;
}
</style>
<script>
// 鼠标对象mouseEvent
document.addEventListener('click', function (e) {
// 1.client返回鼠标在可视区的坐标
console.log(e.clientX);
console.log(e.clientY);
console.log('--------------------');
// 2.page返回鼠标在页面文档的坐标,支持ie9+。最常用
console.log(e.pageX);
console.log(e.pageY);
console.log('--------------------');
// 3.screen返回鼠标在电脑屏幕的坐标
console.log(e.screenX);
console.log(e.screenY);
console.log('--------------------');
})
</script>
控制台输出
案例:跟随鼠标的天使(图片会跟随鼠标移动)
<style>
img {
position: absolute;
}
</style>
<img src="images/angel.gif" alt="">
<script>
var pic = document.querySelector('img')
// 鼠标移动1px时,函数就会执行
document.addEventListener('mousemove', function (e) {
var x = e.pageX;
var y = e.pageY;
// 数字做居中对齐的效果,是鼠标点在图片中央
pic.style.top = y - 40 + 'px';
pic.style.left = x - 48 + 'px';
})
</script>
2.8 常用的键盘事件
1. 常用的键盘事件
注意:
- onkeypress和其余两个的区别是:不识别功能键,如左右箭头、shift等
- 如果键盘按下不松开,onkeydown和onkeypress会一直触发
2. 返回键的ASCII值 keyCode
ASCII表(了解即可)
keyCode返回键的ASCII值
注意: onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。
<script>
// 键盘事件对象的keyCode属性可以得到按键的ASCII值
// 1.keyup和keydown不区分大小写 a和A得到的都是65
// 2.keypress区分大小写
document.addEventListener('keyup', function (e) {
console.log('up:' + e.keyCode);
var a = e.keyCode
/* if (a === 65) {
console.log('你按下了a键');
} else {
console.log('你没有按下a键');
} */
})
document.addEventListener('keypress', function (e) {
// 可以利用keyCode判断用户按下哪个键
console.log('press:' + e.keyCode);
})
</script>
控制台输出
案例:仿京东快递查询
代码
<style>
* {
margin: 0;
padding: 0;
}
.search {
position: relative;
width: 178px;
margin: 100px;
}
.con {
display: none;
position: absolute;
top: -40px;
width: 171px;
border: 1px solid rgba(0, 0, 0, .2);
box-shadow: 0 2px 4px rgba(0, 0, 0, .2);
padding: 5px 0;
font-size: 18px;
line-height: 20px;
color: #333;
}
.con::before {
content: '';
width: 0;
height: 0;
position: absolute;
top: 28px;
left: 18px;
border: 8px solid #000;
border-style: solid dashed dashed;
border-color: #fff transparent transparent;
}
</style>
<div class="search">
<div class="con">123</div>
<input type="text" placeholder="请输入您的快递单号" class="jd">
</div>
<script>
var con = document.querySelector('.con');
var jd_input = document.querySelector('.jd');
jd_input.addEventListener('keyup', function (e) {
if (this.value == '') {
con.style.display = 'none';
} else {
con.style.display = 'block';
con.innerHTML = this.value;
}
})
// 失去焦点时隐藏盒子
jd_input.addEventListener('blur', function () {
con.style.display = 'none';
});
// 获得焦点且内容不为空时显示盒子
jd_input.addEventListener('focus', function () {
if (this.value != '') {
con.style.display = 'block';
}
});
</script>