JavaScript - WebAPIs篇
在上一篇我们我们已经学习完JavaScript的基础语法,但是仅仅会基础语法对于开发而言还远远不够,我们还需要学习使用JavaScript操作 dom(页面文档对象模型)对象 和 bom(浏览器对象模型)对象 才能完成基本的开发,然而操作 dom 与 bom 也统称为 WebAPIs。以下内容也是学自黑马pink老师。视频学习地址:https://www.bilibili.com/video/BV1Sy4y1C7ha?p=191&spm_id_from=pageDriver
1、API 和 Web API
1.1、API
API(Application Programming Interface):翻译过来就是应用程序编程接口
。是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得已访问一组例程的能力,而又无需访问源码或理解内部工作机制的细节。
简单理解:API是给程序员提供的一种工具,以便能轻松的实现想要完成的功能。
比如手机充电接口:
我们要实现充电这个功能:
- 我们不关心手机内部变压器,内部怎么存储电等
- 我们不关心这个充电线怎么制作的
- 我们只知道,我们拿着充电线插进充电接口就可以充电
- 这个
充电接口就是一个API
1.2、Web API
Web API 是浏览器提供的一套操作浏览器功能
和页面元素
的API
(bom和dom)。
现阶段我们主要针对于浏览器讲解常用的 API,主要针对浏览器做交互效果。比如我们想要浏览器弹出一个警示框,直接使用alert("message");
。
MDN详细API介绍地址:https://developer.mozilla.org/zh-CN/docs/Web/API
因为Web API很多,所以我们将这个阶段称为 Web APIs。
API 与 Web API小结:
- API是为我们开发人员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现
- Web API 主要是针对于浏览器提供的接口,主要对于浏览器做交互效果
- Web API 一般都有输入和输出(函数的传参和返回值),WebAPI很多都是方法
- 学习 Web API可以结合前面学习内置对象方法的思路学习
2、文档对象模型DOM
2.1、初识 DOM
文档对象模型(Document Object Model,简称DOM
),是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口
。
1、DOM树
文档:一个页面就是一个文档,DOM中使用 document
表示
元素:页面中的所有标签都是元素,DOM中使用 element
表示
节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用 node
表示
DOM把以上内容都看作是对象
2.2、获取页面元素
DOM 在我们实际开发中主要用来操作元素。获取页面中的元素可以使用以下几种方式:
- 根据 ID 获取
- 根据标签名获取
- 通过 HTML5 新增的方法获取
- 特殊元素获取
1、根据 ID 获取
使用 getElementById()
方法可以获取带有 ID 的元素对象,getElementById()
是Document的方法,返回一个匹配特定 ID的元素.。由于元素的 ID 在大部分情况下要求是独一无二的,这个方法自然而然地成为了一个高效查找特定元素的方法。
语法:
var element = document.getElementById(id);
参数:
- element是一个 Element 对象。如果当前文档中拥有特定ID的元素不存在则返回null
- id是大小写敏感的字符串,代表了所要查找的元素的唯一ID
返回值:
- 返回一个匹配到 ID 的 DOM Element 对象。若在当前 Document 下没有找到,则返回 null。
案例:
// 因为我们文档页面从上往下加载,所以先得有标签。所以我们script标签写到标签下面
var time = document.getElementById("time"); // 返回元素对象
console.log(time); // <div class="box" id="time">2021/05/20</div>
console.log(typeof time); // object
// 查看对象所有属性与方法
console.dir(time);
console.log(time.textContent); // 2021/05/20
2、根据标签名获取
使用 getElementsByTagName()
方法可以返回带有指定标签名的对象的集合
。返回一个包括所有给定标签名称的元素的HTML集合HTMLCollection。 整个文件结构都会被搜索,包括根节点。返回的 HTML集合是动态的, 意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用 document.getElementsByTagName()
语法:
var elements = document.getElementsByTagName(name);
参数:
- name 是一个代表元素的名称的字符串。特殊字符
"*"
代表了所有元素
案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<ul>
<li>Java</li>
<li>Python</li>
<li>C/C++</li>
<li>PHP</li>
<li>javascript</li>
</ul>
</div>
<script type="text/javascript">
// 返回的是,获取过来元素对象的集合
var collection = document.getElementsByTagName("li");
console.log(collection); // HTMLCollection(5) [li, li, li, li, li]
console.log(typeof collection); // object
console.dir(collection[0].innerText); // Java
// 如果页面中只有一个 li 返回的还是伪数组的形式
// 如果页面中没有指定的元素,返回的是空的伪数组
</script>
</body>
</html>
注意:
- 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历
- 得到元素对象是动态的
还可以获取某个元素(父元素)内部所有指定标签名的子元素。
elemet.getElementsByTagName("标签名");
// 注意父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己
如:我们要获取ol标签
下的子元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<ul>
<li>Java</li>
<li>Python</li>
<li>C/C++</li>
<li>PHP</li>
<li>javascript</li>
</ul>
<ol>
<li>市场部</li>
<li>产品部</li>
<li>运营部</li>
</ol>
</div>
<script type="text/javascript">
var ol = document.getElementsByTagName("ol");
var elements = ol[0].getElementsByTagName("li");
console.log(elements);
</script>
</body>
</html>
3、通过 HTML5 新增的方法获取
1、document.getElementsByClassName()
返回一个包含了所有指定类名的子元素的类数组对象。当在document对象上调用时,会搜索整个DOM文档,包含根节点。你也可以在任意元素上调用 getElementsByClassName()
方法,它将返回的是以当前元素为根节点,所有指定类名的子元素。
语法:
document.getElementsByClassName("names");
参数:
- names 是一个字符串,表示要匹配的类名列表,类名通过空格分隔
- getElementsByClassName 可以在任何元素上调用,不仅仅是 document。 调用这个方法的元素将作为本次查找的根元素
案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<script type="text/javascript">
var boxs = document.getElementsByClassName("box");
console.log(boxs);
</script>
</body>
</html>
2、document.querySelector()
指定选择器或选择器组匹配的第一个 HTMLElement对象
。 如果找不到匹配项,则返回null。
语法:
var element = document.querySelector(selectors);
参数:
selectors
包含一个或多个要匹配的选择器的 DOM字符串DOMString。 该字符串必须是有效的CSS选择器字符串;如果不是,则引发SYNTAX_ERR异常。
返回值:
- 表示文档中与指定的一组CSS选择器匹配的第一个元素,一个 HTMLElement对象。如果没有匹配到,则返回null。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="btn">提交</div>
<script type="text/javascript">
// 注意使用 HTML5 新增的方法 querySelector(选择器) 得到的都是第一个匹配到的元素
var boxElement = document.querySelector(".box");
console.log(boxElement);
var btnElement = document.querySelector("#btn");
console.log(btnElement);
</script>
</body>
</html>
3、document.querySelectorAll()
返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。
语法:
var elementList = parentNode.querySelectorAll(selectors);
参数:
- 一个 DOMString 包含一个或多个匹配的选择器。这个字符串必须是一个合法的
CSS selector
如果不是,会抛出一个 SyntaxError 错误。 - 注意: 如果selectors参数中包含 CSS伪元素,则返回的列表始终为空。
返回值:
- 一个静态 NodeList,包含一个与至少一个指定选择器匹配的元素的Element对象,或者在没有匹配的情况下为空NodeList
案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="btn">提交</div>
<script type="text/javascript">
// 注意使用 HTML5 新增的方法 querySelector(选择器) 得到的都是第一个匹配到的元素
var boxElements = document.querySelectorAll(".box");
console.log(boxElements); // NodeList(2) [div.box, div.box]
</script>
</body>
</html>
4、获取特殊元素(body、html)
1、获取 body 元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="btn">提交</div>
<script type="text/javascript">
// 通过 document 对象的 body 属性获取 body 元素对象
var bodyElement = document.body;
console.log(bodyElement);
console.dir(bodyElement);
</script>
</body>
</html>
2、获取 html 元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="btn">提交</div>
<script type="text/javascript">
// 通过 document对象的 documentElement 属性获取 html 元素对象
var htmlElement = document.documentElement;
console.log(htmlElement);
console.dir(htmlElement);
</script>
</body>
</html>
2.3、事件基础
1、事件概述
JavaScript使我们 有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。简单理解 -> 响应机制
网页中的每个元素都可以产生某些可以触发JavaScript的事件。例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
例如:点击一个按钮,弹出一个对话框。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="btn">点我</button>
<script type="text/javascript">
// 事件是由三部分组成:事件源、事件类型和事件处理。这三部分也称为事件三要素。
// 1、获取事件源
var btn = document.getElementById("btn");
// 2、事件类型 (如何触发、什么事件,比如鼠标点击(onclick)、鼠标经过、键盘按下)
// 3、事件处理程序,通过一个函数赋值的方式完成
btn.onclick = function() {
alert("hello world");
}
</script>
</body>
</html>
2、执行事件的步骤
1、获取事件源
2、注册事件(绑定事件)
3、添加事件处理程序(采取函数赋值形式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<!-- 点击div控制台输出,我被点击了 -->
<div>hello world</div>
<script type="text/javascript">
// 执行事件步骤
// 1、获取事件源
var div = document.querySelector("div");
// 2、绑定事件(注册事件)
div.onclick = function() {
// 3、事件处理程序
console.log("我是div标签,我被点击了");
}
</script>
</body>
</html>
3、常见的鼠标事件
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过时触发 |
nomouseout | 鼠标离开时触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
关于这些鼠标事件,以后我们在开发中遇到了要学会自行学习。这里就不一一介绍了。
2.4、操作元素
JavaScript的DOM操作可以改变页面内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。注意以下都是属性。
1、改变元素内容
// 1、从其实位置到终止位置的内容,但它去除 HTML标签,同时空格和换行也会去掉
element.innerText
// 类似于 v-text 语法
// 2、起始位置到终止位置的全部内容,包括 HTML 标签,同时保留空格和换行
element.innerHTML
// 类似于 v-html 语法
案例:点击显示系统当前时间。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
margin: 100px auto;
}
.box #time{
width: 420px;
height: 24px;
margin-top: 10px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div class="box">
<button id="btn">显示系统时间</button>
<div id="time"></div>
</div>
<script type="text/javascript">
// 分析事件三要素
// 1、获取事件源
var btn = document.getElementById("btn");
var content = document.getElementById("time");
// 2、注册(绑定)事件
btn.onclick = function() {
// 3、事件处理程序
var date = new Date();
var h = date.getHours();
h = h < 10 ? "0" + h : h;
var m = date.getMinutes();
m = m < 10 ? "0" + m : m;
var s = date.getSeconds();
s = s < 10 ? "0" + s : s;
var str = h + ":" + m + ":" + s;
var dayArray = ["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
content.innerText = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + " " + str + " " + dayArray[date.getDay()];
}
</script>
</body>
</html>
innerText
与innerHTML
的区别,简单来说就是:
innerText
不识别HTML标签,还会去除换行和空格innerHTML
识别HTML标签,保留空格和换行- 这个两个属性都可以进行读写
验证如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
<button id="btn_text">使用innerText显示</button>
<button id="btn_html">使用innerHTML显示</button>
<div class="box"></div>
<script type="text/javascript">
var message = "<h1 style='color: red;'>welcome to the world of Java.</h1>";
var box = document.querySelector(".box");
var btn_text = document.getElementById("btn_text");
btn_text.onclick = function() {
box.innerText = message;
}
var btn_html = document.getElementById("btn_html");
btn_html.onclick = function() {
box.innerHTML = message;
}
</script>
</body>
</html>
2、常用元素的属性操作
我们拿到DOM元素对象后,通过DOM元素对象.属性名 = xx
的方式操作元素的属性。例如:我们要改变img
标签的title
属性。我们可以这样写imgElement.title = 'IPhone 12'
。
案例:通过点击实现图片的切换。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
}
.box img {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<button id="btn_jjy">鞠婧祎</button>
<button id="btn_yn">杨幂</button>
<div class="box">
<img src="./image/j.jpg" alt="" title="">
</div>
<script type="text/javascript">
// 修改元素属性
var btnJjy = document.getElementById("btn_jjy");
var btnYn = document.getElementById("btn_yn");
var img = document.querySelector("img");
var imgUrlYn = "http://inews.gtimg.com/newsapp_bt/0/11661142708/1000.jpg";
var imgUrlJyj = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2F781f7529c7b97885289b7a4a853bb2729126a4a6.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1622617397&t=a08a7d05d41600af856f414b4d7a44fa";
img.src = imgUrlJyj;
btnJjy.onclick = function() {
img.src = imgUrlJyj;
img.title = "鞠婧祎";
}
btnYn.onclick = function() {
img.src = imgUrlYn;
img.title = "杨幂";
}
</script>
</body>
</html>
表单元素的属性操作:
利用 DOM 可以操作如下表单元素的属性:
type、value、checked、selected、disabled
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
<button>点我</button>
<input type="text" value="123">
<script type="text/javascript">
var btn = document.querySelector("button");
var input = document.querySelector("input");
btn.onclick = function() {
input.value = "hello world";
// 点击后禁用表单
input.disabled = true;
// this.disabled = true; 注意如果是这样写,那么this指向的是 btn dom元素对象
}
</script>
</body>
</html>
案例:仿京东显示隐藏密码明文案例。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 260px;
height: 50px;
margin: 100px auto;
border: 1px solid #ddd;
text-align: center;
line-height: 50px;
}
.box label {
font-size: 14px;
color: #666;
}
.box input {
width: 130px;
height: 30px;
border: 0 none;
margin-left: 15px;
outline: none;
}
.box img {
position: absolute;
width: 10%;
height: 50%;
top: 13px;
right: 8px;
}
</style>
</head>
<body>
<div class="box">
<label for="pwd">设 置 密 码</label>
<input type="password" id="pwd" name="password">
<img src="./image/close.png" alt="" id="change">
</div>
<script type="text/javascript">
var flag = 0;
// 1、获取事件源
var img = document.getElementById("change");
var input = document.getElementById("pwd");
// 2、注册事件(绑定事件)
img.onclick = function() {
// 3、事件处理程序
if (flag === 0){
img.src = "./image/open.png";
input.type = "text";
flag = 1;
}else {
img.src = "./image/close.png";
input.type = "password";
flag = 0;
}
}
</script>
</body>
</html>
3、样式属性操作
我们可以通过JavaScript修改元素的大小、颜色、位置等样式。
element.style // 行内样式操作
element.className // 类名样式操作
① 行内样式(element.style)操作:
如:点击改变背景颜色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: gold;
margin: 100px auto;
}
</style>
</head>
<body>
<div class="box"></div>
<script type="text/javascript">
// 1、获取事件源
var box = document.querySelector(".box");
// 2、注册事件(绑定事件)
box.onclick = function() {
// 3、事件程序处理
// box.style = "background-color: red;"
box.style.backgroundColor = "red";
// 注意:css属性名在这里都采用驼峰命名法书写
}
</script>
</body>
</html>
注意:
- JavaScript里面的样式采取驼峰命名法,比如:
fontSize、backgroundColor
- JavaScript修改 style 样式操作,产生的是行内样式,css权重比较高
案例1:淘宝点击关闭二维码。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 100px;
height: 100px;
margin: 100px auto;
}
.box img {
width: 100%;
height: 100%;
}
.box i {
position: absolute;
width: 12px;
height: 12px;
border: 1px solid #ddd;
text-align: center;
color: #ddd;
line-height: 12px;
font-style: normal;
cursor: pointer;
}
</style>
</head>
<body>
<div class="box">
<!--二维码图片地址,淘宝网里获取-->
<img src="" alt="">
<i id="hid">x</i>
</div>
<script>
var btn = document.getElementById("hid");
var box = document.querySelector(".box");
btn.onclick = function() {
box.style.display = "none"; // 隐藏元素
}
</script>
</body>
</html>
案例2:显示隐藏输入框的内容。
当鼠标点击文本框时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
input {
outline-style: none;
color: #999;
}
</style>
</head>
<body>
<input type="text" value="手机">
<script>
var input = document.querySelector("input");
// 获得鼠标焦点
input.onfocus = function() {
input.style.color = "#333";
if( input.value === "手机") {
input.value = "";
}
}
// 失去鼠标焦点
input.onblur = function() {
this.style.color = "#999";
if(this.value === ""){
this.value = "手机";
}
}
</script>
</body>
</html>
② 类名样式(element.className)操作:
经过前面我们学习我们已知道,我们可以通过element.style.属性名
的方式改变一些css样式!触发事件后我们想改变多个css属性,我们可能会这样写:
var test = document.querySelector("div");
test.onclick = function() {
this.style.backgroundColor = "pink";
this.style.color = "#666";
this.style.fontSize = "20px";
this.style.marginTop = "10px";
}
// 注意:行内样式写法适合样式比较少或者功能简单的情况
我们发现这样书写有很多重复的代码(this.style
),并且比较繁琐,那么有没有一种更简便的方法可以改变多个属性呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.change{
background-color: gold;
color: #666;
font-size: 20px;
margin-top: 20px;
}
</style>
</head>
<body>
<div>hello</div>
<script>
var test = document.querySelector("div");
test.onclick = function() {
/*this.style.backgroundColor = "pink";
this.style.color = "#666";
this.style.fontSize = "20px";
this.style.marginTop = "10px";*/
this.className = "change"; // 把当前元素的类名改为了 change
// 注意:我们可以通过修改元素的className更改元素的样式,适合于样式较多或功能复杂的情况
}
</script>
</body>
</html>
注意:
- 如果样式修改较多,可以采取操作类名方式更改元素样式。
- class因为是个保留子,因此使用
className
来操作元素类名属性 className
会直接更改元素的类名,会覆盖原来的类名。如<div class="a b c"></div>
当使用element.className = "test";
修改后,会覆盖原来的,变为<div class="test"></div>
。
因此想要保留原来的类名,我们可以这样做:
element.className = "a b c test"; // 多类名选择器
案例1:密码框验证。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 500px;
height: 50px;
margin: 100px auto;
}
.box i {
float: left;
font-style: normal; /* 取消倾斜 */
font-size: 20px;
color: red;
margin-top: 10px;
}
.box label {
margin-left: 6px;
color: #666;
font-size: 14px;
}
.box input {
outline-style: none;
}
.box img {
position: absolute;
width: 20px;
height: 20px;
top: 10px;
}
.box p {
display: inline-block;
font-size: 12px;
color: #666;
margin-left: 25px;
}
</style>
</head>
<body>
<div class="box">
<i>*</i>
<label for="pwd">设 置 密 码</label>
<input type="password" id="pwd">
<img src="./image/remin.png" alt="">
<p>请输入6~16位密码</p>
</div>
<script>
var pwd = document.getElementById("pwd");
var p = document.querySelector(".box p");
var img = document.querySelector(".box img");
// 失去焦点
pwd.onblur = function() {
var pwdLength = pwd.value.length;
if(pwdLength < 6 || pwdLength > 16){
p.style.color = "red";
img.src = "./image/error.png";
p.innerText = "密码长度不对,注意是6~16位!";
}else {
p.style.color = "#1afa29";
img.src = "./image/success.png";
p.innerText = "密码设置成功!";
}
}
</script>
</body>
</html>
案例2:批量操作元素 + 排他思想
排他思想:如果有同一组元素,我们想要某一个元素实现某种样式,需要用到循环的排他思想算法:
- 所有元素全部清除样式(干掉其他人)
- 给当前元素设置样式(留下我自己)
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
</div>
<script>
// 获取所有按钮元素
// 1、获取事件源 (注意是批量获取)
var buttons = document.getElementsByTagName("button");
// console.dir(buttons);
// 2、绑定事件 (注册事件)
for (var i = 0; i < buttons.length; i++) {
// 3、事件处理程序
buttons[i].onclick = function() {
// console.log("123");
// 清除其他元素的样式
for (var n = 0; n < buttons.length; n++) {
buttons[n].style.backgroundColor = "";
}
// 改变当前点击元素的样式
this.style.backgroundColor = "red";
}
}
</script>
</body>
</html>
案例3:百度首页更换皮肤效果
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
background: url('https://dss1.bdstatic.com/kvoZeXSm1A5BphGlnYG/skin_zoom/27.jpg?2') no-repeat center top;
}
.box {
width: 300px;
height: 100px;
margin: 50px auto;
}
.box ul li {
float: left;
width: 60px;
height: 40px;
list-style: none;
margin-left: 5px;
}
.box ul li img {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="box">
<ul>
<li>
<img src="https://dss1.bdstatic.com/kvoZeXSm1A5BphGlnYG/skin_zoom/27.jpg?2" alt="">
</li>
<li>
<img src="https://dss1.bdstatic.com/kvoZeXSm1A5BphGlnYG/skin_zoom/1005.jpg?2" alt="">
</li>
<li>
<img src="https://dss0.bdstatic.com/k4oZeXSm1A5BphGlnYG/skin/831.jpg?2" alt="">
</li>
<li>
<img src="https://dss2.bdstatic.com/lfoZeXSm1A5BphGlnYG/skin/813.jpg?2" alt="">
</li>
</ul>
</div>
<script>
// 1、获取事件源
var lis = document.querySelectorAll(".box ul li img");
var body = document.body;
// console.log(lis);
// 2、绑定事件 (注册事件)
for (var i = 0; i < lis.length; i++) {
// 3、事件处理程序
lis[i].onclick = function() {
var imgUrl = this.src;
// console.log(imgUrl);
body.style.backgroundImage = `url(${imgUrl})`;
}
}
</script>
</body>
</html>
案例4:表单全选与取消全选
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.wrap {
width: 400px;
height: 400px;
margin: 100px auto;
}
.wrap table {
border-collapse: collapse;
border-top: 1px solid rgb(230, 189, 189);
border-bottom: 1px solid rgb(230, 189, 189);
}
.wrap table tr {
border-top: 1px solid rgb(230, 189, 189);
}
.wrap table tr th,.wrap table tr td {
padding: 5px 10px;
font-size: 12px;
font-family: Verdana;
color: rgb(177, 106, 104);
}
</style>
</head>
<body>
<div class="wrap">
<table>
<tr>
<th>
<input type="checkbox" name="" id="all">
</th>
<th>商品</th>
<th>价格</th>
</tr>
<tr>
<td>
<input type="checkbox" name="" id="">
</td>
<td>Iphone 12</td>
<td>6000</td>
</tr>
<tr>
<td>
<input type="checkbox" name="" id="">
</td>
<td>Iphone 12</td>
<td>6000</td>
</tr>
<tr>
<td>
<input type="checkbox" name="" id="">
</td>
<td>Iphone 12</td>
<td>6000</td>
</tr>
</table>
</div>
<script>
var all = document.getElementById("all");
var checkboxs = document.querySelectorAll(".wrap table tr td input");
// console.log(checkboxs);
all.onclick = function() {
console.log(this.checked);
for(var i = 0; i< checkboxs.length; i++) {
checkboxs[i].checked = this.checked;
}
/*for(var i = 0;i < checkboxs.length; i++) {
/*console.log(checkboxs[i].checked);
console.log(typeof checkboxs[i].checked);
if(checkboxs[i].checked) {
checkboxs[i].checked = "";
}else{
checkboxs[i].checked = "checked";
}
}*/
}
for (var i = 0; i < checkboxs.length; i++) {
checkboxs[i].onclick = function() {
var flag = true;
for(var n = 0; n < checkboxs.length; n++) {
if(!checkboxs[n].checked){
flag = false;
}
}
all.checked = flag;
}
}
</script>
</body>
</html>
2.5、操作自定义属性
1、getAttribute()
与setAttribute()
在前面我们已经知道,我们可以采用element.属性名
或element.getAttribute("属性名")、element.setAttribute("属性名","属性值")
的方式,对标签的属性进行读写操作:
var div = document.getElementById("demo");
// 获取属性值
// 1.element.属性名
console.log(div.id); // demo
// 2.element.getAttribute("属性名");
var divId = div.getAttribute("id"); // demo
console.log(divId);
// 3、通过element.setAttribute("属性名","属性值"); 设置属性值
div.setAttribute("id","alex");
console.log(div.id); // alex
element.属性名
和element.getAttribute("属性名")
的区别:
element.属性名
获取内置属性值(元素本身自带的属性)element.getAttribute("属性名");
主要获得自定义的属性(标准)我们开发人员自定义的属性
另外我们还可以通过element.removeAttribute("属性名");
移除指定的属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box" id="demo"></div>
<script type="text/javascript">
var div = document.getElementById("demo");
// 移除属性
div.removeAttribute("class");
</script>
</body>
</html>
案例:点击不同 tab ,显示对应的内容。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 600px;
height: 400px;
margin: 100px auto;
/* background-color: gold; */
}
.box .tab_list {
width: 600px;
height: 30px;
line-height: 30px;
background-color: rgb(241, 243, 244);
}
.box .tab_list ul {
margin-left: -40px;
}
.box .tab_list ul li {
float: left;
list-style: none;
padding: 0 10px;
cursor: pointer;
}
.box .tab_list ul .active {
background-color: rgb(253, 38, 74);
color: #ffff;
}
.box .tab_con .item {
display: none; /* 隐藏元素 */
}
</style>
</head>
<body>
<div class="box">
<div class="tab_list">
<ul>
<li class="active">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评价(1000+)</li>
<li>手机社区</li>
</ul>
</div>
<div class="tab_con">
<div class="item" style="display: block;">
商品介绍内容
</div>
<div class="item">
规格与包装内容
</div>
<div class="item">
售后保障内容
</div>
<div class="item">
商品评价内容
</div>
<div class="item">
手机社区内容
</div>
</div>
</div>
<script>
var tabs = document.querySelectorAll(".box .tab_list ul li");
var items = document.querySelectorAll(".box .tab_con .item");
// console.log(tabs);
for (var i = 0; i < tabs.length; i++) {
tabs[i].setAttribute("index",i); // 设置自定义属性,用于标记当前tab
tabs[i].onclick = function() {
for(var n = 0; n < tabs.length; n++) {
// console.log(tabs[n].getAttribute("class"));
if(tabs[n].getAttribute("class") != null) {
tabs[n].setAttribute("class","");
}
this.setAttribute("class","active");
var index = this.getAttribute("index");
items[n].style.display = "";
items[parseInt(index)].style.display = "block";
}
}
}
</script>
</body>
</html>
2、H5自定义属性
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获取是通过getAttribute("属性名");
获取。但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div getIndex="21"></div>
<script>
var div = document.querySelector("div");
console.log(div.getIndex); // undefined
// 自定义属性需要使用 getAttribute() 方法获取
var index = div.getAttribute("getIndex");
console.log(index); // 21
</script>
</body>
</html>
H5给我们新增了自定义属性,H5规定自定义属性data-开头
作为属性名并且赋值。比如<div data-index="21"></div>
,或者是=使用js
设置element.setAttrbute("data-index",2);
3、获取H5自定义的属性
- 兼容性获取
element.getAttribute("data-index");
- H5新增
element.dataset.index
或者element.dataset["index"]
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div data-index="21" data-list-name="alex"></div>
<script>
// h5新增的获取自定义属性的方法
var div = document.querySelector("div");
// 1.dataset.index
// dataset是一个集合,存放了所有以data开头的自定义属性
console.log(div.dataset.index); // 21
console.log(div.dataset.listName); // alex
// 2.dataset.["index"]
console.log(div.dataset["index"]); // 21
console.log(div.dataset["listName"]); // alex
console.log(div.dataset); // DOMStringMap {index: "21", listName: "alex"}
</script>
</body>
</html>
2.6、节点操作
1、为什么学节点操作?
获取元素通常使用两种方式:
- 利用DOM提供的方法获取元素,前面我们已经学习过。例如
element.getElementById()
。但是这些方法逻辑性不强,比较繁琐。特殊情况无法做到简处理。 - 利用节点层级关系获取元素。利用父子节点关系获取元素,这种方式逻辑性强,但是兼容性稍差。
这两种方式都是可以获取元素节点,我们后面都会使用,但是节点操作更简单。
2、节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。
HTML DOM树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。
一般来说,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
注意:
- 元素节点 nodeType 为1(开发中最为常用)
- 属性节点 nodeType 为2
- 文本节点 nodeType 为3(文本节点包含文字、空格、换行等)
3、节点层级
利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系
。
1、父级节点
node.parentNode
-
parentNode
属性可返回某节点的父节点,注意是最近的一个父节点 -
如果指定的节点没有父节点则返回 null
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="box">
<span class="test">x</span>
</div>
<script>
// 父节点 parentNode
var test = document.getElementsByClassName("test");
// console.log(test[0].innerText);
// 获取父级元素
console.log(test[0].parentNode); // 得到的是离元素最近的父级节点。如果获取不到就返回 null
</script>
</body>
</html>
2、子节点
1. node.childNodes // 得到所有节点,使用元素节点需要进行筛选
node.childNodes
返回包含指定节点的子节点的集合,该集合为即时更新的集合。
注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用childNode
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ol>
<li>Java</li>
<li>Python</li>
</ol>
<div class="box">
<span class="test">x</span>
</div>
<script>
// 我们想获取 ul 下的子标签 li:
// 1.DOM 提供的方法(API)获取
var ul = document.querySelector("ul");
var lis = ul.querySelectorAll("li");
console.log(lis); // NodeList(4) [li, li, li, li]
// 2.子节点 node.childNodes 所有的子节点包含元素节点、文本节点等等
console.log(ul.childNodes); // NodeList(9) [text, li, text, li, text, li, text, li, text]
console.log(ul.childNodes[0].nodeType); // 3
console.log(ul.childNodes[1].nodeType); // 1
// 筛选元素节点
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType === 1) {
console.log(ul.childNodes[i]);
}
}
</script>
</body>
</html>
为了方便获得子元素节点,我们有了更好的获得方式:
2. node.children // 获取子元素节点
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// children 获取所有的子元素节点,也是我们实际开发常用的
var ul = document.querySelector("ul");
console.log(ul.children); // HTMLCollection(4) [li, li, li, li]
</script>
</body>
</html>
获取第一个子节点:
3. node.firstChild
fistChild
返回第一个子节点,找不到则返回 null。同样,也是包含所有的节点。
获取最后一个子节点:
4. node.lastChild // 所有节点的最后一个节点
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ol>
<li>Java</li>
<li>Python</li>
<li>JavaScript</li>
</ol>
<script>
var ol = document.querySelector("ol");
// 可自行查看输出结果
console.log(ol.firstChild);
console.log(ol.lastChild);
</script>
</body>
</html>
获取第一个子元素节点:
5. node.firstElementChild // 返回第一个子元素节点,找不到则返回null
获取最后一个子元素节点:
6. node.lastElementChild // 返回最后一个子元素节点,找不到则返回null
注意:这两个方法有兼容性问题,IE9以上才支持。
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ol>
<li>Java</li>
<li>Python</li>
<li>JavaScript</li>
</ol>
<script>
var ol = document.querySelector("ol");
console.log(ol.firstElementChild); // <li>Java</li>
console.log(ol.lastElementChild); // <li>JavaScript</li>
// 实际开发中写法(不存在兼容性问题)
console.log(ol.children[0]);
console.log(ol.children[1]);
console.log(ol.children[2]);
</script>
</body>
</html>
案例:新浪网下拉菜单。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 600px;
height: 100px;
margin: 100px auto;
}
.box .nav {
width: 100%;
height: 30px;
background-color: #fcfcfc;
line-height: 30px;
}
.box .nav .tab {
position: relative;
width: 50px;
height: 30px;
float: left;
text-align: center;
line-height: 30px;
padding: 0 10px;
list-style: none;
}
.box .nav .tab:hover {
background-color: rgb(237, 238, 240);
color: #FF8400;
}
.box .nav .tab a {
text-decoration: none;
color: #4C4C4C;
font-style: normal;
font-size: 12px;
}
.box .nav .tab .sub_nav li {
width: 68px;
height: 30px;
list-style: none;
text-align: center;
line-height: 30px;
font-size: 12px;
color: #4C4C4C;
border: 1px solid #ffc525;
border-top: none;
}
.box .nav .tab .sub_nav li:hover {
color: #ffc525;
background-color: #f7e7c7;
}
.box .nav .tab .sub_nav {
display: none;
position: absolute;
left: -40px;
height: 30px;
}
</style>
</head>
<body>
<div class="box">
<ul class="nav">
<li class="tab">
<a href="#">登录</a>
</li>
<li class="tab">
<a href="#">微博</a>
<ul class="sub_nav">
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li class="tab">
<a href="#">博客</a>
<ul class="sub_nav">
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li class="tab">
<a href="#">邮箱</a>
<ul class="sub_nav">
<li>私信</li>
<li>评论</li>
<li>@我</li>
</ul>
</li>
<li class=tab>
<a href="#">网站导航</a>
</li>
</ul>
</div>
<script>
// 1、获取事件源
var tabs = document.querySelectorAll(".box .nav .tab");
// console.log(tabs);
// 2、绑定事件(注册事件)
for (var i = 0; i < tabs.length; i++) {
// 3、事件处理函数
tabs[i].onmouseover = function() {
this.children[0].style.color = "#FF8400";
// this.children[1].style.display = "";
if(this.children[1] != null) {
this.children[1].style.display = "block";
}
}
tabs[i].onmouseout = function() {
this.children[0].style.color = "#4C4C4C";
if(this.children[1] != null) {
this.children[1].style.display = "none";
}
}
}
</script>
</body>
</html>
3、兄弟节点
1. node.nextSibling // 下一个兄弟节点,包含元素节点或者文本节点等等,如果找不到则返回null。
2. node.previousSibling // 返回当前元素上一个兄弟节点,找不到则返回null,同样也是包含所有的节点。
3. node.nestElementSibling // 返回下一个兄弟元素节点,如果找不到则返回null
4. node.previousElementSibling // 返回上一个兄弟元素节点,如果找不到则返回null
注意:3、4有兼容性问题,IE9以上才支持。
我们既想获取兄弟元素节点,又解决兼容性问题,只能自己封装一个函数:
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if(el.nodeType === 1) {
return el;
}
}
return null;
}
代码验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">
<div>123</div>
<div class="nav">hello</div>
<div>world</div>
</div>
<script>
var nav = document.querySelector(".box .nav");
console.log(nav.nextSibling);
console.log(nav.previousSibling);
console.log(nav.nextElementSibling); // <div>world</div>
console.log(nav.previousElementSibling); // <div>123</div>
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if(el.nodeType === 1) {
return el;
}
}
return null;
}
console.log(getNextElementSibling(nav)); // <div>world</div>
</script>
</body>
</html>
4、创建节点
1. document.createElement("tagName"); // 创建一个元素节点,用于添加添加节点,与 appendChild() 搭配使用
document.createElement()
方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存咋,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点
。
添加节点:
2. node.appendChild(child); // 将一个节点添加到指定父节点的子节点列表末尾。类似于css里面的 after 伪元素。
3. node.insertBefore(child,指定元素);
node.insertBefore()
方法将一个节点添加到父节点的指定子节点前面
。类似于 css 里面的before伪元素。
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">
<div>123</div>
</div>
<script>
// 1、创建元素节点
var div = document.createElement("div");
// console.log(div); // <div></div>
var box = document.querySelector(".box");
// 2、添加节点 node.appendChid(child) node 父级 child 是子集。后面追加元素、类似以数组的 push() 方法
box.appendChild(div);
// 3、node.insertChild 在指定元素前添加节点
var span = document.createElement("span");
box.insertBefore(span,box.children[0]);
// 小结:我们想要页面添加一个新的元素:1.创建元素 2.添加元素
</script>
</body>
</html>
案例:简单版发布留言案例。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 206px;
height: 170px;
margin: 100px auto 0 auto;
}
.box .cont {
width: 200px;
height: 100px;
outline: none;
border: 1px solid #b4d7f8;
}
.box .btn {
display: block;
border: 1px solid #b4d7f8;
background-color: rgb(220, 237, 249);
color: #0070a3;
cursor: pointer;
}
.box .btn:hover {
background-color: #b4d7f8;
}
.r {
float: right;
}
.l {
float: left;
}
.items {
margin-left: 658px;
}
.items table {
border-collapse: collapse;
border-top: 1px solid #b4d7f8;
border-bottom: 1px solid #b4d7f8;
width: 206px;
}
.items table tr {
border-top: 1px solid #b4d7f8;
}
.items table tr th,.items table tr td {
padding: 5px 10px;
font-size: 12px;
font-family: Verdana;
color: #b4d7f8;
}
</style>
</head>
<body>
<div class="box">
<textarea class="cont" name="content" id="comment"></textarea>
<button class="btn l">发布</button>
<button class="btn r">重置</button>
</div>
<div class="items">
<table>
<tr>
<th>评论</th>
</tr>
</table>
</div>
<script>
// 1、获取元素
var textarea = document.getElementById("comment");
var btns = document.querySelectorAll(".box .btn");
var table = document.querySelector(".items table");
// 2、注册事件(绑定事件)
btns[0].onclick = function() {
// 3、事件处理程序
var text = textarea.value;
if (text == "") {
alert("您没有输入内容哦");
return false;
}
// (1)创建节点元素
var tr = document.createElement("tr");
tr.innerHTML = "<td>" + text + "</td>";
// (2)添加节点元素
table.appendChild(tr);
}
btns[1].onclick = function() {
textarea.value = "";
}
</script>
</body>
</html>
5、删除节点
node.removeChild(child);
node.removeChild()
方法从 DOM 中删除一个子节点,返回删除的节点。
案例:逐个删除元素
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ol>
<li>Java</li>
<li>Python</li>
<li>JavaScript</li>
<li>PHP</li>
</ol>
<button class="btn">删除</button>
<script>
var ol = document.querySelector("ol");
var btn = document.querySelector(".btn");
console.log(ol.children);
// 删除元素节点
btn.onclick = function() {
// 点击删除按钮依次删除元素
if (ol.children.length === 0) {
this.disabled = true;
} else {
var reElement = ol.removeChild(ol.children[ol.children.length - 1]);
console.log(reElement);
}
}
</script>
</body>
</html>
6、复制节点(克隆节点)
node.cloneNode();
node.cloneNode()
方法返回调用该方法的节点的一个副本。也称为克隆节点 / 拷贝节点。
注意:
- 如果括号
参数为空或者为false
,则是浅拷贝即之克隆复制节点本身,不克隆里面的子节点。 - 如果括号参数为
true
,则是深度拷贝,会复制节点本身以及里面所有的子节点。
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box">123</div>
<script>
var box = document.querySelector(".box");
// 1、浅拷贝,不拷贝标签内容
var cloneBox1 = box.cloneNode();
console.log(cloneBox1); // <div class="box"></div>
// 2、深拷贝,标签内容一起拷贝
var cloneBox2 = box.cloneNode(true);
console.log(cloneBox2); // <div class="box">123</div>
</script>
</body>
</html>
案例:动态生成表格。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 400px;
margin: 100px auto;
}
.box table {
border-collapse: collapse;
border-top: 1px solid rgb(93, 172, 236);
border-bottom: 1px solid rgb(93, 172, 236);
}
.box table tr {
border-top: 1px solid rgb(93, 172, 236);
}
.box table tr th,.box table tr td {
padding: 5px 10px;
font-size: 12px;
font-family: Verdana;
color: rgb(93, 172, 236);
}
.box table tr td a {
display: inline-block;
width: 30px;
height: 16px;
text-align: center;
line-height: 16px;
text-decoration: none;
color: rgb(234, 240, 243);
background-color: rgb(90, 201, 165);
border-radius: 15%;
}
</style>
</head>
<body>
<div class="box">
<table>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
<!-- <tr>
<td>alex</td>
<td>Java</td>
<td>100</td>
<td>
<a href="javascript:void(0);">删除</a>
</td>
</tr> -->
</table>
</div>
<script>
// 1、准备学生数据
function Student(name,subject,score) {
this.name = name;
this.subject = subject;
this.score = score;
}
var studentDatas = [
new Student("alex","Java","100"),
new Student("howie","Python","100"),
new Student("李白","Linux","100"),
new Student("马超","Java","90"),
new Student("韩信","PHP","70")
]
// console.log(studentDatas);
// 2、获取元素
var tbody = document.querySelector(".box table tbody");
studentDatas.forEach((student,index) => {
// console.log(student);
var tr = document.createElement("tr");
// 行里面创建单元格 td 单元格的数量取决于每个对象里面的属性个数
for (var key in student) {
// console.log(key);
var td = document.createElement("td");
td.innerHTML = student[key];
tr.appendChild(td);
}
// tr.appendChild(tbody.children[1].children[3].cloneNode(true));
var td = document.createElement("td");
var a = document.createElement("a");
a.href = "javascript: void(0);";
a.innerText = "删除";
td.appendChild(a);
tr.appendChild(td);
tbody.appendChild(tr);
});
// 删除操作
var aList = document.querySelectorAll(".box table tbody tr td a");
// var trList = document.querySelectorAll(".box table tbody tr");
for (var i = 0; i < aList.length; i++) {
aList[i].onclick = function() {
tbody.removeChild(this.parentNode.parentNode);
}
}
</script>
</body>
</html>
3、事件高级
3.1、注册事件(绑定事件)
1、注册事件概述
给元素添加事件,称为注册事件
或者绑定事件
。注册事件有两种方式:
传统方式
方法监听注册方式
传统注册方式:
- 利用
on
开头的事件 如onclick:<button onclick="alert('hello world');">点我</button>
或btn.onclick = function() {}
。 - 特点:注册事件的
唯一性
。同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数。
方法监听注册事件:
- W3C标准推荐方式
addEventListener()
它是一个方法- IE9之前的 IE 不支持此方法,可使用
attachEvent()
代替 - 特点:同一个元素同一个事件可以注册多个监听器
- 按注册顺序依次执行
2、addEventListener()事件监听方式
语法:
eventTarget.addEventListener(type, listener[, useCapture]);
/*
参数:
1.type:事件类型字符串,比如click、mouseover,注意这里不要带 on。与传统方式区别开来。
2.listener:事件处理函数,事件发生时,会调用该监听函数
3.useCapture:可选参数,是一个布尔值,默认是false。学完DOM事件流后,在详细介绍。
*/
eventTarget.addEventListener()
方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,就会执行事件处理函数。
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">点我</button>
<script>
// 利用事件侦听注册事件 addEventListener() 里面的事件类型是字符串,必定加引号而且不带 on
var btn = document.querySelector(".btn");
btn.addEventListener("click", function() {
alert("hello world");
});
// 可以注册多个事件
btn.addEventListener("click", function() {
alert("第二个事件,已触发");
});
</script>
</body>
</html>
3、attachEvent 事件监听方式(了解即可)
语法:
eventTarget.attachEvent(eventNameWithOn, callback); // 注意:此方法是非标准的。在生产环境中切记使用。
/*
参数:
1.eventNameWithOn:事件类型字符串,比如 onclick、onmouseover,这里需要带 on
2.callback:事件处理函数,当目标触发事件时回调函数被调用
*/
eventTarget.attachEvent()
方法将指定的监听器注册到,eventTarget(目标对象)上,当该对象触发指定的事件是,指定的回调函数就会被执行。
如:垃圾方法,测试的浏览器都不支持此方法!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">点我</button>
<script>
// IE独有,在 IE浏览器才能测试成功
var btn = document.querySelector(".btn");
btn.attachEvent("onclick", function() {
alert("123");
})
</script>
</body>
</html>
4、注册事件兼容性解决方案
function addEventListener(element, eventName, callback) {
// 判断当前浏览器是否支持 addEventListener() 方法
if (element.addEventListener) {
element.addEventListener(eventName,callback); // 第三个参数默认是 false
}else if (element.attachEvent) {
element.attachEvent("on" + eventName, callback);
}else {
// 相当于 element.onclick = callback
element["on" + eventName] = callback;
}
}
代码演示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">点我</button>
<script>
var btn = document.querySelector(".btn");
function addEventListener(element, eventName, callback) {
// 判断当前浏览器是否支持 addEventListener() 方法
if (element.addEventListener) {
element.addEventListener(eventName,callback); // 第三个参数默认是 false
}else if (element.attachEvent) {
element.attachEvent("on" + eventName, callback);
}else {
// 相当于 element.onclick = callback
element["on" + eventName] = callback;
}
}
addEventListener(btn, "click", function() {
alert("debug -> 123");
});
</script>
</body>
</html>
兼容性处理原则:首先照顾大多数浏览器,在处理特殊浏览器。
3.2、删除事件(解绑事件)
删除事件的方式:
1、对于传统方式注册的事件,我们可以使用eventTarget.onclick = null;
的方式进行事件的删除。
2、方法监听注册方式:eventTarget.removeEventListener(type, listener[, useCapture]);
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divList = document.querySelectorAll("div");
// 1、传统注册事件方式
divList[0].onclick = function() {
alert(this.innerText);
// 事件只执行一次
this.onclick = null;
}
// 2、事件侦听注册事件
divList[1].addEventListener("click", myAlert); // 注意这里调用函数不用加括号
function myAlert() {
alert(this.innerText);
this.removeEventListener("click", myAlert); // 删除事件
}
</script>
</body>
</html>
3.3、DOM事件流
事件流
描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
。
比如我们给一个div注册了点击事件:
- 捕获阶段
- 当前目标阶段
- 冒泡阶段
- 事件冒泡:IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程。
- 事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程。
注意:
- JavaScript代码只能执行捕获或者冒泡其中的一个阶段。
- onclick 和 attrachEvent 只能得到冒泡阶段。
addEventListener(type, listener[, useCapture]);
第三个参数如果是true
,表示在事件捕获阶段调用事件处理程序,如果是false
(不写默认就是false),表示在事件冒泡阶段调用事件处理程序。
代码验证:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
position: relative;
width: 200px;
height: 200px;
background-color: hotpink;
margin: 100px auto;
}
.father .son {
position: absolute;
width: 100px;
height: 100px;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
background-color: lawngreen;
}
</style>
</head>
<body>
<div class="father">
父盒子
<div class="son">子盒子</div>
</div>
<script>
// dom 事件流 三个阶段
/*
1.js代码中只能执行捕获或者冒泡其中的一个阶段
2.onclick 和 attachEvent(id) 只能得到冒泡阶段
*/
/*var son = document.querySelector(".son");
son.addEventListener("click", function() {
// 第三个参数是 true 表明是在捕获阶段执行处理事件程序
// document -> html -> body -> father -> son
alert("son");
}, true);
var father = document.querySelector(".father");
father.addEventListener("click", function() {
alert("father");
}, true);*/
// 3.冒泡阶段:如果addEventListener 第三个参数是 false 或者省略那么则处于冒泡阶段
// son -> father -> body -> html -> document
var son = document.querySelector(".son");
son.addEventListener("click", function() {
// 第三个参数是 true 表明是在捕获阶段执行处理事件程序
// document -> html -> body -> father -> son
alert("son");
}, false);
var father = document.querySelector(".father");
father.addEventListener("click", function() {
alert("father");
}, false);
</script>
</body>
</html>
小结:
- 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
- 有些事件是没有冒泡的,比如
onblur、onfocus、onmouseenter、onmouseleave
- 事件冒泡有时候会带来麻烦,有时候又会帮助我们完成很巧妙的某些事件,具体场景后面再介绍。
3.4、事件对象
1、事件对象概述
官方解释:event对象
代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。
简单理解:事件发生后,跟事件相关的一系列信息数据的集合
都放到这个对象里面,这个对象就是事件对象event
,它有很多属性与方法。
比如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=s, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: greenyellow;
}
</style>
</head>
<body>
<div class="box">
123
</div>
<script>
var box = document.querySelector(".box");
box.onclick = function(event) {
console.log(event); // MouseEvent {isTrusted: true, screenX: 83, screenY: 147, clientX: 83, clientY: 44, …}
}
/*
1.event 就是一个事件对象,写到我们监听函数的小括号里面,当形参来看
2.事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
3.事件对象是我们事件的一系列相关数据的集合,跟事件相关的,比如鼠标点击里面就包含鼠标的相关信息:如鼠标坐标。
如果是键盘事件里面就包含键盘的事件信息。比如判断用户按下那个键。
4.这个事件对象我们可以自己命名,比如event、evt、e等
5.事件对象也有兼容性问题,ie(6/7/8)是通过 window.event 获取。兼容性的写法 event = event || window.event
*/
box.addEventListener("click", function(event) {
event = event || window.event;
console.log(event);
});
</script>
</body>
</html>
2、事件对象常用的属性与方法
事件对象属性方法 | 说明 |
---|---|
event.target | 返回触发事件的对象(标准) |
event.srcElement | 返回触发事件的对象(非标准 ie6-8使用) |
event.type | 返回事件的类型,比如click mouseover等,不带on |
event.cancelBubble | 该属性阻止冒泡(非标准ie6-8使用) |
event.returnValue | 该属性阻止默认事件(默认行为)(非标准ie6-8使用)比如不让链接跳转 |
event.preventDefault() | 该方法阻止默认事件(默认行为)(标准)比如不让链接跳转 |
evnet.stopPropagation() | 阻止冒泡(标准) |
获取事件触发对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 50px;
height: 50px;
background-color: greenyellow
}
ol {
width: 50px;
height: 100px;
background-color: hotpink;
}
</style>
</head>
<body>
<div class="box">123</div>
<ol>
<li>Java</li>
<li>Python</li>
<li>PHP</li>
</ol>
<script>
var box = document.querySelector(".box");
box.addEventListener("click", function(event) {
// 1.event.target 返回的是触发事件的对象(元素)
console.log(event.target); // <div class="box">123</div>
// 注意:this 返回的是绑定事件的对象(元素)
console.log(this); // <div class="box">123</div>
});
var ol = document.querySelector("ol");
ol.addEventListener("click", function(event) {
// 我们给 ul 绑定了事件,那么 this 就指向 ul
console.log(this);
// event.target 指向我们点击的那个对象,谁触发了这个事件,我们点击的是li event.target 指向的就是li
console.log(event.target);
/*
兼容性处理:
event = event || window.event;
var target = event.target || window.srcElement;
console.log(target);
*/
});
</script>
</body>
</html>
阻止默认行为(事件):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<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、返回事件类型 event.type
var div = document.querySelector("div");
div.addEventListener("click", function(event) {
console.log(event.type); // click
});
div.addEventListener("mouseover", myEvent);
div.addEventListener("mouseout", myEvent);
function myEvent(event) {
console.log(event.type);
}
// 2、阻止默认行为(事件)
var a = document.addEventListener("click", function (event){
event.preventDefault(); // DOM标准写法
});
/*
a.onclick = function(event) {
// 1.普通浏览器 event.preventDefault();
event.prventDefault();
// 2.低版本浏览器 ie(6-8) returnValue 属性
event.returnValue;
// 3.我们可以利用 return false 也能阻止默认行为。没有兼容性问题
return false;
}
*/
</script>
</body>
</html>
3.5、阻止事件冒泡
1、阻止事件冒泡的两种方式
事件冒泡:开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点。
事件冒泡本身的特性,会带来好处也会带来坏处,我们需要根据需求灵活掌握。
阻止事件冒泡的两种方式:
1、标准写法:利用事件对象里面的stopPropagation()
方法。例如:event.stopPropagation();
2、非标准写法:IE(6-8)利用事件对象 cancelBubble 属性。例如:event.cancelBubble = true;
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
position: relative;
width: 200px;
height: 200px;
background-color: hotpink;
margin: 100px auto;
}
.father .son {
position: absolute;
width: 100px;
height: 100px;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
background-color: lawngreen;
}
</style>
</head>
<body>
<div class="father">
父盒子
<div class="son">子盒子</div>
</div>
<script>
// 阻止冒泡 dom 推荐的标准 event.stopPropagetion()
var son = document.querySelector(".son");
son.addEventListener("click", function(event) {
alert("son");
// Propagation 是传播的意思
event.stopPropagation(); // 阻止事件冒泡
// cancelBubble:取消冒泡
// event.cancelBubble = true;
}, false);
var father = document.querySelector(".father");
father.addEventListener("click", function() {
alert("father");
});
</script>
</body>
</html>
2、阻止事件冒泡的兼容性解决方案
if (event && event.stopPropagation) {
event.stopPropapation();
} else {
window.event.cancelBubble = true;
}
3.6、事件委托(代理、委派)
事件冒泡本身的特性,会带来的好处也会带来坏处,需要我们灵活掌握。程序中现有这样一个场景:
<ul>
<li>Java</li>
<li>Python</li>
<li>PHP</li>
<li>JavaScript</li>
</ul>
如果点击每个li
都会弹出对话框,以前需要给每个li
注册事件,是非常辛苦的,而且访问DOM的次数越多,这就会延长整个页面的交互就绪时间,那么有没有更好的解决方案呢?
事件委托(event delegation):事件委托也称事件代理,是javaScript中绑定事件的常用技巧。顾名思义,事件代理
就是把原本需要绑定的事件委托给父元素,让父元素负责事件监听。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能。
事件委托原理:不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
以上案例:给ul
注册点击事件,然后利用事件对象的 target
来找到当前点击的li
,因为点击li
,事件会冒泡到ul
上,ul
有注册事件,就会触发事件监听器。
事件委托的作用:我们只操作了一次DOM,提高了程序的性能。
如:上一个案例的实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul li {
float: left;
width: 90px;
height: 20px;
list-style: none;
text-align: center;
margin-left: 10px;
background-color: greenyellow;
cursor: pointer;
}
</style>
</head>
<body>
<ul>
<li>Java</li>
<li>Python</li>
<li>PHP</li>
<li>JavaScript</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加监听器,利用事件冒泡影响每一个子节点。
var ul = document.querySelector("ul");
ul.addEventListener("click", function(event) {
// console.log(event.target);
alert(event.target.innerText);
});
</script>
</body>
</html>
3.7、常用的鼠标事件
1、禁止鼠标右键菜单
contextmenu主要控制应该何时显示上下文菜单,主要用于开发者取消默认的上下文菜单。
element.addEventListener("contextmenu", function(event) {
event.preventDefault();
});
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>选中文字,点击右键试试。</div>
<script>
var div = document.querySelector("div");
div.addEventListener("contextmenu", function(event) {
// 取消弹出上下文菜单
event.preventDefault();
});
</script>
</body>
</html>
2、禁止鼠标选中(selectstart 开始选中)
element.addEventListener("selectstart", function(event) {
event.preventDefault();
});
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>鼠标选中我试试。</div>
<script>
// 禁止选中文字
var div = document.querySelector("div");
div.addEventListener("selectstart", function(event) {
event.preventDefault();
});
</script>
</body>
</html>
3、鼠标事件对象
event
对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象MouseEvent
和键盘事件对象keyboardEvent
。
鼠标事件对象 | 说明 |
---|---|
event.clientX | 返回鼠标相对于浏览器窗口可视区的 x 坐标 |
event.clientY | 返回鼠标相对于浏览器窗口可视区的 y 坐标 |
event.pageX | 返回鼠标相对于文档页面的 x 坐标 IE9+支持 |
event.pageY | 返回鼠标相对于文档页面的 y 坐标 IE9+支持 |
event.screenX | 返回鼠标相对于电脑屏幕的 x 坐标 |
event.screenY | 放回鼠标相对于电脑屏幕的 y 坐标 |
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 鼠标事件对象
document.addEventListener("click", function(event) {
console.log(event); // MouseEvent {isTrusted: true, screenX: 237, screenY: 189, clientX: 237, clientY: 86, …}
// 1、client 鼠标在可视区的 x 和 y 坐标
console.log(event.clientX);
console.log(event.clientY);
// 2、page 鼠标在再也文档的 x 和 y 坐标。注意:IE9+ 才支持(可以把页面高度跳转大一点,才能看到效果)
console.log(event.pageX);
console.log(event.pageY);
});
</script>
</body>
</html>
案例:鼠标跟随效果。
- 鼠标不断的移动,使用鼠标移动事件:
mousemove
- 在页面中移动,给
document
注册事件 - 图片要移动距离,而且不占位置,我们使用绝对定位即可。
- 核心原理:每次鼠标移动,我们都会获得最新的鼠标坐标,把这个鼠标坐标作为图片 top 和 left 值就可以跟随鼠标移动。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
img {
position: relative;
}
</style>
</head>
<body>
<img src="./image/success.png" alt="">
<script>
var img = document.querySelector("img");
img.style.position = "absolute";
document.addEventListener("mousemove", function(event) {
// 1、mousemove 只要我们鼠标移动 1px 就会触发这个事件
// console.log(1);
var x = event.pageX;
var y = event.pageY;
img.style.left = x - 24 + "px";
img.style.top = y - 24 + "px";
});
</script>
</body>
</html>
3.8、常用的键盘事件
1、常用的奖品事件
事件除了使用鼠标触发,还可以使用键盘触发。
键盘事件 | 触发条件 |
---|---|
onkeyup | 某个键盘按键被松开时触发 |
onkeydown | 某个键盘按键被按下时触发 |
onkeypress | 某个键盘按键被按下时触发(它不识别功能键,比如ctrl shift 左右箭头键等) |
注意:如果使用addEventListener不需要on
代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 常用的键盘事件
// 1、keyup 按键弹起的时候触发
document.onkeyup = function() {
console.log("我弹起了");
}
// 2、keydown 按键按下的时候触发,能识别功能键(ctrl shift 左右箭头)
document.addEventListener("keydown", function() {
console.log("按键被按下了");
});
// 3、keypress 按键按下的时候触发,不能识别功能键(ctrl shift 左右箭头)
document.addEventListener("keypress", function() {
console.log("我按下了press");
});
// 4、三个事件的执行顺序 keydown --> keypress --> keyup
</script>
</body>
</html>
2、键盘事件对象
常用对象属性:
键盘事件对象的属性 | 说明 |
---|---|
keyCode | 返回该键的ASCLL值 |
注意:onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。在实际开发中,我们使用更多的是 keydown 和 keyup,它能识别所有的键(包括功能键)keypress 不识别功能键,但是 keyCode 属性能区分大小写,返回不同的 ASCLL 值。
代码示例:
// 键盘事件对象中的 keyCode 属性可以得到相应的 ASCII 码值
document.addEventListener("keydown", function(event) {
console.log(event); // KeyboardEvent {isTrusted: true, key: "g", code: "KeyG", location: 0, ctrlKey: false, …}
console.log(event.keyCode); // 回车键 ==> 13
});
/*
1.我们的 keyup 和 keydown 事件不区分字母大小写 a 和 A 得到的都是65
2.我们的 keypress 事件区分字母大小写
3.我们可以利用 keyCode 返回的 ASCII 码值来判断用户按下了那个键
*/
document.addEventListener("keydown", function(event) {
if(event.keyCode === 13) {
alert("您按下了回车键");
}
});
案例1:京东网搜索框自动获得输入框焦点(无需使用鼠标,按下s键即可查看效果)
实现思路:
- 检测用户是否按下了 s 键,如果按下了s键,就把光标定位到搜索框里面。
- 使用键盘事件对象里面的 keyCode 判断用户按下的是否是 s 键
- 搜索框获得焦点:使用 js 里面的 focus() 方法
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
var input = document.querySelector("input");
document.addEventListener("keyup", function(evnet) {
// console.log(event.keyCode); // s键 --> 83
if (evnet.keyCode == 83) {
input.focus();
}
});
</script>
</body>
</html>
案例2:模拟京东快递查询,扩大显示
思路分析:
- 快递单号输入内容时,上面的大号字体盒子(con)显示(这里面的字号更大)
- 表单检测用户输入:给表单添加键盘事件
- 同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 300px;
height: 100px;
margin: 100px auto;
border: 1px solid #666;
text-align: center;
background-color: #fafafa;
}
.box .text {
display: none;
position: absolute;
top: 6px;
left: 147px;
padding: 0 4px;
margin-left: -100px;
width: 196px;
height: 30px;
text-align: left;
font-size: 18px;
line-height: 30px;
background-color: #fff;
border: 1px solid #e9e1e1;
}
.box .text::before {
position: absolute;
content: "";
top: 30px;
left: 25px;
width: 0;
height: 0;
border: 10px solid transparent;
border-top-color: #fff;
}
.box input {
width: 196px;
height: 24px;
padding: 0 4px;
font-size: 13px;
margin-top: 50px;
outline: none;
border: 1px solid #999;
border-bottom-color: #d8d8d8;
border-right-color: #d8d8d8;
}
</style>
</head>
<body>
<div class="box">
<div class="text"></div>
<input type="text" placeholder="请输入快递单号">
</div>
<script>
var text = document.querySelector(".box .text");
var input = document.querySelector("input");
input.addEventListener("keyup", function() {
if (this.value == "") {
text.style.display = "none";
} else {
text.style.display = "block";
text.innerText = this.value;
}
});
</script>
</body>
</html>
注意:以上案例键盘事件我们选择的是keyup
。当我们选择keydown
时,会出现如下效果:
我们发现与我们输入的数字显示不一致!这是因为keydown 和 keypress 在文本框里面的特点:他们两个事件触发的时候,文字还没有落入文本框中
。
4、浏览器对象模型BOM
4.1、BOM概述
BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象
,其核心对象是window
。
BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。
BOM缺乏标准,JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C,BOM最初是Netscape浏览器标准的一部分。
DOM与BOM:
DOM | BOM |
---|---|
文档对象模型 | 浏览器对象模型 |
DOM就是把文档 当做一个对象 来看待 | 把浏览器 当做一个对象 来看待 |
DOM的顶级对象是document | BOM的顶级对象是window |
DOM主要学习的是操作页面元素 | BOM学习的是浏览器窗口交互的一些对象 |
DOM是W3C标准规范 | BOM是浏览器厂商在各自浏览器上定义的,兼容性较差 |
4.2、BOM的构成
BOM比DOM更大,它包含DOM。
BOM的构成:
window对象是浏览器的顶级对象,它具有双重角色:
- 它是JavaScript访问浏览器窗口的一个接口。
- 它是一个全局对象。定义在全局作用域中的变量、函数都会变成window对象的属性和方法。
var num = 10;
// 定义在全局作用域中的变量、函数都会变成window对象的属性和方法
function eat() {
alert("吃吃~");
}
console.log(window.num); // 10
window.eat();
在调用的时候可以省略 window ,前面学习的对话框都属于 window 对象方法,如alert()、prompt()等
。
注意:window 下的一个特殊属性 window.name
// var name = "alex";
console.log(window.name);
console.dir(window);
4.3、window 对象常见的对象
1、窗口加载事件
window.onload = function() {}
// 或者
window.addEventListener("load", function() {});
window.onload
是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、css文件等),就调用的处理函数。
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
window.addEventListener("load", function() {
var btn = document.getElementsByClassName("btn")[0];
btn.addEventListener("click", function() {
alert("hello world");
});
});
</script>
</head>
<body>
<button class="btn">点我</button>
</body>
</html>
注意:
- 有了
window.onload
就可以吧js代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。 window.onload
传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload
为准。- 如果使用
addEventListener
则没有限制。
对于窗口加载事件还有一个不常使用的:
document.addEventListener("DOMContentLoaded", function() {});
/*
1.DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等
2.IE9+才支持
3.如果页面的图片很多的话,从用户访问到 onload 触发可能需要较长的时间,交互效果就不能实现,必然影响用户体验,此时用 DOMContentLoaded 事件比较合适
*/
2、调整窗口大小事件
window.onresize = function() {}
// 或者
window.addEventListener("resize", function() {});
window.onresize
是调整窗口大小加载事件,当触发时就调用的处理函数。
如:
window.addEventListener("resize", function() {
alert("hello world");
});
注意:
- 只要窗口大小发生像素变化,就会触发这个事件。
- 我们经常利用这个事件完成响应式布局,
window.innerWidth
当前屏幕的宽度
4.4、定时器
1、两种定时器
window对象给我们提供了2个非常好的方法 - 定时器
。
setTimeout()
setInterval()
2、setTimeout()
定时器
语法:
window.setTimeout(调用函数, [延迟的毫秒数]);
/*
1.这个window在调用的时候可以省略
2.setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数
3.这个调用函数可以直接写函数,还可以写函数名或 "函数名()"
4.页面中可能有很多的定时器,我们经常给定时器加标识符(名字)
5.延迟的毫秒数省略默认是0,如果写,必须是毫秒
*/
如:
window.setTimeout(function() {
console.log("hello world");
}, 2000); // 注意延迟的时间,单位是毫秒。也可以省略书写延迟时间,默认是0
setTimeout()
这个调用函数我们也称为回调函数callback
普通函数时按照代码顺序直接调用。而这个函数,需要等待时间,时间到了才去调用这个函数,因此成为回调函数。
以前我们讲的 element.onclick = function() {}
或者element.addEventListener("click", function(){});
里面的函数也是回调函数。
案例:5s后自动关闭广告
思路:5s之后把广告隐藏即可。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 230px;
height: 340px;
margin: 100px auto;
}
</style>
</head>
<body>
<div class="box">
<img src="https://dimg04.c-ctrip.com/images/0zg38120008g0r9reDD61.jpg" alt="">
</div>
<script>
var box = document.querySelector(".box");
setTimeout(function(){
box.style.display = "none";
}, 5000);
</script>
</body>
</html>
3、停止setTimeout()
定时器
在某些特殊情况下,我们需要停止这个定时器。
语法:
window.clearTimeout(timeoutID); // window可以省略
clearTimeout()
方法取消了先前通过调用setTimeout()
建立的定时器。
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 230px;
height: 340px;
margin: 100px auto;
}
</style>
</head>
<body>
<div class="box">
<img src="https://dimg04.c-ctrip.com/images/0zg38120008g0r9reDD61.jpg" alt="">
</div>
<button class="btn">点击停止定时器</button>
<script>
var box = document.querySelector(".box");
var timeout = setTimeout(function(){
box.style.display = "none";
}, 5000);
// 点击停止定时器
var btn = document.querySelector(".btn");
btn.addEventListener("click", function() {
window.clearTimeout(timeout);
});
</script>
</body>
</html>
4、setInterval()
定时器
语法:
window.setInterval(回调函数, [间隔的毫秒数]); // window 可以省略不写
setInterval()
方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
setInterval(function() {
// 每隔一秒打印一次
console.log("hello world");
}, 1000);
/*
1.setTimeout() 只调用一次
2.setInterval() 重复调用
*/
</script>
</body>
</html>
案例:倒计时效果。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.float_left {
float: left;
}
div {
width: 50px;
height: 30px;
color: #fff;
background-color: black;
margin-left: 5px;
text-align: center;
line-height: 30px;
font-size: 18px;
}
</style>
</head>
<body>
<h3>距520还剩:</h3>
<div class="day float_left">0</div>
<div class="hour float_left">1</div>
<div class="minute float_left">2</div>
<div class="second float_left">3</div>
<script>
var day = document.querySelector(".day");
var hour = document.querySelector(".hour");
// console.log(hour);
var minute = document.querySelector(".minute");
var second = document.querySelector(".second");
// 时间处理构造函数
function MyDate(timeString) {
var nowTime = Date.now(); // 当前系统时间戳
var inputTime = new Date(timeString).getTime(); // 用户指定的时间戳
// 求出剩余时间的总秒数
var times = (inputTime - nowTime) / 1000;
this.getDay = function() {
// 1、计算天数
var d = parseInt(times / 60 / 60 / 24);
return d = d < 10 ? "0" + d : d;
};
this.getHour = function() {
// 2、计算小时
var h = parseInt(times / 60 / 60 % 24);
return h = h < 10 ? "0" + h : h;
};
this.getMinute = function() {
// 3、计算分
var m = parseInt(times / 60 % 60);
return m = m < 10 ? "0" + m : m;
};
this.getSecond = function() {
// 4、计算秒
var s = parseInt(times % 60);
return s = s < 10 ? "0" + s : s;
}
}
// 时间处理函数
function countTime(time) {
var nowTime = Date.now(); // 当前系统时间戳
var inputTime = new Date(time).getTime(); // 用户指定的时间戳
// 求出剩余时间的总秒数
var times = (inputTime - nowTime) / 1000;
// 1、计算天数
var d = parseInt(times / 60 / 60 / 24);
d = d < 10 ? "0" + d : d;
// 2、计算小时
var h = parseInt(times / 60 / 60 % 24);
h = h < 10 ? "0" + h : h;
// 3、计算分
var m = parseInt(times / 60 % 60);
m = m < 10 ? "0" + m : m;
// 4、计算秒
var s = parseInt(times % 60);
s = s < 10 ? "0" + s : s;
return d + "天" + h + "时" + m + "分" + s + "秒";
}
var dateStr = "2021-5-20 0:0:0";
// console.log(countTime(dateStr));
// var myDate = new MyDate(dateStr);
// console.log(myDate.getSecond());
function countDown() {
var myDate = new MyDate(dateStr);
// console.log(myDate.getSecond());
day.innerText = myDate.getDay() + "天";
hour.innerText = myDate.getHour() + "时";
minute.innerText = myDate.getMinute() + "分";
second.innerText = myDate.getSecond() + "秒";
// console.log(myDate.getSecond());
myDate = null;
};
// 先调用一次这个函数,防止第一次刷新页面有空白
countDown();
// 每隔一秒执行一次
setInterval(countDown, 1000);
</script>
</body>
</html>
5、停止setInterval()
定时器
语法:
window.clearInterval(intervalID); // clearInterval() 方法取消了先前通过调用 setInterval() 建立的定时器
/*
1.window 可以省略
2.里面的参数就是定时器的标识符
*/
案例1:开启和停止定时器
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
margin: 100px auto;
text-align: center;
}
</style>
</head>
<body>
<div class="box">
<div class="con">
</div>
<button class="start">开启定时器</button>
<button class="stop">停止定时器</button>
</div>
<script>
var con = document.querySelector(".con");
var start = document.querySelector(".start");
var stop = document.querySelector(".stop");
var timeout = setInterval(function () {
var myDate = new MyDate("2021-5-20 0:0:0");
con.innerText = myDate.getHour() + "时" + myDate.getMinute() + "分" + myDate.getSecond() + "秒";
myDate = null;
}, 1000);
start.addEventListener("click", function () {
timeout = setInterval(function () {
var myDate = new MyDate("2021-5-20 0:0:0");
con.innerText = myDate.getHour() + "时" + myDate.getMinute() + "分" + myDate.getSecond() + "秒";
myDate = null;
}, 1000);
});
stop.addEventListener("click", function() {
clearInterval(timeout);
});
// 时间处理构造函数
function MyDate(timeString) {
var nowTime = Date.now(); // 当前系统时间戳
var inputTime = new Date(timeString).getTime(); // 用户指定的时间戳
// 求出剩余时间的总秒数
var times = (inputTime - nowTime) / 1000;
this.getDay = function () {
// 1、计算天数
var d = parseInt(times / 60 / 60 / 24);
return d = d < 10 ? "0" + d : d;
};
this.getHour = function () {
// 2、计算小时
var h = parseInt(times / 60 / 60 % 24);
return h = h < 10 ? "0" + h : h;
};
this.getMinute = function () {
// 3、计算分
var m = parseInt(times / 60 % 60);
return m = m < 10 ? "0" + m : m;
};
this.getSecond = function () {
// 4、计算秒
var s = parseInt(times % 60);
return s = s < 10 ? "0" + s : s;
}
}
</script>
</body>
</html>
案例2:发送短信倒计时。
点击发送按钮后,该按钮60s之内不能再次发送短信,防止重复发送短信。
思路:
- 按钮点击之后,禁用按钮
disabled = true;
- 同时按钮里面的内容会变化,使用
innerHTML
修改
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
margin: 100px auto;
}
</style>
</head>
<body>
<div class="box">
<span class="text">手机号码:</span>
<input type="text">
<button class="btn">发送</button>
</div>
<script>
var btn = document.querySelector(".btn");
var date = 60;
btn.addEventListener("click", function() {
// 禁用按钮
btn.disabled = true;
var timeout = setInterval(() => {
btn.innerText = "还剩下" + date-- + "秒";
if(date === 0) {
btn.disabled = false;
btn.innerText = "发送";
date = 60;
clearInterval(timeout);
}
}, 1000);
});
</script>
</body>
</html>
5、不同情况this指向问题
this
的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this
到底指向谁,一般情况下this
的最终指向的是那个调用它的对象。
现阶段,我们先了解如下几个this指向:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">提交</button>
<script>
// 1、全局作用域或者普通函数中 this 指向全局对象 window (注意定时器里面的 this 指向 window)
console.log(this); // Window {window: Window, self: Window, document: document, name: "", location: Location, …}
function eat() {
console.log(this); // Window {window: Window, self: Window, document: document, name: "", location: Location, …}
}
eat(); // window.eat();
setInterval(() => {
console.log(this);
}, 10000);
// 2、方法调用中谁调用 this 指向谁
var obj = {
eat : function() {
console.log(this); // {eat: ƒ}
}
}
obj.eat();
var btn = document.querySelector(".btn");
btn.onclick = function() {
console.log(this); // <button class="btn">提交</button>
}
// 3、构造函数中 this 指向构造函数的实例
function Person() {
console.log(this);
}
var person = new Person(); // Person {}
</script>
</body>
</html>
6、JavaScript执行机制
6.1、JavaScript是单线程
JavaScript语言的一大特点就是单线程
,也就是说,同一个事件只能做一件事情
。这是因为JavaScript这门脚本语言诞生的使命所致。JavaScript是为处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除。
6.2、同步与异步
为了解决这个问题,利用多核CPU的计算能力,HTML5提出 Web Worker标准,允许JavaScript脚本创建多个线程,于是,JavaScript出现了同步
和异步
。
同步:
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。
异步:
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做的异步做饭,我们在烧水的同时,利用这烧水的时间去切菜、洗菜等。
如:
console.log(1);
setTimeout(() => {
console.log(3);
}, 1000);
console.log(2);
/*
打印顺序是:1 2 3
*/
修改以上代码:
console.log(1);
setTimeout(() => {
console.log(3);
}, 0);
console.log(2);
打印的顺序又是什么呢?我们发现任是 1 2 3。这是为何?
同步任务:同步任务都在主线程上执行,形成一个执行栈
。
同步任务:JavaScript的异步是通过回调函数实现的。
一般而言,异步任务有以下三种类型:
- 普通事件,如click、resize等
- 资源加载,如load、error等
- 定时器,包括setInterval、setTimeout等
6.3、JS执行机制
- 先执行
栈中的同步任务
- 异步任务(回调函数)放入任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取
任务队列
中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
由于主线程不断的重复获得任务、执行任务、再次获取任务、再执行,所以这种机制被称为事件循环(event loop)
7、location对象
7.1、什么是location对象
window对象给我吗提供了一个location属性
用于获取或设置窗体的URL
,并且可以用于解析URL
。因为这个属性返回的是一个对象,所以我们将这个属性也称为location对象
。那么URL又是什么呢?
URL简介:
统一资源定位符(Uniform Resource Locator,URL)
是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL的一般语法格式为:
https://www.bilibili.com/video/BV1Sy4y1C7ha?p=281&spm_id_from=pageDriver
组成 | 说明 |
---|---|
protocol | 通信协议,常用的有http,ftp,maito等 |
host | 主机(域名)www.laizhenghua.cn |
port | 端口号,可选,省略时使用方案的默认端口,如http的默认端口为80 |
path | 路径,由零个或多个/ 符号隔开的字符串,一般用来表示主机上的一个目录或文件地址 |
query | 参数,以键值对的形式,通过& 符号分隔开来 |
fragment | 片段,# 后面内容,常用于链接锚点 |
7.2、location对象的常用属性
location对象属性 | 返回值 |
---|---|
location.href | 获取或设置整个URL |
location.host | 返回主机(域名)www.baidu.com |
location.port | 返回端口号,如果未写返回空字符串 |
location.pathname | 返回路径 |
location.search | 返回参数 |
location.hash | 返回片段 # 后面内容,常见于链接锚点 |
重点记住:href
和search
案例:5秒钟之后自动跳转页面
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">点击</button>
<div class="con"></div>
<script>
var btn = document.querySelector(".btn");
var con = document.querySelector(".con");
btn.addEventListener("click", function() {
// console.log(window.location.href);
location.href = "http://www.baidu.com";
});
var countTime = 5;
setInterval(function() {
con.innerText = "还有"+ countTime-- + "秒跳转至百度首页";
if (countTime === 0) {
location.href = "http://www.baidu.com";
countTime = 5;
}
}, 1000);
</script>
</body>
</html>
7.3、location对象的常用方法
location对象方法 | 返回值 |
---|---|
location.assign() | 跟href一样,可以跳转页面(也称为重定向页面) |
location.replace() | 替换当前页面,因为不记录历史,所以不能后退页面 |
location.reload() | 重新加载页面,相当于刷新按钮或者 f5 如果参数为 true 强制刷新 ctrl+f5 |
如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">跳转</button>
<script>
var btn = document.querySelector(".btn");
btn.addEventListener("click", function() {
location.assign("http://www.baidu.com"); // 记录浏览器历史,所以可以实现后退功能
/*
location.replace("http://www.baidu.com"); // 不记录浏览器历史,不可以实现后退功能
*/
/*
location.reload("http://www.baidu.com"); // 重新加载
*/
});
</script>
</body>
</html>
8、navigator对象
navigator
对象包含有关浏览器的信息,它有很多属性,我们最常用的是userAgent
,该属性可以返回由客户端发送服务端的user-agent
头部的值。
下面前端代码可以判断用户那个终端打开的页面,并实现跳转:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn">跳转</button>
<script>
var btn = document.querySelector(".btn");
btn.addEventListener("click", function() {
location.assign("http://www.baidu.com"); // 记录浏览器历史,所以可以实现后退功能
/*
location.replace("http://www.baidu.com"); // 不记录浏览器历史,不可以实现后退功能
*/
/*
location.reload("http://www.baidu.com"); // 重新加载
*/
});
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOs|Symbian|Windows Phone)/i))) {
window.location.href = ""; // 手机
} else {
window.location.href = ""; // 电脑
}
</script>
</body>
</html>
9、history对象
window
对象给我们提供了一个history对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的URL。
history对象方法 | 作用 |
---|---|
back() | 可以后退功能 |
forward() | 前进功能 |
go(参数) | 前进后退功能,参数如果是1前进1一个页面,如果是-1后退1个页面 |
前进
和后退
功能,我们一般都使用浏览器自带的,history
对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到。
10、Object对象
10.1、defineProperty方法
在JavaScript
中Object.defineProperty()
的作用就是给js对象添加高级属性(是否可被枚举,是否可被修改等),还可以使用此方法定义新属性或修改原有的属性。
基本使用:
var person = {
name: "alex",
age: 25
}
console.log(person);
// 需要添加三个参数:对象 属性名 配置对象
Object.defineProperty(person, "weight", {
value: "120kg",
enumerable: true, // 控制属性是否可以被枚举,默认是false
writable: true, // 控制属性是否可以被修改,默认为false
configurable: true // 控制属性是否可以被删除,默认为false。注意:删除属性语法:delete person.age
});
console.log(person);
var keys = Object.keys(person);
keys.forEach(item => {
console.log(person[item]);
});
console.log(keys);
高级用法:
let number = 20;
let person = {
name: "alex"
}
// person对象与number变量通过 Object.defineProperty() 产生关联
Object.defineProperty(person, "age", {
// 当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
get: function() {
return number;
},
// 当有人修改person的age属性时,set函数就会被调用,且会收到修改的具体值
set(value) {
// 简写形式
number = value;
}
});
console.log(person);
数据代理:通过一个对象代理另一个对象中属性的操作(读/写)
let a = {x: 100}
let b = {y: 200}
Object.defineProperty(a, "x", {
get() {
return b.y;
},
set(value) {
b.y = value;
}
});
11、Vue
事件修饰符:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 阻止默认事件 -->
<a href="https://www.baidu.com" @click.prevent="test($event)">a标签的默认行为是跳转页面</a>
<br>
<!-- 阻止事件冒泡 -->
<div style="width: 100px; height:40px; background-color: aqua" @click="a($event)">
<button @click.stop="b($event)">点我</button>
</div>
<!--阻止事件冒泡 + 阻止默认事件-->
<div style="width: 100px; height:40px; background-color: aqua" @click="a($event)">
<!--<button @click.stop="b($event)">点我</button>-->
<a href="http://www.baidu.com" @click.stop.prevent="b($event)">百度</a>
</div>
<br>
<!-- 事件只触发一次 -->
<button @click.once="onceTest($event)">只触发一次</button>
</div>
<script type="text/javascript">
/*
Vue中的事件修饰符(修饰符可以连续写):
1.prevent 阻止默认事件(常用)
2.stop 阻止事件冒泡(常用)
3.once 事件只触发一次(常用)
*/
let vm = new Vue({
el: "#app",
methods: {
test(event) {
alert("Vue 阻止了a标签的默认行为");
},
a(event) {
alert("aa");
},
b(event) {
alert("bb");
},
onceTest(event) {
alert("只触发一次");
}
}
});
/*
JavaScript事件流:事件捕获 -> 事件冒泡
*/
console.log(vm);
</script>
</body>
</html>
键盘事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- @keyup.enter: 监听回车键,enter我们称为别名 -->
<!-- <input type="text" placeholder="按下回车键提示输入" @keyup.enter="showInfo($event)"> -->
<input type="text" placeholder="按下回车键提示输入" @keyup="showInfo($event)">
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
},
methods: {
showInfo(event) {
console.log(event.key); // CapsLock
// console.log(event.keyCode);
if (event.keyCode === 13) {
// 按下了回车键
alert("hello world");
}
}
}
});
/*
1、Vue中常用的按键别名:
回车 enter
删除 delete(捕获删除和退格键)
退出 esc
空格 space
换行 tab
上 up
下 down
左 left
右 right
除了常用的别名,vue也支持其他键盘英文名称
特殊的 切换大写键(CapsLock) 书写方式为@keyup.caps-lock
2、Vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为 keytab-case (短横线命名)
3、系统装饰键(用法特殊):ctrl alt shift meta
1.配合keyup使用:按下装饰键的同时,再按下其他键,随后释放其他键,事件才被触发
2.配合keydown使用:正常触发事件。
*/
</script>
</body>
</html>
计算属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
姓:<input type="text" v-model="firstName"><br>
名:<input type="text" v-model="lastName"><br>
姓名:<span v-text="fullName"></span>
</div>
<script>
/*
计算属性:
1.定义:要用的属性不存在,要通过已有的属性计算得来
2.原理:底层借助了Object.defineproperty方法提供的getter和setter
3.get()函数什么时候执行?详见以下代码
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
1、计算属性最终会出现在vm上,直接读取使用即可
2、如果计算属性要被修改,那必须写set()函数去响应修改
*/
let vm = new Vue({
el: "#app",
data: {
firstName: "",
lastName: ""
},
methods: {
},
computed: {
// 计算属性。vm._data中是没有fullName属性的。计算好后再把fullName放入vm中
// 计算的属性有缓存
fullName: {
// get有什么作用?当有人读取fullName时,get就会被调用,且返回值 就作为fullName的值
// get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时。
get() {
console.log("debug -> 我被调用了");
return this.firstName + "-" + this.lastName;
},
// set什么时候被调用?当fullName被修改时
set() {
console.log("debug -> set被调用了");
}
}
/*// 简写方式
fullName() {
...
}*/
}
});
console.log(vm);
</script>
</body>
</html>
属性与计算属性:
监视属性:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 注意:模板中只能写一些简单的表达式。如alert()就不行 -->
<!-- <h1>今天天气很{{isHot ? '热' : '凉爽'}}</h1> -->
<h1 v-text="'今天天气很' + message"></h1>
<button @click="change($event)">切换天气</button>
</div>
<script>
/*
监视属性watch:
1.当被监视的属性变化时,回调函数自动调用,进行相关操作
2.监视的属性必须存在,才能进行监视
3.监视属性的两种写法:
new Vue 时传入watch配置
通过vm.$watch监视
*/
let vm = new Vue({
el: "#app",
data: {
isHot: false,
message: '',
number: {
a: 1,
b: 2
}
},
methods: {
change(event) {
this.isHot = !this.isHot;
this.message = this.isHot ? "炎热" : "凉爽";
}
},
// 监听属性
watch: {
isHot: {
immediate: true, // 初始化时让handler调用以下
// handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
}
},
// 深度监视number,注意number是一个对象,对象内容改变,number就算改变
number: {
deep: true, // 深度监视写法(多层级)
handler(newValue, oldValue) {
console.log("number被修改了", newValue, oldValue);
}
},
// 简写方式
isOpen(newValue, oldValue) {
console.log(...);
}
}
});
// 更灵活监视属性的写法
vm.$watch("isHot", {
immediate: true, // 初始化时让handler调用以下
// handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log("isHot被修改了", newValue, oldValue);
}
});
/*
computed 和 watch 之间的区别:
1.computed能完成的功能,watch都可以完成
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
两个重要的原则:
1.所有被vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
2.所有不被vue管理的函数(定时器的回调函数,ajax的回调函数等,Promise的回调函数),最好写成箭头函数,这样this的执行才是vm或组件实例对象
*/
</script>
</body>
</html>
v-for指令中的key:
/*
面试题:我们使用v-for指令遍历数据时,需绑定一个key,这是为何?vue中的key有什么作用?
1、虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,vue会根据【新数据】生成【新的虚拟DOM】,随后vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较(diff算法)
对比规则如下:
2、diff对比规则:
1.就虚拟DOM中找到了与新虚拟DOM相同的key:
1.若旧虚拟DOM中内容没变,直接使用之前的真实DOM!(复用节点)
2.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
2.就虚拟DOM中为找到与新虚拟DOM相同的key
1.创建新的真实DOM,随后渲染到页面
3、使用index作为key可能会引发的问题:
1.若对数据进行逆序添加。逆序删除等破坏顺序的操作,会产生没有必要的真实DOM更新 ==> 页面效果没问题,但效率低
2.如果结构中还包含输入类的DOM,会产生错误DOM更新 ==> 页面渲染也会有问题
4、开发中如何选择key?
1.最好使用每条数据的唯一标识作为key。比如id、手机号、省份证号、学号等唯一值
2.如果不存在对数据的 逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表展示,使用index作为key是没有问题的。
*/
vue监视数据的原理:
// 模拟数据监测(观察者模型)
let person = {
name: "alex",
age: 21
}
/*Object.defineProperty(person, "name", {
get() {
// 此时这里会产生死循环
return person.name;
},
set(value) {
person.name = value;
}
});*/
function Observer(obj) {
// 汇总对象中所有的熟悉封装为一个数组
if (obj === null) {
return null;
}
let keys = Object.keys(obj);
keys.forEach(key => {
// console.log(this); Observer {}
Object.defineProperty(this, key, {
get() {
return obj[key];
},
set(value) {
obj[key] = value;
}
});
});
}
// 创建一个监视的实例对象,用于监视person中属性的变化
const watchObj = new Observer(person);
console.log(watchObj);
// vm实例对象
let vm = {};
vm._data = person = watchObj;
/*
Vue监视数据的原理:
1.vue会监视data中所有层次的数据。
2.如果监视对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据
1、对象中后追加的属性,Vue默认不做响应式处理
2、如需给后添加的属性做响应式,请使用如下API
Vue.set(target, propertyName/index, value) 或
vm.$set(target, propertyName/index, value)
注意:target是一个对象
3.如何监视数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
1、调用原生对应的方法对数组进行更新
2、重新解析模板,进而更新页面
4.在Vue修改数组中的某一个元素一定要用如下方法:
1.使用这些API:push() pop() shift() unshift() splice() sort() reverse() 这些都被vue重写了
2.Vue.set() 或 vm.$set()
*/
自定义指令:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--
1、定义一个v-big指令,和v-text功能类似,但是会把绑定的值放大10倍
2、定义一个v-fbind指令,和v-bind功能类似,但可以让其绑定的input元素默认获取焦点
-->
<div id="root">
当前num = <span v-text="num"></span><br>
(放大10倍)num = <span v-big="num"></span>
<br>
<input type="text" v-fbind="num">
<button @click="add($event)">num++</button>
</div>
<script>
// 全局过滤器
Vue.filter('slice', function(value) {
return value.slice(0, 4);
});
// 全局指令
Vue.directive("fbind", {
bind(element, binding) {
},
inserted() {
},
update() {
}
});
/*
自定义指令注意点:
1.指令定义时不加v-,但使用时要加v-
2.指令名如果是多个单词,要使用kebab-case命名方式,不要使用kebabCase命名
*/
let vm = new Vue({
el: "#root",
data: {
num: 1
},
// 自定义指令(局部指令)
directives: {
// 函数式自定义指令
// big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时
big(element, binding) {
element.innerText = binding.value * 10;
},
// 对象式自定义指令
fbind: {
// 指令与元素成功绑定时
bind(element, binding) {
console.log("bind");
console.log(this); // 此时this的指向是 window 对象
element.value = binding.value;
},
// 指令所在元素被插入页面时
inserted(element, binding) {
console.log("inserted");
element.focus(); // 获取焦点
},
// 指令所在的模板被重新解析时
update(element, binding) {
console.log("update");
element.value = binding.value;
}
}
},
methods: {
add(event) {
this.num++;
}
}
});
</script>
</body>
</html>