admin管理员组

文章数量:1543843

初识JavaScript

1.1 JavaScript是什么

  • JavaScript是世界上最流行的语言之一,是一种运行在客户端的脚本语
  • 脚本语言:不需要编译,运行过程中由解释器逐行来进行解释并执行
  • 现在也可以基于Node.js技术进行服务器端编程

1.2JavaScript的作用

  • 表单动态校验(密码强度检测)(最初目的)
  • 网页特效
  • 服务端开发(Node.js)
  • 桌面程序(Electron)
  • APP(Cordova)
  • 控制硬件-物联网(Ruff)
  • 游戏开发(cocos2d-js)

1.3浏览器执行JS简介

浏览器分成两部分:渲染引擎和JS引擎
渲染引擎:用来解析HTML与CSS,俗称内核,比如chrome浏览器的blinj,老版本的webkit
JS引擎:也称为JS解释器。用来读取网页中的JavaScript代码,对齐处理后运行,比如chrome浏览器的V8
浏览器本身并不会执行JS代码,而是通过内置JavaScript引擎来执行JS代码。JS引擎执行代码时逐行解释每一句源码(转换为机器语言),然后由计算器去执行,所以JavaScript语言归为脚本语言,会逐行解释执行。

1.4 JavaScript的组成

  • JavaScript语法ECMAScript
  • 页面文档对象模型DOM
  • 浏览器对象模型BOM

1.5 JavaScript初体验

JavaScript由3种书写位置:行内、内嵌和外部
1.行内JS

<input type="button" value="点我试试" onclick="alert('Hello World!')" />

2.内嵌JS

<script>
     alert('Hello World!');
</script>

3.外部JS文件

<script src="XXX.js"></script>

1.6 JavaScript注释

1.单行注释// (ctrl + /)
2.多行注释/**/ (shift + alt + a)

1.7 JavaScript输入输出语句

2.变量

2.1什么是变量

变量是用于存放数据的容器。我们通过变量名获取数据,甚至可以修改数据

2.2变量在内存中的存储

变量是程序在内存中申请的一块用来存放数据的空间

2.3变量的使用

1.声明变量 var age;
2.赋值 age = 10;
3.变量的初始化 var age = 18;

2.4声明变量特殊情况

2.5变量命名规范

  • 由字母、数字、下划线、美元符号$ 组成
  • 严格区分大小写
  • 不能以数字开头
  • 不能是关键字、保留字
  • 变量名必须有意义
  • 遵守驼峰命名法:首字母是小写,后面单词的首字母需要大写

3.数据类型

变量是用来存储值的所在处,它们有名字和数据类型。变量的数据类型决定了如何让将代表这些值的位存储到计算机的内存中。JavaScript是一种弱类型或者说动态语言,这意味着不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。

var age = 10;   //这是一个数字型
var areYouOk = '是的'//这是一个字符串

在代码运行过程中,变量的数据类型由JS引擎根据等号=右边变量值的数据类型来判断,运行完毕之后,变量就确定了数据类型。
JavaScript拥有动态类型,同时也意味着相同的变量可用作不同的类型

var x = 6;   //x为数字
var x = "Bill";   //x为字符串

3.1简单数据类型

3.1.1数字型

八进制:0-7,数字前面加 0
十六进制:0-9 a-f 数字前面加 0x
JavaScript中数值的最大和最小值

alert(Number.MAX_VALUE);  //最大值
alert(Number.MIN_VALUE);  //最小值 
alert(Infinity);  //无穷大
alert(-Infinity);  //无穷小
alert(NaN);  //Not a number,代表一个非数值
isNaN():用来判断一个变量是否为非数字的类型,返回truefalse

3.1.2字符串型String

字符串型可以是引号中的任意文本,其语法为双引号""单引号’',JS可以用单引号嵌套双引号,或者用双引号嵌套单引号外双内单,外单内双

var strMsg = "我爱北京天安门";   //使用双引号表示字符串
var strMsg2 = '我爱北京天安门';   //使用单引号表示字符串

//常见错误
var strMsg3 = 我爱大肘子;   //报错,没使用引号,会被认为是js代码,但js没有这些语法

var strMsg4 = '我是"高富帅"程序猿';  //可以用''包含""
var strMsg5 = "我是'高富帅'程序猿";  //可以用""包含''

//常见错误
var badQuotes = 'What on earth?" //报错,单双引号不搭配

1.转义字符

2.字符串长度length
3.字符串拼接

  • 多个字符串之间可以使用 + 进行拼接,其拼接方式为字符串 + 任何类型 = 拼接之后的新字符串
  • 拼接前会把与字符串相加的任何类型转换成字符串,再拼接成一个新的字符串
  • 数值相加,字符相连
  • 变量不要写到字符串里面,是通过和字符串相连的方式实现的:引引加加(删掉数字,变量写加中间)

3.1.3 布尔型Boolean

布尔类型有两个值:true(真)和false(假)
布尔型和数字型相加的时候,true的值为1,false的值为0

console.log(true + 1);  //2
console.log(false + 1);  //1

3.1.4 Undefined和Null

如果一个变量声明未赋值,就是undefined未定义数据类型,undefined和数字相加,最后的结果是NaN
一个声明变量给null值,里面存的值为

var variable;
console.log(variable);  //undefined
console.log('你好' + variable);  //你好undefined
console.log(11 + variable);     //NaN
console.log(true + variable);   //NaN

var vari = null;
console.log('你好' + vari);  //你好null
console.log(11 + vari);     //11
console.log(true + vari);   //1

typeof可用来获取检测变量的数据类型

var num = 10;  
console.log(typeof num); //number
var str = 'pink';  
console.log(typeof str); //string
var flag = true;  
console.log(typeof flag); //boolean
var vari = underfined;  
console.log(typeof vari); //undefined
var timer = null;  
console.log(typeof timer); //object

3.2 字面量

字面量是在源代码中一个固定值的表示法,通俗来说,字面量表示如何表达这个值。

  • 数字字面量:8,9,10
  • 字符串字面量:‘黑马程序员’,“大前端”
  • 布尔字面量:true,false

3.3数据类型转换

3.3.1什么是数据类型转换

使用表单、prompt获取过来的数据默认是字符串类型的,此时就不能直接简单的进行加法运算,而需要转换变量的数据类型。通俗来说,就是把一种数据类型的变量转换成另外一种数据类型。我们通常会实现3种方式的转换:

  • 转换为字符串类型
  • 转换为数字型
  • 转换为布尔型

3.3.2转换为字符串

3.3.3转换为数字型(重点)

console.log(parseInt('120px'));     //120会去掉这个px单位
console.log(parseInt('3.94'));      //3
console.log(parseFloat('120px'));   //120
console.log('12' - 0);        //12
console.log('123' - '120');   //3
console.log('123' * 1);       //123

3.3.4转换为布尔型


代表空、否定的值会被转换为false,如’'、0、NaN、null、underfined
其余值都会被转换为true

3.4解释型语言和编译型语言

计算机不能直接理解任何机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言才能执行程序。程序语言翻译成机器语言的工具,被称为翻译器。

  • 翻译器翻译的方式有两种:编译、解释,两种方式之间的区别在于翻译的时间点不同
  • 编译器在代码执行之前进行编译,生成中间代码文件
  • 解释器在运行时进行及时解释,并立即执行

3.5标识符、关键字、保留字

标识符:指开发人员为变量、属性、函数、参数取的名字,标识符不能是关键字或保留字
关键字:指JS本身已经使用了的字,不能再用它们充当变量名、方法名
保留字:实际上就是预留的“关键字”,意思是现在虽然还不是关键字,但是未来可能会成为关键字,同样不能使用它们当变量名或方法名

4.运算符

运算符也称为操作符,是用于实现赋值、比较和执行算术运算等功能的符号。JavaScript中常见的运算符有:

  • 算数运算符
  • 递增和递减运算符
  • 比较运算符
  • 逻辑运算符
  • 赋值运算符

4.1 算术运算符

算术运算使用的符号,用于执行两个变量或值的算术运算

浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数,所以,不要直接判断两个浮点数是否相等

表达式和返回值

表达式:由数字、运算符、变量等组成的式子
返回值:表达式最终都会返回一个结果

4.2 递增和递减运算符

如果需要反复给数字变量添加或减去1,可以使用递增(++)递减(–)运算符来完成。
在JavaScript中,递增(++)和递减(–)既可以放在变量前面,也可以放在变量后面。放在变量前面时(++num),可以称为
前置递增(递减)运算符(先加/减,再返回值)
,放在变量后面时(num++),可以称为后置递增(递减)运算符(先返回原值,再自加/减)递增和递减运算符必须和变量配合使用

4.3比较运算符

比较运算符是两个数据进行比较时所使用的运算符,比较运算后,会返回一个布尔值作为比较运算的结果。

4.4逻辑运算符

逻辑运算符是用来进行布尔值运算的运算符,其返回值也是布尔值

短路运算(逻辑中断)
原理:当有多个表达式(值)时,左边的表达式(值)可以确定结果时,就不再继续运算右边的表达式的值.

1.逻辑与 &&

  • 语法:表达式1 && 表达式2
  • 如果第一个表达式的值为真,则返回表达式2
  • 如果第一个表达式的值为假,则返回表达式1
 console.log(123 && 456);   //456
 console.log(0 && 456);   //0

2.逻辑或 ||

  • 语法:表达式1 || 表达式2
  • 如果第一个表达式的值为真,则返回表达式1
  • 如果第一个表达式的值为假,则返回表达式2
console.log(123 || 456);   //123
console.log(0 || 456);   //456
console.log(123 || 456 || 789);   //123

4.5赋值运算符

用来把数据赋值给变量的运算符

4.6运算符优先级

5.JavaScript流程控制

在一个程序执行过程中,各条代码的执行顺序对程序的结果是有直接影响的,很多时候我们要通过控制代码的执行顺序来实现我们要完成的功能。
流程控制主要有三种结构:顺序结构、分支结构和循环结构

5.1顺序流程控制

顺序结构是程序中最简单、最基本的流程控制,它没有特定的语法结构,程序会按照代码的先后顺序,依次执行

5.2分支流程控制

由上到下执行代码的过程中,根据不同的条件,执行不同的路径代码,从而得到不同的结果

5.2.1 if语句

