1-JavaScript - DOM
about
首先再来复习网页的构成:
- 结构:HTML
- 表现:CSS
- 行为:JavaScript
DOM(Document Object Model),文档对象模型。js通过dom对象可以操作网页的各种行为,所以它算浏览器中的最顶级的对象了。
- 文档,文档指的是整个网页,也成为文档对象,它由浏览器提供,我们可以直接在js中使用。
- 对象,DOM将网页中的所有内容都转换为了对象。
- 模型用来体现节点之间的关系。
节点(Node),网页中的所有内容都可以称之为节点,只不过,虽然都是节点,它们之间也有着不同的类型:
- 文档节点,表示整个网页。
- 元素节点,各种标签都属于元素节点。
- 属性节点,标签中的属性称之为属性节点。
- 文本节点,标签中的文字、空白。
event
https://www.w3school.com.cn/jsref/dom_obj_event.asp
摘自w3chool
HTML DOM 事件允许 JavaScript 在 HTML 文档中的元素上注册不同的事件处理程序。
事件通常与函数结合使用,在事件发生之前函数不会被执行(例如当用户单击按钮时)。
也就是说,我们能可以通过不同类型的事件,与网页进行交互。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!-- 定义事件的方式1,直接写在标签上 -->
<button id="btn1" onclick="alert('hello');">btn1</button>
<button id="btn2">btn2</button>
<script>
// 定义事件的方式1,在script标签中写
let btn = document.getElementById('btn2');
btn.onclick = function () {
alert('你好啊!')
}
</script>
</body>
</html>
上例演示了事件的基本用法,更多的是事件参考:https://www.w3school.com.cn/jsref/dom_obj_event.asp
文档加载顺序
网页中的代码,在加载时,是自上而下一行一行加载的,如果, 将script标签写到head标签内,此时的js代码会优先于网页中的元素加载,这就肯能导致出现js代码无法正常获取DOM对象的情况,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
// 定义事件的方式1,在script标签中写
let btn = document.getElementById('btn2'); // Uncaught TypeError: Cannot set property 'onclick' of null
btn.onclick = function () {
alert('你好啊!')
}
</script>
</head>
<body>
<button id="btn2">btn2</button>
</body>
</html>
如上例,代码先执行到了获取button按钮对象,但此时button按钮还没有生成呢, 所以,就获取为null,而null有没有onclick事件,所以报错了。
怎么解决这个问题呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
// 法2,如果写在head中,可以通过一个事件来控制
// window.onload事件,会在网页加载完毕后出触发执行
window.onload = function () {
let btn = document.getElementById('btn2');
btn.onclick = function () {
alert('你好啊!')
}
};
</script>
<!-- 法3,如果是外部文件,可以在引入时在script标签添加defer属性使其延迟加载 -->
<script src="./a.js" defer></script>
</head>
<body>
<button id="btn2">btn2</button>
<script>
// 法1,可以直接将script代码写在body标签的底部,这样就就没问题了
let btn = document.getElementById('btn2');
btn.onclick = function () {
alert('你好啊!')
}
</script>
</body>
</html>
选择器
通过不同的选择器来获取标签对象,有了标签对象才能对这个标签做操作或者调整样式,所以掌握几种不同的选择器是十分必要的。
另外,关于节点这里前面也说过有不同类型的节点,所以在找节点的时候,你要考虑到只找元素节点还是包含其他节点,比如文本节点。
直接查找选择器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
window.onload = function () {
/* 直接查找选择器 */
// id选择器,根据id值获取标签对象,因为id值是唯一的,所以直接返回标签对象
// console.log(document.getElementById('d1'));
// 元素(标签)选择器,根据标签名获取标签,因为同名的标签可能有多个,所以以类数组的形式返回标签对象
// console.log(document.getElementsByTagName('div')); // 以类数组的形式返回标签对象
// console.log(document.getElementsByTagName('div')[0]); // 通过索引下标获取标签对象
// document.getElementsByTagName('*'); // 页面中的标签,有一个算一个都获取
// 类选择器,一看就懂,不在多表
// console.log(document.getElementsByClassName('cd'));
// console.log(document.getElementsByClassName('cd')[0]);
// input框专用,根据input框name属性选择器
// console.log(document.getElementsByName('userName'));
// console.log(document.getElementsByName('userName')[0]);
// 当拿到一个标签对象之后,就可以通过属性点属性名的方式操作属性
let inp = document.getElementsByName('userName')[0];
// console.log(inp.value); // 获取输入的value值
// console.log(inp.name); // 获取name属性值
// console.log(inp.id); // 获取id值
// console.log(inp.className); // 获取class属性 inp1 inp2
// console.log(inp.classList); // 以数组的形式返回所有class值
// console.log(inp.classList[0]); // 以数组的形式返回所有class值,可以通过索引来获取
// console.log(inp.type); // 标签的type属性
// 修改对应的属性值,如修改input框的value值,当然其他属性也可以修改
inp.value = '666';
// 获取页面的根元素,即html标签
document.documentElement
// 获取页面的body元素
document.body
}
</script>
</head>
<body>
<input type="text" name="userName" id="userName" class="inp1 inp2">
<div id="d1" class="c1 cd">111</div>
<div id="d2" class="c2 cd">222</div>
<div class="c3">
<div class="c4">444</div>
<div class="c5">555</div>
</div>
<div class="c6">666</div>
</body>
</html>
注意:input框中不能用innerText获取值,它是文本操作,input框的值比较特殊,要获取inpu框中的值用value。
间接查找选择器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="d1" class="c1 cd">111</div>
<div id="d2" class="c2 cd">222</div>
<div class="c3">
<div class="c4">444</div>
<div class="c5">555</div>
</div>
<div class="c6">666</div>
</body>
<script>
/* 间接查找选择器,通过a标签对象找其内的b标签 */
// 找下一个兄弟标签,通过c3找c6
// let e = document.getElementsByClassName('c3')[0];
// console.log(e.nextElementSibling); // 下一个标签节点
// e.nextSibling; // 下一个兄弟节点
// 找上一个兄弟,通过c3找c2
// let e = document.getElementsByClassName('c3')[0];
// console.log(e.previousSibling); // 找上一个兄弟节点,也包括文本节点
// console.log(e.previousElementSibling); // 找上一个兄弟元素节点
// 找儿子,通过c3找c4
// let e = document.getElementsByClassName('c3')[0];
// console.log(e.firstChild); // 找第一个子节点,包括文本节点,即空白文本
// console.log(e.firstElementChild); // 找第一个子标签节点
// console.log(e.lastChild); // 找最后一个子节点,包括文本节点,即空白文本
// console.log(e.lastElementChild); // 找最后一个子节点
// children获取当前节点所有子标签节点
// console.log(e.children); // 获取所有儿子
// console.log(e.children[0]); // 通过索引找儿子
// childNodes获取当前节点的所有子节点,注意,空白的文本也算子节点!!
// console.log(e.childNodes);
// 找爹,通过c4找c3
// let e = document.getElementsByClassName('c4')[0];
// console.log(e.parentElement);
</script>
</html>
CSS样式选择器
根据CSS选择器来获取标签对象,这里介绍两个方法:
document.querySelector()
,它需要一个字符串类型的CSS选择器参数,返回符合条件的第一个标签对象,是的,就算有多个,它也只返回第一个。document.querySelectorAll()
,它需要一个字符串类型的CSS选择器参数,返回符合条件的标签对象数组,可以按照索引取值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
window.onload = function () {
// 只会返回符合条件的第一个标签对象
let items = document.querySelector('.item');
console.log(items.innerHTML); // 选项1
// 以数组的形式返回结果,没有就为空数组
let items2 = document.querySelectorAll('.item');
console.log(items2.length); // 3
}
</script>
</head>
<body>
<div class="d1">
<ul>
<li class="item">选项1</li>
<li class="item">选项2</li>
<li class="item">选项3</li>
</ul>
</div>
</body>
</html>
文本操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="d1" class="c1 cd">111</div>
<div id="d2" class="c2 cd">222</div>
<div class="c3">
<div class="c4">444</div>
<div class="c5">555</div>
</div>
<div class="c6">666</div>
<script>
// 获取文本
let e = document.getElementsByClassName('c3')[0];
// console.log(e.innerText); // 获取标签文本,包括子标签内的文本,但不包含空白
// console.log(e.innerHTML); // 获取标签内的html结构,包括子标签的标签和内容
// console.log(e.textContent); // 跟innerText一样,都是获取文本,包括子标签内的文本,忽略子标签,担包含空白
// 为标签设置文本
// e.innerText = '张开,你好';
// e.innerText = '<span>张开,你好</span>'; // 被赋值的内容中包含的标签,也会被视为普通字符串,没有特殊意义
// e.innerHTML = '<span>张开,你好</span>'; // 被赋值的内容中包含的标签,会被识别并渲染
</script>
</body>
</html>
样式操作
通过dom获取标签对象,然后通过dom操作为标签添加样式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.c7{
width: 100px;
height: 200px;
background-color: yellow;
overflow: scroll;
/*overflow-y: auto;!**!*/
}
</style>
</head>
<body>
<div id="d1" class="c1 cd">111</div>
<div id="d2" class="c2 cd">222</div>
<div class="c3">
<div class="c4">444</div>
<div class="c5">555</div>
</div>
<div class="c6">666</div>
<br>
<div class="c7">
lorem + tab键,生成一堆字符
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem consectetur corporis dolorum, eaque est harum ipsum nisi porro, quas sint temporibus ut vel voluptas. Debitis fugit iste unde? Dolorem, earum.
</div>
<script>
// 通过style.具体的属性,为标签添加内联样式,或者覆盖已有的样式
// let e = document.getElementsByClassName('c3')[0];
// e.style.color = "red";
// e.style.backgroundColor = 'yellow';
// e.style.width = '400px';
// e.style.heigth = '200px';
// 能设置就能读取,但要注意的是,通过style读取,也只能读取内联样式
// console.log(e.style.width);
// 所以,通常使用下面的函数来获取标签的样式,用的较多
// 该函数需要传递一个标签对象,然后返回这个标签的所有生效的样式,你想要啥,你点啥
// console.log(getComputedStyle(e).width);
// getComputedStyle获取的值都是有单位的,所以要是参与计算的话,还需要把单位去掉后再运算
// 比如 parseInt(getComputedStyle(e).width) + 10;
// 其它获取样式的方法,只读属性,无法修改
// console.log(e.clientHeight); // 获取标签的内部区域的高度,包括padding和内容
// console.log(e.clientWidth); // 同上
// 用来获取盒子,可见区域的大小,包括内容、padding、边距
// 只读属性,无法修改
// console.log(e.offsetHeight);
// console.log(e.offsetWidth);
// offsetParent用来获取当前元素的定位父元素
// 如果所有的祖先元素都没有开启定位,则返回body
// console.log(e.offsetParent);
// 获取当前元素相对于定位父元素左侧和上侧的偏移量
// console.log(e.offsetLeft);
// console.log(e.offsetTop);
// clientHeight返回的是可视高度,而scrollHeight适用于带滚动条的标签中,返回滚动区域大小
let c7 = document.getElementsByClassName('c7')[0];
// console.log(c7.clientHeight); // 200
// console.log(c7.scrollHeight); // 504
// console.log(c7.scrollWidth); // 90
// console.log(c7.scrollTop); // 滚动条滚动距离
// scrollTop还能修改
// c7.scrollTop = 20; // 相当于下拉滚动条20px
// c7.scrollLeft; // 横向滚动条,滚动的距离
/**
* 当scrollHeight -scrollTop === clientHeight 时,说明垂直滚动条到底了
* 当scrollWidth -scrollLeft === clientWidth 时,说明水平滚动条到底了
* 这鬼东西有啥用呢?有应用场景,比如某些网站需要你阅读许可协议时,不拉到底,你就无法下一步
*/
</script>
</body>
</html>
值操作
所谓的值操作,多用于获取input框的值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" name="user" class="i1" value="input框默认值">
<script>
// input框中不能用innerText获取值,它是文本操作,input框的值比较特殊
var e = document.getElementsByClassName('i1')[0];
e.value = '来玩呀!!'; // 为input框设置值
console.log(e.value); // 获取input框中的value值
</script>
</body>
</html>
类属性操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0;
}
.c1 div, .c4 {
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
}
.c2{
background-color: aquamarine;
}
/**/
.c3{
background-color: purple;
}
.c4{
background-color: pink;
}
.c5{
background-color: green;
}
</style>
</head>
<body>
<div class="c1">
<div class="c2">c2</div>
<div class="c3">c3</div>
</div>
<div class="c4">c4</div>
<script>
var e = document.getElementsByClassName('c4')[0];
// e.classList.add("c5"); // 添加类属性
// console.log(e.classList); // 以数组的形式返回类属性
// e.classList.remove("c4"); // 删除类属性
// e.classList.toggle("c4"); // 如果类属性存在,就删除,不存在就添加
// e.replace(); // 使用一个新的class,替换原有的class
// e.contains(); // 检查一个元素是否含有某个class,如果包含返回true,否则返回false
</script>
</body>
</html>
自定义DOM对象的操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
window.onload = function () {
let ul = document.getElementsByClassName('list')[0];
// 创建一个空标签对象
let li = document.createElement('li');
// console.log(li); // <li></li>
// 为标签添加文本,有3种方式
// 法1,通过创建文本节点的方式来做,用的不多
// let text = document.createTextNode('猪八戒');
// li.appendChild(text);
// 法2,直接为标签对象通过属性赋值添加文本,设置样式、属性之类的。用的较多
// li.innerHTML = "狐狸精";
// li.className = "item1";
// li.style.color = "red";
// 法3,不推荐!!!这种方式相当于将ul内的标签都重置了一下,那么绑定的一些事件,就失效了,所以不推荐。
// ul.innerHTML += "<li>蜘蛛精</li>"
// 将创建的空标签对象,添加到某个节点中(最后)
// ul.appendChild(li);
// 上面append是将标签插入到指定节点的最后,现在再来介绍一个方法,指定位置插入
// ul.insertAdjacentElement("beforebegin", li);
// ul.insertAdjacentElement("afterbegin", li);
// ul.insertAdjacentElement("afterend", li);
// ul.insertAdjacentElement("beforeend", li);
// 插入到指定子节点的上面
// ul.insertBefore(li, ul.children[2]);
// 用新的标签替换到原理的标签
// ul.replaceChild(li, ul.children[0]);
// 删除子节点,删除第一个自孩子,根据父亲找孩子,他杀
// ul.removeChild(ul.firstElementChild);
// 自杀,根据自己删自己,但老版本的浏览器支持不太好
// let firstLi = ul.firstElementChild;
// firstLi.remove();
}
</script>
</head>
<body>
<!-- beforebegin -->
<ul class="list">
<!-- afterbegin -->
<li>唐僧</li>
<li>沙和尚</li>
<li>孙悟空</li>
<!-- beforeend -->
</ul>
<!-- afterend -->
</body>
</html>
that's all