一、函数
1、什么是函数
假如我们要分别计算1到10、5到12、14到35的整数和。
2、函数的定义和调用
1.和变量类似,函数必须先定义然后才能使用。
使用function关键字定义函数, function是“功能”的意思。
2.函数的定义
function fun(){
//函数体语句
}
//function表示定义函数;fun表示函数名,函数名必须符合JS标识符命名规则;
//圆括号中是形参列表,即使没有形参,也必须书写圆括号
函数表达式
var fun=function(){
//函数体语句
}
//function表示匿名函数
3.函数的调用
执行函数体中的所有语句,就称为“调用函数”。
调用函数非常简单,只需在函数名字后书写圆括号对即可。例:fun();
4.语句执行顺序
按照顺序执行,只有调用函数后,才能执行函数里的语句。
5.函数声明的提升
和变量声明提升类似,函数声明也可以被提升
fun();
function fun(){
alert('函数被执行');
}
//该函数在预解析阶段会被提升
如果函数是用函数表达式的写法定义的,则没有提升特性.
fun();//引发错误
var fun= function(){
alert('函数被执行');
}
函数优先提升:函数优先提升,再提升变量。
3、函数的参数
1.参数是函数内的一些待定值,在调用函数时,必须传入这些参数的具体值。
2.函数的参数可多可少,函数可以没有参数,也可以有多个参数,多个参数之间需要用逗号隔开。
4.形参和实参个数不同的情况
5.arguments
函数内arguments表示它接收到的实参列表,它是一个类数组对象。
类数组对象:所有属性均为从0开始的自然数序列,并且有length属性,和数组类似可以用方括号书写下标访问对象的某个属性值,但是不能调用数组的方法。
4、函数的返回值
1.函数体内可以使用return关键字表示“函数的返回值”。
2.调用一个有返回值的函数,可以被当做一个普通值,从而可以出现在任何可以书写值的地方。
3.遇见return即退出函数:调用函数时,一旦遇见return语句则会立即退出函数,将执行权交还给调用者。
4.结合if语句的时候,往往不需要写else分支了。
比如题目:请编写一个函数,判断一个数字是否是偶数
5、函数算法题
1.寻找喇叭花数
题目:喇叭花数是这样的三位数:其每一位数字的阶乘之和恰好等于它本身。即abc = a! + b! + c! ,其中abc表示一个三位数。试寻找所有喇叭花数。
思路:将计算某个数字的阶乘封装成函数,这样可以让问题简化。
2.JavaScript内置sort()方法
数组排序可以使用sort()方法,这个方法的参数又是一个函数。
这个函数中的a、b分别表示数组中靠前和靠后的项,如果需要将它们交换位置,则返回任意正数;否则就返回负数。
函数是JS中的一等公民,它可以当做参数传入另一个函数。
arr.sort(function(a,b){
return a-b;
});
//从小到大排序;注意格式
6、递归
1.函数的内部语句可以调用这个函数自身,从而发起对函数的一次迭代。在新的迭代中,又会执行调用函数自身的语句,从而又产生一次迭代。当函数执行到某一次时,不再进行新的迭代,函数被一层一层返回,函数被递归。
2.递归是一种较为高级的编程技巧,它把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
3.用求4的阶乘举例
4.递归的要素
边界条件:确定递归到何时终止,也称为递归出口。
递归模式:大问题是如何分解为小问题的,也称为递归体。
5.常见算法题
斐波那契数列:斐波那契数列是这样的数列:1、1、2、3、5、8、13、21,你找到规律了么?
数列下标为0和1的项的值都是1,从下标为2的项开始,每项等于前面两项的和。
7、实现深克隆
1.复习浅克隆:
使用var arr2 = arr1这样的语句不能实现数组的克隆。
浅克隆:准备一个空的结果数组,然后使用for循环遍历原数组,将遍历到的项都推入结果数组。
浅克隆只克隆数组的一层,如果数组是多维数组,则克隆的项会“藕断丝连”。
2.实现深克隆
使用递归思想,整体思路和浅克隆类似,但稍微进行一些改动:如果遍历到项是基本类型值,则
直接推入结果数组;如果遍历到的项是又是数组,则重复执行浅克隆的操作。
8、全局变量和局部变量
1.变量作用域
JavaScript是函数级作用域编程语言:变量只在其定义时所在的function内部有意义。(局部变量)
2.全局变量
如果不将变量定义在任何函数的内部,此时这个变量就是全局变量,它在任何函数内都可以被访问和更改。
3.遮蔽效应
如果函数中也定义了和全局同名的变量,则函数内的变量会将全局的变量“遮蔽”。
4.注意考虑变量声明提升的情况(重点,面试常考)
var a=10;
// 1:局部变量a被自增1,a此时是undefined,自增1结果是NaN
// 2:重新将a赋值为5
function fun(){
a++;
var a=5;
console.log(a);//输出5
}
fun();
console.log(a);//输出10
5.形参也是局部变量
var a=10;
function fun(a){
// 形参a也是函数内部的局部变量
a++;
console.log(a);//输出8
}
fun(7);
console.log(a);//输出10
6.作用域链
(1).先来认识函数的嵌套:一个函数内部也可以定义一个函数。和局部变量类似,定义在一个函数内部的函数是局部函数。
function fun(){
function inner(){//局部函数
console.log('你好');
}
inner();//调用内部函数
}
fun();//调用外部函数
(2).在函数嵌套中,变量会从内到外逐层寻找它的定义。
var a=10,b=20;
function fun(){
var c=30;
function inner(){
var a=40;
var d=50;
// 使用变量时,JS会从当前层开始,逐层向上寻找定义
console.log(a,b,c,d);
}
inner();
}
fun();
// 40 20 30 50
(3).不加var将定义全局变量
var a=1,b=2;
function fun(){
// 字母c没有加var关键字定义,所以它就变为全局变量
c=3;
c++;
var b=4;
b++;
console.log(b);//5
}
fun();
console.log(b);//2
console.log(c);//4
9、闭包
1.JavaScript中函数会产生闭包(closure)。闭包是函数本身和该函数声明时所处的环境状态的组合。
// 创建一个函数
function fun(){
// 定义局部变量
var name="慕课网";
// 返回一个局部函数
return function(){
alert(name);
}
}
// 调用外部函数,就能得到内部函数,用变量inn来接收
var inn=fun();
// 定义一个全局变量
var name='ABC';
// 执行inn函数,就相当于在fun函数的外部,执行了内部函数
inn();
// 输出 慕课网
2.什么是闭包:函数能够“记忆住”其定义时所处的环境,即使函数不在其定义的环境中被调用,也能访问定义时所处环境的变量。
3.观察闭包现象
在JavaScript中,每次创建函数时都会创建闭包。
但是,闭包特性往往需要将函数“换一个地方”执行,才能被观察出来。
4.闭包非常实用
闭包很有用,因为它允许我们将数据与操作该数据的函数关联起来。这与“面向对象编程”有少许相似之处。
5.闭包的功能:记忆性、模拟私有变量。
(1)闭包用途1-记忆性:当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后被自动清除。这就是闭包的记忆性。
举例:创建体温检测函数checkTemp(n),可以检查体温n是否正常,函数会返回布尔值。
但是,不同的小区有不同的体温检测标准,比如A小区体温合格线是37.1℃,而B小区体温合格线是37.3°℃,应该怎么编程呢?
function creatCheckTemp(standardTemp){
function checkTemp(n){
if(n<=standardTemp){
alert('你的体温正常');
}else{
alert('你的体温偏高');
}
}
return checkTemp;
}
// 创建一个checkTemp函数,它以37.1度为标准线
var checkTemp_A=creatCheckTemp(37.1);
// 创建一个checkTemp函数,它以37.3度为标准线
var checkTemp_B=creatCheckTemp(37.3);
checkTemp_A(37.2);
checkTemp_A(37.0);
checkTemp_B(37.2);
checkTemp_B(37.6);
(2)闭包用途2–模拟私有变量
题目:请定义一个变量a,要求是能保证这个a只能被进行指定操作(如加1、乘2),而不能进行其他操作,应该怎么编程呢?
在Java、C++等语言中,有私有属性的概念,但是JavaScript中只能用闭包来模拟。
6.使用闭包的注意点
不能滥用闭包,否则会造成网页的性能问题,严重时可能导致内存泄露。所谓内存泄漏是指程序中己动态分配的内存由于某种原因未释放或无法释放。
7.闭包的一道面试题
function addCount(){
var count=0;
return function(){
count=count+1;
console.log(count);
};
}
var fun1=addCount();
var fun2=addCount();
fun1();
fun2();
fun2();
fun1();
// 输出 1 1 2 2
//fun1和fun2互不影响
10、立即执行函数IIFE
1.什么是IIFE
lIFE (lmmediately Invoked Function Expression,立即调用函数表达式)是一种特殊的JavaScript函数写法,一旦被定义,就立即被调用。
2.形成IIFE的方法
函数不能直接加圆括号被调用。
函数必须转为“函数表达式”才能被调用。圆括号最常见。
3.IFE的作用1-为变量赋值
为变量赋值:当给变量赋值需要一些较为复杂的计算时(如if语句),使用lIFE显得语法更紧凑。
var age = 42;
var sex = '女';
var title = (function () {
if (age < 18) {
return '小朋友';
} else {
if (sex == '男') {
return '先生';
} else {
return '女士';
}
}
})();
alert(title);
lIFE的作用2–将全局变量变为局部变量
lFE可以在一些场合(如for循环中)将全局变量变为局部变量,语法显得紧凑。
var arr=[];
for(var i=0;i<5;i++){
(function(i){
arr.push(function(){
alert(i);
});
})(i);
}
arr[0]();
arr[1]();
arr[2]();
arr[3]();
arr[4]();
// 0 1 2 3 4
11、课程重点
1.什么是函数?函数为编程带来了哪些便利?
2.函数的参数和返回值
3.函数的相关算法题
12、课程难点
1.递归、递归算法题
2.作用域和闭包
3.lIFE
二、DOM
1、DOM基本概念
1.DOM是JS操控HTML和CSS的桥梁。
DOM使JS操作HTML变得优雅。
比如下面的HTML结构,现在想用JavaScript在“牛奶”后面插入一个p标签对,内容是“可乐”。
2.DOM简介:
DOM(Document Object Model,文档对象模型)是JavaScript操作HTML文档的接口,使文档操作变得非常优雅、简便。
DOM最大的特点就是将文档表示为节点树。
2、nodeType常用属性值
节点的nodeType属性可以显示这个节点具体的类型。
nodeType值 | 节点类型 |
---|---|
1 | 元素节点,例如<p>和<div> |
3 | 文字节点 |
8 | 注释节点 |
9 | document节点 |
10 | DTD节点 |
3、document
1.访问元素节点
所谓“访问”元素节点,就是指“得到”、“获取”页面上的元素节点。
对节点进行操作,第一步就是要得到它。
访问元素节点主贾依靠document对象。
2.认识document对象
document对象是DOM中最重要的东西,几乎所有DOM的功能都封装在了document对象中。
document对象也表示整个HTML文档,它是DOM节点树的根。
document对象的nodeType属性值是9。
4、访问元素节点的常用方法
方法 | 功能 | 兼容性 |
---|---|---|
document.getElementById() | 通过id得到元素 | IE6 |
document. getElementsByTagName() | 通过标签名得到元素数组 | IE6 |
document.getElementsByclassName() | 通过类名得到元素数组 | IE9 |
document.querySelector() | 通过选择器得到元素 | IE8部分兼容、IE9完全兼容 |
document.queryselectorAll() | 通过选择器得到元素数组 | IE8部分兼容、IE9完全兼容 |
1.getElementByld()
document.getElementByld()功能是通过id得到元素节点。
注意:参数就是元素节点的id注意不要写#号。
如果页面上有相同id的元素,则只能得到第一个。
不管元素藏的位置有多深,都能通过id把它找到。
<div id="box1">我是盒子1</div>
<div id="box2">我是盒子2</div>
var box1=document.getElementById('box1');
var box2=document.getElementById('box2');
2.getElementsByTagName()
getElementsByTagName()方法的功能是通过标签名得到节点数组.
注意事项:
数组方便遍历,从而可以批量操控元素节点。
即使页面上只有一个指定标签名的节点,也将得到长度为1的数组。
任何一个节点元素也可以调用getElementsByTagName()方法,从而得到其内部的某种类的元素节点。
// 先得到box1
var box1 = document.getElementById('box1');
// 得到box1中的p标签的数组
var ps_inbox1=box1.getElementsByTagName('p');
console.log(ps_inbox1);
3.getElementsByClassName()
getElementsByClassName()方法的功能是通过类名得到节点数组。
注:某个节点元素也可以调用getElementsByClassName()方法,从而得到其内部的某类名的元素节点。
4.querySelector()
querySelector()方法的功能是通过选择器得到元素。
注意事项:
querySelector()方法只能得到页面上一个元素,如果有多个元素符合条件,则只能得到第一个元素。
querySelector()方法从IE8开始兼容,但从IE9开始支持CSS3的选择器,如:nth-child()、 :[src^=‘dog’]等CSS3选择器形式都支持良好
5.querySelectorAll()
querySelectorAll()方法的功能是通过选择器得到元素数组。
即使页面上只有一个符合选择器的节点,也将得到长度为1的数组。
5、延迟运行
在测试DOM代码时,通常JS代码一定要写到HTML节点的后面,否则JS无法找到相应HTML节点。
可以使用window.onload = function()事件,使页面加载完毕后,再执行指定的代码。
<script>
// 给window对象添加onload事件监听,onload表示页面都加载完毕了。
window.onload = function () {
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
console.log(box1);
console.log(box2);
}
</script>
//可以写在head里面
6、节点的关系
关系 | 考虑所有节点 | 只考虑元素节点 |
---|---|---|
子节点 | childNodes | children |
父节点 | parentNode | parentNode |
第一个子节点 | firstChild | firstElementChild |
最后一个子节点 | lastChild | lastElementChild |
前一个兄弟节点 | previousSibling | previousElementSibling |
后一个兄弟节点 | nextsibling | nextElementSibling |
注意:文本节点也属于节点
DOM中,文本节点也属于节点,在使用节点的关系时一定要注意。
在标准的W3C规范中,空白文本节点也应该算作节点,但是在IE8及以前的浏览器中会有一定的兼容问题,它们不把空文本节点当做节点。
7、封装节点关系函数
1.书写常见的节点关系函数
书写IE6也能兼容的“寻找所有元素子节点”函数。
书写IE6也能兼容的“寻找前一个元素兄弟节点”函数。
如何编写函数,获得某元素的所有的兄弟节点?
8、 innerHTML&innerText
1.如何改变元素节点中的内容:
改变元素节点中的内容可以使用两个相关属性:innerHTML和 innerText。
innerHTML属性能以HTML语法设置节点中的内容。
innerText属性只能以纯文本的形式设置节点中的内容。
9、 节点操作
1.如何改变元素节点的CSS样式:
改变元素节点的CSS样式需要使用这样的语句(CSS属性要写成“驼峰”形式):
2.如何更改元素的HTML属性
标准W3C属性,如src、href等等,只需要直接打点进行更改即可:
<img src="images/1.jpg" id="pic">
<a href="http://www.baidu.com" id="link">去百度</a>
<script>
var oPic=document.getElementById('pic');
var oLink=document.getElementById('link');
oPic.src='images/2.jpg';
oLink.href='http://www.imooc.com';
oLink.innerText='去慕课网';
</script>
不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取。
<div id="box"></div>
<script>
var box=document.getElementById('box');
box.setAttribute('data-n',10);
var n=box.getAttribute('data-n');
alert(n);
</script>
10、节点的创建、移除和克隆
1.节点的创建
document.createElement()方法用于创建一个指定tagname的HTML元素。
“孤儿节点”:
新创建出的节点是“孤儿节点”,这意味着它并没有被挂载到DOM树上,我们无法看见它。
必须继续使用appendChild()或insertBefore()方法将孤儿节点插入到DOM树上。
appendChild():任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节
点挂载到它的内部,成为它的最后一个子节点。(append表示追加)
语法:父节点.appendChild(孤儿节点);
insertBefore():任何已经在DOM树上的节点,都可以调用insertBefore()方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点。
语法:父节点.insertBefore(孤儿节点,标杆节点);
<div id="box">
<p>我是原来的段落0</p>
<p>我是原来的段落1</p>
<p>我是原来的段落2</p>
</div>
<script>
var oBox=document.getElementById('box');
var oPs=oBox.getElementsByTagName('p');
// 创建孤儿节点
var oP=document.createElement('p');
// 设置内部文字
oP.innerText='我是新来的';
// 上树
// oBox.appendChild(oP);
oBox.insertBefore(oP,oPs[0]);
</script>
小练习:请动态创建出一个20行12列的表格。
<style>
td{
width: 20px;
height: 20px;
border: 1px solid #000;
}
</style>
<table id="mytable"></table>
<script>
// 请动态创建出一个20行12列的表格
var mytable=document.getElementById('mytable');
for(var i=0;i<20;i++){
// 创建了新的tr标签
var tr=document.createElement('tr');
for(var j=0;j<12;j++){
// 创建了新的td标签
var td=document.createElement('td');
//让tr追加td标签
tr.appendChild(td);
}
// 让mytable追加tr标签
mytable.appendChild(tr);
}
</script>
请制作九九乘法表
<style>
td{
width: 120px;
height: 30px;
border: 1px solid #000;
}
</style>
<table id="mytable"></table>
<script>
// 请动态创建出一个九九乘法表
var mytable=document.getElementById('mytable');
for(var i=1;i<=9;i++){
// 创建了新的tr标签
var tr=document.createElement('tr');
for(var j=1;j<=i;j++){
// 创建了新的td标签
var td=document.createElement('td');
// 设置td内部的文字
td.innerText=i+'乘'+j+'等于'+(i*j);
//让tr追加td标签
tr.appendChild(td);
}
// 让mytable追加tr标签
mytable.appendChild(tr);
}
</script>
2.移动节点
如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将会
被移动。
语法:
新父节点.appendChild(已经有父亲的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);
这意味着一个节点不能同时位于DOM树的两个位置。
<div id="box1">
<p id="para">我是段落</p>
</div>
<div id="box2">
<p>我是box2的原有p标签</p>
<p>我是box2的原有p标签</p>
<p>我是box2的原有p标签</p>
<p>我是box2的原有p标签</p>
</div>
<script>
var box1=document.getElementById('box1');
var box2=document.getElementById('box2');
var para=document.getElementById('para');
var ps_inbox2=box2.getElementsByTagName('p');
box2.appendChild(para);//移动到box2中最后面
box2.insertBefore(para,ps_inbox2[0]);//移动到box2的最前面
</script>
3.删除节点
removeChild()方法从DOM中删除一个子节点。
语法:父节点.removeChild(要删除子节点);
节点不能主动删除自己,必须由父节点删除它。
<div id="box">
<p>我是p结点0</p>
<p>我是p结点1</p>
<p>我是p结点2</p>
</div>
<script>
var box=document.getElementById('box');
var the_first_p=box.getElementsByTagName('p')[0];
box.removeChild(the_first_p);//p的结点0被删除
</script>
4.克隆节点
cloneNode()方法可以克隆节点,克隆出的节点是“孤儿节点”。
语法:
var 孤儿节点=老节点.cloneNode();
var孤儿节点=老节点.cloneNode(true);
参数是一个布尔值,表示是否采用深度克隆:如果为true,则该节点的所有后代节点也都会被克
隆,如果为false,则只克隆该节点本身。
<div id="box1">
<ul>
<li>牛奶</li>
<li>咖啡</li>
<li>可乐</li>
</ul>
</div>
<div id="box2"></div>
<script>
var box1=document.getElementById('box1');
var box2=document.getElementById('box2');
//得到数组
var theul=box1.getElementsByTagName('ul')[0];
// 克隆节点,添加true后为深度克隆
var new_ul=theul.cloneNode(true);
box2.appendChild(new_ul);
</script>
11、 事件监听
1.什么是“事件监听”
DOM允许我们书写JavaScript代码以让HTML元素对事件作出反应。
什么是“事件”:用户与网页的交互动作;当用户点击元素时;当鼠标移动到元素上时;当文本框的
内容被改变时;当键盘在文本框中被按下时;当网页已加载完毕时等等。
什么是“事件监听”:“监听”,顾名思义,就是让计算机随时能够发现这个事件发生了,从而执行程序员预先编写的一些程序。
设置事件监听的方法主要有onxxx和addEventListener()两种,二者的区别将在“事件传播”一课中介绍。
2.最简单的设置事件监听的方法:
最简单的给元素设置事件监听的方法就是设置它们的onxxx属性,像这样:
oBox.onclick = function () {
//点击盒子时,将执行这里的语句
}
3.常见的鼠标事件监听:
事件名 | 事件描述 |
---|---|
onclick | 当鼠标单击某个对象 |
ondblclick | 当鼠标双击某个对象(dbl:double的简写) |
onmousedown | 当某个鼠标按键在某个对象上被按下 |
onmouseup | 当某个鼠标按键在某个对象上被松开 |
onmousemove | 当某个鼠标按键在某个对象上被移动 |
onmouseenter | 当鼠标进入某个对象(相似事件onmouseover) |
onmouseleave | 当鼠标离开某个对象(相似事件onmouseout) |
4.常见的键盘事件监听
事件名 | 事件描述 |
---|---|
onkeypress | 当某个键盘的键被按下(系统按钮如箭头键和功能键无法得到识别) |
onkeydown | 当某个键盘的键被按下(系统按钮可以识别,并且会先于onkeypress发生) |
onkeyup | 当某个键盘的键被松开 |
5.常见的表单事件监听
事件名 | 事件描述 |
---|---|
onchange | 当用户改变域的内容 |
onfocus | 当某元素获得焦点(比如tab键或鼠标点击) |
onblur | 当某元素失去焦点 |
onsubmit | 当表单被提交 |
onreset | 当表单被重置 |
oninput | 当某元素正在输入 |
6.常见的页面事件监听
事件名 | 事件描述 |
---|---|
onload | 当页面或图像被完成加载 |
onunload | 当用户退出页面 |
更多有关窗口或页面的事件在BOM课程中介绍
12、事件传播(面试常考)
2.事件的传播:
实际上,事件的传播是:先从外到内,然后再从内到外。
onxxX写法只能监听冒泡阶段
3.addEventListener()方法
DOM0级事件监听:只能监听冒泡阶段。
oBox.onclick=function(){
};
DOM2级事件监听:
oBox.addEventListener('click',function(){
//这是事件处理函数
},true);
//'click' 事件名不加on
//true监听捕获阶段,false监听冒泡阶段
4.注意事项
最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听。
如果给元素设置相同的两个或多个同名事件,则DOM0级写法后面写的会覆盖先写的;而DOM2级会按顺序执行。
13、事件对象(1)
1.什么是事件对象:
事件处理函数提供一个形式参数,它是一个对象,封装了本次事件的细节。
这个参数通常用单词event或字母e来表示。
oBox.onmousemove=function(e){
//对象e就是这次事件的“事件对象”
};
2.鼠标位置
属性 | 属性描述 |
---|---|
clientX | 鼠标指针相对于浏览器的水平坐标 |
clientY | 鼠标指针相对于浏览器的垂直坐标 |
pageX | 鼠标指针相对于整张网页的水平坐标 |
pageY | 鼠标指针相对于整张网页的垂直坐标 |
offsetX | 鼠标指针相对于事件源元素的水平坐标 |
offsetY | 鼠标指针相对于事件源元素的垂直坐标 |
事件源元素:
相对于浏览器:
整张网页:
![image-20210605114231606](images/7/image-20210605114231606.png)
注:offsetX、clientX,若是盒子里还有其他容器,当在其他容器时,会显示该容器的距离。
14、事件对象(2)
1.e.charCode和e.keyCode属性:
e.charCode属性通常用于onkeypress事件中,表示用户输入的字符的“字符码”。
e.keyCode属性通常用于onkeydown事件和onkeyup中,表示用户按下的按键的“键码”。
2.charCode字符码
字符 | 字符码 |
---|---|
数字0~数字9 | 48 ~ 57 |
大写字母A~ Z | 65 ~ 90 |
小写字母a~ z | 97 ~ 122 |
3.keyCode键码
按键 | 键码 |
---|---|
数字0~数字9 | 48 ~ 57(同charCode键码完全相同) |
字母不分大小a ~ z | 65 ~ 90(同charCode键码的大写字母A ~ Z,而keycode不分大小写,一律为65 ~ 90) |
四个方向键←↑→↓ | 37、38、39、40 |
回车键 | 13 |
空格键 | 32 |
4.小案例:
制作一个特效:按方向键可以控制页面上的盒子移动。
<style>
#box{
position: absolute;
top: 200px;
left: 200px;
width: 100px;
height: 100px;
background-color: orange;
}
</style>
<div id="box"></div>
<script>
var oBox=document.getElementById('box');
// 全局变量t、l,分别表示盒子的top属性值和left属性值
var t=200,l=200;
// 监听document对象的键盘按下事件监听,表示当用户在整个网页上按下按键的时候
document.onkeydown=function(e){
switch(e.keyCode){
case 37:
l-=3;
break;
case 38:
t-=3;
break;
case 39:
l+=3;
break;
case 40:
t+=3;
break;
}
// 更改样式
oBox.style.left=l+'px';
oBox.style.top=t+'px';
}
</script>
15、事件对象(3)
1.e.preventDefault()方法:
e.preventDefault()方法用来阻止事件产生的“默认动作”。—些特殊的业务需求,需要阻止事件的“默认动作”。
2.小案例1
制作一个文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果。
只能输入数字和小写字母:
<input type="text" id="field">
<script>
// 要在英文状态下输入
var oField=document.getElementById('field');
oField.onkeypress=function(e){
console.log(e.charCode);
if(!(e.charCode>=48 && e.charCode<=57 || e.charCode>=97 && e.charCode<=122)){
//阻止浏览器的默认行为
e.preventDefault();
}
};
</script>
3.小案例2
制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1;反之,数字减1.
鼠标滚轮事件是onmousewheel,它的事件对象e提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时返回负值.
<div id="box"></div>
<h1 id="info">0</h1>
<script>
var oBox=document.getElementById('box');
var oInfo=document.getElementById('info');
// 全局变量就是info中显示的数字
var a=0;
// 给box盒子添加鼠标滚轮事件监听
oBox.onmousewheel=function(e){
// 阻止默认事件:就是说当用户在盒子里面滚动鼠标滚轮的时候,此时不会引发页面的滚动条的滚动
e.preventDefault();
if(e.deltaY>0){
a++;
}else{
a--;
}
oInfo.innerText=a;
}
16、事件对象(4)
1.e. stopPropagation()方法
e.stopPropagation()方法用来阻止事件继续传播。在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出bug。
2.小案例:
制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭。
<style>
.modal{
width: 400px;
height: 140px;
background-color: #333;
position: absolute;
top: 50%;
left: 50%;
margin-top: -70px;
margin-left: -200px;
display: none;
}
</style>
<button id="btn">按我弹出弹出层</button>
<div class="modal" id="modal"></div>
<script>
var oBtn=document.getElementById('btn');
var oModal=document.getElementById('modal');
// 点击按钮的时候,弹出层显示
oBtn.onclick=function(e){
// 阻止事件继续传播到document身上
e.stopPropagation();
oModal.style.display='block';
};
// 点击页面任何部分的时候,弹出层关闭
document.onclick=function(){
oModal.style.display='none';
};
// 点击弹出层内部的时候,不能关闭弹出层的,所以应该阻止事件继续传播
oModal.onclick=function(e){
// 阻止事件继续传播到document身上
e.stopPropagation();
};
</script>
17、事件委托(1)(面试常考)
1.批量添加事件监听
题目:页面上有一个无序列表<ul>,它内部共有20个<li>元素,请批量给它们添加点击事件监听,实现效果:点击哪个<li>元素,哪个<li>元素就变红
// 书写循环语句,批量给元素添加监听
for(var i=0;i<lis.length;i++){
lis[i].onclick=function(){
// 在这个函数中,this表示点击的这个元素,this涉及函数上下文的相关知识,我们在“面向对象”课程中介绍
this.style.color='red';
}
}
2.批量添加事件监听的性能问题
每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大。
实际上,每个<li>
的事件处理函数都是不同的函数,这些函数本身也会占用内存。
3.新增元素动态绑定事件:
题目:页面上有一个无序列表<ul>,它内部没有<li>元素,请制作一个按钮,点击这个按钮就能增加一个<li>元素。并且要求每个增加的<li>元素也要有点击事件监听,实现效果点击哪个<li>元素,哪个<li>元素就变红
<button id="btn">按我添加新的li列表项</button>
<ul id="list"></ul>
<script>
var oBtn=document.getElementById('btn');
var oList=document.getElementById('list');
var lis=oList.getElementsByTagName('li');
// 按钮的点击事件
oBtn.onclick=function(){
// 创建一个新的li列表项,孤儿节点
var oLi=document.createElement('li');
oLi.innerHTML='我是列表项';
// 上树
oList.appendChild(oLi);
// 给新创建的这个li节点添加onclick事件监听
oLi.onclick=function(){
this.style.color='red';
};
};
</script>
4.动态绑定事件的问题:
新增元素必须分别添加事件监听,不能自动获得事件监听。
大量事件监听、大量事件处理函数都会产生大量消耗内存。
18、事件委托(2)
1.事件委托:
利用事件冒泡机制,将后代元素事件委托给祖先元素
2.e.target和e.currentTarget属性
事件委托通常需要结合使用e.target属性
属性 | 属性描述 |
---|---|
target | 触发此事件的最早元素,即“事件源元素” |
currentTarget | 事件处理程序附加到的元素 |
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
<script>
var oList=document.getElementById('list');
oList.onclick=function(e){
e.target.style.color='red';
}
</script>
3.事件委托的使用场景:
当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销。
当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听。
4.使用事件委托时需要注意的事项
onmouseenter和onmouseover都表示“鼠标进入”,它们有什么区别呢?
答: onmouseenter不冒泡,onmouseover冒泡。
使用事件委托时要注意:不能委托不冒泡的事件给祖先元素。
最内层元素不能再有额外的内层元素了,比如:
19、 定时器
1.定时器
setInterval()函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔。
函数的参数:setInterval()函数可以接收第3、4…个参数,它们将按顺序传入函数.
具名函数也可以传入setlnterval:
2.清除定时器:
clearInterval()函数可以清除一个定时器。
<h1 id="info">0</h1>
<button id="btn1">开始</button>
<button id="btn2">暂停</button>
<script>
var oInfo=document.getElementById('info');
var oBtn1=document.getElementById('btn1');
var oBtn2=document.getElementById('btn2');
var a=0;
// 全局变量
var timer;
oBtn1.onclick=function(){
// 为了防止定时器叠加,我们应该在设置定时器之前先清除定时器
clearInterval(timer);
// 更改全局变量timer的值为一个定时器实体
timer=setInterval(function(){
oInfo.innerText=++a;
},1000);
};
oBtn2.onclick=function(){
clearInterval(timer);
};
</script>
20、延时器
1.延时器
setTimeout()函数可以设置一个延时器,当指定时间到了之后,会执行函数一次,不再重复执行。
2.清除延时器
clearTimeout()函数可以清除延时器,和clearInterval()非常类似。
3.初步认识异步语句
setInterval()和setTimeout()是两个异步语句。
异步(asynchronous) :不会阻塞CPU继续执行其他语句,当异步完成时,会执行“回调函数”(callback)。
21、使用定时器实现动画
1.使用定时器实现动画较为不便:
(1)不方便根据动画总时间计算步长
(2)运动方向要设置正负
(3)多种运动进行叠加较为困难(比如一个方形一边移动—边变为圆形)
22、JS和CSS3结合实现动画
1.我们知道,CSS3的transition过渡属性可以实现动画。
JavaScript可以利用CSS3的transition属性轻松实现元素动画。
JS和CSs3结合实现动画规避了定时器制作动画的缺点。
2.函数节流
函数节流:一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次。
函数节流非常容易实现,只需要借助setTimeout()延时器。
3.公式
23、动画效果开发1–无缝连续滚动特效
2.代码:
<!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>
*{
margin: 0;
padding: 0;
}
.box{
width: 1000px;
height: 130px;
border: 1px solid #000;
margin: 50px auto;
overflow: hidden;
}
.box ul{
list-style: none;
/* 设置大一点,这样li才能浮动 */
width: 5000px;
position: relative;
}
.box ul li{
float: left;
margin-right: 10px;
}
</style>
</head>
<body>
<div id="box" class="box">
<ul id="list">
<li><img src="images/number/0.png" alt=""></li>
<li><img src="images/number/1.png" alt=""></li>
<li><img src="images/number/2.png" alt=""></li>
<li><img src="images/number/3.png" alt=""></li>
<li><img src="images/number/4.png" alt=""></li>
<li><img src="images/number/5.png" alt=""></li>
</ul>
</div>
<script>
var box=document.getElementById('box');
var list=document.getElementById('list');
// 复制多一遍所有的li
list.innerHTML+=list.innerHTML;
// 全局变量,表示当前list的left值
var left=0;
// 定时器,全局变量
var timer;
move();
// 动画封装成函数
function move(){
// 设表先关,防止动画积累
clearInterval(timer);
timer=setInterval(function(){
left-=4;
// 验收
if(left<=-1260){
left=0;
}
list.style.left=left+'px';
},20);
}
// 鼠标进入停止定时器
box.onmouseenter=function(){
clearInterval(timer);
};
// 鼠标离开继续定时器
box.onmouseleave=function(){
move();
}
</script>
</body>
</html>
24、动画效果开发2–跑马灯轮播图特效(面试问过)
1、原理
2、代码
<!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>
* {
margin: 0;
padding: 0;
}
.carousel {
width: 650px;
height: 360px;
border: 1px solid #000;
margin: 50px auto;
position: relative;
overflow: hidden;
}
.carousel ul {
list-style: none;
width: 6000px;
position: relative;
left: 0px;
transition: left .5s ease 0s;
}
.carousel ul li {
float: left;
}
.carousel .leftbtn {
position: absolute;
left: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
.carousel .rightbtn {
position: absolute;
right: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
</style>
</head>
<body>
<div class="carousel">
<ul id="list">
<li><img src="images/beijing/0.jpg" alt=""></li>
<li><img src="images/beijing/1.jpg" alt=""></li>
<li><img src="images/beijing/2.jpg" alt=""></li>
<li><img src="images/beijing/3.jpg" alt=""></li>
<li><img src="images/beijing/4.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn" id="leftbtn"></a>
<a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
<script>
// 得到按钮和ul,ul整体进行运动
var leftbtn = document.getElementById('leftbtn');
var rightbtn = document.getElementById('rightbtn');
var list = document.getElementById('list');
// 克隆第一张图片
var cloneli = list.firstElementChild.cloneNode(true);
list.appendChild(cloneli);
// 当前ul显示到第几张了,从0开始数
var idx = 0;
// 节流锁
var lock = true;
// 右边按钮监听
rightbtn.onclick = function () {
// 判断锁的状态
if (!lock) return;
lock = false;
// 给list加过渡,为什么要加??css中不是已经加了么??这是因为最后一张图片会把过渡去掉
list.style.transition = 'left .5s ease 0s';
idx++;
if (idx > 4) {
// 设置一个延时器,延时器的功能就是将ul瞬间拉回0的位置,延时器的目的就是让过渡动画结束之后
setTimeout(function () {
// 取消掉过渡,因为要的是瞬间移动,不是“咕噜”回去
list.style.transition = 'none';
list.style.left = 0;
idx = 0;
}, 500);
}
list.style.left = -idx * 650 + 'px';
// 函数节流
setTimeout(function () {
lock = true;
}, 500);
}
// 左边按钮监听
leftbtn.onclick = function () {
if (!lock) return;
lock = false;
// 判断是不是第0张,如果是,就要瞬间用假的替换真的
if (idx == 0) {
// 取消掉过渡,因为要的是瞬间移动,不是“咕噜”过去
list.style.transition = 'none';
// 直接瞬间移动到最后的假图片上
list.style.left = -5 * 650 + 'px';
// 设置一个延时器,这个延时器的延时时间可以是0毫秒,虽然是0毫秒,但是可以让我们过渡先是瞬间取消,然后再加上
setTimeout(function () {
// 加过渡
list.style.transition = 'left .5s ease 0s';
// idx改为真正的最后一张
idx = 4;
list.style.left = -idx * 650 + 'px';
}, 0);
} else {
idx--;
list.style.left = -idx * 650 + 'px';
}
// 函数节流
setTimeout(function () {
lock = true;
}, 500);
}
</script>
</body>
</html>
25、动画效果开发3–呼吸轮播图特效
<!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>
* {
margin: 0;
padding: 0;
}
.carousel {
width: 650px;
height: 360px;
border: 1px solid #000;
margin: 50px auto;
position: relative;
}
.carousel ul {
list-style: none;
}
.carousel ul li {
position: absolute;
top: 0;
left: 0;
/* 透明度都是0 */
opacity: 0;
transition: opacity 1s ease 0s;
}
/* 只有第一张透明度是1 */
.carousel ul li:first-child {
opacity: 1;
}
.carousel .leftbtn {
position: absolute;
left: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
.carousel .rightbtn {
position: absolute;
right: 20px;
top: 50%;
margin-top: -25px;
width: 50px;
height: 50px;
background-color: rgb(28, 180, 226);
border-radius: 50%;
}
</style>
</head>
<body>
<div class="carousel">
<ul id="list">
<li><img src="images/beijing/0.jpg" alt=""></li>
<li><img src="images/beijing/1.jpg" alt=""></li>
<li><img src="images/beijing/2.jpg" alt=""></li>
<li><img src="images/beijing/3.jpg" alt=""></li>
<li><img src="images/beijing/4.jpg" alt=""></li>
</ul>
<a href="javascript:;" class="leftbtn" id="leftbtn"></a>
<a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
<script>
// 得到按钮和ul,ul整体进行运动
var leftbtn = document.getElementById('leftbtn');
var rightbtn = document.getElementById('rightbtn');
var list = document.getElementById('list');
var lis = list.getElementsByTagName('li');
// 当前是第几张图显示
var idx=0;
// 节流
var lock=true;
// 右按钮
rightbtn.onclick=function(){
// 判断节流
if(!lock)return;
lock=false;
// 还没有改idx,此时的idx这个图片就是老图,老图淡出
lis[idx].style.opacity=0;
idx++;
if(idx>4)idx=0;
// 改了idx,此时的idx这个图片就是新图,新图淡入
lis[idx].style.opacity=1;
// 动画结束之后,开锁
setTimeout(function(){
lock=true;
},1000);
}
// 左按钮
leftbtn.onclick=function(){
// 判断节流
if(!lock)return;
lock=false;
// 还没有改idx,此时的idx这个图片就是老图,老图淡出
lis[idx].style.opacity=0;
idx--;
if(idx<0)idx=4;
// 改了idx,此时的idx这个图片就是新图,新图淡入
lis[idx].style.opacity=1;
// 动画结束之后,开锁
setTimeout(function(){
lock=true;
},1000);
}
</script>
</body>
</html>
26、重点内容
1.访问元素节点有哪些方法?
2.节点的关系有哪些?
3.常用节点操作有哪些?
4.节点的创建、移除和克隆要如何实现?
5.事件捕获和冒泡是什么?应该如何设置?(面试常考)
6.什么是事件委托?什么时候要用事件委托?(面试常考)
7.使用定时器和CSS3的过渡实现动画
三、BOM基础
1、Window 对象
1.BOM是什么:
BOM (Browser Object Model,浏览器对象模型)是JS与浏览器窗口交互的接口。
一些与浏览器改变尺寸、滚动条滚动相关的特效,都要借助BOM技术
2.window对象
window对象是当前JS脚本运行所处的窗口,而这个窗口中包含DOM结构,window.document属性就是document对象.
在有标签页功能的浏览器中,每个标签都拥有自己的window对象;也就是说,同一个窗口的标签页之间不会共享一个window对象.
3.全局变量是window的属性
全局变量会成为window对象的属性.
这就意味着,多个js文件之间是共享全局作用域的,即js文件没有作用域隔离功能.
4.内置函数普遍是window的方法
如setlnterval()、alert()等内置函数,普遍是window的方法
5.窗口尺寸相关属性
属性 | 意义 |
---|---|
innerHeight | 浏览器窗口的内容区域的高度,包含水平滚动条(如果有的话) |
innerwidth | 浏览器窗口的内容区域的宽度,包含垂直滚动条(如果有的话)。 |
outerHeight | 浏览器窗口的外部高度 |
outerwidth | 浏览器窗口的外部宽度 |
获得不包含滚动条的窗口宽度,要用document.documentElement.clientWidth
6.resize事件
在窗口大小改变之后,就会触发resize事件,可以使用window.onresize或者window.addEventListener(‘resize’)来绑定事件处理函数。
window.onresize=function(){
var root=document.documentElement;
console.log('窗口改变尺寸了:',root.clientWidth,root.clientHeight);
}
7.已卷动高度
window.scrollY属性表示在垂直方向已滚动的像素值。
8.已动高度
document.documentElement.scrollTop属性也表示窗口卷动高度。
var scrollTop = window.scrollY|| document.documentElement.scrollTop;
document.documentElement.scrollTop不是只读的,而window.scrollY是只读的。
可以document.documentElement.scrollTop=0用来返回顶部。
9.scroll事件
在窗口被卷动之后,就会触发scroll事件,可以使用
window.onscroll或者window.addEventListener(‘scroll’)来绑定事件处理函数。
2、Navigator 对象
window.navigator 属性可以检索navigator对象,它内部含有用户此次活动的浏览器的相关属性和标识。
属性 | 意义 |
---|---|
appName | 浏览器官方名称 |
appVersion | 浏览器版本 |
userAgent | 浏览器的用户代理(含有内核信息和封装壳信息) |
platform | 用户操作系统 |
3、History对象
window.history 对象提供了操作浏览器会话历史的接口。
常用操作就是模拟浏览器回退按钮
<h1>8-History方法网页</h1>
<button id="btn">回退</button>
<a href="javascript:history.back();">回退</a>
<script>
var btn=document.getElementById('btn');
btn.onclick=function(){
// history.back();
history.go(-1);
}
</script>
4、Location 对象
1.window.location标识当前所在网址,可以通过给这个属性赋值命令浏览器进行页面跳转。
如:
window. location = 'http: //www .imooc.com ' ;
window.location.href = 'http: / /www.imooc.com' ;
2.重新加载当前页面
可以调用location的reload方法以重新加载当前页面,参数true表示强制从服务器强制加载。
window.location.reload(true);
3.GET请求查询参数
window.location.search属性即为当前浏览器的GET请求查询参数
比如网址https://www.imooc.com/?a=1&b=2
console.log(window. location.search) ;
5、BOM特效开发
1.返回顶部按钮制作
返回顶部的原理:改变document.documentElement.scrollTop属性,通过定时器逐步改变此值,则将用动画形式返回顶部。
<style>
body{
height: 5000px;
background-image: linear-gradient(to bottom,blue,green,yellow);
}
.backtotop {
width: 60px;
height: 60px;
background-color: rgba(255, 255, 255, .6);
position: fixed;
bottom: 100px;
right: 100px;
/* 小手状 */
cursor: pointer;
}
</style>
<div class="backtotop" id="backtotopBtn">返回顶部</div>
<script>
var backtotopBtn=document.getElementById('backtotopBtn');
var timer;
backtotopBtn.onclick=function(){
// 设表先关
clearInterval(timer);
// 设置定时器
timer=setInterval(function(){
// 不断让scrollTop减少
document.documentElement.scrollTop-=100;
// 定时器肯定要停
if(document.documentElement.scrollTop<=0){
clearInterval(timer);
}
})
};
</script>
2.楼层导航小效果
DOM元素都有offsetTop属性,表示此元素到定位祖先元素的垂直距离。
定位祖先元素:在祖先中,离自己最近的且拥有定位属性的元素。
注:// 净top值,使用这个属性时,所有的祖先元素不要有定位,有定位就不好用了。
记得看代码
6、课程重点
1.窗口相关属性有哪些?
2.窗口卷动事件是什么?如何得到卷动值?
3.要会使用Navigator对象、History对象、Location对象常用属性和方法。