if else语句(21if (条件表达式) {
     // [如果] 条件成立时执行的语句
} else {
     // [否则] 执行的语句
}

多分支语句(多选1if (条件表达式1) {
    // 语句1;
} else if (条件表达式2) {
    // 语句2;
} else if (条件表达式3) {
    // 语句3;
} else {
    // 最后的语句
}

5.2.2 三元表达式

由三元运算符组成的式子称为三元表达式
条件表达式 ? 表达式1 : 表达式2
执行思路:如果条件表达式结果为真,则返回表达式1的值;如果条件表达式结果为假,则返回表达式2的值
把返回值赋值给一个变量

5.2.3 switch语句

switch语句也是多分支语句,它用于基于不同的条件来执行不同的代码。当要针对变量设置一系列的特定值的选项时,就可以使用switch。

switch (表达式) {
      case value1:
          执行语句1;   // 表达式 等于 value1 时
          break;
      case value2:
          执行语句2;
          break;
      .....
      default:
          执行最后的语句;
}

注意:

  • 开发时,表达式经常写成变量
  • 变量的值和case里面的值相匹配时是全等,必须是值和数据类型一致才可以
  • 如果当前case里面没有break,则不会退出switch,而是继续执行下一个case,直到遇到break或default为止

5.2.4 switch语句和if else if语句的区别

  • 一般情况下,它们两个可以相互替换
  • switch…case语句通常处理case为比较确定值的情况,而if…else…语句更加灵活,常用于范围判断(大于、等于某个范围)
  • switch语句进行条件判断后直接执行到程序的条件语句,效率更高;而if…else语句有几种条件,就得判断多少次
  • 当分支较少时,if…else语句的执行效率比switch语句高
  • 当分支较多时,switch语句的执行效率较高,且结构更清晰

5.3循环

目的:重复执行某些语句

  • for 循环
  • while 循环
  • do…while 循环

5.3.1 for循环

在程序中,一组被重复执行的语句称之为循环体,能否继续重复执行,取决于循环的终止条件。由循环体及循环的终止条件组成的语句,称之为循环语句。
for循环主要用于把某些代码循环若干次,通常跟计数有关

for (初始化变量; 条件表达式; 操作表达式) {
       // 循环体
}
// 双重for循环:外层循环循环一次,里面的循环执行全部
for (外层的初始化变量; 外层的条件表达式; 外层的操作表达式) {
    for (内层的初始化变量; 内层的条件表达式; 内层的操作表达式) {
       // 执行语句;
    }
}

5.3.2 while循环

当条件表达式结果为true时,执行循环体,否则退出循环。里面应该有计数器-初始化变量;也应该有操作表达式-完成计数器的更新,防止死循环(先判断再执行

while (条件表达式) {
      // 循环体
}

5.3.3 do while循环

do {
    // 循环体
} while (条件表达式) 

先执行一次循环体,再判断条件。如果条件表达式结果为真,则继续执行循环体,否则退出循环

5.3.4 continue break

continue关键字用于立即跳出本次循环,继续下一次循环(本次循环体中continue之后的代码就会少执行一次)
break关键字用于立即跳出整个循环(循环结束)

6. JavaScript数组

6.1数组的概念

数组(Array)是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组是一种将一组数据存储在单个变量名下的优雅方式

6.2创建数组

数组的创建方式:

  • 利用new创建数组
  • 利用数组字面量[ ]创建数组

1.利用new创建数组

var 数组名 = new Array();
var arr = new Array();  // 创建一个新的空数组

2.利用数组字面量[ ]创建数组

// 1.使用数组字面量方式创建空的数组
var 数组名 = [];
// 2.使用数组字面量方式创建初始值的数组
var 数组名 = ['小白','小黑','大黄','佩奇'];

3.数组元素的类型

数组中可以存放任意类型的数据,例如字符串、数字、布尔值等

6.3 获取数组元素

索引(下标):用来访问数组元素的序号(数组下标从0开始
数组可以通过索引来访问、设置、修改对应的数组元素,通过数组名[索引]的形式来获取(访问)数组中的元素

6.4 遍历数组

遍历:把数组中的每个元素从头到尾访问一次
for循环

// i是计数器,当索引号使用
for (i = 0; i < 数组名.length; i++)

数组长度(元素个数):数组名.length动态监测数组元素的个数

6.5数组中新增元素

6.5.1 通过修改length长度新增数组元素

  • 可以通过修改length长度来实现数组扩容的目的
  • length属性是可读写的
var arr = ['red' , 'blue' , 'yellow' , 'green'];
arr.length = 7;
console.log (arr[4]); // undefined

6.5.2 通过修改数组索引新增数组元素

  • 可以通过修改数组索引的方式追加数组元素
  • 不能直接给数组名赋值,否则会覆盖掉以前的数据
var arr = ['red' , 'blue' , 'yellow'];
arr[3] = 'pink';
console.log (arr); // ['red' , 'blue' , 'yellow' , 'pink'] 追加
arr[0] = 'green';
console.log (arr); // ['green' , 'blue' , 'yellow' , 'pink'] 替换

6.6冒泡排序

是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来,走访数列的工作是重复的进行直到没有再需要交换的,即该数列已排序完成
1.一共需要的趟数(数组长度-1),用外层for循环
2.每一趟交换次数(数组长度-i-1),用里层for循环
3.交换两个变量

var arr2 = [4, 1, 2, 3, 5];
for (var i = 0; i < arr2.length - 1; i++) { //外层for循环管趟数
     for (var j = 0; j < arr2.length - i - 1; j++) { //里层for循环管每一趟交换次数
         //内部交换2个变量的值,前一个和后一个数组元素相比较
          if (arr2[j] > arr2[j + 1]) {
              var temp = arr2[j];
              arr2[j] = arr2[j + 1];
              arr2[j + 1] = temp;
           }
       }
}
console.log(arr2);

7. JavaScript函数

函数就是封装了一段可以被重复执行调用的代码块

7.1函数的使用

函数使用:声明函数 和 调用函数

1.声明函数

function 函数名() {
    // 函数体
}
  • function声明函数的关键字,全部小写
  • 函数是做某件事情,函数名一般是动词
  • 函数不调用,自己不执行

2.调用函数

函数名();

函数的封装:把一个或多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口

7.2函数的参数

参数的作用:在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去

1.形参

声明函数的小括号里(形式上的参数)
形参是接受实参的,可看作是一个不用声明的变量

2.实参

调用函数的小括号里(实际的参数)

3.形参实参个数匹配

  • 如果实参的个数和形参的个数一致,则正常输出结果
  • 如果实参的个数多于形参的个数,会取到形参的个数
  • 如果实参的个数小于形参的个数,多余的形参定义为undefined,最终的结果就是 NaN

7.3函数的返回值

有时,我们希望函数将值返回给调用者,此时通过return语句就可以实现

function 函数名() {
      return 需要返回的结果 ;
}
// 调用
函数名();

只要函数遇到return,就把后面的结果返回给函数的调用者,即
函数名() = return后面的结果
实际开发中,经常使用一个变量来接受函数返回的结果

//利用函数求数组中的最大值
function getArrMax(arr) {
    var max = arr[0];
    for (var i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                    max = arr[i];
            }
     }
     return max;
} 
var re = getArrMax([5,44,67,45,90,101]);
console.log(re);

return语句之后的代码不被执行
return只能返回一个值。如果用逗号隔开多个值,以最后一个为准。如果想返回多个值,可以使用数组 return [a, b, c, d];
如果函数没有return,则返回undefined

break、continue、return的区别

  • break:结束当前循环体
  • continue:跳出本次循环,继续执行下次循环
  • return:不仅可以退出循环,还能返回return语句中的值,同时还可以结束当前的函数体内的代码

7.4 arguments的使用

当我们不确定有多少个参数传递的时候,可以使用arguments来获取。在JavaScript中,arguments实际上是当前函数的一个内置对象所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组(并不是真正意义上的数组)具有以下特点:

  • 具有length属性
  • 按索引方式存储数据
  • 不具有数组的push、pop等方法
// 1.利用函数求任意个数的最大值
function getMax() {
    var max = arguments[0];
    for (var i = 1; i < arguments.length; i++) {
            if (arguments[i] > max) {
                    max = arr[i];
            }
     }
     return max;
}
console.log(getMax(1,2,34,56,78,100));

// 2.利用函数翻转任意数组
function reverse(arr) {
   var newArr = [];
   for (var i = arr.length - 1; i >= 0; i++) {
        newArr[newArr.length] = arr[i];
   }
   return newArr;
}
var arr1 = reverse([1,2,34,56,78,100]);
console.log(arr1);

// 3.利用函数冒泡排序
function sort(arr) {
   for (var i = 0; i < arr.length - 1; i++) {
       for (var j = 0; j < arr.length - i - 1; j++) {
           if (a[j] > a[j+1]) {
               var temp = a[j];
               a[j] = a[j+1];
               a[j+1] = temp;
           }
       }
   }
   return arr;
}
var arr1 = sort([1,2,34,56,78,100]);
console.log(arr1);

// 4.利用函数判断闰年
function isRunYear(year) {
  // 如果是闰年返回true,否则返回false
  var flag = false;
  if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
       flag = true;
  }
  return flag;
}
console.log(isRunYear(2000));   // true
console.log(isRunYear(1999));   // false

7.5 函数可以相互调用

因为每个函数都是独立的代码块,用于完成特殊任务,因此经常会用到函数相互调用的情况

// 用户输入年份,输出当前年份2月份的天数
function backDay() {
  var year = prompt('请输入年份:');
  if (isRunYear(year)) {
      alert('当前年份是闰年,2月份有29天');
  } else {
      alert('当前年份是平年,2月份有28天');
  }
}
backDay();

// 判断是否为闰年的函数
function isRunYear(year) {
   var flag = false;
   if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
       flag = true;
  }
  return flag;
}

7.6 函数的两种声明方式

1.利用函数关键字自定义函数(命名函数)

function 函数名() {}

2.函数表达式(匿名函数)

var 变量名 = function() {}

调用:变量名();

8.JavaScript作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

8.1全局作用域和局部作用域(es6之前)

全局作用域:整个script标签,或者是一个单独的js文件
局部作用域(函数作用域):在函数内部就是局部作用域,这个代码的名字(变量)只在函数内部起效果和作用

8.2变量作用域

  • 全局变量(在函数外部定义的变量):在全局作用域下的变量,在全局下都可以使用;在函数内部没有声明 直接赋值的变量
  • 局部变量(在函数内部定义的变量):在局部作用域下的变量,只能在函数内部使用;函数的形参也可以看作是局部变量
  • 全局变量只有浏览器关闭时才会销毁,比较占内存资源
  • 局部变量当程序执行完毕就会销毁,比较节约内存资源

8.3作用域链

  • 只要是代码,就至少有一个作用域
  • 写在函数内部的局部作用域
  • 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
  • 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链—就近原则

9.JavaScript预解析

JavaScript代码是由浏览器中的JavaScript解析器来执行的,JavaScript解析器在运行JavaScript代码的时候分为两步:预解析和代码执行
1.预解析
js引擎会把js里面所有的var、function提升到当前作用域的最前面
(1)变量预解析(变量提升)
把所有的变量声明提升到当前作用域的最前面,不提升赋值操作
(2)函数预解析(函数提升)
把所有的函数声明提升到当前作用域的最前面,不调用函数
使用函数表达式声明函数时,调用必须写在函数表达式的下面
2.代码执行
按照代码书写的顺序从上往下执行

f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
   var a=b=c=9;  //相当于 var a=9;b=9;c=9; 
   // b和c直接赋值,没有var声明,当全局变量看;
   // 集体声明:var a=9,b=9,c=9;(var a=9; var b=9; var c=9;)
   console.log(a);
   console.log(b);
   console.log(c);
}
//相当于以下代码
function f1() {
   var a;
   a=b=c=9;
   console.log(a);  //9
   console.log(b);  //9
   console.log(c);  //9
}
f1();
console.log(c); //9
console.log(b); //9
console.log(a); //报错 a is not defined

10.JavaScript对象

