01-DOM介绍和元素节点操作
hs 7/19/2022
# 1. DOM介绍
# 1.1 认识DOM
JavaScript是一门编程语言,它的运行环境有两个,一个是浏览器,一个是node,前面我们学的JS必于ECMAScript中的语法,浏览器的JS有三部分组成:
- ECMAScript
- DOM
- BOM
ECMAScript并不能和和网页进行交互,操作浏览器(网页),就需要给我们提供一个API,我们去调用API,实现操作,学习DOM和BOM就是学习浏览器给我们提供的API,所以说,DOM和BOM操作,也叫WebAPI。
深入理解DOM
- 浏览器将我们编写在HTML中的每一个元素(Element)都抽象成了一个个对象
- 所有这些对象都可以通过JavaScript来对其进行访问,那么我们就可以通过JavaScript来操作页面
- 将这个抽象过程称之为 文档对象模型(Document Object Model)
DOM:Docuemnt Object Model 文档对象模型
- 文档:html文档 之前的html文件就可以称一个文档
- Object: 对象 一切都是对象 所有的元素都是对象
- Model:模型 树模型 所有的元素,要形成一个树模型
整个文档被抽象到 document 对象中
- 如document.documentElement对应的是html元素
- 如document.body对应的是body元素
- 如document.head对应的是head元素
<script>
// 下面的一行代码可以让整个页面变成红色
document.body.style.background = "gold";
</script>
DOM树
- 在html结构中,最终会形成一个树结构
# 1.2 重新认识window
前面我们学习了一个window的全局对象,window上事实上就包含了这些内容
- JavaScript语法部分的Object、Array、Date等
- DOM
- BOM
# 2. document对象
Document节点表示的整个载入的网页,它的实例是全局的document对象
- 对DOM的所有操作都是从 document 对象开始的
- 它是DOM的 入口点,可以从document开始去访问任何节点元素
对于最顶层的html、head、body元素,我们可以直接在document对象中获取到
- document.documentElement 得到 html元素
- document.body 得到 body元素
- document.head 得到 head元素
- document.doctype 得到 文档声明元素
<body>
<script>
// document是内置对象 也是GO中的
// 一个节点就是一个对象
// 对象是属性和方法的无序集合 api
// 学习DOM操作就学习一个对象中的属性或方法 api
// console.dir(document);
// 获取head元素节点
// console.log(document.head);
// 获取title元素节点中的文本节点
// console.log(document.title);
// 获取body元素节点
// console.log(document.body);
document.write("<h1>Hello DOM</h1>")
</script>
</body>
# 3. 节点与节点关系
节点分类:
- 元素节点
- 文本节点
- 属性节点
- 注释节点
- ...
注意点:
- 在DOM树上,只需要关注元素节点和文本节点,在DOM上,没有属性节点。
- 我们只需要关注三种节点:1)元素节点 2)文本节点 3)属性节点
<body>
<!-- 我是一个注释 -->
<div id="box" title="haha">我是一个DIV</div>
<a href="http://www.baidu.com">百度一下</a>
<script>
let oDiv = document.getElementById("box");
// nodeType判断一个节点的类型
// 如果是元素节点 它的nodeType是1
console.log(oDiv.nodeType); // 1
// 要获取属性节点,必须先得到元素节点
// 通过打点的形式,就可以获取属性节点
// console.log(oDiv.title); // haha
// getAttribute 根据属性名获取属性值的
console.log(oDiv.getAttribute("title")); // haha
// getAttributeNode 获取属性节点
let attr = oDiv.getAttributeNode("title"); // 2
console.log(attr.nodeType);
let text = oDiv.firstChild;
console.log(text); // 我是一个DIV
console.log(text.nodeType); // 3
</script>
</body>
节点之间的关系一:
- 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系
- 父节点:parentNode
- 前兄弟节点:previousSibling
- 后兄弟节点:nextSibling
- 子节点:childNodes
- 第一个子节点:firstChild
- 最后一个子节点:lastChild
<body>
<!-- 我是一个注释 -->
我是一个文本
<div class="box">
我是一个孤独的DIV
</div>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<!-- <script>
// 获取body的元素节点
let bodyEle = document.body;
console.log(bodyEle.firstChild); // #text 换行节点
console.log(bodyEle.firstChild.nextSibling);
console.log(bodyEle.parentNode);
</script> -->
<script>
// 通过节点关系去获取某些节点,非常麻烦,因为需要考虑换行节点和注释节点
let bodyEle = document.body;
console.log(bodyEle.firstChild.nextSibling.nextSibling.nextSibling);
</script>
</body>
节点之间的关系二:
- 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系
- 父节点:parentElement
- 前兄弟节点:previousElementSibling
- 后兄弟节点:nextElementSibling
- 子节点:children
- 第一个子节点:firstElementChild
- 最后一个子节点:lastElementChild
<body>
<!-- 我是一个注释 -->
我是一个文本
<div class="box">
我是一个孤独的DIV
</div>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<script>
let bodyEle = document.body;
console.log(bodyEle.firstElementChild);
console.log(bodyEle.firstElementChild.nextElementSibling);
// 得到一个伪数组
console.log(bodyEle.firstElementChild.nextElementSibling.children);
</script>
</body>
节点关系总结(加粗的是需要记的):
- parentNode 获取父元素节点 没有兼容性问题
- parentElement 获取父元素节点 没有兼容性问题
- firstChild 获取第一个子节点 会考虑换行节点和注释节点 不要用
- firstElementChild 获取第一个子元素节点 可以使用
- lastChild 获取最后一个子节点 会考虑换行节点和注释节点 不要用
- lastElementChild 获取最后一个子元素节点 可以使用
- nextSibling 获取下一个兄弟节点 会考虑换行节点和注释节点 不要用
- nextElementSibling 获取下一个兄弟元素节点 可以使用
- previousSibling 获取上一个兄弟节点 会考虑换行节点和注释节点 不要用
- previousElementSibling 获取上一个兄弟元素节点 可以使用
# 4. 获取元素节点的方法
通过节点关系可以得到某个元素,但是,在实际开发中,我们希望可以任意的获取到某一个元素应该如何操作呢?
DOM为我们提供了获取元素的方法:
最常用的几个方法如下:
- document.getElementById
- document.getElementsByTagName
- document.querySelectorAll
- document.querySelector
<body>
<div id="box" name="a" class="item">我是一个DIV1</div>
<div id="box" name="a" class="item">我是一个DIV2</div>
<p class="father">
<span class="son">son</span>
</p>
<ul class="wrap">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// 1) ------ document.getElementById 获取1个
// let oDiv = document.getElementById("box")
// console.dir(oDiv);
// 2) ------ document.getElementsByTagName 得到的是伪数组
// 伪数组 本质是对象 Array.from()
// 是文档中所有的div 要获取某个div,需要通过索引
// let oDivs = document.getElementsByTagName("div")
// console.log(oDivs)
// console.log(oDivs[0])
// 3) ------ document.getElementsByName 得到的是伪数组
// 是文档中所有的div 要获取某个div,需要通过索引
// let oDivs = document.getElementsByName("a")
// console.log(oDivs)
// console.log(oDivs[0])
// 4) ------ document.getElementsByClassName 得到的是伪数组
// let oDivs = document.getElementsByClassName("item")
// console.log(oDivs)
// console.log(oDivs[0])
// 5) ------ document.querySelectorAll 得到的是伪数组
// querySelector是选择器的意思 通过选择器来获取元素
// querySelectorAll 获取多个
// let lis = document.querySelectorAll("li")
// console.log(Array.isArray(lis)); // 得到的也是一个伪数组
// console.log(lis[0]);
// 6) ------ document.querySelector 得到的是伪数组
// querySelector 获取第1个
// let li = document.querySelector("li")
// console.log(li);
</script>
</body>
# 5. 节点的属性
# 5.1 节点的属性之nodeType
不同的节点类型有可能有不同的属性,但它们有共有属性:
- nodeType 属性提供了一种获取节点类型的方法
- 其他类型可以查看MDN文档: developer.mozilla.org/zh-CN/docs/…
常见的节点类型有如下:
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
console.log(commentNode.nodeType);
console.log(textNode.nodeType);
console.log(divNode.nodeType);
for(let node of bodyChildNodes){
if(node.nodeType === 8){
console.log(node + "是注释节点");
}else if(node.nodeType === 3){
console.log(node + "是文本节点");
}else if(node.nodeType === 1){
console.log(node + "是元素节点");
}
}
</script>
</body>
# 5.2 节点的属性之nodeName
- nodeName:获取node节点的名字;
- tagName:获取元素的标签名词;
tagName 和 nodeName 之间有什么不同呢?
- tagName 属性仅适用于 Element 节点;
- nodeName 是为任意 Node 定义的
- nodeName 对于元素,它的意义与 tagName 相同,所以使用哪一个都是可以的;
- nodeName 对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 得到节点名
console.log(commentNode.nodeName); // #comment
console.log(textNode.nodeName); // #text
console.log(divNode.nodeName); // #DIV
console.log("-------------");
console.log(commentNode.tagName); // undefined
console.log(textNode.tagName); // undefined
console.log(divNode.tagName); // #DIV
</script>
</body>
# 5.3 节点的属性之innerHTML和textContent
innerHTML 属性
- 将元素中的 HTML 获取为字符串形式
- 设置元素中的内容;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 获取div标签中的内容
console.log(divNode.innerHTML);
// 设置div标签中的内容
divNode.innerHTML = "<strong>我是一个Strong标签</strong>"
</script>
</body>
outerHTML 属性
- 包含了元素的完整 HTML
- innerHTML 加上元素本身一样;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let divNode = bodyChildNodes[3];
// 获取div标签中的内容 outerHTML相比innerHTML来说,带上自己本身
console.log(divNode.outerHTML);
// 对于设置来说,使用innerHTML多一点
divNode.outerHTML = "<strong>我是一个Strong标签</strong>"
</script>
</body>
textContent 属性
- 仅仅获取元素中的文本内容;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let divNode = bodyChildNodes[3];
// 只能获取文本节点
console.log(divNode.textContent);
// 设置也是针对文本节点,如果写了标签,也不会解析
divNode.textContent = "<strong>我是一个Strong标签</strong>";
</script>
</body>
innerHTML和textContent的区别:
- 使用 innerHTML,我们将其“作为 HTML”插入,带有所有 HTML 标签。
- 使用 textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理。
# 5.4 节点的属性之nodeValue
- 用于获取非元素节点的文本内容
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 获取注释节点中的内容
console.log(commentNode.nodeValue);
// 获取文本节点中的内容
console.log(textNode.nodeValue);
// 元素节点的nodeValue是null
console.log(divNode.nodeValue); // null
</script>
</body>
# 5.5 节点的属性之hidden
- 用于设置元素隐藏
<body>
<button id="btn">Toggle</button>
<div class="box" style="color: red;">
Hello DOM~
</div>
<script>
let btn = document.getElementById("btn");
let div = document.getElementsByTagName("div")[0];
// console.log("start...");
// 给btn绑定点击事件 btn叫事件源 click叫点击事件(事件类型)
// function(){} 事件处理程序,也叫监听器
// 事件绑定是异步任务,是宏任务
btn.onclick = function(){
// console.log("click...");
// 隐藏div
// div.style.display = "none"; // 隐藏方式一
// div.hidden = true; // 隐藏方式二
// if(div.hidden === false){
// div.hidden = true;
// }else{
// div.hidden = false;
// }
// 经典
div.hidden = !div.hidden
}
// console.log("end...");
</script>
</body>
# 6. 创建和挂载节点
我们想要插入一个元素,通常会按照如下步骤:
- 步骤一:创建一个元素;
- 步骤二:插入元素到DOM的某一个位置;
创建节点:
- createElement 创建元素节点
- createTextNode 创建文本节点
- createComment 创建注释节点
- createAttribute 创建属性节点
挂载节点:
- node.append(...nodes or strings) —— 在 node 末尾 插入节点或字符串,
- node.prepend(...nodes or strings) —— 在 node 开头 插入节点或字符串,
- node.before(...nodes or strings) —— 在 node 前面 插入节点或字符串,
- node.after(...nodes or strings) —— 在 node 后面 插入节点或字符串,
- node.replaceWith(...nodes or strings) —— 将 node 替换为给定的节点或字符串。
<body>
<div class="father">
<div class="son">
SON
</div>
</div>
<script>
let father = document.querySelector(".father");
// 使用innerHTML也可以添加元素,不推荐
// father.innerHTML = "<h2>我是一个H2标签</h2>";
let h2Ele = document.createElement("h2");
h2Ele.textContent = "我是H2标签";
console.log(h2Ele);
// 需要把h2挂载到DOM树上
father.append(h2Ele);
// father.prepend(h2Ele)
// father.before(h2Ele)
// father.after(h2Ele)
// father.replaceWith(h2Ele)
let spanEle = document.createElement("span");
spanEle.textContent = "我是一个孤独的span";
h2Ele.append(spanEle)
</script>
</body>
# 7. 删除,替换,克隆节点
删除节点:
- removeChild 只有父元素才有资格删除一个子元素
- remove 移除元素我们可以调用元素本身的remove方法:
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
// son.remove, 自己移除自己
// son.remove();
// 父也有能力去移除它里面的元素
father.removeChild(son)
</script>
</body>
替换节点:
- replaceChild
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
let pEle = document.createElement("p");
pEle.textContent = "GrandSon";
father.replaceChild(pEle,son)
</script>
</body>
如果我们想要复制一个现有的元素,可以通过cloneNode方法克隆节点:
- 可以传入一个Boolean类型的值,来决定是否是深度克隆;
- 深度克隆会克隆对应元素的子元素,否则不会;
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
// 默认是浅copy 只copy一个节点,内部的其它节点不会copy
// let newFather = father.cloneNode();
// console.log(newFather);
// 如果传递一个true表示深copy 如果不传或传一个false表示浅copy
let newFather = father.cloneNode(true);
console.log(newFather);
</script>
</body>
# 02-属性节点操作
*hs* *7/19/2022*
## [#](https://tubie.gitee.io/fe-blog/basic/dom/02.html#_1-元素的属性之attribute) 1. 元素的属性之attribute
**一个元素除了有开始标签、结束标签、内容之外,还有很多的属性(attribute)**

