一、DOM简介
1.什么是DOM
文档对象模型,是W3C组织推荐的处理可扩展标记语言(HTML和XMI)的标准编程接口。
W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。
2.DOM树
文档:一个页面就是一个文档,DOM中使用document表示。
元素:页面中的所有标签都是元素,DOM中使用element表示。
节点:页面中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示
DOM把以上内容都看作是对象
二、获取元素
1.如何获取页面元素
获取页面中的元素可以使用以下几种方式:
a.根据ID获取
使用getElementById()方法可以获取带有ID的元素对象。
<body>
<div id="timer">2019-9-9</div>
<script>
//1.文档从上往下加载,js写在下面
//2.get获得element元素by通过 驼峰命名法
//3.参数 id 区分大小写
//4.返回的是一个元素对象
var timer = document.getElementById("timer");
console.log(timer);
console.log(typeof timer);
//5.console.dir 打印我们返回的元素对象 更好的查看里面属性和方法
console.dir(timer);
</script>
</body>
b.根据标签名获取
getElementsByTagName()方法可以返回带有指定标签名的对象的集合
<body>
<ul>
<li>知否知否,应是等你好久!1</li>
<li>知否知否,应是等你好久!2</li>
<li>知否知否,应是等你好久!3</li>
<li>知否知否,应是等你好久!4</li>
<li>知否知否,应是等你好久!5</li>
</ul>
<ul id = "nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
//1.返回的是获取过来元素对象的集合 以伪数组形式存储
var lis = document.getElementsByTagName("li");
console.log(lis);
console.log(lis[0]);
//2.我们想要依次打印里面的元素对象我们可以采取遍历的方式
for(var i = 0; i <lis.length; i++){
console.log(lis[i]);
}
//3.element.getElementsByTagName() 可以得到这个元素里面的某些
var nav = document.getElementById('nav');
var navLis = nav.getElementsByTagName('li');
console.log(navLis);
</script>
</body>
注意:
(1)因为得到的是一个对象的集合,所以我们操作里面元素就需要遍历。
(2)得到元素对象是动态的
c.通过HTML5新增的方法获取
1.getElementsByClassName(‘类名’);根据类名返回元素对象集合。
2.document.querySelector(‘选择器’);根据指定选择器返回第一个元素对象。
3.document.querySelectorAll(‘选择器’);根据指定选择器返回。
<body>
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
//1.getElementsByClassName 根据类名获得某些元素的1集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
//2.querySelector 返回指定选择器的第一个元素对象 切记里面的选择器要加符号 .box #nav
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.querySelectorAll() 返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
</body>
d.特殊元素获取
获取body元素
document.body;
获取html元素
document.documentElement;
<body>
<script>
//1.获取body元素
var bodyEle = document.body;
console.log(bodyEle);
console.dir(bodyEle);
//2.获取html元素
var htmlEle = document.documentElement;
console.log(htmlEle);
</script>
</body>
三、事件基础
1.事件概述
JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。
简单理解:触发——响应机制。
网页中的每个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
2.执行事件步骤
(1)获取事件源
(2)注册事件(绑定事件)
(3)添加事件处理机制(采取函数赋值形式)
3.常见的鼠标事件
四、操作元素
1.DOM操作可以改变网页内容、结构和样式,我们可以利用DOM元素来改变元素里面的内容、属性等。
2.改变元素内容
element.innerText
从起始到终止位置的内容,但它去除html标签,同时空格和换行也会去掉;
element.innerHTML
起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行。
<body>
<button id="dyz">第一张</button>
<button id="dez">第二张</button><br>
<img src='beauty_20200313143156.jpg' title="这是第一张图片">
<script>
//修改元素属性
//1.获取元素
var dyz = document.getElementById('dyz');
var dez = document.getElementById('dez');
var img = document.querySelector('img');
//2.注册事件 处理程序
dez.onclick = function(){
img.src = "beauty_20200313143213.jpg";
img.title = "这是第二张图片";
}
dyz.onclick = function(){
img.src = "beauty_20200313143156.jpg";
img.title = "这是第一张图片";
}
</script>
</body>
<body>
<img src="beauty_20200313143156.jpg">
<div>上午好</div>
<script>
//根据系统时间不同来判断,所以需要用到日期内置对象
//利用多分支语句来设置不同图片
//需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
//需要一个div元素,显示不同的时间问候语,修改元素内容即可
//1.获取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
//2.得到当前的小时数
var date = new Date();
var h = date.getHours();
//3.判断小时数改变图片和文字信息
if(h < 12){
img.src = "beauty_20200313143156.jpg";
div.innerHTML = '亲,上午好,好好写代码';
} else if(h < 18){
img.src = "beauty_20200313143213.jpg";
div.innerHTML = '亲,下午好,好好写代码';
}else {
img.src = "IMG_20190930_204918.jpg";
div.innerHTML = '亲,晚上好,好好写代码';
}
</script>
</body>
3.表单元素的属性操作
利用DOM可以操作如下表单元素的属性:
type、 value、 checked、 selected、 disabled、
<body>
<button>按钮</button>
<input type="text" name="" value="输入内容">
<script>
//1.获取元素
var btn = document.querySelector('button');
var input = document.querySelector('input');
//2.注册事件 处理程序
btn.onclick = function(){
//input.innerHTML = '点击了';这个是普通盒子 比如 div 标签里面的内容
//表单里面的值 文字内容是通过value来修改的
input.value = '被点击了';
//如果想要某个表单被禁用 不能再点击 disabled 我们想要这个按钮 button禁用
// btn.disabled = true;
this.disabled = true;
//this 指向的是事件函数的调用者 btn
}
</script>
</body>
密码框案例:
<head>
<meta charset="utf-8">
<title></title>
<style>
.box{
position: relative;
width: 400px;
border-bottom: 1px solid #ccc;
margin: 100px auto;
}
.box input{
width: 370px;
height: 30px;
border: 0;
outline: none;
}
.box img{
position: absolute;
top: 2px;
right: 2px;
width: 24px;
}
</style>
</head>
<body>
<div class="box">
<label form="">
<img src="close.png" id="eye">
</label>
<input type="password" name="" id="pwd">
</div>
<script>
//1.获取元素
var eye = document.getElementById('eye');
var psw = document.getElementById('pwd');
//2.注册事件 处理程序
var flag = 0;
eye.onclick = function(){
if(flag == 0){
pwd.type = 'text';
flag = 1; //赋值操作
eye.src = 'open.png';
}else {
pwd.type = 'password';
flag = 0;
eye.src = 'close.png';
}
}
</script>
</body>
4.样式属性操作
我们可以通过JS修改元素大小、颜色、位置等样式。
1.element.style 行内样式
2.element.className 类样式操作
注意:(1)JS里面的样式采取驼峰命名法,比如:fontSize、backgroundColor; (2)JS修改style样式操作,产生的是行内样式,css权重比较高
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div></div>
<script>
//1.获取元素
var div = document.querySelector('div');
//2.注册事件 处理程序
div.onclick = function(){
//div.style里面的属性 采取驼峰命名法
this.style.backgroundColor = 'purple';
this.style.width = '250px';
}
</script>
</body>
<style>
.box{
position: relative;
width: 74px;
height: 88px;
border: 1px solid #ccc;
margin: 100px auto;
font-size: 12px;
text-align: center;
color: #f40;
}
.box img{
width: 60px;
margin-top: 5px;
}
.close-btn{
position: absolute;
top: -1px;
left: -16px;
width: 14px;
height: 14px;
border: 1px solid #ccc;
line-height: 14px;
font-family: Arial, Helvetica,sana-serif;
cursor: pointer;
}
</style>
</head>
<body>
<div class="box">
淘宝二维码
<img src="code.png">
<i class="close-btn">x</i>
</div>
<script>
//1.获取元素
var btn = document.querySelector('.close-btn');
var box = document.querySelector('.box');
//2.注册事件 处理程序
btn.onclick = function(){
box.style.display = 'none';
}
</script>
</body>
<style>
input{
color: #999;
}
</style>
</head>
<body>
<input type="text" name="" value="手机">
<script>
//1.获取元素
var text = document.querySelector('input');
//2.注册事件 获得焦点事件 onfocus
text.onfocus = function(){
// console.log('得到了焦点');
if(this.value === '手机'){
this.value = '';
}
//获得焦点 需要把文本框中的文字变黑
this.style.color = '#333';
}
//3.注册事件 失去焦点事件 onblur
text.onblur = function(){
// console.log('失去了焦点');
if(this.value === ''){
this.value = '手机';
}
//失去焦点 颜色变浅
this.style.color = '#999';
}
</script>
</body>
<style>
div{
width: 150px;
height: 150px;
background-color: pink;
}
.change{
background-color: purple;
color: #fff;
font-size: 25px;
margin-top: 100px;
}
</style>
</head>
<body>
<div>文本</div>
<script>
//1.使用element.style获得修改元素样式 如果样式比较少 或者功能简单的情况下使用
var test = document.querySelector('div');
test.onclick = function(){
// this.style.backgroundColor = 'purple';
// this.style.color = '#fff';
// this.style.fontSize = '25px';
// this.style.marginTop = '100px';
//让我们当前元素的类名等于 change
//2.我们可以通过修改元素 的className更改元素的样式 适用于样式较多或者功能更复杂的情况
this.className = 'change';
}
</script>
</body>
注意:
(1)如果样式修改较多,可以采取操作类名方式更改元素样式;
(2)class因为是保留字,因此使用className来操作元素类名属性;
(3)className会直接更改元素的类名,会覆盖原先的类名。
5.自定义属性的操作
(1)获取属性值
element.属性; (获取属性值)
element.getAttribute(‘属性’) ;
区别:
element.属性; 获取内置属性值(元素本身自带的属性)
element.getAttribute(‘属性’); 主要获得自定义的属性(标准)我们程序员自定义的属性
(2)设置属性值
element.属性 = ‘值’; 设置内置属性
element.setAttribute(‘属性’,‘值’);
(3)移除属性
element.removeAttribute(‘属性’);
6.H5自定义属性
自定义属性的目的:是为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获得通过getAttribute(‘属性’)获取;但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。H5给我们新增了自定义属性:
(1)设置H5自定义属性
H5规定自定义属性data-开头:
比如:
<div data-index = "1"></div>
或者使用JS设置
element.setAttribute('data-index', 2);
(2)获取H5自定义属性
1.兼容性获取:element.getAttribute(‘data-index’)
2.H5新增element.dataset.index 或者
element.dataset[‘index’] ie11以上
<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'));
console.log(div.getAttribute('data-list-name'));
//h5新增 他只能获取data- 开头的
//dataset是一个集合,里面存放了所有以data开头的自定义属性
console.log(div.dataset);
console.log(div.dataset.index);
//如果自定义属性里面有多个-连接的单词,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName);
</script>
</body>
7.节点操作
a.获取元素通常使用两种方式:
(1)利用DOM提供的方法获取元素
document.getElementById()
document.getElementsByTagName()
document.querySelector()等,
缺点:繁琐、没有逻辑性
(2)利用节点层级关系获取元素
利用父子兄弟节点关系获取元素
逻辑性强,但是兼容性稍差
b.节点概述
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
元素节点nodeType为1
属性节点nodeType为2
文本节点nodeType为3(文本节点包含文字、空格、换行符)
实际开发中,节点操作主要操作的是元素节点
c.节点层级
常见的是父子兄层级关系
(1)父级节点
node.parentNode
(2)子节点
parentNode.childNodes(标准):拿到的是所有子节点, 包含元素节点 文本节点等等;实际开发中不提倡
parentNode.children(非标准):但是得到了各个浏览器的支持,因此可以放心使用
parentNode.firstChild:firstChild返回第一个子节点,找不到则返回null,同样,也是包含所有节点
parentNode.lastChild:返回最后一个子节点,找不到则返回null,同样,也是包含所有节点
parentNode.firstElementChild和parentNode.lastElementChild都返回的是元素节点,找不到则返回null, 这两个方法都有兼容性问题,IE9以上才支持。
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<script>
var ol = document.querySelector('ol');
//1.fistChild 获取的是第一个子节点 不管是文本节点还是元素节点
console.log(ol.firstChild);
console.log(ol.lastChild);
//2.firstElementChild 返回第一个元素节点
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
//3.实际开发的写法,既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
(3)兄弟节点
node.nextSibling: 返回当前元素的下一个兄弟节点,找不到返回Null,同样包含所有节点
node.prexiousSibling:返回当前元素的上一个兄弟节点,找不到返回Null,同样包含所有节点
node.nextElementSibling: 返回当前元素的下一个兄弟元素节点,找不到返回Null
node.prexiousElementSibling:返回当前元素的上一个兄弟节点,找不到返回Null
node.nextElementSibling和node.prexiousElementSibling都存在兼容性问题,IE9以上才支持
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
//1.nextSibling 下一个兄弟节点 包含元素节点和文本节点
console.log(div.nextSibling);
console.log(div.previousSibling);
//2.nextElementSibling 得到下一个兄弟节点
console.log(div.nextElementSibling);
console.log(div.previousElementSibling);
</script>
</body>
解决方法:封装一个兼容性的函数;
function getNextElementSibling(element) {
var el = element;
while (el = el.nextElementSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
(4)创建节点
document.createElement(‘tagName’)
document.createElement(‘tagName’)方法创建由tagName指定的的HTML元素,因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。
(5)添加节点
node.appendChild(child)
node.appendChild()方法将一个节点添加到指定父节点的子节点列表末尾,类似于css里面的after伪元素。
<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>
简单留言发布:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
textarea{
width: 100px;
height: 100px;
}
ul li{
background-color: pink;
width: 300px;
margin-top: 5px;
}
</style>
</head>
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
//1.获取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
//2.注册事件
btn.onclick = function(){
if(text.value == ''){
alert('你没有输入内容!!!')
return false;
}else{
// console.log(text.value);
//(1)创建元素
var li = document.createElement('li');
//先有 li 才能赋值
li.innerHTML = text.value;
//(2)添加元素
ul.insertBefore(li,ul.children[0]);
}
}
</script>
</body>
</html>
效果图:
(6)删除节点
node.removeChild(child)
node.removeChild()方法从DOM中删除一个子节点,返回删除的节点。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
</body>
<script>
//1.获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
//2.删除元素
// ul.removeChild(ul.children[0]);
//3.点击按钮依次删除里面的子节点
btn.onclick = function() {
if (ul.children.length == 0) {
this.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</html>
(6)复制节点(克隆节点)
node.cloneNode()
node.cloneNode()方法返回调用该方法的节点的一个副本,也称为克隆节点/拷贝节点
注意:
1.如果括号里面参数为空或者false,则是浅拷贝,即只是克隆复制节点本身,不克隆里面的子节点(内容)。
2.如果括号里面为true,则是深拷贝,会复制节点本身及其里面的所有子节点
动态创建删除表格案例:
<!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>
table {
width: 500px;
margin: 100px auto;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
</style>
</head>
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
//1.准备数据
var datas = [{
name: '张三',
subject: 'javascript',
score: 100
}, {
name: '李四',
subject: 'javascript',
score: 90
}, {
name: '王五',
subject: 'javascript',
score: 95
}, {
name: '王七',
subject: 'javascript',
score: 89
}];
//2.tbody中创建行:有几个人就创建几行,通过数组长度得到
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) { //外面的for循环管行数
//创建行
var tr = document.createElement('tr');
tbody.appendChild(tr);
//行里面创建单元格 td 单元格数量取决于每个对象里面的属性个数
//for循环遍历对象
for (var k in datas[i]) { //里面的单元格管列数
var td = document.createElement('td');
//把对象里面的属性值给 td
console.log(datas[i][k]);
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
//3.创建有删除2个字的单元格
var td = document.createElement('td');
td.innerHTML = '<a href = "javascript:;">删除</a>';
tr.appendChild(td);
}
//4.删除操作
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
//点击a 删除当前a所在的行 node.removeChild(child)
tbody.removeChild(this.parentNode.parentNode);
}
}
</script>
</body>
</html>
五、三种动态创建元素的区别
- documen.write()
- document.innerHTML
- document.createElement()
区别:
1.document.write是直接将内容写入页面流,但是文档流执行完毕,则它会导致页面重绘
2.innerHTML是将内容写入某个DOM节点,不会导致页面全部重绘
3.innerHTML创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复
4.createElement()创建多个元素效率稍低一点点,但是结构清晰。