在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
属性:事物的特征,在对象中用属性来表示(常用名词
方法:事物的行为,在对象中用方法来表示(常用动词
保存一个值时,可以使用变量;保存多个值时,可以使用数组。若要保存一个人的完整信息:JS中的对象表达结构更清晰,更强大

10.1创建对象的方式

  • 利用字面量创建对象
  • 利用new Object创建对象
  • 利用构造函数创建对象

10.1.1 利用字面量创建对象

对象字面量:就是花括号{}里面包含了表达这个具体事务(对象)的属性和方法

var obj = {}; //创建了一个空的对象
var obj = {
      uname: '张三疯',
      age: 18,
      sex: '男',
      sayHi: function() {
            console.log('hi~');
      }
 }
  • 里面的属性或者方法采用键值对的形式 键 属性名 : 值 属性值
  • 多个属性或方法中间用逗号隔开
  • 方法冒号后面跟的是一个匿名函数
  • 调用对象的属性: 对象名.属性名对象名['属性名']
  • 调用对象的方法:对象名.方法名()

10.1.2 变量、属性、函数、方法的区别

变量和属性
相同点:都是用来存储数据的
不同点:
变量–单独声明并赋值,使用的时候直接写变量名,单独存在
属性–在对象里面、不需要声明,使用的时候必须是 对象.属性名对象['属性名']
函数和方法
相同点:都是实现某种功能、做某件事
不同点:
函数–单独声明,调用是 函数名()、单独存在
方法–在对象里面,调用是 对象.方法名()

10.1.3 利用new Object创建对象

var obj = new Object();  // 创建了一个空的对象
obj.name = '张三疯';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function() {
     console.log('hi~');
 }

10.1.4 利用构造函数创建对象

构造函数:把对象里面一些相同的属性和方法抽象出来封装到函数里面

// 声明
function 构造函数名() {
     this.属性 =;
     this.方法 = function() {}
}
// 调用
new 构造函数名();
-------------------------------------------------------------------
// 声明
function Star(uname,age,sex) {
     this.name = uname;
     this.age = age;
     this.sex = sex;
     this.sing = function(sang) {
         console.log(sang);
     }
}
// 调用
var ldh = new Star('刘德华',18,'男');  //调用函数返回的是一个对象
console.log(typeof ldh);  //object
console.log(ldh.name); // 刘德华
console.log(ldh['age']); // 18
ldh.sing('冰雨');
  • 构造函数名字首字母要大写
  • 构造函数不需要return,就可以返回结果
  • 调用构造函数必须使用 new
  • 只要调用了构造函数就创建了一个对象
  • 属性和方法前面必须添加 this

构造函数和对象的区别
构造函数:抽象了对象的公共部分,封装到了函数里面,泛指某一大类,类似于java里面的类class
对象:特指,是一个具体的事物
利用构造函数创建对象的方式称为对象实例化

10.2 new关键字

new在执行时会做四件事:

  • 首先在内存中创建一个新的空对象
  • 让this指向这个新的对象
  • 执行构造函数里面的代码,为这个新的对象添加属性和方法
  • 返回这个新对象(所以构造函数里面不需要return)

10.3 遍历对象属性

我们使用for in 里面的变量,经常用k或者key属性名

for (变量 in 对象) {

}
--------------------------------------------------------
for (var k in obj) {
    console.log(k);   // k变量 输出 得到的是 属性名
    console.log(obj[k]); // obj[k] 得到的是 属性值
}

10.4 JavaScript内置对象

JavaScript中的对象分为3种:自定义对象、内置对象、浏览器对象
前两种对象是JS基础内容,属于ECMAScript,第三个浏览器对象属于JS独有的。
内置对象就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)
JavaScript提供了多个内置对象:Math、Date、Array、String等

如何学习对象中的方法

MDN文档:https://developer.mozilla/zh-CN/

  • 查阅该方法的功能
  • 查看里面的参数的意义和类型
  • 查看返回值的意义和类型
  • 通过demo进行测试

10.4.1 Math对象

不是一个构造函数,所以不需要new来调用,而是直接使用里面的属性和方法

// 利用对象封装自己的数学对象,里面有PI、最大值和最小值
var myMath = {
       PI: 3.141592653,
       max: function() {
              var max = arguments[0];
              for (var i = 1; i < arguments.length; i++) {
                       if (arguments[i] > max) {
                                max = arguments[i] ;
                        }
              }
              return max;
       },
       min: function() {
              var min= arguments[0];
              for (var i = 1; i < arguments.length; i++) {
                       if (arguments[i] < min) {
                                min = arguments[i] ;
                        }
              }
              return min;
       }
}
console.log(myMath.PI);
console.log(myMath.max(1,4,7));
console.log(myMath.min(1,4,7));
-------------------------------------------------------------------
Math.abs() // 绝对值
Math.abs('-1') // 隐式转换 会把字符串型-1转换为数字型
Math.abs('pink老师') // NaN
Math.PI   //圆周率
Math.floor() // 向下取整,往小了取值
Math.floor(1.9) // 1
Math.ceil() // 向上取整,往大了取值
Math.ceil(1.1) // 2
Math.round() // 四舍五入 就近取整, .5特殊,它往大了取值
Math.round(-1.5) // -1
Math.random() // 返回一个随机的小数[0,1),不跟参数
// 得到两个数之间的随机整数,并且包含这2个整数
function getRandom(min,max) {
  return Math.floor(Math.random() * (max - min +1)) + min;
}

10.4.2 日期Date对象

Date()是一个构造函数必须使用new来调用创建日期对象。
Date对象是基于1970年1月1日(世界标准时间)起的毫秒数

var date = new Date();   //返回系统当前时间
var date = new Date(2020,2,4);  //2020-03-04 monthIndex 是从“0”开始计算的
var date = new Date('2020-2-4 21:31:31');

// 今天是2021年2月4日 星期四
var date = new Date();
var year = date.getFullYear();
var month= date.getMonth() + 1;
var dates= date.getDate();
var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
var day= date.getDay();
console.log('今天是: '+ year + '年' + month + '月' + dates + '日 ' + arr[day]);

日期格式化

// 封装一个函数放回当前的时分秒 格式 08:08:08
function getTimer() {
     var time = new Date();
     var h = time.getHours();
     h = h < 10 ? '0' + h : h;
     var m = time.getMinutes();
     m = m < 10 ? '0' + m : m;
     var s = time.getSeconds();
     s = s < 10 ? '0' + s : s;
     return h + ':' + m + ':' + s;
}
console.log(getTimer());
-------------------------------------------------------------------
// 获得Date总的毫秒数(时间戳),距离1970年1月1日
// 1.通过valueOf()、getTime
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
// 2.简单的写法(最常用的写法)
var date1 = +new Date();  // 返回的是当前时间总的毫秒数
console.log(date1);
// 3.H5新增的 获得总的毫秒数
console.log(Date.now());
-------------------------------------------------------------------

倒计时效果

  • 核心算法:输入的时间-现在的时间=剩余时间,即倒计时 但不能拿着时分秒相减,比如05分-25分,结果会是负数
  • 时间戳来做 用户输入时间总的毫秒数-现在时间的总的毫秒数=剩余时间的毫秒数
  • 把剩余时间总的毫秒数转换为天、时、分、秒(时间戳转换为时分秒)

转换公式如下:
d = parseInt(总秒数 / 60 / 60 / 24); // 计算天数
h = parseInt(总秒数 / 60 / 60 % 24); // 计算小时
m = parseInt(总秒数 / 60 % 60); // 计算分数
s = parseInt(总秒数 % 60); // 计算当前秒数

function countDown(time) {
    var nowTime = +new Date(); // 返回的时当前时间总的毫秒数
    var inputTime = +new Date(time); // 用户输入时间总的毫秒数
    var times = (inputTime - nowTime) / 1000; // 剩余时间的秒数
    var d = parseInt(times / 60 / 60 / 24); // 天
    d = d < 10 ? '0' + d : d;
    var h = parseInt(times / 60 / 60 % 24); // 时
    h = h < 10 ? '0' + h : h;
    var m = parseInt(times / 60 % 60); // 分
    m = m < 10 ? '0' + m : m;
    var s = parseInt(times % 60); // 当前秒数
    s = s < 10 ? '0' + s : s;
    return d + '天' + h + '时' + m + '分' + s + '秒';
} 
console.log(countDown('2021-2-5 21:12:12'));

10.4.3 数组对象

var arr = new Array();  // 创建了一个空的数组
var arr = new Array(2);  // 2表示数组的长度 里面有2个空的数组元素
var arr = new Array(2,3);  // 等价于[2,3] 里面有2个数组元素 2和3

1.检测是否为数组
(1)instanceof 运算符
(2)Array.isArray(参数) H5新增的方法,ie9以上版本

// 翻转数组
function reverse(arr) {
  if (arr instanceof Array) { // (Array.isArray(arr))
       var newArr = [];
       for (var i = arr.length - 1; i >= 0; i--){
           newArr[newArr.length] = arr[i];
       }
       return newArr;
  } else {
      return 'error 这个参数要求必须是数组格式[1,2,3]'
  }
}
console.log(reverse([1,2,3]));
console.log(reverse(1,2,3)); // error 这个参数要求必须是数组格式[1,2,3]

2.添加删除数组元素的方法

  • push() unshift() 参数直接写数组元素 用逗号隔开即可
  • push unshift 完毕之后,返回的结果是 新数组的长度 原数组也会发生变化
  • pop shift 没有参数 每次只能删除一个元素,返回的是删除的那个元素

3.数组排序

(1)翻转数组

var arr = ['pink' , 'red' , 'blue'];
arr.reverse();
console.log(arr);  // ['blue', 'red', 'pink']

(2)数组排序

var arr1 = [13, 4, 77, 1, 7];
arr1.sort(function() {
    return a - b;  // 升序的顺序排列
    return b - a;  // 降序的顺序排列
});

4.数组索引方法

  • indexOf(数组元素):返回该数组元素的索引号,只返回第一个满足条件的索引号
  • lastIndexOf():从后往前查

案例:数组去重

  • 目标:把旧数组里面不重复的元素选取出来放到新数组中,重复的元素只保留一个,放到新数组中去重
  • 核心算法:遍历旧数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组里面没有出现过,就添加,否则不添加
  • 该元素存在否?利用新数组.indexOf(数组元素) 若返回-1 说明新数组里面没有该元素
// 封装一个去重的函数
function unique(arr) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        if (newArr.indexOf(arr[i]) === -1) {
              newArr.push(arr[i]);
        }
    }
    return newArr;
}

5.数组转换为字符串

10.4.4 字符串对象

1.基本包装类型

为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String、Number、Boolean
基本包装类型:把简单数据类型包装成了复杂数据类型,这样基本数据类型就有了属性和方法。

// 下面代码有什么问题?
var str = 'andy';
console.log(str.length); // 4

按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为js会把基本数据类型包装为复杂数据类型,其执行过程如下:

// 1.生成临时变量,把简单数据类型包装为复杂数据类型
var temp = new String('andy');
// 2.赋值给我们声明的字符变量
str = temp;
// 3.销毁临时变量
temp = null;

2.字符串的不可变

指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。

3.根据字符串返回位置

字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串

案例:查找字符串"abcdoefoxyozzopp"中所有o出现的位置以及次数

  • 核心算法:先查找第一个o出现的位置
  • 然后只要indexOf返回的结果不是-1 就继续往后查找
  • 因为indexOf只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加1,从而继续查找
var str = "abcdoefoxyozzopp";
var index = str.indexOf('o');
var num = 0;
while (index !== -1) {
     console.log(index);
     num++;
     index = str.indexOf('o', index + 1);
}
console.log('o出现的次数是:' + num);

4.根据位置返回字符

案例:判断字符串"abcdoefoxyozzopp"中出现次数最多的字符,并统计其次数

  • 核心算法:利用charAt()遍历这个字符串
  • 把每个字符都存储给对象,如果对象没有该属性,就为1,如果存在就+1
  • 遍历对象,得到最大值和该字符
var str = "abcdoefoxyozzopp";
var o = {};
for (var i = 0; i <str.length; i++) {
       var chars = str.charAt(i);  // chars是字符串中的每一个字符
       if (o[chars]) {  //  o[chars] 得到的是属性值
             o[chars]++;
       } else {
             o[chars] = 1;
       }
 }
 console.log(o);
 // 遍历对象
 var max = 0;
 var ch ='';
 for (var k in o) { // k得到的是属性名 o[k]得到的是属性值
      if (o[k] > max) {
          max = o[k];
          ch = k;
      }
 }
 console.log(max);
 console.log('最多的字符是' + ch);

5.字符串操作方法

toUpperCase()把一个字符串全部变为大写
toLowerCase()把一个字符串全部变为小写

替换字符
replace(‘被替换的字符’,‘替换为的字符’) 只会替换第一个字符
字符转换为数组
split(‘分隔符’)

var str = 'red, blue, pink';
console.log(str.split(','));
var str = 'red&blue&pink';
console.log(str.split('&'));

10.5 简单数据类型和复杂数据类型

简单类型又叫基本数据类型或者值类型,复杂类型又叫做引用类型

  • 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型 string、number、boolean、undefined、null
  • 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型,通过new关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等

10.6 堆和栈

堆栈空间分配区别:
1.栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;简单数据类型存放到栈里面,里面直接开辟一个空间 存放值
2.堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型存放到堆里面:首先在栈里面存放地址,用十六进制表示,然后这个地址指向堆里面的数据

10.7 传参

简单类型传参
函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到外部变量

复杂类型传参
函数的形参也可以看做是一个变量,当我们引用类型变量作为参数传给函数的形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象

Web APIs

