例如鼠标单击事件 onclick 、双击事件 onmouseover 、鼠标移入移出事件 onmouseover 、onmouseout 。又可分为两种。
① 直接写在 HTML 的属性中<button onclick="alert('hello world')">Click</button>
② 在 HTML 中自定义函数
<button onclick="func()">Click</button>
<script type="text/javascript">
var func = () => alert('hello world');
</script>
第一种方法将JS事件和HTML标签写在一起,不利于项目的管理和开发。为了使代码结构清晰合理,按照不同功能将其分离将提高效率。
<button id="btn">Click</button>
<script type="text/javascript">
// 添加事件
document.getElementById('btn').onclick = function func() {
alert('hello world');
}
// 移除事件
document.getElementById('btn').onclick = null;
</script>
虽然第二种方法比第一种好,但也有不足之处。一般一个点击事件上有时候不止触发一个事件。一种设想是把 func() 函数再套一层函数,比如把定义的函数 a() 和 b() 放在 func() 中。但是这样未免太过烦琐了,于是使用 addEventListenr(),他可以给元素重复添加多个事件。
语法:target.addEventListener(type, listener, options);
① target 是DOM 操作获得的对象
② type 指事件类型的字符串。例如 click、mouseout
③ listener 在这里指当事件出发的时候执行的函数。可直接写函数名,也可直接定义函数然后用引号括起来。
④ options 可选( 函数的冒泡或者捕获, false冒泡 / true 捕获)
<button id="btn">Click</button>
<script type="text/javascript">
const btn = document.getElementById('btn');
// 注意:这里func1不能添加括号,不然就立即执行函数了。我们需要给事件添加,当触发事件的时候,执行该函数,所以只需要将函数名称赋值给事件。
btn.addEventListener('click', function(){
console.log("你好")
});
// 注意:上面通过回调添加的事件是无法移除的
btn.addEventListener('click', func1);
btn.addEventListener('mouseout', func2);
function func1() {
console.log("hello")
};
function func2() {
console.log("world")
};
// 鼠标点击事件和移开鼠标事件都被执行,分别输出 hello、world
// 移除mouseout事件
btn.removeEventListener("mouseout",func2);
</script>
接收两个参数:
// 兼容性处理
function addEvent(ele, name ,fn) {
if(ele.attachEvent){
obj.attachEvent("on"+name, fn);
}else{
obj.addEventListener(name,fn)
}
}
这是标准的触发事件方法,使用时需要先创建好事件对象。如下
// 自定义事件
const ev = new Event("click");
// 获取按钮
const btn = document.querySelector(".btn");
let i = 0;
// 绑定点击事件
btn.addEventListener('click',function(){
console.log("我会被自动点击"+++i+"次")
})
// 自动触发点击事件
btn.dispatchEvent(ev);
// 使用定时器来自动点击按钮
setInterval("btn.dispatchEvent(ev)",1000);
btn.addEventListener('click',function(e){
// 事件类型
console.log(e.type);
// 事件绑定者
console.log(e.currentTarget);
// 事件触发者
console.log(e.target);
// 事件传递路径
console.log(e.path)
})
event propagation事件冒泡
element.addEventListener(‘click’, listener, useCapture); useCapture 默认是false, 表示使用bubble(冒泡)。
在DOM事件标准中,定义了事件传播的3个阶段
addEventListener第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
<div id="s1">红
<div id="s2"> 黄
<div id="s3">蓝</div>
</div>
</div>
测试事件冒泡-点击蓝色
s1 = document.getElementById('s1')
s2 = document.getElementById('s2')
s3 = document.getElementById('s3')
s1.addEventListener("click", function(e) {
console.log("红 冒泡事件"); //从底层往上
}, false);
s2.addEventListener("click", function(e) {
console.log("黄 冒泡事件");
});
s3.addEventListener("click", function(e) {
console.log("蓝 冒泡事件");
// 事件传递路径
console.log(e.path)
});
测试事件捕获-点击蓝色
s1.addEventListener("click",function(e){
console.log("红 捕获事件");
},true);
s2.addEventListener("click",function(e){
console.log("黄 捕获事件");
},true);
s3.addEventListener("click",function(e){
console.log("蓝 捕获事件");
},true);
事件捕获与事件冒泡同时存在
事件捕获过程中,先捕获后冒泡
事件到达目标节点,先注册先执行
s1.addEventListener("click",function(e){
console.log("红 冒泡事件");
},false);
s2.addEventListener("click",function(e){
console.log("黄 冒泡事件");
},false);
s3.addEventListener("click",function(e){
console.log("蓝 冒泡事件");
},false);
s1.addEventListener("click",function(e){
console.log("红 捕获事件");
},true);
s2.addEventListener("click",function(e){
console.log("黄 捕获事件");
},true);
s3.addEventListener("click",function(e){
console.log("蓝 捕获事件");
},true);
在不使用任何框架的情况下,我们在js中通过addEventListener方法给Dom添加事件监听。这个方法有三个参数可以传递addEventListener(event,fn,useCapture)。event是事件类型click,focus,blur等;fn是事件触发时将执行的函数方法(function);第三个参数可以不传,默认是false,这个参数控制是否捕获触发。所以我们只传两个参数时,这个事件是冒泡传递触发的,当第三个参数存在且为true时,事件是捕获传递触发的。
<a>
,提交按钮<input type=”submit”>
等。当Event 对象的 cancelable为false时,表示没有默认行为,这时即使有默认行为,调用preventDefault也是不会起作用的。比如我想点击ul标签里面的li获取它的值,有点人就会遍历去给每个li加一个事件监听
其实我们可以在li的父级加一个事件监听,这就相当于把事件监听委托给了ul。
我们点击li的时候,事件冒泡到ul,被注册在ul的事件代理给捕获到,实现了事件委托机制。
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
</ul>
<script>
ul = document.getElementById('ul')
ul.addEventListener("click",function(e){
console.log(e.target.innerText);
},false);
</script>
让我们用事件委托来捕获多个按钮上的点击:
<div id="buttons"> <!-- Step 1 -->
<button class="buttonClass">Click me</button>
<button class="buttonClass">Click me</button>
<!-- buttons... -->
<button class="buttonClass">Click me</button>
</div>
<script>
document.getElementById('buttons').addEventListener('click', event => { // Step 2
if (event.target.className === 'buttonClass') { // Step 3
console.log('Click!');
}
});
</script>
事件委托的思想很简单。你不需要把委托事件监听器直接附加到按钮上,而是可以委托父监听 <div id="buttons">
。单击按钮时,父元素的侦听器将会捕获冒泡事件
使用事件委托需要 3 个步骤:
步骤 1:确定要监视事件的元素的父级元素
在上面的例子中, <div id="buttons">
是按钮的父元素。
步骤 2:把事件侦听器附加到父元素document.getElementById('buttons') .addEventListener('click', handler)
将事件侦听器附加到按钮的父元素。该事件侦听器也会对按钮单击做出反应,因为按钮单击事件冒泡通过祖先元素(由于事件传播)。
步骤 3:用 event.target 选择目标元素
单击按钮时,将会用event 对象参数调用处理函数。属性 event.target 访问在其上调度了事件的元素,在例子中是一个按钮:
.addEventListener('click', event => {
if (event.target.className === 'buttonClass') {
console.log('Click!');
}
});
event.currentTarget
指向事件侦听器直接附加到的元素。在例子中,event.currentTarget
是 <div id="buttons">
。
当发生点击事件(或传播的任何其他事件)时:
事件委托是一种有用的模式,因为你可以只需要用一个事件处理程序就能侦听多个元素上的事件。
使用事件委托需要三个步骤:
属性 | 此事件发生在何时…… |
---|---|
onclick | 当用户点击某个对象时调用 |
ondblclick | 当用户双击某个对象时调用 |
onmousedown | 鼠标按钮被按下 |
onmousemove | 鼠标被移动 |
onmouseout | 鼠标从某元素移开 |
onmouseover | 鼠标移到某元素之上 |
onmouseup | 鼠标按键被松开 |
onfocus | 元素获得焦点 |
onblur | 元素失去焦点 |
onchange | 域的内容被改变 |
onkeydown | 某个键盘按键被按下 |
onkeypress | 某个键盘按键被按下并松开 |
onkeyup | 某个键盘按键被松开 |
onload | 加载完成时 |
<form action="" method="POST" id="login" name="login">
<input type="text" name="username" id="username" placeholder="请输用户名">
<input type="password" name="password" id="password" placeholder="输入密码">
<input type="submit" value="登录">
</form>
获取表单form元素
// const myForm = document.forms[0];
// const myForm = document.forms.item(0);
// const myForm = document.forms.namedItem("login");
// 注意:form.id === form.name
// 除了document.forms[0]和forms.item(0)获取,也可以通过表单ID
// const myForm = document.forms['login'];
const myForm = document.forms.login
表单的提交事件
const myForm = document.forms['login'];
myForm.submit.onclick = function(e){
console.log("表单提交时触发事件");
// 返回当前的绑定者
console.log(e.currentTarget);
// 返回当前表单
console.log(e.currentTarget.form);
// 非空验证
isEmpty(e.currentTarget.form);
// 我们一般都是自定义表单的提交行为,需要禁用默认的提交
// 取消表单提交按钮的默认事件
// return false;
// 取消默认行为
e.preventDefault();
// 取消事件冒泡
e.stopPropagation()
}
function isEmpty(form){
console.log(form.username.value);
console.log(form.password.value);
if(form.username.value.length){
alert("用户名不能为空");
// 将输入焦点设置到邮箱输入框中
form.username.focus();
return false;
} else if(form.password.value.length){
alert("密码不能为空");
// 将输入焦点设置到邮箱输入框中
form.password.focus();
return false;
}else{
alert("验证成功!");
}
}
表单事件 | 何时触发该事件…… |
---|---|
focus | 获取焦点事件 |
blur | 失去焦点事件 |
input | 只要值变化时连续触发,不等失去焦点 |
change | 值发烧改变且失去焦点时触发 |
select | 选中文本时触发 |
invalid | 提交时表单元素值不满足验证条件时触发 |
reset | 将表单值全部重置到默认值(非清空) |
submit | 提交表单时触发,触发的是from,不是按钮 |
keydown | 按下键盘时 |
keyup | 松开键盘时 |
keypress | 按过了键盘时,keydown,keypress,keyup |
load,error | 加载和出错时 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
* {
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: red;
padding: 0.5em 1em;
/* height: 2.2em; */
border: none;
outline: none;
}
.del-btn:hover {
cursor: pointer;
background-color: lime;
}
.info {
font-size: 12px;
color: #aaa;
display: none;
}
</style>
</head>
<body>
<form class="comment">
<label for="content">请留言:</label>
<textarea name="content" id="content" cols="30" rows="5" placeholder="不要超过100个字符" maxlength="100"></textarea>
<span class="info">您还可以输入100个字符</span>
<button class="submit" type="button" name="submit">提交</button>
</form>
<ul class="list ">
</ul>
<script>
// 获取元素
// from
const comment = document.querySelector('.comment');
// textarea
const content = comment.content;
// btn
const submitBtn = comment.submit;
// ul
const commentList = document.querySelector(".list");
// info
const info = document.querySelector(".info");
// 提交按钮点击事件
submitBtn.onclick = function(ev) {
let value = content.value.trim();
if (value.length > 0 && value.length <= 100) {
// 将留言输入的内容插入到列表中
// 最新的留言总是在第一条
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(ev) {
if (confirm("是否删除 ")) {
// 确定:true 取消:false
this.parentNode.remove();
alert('删除成功!');
content.focus();
}
}
// 将删除按钮添加到留言的后面
newComment.append(deleteBtn);
// 将最新留言添加到列表头部
commentList.prepend(newComment);
// 通知用户留言成功
alert('留言成功!');
// 清空输入框
content.value = null;
// 隐藏提示字符
info.style.display = 'none';
// 焦点重新定位到输入框中
content.focus();
} else {
alert("没有内容或者内容超出规定长度…… ");
content.focus();
return false;
}
}
// 输入框的输入事件
content.oninput = function() {
info.style.display = "inline";
info.innerHTML = `您还可以输入${100-this.value.length}个字符`;
}
</script>
</body>
</html>
演示效果图:
为留言板添加字数实时统计与禁止超出功能
解决思路:实时剩余字数统计,主要是给输入框添加input事件监听,只要输入内容就会触发该事件,从而动态的计算剩余字符个数,显示在标签中提示用户。
禁止超出功能,当达到一百个字符的时候,让用户无法输入,可以给textarea标签添加一个 maxlength=”100”属性,从而限制最大字符个数。
//concat(v1, v2,..);
let message="Sam"
let final=message.concat(" is a"," hopeless romantic.")
console.log(final);
//alerts "Sam is a hopeless romantic."
//slice(start, end)
let text="excellent"
text.slice(0,4) // "exce"
text.slice(2,4) // "ce"
// 省略第二个值,就是从当前位置到结束
text.slice(4); // llent
text.slice(0); // excellent
// -1 是反向的第一个字符
text.slice(-4); // lent
//substring(from, to)
let text="excellent"
text.substr(0,4) // "exce"
text.substr(2,4) // "cell"
text.substr(-4,3) // len
//trim()
let str = " Hello Edureka! ";
console.log(str.length); // 24
console.log(str.trim()); // Hello Edureka!
console.log(str.trim().length); // 14
//toLowerCase()
let myString = 'JAVASCRIPT ROX';
myString = myString.toLowerCase();
console.log(myString) // javascript rox
//toUpperCase()
let myString = 'javascript rox';
myString = myString.toUpperCase();
console.log(myString) // JAVASCRIPT ROX
let str = "abcdefg";
let res = str.split('');
console.log(res); // ["a", "b", "c", "d", "e", "f", "g"]
console.log(res.join('|')); // a|b|c|d|e|f|g
// 从一个邮箱中解析出用户名和邮箱地址
let email = "zhangshuai@163.cn";
res = email.split("@");
console.log(email.split("@")); // ["zhangshuai", "163.cn"]
console.log("userName=",res[0]); // zhangshuai
console.log("emailAddress=",res[1]); // 163.cn
//endsWith()
let email1 = "zhangshuai@163.cn";
let email2 = "zhangshuai@qq.cn";
console.log(email1.endsWith("163.cn")); // true
console.log(email2.endsWith("163.cn")); // false
const arr = ["Lily","lucy","Tom"];
var count = arr.push("Jack","Sean");
console.log(count); // 5
console.log(arr); // ["Lily", "lucy", "Tom", "Jack", "Sean"]
var item = arr.pop();
console.log(item); // Sean
console.log(arr); // ["Lily", "lucy", "Tom", "Jack"]
const arr = ["Lily","lucy","Tom"];
var count = arr.unshift("Jack","Sean");
console.log(count); // 5
console.log(arr); //["Jack", "Sean", "Lily", "lucy", "Tom"]
var item = arr.shift();
console.log(item); // Jack
console.log(arr); // ["Sean", "Lily", "lucy", "Tom"]
let arr = [1,2,3,4,5,6];
console.log(arr.join()); // 1,2,3,4,5,6
console.log(arr.join("-")); // 1-2-3-4-5-6
let str = "abcde";
console.log(str.split('')); // ["a", "b", "c", "d", "e"]
console.log(str.split('c')); // ["ab", "de"]
// 可以利用将其快速生成列表
arr = ['电脑','手机','平板'];
let res = arr.join("</li><li>");
console.log(`<li>${res}</li>`);
// <li>电脑</li><li>手机</li><li>平板</li>
let arr = [1,2,3,4,5,6];
console.log(arr.reverse()); // [6,5,4,3,2,1]
console.log(arr); // [6,5,4,3,2,1] (原数组改变)
console.log("hello".concat("world!")); // helloworld!
console.log(["hello"].concat(["world"])); // ["hello", "world"]
const arr = [1,3,5,7,9,11];
console.log(arr.slice(0,3)); // [1,3,5]
console.log(arr.slice(-2)); // [9,11]
splice():很强大的数组方法,它有很多种用法,可以实现删除、插入和替换。
删除:可以删除任意数量的项,只需指定 2 个参数:要删除的第一项的位置和要删除的项数。例如, splice(0,2)会删除数组中的前两项。
const arr = [1,3,5,7,9,11];
let res = arr.splice(3);
console.log(res); // [7, 9, 11]
console.log(arr) // 原数组 [1, 3,5]
res = arr.splice(0,2);
console.log(res); // [1,3]
console.log(arr) // 原数组 [5]
插入:可以向指定位置插入任意数量的项,只需提供 3 个参数:起始位置、 0(要删除的项数)和要插入的项。例如,splice(2,0,4,6)会从当前数组的位置 2 开始插入4和6。
const arr = [1,3,5,7];
// arr.splice(1,0,'a','b','c');
arr.splice(1,0,...['a','b','c']);
console.log(arr); // [1, "a", "b", "c", 3, 5, 7]
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定 3 个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。例如,splice (2,1,4,6)会删除当前数组位置 2 的项,然后再从位置 2 开始插入4和6。
const arr = [1,3,5,7];
arr.splice(1,2,...[2,3,4,5,6]);
console.log(arr); // [1, 2, 3, 4, 5, 6, 7]
splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回一个空数组。
var arr = [1, 2, 3, 4, 5];
arr.forEach(function(item,index,arr){
console(`值:${item},索引:${index},数组:${arr}`)
})
/*
值:1,索引:0,数组:1,2,3,4,5
值:2,索引:1,数组:1,2,3,4,5
值:3,索引:2,数组:1,2,3,4,5
值:4,索引:3,数组:1,2,3,4,5
值:5,索引:4,数组:1,2,3,4,5
*/
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 返回所有奇数成员
var newArr = arr.filter((item,index)=>item%2);
console.log(newArr) // [1, 3, 5, 7, 9]
// 返回所有偶数成员
var newArr = arr.filter((item,index)=>!(item%2));
console.log(newArr) // [2, 4, 6, 8, 10]
var arr = [1, 2, 3, 4, 5];
// 判断所有元素是否都小于10
var arr2 = arr.every(function(x) {
return x < 10;
});
console.log(arr2); //true
// 利用箭头函数简化 判断是否所有元素都小于3
var arr3 = arr.every(x=>x < 3);
console.log(arr3); // false
var arr = [1, 2, 3, 4, 5];
var arr2 = arr.some(x=>x < 3);
console.log(arr2); //true
var arr3 = arr.some((x)=> x > 6);
console.log(arr3); // false
// 下面代码用reduce()实现数组求和,数组一开始加了一个初始值10
var arr = [1,2,3,4,5]; // 15
var sum = arr.reduceRight(function(prev, cur, index, array){
console.log(prev,cur,index,array)
// 当前值会自动累加给前一个值
/* 前一个值 当前值 索引 数组
10 5 4 (5) [1, 2, 3, 4, 5]
15 4 3 (5) [1, 2, 3, 4, 5]
19 3 2 (5) [1, 2, 3, 4, 5]
22 2 1 (5) [1, 2, 3, 4, 5]
24 1 0 (5) [1, 2, 3, 4, 5]*/
return prev + cur;
},10);
console.log(sum); //25
13.map()
map():指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
// 利用map方法实现数组中每个数求平方
var arr = [1, 2, 3, 4, 5];
console.log(arr.map(item=>item**2));
// [1, 4, 9, 16, 25]