1.const let 是块级作用域 var没有块级作用域,var只有函数和全局作用域
2.const let 不存在变量声明的提前,var有声明的提前,所以const和let在声明变量或者常量 之前,是没办法获取到的,称为暂时性死区temporal dead zone
3.const let是ES6提出的,var是ES5
4.const 声明的是常量,常量不能被修改,let和var声明的是变量,可以被修改
5.const在声明时必须赋值,而let和var不需要
6.let 和const不能重复声明同一个值:如 let a=1 ; let a =2 这样是不被允许的,但var可以,最后一个var声明的值会覆盖之前的 如:var b =1 ;var b =2 console.log(b) 结果为2
API:通过JavaScript获取到标签元素,然后会自动转换为Object类型,也就是DOM对象,JS通过操作DOM对象,对网页进行功能操作。
本质就是通过JS去操作html和浏览器,也可以细分为DOM、BOM
DOM
DOM:文档对象模型,用来呈现以及与任意HTML或XML文档交互的API
简而言之是:浏览器为我们提供的一套专门用来操作网页内容的功能。
DOM树
DOM对象
DOM对象:浏览器根据html标签自动生成的一个对象
let btn = document.querySelector('button')
这样就可以拿到button了,并且是一个对象形式。对象就意味着他有方法、属性值。
所有标签的属性都可以在这个对象上面找到,如果对这个对象的属性进行修改,会自动映射到该标签上
DOM核心思想:把网页内容当做对象来处理
DOM的顶层对象是document对象,他提供的方法和属性都是用来访问和操作网页内容的:document.write(),网页所有内容都在document中
获取DOM对象
根据CSS选择器来获取DOM元素(重点)
querySelector
1.1 选择匹配的第一个元素(可能会有很多相同的标签元素,但是我只获取第一个出现的)
document.querySelector('css选择器')
包含一个或多个有效的CSS选择器字符串\
<div>测试1</div>
<div>测试2</div>
<script>
//获取 测试1 元素
let div = document.querySelector('div')
</script>
1.2 除了能够获取标签元素外,还可以根据类来获取
<div>测试1</div>
<div class = ".two">测试2</div>
<script>
let two = document.querySelector('.two')
</script>
1.3 通过css来获取
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let li3 = document.querySelector('ul li:last-child')
</script>
querySelectorAll
选择匹配多个元素
document.querySelectorAll('css选择器')
querySelector返回值是一个对象,querySelectorAll返回的是NodeList对象集合
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
let lis = document.querySelectorAll('ul li')
注:querySelector()可以获取到元素后,对他进行修改操作,而querySelectorAll()不可以直接修改,只能通过遍历的方式,一次给里面的元素做修改
document.querySelectorAll('css选择器')得到的是一个伪数组,他有长度、有索引号,但是没有pop()和push()这种数组方法。要想得到里面的每一个对象,需要通过for来获取
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let lis = document.querySelectorAll('ul li')
for(let i=0;i<lis.lengths;i++){
console.log(lis[i])
}
</script>
哪怕只有一个元素,通过querySelectAll()获取过来的也是一个伪数组,只不过里面的元素数量是1而已。
其他获取DOM元素(了解) 根据id获取一个元素
document.getElementById('nav')
根据标签获取一类元素,获取页面所有的div
document.getElementByTagName('div')
根据类名获取元素,获取页面所有类名为w的
'document.getElementByClassName('w')'
设置修改DOM内容
document.write()方法
只能将文本内容追加到</body>前面的位置,文本中包含的标签会被解析。
document.write('Hello');
document.write('<h3>你好!</h3>')
对象.innerText属性
将文本的内容添加、更新到任意标签位置,并且文本中包含的标签不会被解析
对象.innerText:标签对象中的文字
<div>pink</div>
<script>
let div = document.querySelector('div')
div.innerText = "<strong>不要pink色</strong>"
</script>
直接输出<strong>不要pink色</strong>
对象.innerHTML属性
和innerText用法几乎一样,不同的是:innerHTML可以解析标签
直接输出加粗后的不要pink色
案例
<style>
div{
display:inline-block;
width:150px;
height:30px;
border:1px solid pink;
//相对于该元素所在行的基线的垂直对齐
vertical-align:middle;
text-align:center;
line-height:30px;
}
</style>
<body>
抽中的选手:<div></div>
<script>
//随机数函数
function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1))+min
}
//声明一个数组
let arr = ['张飞','关羽','赵云']
//生成一个随机数,并作为数组的下标
let random = getRandom(0,arr.length-1)
let div = document.querySelector('div')
div.innerHTML = arr[random];
//然后从数组中删除已经抽到过的名字
//从哪开始删,删几个
arr.splice(random,1)
</script>
</body>
设置修改DOM元素属性
常见的属性比如:src、href、title等,通过src更换图片:
对象.属性 = 值
//获取元素
let pic = document.querySelector('img')
//操作元素
pic.src = './images/02.jpg'
pic.title = '这是一个图片'
案例
<img src = "./images/01.png">
<script>
1.获取图片元素
let img = document.querySelector('img')
2.随机得到图片序号
function getRandom(min,max){
return Math.floor(Math.random()* (max-min+1))+min
}
let num = getRandom(1,6)
3.完成src属性的赋值
img.src = `./images/${num}.png`
</script>
修改样式属性
还可以通过JS设置、修改标签元素的样式属性,常见有:轮播图小圆点自动更换颜色,点击按钮可以滚动图片等
通过style属性操作CSS、操作类名来控制CSS、通过classList操作类来控制CSS
通过style操作CSS
语法:对象.style.样式属性 = 值
一定记得要加单位!!!
<head>
<style>
div{
width:300px;
height:300px;
background-color:pink;
}
</style>
</head>
<body>
<div></div>
<script>
let div = document.querySelector('div');
div.style.backgroundColor = 'blue';
div.style.width = '400px';
div.style.marginTap = '100px';
</script>
</body>
注意:JavaScript中没有-这种连接符,而是采用驼峰命名法:CSS中background-color在JS中为:backgroundColor
我们修改后的样式,会变成行内形式
修改随机背景
因为background-image是body的唯一标签,因此不能向上方那样书写,而是:document.body.style,直接通过document拿到body,不再需要querySelector
<style>
body{
background-iamge:url(./images/...png)
}
</style>
<script>
function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
let num = getRandom(1,6)
document.body.style.backgroundImage = `url(./images/${num}.png)`
</script>
设置修改元素样式属性
如果修改的样式比较多,通过修改style属性会比较繁琐,我们可以通过修改css类名的形式
元素.className = 'active'
div{
}
.active{
background-color:pink;
margin-left:100px;
}
<div></div>
<script>
let box = document.querySelector('div')
box.style.width = '500px'
box.className = 'active'
</script>
如果想要给多个类就会存在问题:
box.className = 'active1'
box.className = 'active2'
只会存在active2,因为这是赋值操作,会覆盖前面的一次赋值。因此className会存在缺陷。
修改:
box.className = 'active1 active2'
querySelector也可以获取到类
<div class ="one"></div>
<div id ="two"></div>
let box1 = document.querySelector('one')
let box2 = document.querySelector('#two')
classList
通过classList操作类,来控制CSS
为了解决className容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
追加一个类:不会覆盖之前的类
元素.classList.add('类名')
删除一个类:
元素.classList.remove('类名')
切换一个类:如果我本身存在类,那么使用toggle就会将这个类删除,如果我没有这个类,那么就会追加这个类
元素.classList.toggle('类名')
.active1{
}
.active2{
}
<div class = "one"></div>
let box = document.querySelector('div')
box.classList.add('active1')
box.classList.add('active2')
box.classList.remove('active1')
box.classList.toggle('active1')
设置修改表单元素属性
表单有很多情况也是需要修改属性的,比如点击眼睛,可以看到密码,本质就是把表单类型转换为文本框,正常的有属性有取值。
表单.value = '用户名'
表单.type = 'password'
<input type = "text" value = "请输入">
<script>
let input = document.querySelector('input')
input.value = '华为'
input.type = 'password'
</script>
表单属性中,添加就有效果、移除就没有效果。一律使用布尔值表示,true:添加了该属性;false:移除了该属性
比如:disabled、checked、selected
<button disabled>按钮</button>
let btn = querySelector('button')
btn.disabled = 'false' 启用
禁用状态,不给你选(短信验证码,获取一次后,60秒内不能再次获取)
<input type = "checkbox' name = "" id = "" class = "agree">
let input = document.querySelector('.agree')
input.checked = true
定时器-间歇函数
在网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发,比如:倒计时
开启定时器:setInterval(函数,间隔时间)
作用:每隔一段时间调用这个函数,间隔时间的单位是毫秒
<script>
setInterval(function(){
每个一秒钟执行这个函数
},1000)
---------------两种写法---------------
function show(){
函数
}
不能写小括号,写了小括号表示调用
setInterval(show,1000)
</script>
函数名字不需要加小括号!!!
let timer = setInterval(show,1000)定时器返回的是定时器的序号,如果只有一个定时器,返回的就是1,如果有两个定时器,那么两个定时器分别返回1、2
关闭定时器
let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名)
let timer = setInterval(show,1000)
clearInterval(timer);
清除定时器在定时器里面完成操作
倒计时-案例
需求:按钮60秒之后才可以使用
<textarea name="" id="" cols="30" rows="10">
用户协议,测试
</textarea>
<br>
<button class = "btn" disabled>已阅读用户协议(6)</button>
<script>
let btn = document.querySelector('.btn')
let i = 6;
let timer = setInterval(function(){
i--
btn.innerHTML = `已阅读用户协议(${i})`
if(i == 0){
btn.disabled = false;
btn.innerHTML = '我同意该协议'
clearInterval(timer);
}
},1000)
</script>
焦点图-案例
<div>
<img src="./uploads/banner_1.jpg" alt="">
<div class = "text">
第一张
</div>
</div>
<script>
let data = [
{
imgSrc:'./uploads/banner_1.jpg',
title:'第一张'
},
{
imgSrc:'./uploads/banner_2.jpg',
title:'第二张'
},
{
imgSrc:'./uploads/banner_3.jpg',
title:'第三张'
},
{
imgSrc:'./uploads/banner_4.jpg',
title:'第四张'
}
]
let img = document.querySelector('img');
let i = 0
let text = document.querySelector('.text')
setInterval(function(){
i++;
img.src = data[i].imgSrc;
text.innerHTML = data[i].title
console.log(i);
if(i==data.length-1){
// 避免i++后,i的取值为1的情况
// 这样i++后,i的取值是0
i = -1
}
},3000)
</script>
事件
事件:编程时,系统内发生的动作或者发生的事情。比如:用户在网页上单击了一个按钮、鼠标经过、鼠标离开
事件监听:一旦有事件触发,就立即调用一个函数做出响应,也叫:注册事件。
语法:元素.addEventListener('事件',要执行的函数)
事件监听的三要素:
事件源:哪个DOM元素被事件触发了,要获取DOM元素
事件:用什么方式触发,比如鼠标单击click、鼠标经过mouseover等
事件调用的函数:要做什么事
事件类型要加引号,函数是点击之后再去执行,每次点击都会执行一次!
let btn = document.querySelector('button')
btn.addEventListener('click',function(){
alert('被点击了')
})
这里是获取erweima而不是img,因为我点了x后,所有的全部隐藏
* {
margin: 0;
padding: 0;
}
.erweima {
position: relative;
width: 160px;
height: 160px;
margin: 100px auto;
border: 1px solid #ccc;
}
.erweima i {
position: absolute;
left: -13px;
top: 0;
width: 10px;
height: 10px;
border: 1px solid #ccc;
font-size: 12px;
line-height: 10px;
color: #ccc;
font-style: normal;
cursor:pointer设定鼠标的形状为一只伸出食指的手,
这也是绝大多数浏览器里面鼠标停留在网页链接上方时候的样式
另外可以选择其他鼠标指针样式,
常用的有default 箭头,crosshair 十字,progress 箭头和沙漏等等
cursor: pointer;
}
<div class = "erweima">
<img src = "./images/code.png">
<i class = "close_btn">x</i>
</div>
<script>
let i = document.querySelector('.close_btn')
let erweima = document.querySelector('.erweima')
i.addEventListener('click',function(){
erweima.style.display = 'none'
})
</script>
案例-随机点名
<body>
<div>
<textarea rows="2" cols="10"></textarea>
<button>点击随机点名</button>
</div>
<script>
let names = ['刘备', '关羽', '张飞', '赵云']
let text = document.querySelector('textarea')
let btn = document.querySelector('button')
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
btn.addEventListener('click', function () {
let random = getRandom(0, names.length - 1)
text.value = names[random]
// 将抽到的元素删除
names.splice(random, 1)
if (names.length === 0) {
btn.disabled = true
alert('已经抽完啦')
}
})
</script>
</body>
随机点名-混合
<body>
<div class="box">
<h3 style="text-align: center;">随机问答</h3>
问题是:<span>马超</span>
<br>
<button class="start">开始</button>
<button class="end">结束</button>
</div>
<script>
let qs = document.querySelector('span')
let start = document.querySelector('.start')
let end = document.querySelector('.end')
let arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
//设置成全局变量,不然关闭时找不到
let btnStart = 0;
let num;
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
start.addEventListener('click', function () {
btnStart = setInterval(function () {
num = getRandom(0, arr.length - 1)
console.log(arr[num]);
qs.innerHTML = arr[num]
if (arr.length === 1) {
start.disabled = end.disabled = true
}
}, 50)
})
end.addEventListener('click', function () {
clearInterval(btnStart)
// 删除已经抽到的,再抽到同一个,你是不是有点过分了
arr.splice(num, 1)
})
</script>
</body>
事件拓宽
鼠标事件:mouseenter鼠标经过、mouseleave鼠标离开;
焦点事件:focus获得焦点、blur失去焦点;
键盘触发:Keydown键盘按下触发、Keyup键盘抬起触发;
文本事件:input用户输入事件
案例-小米搜索框
不点击搜索框没有光标,点击搜索框,边框变色、下拉框出现。
focus、blus
<body>
<div class="mi">
<input type="search" placeholder="小米笔记本">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
let input = document.querySelector('input[type=search]')
let result_list = document.querySelector('.result-list')
// 光标获取事件
input.addEventListener('focus', function () {
// 文本框变色
input.classList.add('search')
// 显示下拉菜单
result_list.style.display = 'block'
})
// 光标失去事件
input.addEventListener('blur', function () {
// 文本框去色
input.classList.remove('search')
// 隐藏下拉菜单
result_list.style.display = 'none'
})
</script>
</body>
微博输入-案例
value是有长度的:input.value.length
1.获取元素、文本域
let area = document.querySelector('#area')
let useCount = document.querySelector('.useCount')
2.绑定input输入事件 用户输入事件input
area.addEventListener('input',function(){
得到字符的长度
useCount.innerHTML = area.value.length
})
全选文本框-案例
<body>
<table>
<tr>
<th class="allCheck">
<input type="checkbox" name="" id="checkAll"> <span class="all">全选</span>
</th>
<th>商品</th>
<th>商家</th>
<th>价格</th>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米手机</td>
<td>小米</td>
<td>¥1999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米净水器</td>
<td>小米</td>
<td>¥4999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米电视</td>
<td>小米</td>
<td>¥5999</td>
</tr>
</table>
<script>
let checkAll = document.querySelector('#checkAll')
// 本质是一个数组
let checkbox = document.querySelectorAll('.ck')
// 三个按钮在伪数组里,我们需要遍历,挨着取出来,依次给值
checkAll.addEventListener('click', function () {
for (let i = 0; i < checkbox.length; i++) {
checkbox[i].checked = checkAll.checked
}
})
// 当全选按钮处于选中状态,则可以改为取消
</script>
</body>
底下的小按钮,也可以影响上面的大按钮。
分析:
1.遍历下面所有的checkbox,添加点击事件
2.在事件内部,遍历所有的checkbox状态,只要有一个为false,就会将全选状态设置为false,把文字改为全选,并且直接return退出循环
3.在循环结束,将全选的状态直接设置为true
// 小按钮的做法:同时给多个元素绑定相同事件
for (let i = 0; i < checkbox.length; i++) {
// 给每一个小按钮绑定事件
checkbox[i].addEventListener('click', function () {
for (let j = 0; j < checkbox.length; j++) {
// 看看是不是有人没有选中
if (checkbox[j].checked === false) {
checkAll.checked = false
// 退出函数
return
}
}
// 如果代码能够走出循环,那么说明全部都被选中了
checkAll.checked = true
})
}
购物车案例
表单里面的value永远都是字符串类型的!!
<input type = "text" id = "total" value = "1" readonly>
<input type = "button" value = "+" id = "add">
<input type = "button" value = "-" id = "reduce" disabled>
add.addEventListener('click',function(){
//i++会隐式转换,这里自动转换为number类型
total.value++
reduce.disabled = false
})
reduce.addEventListener('click',function(){
total.value--
if(total.value<=1){
reduce.disabled = true
}
})
<=也存在隐式转换,==也存在;但是===没有
高阶函数-回调函数
高阶函数:函数的高级应用,JavaScript中函数可以被当成【值】来对待,基于这个特性,来实现函数的高级应用。
【值】就是JavaScript中的数据,比如数值、字符串、布尔、对象等
函数表达式:
let fn = function(){}、把函数当成一个数值来赋值给fn
回调函数:
如果将函数A、作为参数、传递给函数B,我们就说:函数A是回调函数。常见的比如:
function fn(){
console.log('我是回调函数...')
}
//fn传递给了setInterval,fn就是回调函数
setInterval(fn,1000)
setInterval(function(){},1000)
box.addEventListener('click',function(){})
function fun(){}
box.addEventListener('click',fun)
环境对象this
环境对象:函数内部特殊的变量this,它代表着当前函数运行时所处的环境。
这里this(环境对象,就是个对象)指向的是window对象
function fn(){
console.log(this)
}
为什么指向的是window?因为当我们调用fn()时,完整写法是:window.fn(),window可以略写。
let btn = document.querySelector('button')
btn.addEventListener('click',function(){
console.log(this)
})
这里返回的是btn(button按钮),因为是button按钮触发的。
函数的调用方式不同,this的指向对象也不同
【谁调用,this就是谁】是判断this执行的粗略规则
排他思想
当前元素为A状态,其他元素为B状态(Tab栏)
使用:
1.干掉所有人:使用for循环
2.复活他自己:通过this或者下标找到自己或者对应的元素
点谁,谁变颜色
<style>
.pink{
background:pink
}
</style>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<script>
let btns = document.querySelectorAll('button')
//给每一个按钮都注册点击事件
for(let i = 0; i<btns.length;i++){
btns[i].addEventListener('click',function(){
//先干掉所有人
for(let j = 0;j<btns.length;j++){
btns[j].classList.remove('pink')
}
//复活我自己本身
this.classList.add('.pink')
})
}
</script>
逻辑:点谁,先通过循环把所有人先清除掉,然后具体点谁、谁在变色。每次一上来先清除。
但是上方的写法浪费资源,我们通过改进,只需要找出那个唯一的pink类,进行删除即可
for(let i = 0;i<btns.length;i++){
btns[i].addEventListener('click',function(){
//我只需要找出那个唯一的pink类,删除
let pink = document.querySelector('.pink')
pink.classList.remove('pink')
//我的
this.classList.add('pink')
})
}
案例-tab栏
<div class="wrapper">
<ul class="tab">
<li class="tab-item active">国际大牌<span>◆</span></li>
<li class="tab-item">国妆名牌<span>◆</span></li>
<li class="tab-item">清洁用品<span>◆</span></li>
<li class="tab-item">男士精品</li>
</ul>
<div class="products">
<div class="main active">
<a href="###"><img src="imgs/guojidapai.jpg" alt="" /></a>
</div>
<div class="main">
<a href="###"><img src="imgs/guozhuangmingpin.jpg" alt="" /></a>
</div>
<div class="main">
<a href="###"><img src="imgs/qingjieyongpin.jpg" alt="" /></a>
</div>
<div class="main">
<a href="###"><img src="imgs/nanshijingpin.jpg" alt="" /></a>
</div>
</div>
</div>
<script>
// 拿到li
let lis = document.querySelectorAll('.tab .tab-item')
let divs = document.querySelectorAll('.products .main')
for (let i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', function () {
// 找到以前的active,并移除;querySelector里面可以写类
//remove和add里面不可以写类符号
document.querySelector('.tab .active').classList.remove('active')
// add()里不要加类符号,添加当前点击的active
this.classList.add('active')
// 底部显示模块
document.querySelector('.products.active').classList.remove('active')
// 让div对应序号的加上active
divs[i].classList.add('active')
})
}
</script>
DOM节点
查找节点+二维码案例
关闭二维码案例:点击关闭按钮,关闭的是二维码的盒子,还需要获取erweima盒子
关闭按钮和二维码是父子关系,所以我们可以点击关闭按钮后,直接关闭二维码(父亲),不用再获取二维码的元素了。
节点关系:父节点、子节点、兄弟节点
父节点查找
parentNode属性:返回最近一级的父节点、找不到返回为null
子元素.parentNode
<div class = "father">
<div class = "son">儿子</div>
</div>
let son = document.querySelector('.son')
//省略获取父级的步骤
son.parentNode.style.display = 'none'
<body>
<div class="erweima">
<img src="./images/code.png" alt="">
<i class="close_btn">x</i>
</div>
<script>
let i = document.querySelector('.close_btn')
i.addEventListener('click', function () {
i.parentNode.style.display = 'none'
})
</script>
</body>
关闭多个二维码:
<div class = "erweima">
<span class = "close"></span>
</div>
<div class = "erweima">
<span class = "close"></span>
</div>
<div class = "erweima">
<span class = "close"></span>
</div>
<div class = "erweima">
<span class = "close"></span>
</div>
let close_btn = document.querySelectorAll('.close')
绑定多个点击事件
for(let i = 0;i<close_btn.length;i++){
close_btn[i].addEventListener('click',function(){
this.parentNode.style.visibility = 'hidden'
})
}
display:'none':隐藏了,下面的div会往上顶替位置
visibility:'hidden':避免顶替位置的情况
子节点和兄弟节点
childNodes:获得所有子节点、包括文本节点(空格、换行)、注释节点等()
children(重点):仅获得所有元素节点(不包括空格换行)、返回的还是一个伪数组(只有数组长度和下标,没有数组push方法)
<button></button>
<ul>
<li>孩子</li>
<li>孩子</li>
<li>孩子</li>
<li>孩子</li>
<li>孩子</li>
</ul>
let btn = document.querySelector('button')
let ul = document.querySelector('ul')
btn.addEventListener('click',function(){
console.log(ul.children)//返回的是存储li的伪数组
for(let i = 0;i<ul.children.length;i++){
ul.children[i].style.color='red'
}
})
下一个兄弟节点:nextElementSibling属性
上一个兄弟节点:previousElementSibling属性
<button>按钮</button>
<ul>
<li>1</li>
<li class = "test">2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let btn = document.querySelector('button')
let two = document.querySelector('.test')
btn.addEventListener('click',function(){
//two.style.color = 'red'
//让第三个变色
two.nextElementSibling.style.color = 'red'
//上一个变色
two.previousElementSibling.style.color = 'red'
})
</script>
追加节点
在很多情况下,我们要在页面中增加元素,比如:点击发布按钮,可以新增一条信息。
document.createElement('标签名'):不用写标签,但是要写标签名、比如:div、span
let div = document.createElement('div')
div.className = 'current'
let li = document.createElement('li')
还需要将写好的元素,插入到父元素的【最后一个子元素】:父元素.appendChild(要插入的元素)
let ul = document.querySelector('ul')
let li = document.createElement('li')
li.innerHTML = '我是li'
//变量不能加引号
ul.appendChild(li)
插入到父元素的【某个子元素前面】:父元素.insertBefore(要插入的元素,在哪个元素的前面)
<ul>
<li>我是大毛<li>
<li>我是二毛<li>
</ul>
let ul = document.querySelector('ul')
let li = document.createElement('li')
li.innerHTML = '我是插入'
追加节点(要插入的元素,在哪个元素前面)
ul.inserBefore(li,ul.children[1])
学成在线-案例
1.准备好空的ul结构
2.根据数据的个数,创建一个新的空的li
3.li里面添加内容、img、标题等
4.追加给ul
let ul = document.querySelector('ul')
//根据数据的个数,决定li的个数
for (let i = 0;i<data.length;i++){
//创建li
let li = document.createElement('li')
//设计好li
li.innerHTML =
`
<img src=${data[i].src} alt="">
<h4>
${data[i].title}
</h4>
<div class="info">
<span>高级</span> • <span> ${data[i].num}</span>人在学习
</div>
`
//追加给ul
ul.appendChild(li)
}
克隆和删除节点
特殊情况下,我们新增节点,按照如下操作:赋值一个原有的节点、把复制的节点放入到指定的元素内部
元素.cloneNode(布尔值)
cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值:
若为true:克隆时,会包含后代节点一起克隆
若为falese:克隆时,不包括后代节点
默认是false
<ul>
<li>1</li>
<li>2</li>
</ul>
let ul = document.querySelector('ul')
let newUI = ul.cloneNode()//默认输出的就是<ul>
let newUI = ul.cloneNode(true)//<ul>包含<li>
document.body.appendChild(newUI)
若一个页面中,某个节点已经不再需要,可以删除它。在JavaScript原生DOM操作中,要【删除元素必须通过父元素删除】。
父元素.removeChild(要删除的元素)
总结:不是自己删除,而是通过爸爸删除儿子
<button></button>
<ul>
<li>我是内容</li>
</ul>
let btn = document.querySelector('button')
let ul = document.querySelector('ul')
btn.addEventListener('click',funcion(){
父元素.removeChild(子元素)
ul.removeChild(ul.children[0])
})
如果不存在父子关系、则删除不成功;
删除节点和隐藏节点有区别的:隐藏节点是还存在的,但是删除,则从html中删除节点。
时间对象
时间对象:表示时间的对象,可以得到当前的系统时间。
创建一个时间对象,并获取时间
获得当前时间:let date = new Date(),输出date返回的就是当前系统的时间。
获得指定时间:let date = new Date('1949-10-01 18:30:00');年月日用【分隔号】隔开、时分秒用【冒号】隔开
通过上方实例化得到的时间,存在格式上的问题,需要我们自己手动修改。
getFullYear():获得4位的年份
getMonth():获得月份(取值0-11)
getDate():获取月份中的每一天,不同月份的取值也不相同
getDay():获取星期,取值0-6
getHours():获取小时,取值0-23
getMinutes():获取分钟,取值0-59
getSeconds():获取秒,取值0-59
全部依靠当前系统的时间
let date = new Date();
date.getFullYear();
date.getMonth()+1
date.getDate()
date.getDay()+1
date.getHours()+1
date.getMinutes()+1
date.getSeconds()+1
案例
按照YYYY-MM-DD HH:mm形式显示在页面
<div></div>
1.实例化
let date = new Date();{}
2.getDay()返回的是一个数字,可以巧妙运用返回的数字作为数组的下标来书写
let arr = ['周日','周一','周二','周三','周四','周五','周六']
setInterval(function(){
let year = date.getFullYear();
let month = date.getMonth()+1;
let date1 = date.getDate();
let hour = date.getHours()+1;
let minutes = date.getMinutes()+1;
let sc = date.getSeconds()+1;
let div = document.querySelector('div')
div.innerHTML =
`现在时间:
${year}-${month}-${date1}${hour}:${minutes}:${sc}${arr[day]}`
},1000)
时间戳及案例
时间戳是总的毫秒数,是独一无二的;
计算倒计时:将来的时间有一个时间戳、现在的时间有一个时间戳,他们俩相减,然后转换日期形式即可。
三种方式获取时间戳:
1.let date = new Date()->date.getTime()\
2.(推荐)简写形式:+new Date()
3.这个只能得到当前的时间戳:Date.now()
返回指定日期的时间戳
console.log(+new Date('2022-6-2zong4 12:00:00'))
转换公式:
时间戳:首先转换为秒数再计算:(未来的-现在的时间戳)/1000
天数:parseInt(总秒数/60/60/24)
小时:parseInt(总秒数/60/60%24)
分数:parseInt(总秒数/60%60)
秒数:parseInt(总秒数%60)\
毕业倒计时效果:
<div class="countdown">
<p class="next">今天是2021年8月28日</p>
<p class="title">下班倒计时</p>
<p class="clock">
<span id="hour">00</span>
<i>:</i>
<span id="minutes">25</span>
<i>:</i>
<span id="scond">20</span>
</p>
<p class="tips">
现在是18:30:00
</p>
</div>
<script>
let hour = document.querySelector('#hour')
let minutes = document.querySelector('#minutes')
let scond = document.querySelector('#scond')
time()
setInterval(time, 1000)
function time() {
// 1. 得到现在的时间戳
let now = +new Date()
// 2. 得到指定时间的时间戳
let last = +new Date('2021-8-29 18:30:00')
// 3. (计算剩余的毫秒数) / 1000 === 剩余的秒数
let count = (last - now) / 1000
// console.log(count)
// 4. 转换为时分秒
// h = parseInt(总秒数 / 60 / 60 % 24) // 计算小时
let h = parseInt(count / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
// m = parseInt(总秒数 / 60 % 60); // 计算分数
let m = parseInt(count / 60 % 60)
m = m < 10 ? '0' + m : m
// s = parseInt(总秒数 % 60); // 计算当前秒数
let s = parseInt(count % 60);
s = s < 10 ? '0' + s : s
// console.log(h, m, s)
hour.innerHTML = h
minutes.innerHTML = m
scond.innerHTML = s
}
</script>
微博发布案例
避免用户在输入的时候,在首尾不小心输入空格,我们可以采用trim()方法。具体使用:'字符串'.trim()
new Date().toLocaleString():生成本地时间
格式为:2022/6/24 下午1:04:12
<div class="w">
<!-- 操作的界面 -->
<div class="controls">
<img src="./images/9.6/tip.png" alt="" /><br />
<!-- maxlength 可以用来限制表单输入的内容长度 -->
<textarea placeholder="说点什么吧..." id="area" cols="30"
rows="10" maxlength="200"></textarea>
<div>
<span class="useCount" id="useCount">0</span>
<span>/</span>
<span>200</span>
<button id="send">发布</button>
</div>
</div>
<!-- 微博内容列表 -->
<div class="contentList">
<ul id="list"></ul>
</div>
</div>
<!-- 添加了hidden属性元素会直接隐藏掉 -->
<li hidden>
<div class="info">
<img class="userpic" src="./images/9.6/03.jpg" />
<span class="username">死数据:百里守约</span>
<p class="send-time">死数据:发布于 2020年12月05日 00:07:54</p>
</div>
<div class="content">死数据:111</div>
<span class="the_del">X</span>
</li>
<script>
// maxlength 是一个表单属性, 作用是给表单设置一个最大长度
// 模拟数据
let dataArr = [
{ uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
{ uname: '女娲', imgSrc: './images/9.5/02.jpg' },
{ uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
{ uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
{ uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
{ uname: '张良', imgSrc: './images/9.5/06.jpg' },
{ uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
{ uname: '李白', imgSrc: './images/9.5/08.jpg' },
{ uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
{ uname: '墨子', imgSrc: './images/9.5/10.jpg' },
{ uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
{ uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
{ uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
{ uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
{ uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
{ uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
{ uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
{ uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
{ uname: '露娜', imgSrc: './images/9.5/19.jpg' },
{ uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
{ uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
{ uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },
]
// 注册input事件
let text = document.querySelector('textarea')
let useCount = document.querySelector('#useCount')
text.addEventListener('input', function () {
useCount.innerHTML = text.textLength
})
let ul = document.querySelector('#list')
// 按钮事件
let send = document.querySelector('#send')
send.addEventListener('click', function () {
// 输入内容不能为空,并且防止用户输入开头结尾多余的空格
if (text.value.trim() === '') {
// 输入空格时,弹出输入内容不能空并且要让字数不变、光标不变。
text.value = ''
useCount.innerHTML = 0
return alert('内容不能为空哦')
}
// 随机数
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// 能够走到这里,说明输入的没问题
let random = getRandom(0, dataArr.length - 1)
let li = document.createElement('li')
li.innerHTML = `
<div class="info">
<img class="userpic" src=${dataArr[random].imgSrc}>
<span class="username">${dataArr[random].uname}</span>
<p class="send-time">${new Date().toLocaleDateString}</p>
</div>
<div class="content">${text.value}</div>
<span class="the_del">X</span>
`
// 在生成数据后,插入数据前,让该数据具有自己的删除功能
// 用li获取,因为一开始没有li,是后续craete出来的,用document会报错
let the_del = li.querySelector('.the_del')
the_del.addEventListener('click', function () {
ul.removeChild(li)
})
ul.insertBefore(li, ul.children[0])
// 每次输入完,将内容重置为空,字符数字也为空
text.value = ''
useCount.innerHTML = 0
})
重绘和回流(面试常问)
浏览器通过解析(Parser)HTML,生成DOM树(DOM Tree)
同时解析(Parser)CSS,生成样式规则(Style Rules)
根据DOM树和样式规则,生成渲染树(Render Tree)
进行布局Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置、大小)
进行绘制Painting(重绘):根据计算和获取的信息进行整个页面的绘制
Display:展示在页面上
回流(重排):当Render Tree中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染 部分或者全部文档的过程就是回流
重绘:由于节点(元素)的样式的改变,并不影响它在文档流中的位置和文档布局(比如修改文字颜色、背景颜色,不影响布局的),称为重绘
重绘不一定影响回流,但是回流一定会引起重绘
案例-购物车
'123abc~!@'通过parseInt('123abc!@#')可以得到其中的数值
<body>
<div class="car">
<table>
<thead>
<tr>
<th><input type="checkbox" id="all" />全选</th>
<th>商品</th>
<th>单价</th>
<th>商品数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody id="carBody">
<tr>
<td>
<input class="s_ck" type="checkbox" readonly />
</td>
<td>
<img src="./images/01.jpg" />
<p>牛奶</p>
</td>
<td class="price">5¥</td>
<td>
<div class="count-c clearfix">
<button class="reduce" disabled>-</button>
<input type="text" value="1" />
<button class="add">+</button>
</div>
</td>
<td class="total">5¥</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr>
<tr>
<td>
<input class="s_ck" type="checkbox" />
</td>
<td>
<img src="./images/01.jpg" />
<p>牛奶</p>
</td>
<td class="price">10¥</td>
<td>
<div class="count-c clearfix">
<button class="reduce" disabled>-</button>
<input type="text" value="1" />
<button class="add">+</button>
</div>
</td>
<td class="total">20¥</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr>
<tr>
<td>
<input class="s_ck" type="checkbox" />
</td>
<td>
<img src="./images/01.jpg" />
<p>牛奶</p>
</td>
<td class="price">20¥</td>
<td>
<div class="count-c clearfix">
<button class="reduce" disabled>-</button>
<input type="text" value="1" />
<button class="add">+</button>
</div>
</td>
<td class="total">40¥</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr>
<tr>
<td>
<input class="s_ck" type="checkbox" />
</td>
<td>
<img src="./images/01.jpg" />
<p>牛奶</p>
</td>
<td class="price">35¥</td>
<td>
<div class="count-c clearfix">
<button class="reduce" disabled>-</button>
<input type="text" value="1" />
<button class="add">+</button>
</div>
</td>
<td class="total">70¥</td>
<td>
<a href="javascript:" class="del">删除</a>
</td>
</tr>
</tbody>
</table>
<div class="controls clearfix">
<a href="javascript:" class="del-all">删除所选商品</a>
<a href="javascript:" class="clear">清理购物车</a>
<a href="javascript:" class="pay">去结算</a>
<p>
已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span>
</p>
</div>
</div>
<script>
// + - 删除是相同的,一一对应的 我们完全可以用一个for来遍历绑定事件
// +
let adds = document.querySelectorAll('.add')
// -
let reduces = document.querySelectorAll('.reduce')
// del
let dels = document.querySelectorAll('.del')
// 输入框input
let inputs = document.querySelectorAll('.count-c input')
\
// 单价 price 5
let prices = document.querySelectorAll('.price')
// 小计 total 5 * 2 = 10
let totals = document.querySelectorAll('.total')
// 总价元素获取
let totalResult = document.querySelector('.total-price')
// 总的数量获取
let totalCount = document.querySelector('#totalCount')
// tbody 获取过来
let carBody = document.querySelector('#carBody')
for (let i = 0; i < adds.length; i++) {
// 总价和单价是一样的
totals[i].innerText = prices[i].innerText
//1. 加号的操作
adds[i].addEventListener('click', function () {
// 点击了谁,就让对应的输入框自增就行了
inputs[i].value++
// 减号要启用
reduces[i].disabled = false
// prices[i].innerText 得到的是 5¥ parseInt('5¥') === 5
console.log(parseInt(prices[i].innerText))
// 计算小计模块
// totals[i].innerText = 单价 * 数量
// totals[i].innerText = 20
totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'
// 计算现在的总额 调用
result()
})
\
//2. 减号的操作
reduces[i].addEventListener('click', function () {
// 点击了谁,就让对应的输入框自增就行了
inputs[i].value--
// prices[i].innerText 得到的是 5¥ parseInt('5¥') === 5
// console.log(parseInt(prices[i].innerText))
// 判断如果表单里面的值 小于等于1 则,禁用按钮
if (inputs[i].value <= 1) {
this.disabled = true
}
// 计算小计模块
// totals[i].innerText = 单价 * 数量
// totals[i].innerText = 20
totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'
\
// 计算现在的总额 调用
result()
})
\
\
// 3. 删除操作
\
dels[i].addEventListener('click', function () {
// 父元素.removeChild(子元素)
// 我们要删除的是那个元素 tr 他的爸爸是 tbody
// 删除的是当前元素爸爸的爸爸 就是 tr 就是当前的tr
carBody.removeChild(this.parentNode.parentNode)
// 调用总计模块
result()
})
}
// div span ul li 标签 有文字内容 怎么得到或则设置文字内容呢 元素.innerText 元素.innerHTML
// 表单 input 单选 复选 textarea select 怎么得到或则设置值 表单的value
// 特殊的 button 是通过inner来设置
// 以前数组求和的方式 累加
// 计算总价 result 函数 把所有的小计 totals 加起来的结果
function result() {
// 小计 total 5 * 2 = 10
let totals = document.querySelectorAll('.total')
// 输入框input
let inputs = document.querySelectorAll('.count-c input')
let sum = 0
let num = 0
for (let i = 0; i < totals.length; i++) {
// sum = sum + 小计的数字 10¥
sum = sum + parseInt(totals[i].innerText)
num = num + parseInt(inputs[i].value)
}
// console.log(sum)
totalResult.innerText = sum + '¥'
// console.log(num)
totalCount.innerText = num
}
result()
</script>
\
</body>
事件对象
事件对象:这个对象里有事件触发时的相关信息,例如:鼠标点击事件中、事件对象就存了鼠标点在哪个位置等信息。
如何获取:
在事件绑定的回调函数的第一个参数,就是事件对象。一般命名为:event、ev、e
元素.addEventListener('click',function(e){
})
<button></button>
let btn = document.querySelector('button')
btn.addEventListener('click',function(e){
console.log(e)
})
事件对象常用属性
type:得到当前的事件类型(是click、还是mouseenter)
clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置(如果浏览器缩小的话,他的值和pageX就会不同)
pageX/pageY:跟文档坐标有关系
offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置;(在一个div中,鼠标离当前盒子的X和Y坐标)
key:用户按下的键盘键的值、现在不提倡使用keyCode
案例-鼠标跟随
一张图片一直跟着鼠标移动
其实就是图片(left和top值)知道鼠标的坐标就行
<style>
img {
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
\
<body>
<img src="./images/tianshi.gif" alt="">
<script>
let img = document.querySelector('img')
document.addEventListener('mousemove', function (e) {
// 不断得到当前的鼠标坐标
// console.log(e.pageX)
// console.log(e.pageY)
// 把坐标给图片
// img.style.left = '100px'
img.style.left = e.pageX - 50 + 'px'
img.style.top = e.pageY - 40 + 'px'
})
</script>
</body>
e.paegX后面必须要加px单位!!!
案例-回车发布微博
按下键盘:keydown或者keyup事件都可以
textarea.addEventListener('keyup',function(e){
//已废弃
console.log(e.keyCode())
if(e.key === 'Enter'){
//按了回车点,自动触发已经写好的发布按钮
send.click()
}
})
e.keyCode属性已经被删除了,取而代之的是:e.key
在开发中,使用键盘事件,我们更推荐使用keyup,而不是keydown,因为keydown后面的逻辑可能没有执行到,因为keydown执行的逻辑过早。
事件流
事件流:事件完整执行过程中的流动路径
捕获阶段:Document--》Element html--》Element body--》Element div
冒泡阶段:Element div--》Element body--》Element html--》Document
冒泡:当元素的事件被触发时,同样的事件(比如都是单击事件)将会在该元素的所有祖先元素中、依次被触发,这个过程就是事件的冒泡。
简单理解:当一个元素触发事件后,会一次向上调用所有父级元素的同名事件
<div class = "yeye">
<div class = "father">
<div class = "son"></div>
</div>
</div>
let fa = document.querySelector('.father')
let son = document.querySelector('.son')
fa.addEventListener('click',function(){
alert('我是父亲')
})
son.addEventListener('click',function(){
alert('我是儿子')
})
yeye.addEventListener('dblclick',function(){
alert('我是爷爷')
})
此时点击父亲,弹出我是父亲,但是点击儿子,不仅会弹出我是儿子,还会弹出我是父亲、但是并不会弹出我是爷爷,因为爷爷的事件是鼠标双击事件!!!
因为在事件中,儿子会冒泡,冒泡到父级,然后冒泡到body--》html--》document
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
addEventListener第三个参数传入true:是捕获阶段触发(很少使用)
若传入false:代表冒泡阶段触发,默认就是false
若是用L0事件(onclick、老写法)监听,则只有冒泡阶段,没有捕获阶段
阻止事件流动
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素,若想把事件就限制在当前元素内,就需要阻止事件的流动
事件对象.stopPropagation()、事件对象是function(e)中的参数e也就是event
我希望我点了儿子,只有儿子弹出窗口
fa.addEventListener('click',function(){
alert('我是父亲')
})
son.addEventListener('click',function(e){
alert('我是儿子')
//阻止冒泡
e.stopPropagation()
})
yeye.addEventListener('dblclick',function(){
alert('我是爷爷')
})
鼠标经过事件:mouseover和mouseout会有冒泡效果
鼠标经过事件:mouseenter、mouseleave没有冒泡效果(推荐)
阻止默认行为:e.preventDefault()
a标签,默认点击后就可以跳转,但是有些情况,我们是不希望他跳转的,就可以使用阻止默认行为
<a href = "www.baidu.com">跳转百度</a>
let a = document.querySelector('a')
a.addEventListener('click',function(e){
e.preventDefault();
})
此时就算点击a标签,他也不会跳转!!!
注册事件
使用addEventListener()事件监听注册:后面注册的事件【不会覆盖】前面注册的事件(同一个事件)、并且可以通过第三个参数去确定是在冒泡或者捕获阶段执行
addEventListener('click',function(e){},false)
必须使用removeEventListener()进行解绑,但是此时【不可以使用匿名函数】!!!
function (){
}
btn.addEventListener('click',add)
//移除事件,并且不可以是匿名函数
btn.removeEventListener('click',add)
事件委托
事件委托:利用【事件流】来快速完成
事件委托是给父级添加事件,而不是子级
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
不需要给每个小li注册事件了,而是把事件委托给li的父级
事件委托是给父级添加事件,而不是子级
let ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
alert('我点击了')
console.log(e.target)
//哪个被点击了,哪个变色
e.target.style.color = 'red'
})
e.target:获得真正触发事件的元素
原理:去具体的某一个li上找点击事件,结果li上面并没有点击事件,然后事件就会默认【冒泡】往上找,就会找到ul的点击事件
案例-学生信息表
不管是添加还是删除,都是往数组里面进行操作!
自动生成的id:arr[arr.length-1].stuId + 1:每次新的id都是上一次最后一个id的值+1、从而实现自增
appendChild():追加,以前存在的数据,还会连通新的数据一样、一起加入数组中
想要避免这个问题,就需要在渲染之前,先让以前的数据为空,再渲染数据:tbody.innerHTML = ''
每次录入完(渲染完)要将数据清空,下拉列表的值也清空
uname.value = age.value = salary.value =''
gender.value = '男'
city.value = '北京'
事件委托:最好是有多个相同的操作使用:比如多个删除操作,我们如果用以前的方法,需要每个tr都获取到,现在通过事件委托,通过tbody就可以了,而录入操作只有一个button,所以不需要使用事件委托
<body>
<h1>新增学员</h1>
<div class="info">
姓名:<input type="text" class="uname">
年龄:<input type="text" class="age">
性别: <select name="gender" id="" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary">
就业城市:<select name="city" id="" class="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="曹县">曹县</option>
\
</select>
<button class="add">录入</button>
</div>
\
<h1>就业榜</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
\
</tbody>
</table>
<script>
// 1. 准备好数据后端的数据
let arr = [
{ stuId: 1001, uname: '欧阳霸天', age: 19, gender: '男', salary: '20000', city: '上海' },
{ stuId: 1002, uname: '令狐霸天', age: 29, gender: '男', salary: '30000', city: '北京' },
{ stuId: 1003, uname: '诸葛霸天', age: 39, gender: '男', salary: '2000', city: '北京' },
]
\
// 获取父元素 tbody
let tbody = document.querySelector('tbody')
// 添加数据按钮
// 获取录入按钮
let add = document.querySelector('.add')
// 获取各个表单的元素
let uname = document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
// 渲染函数 把数组里面的数据渲染到页面中
function render() {
// 先干掉以前的数据 让tbody 里面原来的tr 都没有
tbody.innerHTML = ''
// 在渲染新的数据
// 根据数据的条数来渲染增加 tr
for (let i = 0; i < arr.length; i++) {
// 1.创建tr
let tr = document.createElement('tr')
// 2.tr 里面放内容
tr.innerHTML = `
<td>${arr[i].stuId}</td>
<td>${arr[i].uname}</td>
<td>${arr[i].age}</td>
<td>${arr[i].gender}</td>
<td>${arr[i].salary}</td>
<td>${arr[i].city}</td>
<td>
<a href="javascript:" id="${i}">删除</a>
</td>
`
// 3.把tr追加给 tobdy 父元素.appendChild(子元素)
tbody.appendChild(tr)
}
}
// 页面加载就调用函数
render()
\
add.addEventListener('click', function () {
// alert(11)
// 获得表单里面的值 之后追加给 数组 arr 用 push方法
arr.push({
// 得到数组最后一条数据的学号 1003 + 1
stuId: arr[arr.length - 1].stuId + 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
})
// console.log(arr)
// 重新渲染我们的函数
render()
// 复原所有的表单数据
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
})
\
\
// 删除操作, 删除的也是数组里面的数据 , 但是我们用事件委托
tbody.addEventListener('click', function (e) {
// alert(11)
// 我们只能点击了链接 a ,才会执行删除操作
// 那我们怎么知道你点击了a呢?
// 俺们只能点击了链接才能做删除操作
// console.dir(e.target.tagName)
if (e.target.tagName === 'A') {
// alert('你点击了链接')
// 删除操作 删除 数组里面的数据 arr.splice(从哪里开始删,1)
// 我要得到a的id 需要
// console.log(e.target.id)
arr.splice(e.target.id, 1)
\
// 重新渲染我们的函数
render()
}
})
网页特效篇
能用css做特效就用css做,但是有些需要通过JavaScript来触发改变!!!
手风琴效果
鼠标经过的时候会展示,又叫折叠卡片;鼠标进入:小图片宽度发生变化,鼠标离开:又恢复
//li默认的宽度是240像素
//当我们鼠标经过,当前的li宽度变大800px,其余的li变为100px
//当我们鼠标离开,所有的li全部复原(宽度240px)
let lis = document.querySelectorAll('li')
//绑定鼠标经过、离开事件
for(let i = 0;i<lis.length;i++){
lis[i].addEventListener('mouseenter',function(){
//排他思想:干掉所有人,复活我自己
for(let j = 0;j<lis.length;j++){
lis[j].style.width = 100px
}
//复活
this.style.width = '800px'
})
lis[i].addEventListener('mouseleave',function(){
//所有的全部复原
for(let j = 0;j<lis.length;j++){
lis[j].style.width = '240px'
}
})
}
排他思想:只有自己这样做。。别人都是那样的。。
滚动和加载事件
当滚动某个长页面的时候,一直往下滚,此时右边的状态栏会出现一个回到顶部的功能栏,点击回到顶部就可以回到顶部
这个效果是通过页面滚动事件来实现的,所以我们要学习。
滚动事件:滚动时就会触发(滚动1px就可以触发)、比如:固定导航栏、返回顶部
事件名:scroll
window.addEventListener('scroll',function(){
//执行的操作
})
给window或document、某个盒子里.添加scroll事件
加载事件:加载外部资源(图片、外联CSS)加载完毕时,触发的事件
有些时候需要等到页面资源加载完,处理一些事情
事件名:load
监听页面所有的资源加载完毕:给window添加load事件
window.addEventListener('load',function(){
console.log('当我输出时,说明所有资源已经全部加载完毕')
})
这句话还可以写在<head>标签的script标签的里面:
<head>
<style></style>
<script>
window.addEventListener('load',function(){
console.log(div)
})
</scrpit>
</head>
<body>
<div>
....
</div>
</body>
当初始的HTML文档被完全加载和解析完成后,DOMContentLoaded事件被触发,而无需等待样式表、图像的完全加载
事件名:DOMContentLoaded
监听页面DOM加载完毕
document.addEventListener('DOMContentLoaded',function(){
})
scroll家族:元素大小和位置
我们想要页面滚动一段距离,比如100px,就让某些元素显示和隐藏,那我们怎么知道,页面滚动了100像素呢?
可以使用scroll来检测页面滚动的举例
获取宽高
获取元素的内容总宽高(不包含滚动条)返回值不带单位:scrollWidth、scrollHeight
div{
width:150px;
height:150px;
overflow:auto;//超出的部分用滚动条
}
<div>
...很多很多内容,已经超出了盒子的大小
</div>
<script>
div.addEventListener('scroll',function(){
//内容有多大,就检测有多大,和盒子的大小无关、不带px单位
div.scrollWidth
//返回的是内容的宽度,但是会被滚动条覆盖一点宽度
div.scrollHeight //返回的是内容的高度
})
</scrpit>
获取位置
被卷去的头部和左侧(重点)
获取元素内容往左、往上滚出去看不到的距离:scrollTop、scrollLeft
被卷去的头部:浏览器页面可能一般大,但是内容会很大,超出了浏览器的页面显示范围,需要使用滚动条查看内容的下面部分,这个时候就需要滚动条往下,但是内容实际上是往上走的,往上走的这一部分就是被卷去的头部。
//先做页面滚动事件,再得到页面滚动的距离
div.addEventListener('scroll',function(){
console.log(this.scrollTop)
})
页面
开发中,我们经常检测页面滚动的距离,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素
document.documentElement:就是<html>标签的滚动,也就是所有的标签的滚动多少。
window.addEventListener('scroll',function(){
let num = document.documentElement.scrollTop
console.log(num)
})
也可以赋值修改(不带单位):document.documentElement.scrollTop = 500
返回顶部案例
当滚动到某个位置时,将隐藏的回到顶部显示出来,然后点击后在将scrollTop设置为0就可以了。
<div class = "content"></div>
<div class = "backtop">
<img>
<a>
</div>
0.获取元素
let backtop = document.querySelector('.backtop')
1.页面滚动事件
window.addEventListener('scroll',function(){
2.检测滚动的距离
let num = document.documentElement.scrollTop
if(num>=500){
//显示回到顶部的元素
backtop.style.display = 'block'
}else{
backtop.style.display = 'none'
}
})
3.进行判断显示和隐藏(根据a标签链接返回)
backtop.children[1].addEventListener('click',function(){
//返回顶部
document.documentElement.scrollTop = 0
})
offset家族
刚刚我们的设计存在问题,对于scrollTop我们是写死的,我们应该让js自动获取页面中的位置。
什么时候用offset家族呢?
当我们知道盒子在页面中的距离就可以使用
获取宽高:
获取元素的自身宽高、包含元素自身设置的宽高、padding、border;盒子是多大,他就是多大,受到padding、border的影响
offsetWidth、offsetHeight
scrollWidth和scrollHeight只和内容有关系、不受盒子大小、padding的影响。而offsetWidth和offsetHeight和盒子有关系,受到padding和border的影响
获取位置(offsetLeft、offsetTop):获取元素距离自己定位父级的左、上距离
如果没有父级,那么就获取浏览器距离自己的位置,如果有父级,那么就获取距离父级的左、上距离
也就是说:当某个div没有直接父级,那么他的父级就是html,当这个div和html的距离存在一定等值时,就会触发某些事件
但是:offsetLeft、offsetTop是只读属性,不可以给值
京东固定导航栏案例
当页面滚动到某个位置时,导航栏就会出现把并且固定在上方,但是往回移动的时候,又会消失
offsetTop:距离顶部有多远,如果卷出的头部距离>我秒杀的offsetTop,说明已经往下划过了秒杀盒子
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
\
.content {
overflow: hidden;
width: 1000px;
height: 3000px;
background-color: pink;
margin: 0 auto;
}
\
.backtop {
display: none;
width: 50px;
left: 50%;
margin: 0 0 0 505px;
position: fixed;
bottom: 60px;
z-index: 100;
}
\
.backtop a {
height: 50px;
width: 50px;
background: url(./images/bg2.png) 0 -600px no-repeat;
opacity: 0.35;
overflow: hidden;
display: block;
text-indent: -999em;
cursor: pointer;
}
\
.header {
position: fixed;
top: -80px;
left: 0;
width: 100%;
height: 80px;
background-color: purple;
text-align: center;
color: #fff;
line-height: 80px;
font-size: 30px;
transition: all .3s;
}
\
.sk {
width: 300px;
height: 300px;
background-color: skyblue;
margin-top: 600px;
}
</style>
<div class="header">我是顶部导航栏</div>
<div class="content">
<div class="sk">秒杀模块</div>
</div>
<div class="backtop">
<img src="./images/close2.png" alt="">
<a href="javascript:;"></a>
</div>
let sk = document.querySelector('.sk')
let header = document.querySelector('.header')
1.页面滚动事件
window.addEventListener('scroll',function(){
2.检测滚动的距离>=秒杀模块的offsetTop值(秒杀模块位置)则滑入
如果我页面往下滚动,卷出的头部距离>我秒杀的offsetTop
if (document.documentElement.scrollTop >= sk.offsetTop) {
// alert('改吃饭了')
//默认header导航栏是-80px是看不见的,当滚动到秒杀时,top:0就显示
header.style.top = '0'
} else {
header.style.top = '-80px'
}
})
通过offsetTop就不需要再手写并且写死值了,更加的灵活
电梯导航案例
一个大的内容模块,分为4部分,旁边有导航栏,点击导航栏可以快速切到对应的内容
分析:
点击当前小导航,当前添加active,其余移除active
得到对应内容的offsetTop值
让页面的scrollTop走到对应内容的offsetTop
侧边栏典型的排他思想,点我、只让我自己高亮;
<div class="aside">
<div class="item active">男装/女装</div>
<div class="item">儿童服装/游乐园</div>
<div class="item">电子产品</div>
<div class="item">电影/美食</div>
</div>
<div class="content">
<div class="neirong content1">男装/女装</div>
<div class="neirong content2">儿童服装/游乐园</div>
<div class="neirong content3">电子产品</div>
<div class="neirong content4">电影/美食</div>
</div>
let item = document.querySelectorAll('.item')
内容的盒子获取
let neirong = document.querySelectorAll('.neirong')
for(let i =0;i<item.length;i++){
item[i].addEventListener('click',function(){
//找到active,移除类(直接找到类不需要双重循环了)
document.querySelector('.aside .active').classList.remove('active')
//给自己添加
this.classList.add('active')
//拿到每个盒子距离浏览器顶部的距离:offsetTop
//让页面滚动到对应的地方
document.documentElement.scrollTop = neirong[i].offsetTop
})
}
client家族
获取宽高
获取元素的可见部分的宽高(不包含边框,滚动条等)
scrollHeight:内容超出了盒子,但是我获取的是内容的高度
offsetWidth:盒子多大,我就多大(受到边框、边距的影响)
clientWidth、clientHeight(可见部分、不包含边框、边距)
可见部分是什么意思?当我把浏览器缩小时,能看到的页面部分也变小了,这就是可见部分
窗口改变事件
resize事件:会在窗口尺寸发生改变时,触发
window.addEventListener('resize',function(){
document.documentElement.clientWidth;//当前可视屏幕宽度
})
window.addEventListener('resize',function(){
let w = document.documentElement.clientWidth
if(w<1920){
document.body.style.backgroundColor = 'pink'
}
})
三大家族总结
clientLeft、clientTop:只读属性,获取左边框和上边框的宽度(用的极少)
重点掌握offset和scroll
轮播图综合案例
注解:
上面最大的图片每个都是一个li,但是他们全部都是叠加在一起的,不同的是:通过opacity来设置淡入淡出的效果来实现具体某一张图片的显示
底下的小图,点到谁,谁就高亮、并且还有一个小下划线.
1.小图标鼠标经过事件,鼠标经过小图片、当前高亮,其余兄弟变淡,添加类
let lis = document.querySelectorAll('.indicator li')
//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
lis[i].addEventListener('mouseenter',function(){
//找到上一个具有active类的、并移除
document.querySelector('.indicator .active').classList.remove('.active')
//鼠标经过谁,谁就加上
this.classList.add('.active')
})
}
2.大图片跟随小图片变化,可以使用opacity效果,可以利用CSS淡入淡出效果,还是添加类
目的是让图片对应的li隐藏,li都隐藏了,那图片一定也隐藏(因为img有链接,最好不要隐藏),让li加一个active类,里面有opciaty属性,为1就是显示,0就是透明
一定要放到鼠标经过事件里面
let piclis = document.querySelectorAll('.slides ul li')
//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
lis[i].addEventListener('mouseenter',function(){
//找到上一个具有active类的、并移除
document.querySelector('.indicator .active').classList.remove('.active')
//鼠标经过谁,谁就加上
this.classList.add('.active')
--------------------------------
//选出唯一的那个active,删除类
document.querySelector('.slides .active').classList.remove('active')
piclis[i].classList.add('active')
})
}
3.图片变化,文字也跟着变化
let text = document.querySelector('.extra h3')
let piclis = document.querySelectorAll('.slides ul li')
//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
lis[i].addEventListener('mouseenter',function(){
//找到上一个具有active类的、并移除
document.querySelector('.indicator .active').classList.remove('.active')
//鼠标经过谁,谁就加上
this.classList.add('.active')
//选出唯一的那个active,删除类
document.querySelector('.slides .active').classList.remove('active')
piclis[i].classList.add('active')
-------------------------------------
text.innerHTML = `这是第${i+1}张图片的详细描述信息`
})
}
4.点击按钮、控制图片(大图小图)的转换
需要一个变化量index不断自增、然后播放下一张,如果到了最后一张,必须要还原为第一张
索引号=索引号%数组长度(放到播放前面)
let next = document.querySelector('.next')
let index = 0
next.addEventListener('click',function(){
//点一次按钮,我的index就加一次
index++
index = index % lis.length
//小图片--先移除其他人存在的高亮
document.querySelector('.indicator .active').classList.remove('active')
//复活我自己
lis[index].classList.add('active')
//大图片--和小图片一样的逻辑
document.querySelector('.slides ul .active').classLisst.remove('active')
piclis[index].classList.add('active')
text.innerHTML = `第${index+1}张图的描述信息`
})
但是上方存在bug,点击右侧按钮可以实现播放下一张、但是鼠标经过前面的,播放就会乱序。
因为index不在鼠标移入事件中,所以鼠标事件需要知道index的值
解决方法:让索引号重新赋值为:当前鼠标经过的索引号。index是全局变量,她没有定义在任何语句块中
index = i
5.写左侧按钮
let prev = document.querySelector('.prev')
prev.addEventListener('click',function(){
index--
index = (lis.length+index) % lis.length
document.querySelector('.indicator .active').classList.remove('active')
lis[index].classList.add('active')
document.querySelector('.slides ul .active').classList.remove('active')
piclis[index].classList.add('active')
text.innerHTML = `第${index+1}张图的描述信息`
})
6.自动播放图片模块(实际上定时器播放就相当于点击了一次播放按钮)
let timer = setInterval(function(){
//自动调用右侧按钮的点击事件
next.click()
},1000)
7.鼠标经过:停止定时器(清除)、鼠标离开:开启定时器
let main = document.querySelector('.main')
main.addEventListener('mouseenter',function(){
clearInterval(timer)
})
main.addEventListener('mouseleave',function(){
timer = setInterval(function(){
//自动调用右侧按钮的点击事件
next.click()
},1000)
})
完整代码:
<div class="main">
<div class="slides">
<ul>
<li class="active"><a href="#"><img src="./assets/b_01.jpg" alt="第1张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_02.jpg" alt="第2张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_03.jpg" alt="第3张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_04.jpg" alt="第4张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_05.jpg" alt="第5张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_06.jpg" alt="第6张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_07.jpg" alt="第7张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_08.jpg" alt="第8张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_09.jpg" alt="第9张图的描述信息"></a></li>
<li><a href="#"><img src="./assets/b_10.jpg" alt="第9张图的描述信息"></a></li>
</ul>
<div class="extra">
<h3>第1张图的描述信息</h3>
<a class="prev" href="javascript:;"></a>
<a class="next" href="javascript:;"></a>
</div>
</div>
<div class="indicator">
<ul>
<li class="active">
<img src="assets/s_01.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_02.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_03.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_04.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_05.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_06.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_07.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_08.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_09.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
<li>
<img src="assets/s_10.jpg">
<span class="mask"></span>
<span class="border"></span>
</li>
</ul>
</div>
</div>
// 轮播图开始啦
// 需求①:小图标鼠标经过事件
// 鼠标经过小图片,当前高亮,其余兄弟变淡 添加类
let lis = document.querySelectorAll('.indicator li')
let piclis = document.querySelectorAll('.slides ul li')
let text = document.querySelector('.extra h3')
let next = document.querySelector('.next')
let prev = document.querySelector('.prev')
let main = document.querySelector('.main')
// 给多个小li绑定事件
for (let i = 0; i < lis.length; i++) {
lis[i].addEventListener('mouseenter', function () {
// 选出唯一的那个active ,删除类
document.querySelector('.indicator .active').classList.remove('active')
// 鼠标经过谁,谁加上active 这个类
this.classList.add('active')
// 需求② :大图片跟随变化 一定要放到鼠标经过事件里面
// 对应的大图片跟着显示,如果想要过渡效果,
可以使用opacity效果,可以利用CSS淡入 淡出的效果,还是添加类
// 选出唯一的那个active ,删除类
document.querySelector('.slides ul .active').classList.remove('active')
// 对应序号的那个 li,谁加上active 这个类
piclis[i].classList.add('active')
text.innerHTML = `第${i + 1}张图的描述信息`
// 需求④:解决一个BUG
// 点击右侧按钮可以实现播放下一张,但是鼠标经过前面的,播放就会乱序
// 解决方案: 让变化量 index 重新赋值为 当前鼠标经过的索引号
// 鼠标经过了那个小li 他的索引号就是 i
// 右侧按钮是通过 index 来了控制播放的
index = i
})
}
// 需求③:右侧按钮播放效果
// 点击右侧按钮,可以自动播放下一张图片
// 需要一个变化量 index 不断自增
// 然后播放下一张图片
// 如果到了最后一张,必须要还原为第1张图片
// 教你一招: 索引号 = 索引号 % 数组长度 (放到播放前面)
let index = 0 // 全局变量 信号量 控制器 为了给 右侧按钮和左侧按钮同时使用
next.addEventListener('click', function () {
index++
// 选出 index 小图片 做操作
// console.log(index)
// if (index === lis.length) {
// index = 0
// }
index = index % lis.length
common()
})
// 需求⑤:左侧按钮播放效果
// 点击左侧按钮,可以自动播放上一张图片
// 需要一个变化量 index 不断自减
// 然后播放上一张图片
// 如果到了第一张,必须要从最后一张播放
// 教你一招: 索引号 = (数组长度 + 索引号) % 数组长度
prev.addEventListener('click', function () {
index--
// 选出 index 小图片 做操作
// console.log(index)
if (index < 0) {
index = lis.length - 1
}
// index = (lis.length + index) % lis.length
common()
})
// 需求⑥:
// 因为左侧按钮和右侧按钮里面有大量相同的操作,可以抽取封装一个函数 common
function common() {
document.querySelector('.indicator .active').classList.remove('active')
lis[index].classList.add('active')
// 选出 index 大图片 做操作
document.querySelector('.slides ul .active').classList.remove('active')
piclis[index].classList.add('active')
text.innerHTML = `第${index + 1}张图的描述信息`
}
// 需求⑦:开启定时器
// 其实定时器自动播放,就相当于点击了右侧按钮,此时只需要, next.click()
let timer = setInterval(function () {
// 自动调用右侧按钮的点击事件
next.click()
}, 1000)
// 需求⑧:
// 鼠标经过停止定时器 (清除定时器)
main.addEventListener('mouseenter', function () {
clearInterval(timer)
})
// 鼠标离开开启定时器 (开启定时器)
main.addEventListener('mouseleave', function () {
timer = setInterval(function () {
// 自动调用右侧按钮的点击事件
next.click()
}, 1000)
})