DOM01
DOM:
document object model
文档对象模型
浏览器中输入 window - document 对象
浏览器提供的API:
window.document
对象
网页的HTML代码是如何运行到浏览器上的?
- 浏览器解析HTML文本代码, 转化成 对象类型 –
window.document
- 然后 把这个
document
对象类型, 渲染到页面上 - 所以: 页面真正显示的是
document
这个对象类型 - 利用 document 对象, 就可以直接操作浏览器的界面
01-初识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>初识DOM</title>
</head>
<body>
<!--
原始状态: 只需要 DOM 和 浏览器就能展现网页
但是DOM是JS的代码, 书写繁琐 且 不易读
所以提供了HTML这种标签语言, 便于用户书写网页代码
但是HTML只能完成简单的DOM操作, 更加复杂的浏览器操作还是需要原生DOM才能实现
实际上运行: HTML -> DOM对象 -> 显示在浏览器
-->
<h1>Hello DOM</h1>
<script>
// 浏览器利用 检查->元素 中, 就是document对象 美化之后的样子
// log: 会对打印的内容自动美化, 而非原本的模样
console.log(document) //document 就是DOM对象
// dir: direct 直接, 才能打印出真正样子
console.dir(document) //cdi
// DOM树: 一个形象的比喻, 讲述的是 HTML 如何转化成DOM对象
</script>
</body>
</html>
DOM树
// DOM树: 一个形象的比喻, 讲述的是 HTML 如何转化成DOM对象
DOM总结
常见元素获取-head,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>常见元素</title>
</head>
<body>
<!-- 当HTML转化成DOM时, 有一些固定的元素/节点
1. html整体
2. head
3. body
-->
<h1>Hello DOM</h1>
<script>
console.log(document.head)
console.dir(document.head)
console.log(document.body)
console.dir(document.body)
// html标签
console.log(document.documentElement)
console.dir(document.documentElement)
</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>自定义节点</title>
</head>
<body>
<!--
DOM操作一共分两个步骤:
1. 查找你要操作的元素
2. 操作这个元素
-->
<div>1111</div>
<div>2222</div>
<div>3333</div>
<script>
// 查找元素的方式非常多, 选择合适的
// 1. 根据与已知元素间的关系查找
// 已知 body 节点, 找其第一个子元素
// firstChild: 第一个孩子
console.log(document.body.firstChild) //基本不用
// firstElementChild: 第一个 元素类型 的孩子
console.log(document.body.firstElementChild)
// 最后一个元素类型的孩子:
console.log(document.body.lastElementChild)
// 所有孩子
console.log(document.body.children)
// 获取孩子中的具体某一个, 利用序号
console.log(document.body.children[2])
// item(序号): 由于比 [序号] 麻烦, 所以大家不爱用
console.log(document.body.children.item(2))
</script>
</body>
</html>
自定义元素查找
id方式
<!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>通过id查找</title>
</head>
<body>
<div></div>
<div>
<div>
<!-- id: 唯一标识, 要求不重复 -->
<span id="s1">Hello World</span>
<span>Hello World</span>
</div>
</div>
<div></div>
<script>
// 通过id查找指定元素
// get:获取 Element:元素 By:通过 Id:唯一标识
console.log(document.getElementById('s1'))
// 简化操作: id太常用了, 所以系统会自动完成:
// var id名 = document.getElementById(id名)
console.log(s1)
// 不推荐使用, 两个原因:
// 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>
</head>
<body>
<div>
<div>
<span>11</span>
</div>
<div>
<span>22</span>
</div>
</div>
<script>
// 注意: elements 带s 代表复数, 因为同名标签可以有多个
// get:获取 Elements:元素们 By: 通过 Tag:标签 Name
console.log(document.getElementsByTagName('span'))
</script>
</body>
</html>
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>
<!-- 单标签闭合写法 <单标签 /> -->
<!-- 在H5中, 官方推荐省略 <单标签>, 例如 <br> -->
<!-- 但是 在后续的一些框架中,推荐闭合 -->
<input type="radio" name="sex" id="" />
<span>男</span>
<br />
<input type="radio" name="sex" id="" />
<span>女</span>
</div>
<script>
// 通过 name 属性获取: 名字可重复,所以是多个
// elements : s结尾代表复数
console.log(document.getElementsByName('sex'))
</script>
</body>
</html>
class方式
<!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="danger">111</div>
<div>222</div>
<div class="danger">333</div>
<script>
// 通过 class 名查找
console.log(document.getElementsByClassName('danger'))
</script>
</body>
</html>
通过css选择查找
querySelector
返回满足条件的第一个元素(node),而querySelectorAll
返回满足条件的所有元素(nodelist);
2.在都没有满足条件的元素情况下:querySelector返回null,而querySelectorAll返回空的数组[]。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>通过css选择器查找</title>
</head>
<body>
<div id="left">left</div>
<div id="right">right</div>
<div class="jump">
<a href="">百度</a>
</div>
<script>
var div = document.querySelector('div');
console.log(div);
var div = document.querySelectorAll('div');
console.log(div);
var d = document.querySelector('.jump>a');
console.log(d);
</script>
</body>
</html>
通过关系查找
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>关系型选择器</title>
</head>
<body>
<div class="box">
<div class="bro">
<h1>电影名称</h1>
</div>
<div class="msg">
<h2>这么多年</h2>
<h2>速度与激情10</h2>
</div>
<h3>花海</h3>
<h3>黑色毛衣</h3>
</div>
<script>
// 父选子
var box = document.querySelector('.box');
console.log(box.children);
// 子选父
var msg = document.querySelector('.msg');
console.log(msg.parentNode);
console.log(msg.parentElement);
// 选第一个子元素
console.log(msg.firstElementChild);
// 选最后一个子元素
console.log(msg.lastElementChild);
// 选上一个兄弟
console.log(msg.previousElementSibling);
// 选下一个兄弟
console.log(msg.nextElementSibling);
</script>
</body>
</html>
操作style - 把id='d2’的文字颜色改为红色
<!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>
<div>1111</div>
<div id="d2">2222</div>
<div>3333</div>
</div>
<div></div>
<script>
// 1.找到id='d2' 的元素
let d2 = document.getElementById('d2')
console.log(d2)
console.dir(d2)
// 2.修改其样式, 变红
d2.style.color = 'red'
//给每一个子元素添加边框
// parent: 父母
let d2_p = d2.parentElement
console.log(d2_p)
// children: 所有子元素
let divs = d2_p.children
console.log(divs)
// 伪数组类型, 没有forEach方法, 用 for..of遍历
for (let el of divs) {
// el: 是element 元素的缩写, 可以随意起名
el.style.border = '1px solid blue'
}
</script>
</body>
</html>
事件
所有的DOM事件参考: 所有DOM事件链接
https://www.runoob.com/jsref/dom-obj-event.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>事件</title>
</head>
<body>
<div>
<button>点我</button>
</div>
<script>
// 注意: 伪数组元素 , 要通过 `序号` 来获取具体的元素
let btn = document.getElementsByTagName('button')[0]
console.log(btn)
// 所有的事件都是用 on 开头的, 这是一个明显的标识
// HTML DOM事件: https://www.runoob.com/jsref/dom-obj-event.html
console.dir(btn)
// onclick: 点击
// onclick时 触发函数, 不应该写箭头函数, 因为this的指向问题
// 箭头函数this: 指向声明时所在作用域,此处是window
// 普通函数: 运行时所在对象, 此处是btn
btn.onclick = function () {
console.log('this:', this)
// 点击后 为按钮的父元素添加边框
this.parentElement.style.border = '1px solid red'
}
</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>
.menu {
padding: 4px;
background-color: antiquewhite;
width: 200px;
/* 双击选中文本: 不开启此功能 */
user-select: none;
}
/* +: 兄弟选择器, 代表 .menu 下方的div */
.menu + div {
display: none;
}
</style>
</head>
<body>
<div>
<div class="menu">菜单</div>
<div>
<div>黄焖鸡</div>
<div>盖浇饭</div>
<div>鸭血粉丝汤</div>
<div>肯德基</div>
</div>
</div>
<script>
// 1. 选择 class='menu' 的元素
let menu = document.getElementsByClassName('menu')[0]
console.dir(menu)
// 2. 添加点击事件
menu.onclick = function () {
// 3. 点击后,把其下方的兄弟元素, display:'block'
// next:下一个 Element:元素 Sibling:兄弟姐妹
// style: 优先级比 class选择器高, 所以覆盖class中的 display:none 效果
//this.nextElementSibling.style = 'block'
let style = this.nextElementSibling.style
// style:是对象类型, 地址传递, 引用类型
// 内联样式: 默认值是空字符串
if (style.display == '') {
style.display = 'block'
} else {
style.display = ''
}
// 错误写法: display是普通数据类型 ''
// 类似于: let a = 5; let b = a; b=10; a变不变
// a不变: 普通数据类型是值传递, 是独立的变量
// let display = this.nextElementSibling.style.display
// display = ''
}
</script>
</body>
</html>
升级-每个菜单点击后, 都能实现 展开/收起(XXX)
<!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>
.menu {
margin-top: 2px;
padding: 4px;
background-color: antiquewhite;
width: 200px;
/* 双击选中文本: 不开启此功能 */
user-select: none;
}
/* +: 兄弟选择器, 代表 .menu 下方的div */
.menu + div {
display: none;
}
</style>
</head>
<body>
<div>
<div class="menu">菜单1</div>
<div>
<div>黄焖鸡</div>
<div>盖浇饭</div>
<div>鸭血粉丝汤</div>
<div>肯德基</div>
</div>
<div class="menu">菜单2</div>
<div>
<div>黄焖鸡</div>
<div>盖浇饭</div>
<div>鸭血粉丝汤</div>
<div>肯德基</div>
</div>
<div class="menu">菜单3</div>
<div>
<div>黄焖鸡</div>
<div>盖浇饭</div>
<div>鸭血粉丝汤</div>
<div>肯德基</div>
</div>
</div>
<script>
// 任务: 每个菜单点击后, 都能实现 展开/收起
// 提示: 查询所有 class='menu', for..of 遍历为每个添加点击事件 即可
let menus = document.getElementsByClassName('menu')
console.log(menus)
for (let menu of menus) {
menu.onclick = function () {
let style = this.nextElementSibling.style
// 空字符串代表: 没有 style.display 样式
style.display = style.display == '' ? 'block' : ''
}
}
</script>
</body>
</html>
操作class
<!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>操作class</title>
<style>
.box {
width: 200px;
height: 200px;
border: 1px solid red;
/* 样式变化时的动画时长 */
transition: 0.25s;
}
/* 一会通过按钮进行切换 */
.abc {
background-color: purple;
border-radius: 50%;
}
</style>
</head>
<body>
<button>添加样式</button>
<button>移除样式</button>
<button>切换样式</button>
<div id="box" class="box ab xy ui ww"></div>
<script>
let btns = document.getElementsByTagName('button')
console.log(btns)
let box = document.getElementById('box')
//添加
btns[0].onclick = function () {
//样式操作有两种: className 和 classList
// className: 此方式直接操作class='' 的值, 比较简陋
// box.className = 'box abc'
// classList: 提供了简单操作class的各种方法
// add(): 在原有元素基础上, 添加新的
box.classList.add('abc')
}
//移除
btns[1].onclick = function () {
// remove: 删除
box.classList.remove('abc')
}
//切换
btns[2].onclick = function () {
// toggle: 切换 -- 有->删; 没有->加
box.classList.toggle('abc')
}
</script>
</body>
</html>
class实战 - 菜单添加动画
<!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>
#menu {
width: 200px;
border: 1px solid gray;
}
.title {
background-color: bisque;
padding: 5px;
margin-top: 2px;
}
.title + div {
transition: 1s;
/* 最大高度: 支持动画, height:auto;不支持 */
max-height: 0;
overflow: hidden;
}
/* 显示: 高度改成自动 */
/* div.show: div标签 同时代表 show样式 */
div.show {
max-height: 150px;
}
</style>
</head>
<body>
<div id="menu">
<div class="title">菜单1</div>
<div>
<div>红烧牛肉</div>
<div>老坛酸菜</div>
<div>鲜虾鱼板</div>
<div>香辣牛肉</div>
</div>
<div class="title">菜单2</div>
<div>
<div>红烧牛肉</div>
<div>老坛酸菜</div>
<div>鲜虾鱼板</div>
<div>香辣牛肉</div>
</div>
</div>
<script>
// 选择 class='title' 元素
let titles = document.getElementsByClassName('title')
console.log(titles)
for (let title of titles) {
title.onclick = function () {
// toggle: 切换
// nextElementSibling: 下一个兄弟元素
this.nextElementSibling.classList.toggle('show')
}
}
// 点击事件: 切换 class='show' 样式
</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>
.indicator {
width: 400px;
padding: 5px 0;
text-align: center;
background-color: lightgray;
}
.indicator > span {
display: inline-block;
width: 16px;
height: 16px;
background-color: white;
border-radius: 50%;
}
.indicator > span.cur {
background-color: deepskyblue;
}
</style>
</head>
<body>
<!-- indicator: 指示器 -->
<div class="indicator">
<!-- cur: 是 current 缩写, 当前项的意思 -->
<span class="cur"></span>
<span></span>
<span></span>
<span></span>
</div>
<script>
// 所有 小圆点: class='indicator' 元素下的所有子元素
// let points = document.getElementsByClassName('indicator')[0].children
// 实战中: 通常使用 css 选择器, 最万能 最准确
// querySelectorAll: all 查询多个满足条件的
let points = document.querySelectorAll('.indicator>span')
// 这种用法: 结果的原型中, 有forEach可以用
points.forEach(value => {
// onmouseover: 鼠标悬浮时触发
value.onmouseover = function () {
// 找到之前带有 .cur 样式的元素, 删除样式
// 通过css选择器查找元素, 必须要预判结果的数量
// 此处预判到结果只有一个, 所以:
// querySelector: 结果是单个元素
let span_cur = document.querySelector('.cur')
console.log('span_cur:', span_cur)
span_cur.classList.remove('cur')
console.log(this)
// 鼠标悬浮时, 添加 cur 样式
this.classList.add('cur')
}
})
console.log(points)
</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>
.pages {
background-color: lightgray;
padding: 5px;
width: 400px;
text-align: center;
user-select: none;
}
.pages > span {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
background-color: white;
color: deepskyblue;
border-radius: 4px;
}
.pages > span:hover,
.pages > span.cur {
color: white;
background-color: deepskyblue;
}
</style>
</head>
<body>
<div class="pages">
<span class="cur">1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
</div>
<script>
// document.querySelector 查单个
// document.querySelectorAll 查多个, forEach遍历
let spans = document.querySelectorAll('.pages>span')
console.log(spans)
spans.forEach(span => {
// 参数名:span, 看起来更加容易理解, 比value更有含义
span.onclick = function () {
// 移除之前高亮元素的 cur
let span_cur = document.querySelector('.pages .cur')
span_cur.classList.remove('cur')
// 为当前点击的添加cur
this.classList.add('cur')
}
})
</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>
#menu {
/* 软件: snipaste 截图 屏幕标记 取色器 */
background-color: rgb(0, 44, 105);
color: white;
user-select: none;
}
#menu > span {
display: inline-block;
width: 100px;
padding: 5px 0;
text-align: center;
}
#menu > span.cur {
background-color: orange;
}
</style>
</head>
<body>
<div id="menu">
<span class="cur">首页</span>
<span>关于净美仕</span>
<span>公司动态</span>
<span>产品中心</span>
<span>联系我们</span>
</div>
<script>
const qsa = document.querySelectorAll('#menu>span')
console.log(qsa);
// 推荐用const 代替 let , 因为更安全, 变量没有变更需求的场景
qsa.forEach(value => {
value.onclick = function () {
//删除之前高亮的
const qs = document.querySelector('#menu .cur')
qs.classList.remove('cur')
this.classList.add('cur')
}
})
</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>
#menu {
user-select: none;
}
#menu > span {
display: inline-block;
padding-bottom: 5px;
margin: 0 10px;
color: gray;
font-size: 2em;
}
#menu > .cur {
font-weight: bold;
color: black;
border-bottom: 5px solid blue;
}
</style>
</head>
<body>
<div id="menu">
<span class="cur">账号登录</span>
<span>短信登录</span>
</div>
<script>
const qsa = document.querySelectorAll('#menu span')
qsa.forEach(value => {
value.onclick = function () {
const qs = document.querySelector('#menu .cur')
qs.classList.remove('cur')
this.classList.add('cur')
}
})
</script>
</body>
</html>
作业
作业1:
- 悬浮状态
:hover
, 只有非 当前项才有, 应该利用:not
排除当前项span:not(.cur):hover
: span标签中, 不是.cur样式的元素,悬浮态
作业2:
标签栏切换的头部: 点击之后的项目是白色背景