DOM
DOM (Document Object Model) 译为文档对象模型,是 HTML 和 XML 文档的编程接口。
1-入门
- 操作数据 --直接创建数据类型
- 如果我们要操作界面,就用DOM操作;系统(浏览器团队写的软件),它按照产业联盟的标准,把html文档解析成了一个对象:关键字 document
- document对象提供了很多属性和方法 供我们来操作界面
- document这个单词是window对象的属性,那么window也是一种标准(约定标准,就是没有标准的意思):所有浏览器团队 大同小异的去把浏览器解析成了一个对象,供开发人员用语法去使用
- 把文档解析成一个对象 ,把每一个标签也解析 一个个对象(专业名词:节点),把其他的一共12种数据 ,分别解析成节点 (12类节点)
- 节点关系: 嵌套的节点 ,称为父节点 、子节点 ;最下层的节点没有父节点,叫根节点;把整个节点 叫做文档树,也叫dom树
2-获取元素方法
2-1通过方法获取元素
<div id="div1">hello world</div>
<div class="box">
hello world!
</div>
通过id获取界面元素
var div1=document.getElementById("div1");
//界面上hello world
console.log(div1);
//<div id="div1">hello world</div>
通过class获取界面元素
var div2=document.getElementsByClassName("box");
//界面上hello world!
console.log(div2);//返回值是类数组HTMLCollection [div.box]
注意:如果没有符合条件的class 也是返回类数组(HTMLCollection [])
H5之前的方法
<input id="id1" class="id1" name="id" value="" />
<input id="id2" class="id" name="id" value="" />
var a1=document.getElementById("id1");
//返回对拥有指定id的第一个对象的引用
//节点接对象 没有返回null
var a2=document.getElementsByClassName("id");
//返回对拥有指定class的第一个对象的引用
//类数组 没有返回空类数组
var a3=document.getElementsByName("id");
//返回带有指定名称的对象集合
//类数组 没有返回空类数组
var a4=document.getElementsByTagName("input");
//返回带有指定标签名的对象集合
//类数组 没有返回空类数组
H5的方法
var a5=document.querySelector("#id2");
//满足选择器条件的第一个元素
var a6=document.querySelectorAll(".id");
//满足选择器条件的全部元素
//类数组 没有返回空类数组
比较:两种都很好,H5出的两个方法比较好用,但H5的方法的底层还是以前实现的原理实现的,相对于H5出的技术来说,getElementById这个方法是查询速度最快的。
2-2通过属性获取元素
1.body标签节点的操作频率非常高,系统把它封装到了document的body属性中
document.body
2.所有的表单
document.forms
3.所有的a标签:name属性===>锚
document.anchors
4.所有的a标签:href属性===>连接
document.links
5.所有的图片
document.images
6.当前页面的网址:URL
document.URL
2-3通过关系获取元素
节点父、子和同胞:
- 在节点树中,顶端节点被称为根(root)。
- 每个节点都有父节点、除了根(它没有父节点)。
- 一个节点可拥有任意数量的子节点。
- 同胞是拥有相同父节点的节点。
<div class="box">
<p id="p1">hello</p>
<p>hello</p>
<p>hello</p>
<p id="p2">hello</p>
<p>hello</p>
</div>
父级关系
var p1=document.querySelector("#p1")
var father1=p1.parentElement//获取p1元素的父元素节点
var father2=p1.parentNode//获取p1元素的父节点
var gradpa=p1.parentElement.parentElement//获取p1元素的爷爷元素节点
子级关系
var box1=document.querySelector(".box")
var son1=box1.children //box1的所有子元素节点
var son2=box1.childNodes //box1的所有子节点
var son3=box1.children[1]//box1的第2个儿子元素
兄弟关系
var p2=document.querySelector("#p2")
var b1=p2.nextElementSibling//弟弟元素节点
//没有弟弟返回null
var b3=p2.nextSibling//弟弟节点
var b4=p2.previousElementSibling//哥哥元素节点
var b5=p2.previousSibling//哥哥节点
第一个和最后一个
var box=document.querySelector(".box")
var son1=box.firstElementChild//第一个元素节点
var son2=box.firstChild//第一个节点
var last1=box.lastElementChild
var last2=box.lastChild
var first=box.children[0]
var last=box.children[box.children.length-1]
nodeName属性规则
- nodeName 是只读的
- 元素节点的 nodeName 与标签名相同
- 属性节点的 nodeName 与属性名相同
- 文本节点的 nodeName 始终是 #text
- 文档节点的 nodeName 始终是 #document
2-4 例子
百度换肤
<style>
#img{
width: 200px;
height: 80px;}
</style>
<div style="position: absolute">
<img id="img" src="img/img01.jpg"/>
<img id="img" src="img/img02.jpg"/>
<img id="img" src="img/img03.jpg"/> //没有图
</div>
<img id="show" src="" width="1600px" height="800px"/>
var tu=document.querySelectorAll("img");
var show=document.getElementById("show");
for(var i=0;i<tu.length;i++){
tu[i].onclick=function(){
show.src=this.src;
}
}
排他设计
<div>
<button>1</button>
<button>2</button>
<button>3</button>
</div>
var bt=document.querySelectorAll("button")
for(let i=0;i<bt.length;i++){
bt[i].onclick=function(){
for(let j=0;j<bt.length;j++){ bt[j].style.backgroundColor=""
}
//单独把当前点击的元素设置为blue
bt[i].style.backgroundColor="blue"
}
}
3-节点的增删改查
<style>
#box,
.box2,
.box3{
width: 400px;
height: 50px;
background-color: aquamarine;
}
</style>
<div id="box"></div>
创建节点元素
//1.创建元素节点
//document.createElement()
var box=document.querySelector("#box");
//创建元素节点对象 只在内存 不再文档树
var img=document.createElement(img);
//把创建的节点对象添加到相同的文档树中
img.src="img01.jpg";
var p1=document.createElement("p");
p1.innerHTML="商品"
box.appendChild(img);
box.appendChild(p1);//把p1结点添加到box节点中去(加小儿子)
//appendChild()方法可向节点的子节点列表的末尾添加新的子节点
//调用者是节点对象 参数是节点对象
//调用者可以是文档树中的节点 也可以是创建的节点(不在文档树中)
document.body.appendChild(box);
删除节点元素
<button id="b1">删除</button>
<p id="d1">点击删除掉</p>
//2.删除节点元素
//xx.remove() 移除自己
//father.removeChild() 移除子元素
var b1=document.getElementById("b1");
b1.onclick=function(){
var d1=document.getElementById("d1");
d1.remove();
//把调用remove方法的节点从文档树中删除
// d1.parentElement.removeChild(d1);
}
增添节点元素
//3.增添节点元素
var b2=document.getElementById("b2");
b2.onclick=function(){
var box2=document.querySelector(".box2");
//给元素添加一个大儿子
// box2.appendFirstChild(xx);
Object.prototype.appendFirstChild=function(el){
var arr=[...this.children];
this.innerHTML="";
arr.unshift(el);
// this.appendChild(...arr)
arr.forEach((el)=>{
this.appendChild(el);
})
}
var a=document.createElement("div");
a.innerHTML="son";
box2.appendFirstChild(a);
//给元素添加一个指定位置的元素
Object.prototype.appendtargetChild=function(el,index){
var arr=[...this.children];
this.innerHTML="";
arr.splice(index,0,el);
//从index开始删除0个元素 在这个位置添加el
arr.forEach((el)=>{
this.appendChild(el);
})
}
var b=document.createElement("div");
b.innerHTML="target";
box2.appendtargetChild(b);
//给元素添加一个弟弟
// box2.appendNextChild(xx);
//给元素添加一个哥哥
// box2.appendPrevChild(xx);
}
克隆节点元素
//4.克隆节点元素
//xx.cloneNode(true)
//true深克隆 包含了元素本身和子节点
//false浅克隆(默认) 只包含节点本身
b3.onclick=function(){
console.log(222)
var box3=document.querySelector(".box3");
var box4=box3.cloneNode(true);
document.body.appendChild(box4)
}
4-JSON数据
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。
它在js中就是一个字符串,它的字符串结构和js的对象直接量一样 。
4-1 解析JSON数据
自己解析
var str='{name:"lily",age:18}';
//split字符串转数组
String.prototype.myParse=function(){
var obj={};
var arr=this.split("{")[1].split("}")[0].split(",");
for(let i=0;i<arr.length;i++){
// console.log(arr[i]);//"name":"lily"
let arr2=arr[i].split(":");
obj[arr2[0]]=arr2[1]
}
return obj;
}
var obj=str.myParse();
console.log(obj);
console.log(obj.name, obj.age)
工具解析
//字符串转数组
var obj2=JSON.parse(str);
console.log(obj2);
//数组转字符串
var str2=JSON.stringify(str);
console.log(str2);
4-2把JSON数据写入UI页面
.box{
width: 200px;
height: 200px;
background-color: salmon;
}
img{
width: 50px;
height: 50px;
}
<button type="button" id="b1">加载</button>
第一种
b1.onclick = function() {
var data={name:"jack",img:"img01.jpg",text:"xxxxx1"}
var box=document.createElement("div")
document.body.appendChild(box)
var name1=document.createElement("p")
name1.innerHTML=data.name
box.appendChild(name1)
var img=document.createElement("img")
img.src=data.img
box.appendChild(img)
var content=document.createElement("p")
content.innerHTML=data.text
box.appendChild(content)
}
第二种
b1.onclick = function() {
var arr = [{
name: "jack1",
img: "img01.jpg",
text: "xxxxx1"
},
{
name: "jack2",
img: "img01.jpg",
text: "xxxxx12"
}]
for (var i = 0; i < arr.length; i++) {
var box = document.createElement("div")
box.className = "box"
document.body.appendChild(box)
var name1 = document.createElement("p")
name1.innerHTML = arr[i].name
box.appendChild(name1)
var img = document.createElement("img")
img.src = arr[i].img
box.appendChild(img)
var content = document.createElement("p")
content.innerHTML = arr[i].text
box.appendChild(content)
}
}
5-窗口的相关操作
基本操作
window.scrollTo(0,60000);
//隐藏的内容向上滚动到600的位置:如果只能滚动500 就滚到500
window.scrollBy(0,10);
//点一下滚一下 隐藏的内容在当前位置向上滚动1的距离,直到最底部
var p1=document.getElementById("p1")
p1.scrollIntoView(true);//值为false或true false 页面底部 true页面顶部
获取窗口卷去的高度
//pageXOffset 和 pageYOffset 属性返回文档在窗口左上角水平和垂直方向滚动的像素
//兼容性写法1.
let pageY1;
if(window.pageYOffset){
pageY1=window.pageYOffset
}else if(document.body.scrollTop){
pageY1=pageY1=window.pageYOffset
}else if(document.documentElement.scrollTop){
pageY1=document.documentElement.scrollTop
}
//2.
document.onclick=function(){
let pageY2=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop;
}
let cWidth,cHeight;
if (document.compatMode == "BackCompat") {
//window.innerWidth bom操作 IE8及以下不兼容
//document.documentElement.clientWidth 标准模式下 任何浏览器都兼容
//document.body.clientWidth适合于怪异模式/混杂模式下的浏览器
cWidth = window.innerWidth||document.body.clientWidth;
cHeight =window.innerHeight||document.body.clientHeight;
} else {
//document.compatMode == "CSS1Compat" 可区分标准模式与否
cWidth = window.innerWidth||document.documentElement.clientWidth;
cHeight = window.innerHeight||document.documentElement.clientHeight
}
console.log(cWidth,cHeight)//1536 722 随窗口大小改变 主要用于移动端
6-元素相关
元素的几何尺寸
var box = document.querySelector(".box")
//获取元素的几何尺寸
//x.getBoundingClientRect()
//该方法返回一个对象,对象里边有 left,top,right,bottom,height,width属性
var obj = box.getBoundingClientRect()
console.log(obj)
元素的属性
//写标签的时候 标签的属性 在dom中 直接可以通元素节点对象的点语法访问
var div1=document.querySelector("#div1")
div1.onclick=function() {
div1.style.width="400px"
div1.className="div1"
//.contentEditable 用于设置或返回元素的内容是否可编辑
//true/false/inherit(默认如果父级元素是可编辑,则子元素内容也是可编辑的)
div1.contentEditable=true
console.log(div1.id,div1.className)
}
元素的位置
var f1=document.querySelector(".f1")
console.log(f1.offsetParent)
// f1.style.left相当于f1.offsetLeft
x.offsetWidth:本身宽度+边框线+左右内边距;
x.offsetHeight:本身高度+边框线+上下内边距;
x.offsetTop:相对有定位属性的父节点上偏移量;(如果父元素的padding和子元素的margin也包含)
x.offsetLeft:相对有定位属性的父节点左偏移量;
x.clientWidth:本身的宽度+左右内边距;
x.clientHeight:本身的高度+上下内边距;
x.clientTop:上边框线的宽度
x.clientLeft:左边框线的宽度
x.scrollWidth:盒子的实际宽度(包括滚动条不可见部分,不包括边线)
x.scrollHeight:盒子的实际高度(包括滚动条不可见部分,不包括边线)
x.scrollTop:滚动条向下滚动的距离;
x.scrollLeft:滚动条向右滚动的距离;
window.innerHeight:浏览器窗口可见区域高度;
window.innerWidth:浏览器窗口可见区域宽度;
clientX:当鼠标事件发生时(不管是onclick,还是omousemove,onmouseover等),鼠标相对于浏览器(这里说的是浏览器的有效区域)x轴的位置;
clientY:当鼠标事件发生时,鼠标相对于浏览器(这里说的是浏览器的有效区域)y轴的位置;
screenX:当鼠标事件发生时,鼠标相对于显示器屏幕x轴的位置;
screenY:当鼠标事件发生时,鼠标相对于显示器屏幕y轴的位置;
offsetX:当鼠标事件发生时,鼠标相对于事件源x轴的位置
offsetY:当鼠标事件发生时,鼠标相对于事件源y轴的位置
封装一个求元素相对文档的坐标
Object.prototype.offset = function() {
let parent1 = this.offsetParent;
//获取上一级定位元素对象
let x=this.offsetLeft
let y=this.offsetTop
while (parent1 != null) {
x += parent1.offsetLeft;
y += parent1.offsetTop;
parent1 = parent1.offsetParent;
}
return {x,y};
}
7-盒子模型
//行内样式
//假装有一个盒子
var box = document.querySelector(".box")
console.log(box.clientWidth)//200
box.clientWidth=400 //可读不可改
box.style.width=box.clientWidth*2+"px"
console.log(box.clientWidth)//400
var w=box.style.width*2
console.log(w) //NaN
box.style.width=w
console.log(box.style.width) //字符串400px
//获取css的最终计算样式
var obj=window.getComputedStyle(box)
console.log(obj)
box.style.width=parseFloat(obj.width)*2+"px"
8-重绘与回流
重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
常见的重绘操作:
- 改变元素颜色
- 改变元素背景色
- 隐藏元素:visibility:hidden
回流(reflow):又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
常见的回流操作:
- 页面初次渲染
- 浏览器窗口大小改变
- 元素尺寸/位置/内容发生改变
- 元素字体大小变化
- 隐藏元素,display:none
- 添加或者删除可见的 DOM 元素
- 激活 CSS 伪类(:hover……)
- 移动元素,比如改变top,left(jquery的animate方法就是,改变top,left不一定会影响回流),或者移动元素到另外1个父元素中
- 用户的操作,比如改变浏览器大小,改变浏览器的字体大小等
注意:任何对render tree中元素的操作都会引起回流或者重绘。回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。
// 添加10000格子
var tbUI=document.querySelector("#tb")
var tbCPU=document.createDocumentFragment()
console.log(tbCPU)
//它就像一个只js中操作的元素 放到文档树中 它就不在了但是它子代元素都会添加到文档树中
for(let i=0;i<100;i++){
var tr=document.createElement("tr")
tbCPU.appendChild(tr)
for(let i=0;i<100;i++){
var td=document.createElement("td")
td.innerText=new Date().getTime() //"6"
tr.appendChild(td)
}
}
tbUI.appendChild(tbCPU)
//vue:template标签 wx: block标签 react:tempalte标签
//dom js的方法 document.createDocumentFragment()
如何避免大量使用重绘与回流呢?
- 避免频繁操作样式,可汇总后统一一次修改
- 尽量使用 class 进行样式修改,而不是直接操作样式
- 减少 DOM 的操作,可使用字符串一次性插入
- 优化:
css:1.使用 transform 替代 top
2.使用 visibility 替换 display: none
3.避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局
4.尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响
5.避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多
6.将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局
7.避免使用CSS表达式,可能会引发回流。
8.将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点
9.CSS3 硬件加速(GPU加速)
JavaScript:1.避免频繁操作样式
2.避免频繁操DOM
3.避免频繁读取会引发回流/重绘的属性
4.对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流
面试题:
从重绘与回档的角度讲一讲 为什么要有js框架?
9-表单操作
//表单的获取
document.getElementById()
document.forms[index];
document.forms[form_name]
document.form_name
//document.forms
//获取id是myform表单下所有的复选按钮
document.querySelector('#myform input[type="checkbox"]');
//获取id是myform表单下所有的name是a的复选按钮
document.querySelector('#myform input[type="checkbox"][name="a"]');
//form提交
form.submit()
//form重置;
form.reset();
//可读可写,form元素类型;
元素.type
//只读,包含该元素的formform对象,不存在返回null;
元素.form
//可读可写,元素的名称;
元素.name
//可读可写,简单理解就是输入框的值;
元素.value
表单的提交触发
<form id="fom" action="https://www.baidu.com/index.html" method="get">
<input type="text">
<!-- 系统默认事件: 当点击提交按钮以后 会直接打包数据 发送给action -->
<input type="submit" value="提交"/>
</form>
<button type="button" onclick="fn()">xxx</button>
//提交方法1 系统按钮
fom.submit();
//提交方法2
function fn () {
//前端验证
//验证通过时
// fom.submit()
fom.reset()
}
表单域对象属性
1.readonly
1)input对象 设置了readonly=“readonly”,则该表单域只读(用户不能修改其value属性),但是可以提交
2)通过js为input对象添加“只读”属性,应通过“对象.readOnly = true”添加
3)readonly=“readonly” 只能使用在 及 标签中
2.disabled
1)input对象 设置了disabled=“disabled”,则该表单域不可用(用户不能修改其value属性)且不能提交
2)通过js为input对象添加“不可用”属性,应通过“对象.disabled = true”添加
3)disabled="disabled"可以将所有的表单域失效
3.name
1)用于获取该表单域
2)只有设置了name属性的表单域才可以提交
4.value
1)用户输入的内容就是value,表单会提交该属性的值
2)select标签的value值就是当前选中的option的value值
3)textarea没有value属性,提交时提交标签中间的文本值
5.form
用于获取表单域所在的表单对象
6.type
浏览会根据type的值不同,显示表单域也不同
7.checked
1)对于type=“radio” 和type="checkbox"来讲,checked="checked"表示默认选中该选项
2)type=“radio” 只能给同组的一个添加 checked=“checked”
3)type="checkbox"可以给同组的所有添加 checked=“checked”
4)通过js为对象添加“默认选中”属性,应通过“对象.checked = true”添加
10-事件
事件三要素:事件源 事件类型 事件处理程序(handler)
事件绑定方式
1.行内式
不常用<button onclick="console.log(6)">打印6</button><br>
比较常用:<br>
<button onclick="fn()">打印7</button><br>
<a href="javaScript:console.log(8)">打印8</a><br>
<a href="javaScript:void(1)">提示1</a><br>
//void(1)
function fn() {
console.log(7)
}
2.属性式
<button id="b1">属性式点击</button><br>
document.getElementById("b1").onclick = function () {
console.log(1)
}
//绑定多次会覆盖前面的
3.监听事件
常用的高级方法:<button id="b2">点击打印9</button>
var b2 = document.getElementById("b2");
//监听事件 监听某个绑定事件 一旦触发马上执行
//可以绑定多个事件
//参数1:监听事件 参数2:函数
b2.addEventListener("click", function () {
console.log(9)
}, false)
//默认为false
//true和false决定事件在捕获阶段触发还是在冒泡阶段触发
//并不会阻止
事件解绑
<button type="button" id="b1"></button>
<button type="button" onclick="fn()"></button>
//1
b1.onclick=function(){
console.log(111)
}
//2 不是函数就解绑了
function fn(){
b1.onclick=""
}
//3
b1.addEventListener("click",fn2)
function fn2(){
console.log("222")
}
//4
function fn3(){
b1.removeEventListener("click",fn2)
}
事件中的this和事件对象
<div id="box1" style="width: 100px;height: 100px;background-color: rgb(224, 129, 144);"></div>
<button type="button" onclick="fn2(this)">点击一下</button>
<button id="b3">点击一下</button>
var box1=document.getElementById("box1");
box1.onclick=function(e){
console.log(e);
console.log(this)//div元素
}
//事件对象:事件在某个状态下触发时,会把触发时的信息打包到事件对象中传给事件处理程序
//this代表执行时的环境对象
function fn2(e){
console.log(this) //window
console.log(e) //button
}
var b3 = document.getElementById("b3");
b3.addEventListener("click", function (e) {
console.log(this) //button
console.log(e) //MouseEvent
}, false)
b3.addEventListener("click", (e)=> {
console.log(this) //window 找最近fn 找不到
console.log(e) //MouseEvent
}, false)
事件响应链
事件的三个阶段 先捕获 后目标 再冒泡,只能有一个阶段触发程序执行,比如捕获阶段触发了到了冒泡阶段就不再触发
事件经过所有元素都没有被处理,这个事件消失
事件的传递过程只跟文档树有关,跟界面显示的层叠效果没有任何关系
.div1{
width: 150px;
height: 150px;
background-color: pink;
position: relative;
}
.div2{
width: 100px;
height: 100px;
position: absolute;
left: 200px;
background-color: yellowgreen;
}
.div3{
width: 50px;
height: 50px;
background-color: orange;
}
<div class="div1">1
<div class="div2">2
<div class="div3">3</div>
</div>
</div>
var arr=document.querySelectorAll("div")
// arr[0].addEventListener=("click",function(){
// console.log(1)
// },true)
// arr[1].addEventListener=("click",function(){
// console.log(2)
// },true)
// arr[2].addEventListener=("click",function(){
// console.log(3)
// })
//1 2 3
//第三个参数默认为false 如果设置为true 则在捕获阶段执行
// 阻止事件冒泡
arr[0].addEventListener=("click",function(){
console.log(1)
},false)
arr[1].addEventListener=("click",function(e){
console.log(2)
},false)
arr[2].addEventListener=("click",function(e){
console.log(3)
e.stopImmediatePropagation() //阻止事件对象传递(同元素同事件的其他处理程序也阻止)
})
arr[2].addEventListener=("click",function(e){
console.log(3)
e.stopPropagation() //阻止事件对象传递(向上冒泡)
})
//面试题
//addEventListener的第三个参数填为true是阻止冒泡还是false
//都不是
// arr[2].attachEvent("click",function(e){
// e.cancelBubble=true;//这个代码只在ie浏览器的8版本下生效
// })
事件分为两大类:
- 自己绑定的处理程序
- 系统自带的处理程序:a submit reset
如果给系统自带事件的元素绑定了相同事件,就会先执行绑定的事件处理程序,在执行系统默认事件处理程序
有时候希望系统自带的事件不要触发
<a href="http://www.baidu.com" id="baidu">百度</a>
//阻止系统默认事件
baidu.onclick=function(e){
alert(66);
e.preventDefault()//阻止系统默认事件
}
事件委托
<div class="container">
<div class="box">hello1</div>
<div class="box">hello2</div>
<div class="box">hello3</div>
<div class="box">hello4</div>
<div class="box">hello5</div>
<div class="box">hello6</div>
<div class="box">hello7</div>
<div class="box">hello8</div>
<div class="box">hello9</div>
<div class="box">hello10</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box">hello</div>
<div class="box" id="xx" contenteditable="true">hello</div>
</div>
// var boxs=document.querySelectorAll(".container .box")
// for(let i=0;i<boxs.length;i++){
// boxs[i].οnclick=function() {
// var re=boxs[i].innerHTML
// console.log(re)
// }
// }
//事件委托
var container=document.querySelector(".container")
container.onclick=function(e) {
console.log(e.target.innerHTML)
}
//元素的自定属性
事件对象的兼容性获取
var b1 = document.querySelector("button")
b1.onclick = function (event) {
event = event || window.event || arguments[0]
}
鼠标事件
- onmousedown:鼠标在被选元素区域 按下 触发 onmouseup:鼠标在被选元素区域 松开 触发
- onclick: 鼠标在被选元素区域 按下和松开 触发 onmousemove: 鼠标在被选元素区域内部和某一个时间段内 位置(X,Y)有变化
- 移动端没有 mousedown事件,对应的是 touchstart–touchmove-- touchend
DOM3标准规定:click事件只能监听左键,mousedown和 mouseup来判断鼠标键,event.button来区分鼠标的按键
0:左键 1:滚轮键 2:右键 3:大指姆下 4:大指姆上键
ondblclick: 两次click事件触发事件间隔 ==> 短时间点击两次触发事件 oncontextmenu: 鼠标右击出现的菜单事件
mouseover: 鼠标移入
mouseout:鼠标移出
mouseover: 鼠标移入 (h5)
mouseout:鼠标移出 (h6)
权重优先级计算
选择器权重:
样式类型:1.行内 2.内联 3.外部
权重计算规则:
第一等:代表内联样式,如: style=””,权值为1000。
第二等:代表ID选择器,如:#content,权值为0100。
第三等:代表类,伪类和属性选择器,如.content,权值为0010。
第四等:代表类型选择器和伪元素选择器,如div p,权值为0001。
通配符、子选择器、相邻选择器等的。如*、>、+,权值为0000。
继承的样式没有权值。
a{color: yellow;} /*特殊性值:0,0,0,1*/
div a{color: green;} /*特殊性值:0,0,0,2*/
.demo a{color: black;} /*特殊性值:0,0,1,1*/
.demo input[type="text"]{color: blue;} /*特殊性值:0,0,2,1*/
.demo *[type="text"]{color: grey;} /*特殊性值:0,0,2,0*/
#demo a{color: orange;} /*特殊性值:0,1,0,1*/
div#demo a{color: red;} /*特殊性值:0,1,0,2*/
比较规则
- 选择器都有一个权值,权值越大越优先;
- 当权值相等时,后出现的样式表设置要优于先出现的样式表设置;
- 创作者的规则高于浏览者:即网页编写者设置的 CSS 样式的优先权高于浏览器所设置的样式;
- 继承的 CSS 样式不如后来指定的 CSS 样式;
- 在同一组属性设置中标有!important规则的优先级最大
- 通配符、子选择器、相邻选择器等的。虽然权值为0000,但是也比继承的样式优先
classlist
box.onclick = function () {
flag=!flag
if (flag) {
box.classList.remove("f")
box.classList.add("t")
}else{
box.classList.remove("t")
box.classList.add("f")
}
}
键盘事件
键盘事件 keydown keyup keypress
a) keydown-->keypress-->keyup
b) keydown在按键按下之后会一直不断被触发
//回车发送
var inp=document.querySelector(".inp");
inp.onkeydown=function(e){
// console.log(this.innerHTML);
if(e.keyCode==13){
console.log(this.value)
}
}
输入框输入事件
//input监听
//input框在聚焦状态下的文本变化,例:百度搜索
inp.oninput=function(){
console.log(111);
}
//change监听
//input框在失焦后value变化
inp.onchange=function(){
console.log(222);
}
//focus和blur只在聚焦和失焦的一刻触发一次
//focus 获取焦点时
//用户tab或鼠标点击
inp.onfocus = function () {
console.log(333);
//一般用做用户输入提示
console.log("请用安全键盘输入")
}
// inp.focus(); //默认聚焦状态
inp.onblur = function () {
console.log(111)
//一般做用户输入提示
console.log("这个输入框输入完毕")
}
其他事件
scoll事件
常用于绑定于window对象上,滚动鼠标时触发
//scroll 元素滚动条在滚动时触发
window.onscroll=function(){
console.log(e)
}
//whell 在鼠标滚轮在绑定元素上下滚动时触发
//box绑定时 没有滚动条无法滚动
window.onwheel=function(){
console.log(e)
}
load
等待网页资源下载完毕再执行
//事件执行的时机:
//节点树的加载,资源的加载,缓存的加载,历史加载
//都完成后 触发事件
window.onload = function () {
var box = document.querySelector(".box");
console.log(box);
box.innerHTML="111111"
}
var box = document.querySelector(".box");
console.log(box); /
// box.innerHTML="111111" 报错
//img onload事件
//图片的节点生成完毕时并不会触发
//只有在图片的资源请求完毕(网络请求完毕)
//位置1 写代码 使用图片的节点
img.onload=function(){
//位置2 写代码 使用图片的资源
}
**面试题:**window onload事件和img或者其他媒体资源的onload加载事件的区别:
- img.onload 图片节点加载完毕不会调用 要资源加载完毕就会调用
- window.onload:等待页面所有资源下载完成才执行,包括图片资源的下载,所以它是最慢的
每年都考的面试题:用户从地址栏输入网址按下回车到页面展示出来 整个过程发生了什么?
答案:前端=>网络=>后端=>网络=>前端 这4步都得分析
懒加载
src的意义:
当文档树在解析的过程中, 遇到了src属性
1.按照这个src的url去请求资源(根据网址的协议不同 会按照不同的协议去请求数据)
2.就会按照当前使用src的标签的功能去运行加载的资源
<img class="img"
data-mysrc="https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1591964577&di=c7b659696f8fffdf284d03b333e80407&src=http://n.sinaimg.cn/sinacn10120/74/w1128h546/20190625/b1a6-hyvnhqq9044691.jpg"
alt="">
var img=document.querySelector(".img")
window.onscroll = function () {
let pageY2 = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
if (pageY2 > 600) {
//dataset 自定义
img.src=img.dataset.mysrc;
}
}
预加载
CDN:: js的文件
第三方资源的加载
浏览器请求过的网址,就会把资源缓存起来,当浏览的过程中再次有这个网址资源的网络请求时
不会发送网络请求,直接去缓存中加载资源
面试题:为何使用css精灵? 如何做网页优化(答案的方向有很多,这里是其中一个答案)?
答:为了减少网络请求次数
//在最上面先写好js文件
var img=new Image();
img.src=" ";
11-事件对象
事件对象上存储着事件发生时的相关信息
事件源对象
event.target 火狐
event.srcElement IE6/78
这两个chrome都有
兼容性写法 var ele=event.target|| event.srcElement
path:路径 如 div.box body html document window
鼠标事件触发时:
altKey 鼠标事件发生时,是否按下alt键,返回一个布尔
ctrlKey 鼠标事件发生时,是否按下ctrl键,返回一个布尔
metaKey 鼠标事件发生时,是否按下windows/commond键,返回一个布尔
shiftKey 鼠标事件发生时,是否按下shift键,返回一个布尔
pageX 鼠标点击的 X 坐标;(包含body隐藏的)
pageY 鼠标点击的 Y 坐标;(包含body隐藏的)
clientX clientY返回鼠标位置相对于浏览器窗口左上角的坐标,单位为像素(不包括body隐藏的)
screenX screenY返回鼠标位置相对于屏幕左上角的坐标,单位为像素
movementX,movementY返回一个位移值,单位为像素,表示当前位置与上一个mousemove事件之间的距离
offsetX/offsetY 相对于元素自己的x/y 跟它是否是定位的元素无关
扩展 : 控制浏览器窗口大小 resizeTo(width,height) 宽度必选
键盘事件触发时:
charCode/keyCode 键码值 key 键码
37左
38上
39右
40下
13enter
盒子对象扩展
一 元素的属性
js盒子模型-->浏览器提供一些获取盒子位置信息的属性和方法
1、clientWidth/clientHeight
clientWidth:width+左右padding
clientHeight:height+上下padding
2、clientTop/clientLeft
clientTop:上边框的高度
clientLeft:左边框的宽度
不存在clientRight和clientBottom,这两属性的是都是undefined
3、offsetWidth/offsetHeight
offsetWidth:clientWidth+左右边框
offsetHeight:clientHeight+上下边框
高度不设置的话,高度会自适应,height的值就是实际内容的高度
4、scrollWidth/scrollHeight
在没有内容溢出的情况下和clientWidth/clientHeight是一样的
有内容的溢出情况(在每一个浏览器中的值不太一样,加上overflow: hidden和不加还有少许的区别):
scrollHeight:约等于 真实内容的高度(包含溢出的内容)+上padding
scrollWidth:约等于 真实内容的宽度(包含溢出的内容)+左padding
console.log(oDiv.scrollHeight);
获取当前浏览器一屏幕的宽度和高度
console.log(document.documentElement.clientWidth || document.body.clientWidth);
console.log(document.documentElement.clientHeight || document.body.clientHeight);
当前所有屏加起来的高度(当前网页的高度)~=
console.log(document.documentElement.scrollHeight||document.body.scrollHeight);
5、offsetLeft/offsetTop
偏移量:当前盒子的外边框距离父级参照物产生的位移
offsetLeft:当前元素的外边框距离父级参照物的左偏移量
offsetTop:当前元素的外边框距离父级参照物的上偏移量
获取curEle距离body的偏移量:{top:xxx,left:xxx}
6、scrollLeft/scrollTop是可读写的
scrollTop:当前滚动条卷去的高度
scrollLeft:当前滚动条卷去的宽度
7.xxx.offsetparent
//获取当前点击元素的定位的父级元素
二. 事件对象event的属性
参考:
https://www.cnblogs.com/xulei1992/p/5659923.html
screenX/Y:鼠标位置相对于屏幕的坐标
pageX/Y:相对于文档边缘(包含滚动条距离)
※clientX/Y:相对于当前页面且不包含滚动条距离
※offsetX/Y:ie:相对于当前元素的父级定位元素(块或行内块),除safari外不包含边框。 谷歌:相对于自己的x,y
※因为兼容性问题,需要相对自己的x,y时最好使用自己的clientX/Y-父盒子相对body的x/y 封装了函数offset函数
X/Y:与clientX/Y相同,firefox不支持
layerX/Y:除IE外与pageX/Y相同,IE11下与clientX/Y相同。非官方属性。
12-BOM
BOM
BOM是browser object model的缩写,简称浏览器对象模型
由一系列功能的对象构成,核心对象是window
BOM缺乏标准(不过所有浏览器都支持),JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C//一定要记住,BOM不是W3C的标准模型
window
window对象是BOM的顶层(核心)对象,所有对象都是通过它延伸出来的,也可以称为window的子对象,由于window是顶层对象,因此调用它的子对象时可以不显示的指明//window.document.write(666)等价于document.write(666)
window下的几大功能对象(window的属性)有:navigatior,screen,document,history,location
window属性
closed 返回窗口是否已被关闭。
document 对 Document 对象的只读引用
history 对 History 对象的只读引用
innerHeight 返回窗口的文档显示区的高度
innerWidth 返回窗口的文档显示区的宽度
outerHeight 返回窗口的外部高度,包含工具条与滚动条
outerWidth 返回窗口的外部宽度,包含工具条与滚动条
screenLeft 返回相对于屏幕窗口的x坐标
screenTop 返回相对于屏幕窗口的y坐标
screenX 返回相对于屏幕窗口的x坐标
screenY 返回相对于屏幕窗口的y坐标
location 用于窗口或框架的 Location 对象
navigator 对 Navigator 对象的只读引用
onload 指定所有配置都加载完成时(图片例外)调用的函数.
pageXOffset 返回当前页面相对于窗口显示区左上角的 X 位置(body横向滚动的距离)
pageYOffset 返回当前页面相对于窗口显示区左上角的 Y 位置(body纵向滚动的距离)
screen 对 Screen 对象的只读引用
window方法
alert() 显示带有一段消息和一个确认按钮的警告框。
close() 关闭浏览器窗口。
confirm() 显示带有一段消息以及确认按钮和取消按钮的对话框。
open(url,打开方式,新窗口配置,BOOL) 打开一个新的浏览器窗口
//4个参数都可选(一般就填第一个参数)
//url:新窗口地址 打开方式:_blank(默认),_parent,_self,_top 配置(各种):一般默认 BOOL:新窗口在历史记录里面有,要不要替换
print() 打印当前窗口的内容。
prompt(tishi,value) 显示可提示用户输入的对话框。
scrollBy() 按照指定的像素值来滚动内容(前提是你的有滚动条:内容够多)
scrollTo() 把内容滚动到指定的坐标。(前提是你的有滚动条:内容够多)
setInterval(callback,times) 按照指定的周期(以毫秒计)来调用函数
setTimeout(callback,times) 在指定的毫秒数后调用函数
clearInterval() 取消由 setInterval() 设置的 timeout。
clearTimeout() 取消由 setTimeout() 方法设置的 timeout。
navigator
appCodeName 返回浏览器的代码名
appName 返回浏览器的名称
appVersion 返回浏览器的平台和版本信息
cookieEnabled 返回指明浏览器中是否启用 cookie 的布尔值
platform 返回运行浏览器的操作系统平台
userAgent 返回由客户机发送服务器的user-agent 头部的值
使用:window.navigator.appCodeName
screen
availHeight 返回屏幕的高度(不包括Windows任务栏)
availWidth 返回屏幕的宽度(不包括Windows任务栏)
height 返回屏幕的总高度
width 返回屏幕的总宽度
pixelDepth 返回屏幕的颜色分辨率(每象素的位数)
history
length 返回访问历史列表中的网址数
back() 加载 history 列表中的前一个 URL
forward() 加载 history 列表中的下一个 URL
go(number|url) 加载 history列表中的某个具体页面//负数后退,正数前进
location
当前页面的url
属性:
hash 返回一个URL的锚部分//192.168.1.102:8081?name=jack&pwd=123#page1
host 返回一个URL的主机名和端口
hostname 返回URL的主机名
href 返回完整的URL
pathname 返回的URL路径名
port 返回一个URL服务器使用的端口号
protocol 返回一个URL协议
search 返回一个URL的查询部分
方法:
assign(url) 载入一个新的文档
reload() 重新载入当前文档
replace(url) 用新的文档替换当前文档
使用:window.location.reload()
13-缓存
==>把数据存储到客户端
高频面试题:讲一讲 cookie seessionStorage localStorage 的特点
cookie不安全 每次发网络请求都会携带
使用场景:
API的使用
cookie
Cookie是服务器发送到浏览器的一小段数据,会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
<button type="button" onclick="cookieSet()">cooie存值</button><button type="button" onclick="cookieGet()">cooie取值</button>
//1.cookie的使用
//建立
function cookieSet () {
var dt= new Date(new Date().getTime()+1000*60*60*24)
//expires有效时间
document.cookie=`user=karen&pwd=123;expires=${dt}`//数据持久化
// cookieSet("username","karen")
// cookieSet("pwd","abc123")
}
//获取
function cookieGet () {
var str=document.cookie
console.log(str)
// cookieGet("pwd")
// cookieGet("username")
}
//Cookie的原生api不友好,需要自行封装一下。下面是封装后的方法
//封装几个cookie的使用工具
//设置或者更新缓存数据
function setCookie(cookiekey,cookievalue,expires) {
expires=expires||15*24*60*60*1000
expires=new Date(new Date().getTime()+expires)
var cookieObj={}
//判断是否只有一个缓存信息==>不需要用&分割
var x=document.cookie.indexOf("&");
var xx=document.cookie.indexOf("=");
if(x==-1&&xx!=-1){
var arr=document.cookie.split("=")
cookieObj[arr[0]]=arr[1]
}else{
document.cookie.split("&").forEach((el)=>{
var arr=el.split("=")
cookieObj[arr[0]]=arr[1]
})
}
cookieObj[cookiekey]=cookievalue
//{age:18,name:"karen",count:200}
var keys=Object.keys(cookieObj)
var strlast=""
keys.forEach((el,index)=>{
//最后一个不加&
if(index==(keys.length-1)){
strlast=strlast+el+"="+cookieObj[el]
}else{
strlast=strlast+el+"="+cookieObj[el]+"&"
}
})
document.cookie=`${strlast};${expires}`
}
//存值
function getCookie (key) {
var cookieObj={}
//只有一个值
var x=document.cookie.indexOf("&");
var xx=document.cookie.indexOf("=");
if(x==-1&&xx!=-1){
var arr=document.cookie.split("=")
cookieObj[arr[0]]=arr[1]
}else{
document.cookie.split("&").forEach((el)=>{
var arr=el.split("=")
cookieObj[arr[0]]=arr[1]
})
}
return cookieObj[key]
}
//获取整个
function getCookieAll () {
var cookieObj={}
var x=document.cookie.indexOf("&");
var xx=document.cookie.indexOf("=");
if(x==-1&&xx!=-1){
var arr=document.cookie.split("=")
cookieObj[arr[0]]=arr[1]
}else{
document.cookie.split("&").forEach((el)=>{
var arr=el.split("=")
cookieObj[arr[0]]=arr[1]
})
}
return cookieObj
}
//使用
function cookieSet () {
setCookie("email","2733464076@qq.com")
}
function cookieGet () {
var re1=getCookie("pwd")
var re2=getCookie("username")
var re3=getCookieAll()
console.log(re1,re2)
console.log(re3)
}
防抖和节流
(来自博客)
防抖:
场景:在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。
一般的防抖会有immediate选项,表示是否立即调用。
function now() {
return +new Date()
}
function debounce (func, wait = 50, immediate = true) {
let timer, context, args
// 延迟执行函数
const later = () => setTimeout(() => {
// 延迟函数执行完毕,清空缓存的定时器序号
timer = null
// 延迟执行的情况下,函数会在延迟函数中执行
// 使用到之前缓存的参数和上下文
if (!immediate) {
func.apply(context, args)
context = args = null
}
}, wait)
// 这里返回的函数是每次实际调用的函数
return function(...params) {
// 如果没有创建延迟执行函数(later),就创建一个
if (!timer) {
timer = later()
// 如果是立即执行,调用函数
// 否则缓存参数和调用上下文
if (immediate) {
func.apply(this, params)
} else {
context = this
args = params
}
// 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
// 这样做延迟函数会重新计时
} else {
clearTimeout(timer)
timer = later()
}
}
}
1.对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。
2.对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数
节流:
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为 true,不为 true 则 return
canRun = false; // 立即设置为 false
setTimeout(() => { // 将外部传入的函数的执行放在 setTimeout 中
fn.apply(this, arguments);
// 最后在 setTimeout 执行完毕后再把标记设置为 true(关键)
//表示可以执行下一次循环了。当定时器没有执行的时候
//标记永远是 false,在开头被 return 掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
各种效果
滑动变大(字体,元素
<style>
.box{
width:100px;
height:100px;
background-color: palevioletred;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
position: absolute;
line-height:100px ;
text-align: center;
}
</style>
<div class="box">
<span>hello</span>
</div>
var box=document.querySelector(".box");
document.onwheel=function(e){
if(e.deltaY<0){
// box.style.width //=parseFloat(window.getComputedStyle(box).width)*1.5+"px"
box.style.width=box.offsetWidth*1.5+"px";
box.style.height=box.offsetHeight*1.5+"px";
box.style.lineHeight=parseFloat(window.getComputedStyle(box).lineHeight)*1.5+"px";
box.style.fontSize=parseFloat(window.getComputedStyle(box).fontSize)*1.5+"px";
}else{
box.style.width=box.offsetWidth*0.5+"px";
box.style.height=box.offsetHeight*0.5+"px";
box.style.lineHeight=parseFloat(window.getComputedStyle(box).lineHeight)*0.5+"px";
box.style.fontSize=parseFloat(window.getComputedStyle(box).fontSize)*0.5+"px";
}
}
表单前端验证
工作表格的隔行变色
美团外卖的下拉菜单
12306购票网站多级联动
登录按钮点击跳出模态窗口
模态窗口的拖动
华清远见官网学员信息半透明底部滑动进入
淘宝商品的放大镜效果
京东滚动条滑到一定位置的固定导航栏
app底部的Tab选项卡切换
优化后的无缝轮播图
图片资源加载优化 //1.同源加载优化:浏览器做 2.懒加载优化:自己做
新浪微博JSON数据转UI界面
缓动动画: index=index+(result-index)/n
活动广告弹性效果
斗鱼TV无规则弹幕特效
鼠标移出背景色淡出
百度搜索条动态输入下拉瀑布
信息录入系统(增删改查)
Canvas
HTML5 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.
canvas标签只是图形容器,必须使用脚本来绘制图形。
可以通过多种方法使用 canvas 绘制路径,盒、圆、字符以及添加图像。
ctx.beginPath():开始一段新路径(告诉浏览器要开始画画了);
ctx.moveTo():绘制的初始位置;
ctx.lineWidth:绘制线条宽度;
ctx.strokeStyle:绘制线条颜色(color值);
ctx.fillStyle:图块填充颜色(color值);
ctx.fill():执行图块填充(需放在所有属性设置的后面);
ctx.stroke():执行绘制(需放在所有属性设置的后面);
Math.PI: 数学中的π弧度(换算公式deg*Math.PI/180);
ctx.font:绘制文字设置字号,字体;
ctx.fillText(text, textX, textY):文字内容与位置设置;
ctx.arc(x0,y0,r,beginAngel,endAngel,counterclockwise):弧的一些属性设置;
基本用法
<canvas id="canvas" width="600px" height="600px">如果是低版本浏览器就会显示这句话</canvas>
//1.canvas元素 有宽高属性
//2.宽高被改变 就会重新绘制
//3.获取上下文对象 进行绘制操作
var canvas=document.querySelector("#canvas")
var pen=canvas.getContext("2d")
pen.lineTo(100,100)//x,y的坐标系在canvas 标签左上角 跟上一次的绘制终点 进行连线
pen.lineTo(200,200)
pen.lineTo(100,200)
pen.lineTo(100,100)
pen.stroke()//这个是把前面所有的绘制轨迹 进行绘制
pen.beginPath();//重开画布
pen.moveTo(0,90);//落笔位置
pen.strokeStyle="red" //设置画笔样式
pen.linewidth=2
//绘制格子
pen.beginPath();
var rectH=20;
var rectW=20;
//纵线
for(let i=0;i<canvas.width;i++){
pen.moveTo(rectW*i,0);//落笔
pen.lineTo(rectW*i,canvas.height)//画线
}
//横线
for(let j=0;j<canvas.height;j++){
pen.moveTo(0,rectH*j);
pen.lineTo(canvas.width,rectH*j)
}
饼状图
// ctx.arc(x,y,r,起始角度,结束角度,逆顺时针);
//圆心的x,y 圆的半径 默认顺时针(true)
var info=[120,30,55,60,95];
// 角度换算
var deg=Math.PI/180;
//定义圆心xy
x0=canvas.width*0.5;
y0=canvas.height*0.5;
//开始角度(不能放在里面)
var startDeg=30*deg;
for(let i=0;i<info.length;i++){
ctx.beginPath();
//半径
var r=150;
//扇形角度
var temDeg=info[i]*deg;
//结束角度
var endDeg=startDeg+temDeg;
ctx.moveTo(x0,y0)
//点的位置
var x=Math.cos(startDeg)*r+x0;
var y=Math.sin(startDeg)*r+y0;
ctx.lineTo(x,y)
ctx.arc(x0,y0,r,startDeg,endDeg);
ctx.stroke();
//随机色
// var color="#"+Math.random().toString(16).substr(-6)
var color=["pink","orange","yellow","blue","white"]
ctx.fillStyle=color[i];
ctx.fill();
startDeg=endDeg
}
挂钟
var canvas = document.querySelector("#canvas");
var ctx = canvas.getContext("2d");
var x0 = canvas.width * 0.5;
var y0 = canvas.height * 0.5;
var r = 200;
var deg = Math.PI / 180;
setInterval(() => {
var dt=new Date();
var h=dt.getHours();
var m=dt.getMinutes();
var s=dt.getSeconds();
ctx.clearRect(0, 0, 500, 500);
//每秒钟秒针的变化量 起始值(-90 * deg)
ctx.beginPath();
ctx.moveTo(x0, y0)
var roal = s * 6* deg + (-90 * deg)
ctx.lineTo(Math.cos(roal) * 170 + x0, Math.sin(roal) * 170 + y0)
ctx.stroke()
//每秒钟分针的变化量
ctx.beginPath();
ctx.moveTo(x0, y0)
var roal2 = (m*60+s) * 0.1* deg + (-90 * deg)
ctx.lineTo(Math.cos(roal2) * 150 + x0, Math.sin(roal2) * 150 + y0)
ctx.lineWidth=2;
ctx.stroke()
//每秒钟时针的变化量
ctx.beginPath();
ctx.moveTo(x0, y0)
var roal3 = (h*3600+m*60+s) *0.1* deg/60 + (-90 * deg)
ctx.lineTo(Math.cos(roal3) * 100 + x0, Math.sin(roal3) * 100 + y0)
ctx.lineWidth=3;
ctx.stroke()
scale();
}, 1000)
//绘制刻度
function scale(){
ctx.beginPath();
ctx.arc(x0, y0, r, 0, Math.PI * 2)
ctx.lineWidth = 2;
ctx.stroke()
var r1;
for(let i=0;i<60;i++){
ctx.beginPath();
if(i%5==0){
r1=170;
ctx.lineWidth=3;
}else{
//可以看作起点
r1=180;
ctx.lineWidth=1
}
//一秒钟的度数
var rang=6*deg*i+(-90*deg);
//可以看作终点
var r2=200;
var x1={x:Math.cos(rang)*r1+x0,y:Math.sin(rang)*r1+y0};
var x2={x:Math.cos(rang)*r2+x0,y:Math.sin(rang)*r2+y0};
ctx.moveTo(x1.x,x1.y)
ctx.lineTo(x2.x,x2.y)
ctx.stroke()
}
}
柱状图
var canvas = document.querySelector("#canvas");
var ctx = canvas.getContext("2d");
ctx.moveTo(30,20);
ctx.lineTo(30,450)
ctx.lineTo(450,450)
ctx.fillStyle="pink";
// ctx.fillRect(x,y,w,h)
ctx.fillRect(40,330,40,120);
ctx.fillRect(100,300,40,150);
// ctx.clearRect(x,y,w,h);//清除指定区域
ctx.clearRect(100,300,20,150)
// canvas.width=600; //清屏
//不是canvas的api 是程序员利用了canvas的节点特性,他的宽高重写后,就会被重绘
ctx.stroke();
绘制文字
var canvas=document.querySelector("#canvas")
var ctx=canvas.getContext("2d")
var str="ABCED"
ctx.font="30px webdings"
//复合样式 按照如下顺序书写
// font-style
// font-variant
// font-weight
// font-size/line-height
// font-family
ctx.fillText(str,100,100)
//绘制阴影
//shadowColor 阴影颜色
//shadowBlur 阴影模糊
//shadowOffsetX/Y 阴影X/Y偏移量
绘制图片
var canvas = document.querySelector("#canvas");
var ctx = canvas.getContext("2d");
var img1=new Image();
img1.src="./DOM-img/pika.jpg"";
img1.onload=function(){
ctx.drawImage(img1,50,50)//资源 顶点 顶点
}
var img2=new Image();
//显示先后与创建先后有关系 与src的大小也有关系
img2.src="./DOM-img/pika2.jpg"";
img2.onload=function(){
// .drawImage(img,sx,sy,sw,sh,x,y,w,h);
ctx.drawImage(img2,100,100,300,300,400,200,200,200)
}
游戏动作
<style>
#canvas {
border: 2px solid black;
}
</style>
<canvas id="canvas" width="1500px" height="400px"></canvas>
var canvas = document.querySelector("#canvas");
var ctx = canvas.getContext("2d");
var img1=new Image();
img1.src="./DOM-img/jl.jpg"";
var i=0;
var j=0;
img1.onload=function(){
setInterval(() => {
ctx.clearRect(0,0,1500,400);
i++;
i%=3;
j+=20;
j%=2000;
ctx.drawImage(img1,i*742,0,742,269*1,1400-j,200,370,134)
//资源 顶点 顶点
}, 100);
}
var img2=new Image();
img2.src="./DOM-img/snow.jpg"";
img2.onload=function(){
var k=0;
var time=setInterval(() => {
for(let i=0;i<30;i++){
ctx.clearRect(0,0,1500,200);
k%=150;
// var x=Math.ceil(Math.random()*1500);
ctx.drawImage(img2,100,0,170,260,50,k,50,50)
}
}, 100);
// clearInterval(time)
}
改变画布
var canvas=document.querySelector("#canvas")
var ctx=canvas.getContext("2d")
//矩形
ctx.strokeRect(100,100,120,300)
ctx.save()//保存正常的坐标系
var deg=Math.PI/180
//改变坐标系
ctx.rotate(90*deg)//旋转90度
ctx.translate(0,100)//映射 相对于一开始的矩形而言的xy
ctx.scale(1.5,1.5)//缩放
ctx.globalAlpha=0.3//透明度
//作画
ctx.strokeRect(100,0,120,300)
//还原正常坐标系
ctx.restore()
//正常坐标系下继续作画
ctx.strokeRect(300,0,120,300)
var imgdata=canvas.toDataURL("image/png",1)
console.log(imgdata)
img1.src=imgdata
ES6
1 -let 与const
let
(1)声明变量值在let命令所在的代码块内有效(块级作用域、局部作用域),ES6推荐在函数中使用let,而非var
{
let a=1;
console.log(a); //1
var b=2;
}
console.log(a); //报错
console.log(b); //2
(2)不能重复声明
//var x=2;
//let x=3;
//console.log(x); //报错
let y=4;
let y=5;
console.log(y);
//Identifier 'y' has already been declared
(3)没有变量提升
console.log(z); // 报错ReferenceError
let z= 2;
(4)
var arr=[];
for(var i=0;i<10;i++){
arr[i]=function(){
console.log(i);
}
}
arr[3]();//10
//用var声明,全局下只有一个i,每次循环i的值都会发生改变,循环时function会通过闭包读取到同一个i,所以最终输出的是10
var arr2=[];
for(let i=0;i<10;i++){
arr2[i]=function(){
console.log(i);
}
}
arr2[3]();//3
//用let声明,当前i只在本轮循环有效,每次循环i都是一个新的变量,所以最后输出是3
(5)
for(let i=0;i<3;i++){
let i= "hello";
console.log(i);//打印3次hello
}
//在for循环中,循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域,变量i和外部的变量i是分离的,每次产生新的作用域。
const
count声明时一个只读变量,一旦声明,常量的值就无法改变,意味着,一旦声明就必须初始化,否则会报错
(1)不能改变,不能留到以后赋值,不能重复声明
const c=2;
c=3; //报错 但对前面的没有影响
// const d; //报错 且不会执行前面的语句
(2)代码块内有效:与let命令相同
{
const C=5;
console.log(C);//5
}
console.log(c);//报错
(3)暂时性死区
const命令声明的常量也是不可提升,ton光存在暂时性死区,只能在声明的位置后面使用。
if (true){
console.log(W);//ReferenceError
const W=5;
}
暂时性死区:ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量。从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let和const之前,该变量都是不可用的。
注意:
- let和const都不具备变量提升
- let和const只在最靠近的一个块中有效
- 使用const声明时,使用大写变量
- const在声明时必须被赋值且在声明变量后不能改变
2-解构赋值
(1)概述:解构赋值是对解构运算符的扩展;是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
(2)解构模型:解构的源,解构赋值表达式的右边部分。解构目标,解构赋值表达式的左边部分。
数组模型的解构
let[a,b,c]=[0,1,2];
console.log(a,b,c);//0,1,2
let[d,[e,f]]=[1,[2,3]];
console.log(d,e,f);//1,[2,3],undefined
//右边的值解出来赋给左边的值。解构赋值意思就是说两侧结构域和格式都必须一模一样,否则会得到undefined。解决方法是将e,f括起来==>[e,f]得到[1,2,3]
//可忽略
let[a, ,c]=[1,2,3];
console.log(a,c);//1 3
//不完全解构
let[d,f=3]=[];
console.log(d,f);//undefined 3
//剩余运算符
let [e, ...g] = [1, 2, 3];
console.log(e, g); //a=1,b =[2,3]
//还可做转换 不需要中间量
let a = 1;
let b = 2;
[a,b] = [b,a];
console.log(a,b);//2 1
//字符串
let [a,b,c]="yeh";
console.log(a,b,c);//y e h
//默认值
let [d=5]=[undefined];
console.log(d);//5
let [a=3, b=a] = [1];
console.log(a,b)//1 1
let [c=3, d=c] = [1, 2];
console.log(c,d)//2 2
对象模型的解构
//基本赋值
let a={x:1,y:2};
let {x,y}=a;
console.log(x,y); //1 2
//无声明赋值
//()是必须的,且()之前的表达式需要一个分号
var s,h;
({s,h}={s:3,h:4});
console.log(s,h);//3 4
let obj={a:['hello', {y:'world'}] };
let {a: [x, { y }] } = obj;
console.log(x,y);//hello world
let obj2= {b: ['hello', {y2: 'world'}] };
let {b: [x2, { }] } = obj2;
console.log(x2);//hello 打印y2会报错
//不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
console.log(x,y);//undefined "world"
//剩余运算符
let {a, b, ...c} = {a: 10, b: 20, c: 30, d: 40};
console.log(a,b,c);//10 20 {c: 30, d: 40}
//默认
let{e:ee=1,f:ff=2}={f:3};
console.log(ee,ff);//1 3
3-扩展运算符
是三个点(…),它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。该运算符主要用于函数调用。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
//复制数组
let arr1=[1,2];
arr2=[...arr1];
console.log(arr2);//[1, 2]
let arr3=[1,,3];
arr4=[...arr3];
console.log(arr4);//[1, undefined, 3]
//合并数组
console.log([...[1,3],...[5,6]]);//[1, 3, 5, 6]-
4-Map和Set
Map
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值
var m1=new Map();
//键名 键值
m1.set(2,"hhh");//生成小数组
//取值用上述键名
var r1=m1.get(2)
console.log(r1) //hhh
var m2=new Map();
var arr=[1,2]
// m2.set([1,2],"hello");错误方法
m2.set(arr,"hello")
var r2=m2.get(arr)
console.log(r2);
var m3 = new Map()
m3.set("name", "karen")
m3.set("age", 18)
m3.set("css","yes")
m3.set("js","no")
m3.forEach(function(el,index) {
console.log(el,index)
})
var arr=Array.from(m3)
console.log(arr)
//(4) [Array(2), Array(2), Array(2), Array(2)]
Map特点:有序、键值对(键可以是任意类型)、键名不能重复(如果重复,那么覆盖)
Map 与 Array的转换
var arr1= [["key1", "value1"], ["key2", "value2"]];
//二维数组
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
var myMap = new Map(arr1);
console.log(myMap);
//Map(2) {"key1" => "value1", "key2" => "value2"}
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
var arr2 = Array.from(myMap);
console.log(arr2);
//[["key1", "value1"], ["key2", "value2"]]
Map的克隆
var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
var myMap2 = new Map(myMap1);
console.log(myMap1 === myMap2);
// 打印 false。 Map 对象构造函数生成实例,迭代出新的对象。
Map的合并
var first = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
var second = new Map([[1, 'uno'], [2, 'dos']]);
// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的,对应值即 uno,dos, three
var merged = new Map([...first, ...second]);
Set
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
//基本用法
var s1 = new Set();
s1.add(1);
s1.add(2);
console.log(s1);//Set(2) {1, 2}
s1.clear();//清除
console.log(s1)//{}
面试题
//封装一个数组去重函数
//返回一个新数组
//面试题1 求数组元素出现次数
Array.prototype.norepeat = function () {
var arr1 = []; //
var arr2 = [];
for (var i = 0; i < this.length; i++) {
var counts = 0;
for (var j = 0; j < this.length; j++) {
if (this[i] == this[j]) {
counts++
}
}
arr1.push(counts);
}
//无法实现
// for(let i=0;i<arr1.length;i++){
// if(arr1[i]==1){ //只出现一次
// arr.push(arr[i])
// }else{
// //如果arr1[i]大于1 只向arr添加一次元素
// }
// return arr
// }
//面试题2 数组去重
for (let i = 0; i < arr1.length; i++) {
let flag = false
for (let j = 0; j < this.length; j++) {
if (arr2[j] == this[i]) {
flag = true;
}
}
//一次都没出现过
if (!flag) {
arr2.push(this[i]);
}
}
return { repeatCounts: arr1,norepeatArr:arr2 }
}
//试用
var Arr=[1,2,3,3,44,5,44,66,5]
var Arr2=Arr.norepeat();
console.log(Arr2);
//局限性:不用操作数组中,存放的是引用数据
//+0 -0
//判定为重复时 只保存一个
var s4=new Set();
s4.add(NaN);
s4.add(NaN);
console.log(s4);//Set(1) {NaN}
Set作用
//去重功能
var arr = [44, 55, 44, 66];
var s2 = new Set(arr);//去重
console.log(s2);//Set(3) {44, 55, 66}
//并集
var a1 = new Set([1, 2, 3]);
var b1 = new Set([4, 3, 2]);
var r1 = new Set([...a, ...b]); // {1, 2, 3, 4}
//交集
var a2 = new Set([1, 2, 3]);
var b2 = new Set([4, 3, 2]);
var r2 = new Set([...a].filter(x => b.has(x))); // {2, 3}
//差集
var a3 = new Set([1, 2, 3]);
var b3 = new Set([4, 3, 2]);
var r3 = new Set([...a].filter(x => !b.has(x))); // {1}
//数组转Set
var mySet = new Set(["value1", "value2", "value3"]);
// 用...操作符,将 Set 转 Array
var myArray = [...mySet];
//字符串转Set
var mySet2 = new Set('hello');
console.log(mySet);// Set(4) {"h", "e", "l", "o"}
// 注:Set 中 toString 方法是不能将 Set 转换成 String
5-Symbol
- ES5基本数据: null string number undefined boolean
- ES5引用: 引用数据(对象[各种类型],函数:function)
- ES6基本:Symbol
- ES6引用(对象[出了两种新的类型的对象 Map Set],函数:箭头函数)
//Symbol 独一无二
var s1=Symbol("1")
console.log(s1);//Symbol("1")
var c=s1
var s2=Symbol("1")
console.log(c==s1)//true
console.log(s1==s2)//false
var obj={name:"karen"}
obj["name"]="jack"
obj["name"]="marry"
console.log(obj);//{name: "marry"}
//对象的成员是独一无二的
obj[Symbol("1")]="jack1"
obj[Symbol("1")]="jack2"
console.log(obj);
//{name: "marry", Symbol(1): "jack1", Symbol(1): "jack2"}
var a=Symbol("1")
console.log(a.value)//undefind
let arr={[Symbol("1")]:a}
console.log(arr);//{Symbol(1): Symbol(1)}
console.log(obj[a])//20
6-字符串
- includes():返回布尔值,判断是否找到参数字符串。
- startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部
- 接收两个参数==>参数1 :目标字符串 参数2:开始位置索引
let s = "a,b,o";
s.includes("b"); // true
s.startsWith("a"); // true
s.endsWith("a"); // false
s.startsWith("b",0) // true
//字符串重复
var str1="大小"
var re=str1.repeat(2);
console.log(re)//大小大小
//补全电话号码
var str2="12345678"
var re1=str2.padStart(11,"081");
console.log(re1)//12345678081
var re2=str2.padEnd(11,"081")
console.log(re2)//12345678081
模板字符串:模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中加入变量和表达式。
//为p标签设置随机颜色
//假设界面上有很多p标签
var arr=document.querySelectorAll("p")
for(let i=0;i<arr.length;i++){
var r=Math.round((Math.random()*(255-0)+0))
var g=Math.round((Math.random()*(255-0)+0))
var b=Math.round((Math.random()*(255-0)+0))
//arr[i].style.backgroundColor='rgb('+r+','+g+','+b+')'
//上面这个无法识别换行的字符串
arr[i].style.backgroundColor=`rgb(${r},${g},${b})`
}
标签模板:
应用:过滤 HTML 字符串,防止用户输入恶意内容。
alert`Hello world!`;
// 等价于
alert('Hello world!');
6-数值
//.parseInt()从全局移植到 Number 对象的方法
//相当于向下取整
var a=parseInt("123.456abcd");//123
//.isFinite() 检查一个数值是否为有限的
console.log( Number.isFinite(1)); // true
console.log( Number.isFinite(NaN)); // false
//.parseFloat() 把一个字符串解析成浮点数
//无法解析返回NaN
console.log( Number.parseFloat('123.45'); // 123.45
console.log( Number.parseFloat('123.45abc'); // 123.45
//.isInteger() 用于判断给定的参数是否为整数
Number.isInteger(1); // true
Number.isInteger(1.0); // true
// NaN 和正负 Infinity 不是整数
// 数值的精度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
Number.isInteger(1.0000000000000001) // true
Math对象的扩展
//Math.cbrt() 计算数的立方根
//无法解析返回NaN
Math.cbrt(2); // 16
Math.cbrt('2'); // 16 可以进行转换
//Math.imul()
//两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数
// 大多数情况下,结果与 a * b 相同
Math.imul(1, 2); // 2
//Math.hypot() 用于计算所有参数的平方和的平方根。勾股定理
Math.hypot(3, 4); // 5
7-对象
//普通对象
var obj={fn:function() {}}
//ES6 对象
var obj2={
fn(){},
say(){},
a:20
} //注意是用逗号隔开
//使用
function fn () {
return "its"
}
var obj3={
[fn()](){}
}
console.log(obj3.its) //[fn()](){}
扩展运算符
//基本
let person = {name: "Amy", age: 15};
let someone = { ...person };
console.log(someone); //{name: "Amy", age: 15}
//自定义的属性在拓展运算符后面,则拓展运算符对象内部同名的属性将被覆盖掉
let age = {age: 15,name:"jack"};
let name = {name: "Amy"};
let person = { ...name,...age};
console.log(person); //{age: 15, name: "jack"}
对象新方法
//.is() 用来比较两个数是否严格相等
Object.is("q","q"); // true
Object.is([1],[1]); // false
//此方法与===的区别
//+0不等于-0
Object.is(+0,-0); //false
+0 === -0 //true
//NaN等于本身
Object.is(NaN,NaN); //true
NaN === NaN //false
重要方法
Object.defineProperty()//发布订阅设计模式 来双向数据绑定
Object.keys()//遍历所有属性名 返回数组
Object.values()//遍历所有属性值 返回数组
8-数组
//数组创建
var obj={}
var obj2=new Object()
var obj3=Object.create()
//Array.of() 将参数中所有值作为元素形成真数组
// 参数值可为不同类型
console.log(Array.of(1, '2', true)); // [1, '2', true]
// 参数为空时返回空数组
console.log(Array.of()); // []
//Array.from() 数组对象或可迭代对象转化为数组
console.log(Array.from([1, , 3])); // [1, undefined, 3]
var s1=new Set([1,234])
var re=Array.from(s1)
console.log(re)
//.find() 查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素
var arr=[1,3,4,5,34,65467,756,87678]
var arr=[{name:'karen',age:17},{name:'karen2',age:8},{name:'karen3',age:17}]
var re=arr.find(function(el) {
if(6<el.age&&el.age<10){
return true
}
})
console.log(re)
let arr = Array.of(1, 2, 3, 4);
//参数1:用来填充的值
//参数2:被填充的起始索引
//参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill("hello",2,10));
//[1,2,hello,hello]
//.findindex()查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引
//.fill() 将一定范围索引的数组元素内容填充为单个指定的值
//.entrys() 遍历键值对
//.keys() 遍历所有属性名 返回数组
//.values() 遍历所有属性值 返回数组
9-箭头函数
//普通函数
function fn () {}
var a=function () {}
(function () {})
+function () {}
//基本箭头函数
var f1=(a,b)=>a+b;
console.log(f1(3,3));//6
var f2=(a,b)=>{
return a+b;
}
console.log(f2(1,2));//3
//
var f=(n)=>{return}
f(100);
//当箭头函数没有参数或者有多个参数,要用()括起来,当箭头函数函数体有多行语句,用{}包裹起来,表示代码块;当只有一行语句,并且需要返回结果时,可以省略{},结果会自动返回;当箭头函数要返回对象的时候,为了区分于代码块,要用()将对象包裹起来。
Class类
//ES5
function Person (name) {
this.life=1;
this.name=name
}
// var p1=new Person(“karen”)
function Student (math) {
this.math=math
}
Student.prototype=new Person()
var s1=new Student(3000)
s1.proto.name=“jack”
// ES6
class Person2{
constructor(name) {
this.life=1;
this.name=name
}
}
class Student2 extends Person2{
constructor(name,math) {
super(name,math)
this.math=math
this.say=()=>{}
}
say(){}
}
// var p2=new Person2(“karen”)
var s2=new Student2(“jack”,3000)
// console.log(p1,p2)
console.log(s1,s2)