API(应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节
Web API浏览器提供的一套操作浏览器功能页面元素的API(BOM和DOM),主要针对浏览器做交互效果

1. DOM

关于DOM操作,我们主要针对于元素的操作:创建、增、删、改、查、属性操作、事件操作

1.1 DOM概述

文档对象模型(DOM)是W3C组织推荐的处理可扩展标记语言的标准编程接口。W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、样式和结构

1.2 DOM树

  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 元素:页面中的所有标签都是元素,DOM中使用element表示
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示
  • DOM把以上内容都看作是对象

1.3 获取元素

  • 根据ID获取
  • 根据标签名获取
  • 通过HTML5新增的方法获取
  • 特殊元素获取

(1)根据ID获取
使用getElementById()方法可以获取带有ID的元素对象
参数 id是大小写敏感的字符串,返回元素对象
console.dir()打印返回的元素对象,可更好的查看里面的属性和方法

(2)根据标签名获取
使用getElementsByTagName()方法可以返回带有指定标签名的对象的集合,以伪数组形式存储,所以我们想要得到里面的元素就需要遍历;得到的元素对象是动态的。如果页面中没有这个元素,返回的是空的伪数组的形式

var lis = document.getElementsByTagName('li');

还可以获取某个元素(父元素)内部所有指定标签名的子元素

element.getElementsByTagName('标签名')

父元素必须是单个对象必须指明是哪一个元素对象),获取的时候不包括父元素自己

(3)通过HTML5新增的方法获取

1.document.getElementsByClassName('类名'); // 根据类名返回元素对象集合
2.document.querySelector('选择器'); // 根据指定选择器返回第一个元素对象
document.querySelector('li');
document.querySelector('.box');
document.querySelector('#nav');
3.document.querySelectorAll('选择器'); // 根据指定选择器返回所有元素对象集合

(4)特殊元素获取(body、html)
获取body元素

document.body;

获取html元素

document.documentElement

1.4 事件基础

触发响应的机制,网页中的每个元素都可以产生某些可以触发JavaScript的事件。
组成:事件源、事件类型、事件处理程序
事件源:事件被触发的对象
事件类型:如何触发 比如鼠标点击onclick、鼠标经过、键盘按下
事件处理程序:通过一个函数赋值的方式完成

<button id='btn'>唐伯虎</button>
<script>
  var btn = document.getElementById('btn');
  btn.onclick = function(){
        alert('点秋香');
  }
</script>

执行事件的步骤:
(1)获取事件源
(2)注册事件(绑定事件)
(3)添加事件处理程序(采取函数赋值形式)
常见的鼠标事件

1.5 操作元素

JavaScript中的DOM操作可以改变网页内容、结构和样式,我们可以利用DOM操作元素来改变元素里面的内容、属性等。

1.改变元素内容

(1)element.innerText
从起始位置到终止位置的内容,但它去除html标签(不识别html标签),同时去除空格和换行
(2)element.innerHTML (使用最多)
从起始位置到终止位置的全部内容,包括html标签(识别html标签),同时保留空格和换行

2.改变元素属性

如:src、href、title、alt等

<button id='ldh'>刘德华</button>
<button id='zxy'>张学友</button>
<img src="images/ldh.jpg" alt="" title="刘德华" >
<script>
  // 1.获取元素
  var ldh= document.getElementById('ldh');
  var zxy= document.getElementById('zxy');
  // 2.注册事件 处理程序
  zxy.onclick = function(){
        img.src = 'images/zxy.jpg';
        img.title = '张学友';
  }
  ldh.onclick = function(){
        img.src = 'images/ldh.jpg';
        img.title = '刘德华';
  }
</script>

3.表单元素的属性操作

利用DOM可以操作如下表单元素的属性:
type、value、checked、selected、disabled

<button>按钮</button>
<input type="text" value="输入内容">
<script>
  // 1.获取元素
  var btn= document.querySelector('button');
  var input= document.querySelector('input');
  // 2.绑定事件 处理程序
  btn.onclick = function(){
       // 表单里面的值 文字内容是通过value来修改的
       input.value = '被点击了';
       // 如果想要某个表单被禁用 不能再点击
       // btn.disabled = true;
       this.disabled = true; // this指的是事件函数的调用者btn
  }
 
</script>

伪京东显示密码案例

<div class="box">
     <label for="">
         <img src="images/close.png" alt="" id="eye">
     </label>
     <input type="password" name="" id="pwd">
</div>
<script>
     // 1.获取元素
     var eye = document.getElementById('eye');
     var pwd = document.getElementById('pwd');
     // 2.注册事件 程序处理
     var flag = 0;
     eye.onclick = function () {
         // 点击一次之后,flag一定要发生变化
         if (flag == 0) {
              pwd.type = 'text';
              eye.src = 'images/open.png';
              flag = 1; // 赋值操作
          } else {
              pwd.type = 'password';
              eye.src = 'images/close.png';
              flag = 0;
          }
        }
    </script>

4.样式属性操作

我们可以通过JS修改元素的大小、颜色、位置等样式
(1) element.style 行内样式操作(里面的属性采取驼峰命名法
(2) element.className 类名样式操作(适合样式较多或功能复杂 会直接更改元素的类名,因此会覆盖原先的类名
JS修改style样式操作,产生的是行内样式,css权重比较高

循环精灵图

<script>
   var lis = document.querySelectorAll('li');
   for (var i = 0; i < lis.length; i++) {
       var index = i * 44;
       lis[i].style.backgroundPosition = '0 -' + index + 'px';
   }
</script>

案例:显示隐藏文本框内容(仿京东)
当鼠标点击文本框时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示
①首先需要两个新事件,获得焦点onfocus,失去焦点onblur
②如果获得焦点,判断表单里面内容是否为默认文字,如果是默认文字,就清空表单内容
③如果失去焦点,判断表单内容是否为空,如果为空,则表单内容改为默认文字

<input  type="text" value="手机">
<script>
     // 1.获取元素
     var text = document.querySelector('input');
     // 2.注册事件 获得焦点事件 onfocus
      this.onfocus = function() {
           if(this.value === '手机'){
                this.value = '';
            }
            // 获得焦点需要把文本框里面的文字颜色变黑
            this.style.color = '#333';  
      }
      // 3.注册事件 失去焦点事件 onblur
      this.onblur= function() {
           if(this.value === ''){
                this.value = '手机';
            }
            // 失去焦点需要把文本框里面的文字颜色变浅
            this.style.color = '#999';  
      }
</script>

案例:密码框格式提示错误信息
用户如果离开密码框,里面输入的个数不是6-16位,则提示错误信息,否则提示输入正确信息
①首先判断的事件是表单失去焦点onblur
②如果输入正确则提示正确的信息颜色为绿色小图标变化
③如果输入不是6-16位,则提示错误信息颜色为红色小图标变化

<style>
  .wrong {
      color: red;
      background-image: url(images/wrong.png);
  }
  .right{
      color: green;
      background-image: url(images/right.png);
  }
</style>
<div class = "register">
      <input type="password" class="ipt">
      <p class="message">请输入6~16位密码</p>
<div>
<script>
    // 1.获取元素
    var ipt = document.querySelector('.ipt');
    var message= document.querySelector('.message');
    // 2.注册事件
    ipt.onblur = function() {
       // 根据表单里面值的长度
       if(this.value.length < 6 || this.value.length > 16) {
            message.className = 'message wrong';
            message.innerHTML = '您输入的位数不对要求6~16位';
       } else {
            message.className = 'message right';
            message.innerHTML = '您输入的正确';
       }
    }
</script>

5.排他思想

如果有同一组元素,想要某一个元素实现某种样式,需要用到循环的排他思想算法:
(1)所有元素全部清除样式
(2)给当前元素设置样式
(3)顺序不能颠倒

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
  var btns = document.getElementsByTagName('button');
  for (var i = 0; i < btns.length; i++){
      btns[i].onclick = function(){
         // (1)首先把所有按钮的背景颜色去掉
         for (var i = 0; i < btns.length; i++){
            this.style.backgroundColor = '';
         }
         // (2)然后设置当前元素的背景颜色
         this.style.backgroundColor = 'pink';
      }
  }
</script>

案例:百度换肤

<ul class="baidu">
    <li><img src="images/1.jpg"></li>
    <li><img src="images/2.jpg"></li>
    <li><img src="images/3.jpg"></li>
    <li><img src="images/4.jpg"></li>
</ul>
<script>
  var imgs = document.querySelector('.baidu').querySelectorAll('img');
  for (var i = 0; i < imgs.length; i++){
      imgs[i].onclick = function(){
        // this.src就是点击图片的路径
        document.body.style.backgroundImage = 'url('+ this.src +')';
      }
  }
</script>

案例:表格隔行变色
①鼠标事件 鼠标经过onmouseover 鼠标离开onmouseout
②核心思路:鼠标经过tr行,当前的行变背景颜色,鼠标离开去掉当前的背景颜色
③注意:第一行(thead里面的行)不需要变颜色,因此获取的是tbody里面的行

<style>
        table {
            width: 800px;
            margin: 100px auto;
            text-align: center;
            border-collapse: collapse;
            font-size: 14px;
        }
        thead tr {
            height: 30px;
            background-color: skyblue;
        }
        tbody tr {
            height: 30px;
        }
        tbody td {
            border-bottom: 1px solid #d7d7d7;
            font-size: 12px;
            color: blue;
        }
        .bg {
            background-color: pink;
        }
    </style>
<table>
        <thead>
            <tr>
                <th>代码</th>
                <th>名称</th>
                <th>最新公布净值</th>
                <th>累计净值</th>
                <th>前单位净值</th>
                <th>净值增长率</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>003526</td>
                <td>农银金橞3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
            <tr>
                <td>003526</td>
                <td>农银金橞3个月定期开放债券</td>
                <td>1.075</td>
                <td>1.079</td>
                <td>1.074</td>
                <td>+0.047%</td>
            </tr>
        </tbody>
    </table>
<script>
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        for (var i = 0; i < trs.length; i++) {
            // 鼠标经过
            trs[i].onmouseover = function () {
                this.className = 'bg';
            }
            // 鼠标离开
            trs[i].onmouseout = function () {
                this.className = '';
            }
        }
    </script>

案例:表单全选取消全选
①全选和取消全选:让下面所有复选框的checked属性跟随全选按钮
②下面复选框需要全部选中,上前全选才能选中:给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中,上面全选就不选中
③可设置一个变量,来控制全选是否选中

<script>
        // 1.全选和取消全选
        var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
        // 下面所有的复选框
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
        j_cbAll.onclick = function () {
            // this.checked 可以得到当前复选框的选中状态,如果是true就是选中
            for (var i = 0; i < j_tbs.length; i++) {
                j_tbs[i].checked = this.checked;
            }
        }
        // 2.下面复选框需要全部选中,上前全选才能选中
        for (var i = 0; i < j_tbs.length; i++) {
            j_tbs[i].onclick = function () {
                // flag控制全选按钮是否选中
                var flag = true;
                // 每次点击下面的复选框都要循环检查剩下的小按钮是否全部选中
                for (var i = 0; i < j_tbs.length; i++) {
                    if (!j_tbs[i].checked) {
                        flag = false;
                        break; // 退出for循环,可以提高执行效率,因为只要有一个没被选中,剩下的就不需循环判断
                    }
                }
                j_cbAll.checked = flag;
            }
        }
    </script>

6.自定义属性的操作
(1)获取属性值

element.属性  (获取内置属性值,即元素本身自带的属性)

element.getAttribute('属性')  (主要获取自定义的属性)标准

(2)设置属性值

element.属性 = '值'  设置内置属性值

element.setAttribute('属性','值');  主要针对自定义属性 
element.setAttribute('class','navs');

(3)移除属性

element.removeAttribute('属性');

案例:Tab栏切换
①Tab栏切换有2个大的模块
②上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想)修改类名
③下面的模块内容,会跟随上面的选项卡变化,所以下面模块变化要写到点击事件里面
④规律:下面的模块显示内容和上面的选项卡一一对应,相匹配
⑤核心思路:给上面的tab_list里面的所有li添加自定义属性,属性值从0开始编号
⑥当点击tab_list里面的某个li,让tab_con里面对应序号的内容显示,其余隐藏(排他思想)

<script>
// 获取元素
var tab_list = document.querySelector('.tab_list');
var lis = tab_list.querySelectorAll('li');
var items = document.querySelectorAll('.item'); //获取的内容模块
// for循环绑定事件
for (var i = 0; i < lis.length; i++) {
   // 开始给5个li设置索引号
   lis[i].setAttribute('index',i);
   lis[i].onclick = function() {
   // 1.上模块选项卡:点击某一个底色会是红色,其余不变(排他思想)
    for (var i = 0; i < lis.length; i++) {
       lis[i].className = '';
    }
    this.className = 'current';
    // 2.下面的显示内容模块
    var index = this.getAttribute('index');
    for (var i = 0; i < items.length; i++) {
       items[i].style.display = 'none';
    }
    items[index].style.display = 'block';
   }
}
</script>

7.H5自定义属性

自定义属性的目的:为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性,H5新增了自定义属性:
(1)设置H5自定义属性
H5规定自定义属性以data-开头作为属性名并且赋值
(2)获取H5自定义属性
①兼容性获取 element.getAttribute('data-index');
②H5新增element.dataset.index或者element.dataset['index'] ie11才开始支持(dataset是一个集合,里面存放了所有以data开头的自定义属性);如果自定义属性里有多个-连接的单词,获取时采用驼峰命名法

2. 节点操作

2.1节点概述

(1)利用DOM提供的方法获取元素

  • document.getElementById()
  • document.getElementByTagName()
  • document.querySelector()
  • document.querySelectorAll()等
  • 逻辑性不强、繁琐

(2)利用节点层级关系获取元素

  • 利用父子兄节点关系获取元素
  • 逻辑性强,但是兼容性稍差

网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。HTML DOM树中的所有节点均可以通过JavaScript进行访问,所有HTML元素(节点)均可以被修改,也可以创建或删除。一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性

  • 元素节点 nodeType为 1
  • 属性节点 nodeType为 2
  • 文本节点 nodeType为 3(文本节点包括文字、空格、换行等)

2.2节点层级

利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系
(1)父级节点
node.parentNode 离元素最近的父级节点,若找不到则为null
(2)子节点
parentNode.childNodes (标准)
返回包含指定节点的子节点的集合(包含文本节点、元素节点等等),该集合为即使更新的集合
如果只想要获得里面的元素节点,则需要专门处理,所以一般不提倡使用childNodes

var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
   if (ul.childNodes[i].nodeType === 1) {
          console.log(ul.childNodes[i]);
   }
}