**浏览器在解析HTML元素时,会将对应的attribute也创建出来放到对应的元素对象上。**
- 比如id、class就是全局的attribute,会有对应的id、class属性;
- 比如href属性是针对a元素的,type、value属性是针对input元素的;
**attribute的分类**
- 标准的attribute:某些attribute属性是标准的,比如id、class、href、type、value等;
- 非标准的attribute:某些attribute属性是自定义的,比如abc、age、height等;
## [#](https://tubie.gitee.io/fe-blog/basic/dom/02.html#_2-attribute的操作) 2. attribute的操作
**对于所有的attribute访问都支持如下的方法:**
- elem.hasAttribute(name) — 检查特性是否存在。
- elem.getAttribute(name) — 获取这个特性值。
- elem.setAttribute(name, value) — 设置这个特性值。
- elem.removeAttribute(name) — 移除这个特性。
- attributes:attr对象的集合,具有name、value属性;
**attribute具备以下特征:**
- 它们的名字是大小写不敏感的(id 与 ID 相同)。
- 它们的值总是字符串类型的。
<script>
let boxEle = document.querySelector("#box");
// 可以判断标准的、也可以判断非标准的
// console.log(boxEle.hasAttribute("class"));
// console.log(boxEle.hasAttribute("title"));
// console.log(boxEle.hasAttribute("name"));
// console.log(boxEle.hasAttribute("score"));
// 通过Attribute的属性名,获取属性值
// console.log(boxEle.getAttribute("class"));
// 设置Attribute
// boxEle.setAttribute("adress","bj")
// 删除Attribute
// boxEle.removeAttribute("title")
// 得到所有的Attribute
console.log(boxEle.attributes);
</script>
```
# 3. 元素的属性之property(打点形式)
对于标准的attribute,会在DOM对象上创建与其对应的property属性:
在大多数情况下,它们是相互作用的
- 改变property,通过attribute获取的值,会随着改变;
- 通过attribute操作修改,property的值会随着改变;(但是input的value修改只能通过attribute的方法)
除非特别情况,大多数情况下,设置、获取attribute,推荐使用property的方式
- 因为它默认情况下是有类型的;
<body>
<div class="father" title="hehe" score="88" id="box">
<div class="son" abc="abc" age="18">
son
</div>
</div>
<input type="text" class="ipt" value="我是默认值">
<hr>
<!-- checked="checked" -->
<input type="checkbox" class="ipt2">
<script>
let boxEle = document.querySelector("#box");
let iptEle = document.querySelector(".ipt")
let iptEle2 = document.querySelector(".ipt2")
// obj是对象 对象是属性(property)的无序集合
// 所谓的property就是对象的中的属性
// let obj = {
// name:"wc",
// age:18
// }
// boxEle.setAttribute("title","xixi"); // 改变了attribute
// console.log(boxEle.title);
// boxEle.title = "heihei"; // 改变了propety
// console.log(boxEle.getAttribute("title"));
// iptEle.setAttribute("value","lala")
// iptEle.value = "hehe"
// 建议使用property
console.log(iptEle2.checked);
console.log(iptEle2.getAttribute("checked"));
</script>
</body>
<body>
<div class="father" id="abc" title="hehe" age="18" score="88">
我是一个DVI
</div>
<!-- <input type="checkbox" checked="checked"> -->
<input type="checkbox" checked>
<hr>
<input type="text" class="account">
<button class="btn">设置input的值</button>
<script>
let divEle = document.getElementById("abc");
console.log(divEle.getAttribute("id"));
console.log(divEle.getAttribute("age"));
console.log("-----------------");
console.log(divEle.id);
// 对于非标准的attribute 是没有 对应的property
console.log(divEle.age); // undefined
console.log("-------------------");
let iptEle = document.getElementsByTagName("input")[0];
// if(iptEle.getAttribute("checked")){
// console.log("处于选中状态");
// }
if(iptEle.checked){
console.log("处于选中状态");
}
console.log(typeof iptEle.checked);
console.log("-------------------");
let iptEle2 = document.getElementsByTagName("input")[1];
let btnEle = document.getElementsByClassName("btn")[0];
btnEle.onclick = function(){
// 通过property设置数据时,优先级是高于attribute
iptEle2.value = "66666"; // 通过property来设置
iptEle2.setAttribute("value",88888); // 通过attribute来设置
}
</script>
</body>
# 4. HTML5的data-*自定义属性
*HTML5的data-自定义属性,它们可以在dataset属性中获取到的
<body>
<!-- 以data-打头的,叫html5中的自定义属性 -->
<div class="father" age="18" data-score="88" data-address="bj">
我是一个孤独的DIV
</div>
<script>
let boxEle = document.querySelector(".father");
console.log(boxEle.dataset);
console.log(boxEle.dataset.score);
console.log(boxEle.dataset.address);
</script>
</body>
03-动态修改样式
hs 7/19/2022
# 1. 动态操作样式
JavaScript动态修改样式,有两个选择:
- 选择一:在CSS中编写好对应的样式,动态的添加class;
- 选择二:动态的修改style属性;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active{
color: red;
font-size: 30px;
background-color: gold;
}
</style>
</head>
<body>
<div class="box">
我是一个box
</div>
<script>
let boxEle = document.querySelector(".box");
boxEle.onclick = function(){
// 1)操作行内样式
// boxEle.style.color = "red";
// boxEle.style.fontSize = "30px";
// boxEle.style.backgroundColor = "gold";
// 2)操作class类
// class的attribute 对应的 property是 className
boxEle.className = "active"
}
</script>
</body>
</html>
# 1.1 元素的className和classList
元素的class attribute,对应的property并非叫class,而是className:
- 因为JavaScript早期是不允许使用class这种关键字来作为对象的属性,所以DOM规范使用了className
- 可以对className进行赋值,它会替换整个类中的字符串
如果我们需要添加或者移除单个的class,那么可以使用classList属性:
- elem.classList.add (class) :添加一个类
- elem.classList.remove(class):添加/移除类
- elem.classList.toggle(class) :如果类不存在就添加类,存在就移除它
- elem.classList.contains(class):检查给定类,返回 true/false
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active{
color: red;
font-size: 30px;
background-color: gold;
}
</style>
</head>
<body>
<div class="box">
我是box
</div>
<button>切换</button>
<script>
let boxEle = document.querySelector(".box");
// 把人家本身的覆盖掉了
// boxEle.className = "active"
// boxEle.classList.add("active")
// boxEle.classList.remove("box")
let btn = document.getElementsByTagName("button")[0];
btn.onclick = function(){
// boxEle.classList.add("active")
// boxEle.classList.toggle("active")
console.log(boxEle.classList.contains("box"));
}
</script>
</body>
</html>
# 1.2 元素的style属性
单独修改某一个CSS属性,那么可以通过style来操作:
- 对于多词(multi-word)属性,使用驼峰式 camelCase
- 多个样式的写法,我们需要使用cssText属性,不推荐这种用法,因为它会替换整个字符串
<body>
<div class="box" style="background-color: gold; color: white;">
我是一个孤独的DIV
</div>
<script>
let boxEle = document.querySelector(".box");
// 获取
console.log(boxEle.style.backgroundColor);
console.log(boxEle.style.color);
// 设置
// boxEle.style.fontSize = "30px"
// boxEle.style.color = "red"
// 使用cssText 了解
boxEle.style.cssText = "background-color: pink; font-size:30px; color:red;";
</script>
</body>
# 1.3 元素style的读取 - getComputedStyle
- 对于内联样式,是可以通过style.*的方式读取到的
- 对于style、css文件中的样式,是读取不到的
- 通过getComputedStyle的全局函数来实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
color: gold;
}
</style>
</head>
<body>
<div class="box" style="background-color: red;">
我是一个孤独的DIV
</div>
<script>
let boxEle = document.querySelector(".box");
console.log(boxEle.style.backgroundColor);
// console.log(boxEle.style.color);
console.log(getComputedStyle(boxEle).color);
</script>
</body>
</html>
04-JS操作盒子模型
hs 7/19/2022
# 1. JS操作盒子模型
js操作盒子模型:
- 操作盒子模型的位置
- 操作盒子模型的大小
不管是操作盒子模型的位置,还是盒子模型的大小,一共有13个api: 说明:需要记的没几个,三类:
- client系列 width height top left
- offset系列 width height top left parent
- scroll系列 width height top left
# 1.1 client家族
client系列:
- clientWidth(可视区域宽度): 获取盒子的内容width+左右的padding
- clientHeight(可视区域高度): 获取盒子的内容height+上下的padding
- clientTop:获取盒子的上边框的粗细,了解
- clientLeft:获取盒子的左边框的粗细,了解
要获取当前页面内容的宽度(可视区域):
- console.log(document.body.clientWidth) // 获取一屏的宽度
- console.log(document.documentElement.clientWidth)
要获取当前页面内容的高度(可视区域):
- console.log(document.body.clientHeight); // 获取内容(多屏)的高度 获取可视区域高度
- console.log(document.documentElement.clientHeight); // 获取一屏的高度
client系列的注意点:
- 不能获取,不能设置,它们是只读的
- 得到的是一个数值,没有单位
- 得到的是一个整数,不是小数,及时测试量出来是小数,也会转化成整数
- 不管你的内容是否溢出,得到的是可视区的大小
# 1.2 offset家族
offset系列:
- offsetWidth: box.offsetWidth 在clientWidth的基础上加了border 不常用
- offsetHeight: box.offsetHeight 在clientHeight的基础上加了border 不常用
- offsetTop: 获取一个绝对定位元素相对于参考点的上面的距离
- offsetLeft: 获取一个绝对定位元素相对于参考点的左面的距离
- offsetParent(***): 获取一个定位元素的参考点
# 1.3 scroll家族
scrollWidth:
- 在没有内容溢出(水平方向上的溢出)的情况下:scrollWidth == clientWidth
- 在内容溢出的情况下:scrollWidth的值约等于真实内容的宽度,此时,clientWidth的值是约等于,不同浏览器中得到的值可能不一样
- overflow属性会影响scrollWidth。 只能获取值,不能设置值
scrollHeight:
- 在没有内容溢出的情况下:scrollHeight = clientHeight
- 在内容溢出的情况下:scrollHeight的值约等于真实内容的高度,不同浏览器中得到的值可能不一样
- overflow属性会影响scrollHeight。
- 只能获取值,不能设置值
scrollTop:
- 获取垂直滚动条滚动的距离(获取垂直滚动条卷去的高度)
- 特殊:不只可以获取,也可以设置(可读可写) 重要
问:如何获取当前页面卷上去高度?
答:1)document.body.scrollTop 2)document.documentElement.scrollTop
问:一张网页卷上去最大高度是多少?
答:scrollHeight - clientHeight
可视区的高度 + 卷上去的高度 约等于 内容的高度
scrollLeft: 没什么用
- 特殊:不只可以获取,也可以设置(可读可写)
- 获取水平滚动条滚动的距离(获取水平滚动条卷去的距离) 基本上用不到
问:我要获取当前页面真实内容的高度。
答:1)document.body.scrollHeight 2)document.documentElement.scrollHeight
# 1.4 总结
client系列: width, height, top, left
- clientWidth/clientHeight: 获取盒子或页面可视区域的宽高
- clientTop:获取盒子的上边框的大小
- clientLeft:获取盒子的左边框的大小
offset系列: width, height, top, left, parent
- offsetWidth/offsetHeight: 获取盒子或页面可视区域的宽高+border
- offsetTop:获取一个定位元素相对于参考点的上面的距离
- offsetLeft:获取一个定位元素相对于参考点的左面的距离
- offsetParent:获取一个定位元素的参考点
scroll系列: width, height, top, left
- scrollWidth/scrollHeight:获取盒子或页面真实内容的宽度
- scrollTop:获取页面或盒子向上卷去的高度
- scrollLeft:获取页面或盒子向左卷去的宽度 基本上用不到
需要记的三个常用高度:
- 获取一张网页可视区的高度:document.body.clientHeight
- 获取一张网页真实的高度:document.body.scrollHeight
- 获取一张网页卷去的高度:document.body.scrollTop
05-事件处理
hs 7/19/2022
# 1. 事件类型
事件 + 事件的反馈 = 前端交互(前端的核心)
鼠标事件:
- onclick 点击事件 = onmousedown + onmouseup
- ondbclick 双击事件
- onmousedown 鼠标按下去
- onmouseup 鼠标抬起来
- onmouseover 鼠标移到某元素上面
- onmouseout 鼠标离开某元素上面
- onmousemove 鼠标在元素上面的移动
- ....
键盘事件:
- onkeydown 键盘按下去
- onkeyup 键盘抬起来
- onkeypress 键盘抬住
事件的执行顺序是 onkeydown、onkeypress、onkeyup
- down事件先发生
- press发生在文本被输入
- up发生在文本输入完成
加载事件:
- load 加载 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源 事件源是window
- unload 加载 退出页面 此事件不好捕获到 也没有什么用
- DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 img 和样式表之类的外部资源可能尚未加载 完成。
表单事件:
- focus: 获取焦点
- blur: 失去焦点
- change: 改变输入框中的内容,并失去焦点时触发
- input:内部发生变化,立即触发,不等到失去焦点
- submit: 当点击了提交按钮,会触发submit事件
- reset: 当点击了重置按钮,会触发reset事件
其它事件:
- resize: 改变了浏览器窗口大小时,会触发resize事件
- scroll: 页面滚动时,会触发scroll事件
- ....
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
// 代码执行是从上到下执行,在执行JS代码时,
// HTML结构还没加载完毕,获取DOM节点,是没有
// 办法获取的。
// 所以我们需要等DOM节点加载完毕,再去获取DOM元素,
// 加载事件 load事件 事件源是window
window.onload = function(){
let btn = document.getElementById("btn");
console.log(btn); // null / button
}
</script>
</head>
<body>
<button id="btn">登录</button>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" id="ipt">
<script>
window.onload = function(){
let ipt = document.getElementById("ipt");
// 表示表单获取焦点时触发
ipt.onfocus = function(){
console.log("表单获取了焦点~");
}
ipt.onblur = function(){
console.log("表单失去了焦点~");
}
// 输入框中的数据发生了变化,并失败了焦点就会触发
ipt.onchange = function(){
console.log("表单中的数据发生变化~");
}
ipt.oninput = function(){
console.log("内容变化了~");
}
}
</script>
</body>
</html>
# 2. 事件的绑定和解绑
# 2.1 HTML级别的事件绑定
可以在HTML开始标签上,去绑定事件,这种绑定基本不用
<body>
<!--
button叫事件源
click叫事件类型 on仅仅是前缀
"alert('点我弄啥~')"事件反馈 事件处理程序 也叫监听器
我们说的事件绑定:指的是绑定监听器
-->
<!-- <button onclick="alert('点我弄啥~')">点我</button> -->
<!-- test() 加了() 这行代码在你眼中就是一个值 -->
<!-- 在test()外面,还包一层函数,这一层函数你看不到 -->
<!-- 只有点击了按钮,外面的函数才会执行,才能执行到里面的test() -->
<!-- ()不加不行 -->
<button onclick="test()">点我</button>
<script>
function test(){
alert("点我弄啥~~")
}
</script>
</body>
# 2.2 DOM0事件绑定
DOM0事件绑定,就是给DOM元素的onxxxx属性赋值,是基于属性赋值的操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// DOM0级别的事件绑定
// 基于属性赋值 如果重复绑定多次,后面的会覆盖前面的
btn.onclick = function(){
// console.log("1");
this.innerHTML = "加载中...."
// this.className = "loading"
this.classList.add("loading")
var self = this;
setTimeout(function(){
// this表示window
// this.innerHTML = "加载更多"
// this.classList.remove("loading")
self.innerHTML = "加载更多"
self.classList.remove("loading")
},2000)
}
// 由于是基于属性赋值,后面赋值的会覆盖掉前面的
// btn.onclick = function(){
// console.log("2");
// }
</script>
</body>
</html>
现在学习了ES6,可以使用箭头函数,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
btn.onclick = function(){
this.innerHTML = "加载中...."
this.classList.add("loading")
setTimeout(()=>{
this.innerHTML = "加载更多"
this.classList.remove("loading")
},2000)
}
</script>
</body>
</html>
如果有HTML级别的事件绑定和DOM0的事件绑定,谁的优先给高,演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn" onclick="test()">加载更多</button>
<script>
function test(){
console.log("html级别的事件绑定");
}
// dom0级别的事件绑定优先级是高于HTML级别的事件绑定的
let btn = document.getElementById("btn");
btn.onclick = function(){
console.log("dom0级别的事件绑定");
}
</script>
</body>
</html>
# 2.3 DOM2的事件绑定
DOM2的事件绑定是基于事件池的,不是基于属性赋值的,所以可以多次绑定,不会覆盖掉,演示代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.loading{
background-color: #ccc;
color: #424242;
outline: 0px;
border: none;
}
</style>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
// 基于事件池 事件绑定都是异步代码 都是宏任务
btn.addEventListener("click",function(){
this.innerHTML = "加载中..."
this.className = "loading"
setTimeout(()=>{
this.innerHTML = "加载更多"
this.className = ""
},2000)
});
btn.addEventListener("click",function(){
console.log("~我是有底线的~");
})
</script>
</body>
</html>
特别需要注意,绑定的是事件处理程序,也就是监听器,也就是事件反馈,如果你的监听器是同一个,即使你绑定了多次,也只会执行一次,演示如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
btn.addEventListener("click",function(){
console.log("111");
});
btn.addEventListener("click",function(){
console.log("222");
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">加载更多</button>
<script>
let btn = document.getElementById("btn");
// 如果这样绑定,事件池中只有一个Fn
btn.addEventListener("click",fn);
btn.addEventListener("click",fn)
function fn(){
console.log("xxx");
}
</script>
</body>
</html>
# 3. 事件解绑
绑定它是绑定的监听器,解绑也是解绑监听器,解绑也分DOM0的解和DOM2的解绑,演示DOM0的解绑代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button{
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got{
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// 事件绑定 绑定的监听器
btn.onclick = function(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// 已领取 需要解绑 解绑的也是监听器
this.onclick = null; // DOM0的解绑
}
</script>
</body>
</html>
下面的DOM2的事件解绑是不OK的,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button {
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got {
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",fn,false)
// DOM2的解绑 解绑的参数需要和绑定的参数一样
// 不行:因为上,刚把fn扔到池中,下面紧接着,把fn从池中扔出来的
btn.removeEventListener("click", fn,false)
function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
}
</script>
</body>
</html>
正确的解绑如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
button {
outline: none;
border: none;
width: 100px;
height: 30px;
background-color: red;
color: #fff;
text-align: center;
line-height: 30px;
}
.got {
background-color: #ddd;
}
</style>
</head>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",fn,false)
function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// DOM2的解绑 解绑的参数需要和绑定的参数一样
btn.removeEventListener("click", fn,false)
}
</script>
</body>
</html>
其它的解绑方案如下:
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",function fn(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
btn.removeEventListener("click", fn,false)
})
</script>
</body>
<body>
<button id="btn">领取优惠劵</button>
<script>
let btn = document.getElementById("btn");
// DOM2的事件绑定
btn.addEventListener("click",function(){
this.innerHTML = "已领取"
this.className = " got"
console.log("已领取~");
// console.log(arguments.callee);
btn.removeEventListener("click", arguments.callee, false)
})
</script>
</body>
# 4. 事件冒泡和事件捕获
- 我们会发现默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序我们称之为事件冒泡(Event Bubble)
- 另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture)
如果我们都监听,那么会按照如下顺序来执行:
- 捕获阶段(Capturing phase):事件(从 Window)向下走近元素
- 目标阶段(Target phase):事件到达目标元素
- 冒泡阶段(Bubbling phase):事件从元素上开始冒泡
开发中通常会使用事件冒泡,所以事件捕获了解即可。
# 5. 事件对象
当一个事件发生时,就会有和这个事件相关的很多信息:
- 比如事件的类型是什么,你点击的是哪一个元素,点击的位置是哪里等等相关的信息
- 那么这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象;
- 该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作;
如何获取这个event对象呢?
- event对象会在传入的事件处理(event handler)函数回调时,被系统传入;
- 我们可以在回调函数中拿到这个event对象;
常见的属性:
- type:事件的类型;
- target:当前事件发生的元素;
- currentTarget:当前处理事件的元素;
- eventPhase:事件所处的阶段;
- offsetX、offsetY:事件发生在元素内的位置;
- clientX、clientY:事件发生在客户端内的位置;
- pageX、pageY:事件发生在客户端相对于document的位置;
- screenX、screenY:事件发生相对于屏幕的位置;
常见的方法:
- preventDefault:取消事件的默认行为;
- stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止);
# 6. 事件处理中的this
在函数中,我们也可以通过this来获取当前的发生元素:
- 在浏览器内部,调用event handler是绑定到当前的事件源上的
# 7. 事件委托(event delegation)
事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式(也是一种设计模式)
- 当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击;
- 可以通过event.target获取到当前监听的元素;
06-BOM
hs 7/19/2022
# 1. 认识BOM
BOM:浏览器对象模型(Browser Object Model)
- 简称 BOM,由浏览器提供的用于处理文档(document)之外的所有内容的其他对象
- BOM就是让JS与浏览器进行“对话”,获取浏览器信息和操作浏览器
BOM没有规范
- ECMAScript:JS语法规范的标准
- DOM:W3C, DOM的核心:document
- BOM:没有规范,不是的浏览器厂商去定义自己的API,兼容性非常不好。BOM的核心:window
BOM主要包括一下的对象模型
- window:包括全局属性、方法,控制浏览器窗口相关的属性、方法
- location:浏览器连接到的对象的位置(URL)
- history:操作浏览器的历史
- navigator:用户代理(浏览器)的状态和标识(很少用到)
- screen:屏幕窗口信息(很少用到)
# 2. 再次认识window
window对象在浏览器中可以从两个视角来看待
- 视角一:全局对象 GO
- 视角二:浏览器窗口对象, 提供了对浏览器操作的相关的API
window对象的作用
- 第一:包含大量的属性,localStorage、console、location、history、screenX、scrollX等等(大概60+个属性)
- 第二:包含大量的方法,alert、close、scrollTo、open等等(大概40+个方法)
- 第三:包含大量的事件,focus、blur、load、hashchange等等(大概30+个事件)
- 第四:包含从EventTarget继承过来的方法,addEventListener、removeEventListener、dispatchEvent方法
- MDN文档:developer.mozilla.org/zh-CN/docs/…
<body>
<a href="http://www.baidu.com" target="_blank">百度一下</a>
<button>百度一下</button>
<button class="close">关闭窗口</button>
<script>
console.log(window)
console.log(window.outerHeight)
// 一些方法
var openBtnEl = document.querySelector("button")
var closeBtnEl = document.querySelector(".close")
openBtnEl.onclick = function() {
window.open("./page/new.html", "_blank")
}
closeBtnEl.onclick = function() {
window.close()
}
// 3.常见的事件
// window.onfocus = function() {
// console.log("窗口获取到焦点")
// }
// window.onblur = function() {
// console.log("窗口失去了焦点")
// }
window.onhashchange = function() {
console.log("hash值发生改变")
}
</script>
</body>
# 3. location对象
location对象用于表示window上当前链接到的URL信息
常见的属性有哪些
- href: 当前window对应的超链接URL, 整个URL;
- protocol: 当前的协议;
- host: 主机地址;
- hostname: 主机地址(不带端口);
- port: 端口;
- pathname: 路径;
- search: 查询字符串;
- hash: 哈希值;
我们会发现location其实是URL的一个抽象实现
常见的方法有哪些
- assign:赋值一个新的URL,并且跳转到该URL中
- replace:打开一个新的URL,并且跳转到该URL中(不同的是不会在浏览记录中留下之前的记录)
- reload:重新加载页面,可以传入一个Boolean类型
URLSearchParams介绍
- URLSearchParams 定义了一些实用的方法来处理 URL 的查询字符串
- 可以将一个字符串转化成URLSearchParams类型
- 也可以将一个URLSearchParams类型转成字符串
- 中文会使用encodeURIComponent和decodeURIComponent进行编码和解码
URLSearchParams常见的方法
- get:获取搜索参数的值;
- set:设置一个搜索参数和值;
- append:追加一个搜索参数和值;
- has:判断是否有某个搜索参数;
- developer.mozilla.org/zh-CN/docs/…
# 4. history对象
history对象允许我们访问浏览器曾经的会话历史记录
常见属性
- length:会话中的记录条数
- state:当前保留的状态值
常用方法
- back():返回上一页,等价于history.go(-1);
- forward():前进下一页,等价于history.go(1);
- go():加载历史中的某一页;
- pushState():打开一个指定的地址;
- replaceState():打开一个新的地址,并且使用replace;
# 5. navigator对象(了解)
- navigator 对象表示用户代理的状态和标识等信息。
# 6. screen对象(了解)
- screen主要记录的是浏览器窗口外面的客户端显示器的信息
- 比如屏幕的逻辑像素 screen.width、screen.height