1. 目标
- 能够说出什么是DOM
- 能够获取页面元素
- 能够给元素注册事件
- 能够操作DOM元素的属性
- 能够创建元素
- 能够操作DOM节点
2. DOM简介
2.1 什么是DOM呢?
文档对象模型( Document Object Model ,简称DOM),是W3C推荐的处理可扩展标记语言( HTML或者XML )的标准编程接口。
W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容,结构和样式。
2. DOM树
html 上面的标签时 document。document代表整个文档
- 文档:一个页面就是一个文档,DOM中使用 document 表示
- 元素:页面中的所有标签都是元素,DOM中使用 element 表示
- 节点:网页中的所有内容都是节点(标签,属性,文本,注释等),DOM中使用node表示
DOM 把以上的内容都看作是对象
3. 获取元素
在实际开发中主要用来操作元素。
3.1 获取页面元素
3.1.1 根据 ID 获取
- 返回一个匹配特点 ID 的元素
- id是大小写敏感的字符串
栗子:
<body>
<div id="time">2020-4-8</div>
<script>
// 1. 因为文档页面从上往下加载,所以得先有标签,所以我们script写到标签的下面
// 2. 获得 元素 通过 id(翻译)驼峰命名法
// 3. 参数 id 是大小写敏感的字符串,所以加引号。
// 4. 返回的是一个元素对象
div = document.getElementById("time");
console.log(time);
console.log(typeof(time));
// 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性与方法
console.dir(time);
</script>
</body>
3.1.2 根据标签名获取
使用 getElementByTagName() 方法可以返回带有指定标签名的对象集合。
栗子:
<body>
<ul>
<li>帅哥是我 我是帅哥1</li>
<li>帅哥是我 我是帅哥2</li>
<li>帅哥是我 我是帅哥3</li>
<li>帅哥是我 我是帅哥4</li>
<li>帅哥是我 我是帅哥5</li>
</ul>
<ul id="nav">
<li>嘘!~ 禁止想象 1</li>
<li>嘘!~ 禁止想象 2</li>
<li>嘘!~ 禁止想象 3</li>
<li>嘘!~ 禁止想象 4</li>
<li>嘘!~ 禁止想象 5</li>
</ul>
<script>
// 返回的是 获取过来元素对象的集合 以伪数组的形式存储
var list = document.getElementsByTagName('li');
console.dir(list);
console.log(list[0]);
// 我们想要依次打印里面的元素对象我们可以采用遍历的方式
for (var i = 0; i < list.length; i++) {
console.log(list[i]);
}
// 3. element.getElementsByTagName() 可以得到这个元素里面的某个标签
var nav = document.getElementById('nav'); //可以获得 nav 元素
var navList = nav.getElementsByTagName('li');
console.dir(navList);
</script>
</body>
3. 通过 HTML5 新增的方法获取
- getElementsByClassName(‘类名’)
- querySelctor( )
- querySelctorAll( )
栗子:
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
// 1. getElementsByClassName('类名') 根据类名返回元素对象集合
var box = document.getElementsByClassName('box');
console.log(box);
// 2. querySelctor 返回选择器的第一个元素对象
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelctorAll() 返回指定选择器的所有对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log((lis));
</script>
</body>
4. 特殊元素获取
<body>
<script>
/* 获取body */
var bodyEle = document.body;
console.log(bodyEle);
/* 获取html */
var htmlEle = document.documentElement;
console.log(htmlEle);
</script>
</body>
4. 事件基础
JavaScript使我们有能力创建动态页面事件是可以被JavaScript侦测到的行为。
简单理解:触发—响应机制。
网页中的每个元素都可以产生某些可以触发 JavaScript的事件,例如,我们可以在用户点击某按钮是产生一个事件,然后去执行某些操作。
栗子:唐伯虎点秋香
<body>
<button id="btn">唐伯虎</button>
<script>
// 点击一个按钮,弹出对话框
// 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素
// (1) 事件源 事件被触发的对象 谁 按钮
var btn = document.getElementById('btn');
// (2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
// (3) 事件处理程序 通过一个函数赋值的方式 完成
btn.onclick = function() {
alert('点秋香');
}
</script>
</body>
执行事件的步骤
- 获取事件源
- 注册事件(绑定事件)
- 添加事件处理程序(采取函数赋值的形式)
常见鼠标事件
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onmousemove | 鼠标移动触发 |
onmouseenter | 鼠标经过触发 |
onmouseleave | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
栗子:
<body>
<div>123</div>
<script>
//执行事件步骤
//点击 div 控制台输出 我被选中了
// 1. 获取事件源
var div = document.querySelector('div');
// 2. 绑定事件 注册事件
// div.onclick
// 3. 添加事件处理程序
div.onclick = function() {
console.log('我被选中了');
}
</script>
</body>
5. 操作元素
JavaScript 的 DOM 操作可以改变网页内容,结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容,属性等,注意以下都是属性
5.1 改变元素内容
element.innerText
从起始位置到终止位置的内容,但它除去html标签,同时空格和换行也会同时去掉
element.innerHtml
-
栗子二:
二者区别:
<body>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML 的区别
// 1. innerText 不识别 html标签 非标准
// var div = document.querySelector('div');
// div.innerText = "<strong>最帅</strong>是我";
// 2. innerHTML 识别 html 标签 W3C 标准
// div.innerHTML = "<strong>最帅</strong>是我";
// 这两个属性是可读写的 可以获取元素里面的内容
// var p = document.querySelector('p');
// console.log(p.innerText);
// console.log(p.innerHTML);
</script>
</body>
起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行
innerHTML标准,尽量用
5.2 改变元素属性
再来个栗子:
通过不同时间得到不同的问候语
<body>
<img src="" alt="">
<div>上午好</div>
<script>
// 1. 获取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
// 2. 获取当前小时数
var date = new Date();
var h = date.getHours();
console.log(h);
// 3. 判断小时数
switch (true) {
case h < 12:
img.src = "morning.jpg"
div.innerHTML = '亲!早上好';
break;
case h > 12 && h < 17:
img.src = "afternoon.jpg"
div.innerHTML = '亲!下午好';
break;
default:
img.src = "night.jpg"
div.innerHTML = '亲!晚上好';
break;
}
</script>
</body>
5.3 表单元素的属性操作
type, value, checked, selected, disabled(禁用按钮)
表单里面的值是通过value来变化的
5.4 样式属性操作
我们可以通过 js 修改元素的大小,颜色,位置等样式
方法 | 作用 |
---|---|
element.style | 行内样式操作如果说样式比较少 或者 功能简单的情况下使用 |
element.className | 类名样式操作 如果说样式比较多或者功能比较复杂那么就用这个(更好的分离样式与行为) |
注:
- js里面的样式采用驼峰命名法 比如 fontSize, backgroundColor
- js 修改 style 样式操作,产生的是行内样式, 权重高。
-
栗子1( element.style ):隐藏文本框的内容
当鼠标点击文本框的时候,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示。 -
栗子2( element.className ):改变class名
- 如果样式修改较多,可以采取操作类名方式更改元素样式
- class是个保留字,因此使用className来操作元素类名属性
- className会直接更改元素的类名,会覆盖原先的类名 所以要加新的类名要把原先的写上
<!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>
div {
width: 200px;
height: 200px;
color: saddlebrown;
background-color: red;
}
.change {
margin: 20px;
color: silver;
background-color: royalblue;
}
</style>
</head>
<body>
<div>我是帅哥</div>
<script>
var div = document.getElementsByTagName('div');
div[0].onclick = function() {
this.className = 'change';
}
</script>
</body>
</html>
5.5 排他思想
如果有同一组元素我们想要改变某一个元素实现某种样式,需要用到循环的排他思想:
- 所有元素清空样式(干掉其他人)
- 给当前元素设置样式(留下我自己)
- 注意顺序不能颠倒,首先干掉其他人,在设置自己
-
- 给一组元素注册事件
- 点击后页面背景变化
5.5 自定义属性的操作
获取方法 | 不同点 |
---|---|
element.属性 | 只能获得内置的属性(元素本身自带的) |
element.getAttribute(‘属性’) | 主要获得自定义的属性(标准) |
5.5 设置属性值
设置方法 | 不同点 |
---|---|
element.属性 = ‘属性值’ | 内置的属性 |
element.setAttribute(‘属性’, ‘属性值’) | 自定义属性 |
注:class比较特殊element.setAttribute('class', '啥啥啥')
记住不是className。
5.6 移除属性
element.removeAttribute('属性');
5.7 H5自定义属性
一:
- 自定义属性的目的:是为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库
- 自定义属性是通过getAttribute(‘属性’)获取。
- 但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性
注: 为了解决上述问题我们在设置自定义属性的时候以 data- 开头作为属性名并赋值
例:<div data-index = "1"><div>
二:
-
js 添加自定义属性及属性值:
element.setAttribute('属性', '啥啥啥') = ‘啥啥啥’
-
js 获取自定义属性值:
element.getAttribute('属性')
(兼容性获取方法)element.dataset.自定义属性名
(H5新增方法)element.dataset['自定义属性名']
(H5新增方法)- 注:如果自定义属性中有多个 "- " 链接的单词, 我们获取的时候采取的是 驼峰命名法
栗子(important):
<body>
<div getTime='20' data-index='2' data-list-name="andy"> </div>
<script>
var div = document.querySelector('div');
console.log(div.getAttribute('getTime')); /* 传统获取方法 */
div.setAttribute('data-time', 20); /* 传统添加方式 */
console.log(div.getAttribute('data-index')); /* H5新增命名方法 */
console.log(div.getAttribute('data-list-name'));
/* h5新增的获取自定义属性的方法, 只能获取 data- 开头的自定义属性 ie 11才开始识别*/
/* dataset是一个集合里面存放了所有以data开头的自定义属性 */
console.log(div.dataset); /* 所有的自定义属性都存放在里面 */
console.log(div.dataset.index); /* 和对象的用法一样,直接提取即可 */
console.log(div.dataset['index']); /* 第二种方法提取 */
/* 如果自定义属性中有多个 " - " 链接的单词, 我们获取的时候采取的是 驼峰命名法*/
console.log(div.dataset.listName);
console.log(div.dataset['listName']);
</script>
</body>
6. 节点操作
6.1 为啥学习节点操作
利用DOM提供的方法获取元素 | 利用节点的层级关系获取元素 |
---|---|
document.getElementById() | 1. 利用利用 父 子 兄 节点关系获取元素 |
document.getElementByTagName() | |
document.querySelector等 | |
逻辑性不强,繁琐 | 逻辑性强,操作简单,但是兼容性差, |
6.2 节点概述
一般的,节点至少拥有nodeType(节点类型),nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType 为1
- 属性节点 nodeType 为2
- 文本节点 nodeType 为3(文本节点包含文字,空格,换行等)
- 注:我们在实际开发中,节点操作的是元素节点
6.3 节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是 父 子 兄 的层级关系间 DOM 树
6.3.1 父节点获取(element.parentNode)
栗子(important):
<body>
<div class="box">
<span class="erweima"></span>
</div>
<script>
/* 1. 父节点 parentNode */
var erweima = document.querySelector('.erweima');
/* 2. 得到的是离元素最近的父节点,如果找不到就返回空 */
console.log(erweima.parentNode);
</script>
</body>
6.3.2 子节点获取(element.childNodes)
- parentNode.childNodes (标准)
parentNode.childNodes返回包含指定节点的子元素的集合,该集合为即时更新的集合
注意:
- 返回值里面包含了所有的子节点,包括元素节点,文本节点等
- 如果只想要获得里面的元素节点,则需要专门处理,所以我们一般不提倡使用childNodes
- 如果非要用那么要加一个条件判断(利用 元素节点 nodeType 为1这一性质)
<body>
<ul>
<li>我很帅</li>
<li>我很帅</li>
<li>我很帅</li>
<li>我很帅</li>
</ul>
<script>
var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
// ul.childNodes[i] 是元素节点
console.log(ul.childNodes[i]);
}
}
</script>
</body>
- parentNode.children (非标准) (庶出)但是却被广泛的应用(各个浏览器都支持)
<body>
<ul>
<li>我很帅</li>
<li>我很帅</li>
<li>我很帅</li>
<li>我很帅</li>
</ul>
<script>
/* DOM提供的方法 (API) 获取 */
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
/* 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等 */
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
/* 2. children 获取所有的子元素节点 也是实际开发中常用的 */
console.log(ul.children);
</script>
</body>
6.3.3 第一个和最后一个子节点的获取
- element.firstChild :第一个子节点 不管是文本节点还是元素节点
- element.lastChild:同理
- element.firstElementChild :返回第一个子元素节点,找不到返回null
- element.lastElementChild : 同理
注:3 4 种方法有兼容性问题,IE9 以上才支持
实际开发的写法 既没有兼容性问题又返回第一个子元素 : console.log(ul.children[0]);
实际开发的写法 既没有兼容性问题又返回最后一个子元素 : console.log(ul.children[ul.children.length -1]);
栗子:
<body>
<ul>
<li>我很帅1</li>
<li>我很帅2</li>
<li>我很帅3</li>
<li>我很帅4</li>
</ul>
<script>
var ul = document.querySelector('ul');
/* firstChild 第一个子节点 不管是文本节点还是元素节点 */
console.log(ul.firstChild);
/* 同上 */
console.log(ul.lastChild);
/* firstElementChild 返回第一个子元素节点,找不到返回null*/
console.log(ul.firstElementChild);
/* lastElementChild 返回最后一个子元素节点,找不到返回null*/
console.log(ul.lastElementChild);
/* 实际开发的写法 既没有兼容性问题又返回第一个子元素 */
console.log(ul.children[0]);
/* 实际开发的写法 既没有兼容性问题又返回最后一个子元素 */
console.log(ul.children[ul.children.length - 1]);
</script>
</body>
6.3.4兄弟节点
- node.nextSibling : 返回下一个兄弟节点,找不到返回null,包含所有的节点
- node.previousSibling : 返回上一个兄弟节点,找不到返回null,包含所有的节点
- node.nextElementSibling : 返回当前元素下一个兄弟节点(元素节点),找不到则返回null
- node.previousElementSibling : 返回当前元素上一个兄弟节点(元素节点),找不到则返回null
注:3 4 种方法有兼容性问题,IE9 以上才支持
栗子:
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
/* 得到了文本节点,下一个兄弟节点 包含元素节点或者文本节点等等*/
console.log(div.nextSibling);
/* 得到了文本节点,上一个兄弟节点 包含元素节点或者文本节点等等*/
console.log(div.previousSibling);
console.log(div.nextElementSibling);
console.log(div.previousElementSibling);
</script>
</body>
6.4 创建节点,添加节点
- document.createElement(‘element’) : 创建一个元素
- 父级.appendChild(变量); : 添加在后面
- 父级.insertBefore(变量, 父级.children[0]) : 传入变量,和要插入的位置
<body>
<ul>
<li>123</li>
</ul>
<script>
/* 1. 创建节点元素 */
var li = document.createElement('li');
/* 2. 添加节点 node.appendChild(child) node 父级 child 子级 后面追加元素 类似于数组中的push */
var ul = document.querySelector('ul');
ul.appendChild(li);
/* 3. 添加节点 node.insertBefore(child, 指定元素); */
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
/* 4. 我们想要页面添加一个新的元素: 1. 创建元素 2. 添加元素 */
</script>
</body>
栗子
6.5 删除节点
node.removeChild(child):DOM 中删除一个子节点,删除一个子节点,返回删除的节点
<body>
<button>delate</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1. 获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 删除元素
// 3. 点击依次删除
btn.onclick = function() {
// if(ul.children.length == 0){
// this.disabled = true;
// }
for (var i = 0; i < ul.children.length; i++) {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
阻止链接跳转添加javascript:void(0);
或者javascript:;
栗子
6.6 复制节点(克隆节点)
node.cloneNode():返回调用该方法的节点的一个副本,也称为克隆节点,拷贝节点
注意:
- 如果括号参数为空或者为false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
var lili = ul.children[0].cloneNode(true);
ul.insertBefore(lili, ul.children[0]);
</script>
</body>