一、事件添加和删除
<!-- 1. 添加到元素事件属性上 -->
<button class="btn" onclick="console.log(this.textContent);">Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
<script>
const btn2 = document.querySelector("button:nth-of-type(2)");
// 2. 使用js脚本动态添加事件属性的方式
btn2.onclick = function () {
console.log(this.textContent);
};
btn2.onclick = function () {
this.style.color = "red";
// 控制台没有输出,说明第一个点击事件没有执行
};
// onclick这样的事件属性不允许重复定义同一个事件到同一个元素上
// 通过移除事件属性的方式来删除事件
btn2.onclick = null;
// 3. 事件监听器/注册器
const btn3 = document.querySelector("button:nth-of-type(3)");
// btn3.addEventListener(事件类型,事件回调(异步回调));
// click事件名称,不能有on,带on的叫事件属性
btn3.addEventListener("click", (ev) => console.log(ev.target.textContent, "第1次"));
btn3.addEventListener("click", (ev) => console.log(ev.target.textContent, "第2次"));
// 4. 移除事件监听器
// 通过回调函数添加的事件方法的事件是不能被移除的
const handle = () => console.log(btn3.textContent, "第3次");
btn3.addEventListener("click", handle);
btn3.removeEventListener("click", handle);
// 5. 事件派发
const btn4 = document.querySelector("button:last-of-type");
// 将一个自定义的事件派发给指定的元素
let i = 0;
btn4.addEventListener("click", function () {
console.log("点击了广告,你已赚了: " + i + "元");
i += 0.5;
});
const ev = new Event("click");
// 每派发一次click,就会自动点击了这个按钮
// btn4.dispatchEvent(ev);
// setInterval("btn4.dispatchEvent(ev)", 1000);
setInterval(() => btn4.dispatchEvent(ev), 1000);
</script>
二、事件传递
- 捕获: 从最外层元素逐级向内直到事件的绑定者
- 目标: 到达事件目标
- 冒泡: 从目标再由内向外逐级向上直到最外层元素
1.事件捕获
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
const items = document.querySelectorAll("li");
items.forEach(function (item) {
item.onclick = function (ev) {
// ev: 事件对象,保存了当前事件的所有细节
// console.log(ev.type);
// console.log(ev.target.textContent);
// ev.target: 事件的触发者
// console.log(ev.target);
// ev.currentTarget: 事件的绑定者
console.log(ev.currentTarget);
// 当前ev.target === ev.currentTarget
// console.log(ev.target === ev.currentTarget);
// console.log(ev.target === this);
};
});
// 1.捕获, window, document,html.....
// 第三个参数是事件的传递机制: true, 捕获阶段, false, 冒泡阶段(默认值)
window.addEventListener("click", ev => console.log(ev.currentTarget), true);
// document
document.addEventListener("click", ev => console.log(ev.currentTarget), true);
// html
document.documentElement.addEventListener("click", ev => console.log(ev.currentTarget), true);
// body
document.body.addEventListener("click", ev => console.log(ev.currentTarget), true);
// ul
document.querySelector("ul").addEventListener("click", ev => console.log(ev.currentTarget), true);
</script>
2.事件冒泡
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
const items = document.querySelectorAll("li");
items.forEach(function (item) {
item.onclick = function (ev) {
// ev: 事件对象,保存了当前事件的所有细节
// console.log(ev.type);
// console.log(ev.target.textContent);
// ev.target: 事件的触发者
// console.log(ev.target);
// ev.currentTarget: 事件的绑定者
console.log(ev.currentTarget);
// 当前ev.target === ev.currentTarget
// console.log(ev.target === ev.currentTarget);
// console.log(ev.target === this);
};
});
// 2.冒泡阶段,将第三个参数设置为false
window.addEventListener("click", ev => console.log(ev.currentTarget), false);
// document
document.addEventListener("click", ev => console.log(ev.currentTarget), false);
// html
document.documentElement.addEventListener("click", ev => console.log(ev.currentTarget));
// body
document.body.addEventListener("click", ev => console.log(ev.currentTarget));
// ul
document.querySelector("ul").addEventListener("click", ev => console.log(ev.currentTarget));
</script>
三、事件冒泡的应用:事件委托
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
// 给所有的li添加点击事件
// 第一步先获取所有的li
// const items = document.querySelectorAll("li");
// 循环的给所有li添加点击事件
// items.forEach((items) => (items.onclick = (ev) => console.log(ev.currentTarget)));
// 根据事件冒泡的原理,子元素上的同名事件会自动传递到父元素上的同名事件
// 如果将事件直接绑定到父元素上,那么在子元素上点击时,会自动触发父元素上的同名事件
// 事件的绑定者与事件的触发者不相同
// 事件绑定者: 父元素 ul
// 事件触发者: 子元素 li
document.querySelector("ul").onclick = (ev) => {
// 事件绑定者: 父元素 ul
console.log(ev.currentTarget);
// 事件触发者: 子元素 li
console.log(ev.target);
};
</script>
四、表单事件
<style>
body {
background-color: mediumturquoise;
color: #444;
font-weight: lighter;
}
#login {
width: 20em;
border-radius: 0.3em;
box-shadow: 0 0 1em #888;
box-sizing: border-box;
padding: 1em 2em 1em;
margin: 2em auto;
background-color: paleturquoise;
display: grid;
grid-template-columns: 3em 1fr;
gap: 1em 0;
}
#login .title {
grid-area: auto / span 2;
place-self: center;
}
#login input {
border-radius: 0.3em;
border: none;
padding-left: 0.3em;
}
#login input:focus {
outline: none;
box-shadow: 0 0 5px seagreen;
border-radius: 0.2em;
transition: 0.5s;
}
#login button {
grid-area: auto / 2 / auto;
outline: none;
background-color: lightseagreen;
border: none;
height: 2em;
color: #fff;
}
#login button:hover {
cursor: pointer;
background-color: seagreen;
transition: 0.5s;
}
</style>
<!-- 表单 -->
<form action="" method="POST" id="login">
<label class="title">用户登录</label>
<label for="email">邮箱:</label>
<input type="email" id="email" name="email" value="" autofocus />
<label for="password">密码:</label>
<input type="password" id="password" name="password" />
<!-- <button name="submit" type="button">登录</button> -->
<button name="submit">登录</button>
</form>
<script>
// 获取表单
const login = document.forms.login;
// login.onsubmit = () => alert("提交了");
// ev.preventDefault();专用于禁用元素的默认行为,例如跳转,提交
// login.onsubmit = (ev) => ev.preventDefault();
// console.log(login.submit);
login.submit.onclick = (ev) => {
// 禁止默认行为
ev.preventDefault();
// 禁止冒泡
ev.stopPropagation();
// console.log(ev.currentTarget);
// form属性返回控件所属的表单对象
// console.log(ev.currentTarget.form);
// 非空验证
isEmpty(ev.currentTarget.form);
};
function isEmpty(form) {
console.log(form.email.value.length);
console.log(form.password.value.length);
if (form.email.value.length === 0) {
alert("邮箱不能为空");
// 将输入焦点设置到邮箱的输入框中
form.email.focus();
return false;
} else if (form.password.value.length === 0) {
alert("密码不能为空");
// 将输入焦点设置到密码的输入框中
form.email.focus();
return false;
} else {
alert("验证通过");
}
}
</script>
与表单相关的事件:
- focus: 获取焦点事件
- blur: 失去焦点事件
- input: 只要值发生变化时连续触发,不等失去焦点
- change: 值发生了改变且失去焦了点时触发,
- select: 选中文本时触发,
- invalid: 提交时表单元素值不满足验证条件时触发
- reset: 将表单值全部重置到默认值(并非清空)
- submit: 提交表单时触发,注意触发对象是,提交的是表单不是按钮
- keydown: 按下键盘时
- keyup:松开键盘时
- keypress: 按过了键盘时, 按下有值键时(除Ctrl/Alt/Shift/Meta),先触发keydown
- 按下一直不放手的触发顺序: keydown,keypress,重复这二个事件,直到keyup
- load,error: 在后面xhr时细说
五、实战:留言板/评论
html 文件:
<!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>留言板</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<form class="comment">
<label for="content">请留言:</label>
<!-- maxlength="100" 设置文本可输入的最大长度 -->
<textarea name="content" id="content" placeholder="不要超过100个字符" maxlength="100" cols="30" rows="5""></textarea>
<span class="notice">还可以输入100字</span>
<button class="submit" type="button" name="submit">提交</button>
</form>
<ul class="list"></ul>
<script>
// 获取元素
const comment = document.querySelector(".comment");
const content = comment.content;
const submitBtn = comment.submit;
const commentList = document.querySelector(".list");
const notice = document.querySelector(".notice");
// 提交按钮的点击事件
submitBtn.onclick = (ev) => {
// 获取用户的留言内容
let value = content.value;
// 创建留言元素样式
const newComment = document.createElement("li");
newComment.textContent = value;
newComment.style.borderBottom = "1px solid white";
newComment.style.minHeight = "3em";
// 添加删除留言的功能,为每条留言添加删除按钮
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "删除";
deleteBtn.style.float = "right";
deleteBtn.classList.add("del-btn");
// 用户点击删除留言后的操作
deleteBtn.onclick = function(){
if(confirm('是否删除?')){
// 确定:true,取消: false
this.parentNode.remove();
alert("删除成功!")
content.focus();
return false;
}
}
// 将留言控制在 0~100 字
if(value.length > 0 && value.length <= 100){
// 将删除按钮追加到新留言的后面
newComment.append(deleteBtn);
// 将留言追加到页面中显示
commentList.prepend(newComment);
alert('留言成功');
content.value=null;
notice.textContent = `还可以输入100字`;
content.focus();
}else{
alert("没有内容或内容超出规定长度");
content.focus();
return false;
}
}
// oninput 监控输入, 提示信息
content.oninput=function(){
let textLength = content.value.trim().length;
notice.textContent = `还可以输入${100 - textLength}字`;
}
</script>
</body>
</html>
css 文件:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
body {
background-color: rgb(174, 236, 236);
color: #555;
}
.comment {
width: 85%;
margin: 1em auto;
display: grid;
gap: 0.5em;
}
.comment #content {
resize: none;
border: none;
padding: 0.5em;
outline: none;
}
.comment #content:focus,
.comment #content:hover {
box-shadow: 0 0 8px steelblue;
transition: 0.6s;
}
.comment .submit {
width: 30%;
margin-left: auto;
background-color: lightseagreen;
border: none;
outline: none;
color: white;
height: 2.5em;
}
.comment .submit:hover {
background-color: seagreen;
transition: 0.6s;
cursor: pointer;
}
.list {
width: 80%;
/* background-color: yellow; */
margin: auto;
padding: 1em;
}
.del-btn {
background-color: wheat;
color: #000;
padding: 0.5em 1em;
/* height: 2.2em; */
border: none;
outline: none;
}
.del-btn:hover {
cursor: pointer;
background-color: rgb(31, 230, 163);
}
六、字符串方法
1.concat()
// 1. concat()拼装
let str = "html".concat("css", "js", "php", "!", 888);
console.log(str);
2.slice(start, end)
// 2. slice(start, end)取子串
str = "hello php.cn";
let res = str.slice(0, 5);
console.log(res);
res = str.slice(0);
console.log(res);
res = str.slice(6);
console.log(res); // php.cn
// -1是反向的第一个字符, n
res = str.slice(-6);
console.log(res); // php.cn
res = str.slice(-2);
console.log(res); // cn
3.substr(start, size)
// 3. substr(start, size): 取子串
res = str.substr(0, 5);
console.log(res); // hello
res = str.substr(-6, 3);
console.log(res); // php
4.trim()
// 4. trim(): 删除字符串二边的空白字符
let psw = " root888 ";
console.log(psw.length);
console.log(psw.trim().length);
5. split()
// 5. 将字符串打散成数组
res = str.split("");
console.log(res);
// 从一个邮箱中解析出用户名和邮箱地址
let email = "admin@php.cn";
res = email.split("@");
console.log(res);
console.log("userName = ", res[0]);
console.log("emailAddress = ", res[1]);
七、数组常用方法
1.栈方法
// 1.栈方法
// 栈: 只允许在数组一端进行增删的操作
let arr = [];
// push():尾部添加,入栈
console.log(arr.push(1, 2, 3));
console.log(arr);
console.log(arr.push(4));
console.log(arr);
// pop():尾部删除:出栈
console.log(arr.pop());
console.log(arr);
console.log(arr.pop());
console.log(arr);
// 还有一对是头部进行元素的增删
// unsift(): 在头部添加
// shift(): 从头部删除
arr = [];
arr.unshift("a", "b", "c");
console.log(arr);
arr.unshift("d");
console.log(arr);
console.log(arr.shift());
console.log(arr);
console.log(arr.shift());
console.log(arr);
2.join()
// 2. join(): 与字符串 split()相反,将数组转为字符串
arr = ["电脑", "西瓜", "外套"];
// 默认转为逗号连接的字符串
let res = arr.join();
let res = arr.join("</li><li>");
console.log(`<li>${res}</li>`);
3.concat()
// 3. concat()
console.log("hello".concat("php.cn"));
console.log(["hello"].concat(["php.cn"]));
4.slice()
// 4. slice(): 取部分成员
arr = [1, 2, 3, 4, 5];
res = arr.slice(0, 3);
res = arr.slice(-2);
res = arr.slice(-3);
console.log(res);
5.splice()
// 5. splice(): 增删改
arr = [1, 2, 3, 4, 5];
// 删除,1个或2个参数
res = arr.splice(2, 2);
console.log(res);
console.log(arr);
// 新增
arr = [1, 2, 3, 4, 5];
// 不删除元素,将第2个参数设为0,但是要传入第三个参数
res = arr.splice(1, 0, 88, 99);
res = arr.splice(1, 0, ...[88, 99, 66]);
console.log(arr);
// 更新
// 更新元素,将第2个参数设为删除数量,但是要传入第三个参数,用来替换掉被删除的元素
arr = [1, 2, 3, 4, 5];
res = arr.splice(1, 3, ...[88, 99, 66]);
console.log(arr);
6.forEach()
// forEach功能一样,但有一个返回值 map()
7.filter()
// 7. filter()对每个成员应用回调函数进行处理返回true的成员组成的数组
arr = [1, 2, 3, 4, 5];
// res = arr.filter((item) => item % 2);
res = arr.filter(item => !(item % 2));
console.log(res);
8.reduce()
// 8. reduce():归内操作,将多成员进行统计后,单值返回
res = [1, 2, 3, 4, 5];
// res = arr.reduce(function (prev, curr) {
// return (prev += curr);
// });
// 使用箭头函数,简化写法
res = arr.reduce((prev, curr) => (prev += curr), 20);
console.log(res);