Web APIs
0x01 概述
- API 是一些预先定义的函数,提供应用程序与开发人员基于某软件或硬件访问一组例程的能力
- Web APIs 是 W3C 组织的标准,是 JS 独有的部分
- Web API 是浏览器提供的一套操作浏览器功能和页面元素的 API,即 DOM 与 BOM
0x02 DOM
(1)简介
-
文档对象模型(Document Object Model)是 W3C 组织推荐的处理可扩展标记语言的标准编程接口
- 可以改变网页内容、结构和样式
-
DOM 树:DOM 对象
graph TB 文档-->0[根元素<br/>< html >]-->1[元素<br/>< head >] & 2[元素<br/>< body >] 1-->11[元素<br/>< title >]-->111[文本<br/>文档标题] 2-->21[元素<br/>< a >] & 22[元素<br/>< h1 >] 21-->211[属性<br/>href] & 212[文本<br/>链接文本] 22-->221[文本<br/>一级标题]- 文档:即页面,DOM 中使用
document
表示 - 元素:页面中的任何标签,DOM 中使用
element
表示 - 节点:页面中的任何内容,DOM 中使用
node
表示
- 文档:即页面,DOM 中使用
(2)获取元素
a. 根据 id
属性
-
getElementById()
-
返回带有指定
id
的元素对象
<p id="content">This is a paragraph.</p>
<script>
const pElement = document.getElementById("content");
console.dir(pElement);
</script>
b. 根据标签名
-
getElementByTagName()
-
返回带有指定标签名的元素对象集合
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
const liElements = document.getElementsByTagName("li");
console.log(liElements);
</script>
-
也可以用于获取父元素内所有指定标签名的子元素
- 父元素必须为单个对象
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> const ulElement = document.getElementsByTagName("ul"); const liElements = ulElement[0].getElementsByTagName("li"); console.log(liElements); </script>
c. 通过 HTML5 新方法
-
根据类名返回元素对象集合
getElementByClassName()
<div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <script> const divElements = document.getElementsByClassName("item"); console.log(divElements); </script>
-
根据指定选择器返回第一个元素对象
querySelector()
<div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <script> const divElement = document.querySelector(".item"); console.log(divElement); </script>
-
根据指定选择器返回元素对象集合
querySelectorAll()
<div class="item">Item 1</div> <div class="item">Item 2</div> <div class="item">Item 3</div> <script> const divElements = document.querySelectorAll(".item"); console.log(divElements); </script>
d. 特殊元素
-
html
元素<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> This is a simple page. <script> const htmlElement = document.documentElement; console.dir(htmlElement); </script> </body> </html>
-
body
元素<body> This is a simple page. <script> const bodyElement = document.body; console.dir(bodyElement); </script> </body>
(3)事件基础
-
JavaScript 可用于创建动态页面,事件是可以被 JavaScript 侦测到的行为
-
页面中每个元素都可以产生某些可以触发 JavaScript 的事件
<p>This is a paragraph.</p> <script> const pElement = document.querySelector("p"); pElement.onclick = function (e) { console.log(e); }; </script>
-
事件三要素包括:
- 事件源:事件被触发的对象
- 事件类型:触发事件的方式
- 事件处理:通过函数处理事件
-
执行事件的步骤:
- 获取事件源
- 注册(或绑定)事件
- 添加事件处理程序
- 采用函数赋值形式
-
常见鼠标事件
事件 条件 onclick
左键点击后触发 onmouseover
经过时触发 onmouseout
离开时触发 onfocus
获得焦点触发 onblur
失去焦点触发 onmousemove
移动触发 onmouseup
弹起触发 onmousedown
按下触发
(4)操作元素
- 使用 DOM 操作可以改变网页内容、结构和样式
a. 内容
innerHTML
:起始位置到终止位置的全部内容,包括 HTML 标签、空格、换行等innerText
:类似innerHTML
,不包括 HTML 标签、空格、换行等
<p>
This is a paragraph.<br />
<span>Text in span</span>
</p>
<script>
const pElement = document.querySelector("p");
console.log(pElement.innerHTML);
console.log(pElement.innerText);
</script>
b. 属性
-
如
src
、href
、id
、value
等 -
举例 1:图片切换至下一张
<button id="next">下一张图片</button> <img src="./1.png" alt="图片1" /> <script> const next = document.getElementById("next"); const imgElement = document.querySelector("img"); next.onclick = () => { imgElement.src = "./2.png"; imgElement.alt = "图片2"; next.disabled = true; }; </script>
-
举例 2:显隐密码
<input type="password" name="password" autofocus /> <label>显示密码</label> <script> const label = document.querySelector("label"); const input = document.querySelector("input"); let flag = 1; label.onclick = () => { if (flag) { label.innerText = "隐藏密码"; input.type = "text"; flag = 0; } else { label.innerText = "显示密码"; input.type = "password"; flag = 1; } }; </script>
c. 样式
-
行内样式操作
<div style="width: 300px; height: 300px; background-color: red"></div> <script> const div = document.querySelector("div"); div.onclick = () => { div.style.backgroundColor = "blue"; }; </script>
- JavaScript 中,样式采取驼峰命名法,如
backgroundColor
等 - JavaScript 修改样式操作产生的是行内样式,CSS 的权重更高
- JavaScript 中,样式采取驼峰命名法,如
-
类名样式操作
<div style="width: 300px; height: 300px; background-color: red"></div> <script> const div = document.querySelector("div"); div.onclick = () => { div.className = "clicked"; }; </script>
className
会更改(覆盖)元素的类名
-
举例 1:密码框格式错误提示信息
<div> <input type="password" /> <p>输入 3~10 个字符</p> </div> <script> const input = document.querySelector("input") const p = document.querySelector("p"); input.onblur = function() { if (this.value.length < 3 || this.value.length > 10) { p.className = 'error' p.innerText = "字符数错误" } else { p.className = 'right' p.innerText = "字符数正确" } } </script>
-
举例 2:(排他思想)
<button>按钮 1</button> <button>按钮 2</button> <button>按钮 3</button> <script> const buttons = document.querySelectorAll("button"); for (let i = 0; i < buttons.length; i++) { buttons[i].onclick = function () { for (let j = 0; j < buttons.length; j++) { buttons[j].style.backgroundColor = ""; } this.style.backgroundColor = "red"; }; } </script>
-
举例 3:表格行在鼠标悬浮时换色
<table border="1"> <thead> <tr> <th>Name</th> <th>Age</th> <th>Gender</th> </tr> </thead> <tbody> <tr> <td>John</td> <td>20</td> <td>Male</td> </tr> <tr> <td>Jane</td> <td>21</td> <td>Female</td> </tr> <tr> <td>Jim</td> <td>22</td> <td>Male</td> </tr> </tbody> </table> <script> const trs = document.querySelector("tbody").querySelectorAll("tr"); for (let i = 0; i < trs.length; i++) { trs[i].onmouseover = function () { this.style.backgroundColor = "red"; }; trs[i].onmouseout = function () { this.style.backgroundColor = ""; }; } </script>
-
举例 4:表单全选的选中与取消
<table border="1"> <thead> <tr> <th><input type="checkbox" id="selectAll" /></th> <th>Name</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td><input type="checkbox" /></td> <td>John</td> <td>20</td> </tr> <tr> <td><input type="checkbox" /></td> <td>Jane</td> <td>21</td> </tr> <tr> <td><input type="checkbox" /></td> <td>Jim</td> <td>22</td> </tr> </tbody> </table> <script> const selectAll = document.getElementById("selectAll"); const checkboxes = document.querySelector("tbody").querySelectorAll("input"); selectAll.onchange = function () { checkboxes.forEach(function (checkbox) { checkbox.checked = selectAll.checked; }); }; for (let i = 0; i < checkboxes.length; i++) { checkboxes[i].onchange = function () { if (!this.checked) { selectAll.checked = false; } else { let allChecked = true; checkboxes.forEach(function (checkbox) { if (!checkbox.checked) { allChecked = false; } }); selectAll.checked = allChecked; } }; } </script>
d. 自定义属性
element.属性
获取的是内置属性(即元素本身自带的属性)element.getAttribute('属性')
获取的是自定义和内置的属性
<div></div>
<script>
const div = document.querySelector("div");
div.setAttribute("data-tabindex", 1); // 设置属性
const tabindex = div.getAttribute("data-tabindex"); // 获取属性
console.log(tabindex);
div.removeAttribute("data-tabindex"); // 移除属性
</script>
-
自定义属性的目的:保存并使用数据
- 对于可以简单且可以明文展示的数据可以保存在页面中,省去使用数据库
-
HTML5 规定,自定义属性使用
data-
前缀命名并赋值 -
HTML5 新增以下方法获取属性值:
<div data-class-name="div"></div> <script> const div = document.querySelector("div"); console.log(div.dataset.className); // 方法一 console.log(div.dataset["className"]); // 方法二 </script>
(5)节点操作
a. 简介
-
节点操作主要是利用节点层级关系获取元素
- 利用父子兄节点关系获取元素
- 逻辑性强
- 兼容性差
-
节点是页面中的任何内容,DOM 中使用
node
表示,均可使用 JavaScript 访问 -
一般地,节点至少拥有以下基本属性:
-
节点类型:
nodeType
节点类型 nodeType
值HTML 元素 1 属性 2 文本 3 -
节点名称:
nodeName
-
节点值:
nodeValue
-
b. 父节点
<div id="parent">
<div id="child"></div>
</div>
<script>
const parent = document.getElementById("child").parentNode;
console.log(parent);
</script>
c. 子节点
-
获取子节点集合,包括元素节点、文本节点等
<div id="parent"> <div id="child1"></div> <div id="child2"></div> <div id="child3"></div> </div> <script> const children = document.getElementById("parent").childNodes; console.log(children); </script>
-
只获取子节点集合中的元素节点
const children = document.getElementById("parent").childNodes; for (let i = 0; i < children.length; i++) { if (children[i].nodeType === 1) { console.log(children[i]); } }
或
const children = document.getElementById("parent").children; console.log(children);
-
第一个子节点
const first = document.getElementById("parent").firstChild; console.log(first);
第一个元素子节点
-
推荐方法
const first = document.getElementById("parent").children[0]; console.log(first);
-
存在兼容性问题,需要 IE9+
const first = document.getElementById("parent").firstElementChild; console.log(first);
-
-
最后一个子节点
const last = document.getElementById("parent").lastChild; console.log(last);
最后一个元素子节点
-
推荐方法
const parent = document.getElementById("parent"); const last = parent.children[parent.children.length - 1]; console.log(last);
-
存在兼容性问题,需要 IE9+
const last = document.getElementById("parent").lastElementChild; console.log(last);
-
-
举例:导航栏及其下拉菜单
<ul id="nav"> <li> Item 1 <ul style="display: none"> <li>Subitem 1</li> <li>Subitem 2</li> </ul> </li> <li> Item 2 <ul style="display: none"> <li>Subitem 1</li> <li>Subitem 2</li> </ul> </li> </ul> <script> const nav = document.body.children[0]; const items = nav.children; for (let i = 0; i < items.length; i++) { items[i].onmouseover = function () { this.children[0].style.display = "block"; }; items[i].onmouseout = function () { this.children[0].style.display = "none"; }; } </script>
d. 兄弟节点
-
获取当前元素的下一个兄弟节点
<h1>Title</h1> <p>This is a paragraph.</p> <script> const p = document.querySelector("h1").nextSibling; console.log(p); </script>
-
元素节点
const p = document.querySelector("h1").nextElementSibling; console.log(p);
-
-
获取当前元素的上一个兄弟节点
<h1>Title</h1> <p>This is a paragraph.</p> <script> const h1 = document.querySelector("p").previousSibling; console.log(h1); </script>
-
元素节点
const h1 = document.querySelector("p").previousElementSibling; console.log(h1);
-
-
上述获取兄弟元素节点的方法,均存在兼容性问题,需要 IE9+,为解决此问题,可以封装以下方法:
function getNextElementSibling(element) { let next = element.nextSibling; while (next && next.nodeType !== 1) { next = next.nextSibling; } return next; } function getPreviousElementSibling(element) { let prev = element.previousSibling; while (prev && prev.nodeType !== 1) { prev = prev.previousSibling; } return prev; }
e. 创建与添加节点
-
动态创建元素节点:
createElement()
const p = document.createElement("p");
-
添加节点至指定父节点的子节点的末尾:
appendChild()
p.innerText = "This is a paragraph"; // 设置标签内容 document.body.appendChild(p); // 添加节点
-
在指定元素前面插入元素:
insertBefore()
const h1 = document.createElement("h1"); h1.innerText = "Title"; document.body.insertBefore(h1, p);
-
举例:发布留言
<textarea></textarea> <button>发布</button> <ul></ul> <script> const btn = document.querySelector("button"); btn.onclick = function () { const text = document.querySelector("textarea"); if (text) { const ul = document.querySelector("ul"); const li = document.createElement("li"); li.innerHTML = text.value; // ul.appendChild(li); ul.insertBefore(li, ul.firstChild) } else { alert("发布内容不能为空"); } }; </script>
-
直接将内容写入页面的文档流:
write()
<script> document.write("<button>点击</button>"); const btn = document.querySelector("button"); btn.onclick = () => document.write("<p>This is a paragraph.</p>"); </script>
-
write()
、innerHTML()
、createElement()
三种方法区别:write()
在当文档流执行完成后,会导致页面重绘innerHTML()
是将内容写入某个 DOM 节点,适合创建多个元素,结构稍复杂createElement()
结构清晰,效率较低
f. 删除节点
-
删除一个节点:
removeChild()
<p>This is a paragraph.</p> <button>删除节点</button> <script> document.querySelector("button").onclick = () => document.body.removeChild(document.querySelector("p")); </script>
-
举例 1:删除留言(在“发布留言”案例的基础上修改)
const btn = document.querySelector("button"); btn.onclick = function () { const text = document.querySelector("textarea"); if (text) { const ul = document.querySelector("ul"); const li = document.createElement("li"); li.innerHTML = text.value + "<a href='javascript:;'>删除</a>"; ul.insertBefore(li, ul.firstChild); const as = document.querySelectorAll("a"); for (let i = 0; i < as.length; i++) { as[i].onclick = function () { // this.parentNode.remove(); ul.removeChild(this.parentNode); }; } } else { alert("发布内容不能为空"); } };
-
举例 2:动态表格
<table border="1"> <thead> <tr> <th>姓名</th> <th>年龄</th> <th>操作</th> </tr> </thead> <tbody></tbody> </table> <script> let data = [ { name: "张三", age: 24 }, { name: "李四", age: 22 }, { name: "王五", age: 26 }, { name: "赵六", age: 21 }, ]; const tbody = document.querySelector("tbody"); for (let i = 0; i < data.length; i++) { const tr = document.createElement("tr"); for (const key in data[i]) { const td = document.createElement("td"); td.innerText = data[i][key]; tr.appendChild(td); } const td = document.createElement("td"); td.innerHTML = "<a href='javascript:;'>删除</a>"; tr.appendChild(td); tbody.appendChild(tr); } const as = document.querySelectorAll("a"); for (let i = 0; i < data.length; i++) { as[i].onclick = function () { tbody.removeChild(as[i].parentNode.parentNode); }; } </script>
g. 复制节点
-
克隆一个节点:
cloneNode()
<p>This is a paragraph.</p> <script> const p = document.querySelector("p").cloneNode(true); document.body.appendChild(p); </script>
-
其中,
cloneNode()
的参数默认为false
,即浅拷贝,不会拷贝子节点
0x03 事件高级
(1)注册事件
a. 简介
- 注册事件又称绑定事件,给元素添加事件
- 注册事件方式包括:
- 传统方式
- 以
on
为前缀的事件,如onclick
、onchange
等 - 注册事件的唯一性,即同一元素同一事件只能设置一个处理函数,最后注册的处理函数会覆盖前面注册的
- 以
- 方法监听注册方式
- 采用 W3C 标准
- 使用
addEventListener()
监听 - IE9 之前可以使用
attachEvent()
代替 - 同一元素同一事件可以注册多个监听器,并按注册顺序执行
- 传统方式
b. addEventListener
-
将指定的监视器注册到目标对象上,当该对象触发指定事件时,就会执行事件处理函数
-
语法:
addEventListener(type, listener[, useCapture])
-
type
:事件类型字符串,如click
、change
等 -
listener
:事件处理函数,即监听函数 -
useCapture
:(可选)是否在捕获阶段触发,是布尔值,默认false
“事件捕获”在本章节第(3)节说明
<button>点击</button> <script> document.querySelector("button").addEventListener("click", () => { alert("触发点击事件"); }); </script>
-
c. attachEvent
- 将指定的监视器注册到目标对象上,当该对象触发指定事件时,就会执行指定的回调函数
- 语法:
attachEvent(eventNameWiteOn, callback)
eventNameWithOn
:事件类型字符串,如onclick
、onchange
callback
:回调函数,用于事件处理
d. 兼容性解决方案
兼容性处理原则:首先照顾大多数浏览器,再处理特殊浏览器
function addEventListener(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, eventName);
} else if (element.attachEvent) {
element.attachEvent(eventName, callback);
} else {
element["on" + eventName] = callback;
}
}
(2)删除事件
-
删除事件又称解绑事件
-
删除事件方式包括:
-
传统方式
<button>点击</button> <script> document.querySelector("button").onclick = null; </script>
-
方法监听删除方式
-
使用
removeEventListener()
删除<button>点击</button> <script> const btn = document.querySelector("button"); function clickEvent() { alert("触发点击事件"); btn.removeEventListener("click", clickEvent); } btn.addEventListener("click", clickEvent); </script>
-
IE9 之前可以使用
detachEvent()
代替
-
-
-
兼容性解决方案
function removeEventListener(element, eventName, callback) { if (element.removeEventListener) { element.removeEventListener(eventName, callback); } else if (element.attachEvent) { element.detachEvent("on" + eventName, callback); } else { element["on" + eventName] = null; } }
(3)DOM 事件流
-
事件流描述的是从页面中接收事件的顺序
-
DOM 事件流:事件发生时会在元素节点之间按照特定顺序的传播过程
-
DOM 事件流分三个阶段:
- 捕获阶段
- 事件捕获:由 DOM 最顶层节点开始,逐级向下传播到最具体的元素接收的过程
- 当前目标阶段
- 冒泡阶段
- 事件冒泡:事件开始时,由最具体的元素接收,之后逐级向上传播到 DOM 最顶层节点的过程
- 捕获阶段
-
举例:注册事件采取捕获阶段触发,先父元素节点,后子元素节点
<div id="parent" style="width: 200px; height: 200px; background: red"> <div id="child" style="width: 100px; height: 100px; background: green"></div> </div> <script> document.getElementById("parent").addEventListener( "click", () => { alert("parent"); }, true ); document.getElementById("child").addEventListener( "click", () => { alert("child"); }, true ); </script>
-
部分事件没有冒泡,如
onblur
等
(4)事件对象
-
事件对象代表事件的状态,是事件一系列相关数据的集合,包括鼠标坐标等数据
-
在以下代码中,形参
event
就是事件对象<button>点击</button> <script> document.querySelector("button").onclick = function (event) { console.log(event); }; </script>
-
IE6~8 使用
window.event
写法,兼容性写法为:e = event || window.event;
-
常见属性和方法
属性方法 说明 target
返回触发事件的对象 srcElement
返回触发事件的对象(在 IE6~8 中使用) type
返回事件类型,如 click 等 stopPropagation()
阻止冒泡 cancelBubble
阻止冒泡(在 IE6~8 中使用) preventDefault()
阻止默认事件 returnValue
阻止默认事件(在 IE6~8 中使用) -
阻止事件冒泡的兼容性写法
if (e & e.stopPropagation) { e.stopPropagation(); } else { window.event.cancelBubble = true; }
(5)事件委托
-
事件委托又称事件代理,在 jQuery 中称为事件委派
-
原理:将每个子节点的事件监听器统一设置在其父节点上,利用冒泡原理影响每个子节点
-
作用:提高性能
-
举例:
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> <li>Item 4</li> <li>Item 5</li> <li>Item 6</li> </ul> <script> document.querySelector("ul").addEventListener("mouseover", function (e) { e.target.style.backgroundColor = "red"; }); </script>
(6)常用鼠标事件
-
禁用右键菜单
document.addEventListener("contextmenu", function (e) { e.preventDefault(); });
-
禁止鼠标选中
document.addEventListener("selectstart", function (e) { e.preventDefault(); });
-
举例:跟随鼠标移动的方框
<div style=" width: 100px; height: 100px; background-color: red; position: absolute; transform: translate(-50%, -50%); " ></div> <script> const div = document.querySelector("div"); document.addEventListener("mousemove", (e) => { div.style.left = e.pageX + "px"; div.style.top = e.pageY + "px"; }); </script>
-
mouseenter
与mouseover
的区别在于:mouseenter
不会冒泡mouseenter
只在鼠标经过盒子自身时触发,而mouseover
经过其子盒子也会触发
<div style=" width: 200px; height: 200px; background-color: red; position: relative " > <div style=" width: 100px; height: 100px; background-color: green; position: absolute; top: 50px; left: 50px; " ></div> </div> <script> const parent = document.querySelector("div"); const child = document.querySelector("div div"); parent.addEventListener("mouseenter", function () { console.log("mouseenter"); }); parent.addEventListener("mouseover", function () { console.log("mouseover"); }); </script>
(7)常用键盘事件
-
常用键盘事件
事件 说明 keyup 按键松开时触发 keydown 按键按下时触发 keypress 按键按下时触发(区别大小写,不识别功能键,如 Ctrl 等) keyCode 返回按键的 ASCII 值(区分大小写) -
按键执行顺序:
graph LR keydown-->keypress-->keyup -
举例:按
/
键选中输入框<input /> <script> const input = document.querySelector("input"); document.addEventListener("keyup", function (e) { if (e.keyCode === 191) { input.focus(); } }); </script>
0x04 BOM
(1)简介
-
浏览器对象模型(Browser Object Model)提供了独立于内容而与浏览器窗口进行交互的对象
-
BOM 由一系列相关对象构成
-
BOM 缺乏标准,最初是 Netscape 浏览器标准的一部分
-
与 DOM 对比:
DOM BOM 名称 文档对象模型 浏览器对象模型 对象 文档 浏览器 顶级对象 document
window
功能 操作页面元素 浏览器窗口交互 标准 W3C 根据浏览器厂商 -
BOM 构成:
graph TB window-->document & location & navigation & screen & history- document:BOM 中包含 DOM
- window:是浏览器的顶级对象,既是 JS 访问浏览器窗口的接口,也是全局对象
- 调用 window 对象方法时可以省略,如
alert()
,除了window.name
- 调用 window 对象方法时可以省略,如
(2)window 对象常见事件
-
窗口加载完成事件
window.onload = function () {}; // 或 window.addEventListener("load", function () {});
-
DOM 加载完成事件(需要 IE9+)
window.addEventListener("DOMContentLoaded", function () {});
-
-
窗口大小调整事件
window.onresize = function () {}; // 或 window.addEventListener("resize", function () {});
(3)定时器
a. timeout
-
设置计时器
-
语法:
setTimeout(callback[, timeMS])
-
作用:经过指定毫秒后执行回调函数
-
-
清除定时器
- 语法:
clearTimeout(timer)
- 作用:清除
setTimeout()
设置的定时器
- 语法:
-
举例:
let timer = setTimeout(function() {}, 1000); clearTimeout(timer);
b. interval
-
设置计时器
-
语法:
setInterval(callback[, timeMS])
-
作用:间隔指定毫秒后重复执行回调函数
-
举例:倒计时器
<div> <span class="hour">00</span> <span class="minute">00</span> <span class="second">00</span> </div> <script> const hour = document.querySelector(".hour"); const minute = document.querySelector(".minute"); const second = document.querySelector(".second"); function countDown() { const now = new Date(); console.log(now); const target = new Date(2024, 1, 1, 0, 0, 0); const diff = target - now; hour.innerHTML = Math.floor(diff / 1000 / 60 / 60); minute.innerHTML = Math.floor((diff / 1000 / 60) % 60); second.innerHTML = Math.floor((diff / 1000) % 60); } setInterval(countDown, 500); </script>
-
-
清除定时器
- 语法:
clearInterval(timer)
- 作用:清除
setInterval()
设置的定时器
- 语法:
-
举例:发送短信间隔
手机号: <input type="number" /><button>获取验证码</button> <script> document.querySelector("button").addEventListener("click", callback); function callback() { let phone = document.querySelector("input").value; if (!/^1[3-9]\d{9}$/.test(phone)) { alert("手机号格式不正确"); return; } this.disabled = true; let time = 10; let timer = setInterval(() => { this.innerHTML = `重新发送(${time})`; time--; if (time === 0) { clearInterval(timer); this.innerHTML = "获取验证码"; this.disabled = false; time = 10; } }, 1000); } </script>
(4)执行队列
a. 执行过程
-
JavaScript 的特点是单线程,即同一时间只做一件事
- 缺点:当 JS 执行时间过长,会导致页面渲染不连贯,产生阻塞的感受
-
HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,即同步与异步
- 同步:顺序依次执行
- 异步:多任务同时执行
-
同步任务都在主线程上执行,形成执行栈
-
异步任务通过回调函数实现,分为以下类型:
- 普通事件:如
click
等 - 资源加载:如
load
等 - 定时器
异步任务相关回调函数会被添加到任务队列(又称消息队列)中
- 普通事件:如
b. 执行机制
- JavaScript 执行机制如下:
- 先执行执行栈中的同步任务
- 将异步任务放入任务队列
- 完成执行栈后,按次序读取任务队列,将异步任务放入执行栈并执行
- 由于主线程不断地重复获取任务、执行任务,因此该机制称为事件循环
(5)location 对象
-
用于获取或设置窗体的 URL,并且可以用于解析 URL
-
属性:
属性 说明 href
获取或设置 URL host
返回主机(域名) port
返回端口号 pathname
返回路径 search
返回参数 hash
返回片段 -
举例:获取 URL 中携带的参数
<div></div> <script> const params = location.search.substr(1); const array = params.split("="); document.querySelector("div").innerHTML = array[1]; </script>
-
-
常用方法:
方法 说明 assign()
重定向页面,与属性 href 类似,可以跳转页面 replace()
替换当前页面,替换后不能后退 reload()
重新加载页面,相当于刷新或 F5
参数为true
表示强制刷新或 Ctrl+F5
(6)navigator 对象
-
包含有关浏览器的信息
-
举例:根据终端(浏览器)的不同跳转相应的页面
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|iPad|ios|Android|BlackBerry|IEMobile|Opera Mini|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) { window.location.href = "https://m.example.com/"; // Mobile } else { window.location.href = "https://www.example.com/"; // PC }
(7)history 对象
-
用于与浏览器历史记录进行交互,包含用户(在浏览器窗口中)访问过的 URL
-
方法:
方法 说明 back()
后退 forward()
前进 go(arg)
参数为正数:前进 arg
个页面
参数为负数:后退arg
个页面 -
常见于 OA 办公系统
0x05 PC 端网页特效
(1)元素偏移量 offset
a. 简介
-
动态获取元素的位置(偏移)、大小等
- 获取元素距离带有定位父元素的位置
- 获取元素自身的大小(宽高)
- 获取的数值不携带单位
-
常用属性:
属性 说明 offsetParent
返回作为该元素带有定位的父级元素 offsetTop
返回元素相对带有定位父元素上方的偏移 offsetLeft
返回元素相对带有定位父元素左边框的偏移 offsetWidth
返回自身包括内边距、边框、内容的宽度 offsetHeight
返回自身包括内边距、边框、内容的高度 -
相比之下,
offset
更适合获取元素的大小与位置,而style
更适合修改元素
b. 获取鼠标在盒子中的坐标
<div style="width: 200px; height: 200px; background-color: red"></div>
<script>
document.querySelector("div").addEventListener("mousemove", function (e) {
let x = e.pageX - this.offsetLeft;
let y = e.pageY - this.offsetTop;
this.innerText = `x: ${x}, y: ${y}`;
});
</script>
c. 拖动模态框
-
HTML
<button id="open">点击登录</button> <div id="mask"></div> <div id="card"> <h3 id="header">登录框标题</h3> <button id="close">关闭</button> </div>
-
CSS
#mask { width: 100%; height: 100%; position: fixed; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.3); display: none; } #card { width: 400px; height: 300px; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10; background-color: white; border-radius: 30px; box-shadow: 0 0 10px rgb(0, 0, 0); text-align: center; line-height: 40px; display: none; } #header { width: 100%; margin-top: 0; border-bottom: 2px solid rgba(0, 0, 0, 0.3); cursor: move; }
-
JavaScript
const mask = document.querySelector("#mask"); const card = document.querySelector("#card"); const header = document.querySelector("#header"); document.querySelector("#open").addEventListener("click", () => { mask.style.display = "block"; card.style.display = "block"; }); document.querySelector("#close").addEventListener("click", () => { mask.style.display = "none"; card.style.display = "none"; }); header.addEventListener("mousedown", function (e) { e.preventDefault(); let x = e.clientX - card.offsetLeft; let y = e.clientY - card.offsetTop; function move(e) { card.style.left = e.pageX - x + "px"; card.style.top = e.pageY - y + "px"; } document.addEventListener("mousemove", move); document.addEventListener("mouseup", function (e) { document.removeEventListener("mousemove", move); }); });
d. 图片放大镜
-
HTML
<div id="image"> <div id="mask"></div> <div id="preview"> <img src="./image.jpg" alt="" id="previewImg" /> </div> </div>
-
CSS
#image { position: relative; width: 200px; height: 200px; background-image: url("./image.jpg"); background-size: cover; border: 1px solid black; } #mask { position: absolute; top: 0; left: 0; width: 100px; height: 100px; background-color: red; opacity: 0.5; cursor: move; display: none; } #preview { position: absolute; top: 0; left: 210px; width: 300px; height: 300px; overflow: hidden; z-index: 10; border: 1px solid black; display: none; } #previewImg { position: absolute; top: 0; left: 0; }
-
JavaScript
const image = document.querySelector("#image"); const mask = document.querySelector("#mask"); image.addEventListener("mouseover", () => { mask.style.display = "block"; preview.style.display = "block"; }); image.addEventListener("mouseout", () => { mask.style.display = "none"; preview.style.display = "none"; }); image.addEventListener("mousemove", function (e) { let x = e.pageX - this.offsetLeft; let y = e.pageY - this.offsetTop; let maskX = x - mask.offsetWidth / 2; let maskY = y - mask.offsetHeight / 2; let maskMaxX = this.offsetWidth - mask.offsetWidth; let maskMaxY = this.offsetHeight - mask.offsetHeight; if (maskX < 0) { maskX = 0; } else if (maskX > maskMaxX) { maskX = this.offsetWidth - mask.offsetWidth; } if (maskY < 0) { maskY = 0; } else if (maskY > maskMaxY) { maskY = this.offsetHeight - mask.offsetHeight; } mask.style.left = maskX + "px"; mask.style.top = maskY + "px"; const preview = document.querySelector("#preview"); const previewImg = document.querySelector("#previewImg"); let previewMaxX = previewImg.offsetWidth - preview.offsetWidth; let previewMaxY = previewImg.offsetHeight - preview.offsetHeight; previewImg.style.left = -((maskX * previewMaxX) / maskMaxX) + "px"; previewImg.style.top = -((maskY * previewMaxY) / maskMaxY) + "px"; });
(2)元素可视区 client
-
获取元素可视区相关信息,如边框大小、元素大小等
-
常见属性:
属性 说明 clientTop
返回元素上边框的大小 clientLeft
返回元素左边框的大小 clientWidth
返回自身包括内边距、内容的宽度 clientHeight
返回自身包括内边距、内容的高度
立即执行函数:
(function() {})()
主要作用:创建一个独立的作用域
(3)元素滚动 scroll
-
获取元素的大小、滚动距离等
-
如果浏览器的高度(宽度)不足以显示全部内容,则会自动出现滚动条
- 当滚动条向下(向右)滚动后,未显示在当前窗口的页面称为被卷去的页面
- 滚动条在滚动时会触发
onscroll
事件
-
常见属性:
属性 说明 scrollTop
返回被卷去的上侧距离 scrollLeft
返回被卷去的左侧距离 scrollWidth
返回自身内容的宽度 scrollHeight
返回自身内容的高度 -
被卷去的头部兼容性写法
function getScroll() { return { left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0, top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0, }; }
-
举例:侧边栏
-
HTML
<header></header> <div id="content"></div> <div id="bar"> <button>回到顶部</button> </div>
-
CSS
header { width: 90%; height: 200px; background-color: #ccc; margin: 10px auto; } #content { width: 90%; height: 1000px; background-color: #eee; margin: 10px auto; } #bar { position: absolute; top: 300px; right: 0; width: 30px; height: 150px; background-color: #ddd; } button { display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
-
JavaScript
const bar = document.querySelector("#bar"); const btn = document.querySelector("button"); let ctnTop = document.querySelector("#content").offsetTop; let barTop = bar.offsetTop - ctnTop; document.addEventListener("scroll", () => { if (window.scrollY > ctnTop) { bar.style.position = "fixed"; bar.style.top = barTop + "px"; btn.style.display = "block"; } else { bar.style.position = "absolute"; bar.style.top = "300px"; btn.style.display = "none"; } }); btn.addEventListener("click", () => { window.scrollTo({ top: 0, behavior: "smooth", }); });
-
(4)动画函数封装
-
原理:通过定时器
setInterval()
不断移动盒子的位置- 盒子需要定位才能使用动画
<button>开始</button> <div style=" width: 200px; height: 200px; background-color: red; position: absolute; left: 0; " ></div> <script> document.querySelector("button").addEventListener("click", () => { const div = document.querySelector("div"); let timer = setInterval(() => { if (div.offsetLeft >= 400) { clearInterval(timer); } div.style.left = div.offsetLeft + 1 + "px"; }, 25); }); </script>
-
动画函数的简单封装需要传递两个参数:动画对象、移动距离
function animate(obj, target) { clearInterval(obj.timer); obj.timer = setInterval(() => { if (obj.offsetLeft >= target) { clearInterval(obj.timer); } obj.style.left = obj.offsetLeft + 1 + "px"; }, 25); } document.querySelector("button").addEventListener("click", () => { animate(document.querySelector("div"), 500); });
-
缓动动画是让元素的运动速度发生变化(即,将移动距离递减)
- 步长需要取整
- 正步长向上取整,负步长向下取整
function animate(obj, target) { clearInterval(obj.timer); obj.timer = setInterval(() => { if (obj.offsetLeft === target) { clearInterval(obj.timer); } let step = (target - obj.offsetLeft) / 50; step = step > 0 ? Math.ceil(step) : Math.floor(step); obj.style.left = obj.offsetLeft + step + "px"; }, 25); }
-
在动画函数中引入回调函数
function animate(obj, target, callback) { clearInterval(obj.timer); obj.timer = setInterval(() => { if (obj.offsetLeft === target) { clearInterval(obj.timer); if (callback) callback(); } let step = (target - obj.offsetLeft) / 50; step = step > 0 ? Math.ceil(step) : Math.floor(step); obj.style.left = obj.offsetLeft + step + "px"; }, 25); } document.querySelector("button").addEventListener("click", () => { animate(document.querySelector("div"), 500, () => { alert("移动结束"); }); });
-
将动画函数封装到单独的 JavaScript 文件中
-
创建 animate.js
/** * 实现元素的平滑移动动画。 * @param {Object} obj - 需要进行动画的DOM对象。 * @param {number} target - 目标位置,即元素移动到的左偏移量。 * @param {Function} callback - 动画完成后的回调函数。 */ function animate(obj, target, callback) { // 停止当前正在进行的动画 clearInterval(obj.timer); // 设置定时器,每25毫秒执行一次动画逻辑 obj.timer = setInterval(() => { // 当元素移动到目标位置时,停止动画 if (obj.offsetLeft === target) { clearInterval(obj.timer); // 如果设置了回调函数,则动画完成后执行回调 if (callback) callback(); } // 计算每次移动的距离,平滑移动 let step = (target - obj.offsetLeft) / 50; // 确保移动方向正确,且移动步长为整数 step = step > 0 ? Math.ceil(step) : Math.floor(step); // 更新元素的左偏移量 obj.style.left = obj.offsetLeft + step + "px"; }, 25); }
-
在页面中使用
<script src="./animate.js"></script> <script> document.querySelector("button").addEventListener("click", () => { animate(document.querySelector("div"), 500, () => { alert("移动结束"); }); }); </script>
-
0x06 本地存储
- 本地存储特性:
- 数据存储在用户浏览器中
- 存储与读写方便
- 容量大(
sessionStorage
- 5M,localStorage
- 20M) - 只能存储字符串,可以将对象使用
JSON.stringify()
编码后存储
(1)sessionStorage
-
当浏览器窗口关闭后,
sessionStorage
生命周期终止 -
在同一窗口(页面)下,数据可以共享
-
以键值对的形式存储和使用
-
举例:
<input type="text" /> <button id="save">存储</button> <button id="read">读取</button> <button id="delete">删除</button> <button id="clear">清空</button> <script> let input = document.querySelector("input"); document.querySelector("#save").addEventListener("click", () => { sessionStorage.setItem("value", input.value); alert("存储成功"); }); document.querySelector("#read").addEventListener("click", () => { let value = sessionStorage.getItem("value"); alert(`读取内容: ${value}`); }); document.querySelector("#delete").addEventListener("click", () => { sessionStorage.removeItem("value"); alert("删除成功"); }); document.querySelector("#clear").addEventListener("click", () => { sessionStorage.clear(); alert("清空成功"); }); </script>
(2)localStorage
-
生命周期永久有效,只能手动删除
-
在同一浏览器下,数据可以共享
-
以键值对的形式存储和使用
-
举例:
<input type="text" /> <button id="save">存储</button> <button id="read">读取</button> <button id="delete">删除</button> <button id="clear">清空</button> <script> let input = document.querySelector("input"); document.querySelector("#save").addEventListener("click", () => { localStorage.setItem("value", input.value); alert("存储成功"); }); document.querySelector("#read").addEventListener("click", () => { value = localStorage.getItem("value"); alert(`读取内容: ${value}`); }); document.querySelector("#delete").addEventListener("click", () => { localStorage.removeItem("value"); alert("删除成功"); }); document.querySelector("#clear").addEventListener("click", () => { localStorage.clear(); alert("清空成功"); }); </script>
(3)记住用户名
<input type="text" id="username" />
<input type="checkbox" id="rememberMe" />
<label for="rememberMe">记住我</label>
<script>
const username = document.querySelector("#username");
const rememberMe = document.querySelector("#rememberMe");
if (localStorage.getItem("username")) {
username.value = localStorage.getItem("username");
rememberMe.checked = true;
}
rememberMe.addEventListener("change", function () {
if (this.checked) {
localStorage.setItem("username", username.value);
} else {
localStorage.removeItem("username");
}
});
</script>