事件
补充表格代码:
<!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>Document</title>
<style>
table {
width: 600px;
border: 1px solid #000;
text-align: center;
border-collapse: collapse;
border-spacing: 0px;
}
tr,
th,
td {
border: 1px solid #000;
}
tbody input {
width: 90%;
border: none;
outline: none;
}
</style>
</head>
<body>
<div>
<p>
序列号 <input id="sId">
</p>
<p>
姓 名 <input id="sName">
</p>
<p>
电 话 <input id="sTel">
</p>
<p>
<button id="save">保存</button>
</p>
</div>
<input id="txt"> <button id="search">搜索</button>
<table>
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>电话</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tab">
</tbody>
</table>
</body>
</html>
<script>
var oId = document.getElementById("sId");//id
var oName = document.getElementById("sName");//姓名
var oTel = document.getElementById("sTel"); //电话
var oTxt = document.getElementById("txt"); //输入内容
var oSearch = document.getElementById("search");//搜索按钮
var oSave = document.getElementById("save");//保存按钮
var oTab = document.getElementById("tab"); //表格
//1. 模拟一些数据
var data = [{
id: 1,
name: "刘德华",
tel: 13812345678
},
{
id: 2,
name: "周星驰",
tel: 13812345678
}]
//0.页面加载显示 模拟数据到页面
loadData(data);
//2.点击保存按钮,输入框的数据,做成一个对象,把该对象添加到模拟数据里
oSave.onclick = function () {
var obj = {
id: oId.value,
name: oName.value,
tel: oTel.value,
}
//在保存的时候,拿 obj里的id与 模拟数据里的id继续比较
//id与电话号码不能重复
var isReg = data.some(function (item) {
return item.id == obj.id || item.tel == obj.tel;
})
//如果isReg 为true,说明已经存在
if (isReg) {
return;
}
data.push(obj);
//再一次调用显示数据
loadData(data);
}
function loadData(arr) {
//判断 arr 必须是数组
if (!Array.isArray(arr)) {
return;
}
var strHtml = "";
arr.forEach(function (item) {
strHtml += `
<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.tel}</td>
<td>
<a href='javascript:void(0)' onclick=del(this,${item.id})>删除</a>
<a href='javascript:void(0)' onclick=edit(this,${item.id})>编辑</a>
<a style='display:none' href='javascript:void(0)' onclick=save(this,${item.id})>保存</a>
</td>
</tr> `
});
oTab.innerHTML = strHtml;
}
//定义了一个删除函数
function del(o, id) {
//1.先 根据id先出模拟数据里的 对应数据
var findIndex = -1;
data.forEach(function (item, index) {
if (item.id == id) {
findIndex = index;
}
})
//根据下标删除
data.splice(findIndex, 1);
//2.页面上的 tr标签 也要被删除
o.parentNode.parentNode.remove();
}
//搜索功能
oSearch.onclick = function () {
//1.获取到输入框的内容
var strName = oTxt.value;
//2.拿strName 与模拟的数据,进行过滤
var newData = data.filter(function (item) {
return item.name.indexOf(strName) != -1
})
//加载显示的数据,inneerHTML会把之前的数据覆盖掉
loadData(newData)
}
//编辑 o是a标签
function edit(o, id) {
o.style.display = "none";
(o.nextElementSibling || o.nextSibling).style.display = "inline-block";
// 拿到用户名的td里头 插入一个input
//先取值 刘德华
var strName = o.parentNode.parentNode.children[1].innerText;
//把刘德华放入到一个 输入框里,在把输入框插入到 第2个td里
o.parentNode.parentNode.children[1].innerHTML = `<input value=${strName}>`;
//先取出电话号码
var strTel = o.parentNode.parentNode.children[2].innerText;
o.parentNode.parentNode.children[2].innerHTML = `<input value=${strTel}>`;
}
//保存 o是a标签
function save(o, id) {
o.style.display = "none"; //保存 隐藏
// 编辑显示
(o.previousElementSibling || o.previousSibling).style.display = "inline-block"
// 1.根据id找到下标,在于对应下标的地方,把输入的值,覆盖模拟数据里某对象的数据
var findIndex = -1;
data.forEach(function (item, index) {
if (item.id == id) {
findIndex = index;
}
})
data[findIndex].name = o.parentNode.parentNode.children[1].children[0].value;
data[findIndex].tel = o.parentNode.parentNode.children[2].children[0].value;
//把输入的内容,利用innerHTML 覆盖原来的 input框
o.parentNode.parentNode.children[1].innerHTML = data[findIndex].name
o.parentNode.parentNode.children[2].innerHTML = data[findIndex].tel;
}
</script>
事件
日常生活中事件: 发生并得到处理的操作
在JS中的事件是: 用户在页面上操作, 然后我们要调用函数来处理.
事件触发:用户在页面上操作(如点击按钮, 鼠标滑过, 鼠标点击, 鼠标松开, 文本框获得焦点, 失去焦点等), 就是事件触发.
事件的模式
JavaScript有两种事件实现模式: 内联模式, 脚本模式.
内联模式 :
直接在HTML标签中添加事件. 这种模型是最传统简单的一种处理事件的方法。但是这种模式中事件和HTML是混写的, 并没有将JS与HTML分离, 当代码量多了以后,对后期代码维护和扩展很不利.
<input type="button" value="按钮" onclick="alert('hello');" />
注意: 单双引号
//执行自定义的JS函数
<input type="button"value="按钮" onclick="btnClick();" />
注意: 内联模式调用的函数不能放到window.onload里面, 否则会找不到该函数.
脚本模式
脚本模式能将JS代码和HTML代码分离, 符合代码规范.
使用脚本模式我们需要先获取到元素节点对象, 再针对该节点对象添加事件.
var box = document.getElementById('box');
添加事件方式一 : 通过匿名函数,可以直接触发对应的代码 (推荐)
box.onclick = function() { //给box节点对象添加点击事件onclick
console.log('Hello world!');
};
添加事件方式二 : 通过指定的函数名赋值的方式 来执行函数
box.onclick = func; //注意这里不能写成func()
function func() { //给box节点对象添加点击事件onclick
console.log('Hello world!');
};
事件处理由三个部分组成:1, 触发事件的元素节点对象;2, 事件处理函数;3, 事件执行函数
例如:单击文档任意处。
document.onclick = function(){
console.log('单击了文档页面的某一个地方');
};
在上面的程序中:
document : 是触发事件的对象, 表示触发事件的元素所在区域;
onclick : 表示一个事件处理函数(on+事件类型click)
function(){} : 匿名函数是被执行的函数, 用于触发事件后执行;
所有的事件处理函数都会都有两个部分组成,on+事件类型;
例如 : onclick事件处理函数就是由on加上click;
注意: 事件处理函数一般都是小写字母
事件的分类
JavaScript 可以处理的事件种类有三种:鼠标事件, 键盘事件和 HTML事件.
鼠标事件,页面所有元素都可触发鼠标事件;
click: 当单击鼠标按钮并在松开时触发
onclick = function() {
console.log('单击了鼠标');
};
dblclick: 当双击鼠标按钮时触发。
ondblclick = function() {
console.log('双击了鼠标');
};
mousedown:当按下了鼠标还未松开时触发。
onmousedown = function() {
console.log('按下鼠标');
};
mouseup: 释放鼠标按钮时触发。
onmouseup = function() {
console.log('松开了鼠标');
};
mouseover:当鼠标移入某个元素的那一刻触发。
onmouseover = function() {
console.log('鼠标移入了');
};
mouseout:当鼠标刚移出某个元素的那一刻触发。
onmouseout = function() {
console.log('鼠标移出了');
};
mousemove:当鼠标指针在某个元素上移动时触发。
onmousemove = function() {
console.log('鼠标移动了');
};
mouseenter:当鼠标移入某个元素的那一刻触发。
onmouseenter = function() {
console.log('鼠标移入了');
};
mouseleave:当鼠标刚移出某个元素的那一刻触发。
onmouseleave = function() {
console.log('鼠标移出了');
};
(onmouseover与onmouseout) 比(onmouseenter和onmouseleave) 早执行
(onmouseover与onmouseout)遇到子元素 也会触发,
(onmouseenter和onmouseleave) 遇到子元素 '不'会触发
鼠标的滚轮事件
oBox.onmousewheel = function () {
console.log(1111);
}
键盘事件,在键盘上按下键时触发的事件; (一般由window对象或者document对象调用)
keydown:当用户按下键盘上某个键触发,如果按住不放,会重复触发。
window.onkeydown = function() {
console.log(按下了键盘上的某个键);
};
keypress:当用户按下键盘上的字符键触发,如果按住不放,会重复触发
window.onkeypress = function() {
console.log('按下了键盘上的字符键');
};
keyup:当用户释放键盘上的某个键触发。
window.onkeyup = function() {
console.log(松开键盘上的某个键);
};
HTML事件,跟HTML页面相关的事件;
load:当页面完全加载后触发
window.onload = function() {
console.log('页面已经加载完毕');
};
unload:当页面完全卸载后触发
window.onunload = function() {
console.log('页面已经卸载完毕');
};
select:当用户选择文本框(input 或 textarea)中的内容触发。
input.onselect = function() {
console.log('选择了文本框中的内容');
};
change:当文本框(input 或 textarea)内容改变且失去焦点后触发。
input.onchange = function() {
console.log('文本框中内容改变了');
};
//页面结构加载完成,就触发
window.addEventListener("DOMContentLoaded", function () {
console.log("over");
卸载
window.onunload = function () {
console.log("卸载.......");
改变窗口事件
window.onresize = function () {
console.log("onresize");
滚轮 onwheel onmousewheel
var oBox=document.querySelector(".box");
oBox.onwheel=function(){
console.log("onwheel");
}
focus:当页面或者元素获得焦点时触发。
input.onfocus = function() {
console.log('文本框获得焦点');
};
blur:当页面或元素失去焦点时触发。
input.onblur = function() {
console.log('文本框失去焦点');
};
submit:当用户点击提交按钮在<form>元素节点上触发。
form.onsubmit = function() {
console.log(‘提交form表单’);
};
reset:当用户点击重置按钮在<form>元素节点上触发。
form.onreset = function() {
console.log('重置form表单');
};
scroll:当用户滚动带滚动条的元素时触发。
window.onscroll= function() {
console.log('滚动了滚动条了');
};
submit 重点
<form>
<p>
<input name="uname" placeholder="用户名">
</p>
<p>
<!-- button在表单里,默认是submit类型 -->
<!-- <button>提交</button> -->
<input type="submit" value="提交">
<button type="reset">重置</button>
</p>
</form>
<script>
// 只有submit才会触发 form表单的默认提交行为
//提交的时候,必须要先经过js的控制,如果js同意提交,才触发默认行为
//重点
document.forms[0].onsubmit = function () {
console.log(11111);
// return false; 阻止onsubmit默认的提交行为
return false;// js 说 不让提交
// return true;
}
//了解 重置输入的内容
document.forms[0].onreset = function () {
console.log(111);
// return true;
}
</script>
事件对象
在事件触发时的一条记录信息,就是事件对象
该对象里有哪些信息?
- 位置,事件的类型,目标元素,到元素,屏幕等等的距离
事件对象的属性:button 0 左键 ,1 滚轮键 ,2 右键
target 目标 点了谁? srcElement ie
var oBox = document.querySelector(".box");
oBox.onmousedown = function (evt) {
//在ie下 window.event
//在google,使用形参true
//事件对象的兼容写法
// google ie
var e = evt || window.event;
// console.log(e.button);
// console.log(e.srcElement);
// this不等于 target
// this 就是万年不变的 box
// target 鼠标点了谁,就是谁 (事件委托就要借助它)
var ele = e.target || e.srcElement;
console.log(ele);
console.log(this);
浏览器渲染页面的原理及流程
1.根据html文件构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS,阻塞DOM树及CSSOM树的构建,优先加载JS文件,加载完毕,再继续构建DOM树及CSSOM树。
2.构建渲染树(Render Tree)。
3.页面的重绘(repaint)与重排(reflow,也有称回流)。页面渲染完成后,若JS操作了DOM节点,根据JS对DOM操作动作的大小,浏览器对页面进行重绘或是回流
重绘和回流(repaint&reflow)
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
导致回流的操作:
1、页面首次渲染
2、浏览器窗口大小发生改变
3、元素尺寸或位置发生改变
4、元素内容变化(文字数量或图片大小改变而引起的计算值宽度和高度改变)
5、元素字体大小变化
6、添加或者删除可见的DOM元素
7、激活CSS伪类(例如::hover)
8、查询某些属性或调用某些方法
9、offsetWidth,width,clientWidth,scrollTop/scrollHeight的计算,会使浏览器将渐进回流队列Flush,立即执行回流。
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流必定会发生重绘,重绘不一定会引发回流。
如何避免重绘和回流?
css:
1.避免使用table布局,可能很小的一个小改动会造成整个table的重新布局
2.尽可能在DOM树的最末端改变class。
3.避免设置多层内联样式。
4.将动画效果应用到position属性为absolute或fixed的元素上。
5.动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用requestAnimationFrame
6.避免使用CSS表达式(例如:calc())
7.使用transform替代top
8.使用visibility替换display: none,因为前者只会引起重绘,后者会引发回流(改变了布局)
将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点
js:
1.避免频繁操作样式,最好一次性重写style属性,cssText,或者将样式列表定义为class并一次性更改class属性。
2.避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
3.也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
4.避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
5.对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。