parentNode.children(非标准)
是一个只读属性,返回所有的子元素节点(重点掌握)
parentNode.firstChild 返回第一个子节点,不管是文本节点还是元素节点
parentNode.lastChild 返回最后一个子节点
parentNode.firstElementChild 返回第一个子元素节点,找不到返回null(有兼容性问题,IE9以上才支持)
parentNode.lastElementChild 返回最后一个子元素节点,找不到返回null(有兼容性问题,IE9以上才支持)
解决方案:
parentNode.children[0] 返回第一个子元素节点
parentNode.children[parentNode.children.length-1] 返回最后一个子元素节点

案例:新浪下拉菜单

<ul class="nav">
 <li>
   <a href="#">微博</a>
 <ul>
   <li><a href="#">私信</a></li>
   <li><a href="#">评论</a></li>
   <li><a href="#">@我</a></li>
 </ul>
 </li>
 <li>
   <a href="#">博客</a>
 <ul>
   <li><a href="#">博客评论</a></li>
   <li><a href="#">未读提醒</a></li>
   <li><a href="#">@我</a></li>
 </ul>
 </li>
<li></li>
<li></li>
</ul>
<script>
// 1.获取元素
var nav = document.querySelector(".nav");
var lis = nav.children; // 获取4个li
// 2.循环注册事件
for (var i = 0;i < lis.length; i++) {
  lis[i].onmouseover = function() {
     this.children[1].style.display = "block";
  }
  lis[i].onmouseout = function() {
     this.children[1].style.display = "none";
  }
}
</script>

(3)兄弟节点
node.nextSibling 返回当前元素的下一个兄弟节点,找不到返回null,包含所有的节点(文本、元素…)
node.previousSibling 返回当前元素的上一个兄弟节点,找不到返回null,包含所有的节点(文本、元素…)

有兼容性问题,IE9以上才支持
node.nextElementSibling 返回当前元素下一个兄弟元素节点,找不到返回null
node.previousElementSibling 返回当前元素上一个兄弟元素节点,找不到返回null
解决方案:封装一个兼容性函数

function getNextElementSibling(element) {
   var el = element;
   while (el = el.nextSibling) {
      if (el.nodeType == 1) {
           return el;
      }
   }
   return null;
}

2.3 节点操作

1.创建节点

document.creatElement('tagName')

创建由tagName指定的HTML元素,因为这些元素原先不存在,是根据我们的需求动态生成的,所以也称为动态创建元素节点

2.添加节点

node.appendChild(child)将一个节点添加到指定父节点的子节点列表末尾,类似css里面的after伪元素(node是父级 child是子级)
node.insertBefore(child,指定元素) 将一个节点添加到父节点的指定子节点前面,类似于css里面的before伪元素

案例:简单版留言板、留言删除
①核心思路:点击按钮之后,动态创建一个li,添加到ul里面
②创建li 的同时,把文本域里面的值通过li.innerHTML赋值给li
③如果想要新的留言后面显示就用appendChild,前面显示就用insertBefore
删除留言
①当把文本域里面的值赋值给li时,多添加一个删除的链接
②需要获取所有的链接,当点击当前链接时,删除当前链接所在的li
③阻止链接跳转需要添加javascript:;或者javascriptvoid(0);

<script>
 // 1.获取元素
var btn = document.querySelector("button");
var text = document.querySelector("textarea");
var ul = document.querySelector("ul");
// 2.注册事件
btn.onclick = function () {
    if (text.value == '') {
       alert("您没有输入内容");
       return false;
     } else {
        // (1)创建元素
        var li = document.createElement("li");
        // 先有li 才能赋值
        li.innerHTML = text.value + "<a href = 'javascript:;'>删除</a>";
        // (2)添加元素
        //ul.appendChild(li);
        ul.insertBefore(li, ul.children[0]);
        text.value = "";
        // (3)删除元素 删除的是当前链接的li 它的父亲
        var as = document.querySelectorAll('a');
        for (var i = 0; i < as.length; i++){
           as[i].onclick = function() {
              ul.removeChild(this.parentNode);
           }
        }
        }
}
</script>

3.删除节点

node.removeChild(child)

从DOM中删除一个子节点,返回删除的节点(node是父节点)

<button>删除</button>
<ul>
   <li>熊大</li>
   <li>熊二</li>
   <li>光头强</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector("ul");
var btn = document.querySelector("button");
// 2.点击按钮 依次删除里面的孩子
btn.onclick = function() {
  if (ul.children.length == 0) {
      this.disabled = true; // 禁用
  } else {
    ul.removeChild(ul.children[0]);
  }
}
</script>

4.复制节点(克隆节点)

node.cloneNode()

返回调用该方法的节点的一个副本
(1)如果括号参数为空或者为false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点
(2)如果括号参数为true,则是深拷贝,会克隆复制节点本身以及里面所有的子节点

案例:动态生成表格
①因为数据都是动态的,需要js动态生成,采取对象形式存储
②所有的数据都是放到tbody里面的行里面
③因为行很多,需要循环创建多个行,由数组的长度决定创建几行
④每行里又有很多单元格(对应里面的数据),使用循环创建多个单元格,单元格的数量取决于每个对象里面的属性个数,并且把数据存入里面(双层for循环)即对象的属性值
⑤最后一列单元格是删除,需要单独创建单元格

<script>
// 1.先准备数据
var datas = [{
name:'魏璎珞',
subject:'JavaScript',
score:100
},{},{},{}];
// 2.往tbody里面创建行
var tbody = document.querySelector("tbody");
for (var i = 0; i < datas.length; i++) { //外层for循环管行tr
   // 创建行
   var tr = document.createElement("tr");
   tbody.appendChild(tr);
   // 创建单元格,单元格的数量取决于每个对象datas[i]属性个数
   for (var k in datas[i]) { //内层for循环管列td
     var td = document.createElement("td");
     // 把对象里面的属性值给单元格td,datas[i]指对象
     tr.innerHTML = datas[i][k];
     tr.appendChild(td);
   }
   // 3.创建带有删除两个字的单元格
   var td = document.createElement("td");
   td.innerHTML = "<a href='javascript:;'>删除</a>";
   tr.appendChild(td);
}
// 4.删除操作
var as = document.querySelectorAll("a");
for (var i = 0; i < as.length; i++) {
  as[i].onclick = function() {
     tbody.removeChild(this.parentNode.parentNode);
  }
}
</script>

5.三种动态创建元素区别

  • document.write():直接将内容写入页面的内容流,但是文档流执行完毕,则会导致页面全部重绘
  • element.innerHTML:将内容写入某个DOM节点,不会导致页面全部重绘;创建多个元素效率更高(不要以拼接字符串的方式,采取数组形式拼接-先把元素放数组里,然后再把数组转换为字符串),结构稍微复杂
  • document.createElement():创建多个元素效率稍低一点点,但是结构更清晰

3.事件高级

3.1 注册事件(绑定事件)

1.概述

给元素添加事件,称为注册事件或绑定事件
注册事件有两种方式:传统方式和方法监听注册方式

传统注册方式

  • 利用on开头的事件,如onclick
  • 特点:注册事件的唯一性
  • 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数

方法监听注册方式

  • w3c标准 推荐方式
  • addEventListener() 它是一个方法
  • IE9之前的IE不支持此方法,可使用attachEvent()代替
  • 特点:同一个元素同一个事件可以注册多个监听器(事件处理程序)
  • 按注册顺序依次进行

2.addEventListener 事件监听方式

eventTarget.addEventListener(type,listener[,useCapture])

将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数

  • type:事件类型字符串,比如click、mouseover,不要带on
  • listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数,是一个布尔值,默认false
<script>
var btns = docuemnt.querySelector("button");
btns.addEventListener('click', function() {
   alert();
})
</script>

3.attachEvent 事件监听方式(ie9以前的版本支持)

eventTarget.attachEvent(eventNameWithOn, callback)

将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定的事件时,指定的回调函数就会被执行

  • eventNameWithOn:事件类型字符串,比如onclick、onmouseover,要带on
  • callback:事件处理函数,当目标触发事件时回调函数被调用

3.2 删除事件(解绑事件)

删除事件的方式

(1)传统注册事件

eventTarget.onclick = null;

(2)方法监听注册方式

eventTarget.removeEventListener(type,listener[,useCapture]);
eventTarget.detachEvent(eventNameWithOn,callback)
eventTarget.removeEventListener(type,listener[,useCapture]);
<script>
btns.addEventListener("click", fn);
function fn() {
    alert(22);
    btns.removeEventListener("click", fn);
}
</script>
eventTarget.detachEvent(eventNameWithOn,callback)
<script>
btns.attachEvent("onclick", fn1);
function fn1() {
    alert(32);
    btns.detachEvent("onclick", fn1);
}
</script>

3.3 DOM事件流

