1 正则
1.1 正则表达式
javascript
// 正则表达式 Regular Expression Regular 有规律的
// 定义:是一种字符串匹配的模式(pattern) 模式 理解为 规则
// 作用,应用场景:
// 1. 表单验证,比如验证手机号 看是否满足条件 : 匹配
// 2. 过滤敏感词:替换
// 3. 从字符串中提取一些我们想要的字符 : 提取
// 使用,分为两个步骤
// 1. 定义规则
// 2. 使用正则 ,看是否匹配
const reg = /前端/
// 字面量: 字面意思,我们看到它就知道它是正则 [] , {}
console.log(typeof reg) // object
console.log(reg instanceof Object) // true
console.log(Object.prototype.toString.call(reg)) // [object RegExp]
// ==> 使用
const str = '我们正在学习正则'
// 1. 定义正则表达式
const reg2 = /正则/
// reg.test(str)
// 2. 看是否匹配 符合返回true, 不符合返回false
console.log(reg2.test(str)) // true
console.log(reg.test('正则哈哈哈')) // false 注意,正则表达式写在test的前面
1.2 边界符
DANGER
^ 以谁开头 $ 以谁结尾 /^xxx$/ 精确匹配
javascript
// 元字符之边界符
console.log(/哈/.test('哈')) // true
console.log(/哈/.test('二哈')) // true
console.log(/啊哈/.test('哈啊哈')) // true
console.log(/啊哈/.test('啊哈啊')) // true
console.log(/哈啊哈/.test('啊哈啊')) // false
// 注意,这里的正则,不能拆分,它是一个整体
// 边界符
console.log('--------------')
// 1. ^ 脱字符, 表示以谁开头 6号键位
console.log(/^哈/.test('哈')) // true
console.log(/^哈/.test('哈哈')) // true
console.log(/^哈/.test('二哈')) // false
console.log('---------------------')
// 2. $ 以谁结尾 $ 是4号键位
console.log(/web$/.test('web前端')) // false
console.log(/web$/.test('前端web')) // true
console.log(/web$/.test('前端web学习')) // false
console.log(/web$/.test('前端we')) // false
console.log('---------------')
// 3. 精确匹配 ^ $ 一起在正则表达式的前后写 ==> 必须一模一样
console.log(/^哈$/.test('哈')) // true
console.log(/^哈$/.test('二哈')) // false
console.log(/^哈$/.test('哈哈')) // false ==> 因为精确匹配,多一个都不行
console.log(/^哈二哈$/.test('哈二哈哈')) // false
1.3 量词
javascript
// 元字符之量词
// 1. * 重复次数 >= 0 次
const reg1 = /^哈*$/
console.log(reg1.test('')) // true
console.log(reg1.test('哈哈哈')) // true
console.log(reg1.test('哈哈哈哈哈哈')) // true
console.log('------------')
// 2. + 重复次数 >= 1 次
const reg2 = /^w+$/ // w ww www wwww
console.log(reg2.test('')) // false
console.log(reg2.test('w'))
console.log(reg2.test('ww'))
console.log(reg2.test('www'))
console.log('---------------------')
// 3. ? 重复次数 0 || 1
const reg3 = /^w?$/
console.log(reg3.test('')) // true
console.log(reg3.test('w')) // true
console.log(reg3.test('ww')) // false
console.log('------------')
// 4. {n} 重复 n 次
const reg4 = /^w{3}$/
console.log(reg4.test('ww'))
console.log(reg4.test('www'))
console.log(reg4.test('wwww'))
console.log('------------')
// 5. {n,} 重复次数 >= n
const reg5 = /^w{3,}$/
console.log(reg5.test('ww'))
console.log(reg5.test('www'))
console.log(reg5.test('wwww'))
// 6. {n,m} n =< 重复次数 <= m , 注意,逗号左右不要加空格
console.log('----------------')
const reg6 = /^w{3,6}$/
console.log(reg6.test('ww')) // false
console.log(reg6.test('www')) // true
console.log(reg6.test('wwww'))
console.log(reg6.test('wwwww'))
console.log(reg6.test('wwwwww'))
console.log(reg6.test('wwwwwww')) // false
// eg. 量词作用的是它相邻的前面那一个字符
const reg7 = /^ab{2,5}c$/
const res = reg7.test('abc')
// abbc abbbc abbbbc abbbbbc
console.log(res)
1.4 字符组
javascript
// 字符组: 同一个位置上可能出现各种字符,也叫做纵向模糊匹配
// 写法:[]方括号中列出所有可能出现的字符,但是,最后只选一个!
// 联想 :抽奖的老虎机,只能选一个
// /[abc]/ ==> a || b || c
console.log(/^[abc]$/.test('a'))
console.log(/^[abc]$/.test('b'))
console.log(/^[abc]$/.test('c'))
console.log(/^[abc]$/.test('ab')) // false 因为只能选一个
console.log(/^[abc]$/.test('bc'))
console.log('-------------')
// 2. 如果不加边界符 , 右边的字符串如果包含a 或 b 或 c ,则为true
console.log(/[abc]/.test('bc'))
// 3. 字符组+量词
console.log(/^[abc][abc]$/.test('ab')) // true
console.log(/^[abc]{3}$/.test('abc')) // true
console.log(/^[ef][abc]{2}$/.test('eac')) // true
// [abc][abc] ==> a b c ; a b c ; aa ab ac ba bb bc ca cb cc
1.5 取反
javascript
// 取反
// 在方括号的开头写上 ^ , 表示取反
// 1. /^[^0-9]$/ 除了0-9以外的任意单个字符
// 1. /^[^a-z]$/ 除了a-z以外的任意单个字符
// 1. /^[^abc]$/ 除了abc以外的任意单个字符
// [^a-zA-Z0-9]
console.log(/^[^0-9]$/.test('a'))
console.log(/^[^0-9]$/.test('9'))
1.6 字符组简写
javascript
// 字符组 简写
// \d [0-9] 记忆:digit 数字字符 小写
// \w [a-zA-Z0-9_] 记忆: word 单词字符
// \s [ \t\r\n\v\f] 记忆:空白字符 space
// 大写的 ==> \D \W \S 是刚才的分别取反得到
// \D [^0-9] 除了数字以外的任意单个字符
// \W [^0-9a-zA-Z_] 除了这些字符以外的
// \S [^ \t\r\n\v\f] 非空白字符
console.log(/\d/.test('abc')) // false [0-9]
console.log(/\D/.test('abc')) // true [^0-9]
console.log(/\w/.test('9999')) // true 单词包含了数字
// 匹配 2023-4-1 日期 的正则?
const reg = /^\d{4}-\d{1,2}-\d{1,2}$/
1.7 替换和修饰符
javascript
// 1. 字符串的方法 替换
// str.replace(/正则/, 要替换成什么)
// 返回值:替换之后的字符串
const str = '练练老师和果果老师有基情~'
const resStr = str.replace(/基情/, '**')
console.log(resStr)
// 2. 修饰符
// 约束正则的某些细节行为,比如是否区分大小写,是否匹配所有情况
// /正则/修饰符
// 2.1 掌握两个修饰符
// 1. i ignore 忽略 忽略大小写
// 2. g global 全局匹配,找到所有匹配的
// eg2.
const str2 = 'Java是一门编程语言,学完Java之后找到工作,薪资还不错~'
const res = str2.replace(/java/ig, 'Go')
console.log(res)
1.8 正则练习替换
javascript
// 隐藏手机号中间四位案例
const tel = '13611112222'
// 1. 利用正则,把手机号化为3份
// 正则用小括号分组了
const reg = /^(\d{3})\d{4}(\d{4})$/
// 2. 利用replace替换
// $1 可以得到正则第一个小括号中的内容 136
// $2 可以取到正则第二个小括号中的内容 2222
const resStr = tel.replace(reg, '$1****$2')
console.log(resStr)
document.querySelector('.phone').innerHTML = resStr
// const res = tel.replace(reg, '$1')
// console.log(res)
// 3. 新需求:只显示后面四位手机号 *******2222
const reg2 = /^\d{7}(\d{4})$/
const resStr2 = tel.replace(reg2, `${'*'.repeat(7)}$1`)
// const resStr2 = tel.replace(reg2, `*******$1`)
// 字符串重复 repeat方法
// console.log('ab'.repeat(7))
console.log(resStr2)
2.扩展
2.1 函数返回值
javascript
const fn = function(){
console.log('今天大家学废了吗?')
console.log('今天又废了')
console.log('今天站起来了')
return 666
}
const res = fn()
console.log(res)
// 1. 函数内部,如果不写return,那么调用函数之后的表达式的值就是undefined
// 2. 如果函数内部写了return,fn() 的值 就是 return后面的值
2.2 flag状态切换
html
<button>click</button>
<script>
const btn = document.querySelector('button')
let flag = true // == true表示 白色
// 当我们需要做一种状态切换的时候,就可以使用flag变量 status
// 比如开关的时候,一个开,一个关
btn.addEventListener('click', function(){
if (flag){
document.body.style.backgroundColor = 'black'
// 样式变了,状态我们也一起改变
flag = false // 表示现在是关着的状态
} else {
document.body.style.backgroundColor = 'white'
flag = true
}
})
</script>
2.3 change事件
change 当输入的内容发生改变的时候,并且按下回车或者失去焦点时触发 input:只要内容发生改变就触发
javascript
const input = document.querySelector('input')
// input 当我们用户输入的内容发生改变的时候,实时触发
input.addEventListener('input', function(){
console.log(input.value)
})
// 针对表单 change 事件
// change : 1. 当我们输入的内容发生改变的时候
// 2. 并且失去焦点或者按下回车的时候触发
input.addEventListener('change', function(){
console.log(input.value)
})
3. 综合案例
todo01 发送验证码
javascript
// 1. 发送短信5秒倒计时业务
// 获取元素,注册事件,开启定时器 setInterval() 关定时器
const btn = document.querySelector('.code')
console.log(btn)
// 注册点击事件
btn.addEventListener('click', function(){
let i = 5
// 点完之后文字内容立马就切换了
btn.innerHTML = `${i}秒后重新获取`
let timerId = setInterval(function(){
i--
btn.innerHTML = `${i}秒后重新获取`
if (i === 0) {
// 清除定时器
clearInterval(timerId)
btn.innerHTML = `重新获取`
}
}, 1000)
})
验证码多次点击问题解决 - flag
javascript
// 获取元素,注册事件,开启定时器 setInterval() 关定时器
const btn = document.querySelector('.code')
console.log(btn)
// 注册点击事件
let flag = true // ===> 表示可以点击的状态
btn.addEventListener('click', function(){
// 第一次点击的时候 flag 为true, 进入if条件中,开启了一个定时器,同时将状态切换为false
// 第二次点击的时候,5秒之后,flag为false,进不了条件,
// 只有5秒之后,flag变为true,又可以开启定时器
if (flag) {
flag = false // 点完之后,状态就应该是不能点击了
let i = 5
// 点完之后文字内容立马就切换了
btn.innerHTML = `${i}秒后重新获取`
let timerId = setInterval(function(){
i--
btn.innerHTML = `${i}秒后重新获取`
if (i === 0) {
// 清除定时器
clearInterval(timerId)
btn.innerHTML = `重新获取`
flag = true // 当倒计时为0,让btn又可以重新点击
}
}, 1000)
}
})
// 要的效果是,1. 点击了一次之后,不能再点击了(不能再开启定时器了)
// 只能让当前环境中,只有一个定时器在运行
// 什么时候又可以重新点击? 2. 当我们倒计时为0的时候,又可以让他重新点击
// ==> 联想到flag,做状态切换,因为分析了一下,可以点,和不可以点两种状态
flag的另一种写法
javascript
let flag = true // ===> 表示可以点击的状态
btn.addEventListener('click', function(){
// 第一次点击的时候 flag 为true, 进入if条件中,开启了一个定时器,同时将状态切换为false
// 第二次点击的时候,5秒之后,flag为false,进不了条件,只有5秒之后,flag变为true,
// 又可以开启定时器
if (!flag) return
// 一开始flag true, !flag ==> false , if(!flag) 不成立,后面的return就不生效,
// 那么下面的代码就会执行
// 当flag => true 下面的代码就会执行
// 当点完一次之后, flag 为false , !flag ==> true , if (!flag) 成立,return生效,
// 后面的代码不再执行
// 当flag => false 下面的代码就不执行
// if (flag) {
flag = false // 点完之后,状态就应该是不能点击了
let i = 5
// 点完之后文字内容立马就切换了
btn.innerHTML = `${i}秒后重新获取`
let timerId = setInterval(function(){
i--
btn.innerHTML = `${i}秒后重新获取`
if (i === 0) {
// 清除定时器
clearInterval(timerId)
btn.innerHTML = `重新获取`
flag = true // 当倒计时为0,让btn又可以重新点击
}
}, 1000)
// }
})
todo02 验证用户名
javascript
const username = document.querySelector('[name="username"]')
const verifyName = function(){
const reg = /^[a-zA-Z0-9-_]{6,10}$/
// if (regName.test(username.value)){
// username.nextElementSibling.innerHTML = ''
// return true
// } else {
// username.nextElementSibling.innerHTML = '请输入6-10位'
// return false
// }
// 反着逻辑改改 ==> 如果验证不通过
if (!regName.test(username.value)){
// 不通过的逻辑
username.nextElementSibling.innerHTML = '请输入6-10位'
return false
}
username.nextElementSibling.innerHTML = ''
return true
}
username.addEventListener('change', verifyName)
todo03 其他验证
javascript
// 用户名验证
const reg = /^[a-zA-Z0-9-_]{6,10}$/
// 手机号
const reg = /^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
// 验证码
const reg = /^\d{6}$/
// 密码验证
const reg = /^[a-zA-Z0-9-_]{6,20}$/
javascript
/* ==================== 手机号验证 ===================== */
const phone = document.querySelector('[name="phone"]')
const verifyPhone = function(){
const reg = /^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
// 反着逻辑改改 ==> 如果验证不通过
if (!reg.test(phone.value)){
// 不通过的逻辑
phone.nextElementSibling.innerHTML = '请输入正确的手机号'
return false
}
phone.nextElementSibling.innerHTML = ''
return true
}
phone.addEventListener('change', verifyPhone)
/* ==================== 短信验证码验证 ===================== */
const code = document.querySelector('[name="code"]')
const verifyCode = function(){
const reg = /^\d{6}$/
if (!reg.test(code.value)){
code.nextElementSibling.innerHTML = '请输入正确的验证码'
return false
}
code.nextElementSibling.innerHTML = ''
return true
}
code.addEventListener('change', verifyCode)
/* ==================== 密码验证 ===================== */
const pwd = document.querySelector('[name="password"]')
const verifyPwd = function(){
const reg = /^[a-zA-Z0-9-_]{6,20}$/
if (!reg.test(pwd.value)){
pwd.nextElementSibling.innerHTML = '请输入正确的格式,数字字母下划线 6-20位'
return false
}
pwd.nextElementSibling.innerHTML = ''
return true
}
pwd.addEventListener('change', verifyPwd)
/* ==================== 密码再次确认验证 ===================== */
const pwdConfirm = document.querySelector('[name="confirm"]')
const verifyRePwd = function(){
if (pwdConfirm.value !== pwd.value){
pwdConfirm.nextElementSibling.innerHTML = '两次密码不一致'
return false
}
pwdConfirm.nextElementSibling.innerHTML = ''
return true
}
pwdConfirm.addEventListener('change', verifyRePwd)
todo04 阅读同意协议
点击的时候切换添加一个类名
icon-queren2
javascript
const agree = document.querySelector('.icon-queren')
agree.addEventListener('click', function(){
// 切换,一开始没有这个类,添加
// 再次点击,有了这个类,移除
this.classList.toggle('icon-queren2')
})
todo05 提交整个表单的验证
- 先判断是否勾选了同意协议, 如果没有勾选,不提交 ,阻止默认提交 e.preventDefault()
- 校验每一个表单是否满足校验条件,如果没有通过校验,阻止默认提交 e.preventDefault()
javascript
const form = document.querySelector('.xtx-form')
form.addEventListener('submit', function(e){
// 1. 如果同意协议没有勾选,我们就阻止默认提交的行为
if (!agree.classList.contains('icon-queren2')){
e.preventDefault() // 阻止默认提交
return alert('请勾选同意协议') // 加上return 阻止后面的代码运行
}
// 判断某个元素是否包含某个类名,包含返回true,否则,返回false
// console.log(agree.classList.contains('icon-queren2'))
// 2. 之前的那些输入的表单,如果验证不通过,也要阻止默认提交
// 连着写,逻辑中断,后面的不会执行
if (!verifyName()) e.preventDefault()
if (!verifyPhone()) e.preventDefault()
if (!verifyCode()) e.preventDefault()
if (!verifyPwd()) e.preventDefault()
if (!verifyRePwd()) e.preventDefault()
// if (!verifyName() || !verifyPhone() || !verifyCode()
// || !verifyPwd() || !verifyRePwd()){
// e.preventDefault()
// }
})
todo06 登录页tab切换 login.html
javascript
const tab = document.querySelector('.tab-nav')
const tabPanes = document.querySelectorAll('.tab-pane')
tab.addEventListener('click', function(e){
if (e.target.tagName === 'A'){
// 上面的tab高亮
tab.querySelector('.active').classList.remove('active')
e.target.classList.add('active')
// 下面对应的盒子显示
console.log(e.target.dataset.id)
// 1. 先让其他的隐藏掉, 干掉所有人
// for(let i = 0; i < tabPanes.length; i++){
// tabPanes[i].style.display = 'none'
// }
// 注意:querySelectorAll获取的伪数组 有forEach!
// 但是children子元素伪数组没有forEach方法
console.dir(tabPanes)
tabPanes.forEach(function(el){
el.style.display = 'none'
})
// 2. 让对应的盒子显示
tabPanes[e.target.dataset.id].style.display = 'block'
} // end if
})
todo07 登录页login.html 用户名密码输入登录去首页
javascript
// 点击登录 , 要把用户名存到本地,给首页index使用
const form = document.querySelector('form')
const agree = document.querySelector('.remember')
const username = document.querySelector('[name="username"]')
form.addEventListener('submit', function(e){
e.preventDefault()
if (!agree.checked){
return alert('请勾选同意')
}
// 同意了,要把用户名存到本地
localStorage.setItem('xtx-username', username.value)
location.href = './index.html' // 跳转
})
通过action跳转
javascript
form.addEventListener('submit', function(e){
// e.preventDefault() // 一开始就阻止了跳转到action的地址上
if (!agree.checked){
e.preventDefault()
return alert('请勾选同意')
}
// 同意了,要把用户名存到本地
localStorage.setItem('xtx-username', username.value)
// location.href = './index.html'
// 一个是可以用location.href
// 还可以用form表单的action属性设置跳转的地址,但是注意 ==> 会被e.preventDefault()阻止
})
首页渲染
javascript
;(function(){
const li1 = document.querySelector('.xtx_navs').children[0]
const li2 = li1.nextElementSibling
// 一进入页面,就渲染两个li标签
// 本地存了username,
const render = function(){
// 这里不需要转,因为刚才存的就是字符串
const username = localStorage.getItem('xtx-username')
if (username){
li1.innerHTML = `
<a href="javascript:;">
<i class="iconfont icon-user">${username}</i>
</a>
`
li2.innerHTML = `<a href="javascript:;">退出登录</a>`
} else {
li1.innerHTML = `<a href="./login.html">请先登录</a>`
li2.innerHTML = `<a href="./register.html">免费注册</a>`
}
}
// 已进入页面,调用render
render()
// 点击退出登录
li2.addEventListener('click', function(){
// 删除本地的username
localStorage.removeItem('xtx-username')
render()
})
})()