DOM(Document Object Module) 不属于原生js的一部分,是浏览器提高给js操作网页的一个API
DOM文档对象模型,就是浏览器把html的每个元素生成了一个js对象。通过js可以去操作DOM对象,就是DOM编程。
在浏览器里面window对象是js的根(最顶层)对象,所有的标准库都是放在window对象里面,一般都是省略window对象。
浏览器会把整个网页解析为一个document对象,网页的所有的元素搜索该对象的一个属性。
document对象下面的每一个节点都是一个Node节点类,每一个标签或者文本就是一个节点,节点不是一个确定的html标签。
每一个Node节点会有一个element 类,一般是一个具体的html标记(element类是继承自Node,一个element有Node的属性和自身属性)
htmlElement 继承自 element 继承自 Node
// 1. 在浏览器里面window对象是js的根(最顶层)对象,所有的标准库都是放在window对象里面,一般都是省略window对象。
console.log(window.document);// 一般省略window对象
// 2. 浏览器把整个网页解析为一个document对象
console.log(document);
// 1.直接可以通过.body,.head取到对应的子元素对象
console.log(document.body); //输出body对象
console.log(document.head); //输出head对象
console.log(Object.getPrototypeOf(document.body)); //获取body对象的原型
// 2. baseURI 获取网页的url地址
console.log(document.baseURI);
// 3. domain 返回网页的域名(或者ip地址)
console.log(document.domain);
// 4. forms 返回网页中的所有form表单对象
console.log(document.forms);
// 5. images 返回网页中所有的img图片对象
console.log(document.images);
// 6. links 返回网页中的所有a标签对象
console.log(document.links);
// 7. characterSet 网页编码格式
console.log(document.characterSet); // UTF-8
实际开发中一般需要精准的查找到对应的DOM,需要用查询dom的方法去查询。
// 1. getElementById("id名称") 根据元素的id返回元素对象(不是集合)
console.log(document.getElementById("a1"));// 查询id为a1的元素dom对象
// 2. querySelector("css选择器") 根据css选择器条件查询返回查询到的第一个dom对象
console.log(document.querySelector(".f1"));// 查询第一个对象class为f1的dom
// 3. querySelectorAll("css选择器") 选择符合css选择条件的所有dom对象集合
var dom = document.querySelectorAll(".f2");// 返回class=f2的所有元素的集合
dom.forEach(function(item){ // 原型上有forEach方法 可以遍历每个元素
console.log(item)
})
// 4. getElementByClassName("类名") 根据标签的类名查询dom,返回一个集合
console.log(document.getElementsByClassName("f1"));// 查询class=f1的所有元素
// 5. getElementByTagName("标签名") 根据标签名查询标签,返回的是一个标签对象集合
console.log(document.getElementsByTagName("div"));// 查询网页中的所有div元素
// 6. getElementByName("name属性名称") 根据标签的name属性名称查询dom,返回的是一个集合
console.log(document.getElementsByName("pwd"));// 查询name属性为pwd的dom元素
var div = document.querySelector("#box2")
// 1. 获取node的id属性,如果赋值就是修改属性
console.log(div.id);// 获取id属性
div.id = "divb"// 修改id属性值
// 2. className 获取class属性值
console.log(div.className);// 获取class属性
div.className = "divq" // 修改class值
// 3. innerHTML 返回元素内部的所有内容(包含的html标签,只读取一层)返回的是字符串
console.log(div.innerHTML);
// 修改元素内容,会渲染html标签,一般用该方式修改dom的显示元素,效率比较高
div.innerHTML = "vbka"
// 4. innerText 获取元素的文本内容,过滤html标签
console.log(div.innerText);
// 修改元素的文本内容,有html标签,当做字符串显示在网页
div.innerText = "vbka"
// 5. tagName 获取元素的标签名称(大写字母)
console.log(div.tagName);
// 6. attributes 返回元素的可见属性的map(id,class...)
console.log(div.attributes);
// 7. classList 返回元素class名的集合(类似数组对象)
console.log(div.classList,div.className);
//classList操作class,不会影响其他class
div.classList.add("warp","a")// 给div元素添加c3的class值
div.classList.remove("a")// 删除div上class为a的属性
div.classList.replace("warp","box")// 替换warp值为box
div.classList.toggle("box1")// 在一个样式之间切换(有该样式则是删除,没有则是添加)
// 8. style 返回元素的css样式结合(css的-线链接的样式名称在js里用小驼峰命名)
console.log(div.style.fontSize);
console.log(div.style.color);
// 给样式赋值就是修改样式
div.style.color = "gray"
// 9. nodeType 返回元素节点类型(返回的是一个数字,用不同的数字代表不同的节点类型)
console.log(div.nodeType);
// 10. dataset 获取元素自定义数据(data-后面的值)的对象结构
//aaa
var box = document.querySelector("#box")
console.log(box.dataset); //{text: 'text', index: 'index'}
// 11. parentNode 获取当前元素的父元素
console.log(span.parentNode);
var btn = document.querySelector('.btn')
// 1. 添加类 是在后面追加类名 不会覆盖前面的类名
btn.classList.add('submit')
// 2. 删除类
btn.classList.remove('submit')
// 3. 切换类 当前有这个类名就取消,没有就添加
btn.classList.toggle('submit')
方法就是一个函数,一般也叫事件。用户对DOM操作的一个响应。
var box1 = document.querySelector(".box1")
// 1. onclick 点击事件,用户点击dom以后触发的函数
box1.onclick = function(){ // 用户点击box1触发该函数
box1.style.backgroundColor = "yellowgreen"
// dom的事件里面的this一般代表当前dom对象
this.style.color = "gray"
}
// 2. onmouseenter() 鼠标移入元素触发的事件。不受子元素影响,只有移入的时候触发一次
box1.onmouseenter = function(e){
console.log("enter");
console.log(e); // e表示鼠标事件对象
}
// 3. onmouseover() (较少用)鼠标移入滑过元素会触发,元素内部有其他子元素,离开子元素又会触发over
box1.onmouseover = function(){
console.log("over");
}
// 4. onmouseout() 鼠标离开元素触发,受子元素影响,移入离开子元素也会触发out
box1.onmouseout = function(){
console.log("out");
}
// 5. onmousemove() 鼠标在元素内部只要移动就触发的事件
box1.onmousemove = function(){
console.log("move");
}
// 6. onkeydown() 键盘按下触发的事件
inputName.onkeydown = function(e){
console.log("keydown");
// 默认参数e是键盘事件对象, e.key按键符号,e.keyCode按键符号的码点
console.log(e);
// console.log(this);
}
// 一般用在form表单的输入控件里面:
// 7. onchange 元素内容发生改变触发的事件,一般用在form表单的输入控件里面
// 失去焦点的时候才对比内容,有改变则执行该函数。
var inputName = document.querySelector("input[name='name']")
inputName.onchange = function(){
// 获取input的value值,赋值则是修改value值
console.log(this.value,"onchange");
}
// 8. onblur 元素失去焦点触发的事件(一般输入控件有该事件)
inputName.onblur = function(){
console.log("onblur");
}
// 9. onfocus 元素获取焦点触发
inputName.onfocus = function(){
console.log("onfocus");
}
/* 10. dom对象可以通过getElementById(),getElementsByTagName(),getElementsByClassName(),getElementsByName()
querySelector(),querySelectorAll()
查询dom对象的后代元素
*/
var span = document.querySelector("span")
// 11. document.createElement(tagName) 创建element对象(一般不用此方法,用innerHTML)
var div2 = document.createElement("div") // 创建一个div 元素
div2.style.color = "red"
div2.innerText = "div2"
console.log(div2);
// box.innerHTML = "HELLO"
var box1 = document.querySelector(".box1")
// 12. 在某个dom对象里面添加dom对象
box1.appendChild(div2)// 把div2的dom对象添加到box1里面,box1里面会渲染该元素
// 移出dom里面的某个dom元素
box1.removeChild(span) // 删除box1元素内部的span元素
// 13. setAttribute("属性名","属性值"),给元素添加属性
box1.setAttribute("id","c1")
box1.setAttribute("onclick","console.log(1)")
// 1. 父节点 parentNode
son.parentNode // 得到离元素最近的父级元素 找不到返回null
// 2. 子节点 childNodes和children
ul.childNodes // 包含 元素节点、文本节点等等
ul.children // 只获得元素节点 实际开发常用
ul.firstChild // 获取第一个子节点 包含 元素节点、文本节点等等
ul.firstElementChild // 返回第一个子元素节点
// 实际开发常用
ul.children[0] // 第一个
ul.children[ul.children.length - 1] // 最后一个
// 3. 兄弟节点
div.nextSibling // 上一个兄弟节点 包含 元素节点、文本节点等等
div.nextElementSibling // 返回上一个子元素节点
div.previousSibling // 下一个兄弟节点 包含 元素节点、文本节点等等
div.previousElementSibling // 返回下一个子元素节点
// 4. 创建、添加 节点
var li = document.createElement('li') // 创建一个li标签
ul.appendChild(li) // 给ul后面追加li节点
ul.insertBefore(li,ul.children[0]) // 给ul前面添加li节点 两个参数:1.添加的元素 2.在哪个节点前添加
// 5. 删除节点
ul.removeChild(ul.children[0]) // 把ul里第一个li元素删除
ul.removeChild(ul.children[ul.children.length - 1]) // 把ul里最后一个li元素删除
// 6. 复制节点
/**
* 1. node.childNode() 括号内为空或者为false --浅拷贝 只复制标签不复制里面的内容
* 2. node.childNode(true) 括号内为true --深拷贝 复制标签和里面的内容
*/
var li = ul.children[0].cloneNode(true)
ul.appendChild(li)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态生成表格title>
<style>
.box6 table {
margin: 100px auto;
width: 500px;
text-align: center;
}
style>
head>
<body>
<div class="box6">
<table cellspacing="0" border="1">
<thead>
<tr>
<td>姓名td>
<td>科目td>
<td>成绩td>
<td>操作td>
tr>
thead>
<tbody>
tbody>
table>
div>
<script>
// 动态生成表格
// 准备学生的数据,用数组来存放数据
var dates = [{
name: '张三',
subject: 'javascript',
score: 100
},
{
name: 'Anna',
subject: 'javascript',
score: 78
},
{
name: '约翰',
subject: 'javascript',
score: 89
},
{
name: '杨幂',
subject: 'javascript',
score: 90
}]
// 往tbody里面创建行 有几个人就建几行
var tbody = document.querySelector('.box6 tbody')
for (var i = 0; i < dates.length; i++){
// 1. 创建行 tr
var tr = document.createElement('tr')
tbody.appendChild(tr)
// 2. 创建单元格td 单元格数量取决每个对象里面的属性个数 for循环遍历对象
for(var k in dates[i]){
// 创建单元格td
var td = document.createElement('td')
// 把对象里面的属性值给单元格td
// console.log(dates[i][k]);
td.innerHTML = dates[i][k]
tr.appendChild(td)
}
// 3. 创建删除单元格
var td = document.createElement('td')
td.innerHTML = '删除'
// 添加 删除 操作
tr.appendChild(td)
}
// 4. 点击可以删除单元格
var dea = document.querySelectorAll('.box6 a')
for(let i = 0 ; i < dea.length;i++){
dea[i].onclick = function(){
// this.parentNode仅表示单元格td this.parentNode.parentNode才表示它所在的整个tr
tbody.removeChild(this.parentNode.parentNode)
}
}
script>
body>
html>
innerHTML 和 createElement() 创建多个元素的效率 哪个更高?
:
innerHTML采用数组(8ms左右)形式 而不采用拼接字符串格式(3s左右)效率更高,但结构稍微复杂
createElement()创建多个元素稍微低一点点(20ms左右),但结构更清晰
它可以触发多个操作函数 不会像 原始写法后面操作会覆盖前面的操作
// 1. 传统的事件属性只能执行一个函数,不能同时执行多个
var btn = document.querySelector("button")
btn.onclick = function(){
alert("aaa")
}
btn.onclick=function(){
alert(11) // 会覆盖前面的函数,不能同时执行两个函数
}
// 2. 事件监听,通过js的事件监听系统,监听dom对象的事件,只要监听的事件触发就好执行对应的函数,可以监听多个事件。
/* element.addEventListener(eventName,callback),可以监听多个事件
eventName 事件名称,js事件属性去掉on
callback 监听事件执行的函数 */
// 给btn按钮添加点击事件的监听,两个函数都可执行
btn.addEventListener("click",function(){
alert(222)
})
var btn1 = function(){
alert("XXX");
}
btn.addEventListener("click",btn1)
// 3. 移出事件监听
/* element.removeListener(eventName,callback)
eventName 事件名称,js事件属性去掉on
callback 移出事件执行的函数(要和监听的函数是同一个内存地址) */
btn.removeEventListener("click",btn1) // 移出监听点击事件执行f1函数的监听
鼠标移入移出,内容发生变化才会触发
//
const input = document.querySelector('input')
// 只有文本框的内容有变化才会触发
input.addEventListener('change',function(){
console.log('触发');
})
鼠标每移入移出就会触发一次
//
const input = document.querySelector('input')
// 文本框的内容有无变化都会触发
input.addEventListener('blur',function(){
console.log('触发');
})
多个重叠dom的事件触发顺序是 从内往外 执行,像水泡一样,所以叫事件冒泡(默认行为)
e是当前事件对象:
e.stopPropagation(); 阻止事件冒泡(停止事件传递)
e.preventDefault();阻止默认事件,(阻止a标签的默认跳转链接事件)
// 阻止dom的默认事件(a标签有默认的跳转事件)
var a = document.querySelector("a")
a.addEventListener("click",function(e){
e.preventDefault() // 阻止默认事件,阻止a标签的默认跳转链接事件
})
// selectstart 禁止选中文字
box7.addEventListener('selectstart',function(e){
e.preventDefault()
})
// contextmenu 禁用右键菜单
box7.addEventListener("contextmenu",function(e){
e.preventDefault()
})
/*HTML代码:
*/
var div = document.querySelectorAll("div")
// 默认会有冒泡关系
div[0].addEventListener("click",function(){
alert("box1")
})
div[1].addEventListener("click",function(e){// e是当前事件对象
e.stopPropagation()// 阻止事件冒泡(停止事件传递)
alert("box2")
})
div[2].addEventListener("click",function(e){
alert("box3")
})
事件委托原理: 不是给每个子元素添加监听器,通过给元素的父元素添加事件,利用事件冒泡原理,设置到每个子元素上
通过获取点击元素的标签名(tagName),触发的事件却是父元素的事件。
如: 需要给li标签添加点击事件,获取点击li标签的所有值。
/*
- 1
- 2
- 3
*/
var ul = document.querySelector("ul")
ul.addEventListener('click',function(e){
// e是当前点击事件对象,事件对象的target属性会记录点击的那个dom元素
if(e.target.tagName === "LI"){// 判断是否点击的li标签
var index = +e.target.dataset.indext// 获取自定义数据index并转为number类型
console.log(index);
}
})
this 指向绑定事件
e.target 指向我们点击的那个对象
var ul = document.querySelector('ul')
ul.addEventListener("click",function(e){
// this 指向绑定事件 --> ul
console.log(this);
// e.target 指向我们点击的那个对象 --> li
console.log(e.target);
})
多个重叠的dom元素,事件执行顺序是 从外向内 执行
addEventListener(event,callback,bool)
bool为true则是事件捕获,为false则是事件冒泡,默认是false(一般省略)
三个阶段:
1.捕获阶段(从祖先元素document向目标元素进行事件的捕获) --从外向内
2.目标阶段(触发事件的对象)
3.冒泡阶段(再由目标元素向祖先元素进行事件的冒泡) --从内向外
默认在捕获阶段 事件不会触发,将addEventListener的第三个参数设置为true
div[0].addEventListener("click",function(){
alert("box1")
},true)
div[1].addEventListener("click",function(e){// e是当前事件对象
// e.stopPropagation()// 阻止事件冒泡(停止事件传递)
alert("box2")
},true)
div[2].addEventListener("click",function(e){
alert("box3")
},true)
一定时间内函数只指向一次(每隔一段时间可以执行函数)
var box1 = document.querySelector(".box1")
var canMove = true // 创建变量控制函数内部逻辑代码是否执行
box1.onmousemove = function (){
if (canMove){
canMove = false //执行了代码修改变量值为false
console.log(11);
setTimeout(function(){// 控制函数内部逻辑指向的时间间隔
canMove = true
},500)
}
}
函数频繁触发的情况下,只有足够的空闲时间才执行函数代码(某个操作结束后才执行该函数)
如:window窗口的滚动事件,需要滚动停止后再执行函数代码。
var setIndex = ""
window.onscroll=function(){
// 记录定时器的索引值,每次滚动都清除上一个定时器
clearTimeout(setIndex)
setIndex = setTimeout(function(){
console.log("scroll")
},500)
}
JS执行分:主线程执行栈(同步任务)和任务队列(异步任务)
1. 先执行 主线程执行栈的同步任务
2. 异步任务(回调函数)放入任务队列中
3. 执行栈的同步任务执行完后,系统会按次序读取任务队列的异步任务,被读取的异步任务结束等待状态,进入执行栈,开始执行。异步任务处理完毕清空
window 是浏览器窗口对象,是BOM的核心对象
在浏览器中window也是一个全局对象,是一个顶层对象,BOM自带的对象都是window对象的属性。 如: alert,propt,document等,一般省略了window —>(window.document)
var 关键词创建的全局变量都是做为window对象的属性
document.addEventListener("click",function(e){
// 1. client 鼠标在当前可视区的x和y坐标
console.log(e.clientX);
console.log(e.clientY);
// 2. page 鼠标在页面文档的x和y坐标(包括右边拉伸条)
console.log(e.pageX);
console.log(e.pageY);
// 3. screen 鼠标在电脑屏幕的x和y坐标(包括缩小浏览器窗口)
console.log(e.screenX);
console.log(e.screenY);
})
//1.获取浏览器窗口的宽度和高度
console.log(window.innerWidth,window.innerHeight)
//2.open(url,name) 打开一个窗口并显示url的网页内容
//name是窗口的名称(可省略),如果打开多个窗口,窗口的name一致,则只会打开一个窗口,后面覆盖前面的网页窗口。
window.open("https://www.baidu.com","a")
window.open("https://www.taobao.com","a")
mouseover:经过自身盒子会触发一次,经过子盒子再触发一次(有冒泡)
mouseenter:仅经过自身触发一次(没有冒泡)
mouseenter 和 mouseleave搭配 都不会冒泡
// 1. keyup键盘弹起 触发事件
document.addEventListener("keyup",function(e){
// 事件对象的 keyCode 可以得到相应的ASCII码值
console.log('upup');
})
// 2. keydown键盘按下 触发事件(比 keypress先触发)
document.addEventListener('keydown',function(e){
console.log(e.keyCode); // 不区分大小写 a和A得到的都是65
})
// 3. keypress 键盘按下 触发事件
document.addEventListener('keypress',function(e){
console.log('keypress');
console.log(e.keyCode); // 区分大小写 a:65 A:97
})
// 一、 window.onscroll 窗口滚动条滚动触发的事件
// 1. dom.scrollTop 滚动条离顶部的距离,如果用等号赋值就是修改滚动条的位置
// 2. dom.scrllLeft 滚动条离左边的距离
var bodyHeight = document.body.clientHeight// 获取body的可见高度
var wHeight = window.innerHeight // 获取windown窗口的高度
var btn1 = document.querySelector(".btn")
window.onscroll = function(){
/*
获取网页滚动条滚动距离用 document.documentElement.scrollTop 或者
document.body.scrollTop (不同的浏览器写法不一样,一般需要两者都用)
*/
var top = document.documentElement.scrollTop || document.body.scrollTop
// 例: 当网页滚动条滚动到底部就出现返回顶部的按钮
// 网页滚动到底部的scrollTop = body的高度 - 窗口的高度 - 滚动条高度(一般50px)
var bottom = bodyHeight - wHeight - 80
if (top > bottom){
console.log("bottom!");
btn1.style.display = "block" // 到底部显示
} else {
btn1.style.display = "none"// 没到底部隐藏
}
}
// 点击返回顶部按钮,滚动条返回顶部
btn1.onclick = function(){
// 修改scrollTop滚动条返回顶部
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
}
// 二. dom对象也有scroll系列属性
var div1 = document.querySelector(".box")
div1.onscroll = function(){
console.log(this.scrollTop,this.scrollLeft);
}
setTimeout(function(){// 设置定时器让它5秒内还原
div1.scrollLeft = 0
div1.scrollTop = 0
},5000);
// 三. window.onresize 窗口尺寸发生变化触发的事件
window.onresize = function(){
console.log(window.innerWidth); // 获取窗口宽度
console.log(window.innerHeight);// 获取窗口高度
}
// 四. dom.offset 相对于非static父元素的偏移距离(如果没有非static定位,则是相对于body)
var warp = document.querySelector(".warp")
console.log(warp.offsetLeft);// 离最近非static父辈元素的左边的距离
console.log(warp.offsetTop);// 离最近非static父辈元素的顶部的距离
可以获取浏览器相关的信息,常用userAgent属性判断用户的操作平台
如果包含了 Android 关键词则是安卓手机
如果包含了 iPhone 关键词则是iphone设备
如果都没有包含,则是pc平台
// 判断浏览器是在 移动端平台 还是在 pc平台
function isMobile(){
const userAgent = navigator.userAgent
return userAgent.match("Android") || userAgent.match('iPhone') || false
}
document.write(isMobile()?"移动端平台":"PC端平台")
操作url网址相关的对象
http://127.0.0.1:5500/demo3.html?id=11&name=123#444
location.href="https://www.baidu.com" // 赋值则是跳转到该url地址
// location的 href地址跳转案例
//支付将在5秒后自动跳转页面
const a = document.querySelector('a')
let num = 5
let timer = setInterval(function(){
num--
a.innerHTML = `支付将在${num}秒后自动跳转页面`
if (num === 0){ //0秒后清除定时器并跳转页面
clearInterval(timer)
location.href = "http://www.baidu.com"
}
},1000)
#444
?id=11&name=123
一般需要获取到某个查询参数的值,要封装一个函数来查询
/**
* @params key 查询参数名称
*/
function getParam(key){
var search = location.search//获取查询参数字符串
// 1.替换 ? 为空字符串
search = search.replace("?","")
// 2.按 & 符合分隔数组
search = search.split("&")
// 3.按 = 号分隔数组
for (let i = 0; i < search.length;i++){// 先遍历分隔的数组
var items = search[i].split("=") // 再= 号分隔
if(key === items[0]){ // 如果传入的参数等于数组的参数
// url中的中文字符串会被编码,需要用解码函数解码
return decodeURI(items[1]) // 就返回它对应的属性值
}
}
return "" // 都没有找到返回空值
}
console.log(getParam("id"));// 11
console.log(getParam("name"));// 123
console.log(encodeURI('中国')); // 编码
console.log(decodeURI('%E4%B8%AD%E5%9B%BD')); //解码
127.0.0.1:5500
127.0.0.1
/demo3.html
:http
location.reload() == F5 (普通刷新)
location.reload(true) == CTRL + F5 (强制刷新)
// 用定时器每3秒自动刷新一次
setTimeout(function() {
location.reload()
}, 3000);
用一个url网址替换当前网站,不会有浏览器历史记录
location.replace("https://taobao.com")
/*
百度
*/
const btn = document.querySelectorAll('button')
btn[0].onclick = function (){ // back 按钮
// 打开上一个浏览历史记录
history.back()
history.go(-1)
}
btn[1].onclick = function (){// next 按钮
// 打开下一个浏览历史记
history.forward()
history.go(1)
}
console.log(history.length);
必须给元素加定位 才能控制它的left等值
// offsetLeft 元素距窗口左边的距离
console.log(div.offsetLeft);
// offsetLeft 元素距窗口顶部的距离
console.log(div.offsetLeft);
// offsetWidth 元素本身的宽度 包括:padding + border + width
console.log(div.offsetWidth);
//offsetHeight 元素本身的高度 包括:padding + border + width
console.log(div.offsetHeight);
/offset 和 style 的区别
1. offset可以得到任意样式表值,style只能得到行内样式表值
2. offset获得的数值没有单位,style获得是带单位的字符串
3. offsetWidth包含padding + border + width,style.width不包含padding和border值
4. offsetWidth等属性是只读属性 不能赋值,style.width是可读属性,可获取可赋值
-
综上:获取元素大小位置用offset,给元素更改值用style
element.pageX:元素到页面的X坐标长度
element.pageY:元素到页面的Y坐标长度
针对盒子元素:
element.scrollTop:元素被卷走的头部
element.scrollHeight:元素总的高度
针对整个页面:
window.pageYOffset:页面被卷走的头部
动画原理:
1. 获得盒子当前位置
2. 让盒子在当前位置加一个移动的距离
3. 利用定时器不断重复这个操作
4. 加一个结束定时器的条件
5. 注意元素一定要加定位,才能使用 element.style.left
盒子当前的位置 + 固定值
// 封装函数:函数传递两个参数,一个操作对象,一个结束条件
function animation(obj,target){
// 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
// 解决方案:让我们元素只有一个定时器
// 先清除以前的定时器,只保留当前的一个定时器执行函数
clearInterval(obj.timer)
obj.timer = setInterval(function(){ // obj.timer性能优化更好
if (obj.offsetLeft == target){
clearInterval(obj.timer)
} else {
obj.style.left = obj.offsetLeft + 1 + 'px'
}
},30)
}
// 调用函数
var box = document.querySelector('.bar')
var banner = document.querySelector('.banner')
animation(box,300)
var btn = document.querySelector('button')
btn.addEventListener('click',function(){
animation(banner,200)
})
也可以实现多个目标值之间的移动
盒子当前的位置 + 变化值(目标值 - 当前的距离值 / 值)
思路:
- 让盒子每次移动的距离慢慢变小 速度就会慢慢落下来
- 核心算法:(目标值 - 现在的位置) / 10 作为每次移动的距离步长
- 停止条件:让当前的盒子位置等于目标位置就停止定时器
// 封装函数:函数传递两个参数,一个操作对象,一个结束条件
function animation(obj,target){
// 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
// 先清除以前的定时器,只保留当前的一个定时器执行函数
clearInterval(obj.timer)
obj.timer = setInterval(function(){ // obj.timer性能优化更好
var step = (target - obj.offsetLeft) / 10
// 把步长改为整数 不要出现小数问题
tep = step > 0 ? Math.ceil(step) : Math.floor(step)
if (obj.offsetLeft == target){
clearInterval(obj.timer)
} else {
// 每次加的步长值都是一个慢慢变小的值
obj.style.left = obj.offsetLeft + step + 'px'
}
},15)
}
// 封装函数:函数传递两个参数,一个操作对象,一个结束条件 ,回调函数
function animation(obj,target,callback){
// 当我们不断点击按钮,这个元素会越来越快 因为每点击一次,元素就会开启一个定时器
// 先清除以前的定时器,只保留当前的一个定时器执行函数
clearInterval(obj.timer)
obj.timer = setInterval(function(){ // obj.timer性能优化更好
var step = (target - obj.offsetLeft) / 10
// 把步长改为整数 不要出现小数问题
step = step > 0 ? Math.ceil(step) : Math.floor(step)
if (obj.offsetLeft == target){
clearInterval(obj.timer)
// 结束调用 回调函数
if (callback){// 有回调函数就调用 没有就不调用
callback()
}
} else {
// 每次加的步长值都是一个慢慢变小的值
obj.style.left = obj.offsetLeft + step + 'px'
}
},15)
}
// 调用函数
var banner = document.querySelector('.banner')
var btn2 = document.querySelector('.btn2')
btn2.addEventListener('click',function(){
animation(banner,800,function(){
banner.style.backgroundColor = 'red'
})
})
js程序如果报错,会终止代码运行并显示错误。有时不希望出错就停止执行代码,需要把可能出错的代码写入到异常处理代码块里面。
不要乱用try…catch,一般js代码需要包装逻辑的完整性,正确性,容易出错的地方用if判断解决。
try {// 把可能出错的底代码放入try语句中,如果代码出错,错误会抛到catch语句中,没有错误直接执行语句,不会执行catch语句
console.log(text1); // 错误语句
}catch(err){// catch语句接受try抛出的异常,err就是错误的描述对象
console.log(err.message); //text1 is not defined
}finally{// 不管执行了try还是catch语句,都会执行finally语句(可省略)
console.log("都会执行的语句");
}
//1. 可以用Error构造函数收到抛出一个错误
function err1(){
// throw 语句抛出错误
throw new Error("err1 是一个错误语句");// 实例化一个错误对象,参数是错误显示内容
}
//2. 测试
try{
err1() // 调用错误函数
}catch(err){
console.log(err.message); //err1 是一个错误语句
}
cookie 是一种小型的文本文件,可以让程序员在计算机中存储少量的数据(4kb),用来记录用户的一些信息。比如记录用户的身份,喜好,浏览器网页的习惯等等。cookie最初设计主要是为php,asp等服务器端语言设计,前端也可以通过js操作cookie。cookie可以设置存储的过期时间。
cookie的数据是保留在客户端的硬盘上,cookie是绑定在域名上的,不同域名的cookie不能互相访问,cookie创建数量有限制,大部分浏览器现在在20-50个之间;一个cookie存储数据大小是4kb左右。前端开发一般不用cookie
// 1. 创建cookie,cookie的值是一个字符串,字符串用key=value方式存储内容
/* 存储的cookie 的 value数据中不能包含分号,逗号或者cookie,
建议存储数据使用encodeURI对内容进行编码 */
// 读取的时候用decodeURI解码。默认数据是浏览器关闭,cookie数据就消失
document.cookie = `name=${encodeURI("张珊")}`
// 2. 如果要延长cookie数据存储时间,用max-age指定cookie数据存储时长(单位s),默认为-1,浏览器关闭,数据消失
document.cookie = `test=${encodeURI("安娜")};max-age=${60*60*24*30}`// test数据30天过期
// 3. expires属性指定cookie过期日期(日期字符串格式)
document.cookie = `sex=11;expires=Fri, 17 Jul 2024 21:15:42 GMT`
// 4. 修改cookie的值,创建一个相同的cookie覆盖
document.cookie = "name=Amy"
// 5. 删除cookie,把cookie的有效期设置为一过期时间则是删除
document,cookie = "sex='';expires=Fri, 17 Jul 2020 21:15:42 GMT"
// 6. 读取cookie的值
// 封装读取cookie指定值的函数
function getCookie(key){
const cookie = document.cookie.split("; ")
for (let i = 0; i < cookie.length; i++){
const items = cookie[i].split("=")
if(items[0] === key){
return decodeURI(items[1])
}
}
return ""
}
localStorage 永久存储数据,不主动删除,数据不会消失。
sessionStorage用法和localStorage一致,浏览器关闭,数据消失。storage 存储数据大小是5M
// 1. 存储数据 localStorage.setItem('key','value') ,只能存字符串(非字符串数据会转字符串)
localStorage.setItem('uame','anna') // 本地存储空间,数据永久存储
localStorage.setItem('age',18)// 数字会转成字符串
// 2. 读取key值 localStorage.getItem('key') 读取key值对应的value
console.log(localStorage.getItem('age'));
// 3. 删除key localStorage.removeItem(key)
localStorage.removeItem('age')
// 4. 删除storage里面所有数据 localStorage.clear()---也可用上面的禁用符删除
localStorage.clear()
用法同 localStorage 一样,除生命周期不同,页面关闭即消失。
// 添加数据
setnum.addEventListener('click',function(){
var val = ipt.value
sessionStorage.setItem('age',val)
sessionStorage.setItem('uname',val)
})
// 获取数据的值
getnum.addEventListener('click',function(){
console.log(sessionStorage.getItem('age'));
})
// 删除一条数据
remove.addEventListener('click',function(){
sessionStorage.removeItem('uname')
})
// 清除所有数据 不推荐使用
detele.addEventListener('click',function(){
sessionStorage.clear()
})
本地只能存储字符串,不能存储复杂的数据类型,需要将复杂数据类型转换成JSON字符串存储到本地
// 1.存储 JSON.stringify() 把对象转成JSON字符串
localStorage.setItem('user',JSON.stringify(user)) // {"name":"Anne","age":29,"gender":"女"}
// 2.取值 JSON.parse() 把JSON字符串转成对象
console.log(JSON.parse(localStorage.getItem('user'))); //{name: 'Anne', age: 29, gender: '女'}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>记住用户名title>
<style>
style>
head>
<body>
<input type="text" placeholder="请输入用户名" class="username">
<input type="checkbox" name="" class="checkbox">
<script>
var username = document.querySelector('.username')
var checkbox = document.querySelector('.checkbox')
// 先判断有没有存储的 username
if (localStorage.getItem('username')){
username.value = localStorage.getItem('username')
checkbox.checked = true
}
// 如果复选框勾选就存储用户名 如果取消就移出用户名
checkbox.addEventListener('change',function(){
if (this.checked){
localStorage.setItem('username',username.value)
} else {
localStorage.removeItem('username')
}
})
script>
body>
html>
正则表达式: 是一种匹配字符的逻辑公式,主要是匹配字符串中的字符。
匹配符:
// a. ^ 表示以某个字符开头的(开头不能有其他任何的字符)
const reg1 = /^hello/; // 匹配字符以hello开头的hello字符串
// b. $ 匹配以某个字符结尾(结尾不能有其他任何的字符)
const reg2 = /css$/; // 匹配以css结尾的css字符串
// c. \b 匹配单词是否到边界(单词后面没有其他任意字符,可以有空格)
const regc3 = /java\b/g
修饰符:
- g 全局匹配,查询字符串中的所有。
2. i 不区分大小字母。大小写字母都会匹配
3. m 多行匹配,匹配多行中的字符
4. u 字符按照unicode编码匹配
// a. [] 范围查询,查询[]里面包含的单个字符
const reg3 = /[ak]/g // 查询a或者l字符
console.log(str.match(reg3))
// b. - 表示一段连续的内容
const reg = /[0-9a-z]/ig // 查询所0-9的所有数字,A-z查询所有的字母,不区分大小写
// const reg = /[0-9A-z]/g // 从大A - 小z
// const reg = /[0-9a-zA-Z]/g
// c. [^a] 查询除了中括号里面的所有内容
const reg5 = /[^a-z]/ig // 查询字符串里面a-z(不区分大小写)的以外的所有字符
// d. | 分隔正则逻辑(或的关系)
const reg6 = /(hello)|java/g // 查询hello或者java字符串,()表示一个整体
// e. \ 转义符号,正则表达式的内置关键字符如果要作为字符串匹配,需要添加\转义符
const reg7 = /\[java\]/ // 匹配[java],而不是作为正则表达式符号
const reg8 = /\//g // 匹配 /符号
// 1. \d 匹配所有的数字 等价:/[0-9]/g
const rega = /\d/g
// 2. \D 匹配所有的非数字 等价:/[^0-9]/g
const regb = /\D/g
// 3. \w 匹配数字,字母,下划线 等价:/[0-9A-z_]/g
const regc = /\w/g
// 4. \W 匹配非数字\字母\下划线的字符 等价:/[^0-9A-z_]/g
const regd = /\w/g
// 5. \s 匹配所有的空白字符串(空格)
const rege = /\s/g
// 6. .表示任意字符
const regf = /./g // 匹配所有的字符
// 7. u修饰符匹配中文字符(内部其实是去匹配中文字符对应的unicode编码)
const regg = /\p{Unified_Ideograph}/gu // 匹配所有的中文字符
// 1. + 表示匹配字符 >=1 个以上
var rega1 = /go+d/g // 匹配gd中间带有o的字符串
// 2. * 表示匹配字符0个以上(最少是0个)
var rega2 = /go*d/g // 匹配gd中间可以有o也可以没有o的字符串
// 3. ? 表示0个或者1个
var rega3 = /go?d/g // gd中间有1个o或者没有o的字符串
// 4. {min,max} 自定义匹配数量,min是最小数量,max是最大数量
var regb1 = /go{1,3}d/g // 匹配gd之间有1-3个o的字符串
var regb2 = /go{3}d/g // 匹配gd之间只有3个o的字符串
var regb3 = /go{3,}d/g // 匹配gd之间最少有3个o的字符串
字符串的test(),match(),search(),replace()可以使用正则表达式对象
正则表达式的test(str) 检查str字符串是否满足正则对象规则,满足返回true,不满足返回false
// 例: 校验手机号码规则
const reg = /^1[3-9]\d{9}$/
function ipone(num){
if (reg.test(num)){
console.log("手机号码正确");
} else {
console.log("手机号码格式错误");
}
}
console.log(ipone(13555555547));
// 例: 用户名注册用户名的规则:
// 以字母开头,第二位开始可以是数字,字母下划线,-,.的组合。用户名长度是4-8位
const userReg = /^[a-z][\w-\.]{3,7}$/i
const u1 = "w888"
console.log(userReg.test(u1));
const str = "124 GoodJGsiwncKK 883 *4*"
// 查询数字和所有的字母
const reg = /[0-9a-z]/ig
console.log(str.match(reg));
str = str.replace(rege,"") //替换字符串里面所有的空格为""
预判某个位置是某个字符。也叫零宽断言,预判位置的字符不会出现在查询结果
// a1 char(?=exp) 0宽正预测(先行预测)。判断char字符后面必需符合给定的正则表达式
var str6 = "1hello-world 2hello-js 3hello-html";
var regd1 = /2hello(?=-js)/g; // 匹配hello字符串后面是-js的hello字符
console.log(str6.match(regd1))
// a2 char(?!exp) 0宽负预测先行断言(char字符后面不满足exp正则表达式)
var regd2 = /\dhello(?!-js)/g; // 匹配hello后面没有接-js的hello字符串
console.log(str6.match(regd2))
// a3 (?<=exp)char 0宽正预测回顾断言(char字符左边是否符合某个正则表达式)
var regd3 = /(?<=2hello-)\w+/g; // 查询左是2hello-字符串的字符
console.log(str6.match(regd3))
// a4 (?
var regd4 = /(?/g; // 查询左边不是2hello-字符串的字符
console.log(str6.match(regd4))
// 1.验证用户名 6-10位
const reg = /^\w{6,10}$/
// 2. 手机号验证 11位手机号码
const reg = /^1[3-9]\d{9}$/
// 3. 验证码 6位数字
const reg = /^\d{6}$/
// 4. 密码 6~20位数字字母符号
const reg = /^[\w-]{6,20}$/
// 企业邮箱
var emailReg = /^(?!.*\.\-)^(?!.*\-\.)\w+[-\.\w]*@([0-9A-z]+\.[0-9A-z]+)+$/
// 身份证
const reg2 = /^\d{17}[\dX]$/gi
// 复杂密码校验
// 至少包含小写字母/大写字母/数字,字母下划线的组合的4-8位密码
const pwdReg = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])\w{4,8}$/
const pwd = "123Z2a2"
// 1. touchstart手指触摸DOM元素事件
div.addEventListener('touchstart',function(e){
console.log(e);
/**
* 1. touches 正在触摸屏幕的手指列表
* 2. targetTouches 正在触摸当前DOM元素的手指列表(常用)
* 3. changedTouches 手指状态发生了改变的列表 从无到有 或 从有到无
*/
// 可以拿到第一个手指触摸的相关信息 坐标等等
console.log(e.targetTouches[0]);
})
// 2. touchmove手指在DOM元素身上移动事件
div.addEventListener('touchmove',function(e){
console.log('move~~');
})
// 3. touchend 手指离开DOM元素
div.addEventListener('touchend',function(e){
console.log(e);
//当手指离开屏幕的时候,就没有 touches 和 targetTouches 列表
// 但会存在 changedTouches
})
var div = document.querySelector('div')
// 获取手指的初始坐标 (声明全局变量方便后面使用)
var startX = 0
var startY = 0
// 获取盒子原来的位置
var x = 0
var y = 0
div.addEventListener('touchstart',function(e){
// 第一个手指的坐标
startX = e.targetTouches[0].pageX
startY = e.targetTouches[0].pageY
// 盒子的初始值
x = this.offsetLeft
y = this.offsetTop
})
div.addEventListener('touchmove',function(e){
// 手指移动的距离 = 手指移动的坐标 - 开始手指的坐标
var moveX = e.targetTouches[0].pageX - startX
var moveY = e.targetTouches[0].pageY - startY
// 盒子移动的距离 = 盒子原来的位置 + 手指移动的距离
this.style.left = x + moveX + 'px'
this.style.top = y + moveY + 'px'
e.preventDefault() // 阻止屏幕滚动的默认行为
})