事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
分为3个阶段:
1.捕获阶段
2.当前目标阶段
3.冒泡阶段

  • 事件冒泡:IE最早提出,事件开始时由具体的元素接受,然后逐级向上传播到DOM最顶层节点的过程
  • 事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程

    注意
  • JS代码中只能执行捕获或者冒泡其中的一个阶段
  • onclick和attachEvent只能得到冒泡阶段
  • addEventListener(type, listener [,useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(默认),表示在事件冒泡阶段调用事件处理程序
  • 实际开发中很少使用事件捕获,更关注事件冒泡
  • 有些事件是没有冒泡的,如onblur、onfocus、onmouseenter、onmouseleave

3.4 事件对象

<script>
var div = document.querySelector("div");
 div.onclick = function(event) {
    console.log(event);
 }
 div.addEventListener("click", function(event) {
    console.log(event);
 })
</script>
  • event就是一个事件对象,写到侦听函数的小括号里,当作形参来看
  • 事件对象只有有了事件才会存在,它是系统自动创建的,无需传递参数(实参)
  • 事件对象是跟事件相关的一系列信息数据的集合,如鼠标点击,里面包含了鼠标的相关信息-鼠标坐标;若是键盘事件就包含了键盘事件的信息-判断用户按下了哪个键
  • 这个事件对象可以自己命名,比如event、evt、e
  • 事件对象也有兼容性问题,ie678 通过window.event,兼容性写法 :
event = event ||  window.event

事件对象的常见属性和方法

e.target返回的是触发事件的对象(元素)-点击了哪个元素,就返回哪个元素
this返回的是绑定事件的对象(元素)-哪个元素绑定了这个点击事件,就返回谁
兼容性:

div.onclick = function(e) {
   e = e || window.event
   var target = e.target || e.srcElement;
   console.log(target);
}

跟this相似的一个属性:currentTarget,ie678不认识

<a href="http://www.baidu">百度</a>
<form action="http://www.baidu">
   <input type="submit" value="提交" name="sub" />
</form>
<script>
// 阻止默认行为(事件)让链接不跳转或者让提交按钮不提交
var a = document.querySelector("a");
a.addEventListener("click",function(e){
   e.preventDefault();  //dom标准写法
})
 // 传统的注册方式
 a.onclick = function(e) {
    // 普通浏览器
    e.preventDefault();
    // 低版本浏览器 ie678
    e.returnValue;
    // 没有兼容性问题,但是return后面的代码不执行了,而且只限于传统的注册方式
    return false;
}
</script>

3.5 阻止事件冒泡

阻止事件冒泡

  • 标准写法:利用事件对象里面的stopPropagation()方法
  • 非标准写法:IE 678利用事件对象cancellBubble属性
<div class="father">
  <div class="son">son儿子</div>
</div>
<script>
 var son = document.querySelector(".son");
 son.addEventListener("click",function(e){
   alert("son");
   e.stopPropagation();
},false);
var father= document.querySelector(".father");
 father.addEventListener("click",function(e){
   alert("father");
},false);
 document.addEventListener("click",function(e){
   alert("document");
});
</script>

3.6 事件委托(代理、委派)

事件委托

事件委托也称事件代理,在jQuery里面称为事件委派

事件委托原理

不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点

事件委托的作用

只操作了一次DOM,提高了程序的性能

<ul>
   <li>知否知否,点我应有弹框在手!</li>
   <li>知否知否,点我应有弹框在手!</li>
   <li>知否知否,点我应有弹框在手!</li>
   <li>知否知否,点我应有弹框在手!</li>
   <li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
//事件委托的核心原理:给父节点添加侦听器,利用冒泡事件影响每一个子节点
var ul = document.querySelector("ul");
ul.addEventListener("click",function(e){
  alert("知否知否,点我应有弹框在手!")
  // e.target可以得到点击的对象
  e.target.style.backgroundColor="pink";
})
</script>

3.7 常用的鼠标事件


1.禁止鼠标右键菜单
contentmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

document.addEventListener("contentmenu",function(e){
   e.preventDefalut();
});

2.禁止鼠标选中(selectstart 开始选中)

document.addEventListener("selectstart",function(e){
   e.preventDefalut();
});

3.鼠标事件对象
event对象代表事件的状态,跟事件相关的一系列信息的集合,现阶段我们主要是用鼠标事件对象MouseEvent和键盘事件对象KeyboardEvent

案例:跟随鼠标的天使

  • 鼠标不断的移动,使用鼠标移动事件mousemove
  • 在页面中移动,给document注册事件
  • 图片要移动距离,而且不占位置,使用绝对定位
  • 核心原理:每次鼠标移动,都会获得最新的鼠标坐标,把这个x和y坐标作为图片的top和left值就可以移动图片
<style>
   img {position:absolute;}
</style>
<img src="images/angle.gif" alt="">
<script>
var pic = document.querySelector("img");
document.addEventListener("mousemove",function(e){
   var x = e.pageX;
   var y = e.pageY;
   // 不要忘记给left和top添加单位
   pic.style.left = x + "px";
   pic.style.top = y + "px";
})
</script>

3.7 常用的键盘事件


三个事件的执行顺序:
keydown – keypress – keyup

键盘事件对象

keyCode属性可以得到相应键的ASCII码值

  • keyup和keydown事件不区分字母大小写 a和A得到的都是65
  • keypress事件区分大小写 a-97 A-65
  • 可以利用keyCode属性返回的ASCII码值来判断用户按下了哪个键

案例:模拟京东案件输入内容
当我们按下s键,光标就定位到搜索框

  • 核心思路:检测用户是否按下了s键,如果按下s键,就把光标定位到搜索框
  • 使用键盘事件对象里面的keyCode判读用户按下的是否是s键
  • 搜索框获得焦点:使用js里面的focus()方法
<input type="text">
<script>
var search = document.querySelector("input");
document.addEventListener("keyup ",function(e){
   if(e.keyCode === 83) {
       search.focus();
   }
})
</script>

案例:模拟京东快递单号查询

要求:当我们在文本框中输入内容时,文本框上面自动显示大字号的内容

  • 快递单号输入内容时,上面的大号盒子字体(con)显示(这里面的字号更大)
  • 表单检测用户输入:给表单添加键盘事件
  • 同时把快递单号里面的值value获取过来赋值给con盒子作为内容
  • 如果快递单号里面内容为空,则隐藏大号字体盒子
  • 注意:keydown和keypress在文本框里面的特点:它们两个事件触发的时候,文字还没有落入文本框中
  • keyup事件触发的时候,文字已落入文本框里面了
  • 当失去焦点,就隐藏con盒子
  • 当获得焦点,并且文本框内容不为空,就显示con盒子
<div class="search">
   <div class="con">123</div>
   <input type="text" placeholder="请输入您的快递单号" class="jd">
</div>
<script>
var con = document.querySelector(".con");
var jd_input= document.querySelector(".jd");
jd_input.addEventListener("keyup ",function(e){
   if(this.value == "") {
      con.style.display = "none";
   } else {
      con.style.display = "block";
      con.innerText = this.value;
   }  
})
// 当失去焦点,就隐藏con盒子
jd_input.addEventListener("blur",function(e){
      con.style.display = "none";
})
// 当获得焦点,并且文本框内容不为空,就显示con盒子
jd_input.addEventListener("focus",function(e){
   if(this.value !== "") {
       con.style.display = "block";
   } 
})
</script>

4. BOM

4.1 BOM概述

4.1.1 BOM概述

BOM即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window
BOM由一系列相关的对象构成,并且每个对象都提供了很多方法和属性。
BOM缺乏标准,JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C,BOM最初是Netscape浏览器标准的一部分。

DOM

  • 文档对象模型
  • DOM就是把文档当作一个对象来看待
  • DOM的顶级对象是document
  • DOM主要学习的是操作页面元素
  • DOM的W3C标准规范

BOM

  • 浏览器对象模型
  • BOM就是把浏览器当作一个对象来看待
  • BOM的顶级对象是window
  • BOM主要学习的是浏览器窗口交互的一些对象
  • BOM是浏览器厂商在各自浏览器上定义的,兼容性较差
4.1.2 BOM的构成


window对象是浏览器的顶级对象,它具有双重角色

  • 它是JS访问浏览器窗口的一个接口
  • 它是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法。在调用时可以省略window,前面学习的对话框都属于window对象方法,如alert() 、prompt()等
  • 注意:window下的一个特殊属性window.name

4.2 window对象的常见事件

4.2.1 窗口加载事件
window.onload = function(){}
window.addEventListener("load",function(){});

window.onload是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等),就调用的处理函数

注意

  • 有了window.onload就可以把JS代码写到任意位置,因为onload是等页面内容全部加载完毕,再去执行处理函数
  • window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准
  • 如果使用addEventListener则没有限制
document.addEventListener("DOMContentLoaded",function(){})

DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表、图片、flash等,IE9以上才支持。
如果页面的图片很多,从用户访问到onload触发可能需要较长的时间,交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded事件比较合适

4.2.2 调整窗口大小事件
window.onresize = function(){}
window.addEventListener("resize",function(){});

window.onresize是调整窗口大小加载事件,当触发时就调用的处理函数

注意

  • 只要窗口大小发生像素变化,就会触发这个事件
  • 经常利用这个事件完成响应式布局,window.innerWidth获得当前屏幕的宽度

4.3 定时器

4.3.1 两种定时器
  • setTimeout()
  • setInterval()
4.3.2 setTimeout() 定时器
window.setTimeout(调用函数,[延迟的毫秒数]);

setTimeout()方法用于设置一个定时器,该定时器在定时器到期后执行调用函数;这个调用函数也称为回调函数callback(因为这个函数需要等待时间,时间到了才去调用这个函数)

注意

  • window可以省略
  • 这个调用函数可以直接写函数,还可以写函数名,还有一个写法:“函数名()”(不提倡)
  • 延迟的毫秒数省略默认是0,如果写,必须是毫秒
  • 页面中可能有多个定时器,给定时器赋值一个标识符(名字)

停止setTimeout() 定时器

window.clearTimeout(timeoutID)

clearTimeout()方法取消了先前通过调用setTimeout() 建立的定时器

  • window可以省略
  • 里面的参数就是定时器的标识符
4.3.3 setInterval() 定时器
window.setInterval(调用函数,[延迟的毫秒数]);

setInterval()方法重复调用一个函数,每隔这个时间就去调用一次回调函数

案例:定时器

  • 这个倒计时是不断变化的,因此需要定时器来自动变化
  • 三个盒子里面分别存放时分秒
  • 三个盒子利用innerHTML放入计算的小时分钟秒数
  • 第一次执行也是间隔毫秒数,因此刚刷新页面会有空白
  • 最好采用封装函数的方式,这样可以先调用一次这个函数,防止刚开始刷新页面会有空白问题
<div>
   <span class="hour">1</span>
   <span class="minute">2</span>
   <span class="second">3</span>
</div>
<script>
// 1.获取元素
var hour = document.querySelector(".hour"); // 小时的盒子
var minute = document.querySelector(".minute"); // 分钟的盒子
var second = document.querySelector(".second"); // 秒数的盒子
 var inputTime = +new Date("2021-3-21 11:00:00"); // 返回用户输入时间的总秒数
countDown(); // 先调用一次这个函数,防止刚开始刷新页面会有空白问题
// 2.开启定时器
setInterval(countDown, 1000);
function countDown() {
   var nowTime = +new Date(); // 返回当前时间的总秒数
   var times = (inputTime - nowTime) / 1000; //剩余时间总秒数
   var h = parseInt(times / 60 /60 % 24); // 时
   h = h < 10 ? "0" + h : h;
   hour.innerHTML = h; // 把剩余的小时给小时的盒子
   var m = parseInt(times / 60 % 60); // 分
   m = m < 10 ? "0" + m : m;
   minute.innerHTML = m; // 把剩余的分钟给分钟的盒子
   var s = parseInt(times % 60); // 秒
   s = s < 10 ? "0" + s : s;
   second.innerHTML = s; // 把剩余的秒数给秒数的盒子
}
</script>

停止setInterval() 定时器

window.clearInterval(intervalID)

clearInterval()方法取消了先前通过调用setInterval() 建立的定时器

  • window可以省略
  • 里面的参数就是定时器的标识符
  • 定义timer为全局变量,赋值null空对象

案例:发送短信
点击按钮后,该按钮60秒内不能再次点击,防止重复发送短信

  • 点击按钮之后,禁用disabled为true
  • 按钮里面的内容会变化,button里面的内容通过innerHTML修改
  • 里面秒数是有变化的,因此需要用到定时器
  • 定义一个变量,在定时器里面,不断递减
  • 如果变量为0说明到时间了,需要停止定时器,并且恢复原按钮初始状态
手机号码:<input type="text" name="" id="">
<button>发送</button>
<script>
var btn = document.querySelector("button");
var time = 3;
btn.addEventListener("click", function () {
     btn.disabled = true;
     var timer = setInterval(function () {
         if (time == 0) {
             // 清除定时器,恢复初始状态
             clearInterval(timer);
             btn.disabled = false;
             btn.innerHTML = "发送";
             time = 3; // 这个3需要重新开始
         } else {
              btn.innerHTML = "还剩" + time + "秒";
              time--;
              }
     }, 1000);
})
</script>
4.3.4 this

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下,this最终指向的是那个调用它的对象

  • 全局作用域或者普通函数中,this指向全局对象window
  • 方法调用中谁调用,this就指向谁
  • 构造函数中,this指向构造函数的实例

4.4 JS执行队列

4.4.1 JS是单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为JavaScript这门脚本语言诞生的使命所致:JavaScript是为处理页面中用户的交互,以及DOM操作而诞生的。比如对某个DOM页面进行添加和删除操作,不能同时进行,应该先进行添加,之后再删除。
单线程就意味着,所有任务需要排队,前一个任务结束后,才会执行后一个任务。这样所导致的问题是:如果JS执行的时间过长,就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

4.4.2 同步和异步

为了解决这个问题,利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是,JS中出现了同步和异步。

同步

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的

异步

同时做多个任务

本质区别:这条流水线上各个流程的执行顺序不同

同步任务

都在主线程上执行,形成一个执行线

异步任务

JS的异步是通过回调函数实现的。一般而言,异步任务有以下三种类型:

  • 普通事件,如click、resize等
  • 资源加载,如load、error等
  • 定时器,包括setIterval、setTimeout等

异步任务相关回调函数添加到任务队列(消息队列)中

4.4.3 JS执行队列
  • 先执行执行栈中的同步任务
  • 异步任务(回调函数)放入任务队列中
  • 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行

    由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)

