DOM基础详细 包含多个案例
关于DOM的高级事件请查看DOM的高级事件
DOM基础 详细
文档对象模型,是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口
W3C已经定义了一些系列的DOM接口,通过这些接口可以改变网页的内容、结构和样式
DOM树
- 文档:一个网页就是一个文档,DOM中使用document表示
- 元素:页面中的所有标签都是元素,DOM中使用element表示
- 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),使用node表示
DOM把以上内容都看作对象
获取元素
因为我们文档页面从上往下加载,所以先有标签,我们将js卸载标签下main
如何获取元素
DOM在我们实际开发中主要用来操作元素
获取页面元素可以使用以下几种方法
- 根据ID获取
- 根据标签名获取
- 通过HTML5新增的方法获取
- 特殊元素获取
根据ID获取
使用getElementById()
方法可以获取带有ID的元素对象
id是大小写名敏感的字符串,代表了所要查找的元素的唯一ID
返回值:返回一个匹配到ID的DOM Element对象。若在文档中没有找到对象,则返回null
<div id="time">2021-7-21</div>
<script>
var timer = document.getElementById('time');//document(文档),指在文档中选出该元素
console.log(timer);//<div id="time">2021-7-21</div>
//console.dir()打印我们返回的元素对象,更好的查看里面的属性和方法
console.dir(timer)
</script>
根据标签名获取
返回的是 获取过来元素对象的集合,以伪数组的形式存储
使用getElementsByTagName()
方法可以返回带有指定标签名的对象的集合
<ul>
<li>知否知否,应是绿肥红瘦1</li>
<li>知否知否,应是绿肥红瘦2</li>
<li>知否知否,应是绿肥红瘦3</li>
<li>知否知否,应是绿肥红瘦4</li>
</ul>
<script>
var list = document.getElementsByTagName('li');
console.log(list);
console.log(list[0]);
</script>
注意:
- 因为得到的是一个对象的集合,所以我们向要操作里面的元素就需要遍历
- 得到的元素对象是动态的
- 如果取到的元素只有一个,返回的还是位数组
- 如果页面中没有这个元素,返回的是空的位数组
从父类中获取元素
element.getElementsByTagName('标签名')
伪数组不能作为父元素
父元素必须是单个对象(必须指明是哪个元素对象),获取的时候不包括父元素自己
<ul>
<li>知否知否,应是绿肥红瘦1</li>
<li>知否知否,应是绿肥红瘦2</li>
<li>知否知否,应是绿肥红瘦3</li>
<li>知否知否,应是绿肥红瘦4</li>
</ul>
<ol>
<li>你</li>
<li>我</li>
</ol>
要求:选取ol中的li
代码:
<script>
var ol = document.getElementsByTagName('ol');
for(var i = 0; i < ol.length; i++)
console.log(ol[i].getElementsByTagName('li'));
</script>
通过HTML5新增的方法获取
类名获取
document.getElementsByClassName('类名')
找到的依旧是一个伪数组
选择器单个获取
document.querySelector('选择器')
根据指定的选择器返回第一个元素,不是一个伪类数组
选择器多个获取
document.querySelectorAll('选择器')
根据指定选择器返回全部的元素的集合,是一个伪数组
获取特殊元素
-
获取body元素
document.body
-
获取html元素
document.documentElement
事件基础
JavaScript使我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为
简单理解:触发–响应机制
网页中的每一个元素都可以产生某些可以触发JavaScript的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作
事件的组成
事件源:事件被触发的对象,比如:按钮
事件类型:如何触发,什么事件,比如鼠标点击(onclick)
事件处理程序:通过一个函数赋值的形式完成
<button id="btn">唐伯虎</button>
<script>
var btn = getElementById('btn');
btn.onclick = function(){
alert('点秋香');
}
</script>
事件源.事件类型 = function(){
事件处理程序
}
鼠标事件类型:
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获取鼠标焦点触发(焦点就是光标的位置) |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触动 |
操作元素
JavaScript的DOM操作可以改变页面内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等
改变元素的内容
element.innerText
从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉
innerText不能识别html标签,所以在修改的时候不能加入html标签
<p>1589</p>
<script>
function getDate(){
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
var day = date.getDay();
return '今天是' + 'year' + '年' + 'month' + '月' + 'dates' + '日' + arr[day];
}
var p = document.querySelector('p')
p.innerText =getDate();
</script>
那么打开网页就显示当前的时间
element.innerHTML
可以识别html标签
起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行
修改src(其他的类似)
<botton id='ldh'>刘德华</botton>
<botton id='zxy'>张学友</botton>
<img src="imges/ldh.jpg">
<script>
var ldh = document.getElementById('ldh');
var zxy = document.getElementById('zxy');
var img = document.querySelector('img');
zxy.onclic = function(){
img.src = 'imges/zxy.jpg';
}
ldy.onclick = function(){
img.src = 'imges/zxy.jpg';
}
</script>
根据时间,显示不同的问候语和图片
<img src="imges/s.gip">
<div>上午好</div>
<script>
var img = querySelector('img');
var div = querySelector('div');
var date = new Date();
var hour = date.getHours();
if(hour < 12){
img.src = 'imges/s.gip';
div.innerText = '上午好';
}else if (hour <18){
img.src = 'imges/z.jpg';
div.innerText = '下午好';
}else if{
img.src = 'imges/w.jpg';
div.innerText = '晚上好';
}
</script>
表单元素的修改
<button>按钮</button>
<input type="text" value="输入内容">
<script>
var btn = document.querySelector('botton');
var input = document.querySelector('input');
btn.onclick = function(){
//input.innerText = '点击了';这个是普通标签使用的,但是表单标签不适用
input.value = '点击了';
//如果想要某个表单被禁用,不能再次点击,我们可以修改disable
btn.disable = true;//或者为this.disable = true;
}
</script>
表单属性不在使用,可以将disable改为true
函数代码中的this指向函数的调用对象
显示和隐藏密码
- 核心思路:点击眼睛按钮,把密码框类型改为文本框就可以看到里面的密码
- 一个按钮两个状态,点击一次,切换为文本,继续点击一次切换为密码框
- 算法:利用一个flag变量,来判断flag的值,如果为1就切换为文本框,flag设置为0,如果是0就切换为密码框,flag设置为1
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<style type="text/css">
.box{
width: 400px;
border-bottom: 5px solid #E7E3E3;
margin: 100px auto;
position: relative;
}
.box>input{
height: 30px;
width: 370px;
border: 0px;
outline: none;
}
img{
height: 10px;
width: 24px;
position: absolute;
top: 10px;
right: 10px;
}
</style>
</head>
<body>
<div class="box">
<input type="password" name="">
<img src="./img/01.jpg">
<script type="text/javascript">
var img = document.querySelector('img');
var input = document.querySelector('input');
var flag = 1;
img.onclick = function(){
if(flag == 1){
input.type = 'text';
flag = 0;
}else{
input.type = 'password';
flag = 1;
}
}
</script>
</div>
</body>
</html>
操作元素的大小、颜色及样式
我们可以通过js修改元素的大小、颜色、位置等样式
1.element.style 行内样式操作
2.element.className 类名样式操作
行内样式操作
注意:
- js中样式采用驼峰命名法,比如fontSize、backgroundColor
- js修改style样式操作,产生的是行内样式,css权重比较高
<style>
div{
background-color:black;
}
</style>
<html>
<div></div>
</html>
<script>
var div = document.querySelector('div');
div.onclick = function(){
this.style.backgroundColor = 'purple';
}
</script>
循环精灵图
精灵图:就是一张图有一列的图标
在循环精灵图的时候,在css中要先有精灵图的url
在遍历的时候,图片的位置就是相当于在ps中的x,y轴中的位置,一般每个图标的大小都是一致的,所以方便遍历
<style>
li{
background:url(images/sprite.png) no-repeat;
}
</style>
<script>
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++){
var index = i * 44;
lis[i].style.backgroundPosition = '0'+ '-' + index + 'px'
}
</script>
显示隐藏文本框内容
案例分析:
-
首先表单需要2个新事件,获取焦点onfocus,失去焦点onblur
-
如果获得焦点,判断表单里面的内容是否为默认文字,如果为默认文字,就清空表单内容
-
如果失去焦点,判断表单内容是否为空,如果为空,则表单内容改为默认文字
<!DOCTYPE html> <html> <head> <title>test</title> <style type="text/css"> input{ color: #999; } </style> </head> <body> <div class="box"> <input type="text" name="" value="收集"> <script type="text/javascript"> var input = document.querySelector('input'); //获取焦点时 input.onfocus = function(){ if (input.value === '收集'){ input.value = ''; //将文字加深 this.style.color = '#333'; } } //失去焦点时 input.onblur = function(){ if (this.value === ''){ this.value = '收集'; //将文字变浅 this.style.color = '#999' } } </script> </div> </body> </html>
使用className修改样式属性
注意:
- 如果样式修改较多,可以采取操作类名方式更改元素样式
- class因为是个保留字,因此使用className来操作元素类名属性
- className会直接更改元素的类名,会覆盖原先的类名
<!DOCTYPE html>
<html>
<head>
<title>none</title>
<style>
.change1{
background-color:red;
height:70px;
margin-top:90px;
}
.change2{
background-color:pink;
height:10px;
}
</style>
</head>
<body>
<div class="change1"></div>
<script>
var div = document.querySelector('div');
div.onclick = function(){
this.className = 'change2';
}
</script>
</body>
</html>
密码框验证信息
- 首先判断的事件是表单失去焦点onblur
- 如果输入正确则提示正确的信息颜色为绿色小图标变化
- 如果输入不是6位到16位,则提示错误信息颜色为红色小图标变化
- 因为里卖弄变化的样式较多,我们采用className修改样式
<!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>
div{
width: 600px;
margin: 100px auto;
}
.message{
display: inline-block;
font-size: 12px;
color: #999;
background: url(taobao.PNG) no-repeat;
padding-left: 25px;
}
.wrong{
color: red;
}
.right{
color: green;
}
</style>
</head>
<body>
<div class="register">
<input type="password" class="ipt">
<p class="message">请输入6~16位密码</p>
</div>
<script>
var ipt = document.querySelector('.ipt');
var message = document.querySelector('.message');
ipt.onblur = function(){
if(this.value.length <6 || this.value.length > 16)
{
message.className = 'message wrong';//增加class值 而不是粗暴的修改
message.innerHTML = '你输入的位数不对';
}else{
message.className = 'message right';
message.innerHTML = '你输入的位数正确';
}
}
</script>
</body>
</html>
在修改class名的时候,在原来类名的基础上加一个,使一个元素拥有两个相同的属性和新加入类名的覆盖属性
排它思想
如果有同一组元素,我们想要某一个元素实现某种样式,需要用到循环的排他思想算法:
- 所有元素全部清除样式
- 给当前元素设置样式
- 注意顺序不能颠倒,首先干掉其他人,再设置自己
点击图片修改背景
<!DOCTYPE html>
<html>
<head>
<title>换皮肤</title>
<style type="text/css">
.imgLine{
margin: 500px auto;
padding-left:500px;
}
.imgLine>img{
width: 100px;
height: 100px;
}
body{
background-image: url('./img/01.jpg');
}
</style>
</head>
<body>
<div class="imgLine">
<img src="./img/01.jpg" class="firstImg">
<img src="./img/02.jpg" class="secondImg">
</div>
<script type="text/javascript">
var img = document.querySelectorAll('img');
console.log(img);
for (let i = 0; i < img.length; i++){
img[i].onclick = function(){
console.log(this.src);
document.body.style.backgroundImage = 'url(' + this.src+ ')';
}
}
</script>
</body>
</html>
全选和单选
案例分析
-
全选和取消全选做法:让下面所有的复选框的checked属性(选中状态)跟随全选按钮即可
-
当单选全部勾选的时候,全选的选框自动勾选,当失去个别单选选框的时候,全选选框失去勾选
<!DOCTYPE html>
<html>
<head>
<title>全选和单选</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th><input type="checkbox" name="choseAll" id="choseAll"></th>
<th>商品</th>
<th>价格</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="goodList"></td>
<td>商品1</td>
<td class="price">1000</td>
</tr>
<tr>
<td><input type="checkbox" name="goodList"></td>
<td>商品2</td>
<td class="price">20000</td>
</tr>
<tr>
<td><input type="checkbox" name="goodList"></td>
<td>商品3</td>
<td class="price">100</td>
</tr>
</tbody>
</table>
<button>确定</button>
<a>最后总共消费0元</a>
<script type="text/javascript">
var chose_all = document.getElementById('choseAll');
var inputList = document.getElementsByName('goodList');
var price = document.querySelectorAll('.price');
var submit = document.querySelector('button');
var result = document.querySelector('a');
//全选或者取消全选
chose_all.onclick = function(){
for (let i = 0; i < inputList.length; i++) {
inputList[i].checked = this.checked;
}
}
//单选对全选的影响
flag = false;
for (let i = 0; i < inputList.length; i++) {
inputList[i].onclick = function(){
for (let i = 0; i < inputList.length; i++) {
if(inputList[i].checked == true){
flag = true;
}else{
flag = false;
break;
}
}
chose_all.checked = flag;
}
}
submit.onclick = function(){
let sum = 0;
for (let i = 0; i < inputList.length; i++) {
if(inputList[i].checked == true){
sum += parseInt(price[i].innerText);
}
}
result.innerText = '最后总共消费' + sum + '元';
}
</script>
</body>
</html>
自定义属性
自定义属性目的:为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中
在H5规定中自定义属性data-
开头为属性名并且赋值
例如:<div data-index = "1"></div>
获取自定义属性
-
element.属性
<html> <div id="demo"> 哈哈哈哈 </div> </html> <script> var div = querySelector('div'); console.log(div.id); </script>
-
element.getAttribute(‘属性’)
get得到获取attribute属性的意思
<html> <div id="demo"> 哈哈哈哈 </div> </html> <script> var div = querySelector('div'); console.log(div.getAttribute('id')); </script>
区别:
- element.属性 获取内置属性值(元素自带的属性)
- element.getAttribute(‘属性’) 主要获取自定义的属性,我们程序员自定义的属性
获取所有自定义属性:
element.dataset
,返回的是一个集合
**如果自定义属性里面有多个-
连接的单词,此时我们使用驼峰命名法 **
<div data-index = "2" data-list-name = "andy">1</div>
<script>
console.log(div.dataset);//返回的是一个集合
console.log(div.dataset['index']);//返回data-index的属性值
console.log(div.dataset.index);
console.log(div.dataset.listName);
</script>
设置自定义属性
element.setAttribute('属性')
<html>
<div id="demo" index="1">
哈哈哈哈
</div>
</html>
<script>
var div = querySelector('div');
div.setAttribute('属性','值');
</script>
移除属性
element.removeAttribute('属性')
tab栏切换
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="./CSS/reset.css">
<style type="text/css">
.tabBox{
margin: 100px;
position: relative;
}
ul li {
float: left;
width: 100px;
height: 30px;
line-height: 30px;
text-align: center;
background-color: #fff;
border: 1px #bbb solid;
border-bottom: none;
cursor: pointer;
}
#tab_con{
position: absolute;
top: 31px;
}
.onclickStyle{
background-color: red;
color: #fff;
}
</style>
</head>
<body>
<div class="tabBox">
<ul id="tab">
<li class="onclickStyle">tab1</li>
<li>tab2</li>
<li>tab3</li>
</ul>
<div id="tab_con">
<div >aaaa</div>
<div style="display:none">bbbb</div>
<div style="display:none">cccc</div>
</div>
</div>
<script type="text/javascript">
var liList = document.querySelectorAll('ul>li');
var divList = document.querySelectorAll('#tab_con>div');
for (let i = 0; i < liList.length; i++) {//注意此处不能使用var定义,var定义之后,i是全局变量,使tab栏内容无法出现
liList[i].onclick = function(){
for (let i = 0; i < liList.length; i++) {
liList[i].className = '';
divList[i].style.display = 'none';
}
this.className = 'onclickStyle';
divList[i].style.display = 'block';
}
}
</script>
</body>
</html>
节点操作
利用结点层级关系获取元素
- 利用父子兄关系获取元素
- 逻辑性强,但是兼容性稍差
节点概述
一般地,节点至少拥有节点类型、节点名称、节点值这三个基本属性
- 元素节点 nodeType 为1
- 属性节点 nodeType 为2
- 文本节点 nodeType 为3(文本节点包含文字、空格、换行)
我们实际开发中,节点操作主要操作的是元素节点
节点层级
父级节点
element.parentNode
<html>
<div class="box">
<span class="erweima">x</span>
</div>
</html>
在此代码中,div
和span
是父子关系
//选择器选择元素
var erweima = document.querySelector('.erweima');
var box = document.querySelector('.box');
//使用节点选择元素
var erweima = document.querySelector('.erweima');
var box = erweima.parentNode;
- parentNode属性可返回某个节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回null
子级节点
element.childNodes
element.children
注意:childNodes获得的是所有子节点,包含元素节点、文本结点等等;children是获取所有的子元素节点
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
console.log(ul.childNodes);//NodeList(9)
console.log(ul.children)//HTMLCollection(4)
</script>
ul下li的个数只有4个,但是集合中却出现了9个,这是因为 使用childNode选取的节点包括换行(文本节点),所以加起来总共有9个
如果需要使用childNodes选取所有元素节点,这就需要for循环
var ul = document.querySelector('ul');
for(let i = 0; i < ul.childNodes.length; i++){
if (ul.childNodes[i].nodeType == 1){
//ul.childNodes[i]是元素节点
console.log(ul.childNodes[i]);
}
}
子节点补充:
- firstChild 返回第一个子节点(不管是文本节点还是元素节点)
- lastChild 返回最后一个子节点(不管是文本节点还是元素节点)
- firstElementChild 返回第一个子元素节点
- lastElementChild 返回最后一个子元素节点
最后两个方法有兼容性问题,IE9以上才支持
兄弟节点
element.nextSibling
返回当前元素的下一个兄弟节点(包括文本节点和元素节点)
element.previousSibling
返回当前元素的上一个兄弟节点(包括文本节点和元素节点)
element.nextElementSibling
返回当前元素下一个兄弟元素节点
element.previousElementSibling
返回当前元素上一个兄弟元素节点
节点操作
创建节点
document.createElement('tagName')
该方法创建由tagName指定的HTML元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们称为动态创建元素节点
添加节点
-
node.appendChild(child)
node 父级 child子级node.appendChild()方法将一个节点添加到指定父节点的节点列表末尾,类似于css中的after伪元素
var ul = document.querySelector('ul'); var li = document.createElement('li');//创建li元素 ul.appendChild(li);//将li元素放在ul下的列表末尾
-
node.insertBefore(child,指定元素)
该方法是将一个节点添加到父节点的指定子节点的 前面,类似css中的before伪元素
删除节点
node.removeChild(child)
从DOM中删除一个子节点,返回删除的节点
复制节点
node.cloneNode()
node.cloneNode()方法返回调用该方法的节点的一个副本,也称为克隆节点/拷贝节点
注意:()括号为空或者为false,那么为浅拷贝,只复制标签,不复制里面的内容,括号中为true,复制标签及里面的内容
简单版发表留言
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>简单版发表留言</title>
<style type="text/css">
textarea {
resize: none;
height: 65px;
display:block;
margin: 70px auto;
font-size: 15px;
color: #B8B8B8;
border: 5px black solid;
background-color: #E1E1E1;
}
button {
width: 50px;
height: 50px;
position: relative;
top: -140px;
left: 1100px;
background-color: #99DEE0;
color: #ffffff;
}
.box{
width: 520px;
margin: 30px auto;
border: 3px black solid;
}
li{
width: 300px;
background-color: #4FD6DD;
margin-bottom: 5px;
}
</style>
</head>
<body>
<textarea rows="10" cols="70">请发表一条善意的言论</textarea>
<button>发表</button>
<div class="box">
<ul>
评论区:
</ul>
</div>
<script type="text/javascript">
var text = document.querySelector('textarea');
var button = document.querySelector('button');
var ul = document.querySelector('ul');
text.onfocus = function(){
if (this.value === '请发表一条善意的言论'){
this.value = '';
this.style.backgroundColor = '#fff';
}
}
text.onblur = function(){
if (this.value == ''){
this.value = '请发表一条善意的言论';
}
}
button.onclick = function(){
if(text.value !== ''){
var li = document.createElement('li');
li.innerText = text.value;
ul.insertBefore(li,ul.children[0]);
}
}
</script>
</body>
</html>
动态表格
案例分析
-
因为数据是动态的,我们需要js动态生成,这里我们模拟数据,自己定义好数据。数据我们采用对象形式存储
-
所有的数据都是放在tbody里面的行里面
-
因为行很多,我们需要循环创建多个行(对应多少人)
-
每个行里面又有很多单元格(对应里面的数据),我们还继续使用循环创建多个单元格
-
最后一列单元格式删除,需要单独创建单元格
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> thead tr{ background-color:#CAC8C8; font-size:20px; } th{ width: 70px; } </style> </head> <body> <table border="1"> <thead> <tr> <th>姓名</th> <th>班级</th> <th>成绩</th> <th>操作</th> </tr> </thead> <tbody> </tbody> </table> <script type="text/javascript"> // 1.创建数据 var datas = [{ name : '小明', class : 1, score : 98 }, { name : '小红', class : 2, score : 88 }, { name : '小刚', class : 3, score : 100 }]; // 2.往tbody中创建行,有几个人创建几行(由数组的长度决定) var tbody = document.querySelector('tbody'); for (let i = 0; i < datas.length; i++) { let tr = document.createElement('tr'); tbody.appendChild(tr); // 3.创建单元格,单元格的数量取决于每个对象中属性个数 for (let j in datas[i]) {//获取到的是对象的键,而不是值 let td = document.createElement('td'); td.innerText = datas[i][j]; tr.appendChild(td); } let td = document.createElement('td'); td.innerHTML = '<a href="javascript:;">删除</a>'; tr.appendChild(td); } // 4.删除操作 var all_remove = document.querySelectorAll('a'); for (let i = 0; i < all_remove.length; i++) { all_remove[i].onclick = function() { tbody.removeChild(this.parentNode.parentNode); } } </script> </body> </html>