4.5 location对象

4.5.1 什么是location对象

window对象给我们提供了一个location属性用于获取或设置窗体的URL,并且可以用于解析URL。因为这个属性返回的是一个对象,所以我们将这个属性也称为location对象。

4.5.2 URL

统一资源定位符(Uniform Resource Locator,URL)是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL的一般语法格式为:

protocol://http[:port]/path/[?query]#fragment
http://www.itcast/index.html?name=andy&age=18#link

4.5.3 location对象的属性


案例:5秒之后跳转页面

<button>点击</button>
<div></div>
<script>
 var btn = document.querySelector("button");
 var div = document.querySelector("div");
 var time = 5;
 setInterval(function(){
      if(time == 0) {
         location.href = "http://www.itcast";
      } else {
         div.innerHTML = "您将在" + time + "秒钟之后跳转到首页";
         time--;
      } 
 },1000);
</script>

案例:获取URL参数数据

  • 第一个登录页面,里面有提交表单,action提交到index.html页面
  • 第二个页面,可以使用第一个页面的参数,这样实现了一个数据不同页面之间的传递效果
  • 第二个页面之所以可以使用第一个页面的数据,是利用了URL里面的location.search参数
  • 在第二个页面中,需要把这个参数提取
  • 第一步去掉?,利用substr
  • 第二步,利用=号分割键和值 split(‘=’)
login.html
<form action="index.html">
   用户名:<input type="text" name="uname">
   <input type="submit" value="登录">
</form>

index.html
<div></div>
<script>
 location.search --- ?uname=andy
 // 1.先去掉? substr('起始的位置',截取几个字符)
 var params = location.search.substr(1); // uname=andy
 // 2.利用=号把字符串分割为数组 split('=')
 var arr = params.split('='); // ["uname","andy"]
 // 3.把数据写入div中
 var div = document.querySelector("div");
 div.innerHTML = arr[1] + "欢迎光临";
</script>
4.5.4 location对象的方法

4.6 navigator对象

navigator对象包含有关浏览器的信息,它有很多属性,最常用的是userAgent,该属性可以返回由客户机发送服务器的user-agent头部的值
下面代码可以判断用户再哪个终端打开页面,实现跳转

4.7 history对象

history对象与浏览器历史记录进行交互,该对象包含用户(在浏览器窗口中)访问过的URL。

history对象一般在实际开发中比较少用,但是会在一些OA办公系统中见到

PC端网页特效

1.元素偏移量offset系列

1.1 offset概述

offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态得到该元素的位置(偏移)、大小等

  • 获得元素距离带有定位父元素的位置
  • 获得元素自身的大小(宽高)
  • 返回的数值都不带单位

offset系列常用属性:

1.2 offset与style区别

offset

  • offset可以得到任意样式表中的样式值
  • offset系列获得的数值没有单位
  • offsetWidth包含padding+border+width
  • offsetWidth等属性是只读属性,只能获取不能赋值
  • 要想获取元素大小位置,用offset更合适

style

  • style只能得到行内样式表中的样式值
  • style.width获得的是带有单位的字符串
  • style.width获得不包含padding和border的值
  • style.width是可读属性,可以获取也能赋值
  • 要想给元素更改值,需要使用style改变

案例:获取鼠标在盒子内的坐标

  • 在盒子内点击,想要得到鼠标距离盒子左右的距离
  • 首先得到鼠标在页面中的坐标(e.pageX, e.pageY)
  • 其次login.style.left = e.pageX - x + “px”;得到盒子在页面中的距离(box.offsetLeft, box.offsetTop)
  • 用鼠标距离页面的坐标-盒子在页面中的距离,得到鼠标在盒子内的坐标
  • 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动事件mousemove
<div class="box"></div>
<script>
  var box= document.querySelector(".box");
  box.addEventListener("mousemove",function(e){
     var x = e.pageX - this.offsetWidth;
     var y = e.pageY - this.offsetTop;
     this.innerHTML = "x坐标是" + x + "y坐标是" + y;
  })
</script>

案例:模态框拖拽
弹出框,也称为模态框

  • 点击弹出层,会弹出模态框,并且显示灰色半透明的遮挡层
  • 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层
  • 鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动
  • 拖拽原理及触发事件:鼠标按下(mousedown)并且移动(mousemove),之后松开鼠标(mouseup)
  • 拖拽过程:鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框就可以跟着鼠标走了
  • 鼠标的坐标减去鼠标在盒子内的坐标=模态框真正的位置
  • 鼠标松开,可以停止拖动模态框移动
<script>
// 1.获取元素
 var login = document.querySelector(".login");
 var mask= document.querySelector(".login-bg");
 var link = document.querySelector("#link");
 var closeBtn = document.querySelector("#closeBtn");
 var title= document.querySelector("#title");
 
// 2.点击弹出层这个链接link 让mask和login显示出来
 link.addEventListener("click",function(){
    mask.style.display = "block";
    login.style.display = "block";
 })
// 3.点击closeBtn 就隐藏mask和login
 closeBtn.addEventListener("click",function(){
    mask.style.display = "none";
    login.style.display = "none";
 })
// 4.开始拖拽
// (1)当鼠标按下,获得鼠标在盒子内的坐标
 title.addEventListener("mousedown",function(e){
    var x = e.pageX - login.offsetLeft;
    var y = e.pageY - login.offsetTop;
   // (2)鼠标移动时,把鼠标在页面中的坐标-鼠标在盒子内的坐标=模态框的left和top值
   document.addEventListener("mousemove", move)
   function move(e){
      login.style.left = e.pageX - x + "px";
      login.style.top= e.pageY - y + "px";
   }
   // (3)鼠标弹起,让鼠标移动事件移除
   document.addEventListener("mouseup", function(){
      document.removeEventListener("mousemove",move);
   })
 })
</script>

案例:京东放大镜效果

整个案例可以分为三个功能模块:

  • 鼠标经过小图片盒子,黄色的遮挡层和大图片显示,离开隐藏2个盒子功能
  • 黄色的遮挡层跟随鼠标功能
  • 移动黄色遮挡层,大图片跟随移动功能

黄色的遮挡层跟随鼠标功能

  • 把鼠标坐标给遮挡层不合适,因为遮挡层坐标以父盒子为准
  • 首先是获得鼠标在盒子内的坐标
  • 之后把数值给遮挡层作为left和top值
  • 此时用到鼠标移动事件,但是还是在小图片盒子内移动
  • 遮挡层不能超出小图片盒子范围
  • 如果小于0,就把坐标置为0
  • 如果大于遮挡层最大移动距离,就把坐标设置为最大移动距离
  • 遮挡层最大移动距离:小图片盒子宽度-遮挡层盒子宽度

移动黄色遮挡层,大图片跟随移动功能

  • 大图片移动距离=(遮挡层移动距离*大图片最大移动距离)/遮挡层最大移动距离
<script src="js/detail.js"></script>

外部的js文件

window.addEventListener("load",function(){
  var preview_img = document.querySelector(".preview_img");
  var mask = document.querySelector(".mask");
  var big = document.querySelector(".big");
  // 1.鼠标经过preview_img,就显示和隐藏 mask遮挡层和big大盒子
  preview_img.addEventListener("mouseover",function(){
     mask.style.display = "block";
     big.style.display = "block";
  })
  preview_img.addEventListener("mouseout",function(){
     mask.style.display = "none";
     big.style.display = "none";
  })
  // 2.鼠标移动时,黄色的盒子跟着鼠标走
  preview_img.addEventListener("mousemove",function(e){
     // (1).先计算鼠标在盒子内的坐标
     var x = e.pageX - this.offsetLeft;
     var y = e.pageY - this.offsetTop;
     // (2)减去盒子高度的一半就是mask的最终left和top值
     // (3)mask移动的距离
     var maskX = x - mask.offsetWidth / 2;
     var maskY = y - mask.offsetHeight / 2;
     // (4)如果x<0,就让它停在0的位置
     // 遮挡层移动距离
     var maskMax = preview_img.offsetWidth - mask.offsetWidth;
     if (maskX <= 0) {
        maskX = 0;
     } else if (maskX >= maskMax){
        maskX = maskMax;
     }
     if (maskY <= 0) {
        maskY = 0;
     } else if (maskY >= maskMax){
        maskY = maskMax;
     }
     mask.style.left = maskX + "px"; // 鼠标在盒子中央
     mask.style.top = maskY + "px";
     //3.大图片移动距离=(遮挡层移动距离*大图片最大移动距离)/遮挡层最大移动距离
     // 大图片
     var bigIMg = document.querySelector(".bigImg");
     // 大图片最大移动距离
     var bigMax = bigIMg.offsetWidth - big.offsetWidth;
     // 大图片移动距离
     var bigX = maskX * bigMax / maskMax;
     var bigY = maskY * bigMax / maskMax;
     bigIMg.style.left = -bigX + "px";
     bigIMg.style.top = -bigY + "px";
  })
})

2.元素可视区client系列

client翻译过来就是客户端,我们使用client系列的相关属性来获取元素可视区的相关信息。通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等

立即执行函数

不需要调用,立马能够自己执行的函数
写法:
①(function() {})() // 第二个()可以看作是调用函数,也可以传递参数
②(function() {}());
多个立即执行函数,要用分号;隔开
最大作用:独立创建了一个作用域,里面所有的变量都是局部变量,不会有命名冲突情况

dpr:物理像素比

下面三种情况都会刷新页面,都会触发load事件
① a标签的超链接
② F5或者刷新按钮(强制刷新)
③ 前进后退按钮
但是火狐中,有个“往返缓存”,这个缓存不仅保留着页面数据,还保存了DOM和JavaScript的状态,实际上是将整个页面都保存在了内存里。所以此时后退按钮不能刷新页面。
可以使用pageshow事件来触发,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面出发的pageshow事件,这个事件给window添加。

3.元素滚动scroll系列

scroll翻译过来就是滚动的,使用scroll系列的相关属性可以动态的得到该元素的大小、滚动距离等

如果浏览器的高(宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,就是scrollTop称为页面被卷去的头部,滚动条在滚动时会触发onscroll事件。

案例:仿淘宝固定右侧侧边栏

  • 原先侧边栏是绝对定位
  • 当页面滚动到一定位置,侧边栏改为固定定位
  • 页面继续滚动,会让 返回顶部 显示出来
  • 需要用到页面滚动事件scroll,因为是页面滚动,所以事件源是document
  • 滚动到某个位置,就是判断页面被卷去的上部值
  • 页面被卷去的头部:window.pageYOffset获得,如果被卷去的左侧window.pageXOffset
  • 元素被卷去的头部是element.scrollTop
<div class="slider-bar">
   <span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体部分</div>
<script>
  // 1.获取元素
  var sliderbar = document.querySelector(".slider-bar");
  var banner= document.querySelector(".banner");
  // 被卷去的头部大小
  var bannerTop = banner.offsetTop;
  // 当侧边栏固定定位之后应该变化的数值
  var sliderbarTop = sliderbar.offsetTop - bannerTop;
  // 获取main 主体元素
  var main= document.querySelector(".main");
  var goBack= document.querySelector(".goBack");
  var mainTop = main.offsetTop;
  // 2.页面滚动事件
  document.addEventListener("scroll",function(){
    // 3.当页面被卷去的头部≥bannerTop,此时侧边栏改为固定定位
    if (window.pageYOffset >= bannerTop) {
         sliderbar.style.position = "fixed"; 
         sliderbar.style.top = sliderbarTop + "px"; 
    } else {
         sliderbar.style.position = "absolute"; 
         sliderbar.style.top = "300px"; 
    }
    // 4.当页面滚动到main盒子,就显示goBack模块
    if (window.pageYOffset >= mainTop) {
         goBack.style.display= "block"; 
    } else {
         goBack.style.display= "none";
    }
  })
</script>
  • offset系列经常用于获得元素位置 offsetLeftoffsetTop
  • client经常用于获取元素大小 clientWidthclientHeight
  • scroll系列经常用于获取滚动距离 scrollTop scrollLeft
  • 页面滚动距离 window.pageYOffsetwindow.pageXOffset

mouseenter和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter事件
  • 类似mouseover,它们两者之间的差别是:mouseover鼠标经过自身盒子会触发,经过子盒子还会触发,mouseenter只会经过自身盒子触发
  • mouseenter不会冒泡
  • 跟mouseenter搭配,鼠标离开mouseleave同样不会冒泡

4.动画函数封装

4.1 动画实现原理

核心原理:通过定时器setInterval()不断移动盒子位置
实现步骤

  • 获得盒子当前位置offsetLeft
  • 让盒子在当前位置加上1个移动距离
  • 利用定时器不断重复这个操作
  • 加一个结束定时器的条件
  • 此元素需要添加定位,才能使用element.style.left(赋值)
<script>
 var div = document.querySelector("div");
 var timer = setInterval(function(){
     if (div.offsetLeft >= 400) {
       // 停止动画 本质是停止定时器
       clearInterval(timer);
     } else {
        obj.style.left = obj.offsetLeft + 1 + "px";
     }  
 },30);
</script>

4.2 动画函数简单封装

函数需要传递两个参数:动画对象移动到的距离

<script>
// 简单动画函数封装 obj目标对象 target目标位置
function animate(obj, target){
 var timer = setInterval(function(){
     if (obj.offsetLeft >= target) {
       // 停止动画 本质是停止定时器
       clearInterval(timer);
     } else {
        obj.style.left = obj.offsetLeft + 1 + "px";
     }  
 },30);
}
 var div = document.querySelector("div");
 var span = document.querySelector("span");
 // 调用函数
 animate(div,400);
 animate(span,300);
</script>

4.3 动画函数给不同元素记录不同定时器

如果多个元素都使用同一个动画函数,每次都要var声明定时器。可以给不同元素使用不同的定时器(自己专门用自己的定时器)
核心原理:利用JS是一门动态语言,可以很方便的给当前对象添加属性

<script>
// var obj = {};
// obj.name = "andy";
// 简单动画函数封装 obj目标对象 target目标位置
// 给不同元素指定了不同的定时器
function animate(obj, target){
 // 先清除以前的定时器,只保留当前的一个定时器执行
 clearInterval(obj.timer);
 obj.timer = setInterval(function(){
     if (obj.offsetLeft >= target) {
       // 停止动画 本质是停止定时器
       clearInterval(obj.timer);
     } else {
        obj.style.left = obj.offsetLeft + 1 + "px";
     }  
 },30);
}
 var div = document.querySelector("div");
 var span = document.querySelector("span");
 // 调用函数
 animate(div,400);
 animate(span,300);
</script>

4.4 缓动效果原理

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
思路:
① 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来
② 核心算法:(目标值-现在的位置)/ 10 作为每次移动的距离、步长
③ 停止的条件:盒子当前位置=目标位置 停止定时器
④ 步长值取整数

<script>
// var obj = {};
// obj.name = "andy";
// 简单动画函数封装 obj目标对象 target目标位置
// 给不同元素指定了不同的定时器
function animate(obj, target){
 // 先清除以前的定时器,只保留当前的一个定时器执行
 clearInterval(obj.timer);
 obj.timer = setInterval(function(){
     // 步长值写到定时器里面
     // 把步长值改为整数,不要出现小数的问题
     var step = (target - obj.offsetLeft) / 10;
     // 前进往大取整,后退往小取整
     step = step > 0 ? Math.ceil(step) : Math.floor(step);
     if (obj.offsetLeft == target) {
       // 停止动画 本质是停止定时器
       clearInterval(obj.timer);
     } else {
       // 把1这个步长值改为一个慢慢变小的值
        obj.style.left = obj.offsetLeft + step + "px";
     }  
 },15);
}
 var div = document.querySelector("div");
 var span = document.querySelector("span");
 var btn= document.querySelector("button");
 btn.addEventListener("click",function(){
     // 调用函数
     animate(span,500);
 })
</script>

匀速动画:盒子当前位置 + 固定的值
缓动动画:盒子当前位置 + 变化的值(目标值-现在的位置)/ 10

4.5 缓动动画添加回调函数

回调函数原理:函数可以作为一个参数。将这个函数作为参数传递到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。

<script>
// var obj = {};
// obj.name = "andy";
// 简单动画函数封装 obj目标对象 target目标位置
// 给不同元素指定了不同的定时器
function animate(obj, target, callback){
 // 先清除以前的定时器,只保留当前的一个定时器执行
 clearInterval(obj.timer);
 obj.timer = setInterval(function(){
     // 步长值写到定时器里面
     // 把步长值改为整数,不要出现小数的问题
     var step = (target - obj.offsetLeft) / 10;
     // 前进往大取整,后退往小取整
     step = step > 0 ? Math.ceil(step) : Math.floor(step);
     if (obj.offsetLeft == target) {
       // 停止动画 本质是停止定时器
       clearInterval(obj.timer);
       // 回调函数写到定时器结束里面
       if(callback) {
            // 调用函数
           callback();
       }
     } else {
       // 把1这个步长值改为一个慢慢变小的值
        obj.style.left = obj.offsetLeft + step + "px";
     }  
 },15);
}
 var div = document.querySelector("div");
 var span = document.querySelector("span");
 var btn= document.querySelector("button");
 btn.addEventListener("click",function(){
     // 调用函数
     animate(span,500, function(){
        span.style.backgroundColor = "red";
     });
 })
</script>

4.6 动画函数封装到单独JS文件里面

<script src="animate.js"></script>
<div class="sliderbar">
    <span></span>
    <div class="con">问题反馈</div>
</div>
<script>
   var sliderbar = document.querySelector(".sliderbar");
   var con = document.querySelector(".con");
   // 当鼠标经过sliderbar就会让con盒子滑动到左侧
   sliderbar.addEventListener("mouseenter",function(){
      animate(con, -160, function(){
         sliderbar.children[0].innerHTML = "➡";
      });
   })
   // 当鼠标离开sliderbar就会让con盒子滑动到右侧
   sliderbar.addEventListener("mouseleave",function(){
      animate(con, 0, function(){
         sliderbar.children[0].innerHTML = "⬅";
      });
   })
</script>

5.常见网页特效案例

网页轮播图
功能需求:

  • 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮
  • 点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理
  • 图片播放的同时,下面小圆圈模块跟着一起变化
  • 点击小圆圈,可以播放相应图片
  • 鼠标不经过轮播图,轮播图会自动播放图片
  • 鼠标经过轮播图,轮播图自动播放图片停止

因为js较多,单独创建js文件夹,在创建js文件,引入到页面中
此时需要添加load事件
鼠标经过轮播图,左右按钮显示,离开隐藏左右按钮
显示隐藏display按钮

动态生成小圆圈

  • 核心思路:小圆圈的个数要跟图片张数一致
  • 首先获得ul里面图片的张数(图片放入li里面,所以就是li的个数)
  • 利用循环动态生成小圆圈(小圆圈要放入ol里面)
  • 创建节点createElement("li")
  • 将创建的节点插入到ol里:ol.appendChild(li)
  • 第一个小圆圈需要添加current类
  • 小圆圈的排他思想:点击当前小圆圈,就添加current类;其余的小圆圈就移除current类
<script>
  var ul = focus.querySelector("ul");
  var ol = focus.querySelector(".circle");
  for (var i = 0; i < ul.children.length; i++) {
    // 创建一个li
    var li = document.createElement("li");
    // 把li插入到ol
    ol.appendChild(li);
    // 小圆圈的排他思想,在生成小圆圈的同时直接绑定点击事件
    li.addEvendListener("click",function(){
       for(var i = 0; i < ol.children.length; i++) {
          // 干掉所有人,把所有的li清除current类名
          ol.children[i].className = "";
       }
       // 留下我自己,当前的li设置current类名
       this.className = "current";
    })
  }
  // 把ol里面的第一个li的类名current
  ol.children[0].className="current";
</script>

点击小圆圈滚动图片

  • 此时用到animate动画函数,将js引入(注意:index.js依赖animate.js,所以animate.js要写到index.js上面)
  • 使用动画函数的前提:该元素必须有定位
  • 注意是ul移动,而不是li
  • 滚动图片的核心算法:点击某个小圆圈,就让图片滚动,ul的移动距离=-小圆圈的索引号 * 图片的宽度
  • 此时需要知道小圆圈的索引号,可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定义属性即可
<script>
  var ul = focus.querySelector("ul");
  var ol = focus.querySelector(".circle");
  for (var i = 0; i < ul.children.length; i++) {
    // 创建一个li
    var li = document.createElement("li");
    // 记录当前小圆圈的索引号 通过自定义属性来做
    li.setAttribute("index",i);
    // 把li插入到ol
    ol.appendChild(li);
    // 小圆圈的排他思想,在生成小圆圈的同时直接绑定点击事件
    li.addEvendListener("click",function(){
       for(var i = 0; i < ol.children.length; i++) {
          // 干掉所有人,把所有的li清除current类名
          ol.children[i].className = "";
       }
       // 留下我自己,当前的li设置current类名
       this.className = "current";
       // 当点击了某个li 就拿到当前li的索引号
       var index = this.getAttribute("index");
       // 点击小圆圈,移动图片 ul的移动距离为:-小圆圈的索引号*图片宽度
       var focusWidth = focus.offsetWidth;
       animate(ul, -index * focusWidth);
    })
  }
</script>

点击右侧按钮一次,就让图片滚动一张

  • 声明一个变量num,点击一次,自增1,让这个变量*图片宽度,就是ul的滚动距离
  • 图片无缝滚动原理
  • 把ul的第一个li复制一份,放到ul的最后面
  • 当图片滚动到克隆的最后一张图片时,让ul快速的、不做动画的跳到最左侧:left为0
  • 同时num赋值为0,可以重新开始滚动动画了

克隆第一张图片

  • 克隆ul第一个li cloneNode(),若参数为true,表示深克隆,复制里面的子节点;false为浅克隆
  • 添加到ul最后面appendChild()
  • 写到生成小圆圈后面

点击右侧按钮,小圆圈跟随变化

  • 再声明一个变量circle,每次点击自增1,因为左侧按钮也需要这个变量,因此要声明全局变量
  • 但是图片有5张,小圆圈只有4个,必须加一个判断条件
  • 如果circle==ol.children.length,就重新复原为0

自动播放功能

  • 添加一个计时器
  • 自动播放轮播图,实际类似于点击了右侧按钮
  • 使用手动调用右侧按钮点击事件 arrow_r.click()
  • 鼠标经过focus就停止定时器
  • 鼠标离开focus就停止定时器

5.1 节流阀

阻止轮播图按钮连续点击造成播放过快。
节流阀的目的:当上一个函数动画内容执行完毕,再去执行下一个动画函数,让事件无法继续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数
开始设置一个变量,var flag = true;
if(flag) {flag = false; do something} 关闭水龙头
利用回调函数 动画执行完毕 flag = true 打开水龙头

本文标签: JavaScript