JavaScript基础

JavaScript

一、JS有3种书写方式,行内,内嵌和外部

1、行内样式
<!-- 行内式的js,直接写到元素的内部 -->
    <input type="button" value="唐伯虎" onclick="alert('秋香')"> 
2、内嵌样式
<!-- 内嵌式的JS -->
    <script>
        alert("你好!");
    </script>
3、外部样式
<script src="my.js"></script>
<!-- 注意:外部样式中script标签内部不可以写代码 -->

二、JS输入输出语句

1、prompt()
// 这是一个输入框
  prompt('请输入您的年龄:');
2、alert()
// 这是一个弹出警示框,输出用户可以看到
  alert('输入的结果是:');
3、console.log()
 // 控制台输出,程序员测试用的
  console.log('我是程序员能看到的');

三、变量

1、变量案例弹出姓名
<script>
        var myname = prompt('请输入您的姓名:');
        alert(myname);
</script>
2、变量语法拓展
2.1更新变量
<script>
        // 更新变量
        var name = console.log('迪丽热巴');
        name = '古力娜扎';
        console.log(name);
</script>
2.2声明多个变量
// 声明多个变量
        var name = '火影忍者',
            age = 18;
2.3声明变量特殊情况
// 只声明,不赋值
var age;
console.log(age);
// 结果:undifined
// 不声明,不赋值,直接使用
console.log(age);
// 结果:报错
// 不声明,只赋值
age = 10;
console.log(age);
// 结果:10

四、数据类型

1、isNaN()这个方法用来判断非数字,并且返回一个值,如果是数字返回false,如果不是数字返回true
<script>
        console.log(isNaN(12));//false
        console.log(isNaN('nihao'));//true
</script>
2、检测字符串长度
<script>
        var str = 'my name is zmh';
        console.log(str.length);//14
</script>
3、字符串拼接
alert('hello' + 12); //hello12
4、typeof检测变量数据类型
<script>
        var num = 11;
        console.log(typeof num); //number
        var name = '你好';
        console.log(typeof name); //string
        // prompt()输入的值都为字符串类型
        var age = prompt('请输入您的年龄:');//18
        console.log(age);//18
        console.log(typeof age);//string
</script>
5、数据类型转换
// toString()转换字符串
var num = 1;
alert(num.toString());
//String()强制转化字符串
var num = 1;
alert(String(num));
//加号拼接字符串
var num = 1;
alert(num + 'woshi');
// 转化成数字类型
console.log(parseInt(3.14));  //3
// 转化成浮点数
console.log(parseFloat(3.14)); //3.14
// 转化为数值型
var str = '123';
console.log(Number(str));
// 利用算式运算 - 号 隐式运算
console.log('122' - 0); //122
// 转换为布尔型Boolean,代表空,或者否定的值会被转化为false,如:0,NaN,null,undifined
console.log(Boolean(null));//false

五、循环

1、continue关键字

continue关键字用于立即跳出本次循环,继续下一次循环

<script>
        // 求1~100之间,除了能被7整除的整数之和
        var sum = 0;
        for(var i = 0 ; i <= 100; i++){
            if(i % 7 == 0){
                continue;
            }
            sum = sum + i;
        }
        console.log(sum);
</script>

六、数组

1、数组概念:就是一组数据的结合,数组是一种将一组数据存储在单个变量名下的优雅方式。
2、数组的创建方式
// 1.利用new创建数组
        var arr = new Array();
// 2.利用数组字面量创建数组
        var arr = [];
        var arr1 = [1,2,'pink',3,4];
		console.log(arr1); //1,2,pink,3,4
3、数组的使用
// 数组的定义
        var arr2 = ['迪丽热巴','古力娜扎','佟丽娅'];
// 根据数组的索引号来搜索要查找的人
        console.log(arr2[0]); //迪丽热巴
        console.log(arr2[3]); // 因为没有这个元素,所以输出的是undifined
4、遍历数组
<script>
        var prople = ['张飞','关羽','黄忠','马超'];
        // 遍历数组
        for(var i = 0 ; i <= prople.length ; i ++){
            console.log(prople[i]);
        }
</script>
5、新增数组元素
<script>
        // 1.通过修改数组长度length来新增元素
        var arr = ['red','blue','yellow'];
        arr.length = 5;
        arr[3] = 'pink';
        arr[4] = 'gray';
        console.log(arr);	//red,blue,yellow,pink,gray
        // 2.通过新增数组元素
        var arr1 = ['red','blue','yellow'];
        arr1[3] = 'black';
        console.log(arr1);	//red,blue,yello,black
        // 替换数组元素
        arr1[0] = 'orange';
        console.log(arr1);	//orange,blue,yellow,black
        // 不要给数组名赋值,否则里面的数组元素都没有了
        arr = '水果';
        console.log(arr);	//水果
</script>
6、数组案例
6.1、翻转数组
<script>
        var arr = ['red','green','yellow','blue'];
        var newarr = [];
        for(var i = arr.length - 1; i  >= 0 ; i --){
            newarr[newarr.length] = arr[i];
        }
        console.log(newarr);   // blue,yellow,green,red   
</script>
6.2、删除指定数组元素
<script>
        var str = [2,0,6,1,77,0,52,0,25,7];
        var newstr = [];
        for(var i = 0; i < str.length; i ++){
            if(str[i] > 0){
                newstr[newstr.length] = str[i];
            }
        }
        console.log(newstr);    // 2,6,1,77,52,25,7
</script>
6.3数组冒泡排序
<script>
        var arr = [1,2,3,4,5];
        for(var i = 0 ; i <= arr.length - 1; i ++ ){ //外层循环管趟数
            for(var j = 0; j <= arr.length - 1 - i; j ++){ // 里层循环管交换次数
                if(arr[j + 1] > arr[j]){
                    var temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        console.log(arr);   //5,4,3,2,1
</script>

七、函数

1、函数概念:函数就是封装了一段可以被重复执行调用的代码块,目的就是让大量代码重复使用
2、函数的使用分为两步:声明函数、调用函数
// 声明函数
function 函数名(){
    函数体;
}
// 调用函数
函数名();

// 案例
<script>
        function sayHi(){
            console.log('你好!');
        }
        sayHi();// 你好!
</script>
3、函数的参数
function 函数名(形参1,形参2){
    函数体;
}
函数名(实参1,实参2);

// 案例:求两个数的和
function sum(num1,num2){
    console.log(num1 + num2);
}
sum(2,3);	//5

// 注意:函数形参实参不匹配的问题
// 1、实参个数等于形参个数,正常输出
// 2、实参个数大于形参个数,只取形参个数
// 3、实参个数小于形参个数,多的形参定义为undifined,结果为NaN
4、函数的返回值return
function 函数名(){
    函数体;
    return 需要返回的结果;
}
函数名();

// 案例:求数组中的最大值
<script>
        function getMax(arr){
            var max = arr[0];
            for(var i = 0; i < arr.length; i ++){
                if(max < arr[i]){
                    max = arr[i];
                }
            }
            return max;
        }
        // 在实际开发中,我们经常使用一个变量来接受函数的返回结果
        var result = getMax([5,2,99,101,990,89]);
        console.log(result);
</script>
return作为返回值注意事项:

1、return终止函数

<script>
        function getMax(num1,num2){
            return num1 + num2;
            alert('不会被执行的语句!');
        }
        console.log(getMax(1,2));	//执行结果:3
</script>

2、return只能返回一个值

function getNum(num1,num2){
            return num1,num2;   //返回的结果是最后一个值
        }
console.log(getNum(1,2));	//	执行结果:2

3、return可以返回一个数组

function getArr(num1,num2){
            return [num1 + num2,num1 - num2,num1 * num2,num1 / num2];//返回的值是一个数组
}
console.log(getArr(1,2));	// 执行结果:[3,-1,2,0.5]

4、函数中如果有return,返回的就是return后面的值,如果没有return,返回的就是undifined

5、arguments的使用

​ 当我们不确定有多少个参数传递的时候,可以使用arguments来获取,在JavaScript中,arguments实际上它是当前函数的内置对象,所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参。

arguments展示形式是一个伪数组,因此可以进行遍历,伪数组具有以下特点:

1、具有length属性

2、按索引方式存储数组

3、不具有数组的push、pop等方法

八、作用域

1、JavaScript作用域:就是代码名字(变量)在某个范围内起作用或效果,目的是为了提高程序的可靠性更重要的是减少命名冲突。
2、JavaScript作用域(es6之前)可以分为:全局作用域和局部作用域

全局作用域:整个script标签,或者是一个单独的js文件。

局部作用域(函数作用域):在函数内部就是局部作用域,这个代码名字之在函数内部起效果和作用。

// 全局作用域
var str = 10;
console.log(str);	// 执行结果:10
// 局部作用域
function fn(){
    var str = 20;
    console.log(str);
}
fn();	// 执行结果:20
3、根据作用域的不同可以分为全局变量和局部变量

全局变量:在全局作用域下的变量

注意:在函数内部,没有声明直接赋值的变量也属于全局变量
var num = 10;   // num就是一个全局变量
console.log(num);
function fn(){
     console.log(num);   //在函数内部也可以调用全局变量
     num2 = 20;  // 注意:在函数内部,没有声明直街赋值的变量也是全局变量
}
fn();
console.log(num2);

局部变量:在局部作用域下的变量,也可以说是在函数内部的变量

注意:函数的形参也可以看作局部变量
function fun(arr){  //  函数的形参也属于局部变量
       var num3 = 20;  // 在函数内部定义的变量是局部变量 
}
console.log(fun(10));

从执行效率来看全局变量和局部变量:

(1)全局变量只在浏览器关闭的时候销毁,比较占内存资源

(2)局部变量在我们程序执行完毕后会销毁,比较节约内存资源

九、预解析

​ JavaScript代码是由浏览器中的Javascript解析器来执行的,Javascript解析器在运行Javascript代码的时候分为两步:预解析和代码执行

1、预解析:js引擎会把js里面所有的var还有function提升到当前作用域的最前面

2、代码执行:按照代码书写的顺序从上往下执行

预解析分为变量预解析(变量提升)和函数预解析(函数提升)

变量提升:也就是把所有变量的声明提升到当前作用域的最前面,不提升赋值操作

console.log(num);
var num = 20;	//执行结果:undifined
// 相当于执行了以下代码
var num;
console.log(num);	//只声明不赋值执行结果:undifined
num = 20;

函数提升:也就是把所有的函数的声明提升到当前作用域的最前面,不调用函数

fun();
var fun = function(){
    console.log(22);	//	执行结果:报错
} 
//	相当于执行了以下代码
var fun;
fun();
fun = function(){
    console.log(22);
}
预解析案例:
// 案例一:结果是几?
var a = 18;
f1();
function f1(){
    var b = 9;
    console.log(a);
    console.log(b);
    var a = '123';
}

//相当于下面的代码
var a;
function f1(){
    var b;
    var a;
    b = 9;
    console.log(a);	// 执行结果:undifined
    console.log(b); //	执行结果:9
    a = '123';
}
a = 18;
f1();
//	案例二:结果是几?(典型面试题)
f1();
console.log(c);
console.log(b);
console.log(a);
function f1(){
    var a = b = c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}

//	相当于执行下面的代码
function f1(){
    var a;
    a = b = c = 9;	// b与c不声明直街赋值相当于全局变量
    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属于局部变量,没声明也没赋值

十、JavaScript对象

1、对象的概念:在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等

对象是由属性方法组成的

属性:事物的特征,在对象中用属性来表示(常用名词)

方法:事物的行为,在对象中用方法来表示(常用动词)

2、创建对象的三种方式(object):

(1)利用字面量创建对象

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

//	利用函数字面量创建对象
var obj = {
    name : '迪丽热巴',
    age : 18,
    sex : '女',
    sayHi : function(){
        console.log('hello!');
    }
}
//	注意事项:
//	(1)里面的属性或者方法采用键值对的形式
//	(2)多个属性或方法中间用逗号隔开
//	(3)方法后面跟的是一个匿名函数

//	使用对象
//	调用对象的属性,采用对象名.属性名
console.log(obj.name);	// 迪丽热巴
//	调用对象的属性还有一种方法:对象名['属性名']
console.log(obj['age']);
//	调用对象的方法,采用对象名.方法名()
obj.sayHi();

(2)利用new Object创建对象

<script>
        var obj = new Object(); // 创建了一个空对象
        obj.name = '鸣人';  //  利用等号 = 赋值的方法,添加对象的属性和方法
        obj.sex = '男'; // 每个属性和方法之间用分号结束
        obj.age = 19;
        obj.skill = function(){
            console.log('影分身');
            
        }
        console.log(obj.name);
        console.log(obj.age);
        obj.skill();
</script>

(3)利用构造函数创建对象

构造函数:是一种特殊的函数,主要用来初始化函数,即为对象成员变量赋初始值,它总与new运算符一起使用,我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

// 构造函数的语法格式:
function 构造函数名(){
    this.属性名 = 值;
    this.方法 = function(){
    }
}
new 函数构造名();

// 案例:
function Star(name,age,sex){	// 构造函数的名字首字母要大写
    this.name = name;
    this.age = age;
    this.sex = sex;	// 构造函数不需要return也可以返回结果
    this.sing = function(sang){	// 属性和方法前面必须使用this
        console.log(sang);
    }
}
var people = new Star('刘德华',18,'男');
console.log(people['name']);	// 刘德华
people.sing('冰雨');	// 冰雨
3、变量、属性、函数、方法的区别:

(1)变量和属性都是用来存储数据的

​ 变量:单独声明并赋值,使用的时候直接写变量名,单独存在

​ 属性:在对象里面不需要声明,使用的时候必须是对象.属性

// 变量的使用
var num = 20;
console.log(num); 	// 执行结果:20
// 属性的使用
var obj = {
    name = '可可';
}
console.log(obj.name);	// 执行结果:可可

(2)函数和方法都是描述该对象的行为和功能

​ 函数:单独存在的,通过“函数名()”被调用

​ 方法:对象里面的函数成为方法,方法不需要声明,使用”对象名.方法()“的方式可以调用

//	函数的使用
function arr(){
    console.log('hello');	// hello
}
arr();
//	方法的使用
var obj = {
    sayHi : function(){
        console.log('hello');
    }
}
obj.sayHi();
4、遍历对象属性

for...in用于对数组或者对象的属性进行循环操作

// 声明方式
for (变量 in 对象){
    
}
// 案例
var obj = {
    name = 'hello',
    age = 18,
    sex = '男';
}
for(var k in obj){
    console.log(k);	//k作为变量输出得到的是属性名 
    console.log(obj[k]);	//obj[k]得到的是属性值
}

十一、内置对象

JavaScript中的对象分为3种,自定义对象、内置对象、浏览器对象

前面两种对象是JS基础内容,属于ECMAScript;第三个浏览器属于我们JS独有的,属于API

内置对象就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必用的功能(属性和方法)

JavaScript提供了多个内置对象:Math、Data、Array、String等

1、查文档

可以通过MDN/W3C来查文档

Mozilla开发者网络(MDN)提供了有关开放网络技术的信息,包括HTML、CSS和万维网及HTML5应用的API

MDN网址:https://developer.mozilla.org/zh-CN/

2、Math数学对象

​ Math数学对象不是一个构造函数,所以我们不需要new来调用,而是直接使用里面的属性和方法即可

2.1、Math.max()函数返回一组数中的最大值

// 语法格式:
Math.max([value1[,value2,....]])
// 案例
console.log(Math.max(1,67,8)); // 执行结果:67
console.log(Math.max(3,5,'red')); // 执行结果:NaN
console.log(Math.max()); // 	执行结果:-Infinity

返回值:

​ 返回给定的一组数字中的最大值,如果给定的参数中至少有一个参数无法被转化为数字,则会返回NaN

2.2、Math.PI函数返回圆周率

// 案例
console.log(Math.PI); //执行结果:3.1415926

2.3、Math.floor()向下取整

console.log(Math.floor(1.5)); // 执行结果:1

2.4、Math.ceil()向上取整

console.log(Math.ceil(1.1)); // 执行结果:2

2.5、Math.round()四舍五入取整

console.log(Math.round(-3.5)); // 执行结果:3
// 0.5特殊,往大取整

2.6、Math.abs()绝对值

console.log(Math.abs(-3)); // 执行结果:3

2.7、Math.random()随机数

random随机返回一个小数,0~1之间,包含0

这个方法里面不跟参数

console.log(Math.random()); //0.13455433345
 
// 案例
// 想得到两个数中的随机整数,并且包含着两个数
        function getRandom(min,max){
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }
        console.log(getRandom(1,10));
// 随机点名
        var arr = ['张三','李四','王五','赵本山'];
        console.log(arr[getRandom(0,arr.length - 1)]);
3、Date日期对象

Date对象是一个构造函数,需要实例化才能使用

Date实例用来处理时间和日期

3.1、Date()方法的使用

3.1.1、获取当前时间必须实例化

var now = new Date();
console.log(now); //Data日期对象.html:10 Fri Nov 20 2020 18:32:31 GMT+0800 (中国标准时间)

3.1.2、Date()构造函数的参数

​ 如果括号里面有时间,就返回参数里面的时间。

var date = new Date(2019,10,1);
console.log(date);	//Fri Nov 01 2019 00:00:00 GMT+0800 (中国标准时间)
//显示的时间比设置的时间少一个月
var date1 = new Date('2019-10-1 00:00:00');
console.log(date1);	//Tue Oct 01 2019 00:00:00 GMT+0800 (中国标准时间)
3.2、格式化日期
//格式化日期:年,月,日
        var data = new Date();
        console.log(data.getFullYear());	//年份执行结果:2020
        console.log(data.getMonth() + 1);//月份执行结果:11(如果不加一,返回的结果比真实值少一个月)
        console.log(data.getDate());	//日期执行结果:23
        console.log(data.getDay());		//星期执行结果:1
        var year = data.getFullYear();
        var month = data.getMonth();
        var data1 = data.getDate();
        var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
        var day = data.getDay();	//星期直接返回的是数字,所以用到数组索引
        console.log('今天的日期是:' + year + '年' + month + '月' + data1 + '日 ' + arr[day]);
//	执行结果:今天的日期是:2020年10月23日 星期一
3.3、格式化时间
//格式化:时,分,秒
        var time = new Date(); 
        console.log(time.getHours());	//小时执行结果:11
        console.log(time.getMinutes());	//分钟执行结果:15
        console.log(time.getSeconds());	//毫秒执行结果:23
        function getTime(){
            var time1 = new Date();
            var h = time1.getHours();
            h = h < 10 ? '0' + h : h;
            var m = time1.getMinutes();
            m = m < 10 ? '0' + m : m;
            var s = time1.getSeconds();
            s = s < 10 ? '0' + s : s;
            return '现在的日期是:' + h + ':' + m + ':' + s;
        }       
        console.log(getTime());	//执行结果:现在的日期是:11:13:37
3.4、获得Date总得毫秒数
//  获得Date总得毫秒数(时间戳) 不是当前时间的毫秒数,而是距离1970年1月1日过了多少毫秒数
        // 1.通过valueOf() getTime()
        var date = new Date();
        console.log(date.valueOf());	//1606102901929
        console.log(date.getTime());	//1606102901929
        // 2.简单的写法(最常用的写法)
        var date1 = +new Date();
        console.log(date1);		//1606102901929
        // 3.H5新增的 获得总的毫秒数
        console.log(Date.now());	//1606102901929
3.5、倒计时效果
// 1.核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能直接拿时分秒相减,结果会是负数
// 2.用时间戳来做,用户输入时间总得毫秒数减去现在时间总得毫秒数,得到的就是总得毫秒数
// 3.把剩余的时间转换为天、时、分、秒
//  转换公式如下:
// 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;   //times是剩余总的毫秒数
            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-01-01 20:20:20'));
        var date = new Date();
        console.log(date);
4、数组对象
// (1)instanceof 运算符 ,它可以用来检测是否为数组
        var arr = [];
        var obj = {};
        console.log(arr instanceof Array);	//执行结果:true
        console.log(obj instanceof Array);	//执行结果:false
// (2)Array.isArray(参数); H5新增的方法,IE9以上版本支持
        console.log(Array.isArray(arr));	//执行结果:true
        console.log(Array.isArray(obj));	//执行结果:false

4.1、添加删除数组元素

		var arr = ['red','yellow','blue'];
// push()在数组的末尾,添加一个或多个元素,参数直接写数组元素就可以了
        console.log(arr.push('gray','pink'));	//执行结果:5(输出的是新数组的个数)
        console.log(arr);	//执行结果:["red", "yellow", "blue", "gray", "pink"]

// unshift() 在数组的开头,添加一个或多个元素,参数直接写数组元素就可以了
        console.log(arr.unshift('black','white'));	//执行结果:7(输出的是新数组的个数)
        console.log(arr);	//执行结果:["black", "white", "red", "yellow", "blue", "gray", "pink"]

// pop() 它可以删除数组的最后一个元素,pop()没有参数
		console.log(arr.pop());		//执行结果:pink(输出的是要删除的元素)
        console.log(arr);	//执行结果:["black", "white", "red", "yellow", "blue", "gray"]

// shift 它可以删除数组第一个元素,shift()没有参数
        console.log(arr.shift());	//执行结果:shift(输出的是要删除的元素)
        console.log(arr);	//执行结果:["white", "red", "yellow", "blue", "gray"]

4.2、数组排序

4.2.1、reverse()翻转数组

var arr = ['red','yellow','blue'];
arr.reverse();
console.log(arr);	//执行结果:["blue", "yellow", "red"]

4.2.2、sort()数组排序(冒泡排序)

var arr1 = [1,7,3,5];
arr1.sort();
console.log(arr1);	//执行结果:[1, 3, 5, 7]
//sort()这个方法有一个弊端,只有个位数可以正常排序,多位数都是按照第一个数字进行排序
var arr1 = [1,17,3,25];
arr1.sort();
console.log(arr1);	//执行结果:[1, 17, 25, 3]
//解决sort()方法的问题
var arr2 = [3,99,62,2,43];
arr2.sort(function(a,b){
     // return a - b;    按照升序的顺序排序
     return b - a;   //按照降序的顺序排序
});
console.log(arr2);	//执行结果:[99, 62, 43, 3, 2]

4.3、获取数组元素索引方法

//  返回数组元素索引号方法 indexOf(数组元素),作用是返回该数组的索引号,从前面开始查找
        var arr = ['red','blue','yellow','blue'];
// 它只返回第一个满足条件的索引号
        console.log(arr.indexOf('blue'));	//执行结果:1
        var arr1 = ['red','yellow','white'];
// 如果数组里面没有该元素,则返回的值为-1
        console.log(arr1.indexOf('blue'));	//执行结果:-1
//  返回数组元素索引号方法 indexOf(数组元素),作用是返回该数组的索引号,从后面开始查找
        var arr2 = ['red','blue','yellow','red'];
// 它只返回最后一个满足条件的索引号
        console.log(arr2.lastIndexOf('red')); //执行结果:3

4.4、数组去重

// 核心算法:遍历旧数组,拿旧数组元素查询新数组,如果该元素在新数组中没有,就添加,否则不添加
        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;
        }
        var demo = unique(['c','z','a','c','a','c']);
        console.log(demo);

4.5、数组转化为字符串

// 1.toString()将我们的数组转化为字符串
        var arr = [1,2,3];
        console.log(arr.toString());    //1,2,3
// 2.join(分隔符)
        var arr1 = ['blue','green','pink'];
        console.log(arr1.join());	//blue,green,pink
        console.log(arr1.join('-'));	//blue-green-pink
        console.log(arr1.join('&'));	//blue&green&pink

4.6、concat(str1,str2...):连接两个或多个数组,不影响原数组,,返回一个新的数组

var str = 'andy';
console.log(str.concat('red'));	//andyred

4.7、slice():数组截取slice(begin,end),返回被截取项目的新数组

4.8、splice():数组删除splice(第几个开始,要删除个数),返回被删除项目的新数组,注意:这个会影响原数组

4.9、substr(start,length):从start位置开始(索引号),length取得个数
var str1 = '改革春风吹满地';
// 第一个2是索引号的2,从第几个开始,第二个2是取几个字符
console.log(str1.substr(2,2)); 	//春风
5、字符串对象

5.1、基本包装类型

​ 为了方便操作基本数据类型,J avaScript还提供了三个特殊的引用类型:String、Number和Boolean。

基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。

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

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

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

5.2、字符串的不可变

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

var str = 'abc';
str = 'hello';
// 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for(var i = 0;i < 1000000; i ++){
    str += i;
}
console.log(str);	//这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间

5.3、查找字符串中某个字符出现的位置和次数

		var str = 'avdnfjgueda';
        var index = str.indexOf('a');
        var sum = 0;
        while(index !== -1){
            console.log(index);	//执行结果:0、10
            sum ++;
            index = str.indexOf('a',index + 1);
        }
        console.log('a出现的次数:' + sum);	//执行结果:a出现的次数:2

5.4、根据位置返回字符

5.4.1charAt(index):返回指定位置的字符(index字符串的索引号),例:str.charAt(0);

var str = 'pink';
console.log(str.charAt(3));	//执行结果:k

5.4.2、获取指定位置处字符的ASCII码(index索引号),例:str.charCodeAt(0);

var str = 'pink';
console.log(str.charCodeAt(2));	//执行结果:110

5.4.3、获取指定位置处字符,例:HTML5,IE8+支持和charAt()等效

var str = 'pink';
console.log(str[2]);	//执行结果:n
5.5、返回字符位置
// 判断一个字符串“ancahdidanf”中出现次数最多的字符,并统计其次数
        var str = 'ancahdidanf';
        var o= {};
        for(var i = 0; i < str.length; i ++){
            var char = str.charAt(i);   //char是字符串的每一个字符
            if(o[char]){    //o[char]得到的是属性值
                o[char]++;
            }else{
                o[char] = 1;
            }
        }
        console.log(o);
// 2.遍历对象
        var max = 0;
        var ch = '';
        for(k in o){
            // k得到的是是属性
            // o[k]得到的是属性值
            if(o[k] > max){
                max = o[k];
                ch = k;
            }
        }
        console.log('最多的字符' + ch);
        console.log(max);
5.6、replace('被替换的字符','替换为的字符'):替换字符,它只会替换第一个字符
var str = 'andyandy';
console.log(str.replace('a','b'));	//bndyandy

//案例:将字符串中的a替换成*
		var str1 = 'avadjdalfdjk';
        while(str1.indexOf('a') !== -1){
            str1 = str1.replace('a','*');
        }
        console.log(str1);	//执行结果:*v*djd*lfdjk
5.7、split('分隔符'):字符转化为数组,前面学过join把数组转化为字符串
var str2 = 'red,blue,yellow';
console.log(str2.split(','));	//执行结果:["red", "blue", "yellow"]
var str3 = 'red&blue&yellow';
console.log(str3.split('&'));	//执行结果:["red", "blue", "yellow"]

十二、JavaScript简单类型与复杂类型

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

值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型

string,number,boolean.undefined,null

// 简单数据类型 null 返回的是一个空的对象 object
var timer = null;
console.log(typeof timer); //执行结果:object
// 如果有个变量我们以后打算存储为对象,暂时没想好放什么,这个时候就给null

引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

通过new 关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等

2、堆和栈

堆栈空间分配区别:

​ 1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方法类似于数据结构中的栈;简单数据类型存放到栈里面

2、堆(操作系统):存放复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。**复杂数据类型存放到堆里面**

注意:JavaScript中没有堆栈的概念,通过堆栈的方式,可以让大家更容易理解代码的一些执行方式,便于将来学习其他语言

​ 3、简单数据类型与复杂数据类型存放方式:

​ (1)简单数据类型是存放在栈里面,里面开辟一个空间存放的是值

​ (2)复杂数据类型首先在栈里面存放地址,用十六进制表示,然后这个地址指向堆里面的数据

十三、Web APIs

1、Web APIs和 JS 基础关联性
1.1、JS的组成

1.2、JS基础阶段以及Web APIs阶段

总结:JS基础学习ECMAScript基本语法为后面作铺垫,Web APIs是JS的应用,大量使用JS基础语法做交互效果

2、API和Web API
2.1、API

​ API(Application Programming Interface,应用程序编程接口)是一些预定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

简单理解:API是给程序员提供的一种工具,以便能更轻松地实现想要完成的功能

2.2、Web API

Web API是浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM).

​ 现阶段我们主要针对于浏览器讲解常用的API,主要针对浏览器做交互效果。

​ 比如我们想要浏览器弹出一个警示框,直接使用alert('弹出')

​ MDN详细API:https://developer.mozilla.org/zh-CN/docs/Web/API

​ 因为Web API很多,所以我们将这个阶段成为Web APIs

2.3、API和Web API总结

​ 1、API是为程序员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现

​ 2、Web API主要是针对于浏览器提供的接口,主要针对于浏览器做交互效果

​ 3、Web API一般都有输入和输出(函数的传参和返回值),Web API很多都是方法(函数)

​ 4、学习Web API可以结合前面学习内置对象方法的思路学习

3、DOM
3.1、DOM概念

​ 文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口

​ W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式

3.2、DOM树

(1)文档:一个页面就是一个文档,DOM中使用document表示

(2)元素:页面中的所有标签都是元素,DOM中使用element表示

(3)节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示

DOM把以上内容都看作是对象

4、获取元素
4.1、如何获取页面元素

​ DOM在我们实际开发中主要用来操作元素

​ 获取页面中的元素可以使用一下几种方式:

​ (1)根据ID获取

​ (2)根据标签名获取

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

​ (4)特殊元素获取

4.1.1、根据ID获取

​ 使用getElementById()方法可以获取带有ID的元素对象

document.getElementById('id名');
<div id="time">2020-11-25</div>
    <script>
        // 1.因为文档页面从上往下加载,所以先得有标签,所以我们script写到标签的下面
        // 2.get获得 element元素 by通过 getElementById利用驼峰命名法
        // 3.参数id是大小写敏感的字符串
        // 4.返回的是一个元素对象
        var timer = document.getElementById('time'); //time为参数id
        console.log(timer);
        // 利用typeof查看timer返回值类型
        console.log(typeof timer);
        // 5.console.dir 打印我们返回的元素对象,更好的查看里面的属性和方法
        console.dir(timer);
    </script>
4.1.2、根据标签名获取

​ 使用getElementsByTagName()方法可以返回带有指定标签名的对象的集合

document.getElementByTagName('标签名');

注意:

1.因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历

2.得到元素对象是动态的(也就是标签里面的内容改变,JS中获取的元素不受影响)

<ul>
        <li>知否知否,应是绿肥红瘦</li>
        <li>知否知否,应是绿肥红瘦</li>
        <li>知否知否,应是绿肥红瘦</li>
        <li>知否知否,应是绿肥红瘦</li>
        <li>知否知否,应是绿肥红瘦</li>
</ul>
<ul id="nav">
        <li>昨夜雨疏风骤</li>
        <li>昨夜雨疏风骤</li>
        <li>昨夜雨疏风骤</li>
        <li>昨夜雨疏风骤</li>
        <li>昨夜雨疏风骤</li>
</ul>
<script>
        // 1.返回的是 获取过来元素对象的集合,以伪数组的形式存储的
        var lis = document.getElementsByTagName('li');
		//执行结果:HTMLCollection(10) [li, li, li, li, li, li, li, li, li, li]
        console.log(lis);
        console.log(lis[0]);
        // 2.依次打印里面的元素对象我们可以采取遍历的方式
        for(var i = 0; i < lis.length; i ++){
            console.log(lis[i]);
        }
        // 3.element.getElementsByTagName()可以得到这个元素里面的某些元素
        var nav = document.getElementById('nav');
        var navLis = nav.getElementsByTagName('li');
		// 执行结果:HTMLCollection(5) [li, li, li, li, li]
        console.log(navLis);
</script>
4.1.3、通过HTML5新增的方法获取
1.document.getElementsByClassName('类名'); //根据类名返回元素对象集合
2.document.querySelector('选择器'); //根据指定选择器返回第一个元素对象
3.document.querySelectorAll('选择器'); //根据指定选择器返回
<div class="box">主页</div>
    <div class="box"></div>
    <div id="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
</div>
<script>
// 1.getElementsByClassName 根据类名获得某些元素集合
        var boxs = document.getElementsByClassName('box');
        console.log(boxs);
// 2.querySelector 返回指定选择器的第一个元素对象 注意:里面的选择器需要添加符号 id选择器加‘.’, 类选择器加‘#’
        var firstBox = document.querySelector('.box');
        console.log(firstBox);
        var nav = document.querySelector('#nav');
        console.log(nav);
        var li = document.querySelector('li');
        console.log(li);
// 3.querySelectorAll()返回指定选择器的所有元素对象集合
        var allBox = document.querySelectorAll('.box');
        console.log(allBox);
        var lis = document.querySelectorAll('li');
        console.log(lis);
</script>
4.1.4、获取特殊元素(body,html)
// 获取body元素
1.document.body	//返回body元素对象

// 案例
var bodyEle = document.body;
console.log(bodyEle);
// console.dir()可以显示一个对象的所有属性和方法
console.dir(bodyEle);	//body
// 获取html元素
1.document.documentElement	//返回html元素对象

// 案例
var htmlEle = document.documentElement;
console.log(htmlEle);
5、事件基础
5.1、事件概述

​ JavaScript是我们有能力创建动态页面,而事件是可以被JavaScript侦测到的行为。

​ 简单理解:触发---响应机制

​ 网页中的每个元素都可以产生某些可以触发JavaScript的事件,例如:我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。

<button id="btn">唐伯虎</button>
    <script>
        // 点击一个按钮,弹出对话框
        // 1.事件是有三部分组成:事件源 事件类型 事件处理程序 我们也称为事件三要素
        // (1)事件源:事件被触发的对象
        var btn = document.getElementById('btn');
        // (2)事件类型:如何触发 什么事件 比如:鼠标点击还是鼠标经过还是键盘按下
        // btn.onclick;
        // (3)事件处理程序:通过一个函数赋值的方式
        btn.onclick = function() {
            alert('点秋香');
        }
    </script>
5.2、执行事件的步骤

​ 1.获取事件源

​ 2.注册事件(绑定事件)

​ 3.添加事件处理程序(采用函数赋值的形式)

<div>123</div>
    <script>
        // 执行事件的步骤
        // 点击div,控制台输出,我被选中了
        // 1.获取事件源
        var div = document.querySelector('div');
        // 2.绑定事件(注册事件)
        // div.onclick
        // 3.添加事件处理程序
        div.onclick = function () {
            console.log('我被选中了');
        }
    </script>

执行结果:

十四、操作元素

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

1、改变元素内容
element.innerText

从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉

element.innerHTML

起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行

<button>显示时间</button>
<div>某个时间</div>
<p>时间</p>
    <script>
        // 当我们点击了按钮,div里面的文字会发生变化
        // 1.获取元素
        var button = document.querySelector('button');
        var div = document.querySelector('div');
        // 2.注册事件
        // button.onclick = function () {
        //     div.innerText = '2020-10-01';
        // }
		// 元素可以添加事件
        button.onclick = function () {
            // 点击按钮,div中的文字显示:现在的日期是:14:01:20
             div.innerText = getTime();	
        }
        function getTime(){
            var time1 = new Date();
            var h = time1.getHours();
            h = h < 10 ? '0' + h : h;
            var m = time1.getMinutes();
            m = m < 10 ? '0' + m : m;
            var s = time1.getSeconds();
            s = s < 10 ? '0' + s : s;
            return '现在的日期是:' + h + ':' + m + ':' + s;
        }  
        // 元素可以不用添加事件     
        var p = document.querySelector('p');
        p.innerText = getTime();	
    </script>

执行结果:

2、innerText和innerHTML的区别
<div></div>
    <p>
        我是文字
        <span>123</span>
    </p>
    <script>
        // innerText 和 innerHTML的区别
        // 1.innerText不识别html标签,它是IE发起的,所以是非标准的,控制台输出的内容去除了空格和换行
        var div = document.querySelector('div');
        div.innerText = '你好';
        // 2.innerHTML识别html标签,它是W3C发起的,所以是标准的,控制台输出的内容去除了空格和换行
        div.innerHTML = '<strong>你好</strong>';
        // 这两个属性是可读写的,可以获取元素里面的内容
        var p = document.querySelector('p');
        console.log(p.innerText);
        console.log(p.innerHTML);
    </script>

执行结果:

3、常用元素的属性操作
1.innerText、innerHTML改变元素内容
2.src、href
3.id、alt、title
// 案例:通过不同的按钮,显示对应人物的图片以及姓名
<button id="lyf">李易峰</button>
<button id="dl">邓伦</button>
<img src="images/timg.jfif" alt="" title="李易峰">
<script>
        var lyf = document.getElementById('lyf');
        var dl = document.getElementById('dl');
        var img = document.querySelector('img');
        lyf.onclick = function () {
            img.src = 'images/timg.jfif';
            img.title = '李易峰';
        }
        dl.onclick = function () {
            img.src = 'images/1.jpg';
            img.title = '邓伦';
        }
</script>
4、操作元素案例
// 案例:根据现在的时间显示不同时间段的图片和提示文字
<img src="images/u=3283198037,3491165715&fm=26&gp=0.jpg" alt="">
<div>早上好</div>
<script>
        // 1.获取元素
        var img = document.querySelector('img');
        var div = document.querySelector('div');
        // 2.得到当前的小时数
        var date = new Date();
        var h = date.getHours();
        // 3.判断小时数改变图片和文字信息
        if(h < 12){
            img.src = 'images/u=3283198037,3491165715&fm=26&gp=0.jpg';
            div.innerHTML = '上午好';
        }else if(h < 18){
            img.src = 'images/u=3909080511,3014927893&fm=26&gp=0.jpg';
            div.innerHTML = '下午好';
        }else{
            img.src = 'images/timg (1).gfif';
            div.innerHTML = '晚上好';
        }
</script>
5、表单元素的属性操作

​ 利用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 () {
            // input.innerHTML = '点击了';  这个是普通盒子,并不能改变input中value中的值
            // 表单里面的值,文字内容是通过value来修改的
            input.value = '改变了';
            // 如果想要某个表单被禁用,不能被点击,就用disabled,将button按钮禁用
            // btn.disabled = true;
            // this 指向的是事件函数的调用者btn
            this.disabled = true;
        }
</script>
6、样式属性操作

​ 我们可以通过JS修改元素的大小、颜色、位置等样式

1.element.style		//行内样式操作
2.element.className		//类名样式操作

使用element.style注意:

1.JS 里面的样式采取驼峰命名法,比如:fontSize、backgroundColor

2.JS修改style样式操作,产生的是行内样式,css权重比较高

3..如果样式修改较少或功能简单的情况下,可以采取操作行内样式更改元素样式

//案例:块元素原为粉色,当被点击后,块元素颜色变为紫色
// CSS代码
<style>
        div{
            width: 300px;
            height: 300px;
            background-color: #ff00ff;
        }
</style>
// HTML与JS代码
<div></div>
<script>
        // 1.获取元素
        var div = document.querySelector('div');
        // 2.注册事件 处理程序
        div.onclick = function () {
            // div.style里面的属性采用驼峰命名法
            // div.style.backgroundColor = 'purple';
            this.style.backgroundColor = 'purple';
        }
</script>

使用element.className注意:

1.如果样式修改较多,可以采取操作类名方式更改元素样式

2.class因为是个保留字,因此使用className来操作元素类名属性

3.className会直接更改元素的类名,会覆盖原先的类名

// 案例:块元素为粉色,里面文字内容为你好,当它被点击,块元素颜色变为橙色,字体颜色、大小、位置改变

// CSS样式
<style>
        div{
            font-size: 10px;
            width: 200px;
            height: 200px;
            background-color: #ff00ff;
        }
        .change{
            text-align: center;
            font-size: 20px;
            color: #fff;
            background-color: #ff9900;
        }
</style>
// HTML与JS代码
<div>你好</div>
<script>
        //  1.获取元素
        var div = document.querySelector('div');
        // 2.当点击元素时,元素的类名就改变了,变为change
        div.onclick = function () {
            div.className = 'change';
        }
</script>

// 如果div本身带有class属性,利用以下代码:
		div.onclick = function () {
            div.className = 'change';
        }
//元素被点击时,类名变为change;
//如果想要保留原先的类名,可以使用以下代码:
		<div class="first">你好</div>
		var div = document.querySelector('div');
		div.onclick = function () {
            div.className = 'first change';
        }
7、操作元素总结

操作元素是DOM核心内容

8、自定义属性的操作

8.1、获取属性值

1.element.属性		获取属性值
2.element.getAttribute('属性');

区别:

1.element.属性	获取内置属性值(元素本身自带的属性)
2.element.getAttribute('属性');	主要获得自定义的属性(标准)我们程序员定义的属性
// 案例:
<div id="demo" index="1"></div>
<script>
        // 1.获取元素
        var div = document.querySelector('div');
        // 2.获取元素的属性值
        // (1)element.属性
        console.log(div.id);	//执行结果:demo
        // (2)element.getAttribute('属性') get得到获取 attribute属性的意思 程序员自己添加的属性我们称为自定义属性 index
        console.log(div.getAttribute('id'));	//执行结果:demo
        console.log(div.getAttribute('index'));	//执行结果:1
</script>

8.2、设置属性值

1.element.属性 = '值'		设置内置属性值
2.element.setAttribute('属性','值');	

区别:

1.element.属性	设置内置属性值
2.element.setAttribute('属性','值');	主要设置自定义的属性(标准)
// 案例:
<div id="demo" index="1" class="nav"></div>
<script>
    	// 1.获取元素
        var div = document.querySelector('div');
        // 2.设置属性值
        // (1)element.属性='值'
        var test = div.id = 'test';	//执行结果:将id属性值改为test
        var nav = div.className = 'navs';	//执行结果:将class属性值改为navs
        console.log(test);
        // (2)element.setAttribute('属性','值');
        div.setAttribute('index',2); 	//执行结果:将index属性值改为2
        div.setAttribute('class','footer');	//执行结果:将class属性值改为footer
// 特殊属性class:
// (1)利用element.属性 修改class属性值,用element.className
// (2)利用element.setAttribute('属性','值'); 修改class属性值,属性直接写class
</script>
9、H5自定义属性

自定义属性目的:是为了保存并使用数据,有些数据可以保存到的页面中而不用保存到数据库中

自定义属性获取是通过getAttribute('属性')获取

但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性

H5给我们新增了自定义属性

9.1、设置H5自定义属性

​ H5规定自定义属性data开头作为属性名并且赋值

​ 比如

或者使用JS设置element.setAttribute('data-index',2);

9.2、获取H5自定义属性

​ 1、兼容性获取:element.getAttribute('data-index');

​ 2、H5新增element.dataset.index 或者element.dataset['index'] IE 11以上版本支持

<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
        var div = document.querySelector('div');
        console.log(div.getAttribute('getTime'));
        div.setAttribute('data-time',20);
        console.log(div.getAttribute('data-index'));
        console.log(div.getAttribute('data-list-name'));
        // h5新增的获取自定义属性的方法,它只能后去data-开头的
        // dataset 是一个集合里面存放了所有以data开头的自定义属性
        console.log(div.dataset);
        console.log(div.dataset.index);
        console.log(div.dataset['index']);
        // 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
        console.log(div.dataset.listName);
        console.log(div.dataset['listName']);
</script>

十四、节点操作

1、获取元素通常使用两种方式:

1.1、利用DOM提供的方法获取元素

​ (1)document.getElementById()

​ (2)document.getElementByTagName()

​ (3)document.querySelector等

​ (4)缺点:逻辑性不强、繁琐

1.2、利用节点层级关系获取元素

​ (1)利用父子兄节点关系获取元素

​ (2)逻辑性强,但是兼容性稍差

这两种方式都可以获取元素节点,我们后面都会使用,但是节点操作更简单

2、节点概述

​ 网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。

​ HTML DOM树中的所有节点均可通过JavaScript进行访问,所有HTML元素(节点)均可被修改,也可以创建或删除。

​ 一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。

​ (1)元素节点 nodeType为1

​ (2)属性节点 nodeType为2

​ (3)文本节点 nodeType为3(文本节点包含文字、空格、换行等)

​ 我们在实际开发中,节点操作主要操作的是元素节点

3、节点层级

​ 利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系

3.1、父级节点

node.parentNode

(1)parentNode属性可返回某节点的父节点,注意是最近的一个父节点

(2)如果指定的节点没有父节点则返回null

<div class="dome">
        <div class="box">
            <span class="erweima">你好</span>
        </div>
</div>
<script>
        // 1.父节点 parentNode
        var erweima = document.querySelector('.erweima');
		// 得到的是离元素最近的父级节点,如果找不到父节点就返回为null
        console.log(erweima.parentNode);
		//执行结果:<div class="box"><span class="erweima">你好</span></div>
</script>

3.2、子节点

1.parentNode.childNodes(标准)

parentNode.childNodes返回包含指定节点的子节点的集合,该集合为即使更新的集合。

​ 注意:返回值里面包含了所有的子节点,包含元素节点,文本节点等

​ 如果只想要获得里面的元素节点,则需要专门处理,所以我们一般不提倡使用childNodes

<ul>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ul>
<ol>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ol>
<script>
	var ul = document.querySelector('ul');
    for(var i = 0; i < ul.childNodes.length; i ++){
        if(ul.childNodes[i].nodeType == 1){
            // ul.childNodes[i]是元素节点
            console.log(ul.childNodes[i]);
            //执行结果:<li>...</li><li>...</li><li>...</li><li>...</li>
        }
    }
</script>
2.parentNode.children(非标准)

parentNode.children是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回(这个是我们重点掌握的)。

​ 虽然children是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用

<ul>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ul>
<ol>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
    <li>我是li</li>
</ol>
<script>
    var ul = document.querySelector('ul');
	console.log(ul.childNodes[0].nodeType);		//执行结果:3
</script>
3.parentNode.firstChild

firstChild返回第一个子节点,找不到则返回null。同样,也是包含所有的节点

4.parentNode.lastChild

lastChild返回第一个子节点,找不到则返回null。同样,也是包含所有的节点

// 利用firstChild与lastChild,输出的结果为文本节点,因为换行属于文本,所以第一个子节点为文本节点
<ul>
   <li>li1</li>
   <li>li2</li>
   <li>li3</li>
   <li>li4</li>
</ul>
<script>
        var ul = document.querySelector('ul');
        // 1.firstChils 第一个子节点,不管是文本节点还是元素节点
        console.log(ul.firstChild);	//执行结果:#text
        console.log(ul.lastChild);	//执行结果:#text
</script>
5.parentNode.firstElementChild

firstElementChild 返回第一个子元素节点,找不到返回null

6.parentNode.lastElementChild

lastElementChild 返回最后一个子元素节点,找不到则返回null

注意:这两个方法有兼容性问题,IE9以上才支持

​ 实际开发中,firstChild和lastChild包含其他节点,操作不方便,而firstElementChild和lastElementChild又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?

解决方案:

1.如果想要第一个子元素节点,可以使用parentNode.children[0]

<ul>
     <li>li1</li>
     <li>li2</li>
     <li>li3</li>
     <li>li4</li>
</ul>
<script>
        var ul = document.querySelector('ul');
        // 1.firstChils 第一个子节点,不管是文本节点还是元素节点
        console.log(ul.firstChild);
        console.log(ul.lastChild);
        // 2.firstElementChild 返回第一个子元素节点
        console.log(ul.firstElementChild);
        console.log(ul.lastElementChild);
        // 3.实际开发的写法,既没有兼容性问题又返回第一个元素
        console.log(ul.children[0]);
        console.log(ul.children[ul.children.length - 1]);   
</script>

3.3、兄弟节点

1.node.nextSibling

nextSibling返回下一个兄弟节点,该节点有可能是文本节点也有可能是元素节点,找不到则返回null

2.node.nextElementSibling

nextElementSibling返回当前元素下一个兄弟元素节点,找不到则返回null

3.node.previousElementSibling

previousElementSibling返回当前元素上一个兄弟元素节点,找不到则返回null

注意:第二个和第三个方法都有兼容性问题,IE9以上才支持

<p>你好</p>
<div class="nav">兄弟</div>
<span class="">干杯</span>
<script>
        // 1.获取元素
        var nav = document.querySelector('.nav');
        // 2.nextSibling 返回下一个兄弟节点,下一个兄弟节点可能是文本节点也可能是元素节点
        console.log(nav.nextSibling);	//执行结果:#text(换行属于文本节点)

        // 3.nextElementSibling 返回下一个兄弟元素节点	
        console.log(nav.nextElementSibling);	
		//执行结果:<span class="">干杯</span>

        // 4.previousElementSibling 返回上一个兄弟元素节点
        console.log(nav.previousElementSibling);
		// 执行结果:<p>你好</p>
</script>

解决兼容性问题:自己封装一个兼容性的函数

function getNextElementSibling(element){
        var el = element;
        while(el = el.nextSibling) {
        if(el.nodeType == 1){
           return nav1;
                }
            }
           return null;
        }
// 案例:利用封装函数解决兄弟节点兼容性问题
<p>你好</p>
<div class="nav">兄弟</div>
<span class="">干杯</span>
<script>
       // 1.获取元素
       var nav = document.querySelector('.nav');
	   // 2.封装函数
	   function getNextElementSibling(nav){
            // 将获取到的元素以形参的形式赋值给变量nav1
            var nav1 = nav;
            // 循环遍历将下一个兄弟元素节点赋值给变量
            while(nav1 = nav1.nextSibling) {
                // 如果下一个兄弟节点是元素节点
                if(nav1.nodeType == 1){
                    // 返回下一个兄弟元素节点
                    return nav1;
                }
            }
            // 如果没有找到则返回null
            return null;
        }
        console.log(getNextElementSibling(nav));

3.4添加节点

1.node.appendChild(child)

node.appendChild()方法将一个节点添加到指定父节点的子节点列表末尾。类似于css里面的after伪元素。

2.node.insertBefore(child,指定元素)

node.insertBefore()方法将一个节点添加到父节点的指定子节点前面.类似于css里面的before伪元素。

// 案例:将li元素添加到ul里面
<ul>
        <li>你好</li>
</ul>
<script>
    // 1.获取元素
    var ul = document.querySelector('ul');
    // 创建一个新的节点元素
    var li = document.createElement('li');
    // 利用node.appendChild()添加节点 node 父级 child 子级 在元素最后面追加元素,类似于数组中的push
    ul.appendChild(li);	
	//执行效果:
		<ul>
        	<li>你好</li>
			<li></li>
   	 	</ul>

    // 利用node.insertBefore(child,指定元素)添加节点
    var lili = document.createElement('li');
    ul.insertBefore(lili,ul.children[0]);
	// 执行效果:
		<ul>
            <li></li>
        	<li>你好</li>
			<li></li>
   	 	</ul>
</script>
// 案例:做一个简易版发布留言,将用户输入的内容显示在第一行的位置,同时判断用户输入的内容是否为空
<textarea name="" id="" cols="30" rows="10">123</textarea>
<button>发布</button>
<ul>

</ul>
<script>
        // 1.获取元素
        var textarea = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var ul = document.querySelector('ul');
        // 2.注册事件
        btn.onclick = function () {
            // 3.判断用户是否输入的内容
            if(textarea.value == ''){
                alert('您输入的内容为空,不能进行发布');
            }else{
                // 4.创建元素
                var li = document.createElement('li');
                // 5.将用户输入的内容赋值给li
                li.innerHTML = textarea.value;
                // 6.添加元素节点,添加的内容处在第一个位置
                ul.insertBefore(li,ul.children[0]);
            }
        }
</script>

3.5、删除节点

node.removeChild(child)

node.removeChild()方法从DOM中删除一个子节点,返回删除的节点

// 案例:通过点击删除按钮删除ul里面的元素
<button>删除</button>
<ul>
    <li>熊大</li>
    <li>熊二</li>
    <li>光头强</li>
</ul>
<script>
        // 1.获取元素
        var btn = document.querySelector('button');
        var ul = document.querySelector('ul');
        // 2.点击按钮依次删除ul里面的li
        btn.onclick = function () {
            // 3.判断如果ul里面没有元素则按钮失效,不能在进行点击,如果里面有元素,则可以进行删除
            if(ul.children.length == 0){
                btn.disabled = true;
            }else{
                ul.removeChild(ul.children[0]);
            }
        }
</script>

3.6、复制节点(克隆节点)

node.cloneNode()

node.cloneNode()方法返回调用该方法的节点的一个副本,也称为克隆节点/拷贝节点

注意:

1.如果括号参数为空或者为false,则是浅拷贝,只复制节点,不复制节点里面的内容

2.如果括号参数为true,则是深拷贝,会复制节点本身以及里面节点的内容

// 案例:复制ul中第一个li,添加到ul中
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    // 获取元素
    var ul = document.querySelector('ul');
    // 复制ul里面第一个元素li,设置cloneNode(true)是复制li里面的值
    var lili = ul.children[0].cloneNode(true);
    // 将复制的元素添加到ul中
    ul.appendChild(lili);
</script>

3.7三种动态创建元素区别

1.document.write()
2.element.innerHTML
3.document.createElement()

区别:

1.document.write()是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘

2.innerHTML是将内容写入某个DOM节点,不会导致页面全部重绘

3.innerHTML创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂

4.createElement()创建多个元素效率稍低一点点,但是结构更清晰

总结:不同浏览器下,innerHTML效率要比createElement()高

// 1.document.write()创建元素
<button>点击</button>
<div>你好</div>
<script>
    var btn = document.querySelector('button');
    btn.onclick = function () {
        // 利用document.write()创建元素,执行后会跳转到另外一个页面,原来的内容都不存在了
        document.write('<div>hello</div>');
    }
</script>

// 2.element.innerHTML创建元素
var inner = document.querySelector('.inner');
for(var i = 0; i < 100; i ++){
    inner.innerHTML += '<a href= "javascript:;"百度</a>';
}
//innerHTML创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),使用拼接字符串方式,效率比createElement()效率低,使用数组形式拼接效率最高

// 3.document.createElement()创建元素
var create = document.querySelector('.create');
for(var i = 0; i < 100; i ++){
    var a = document.createElement('a');
    a.innerHTML = '百度';
    create.appendChild(a);
}

十五、总结DOM重点核心

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口

​ W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式

​ 1.对于JavaScript,为了能够使JavaScript操作HTML,JavaScript就有了一套自己的DOM编程接口

​ 2.对于HTML,DOM使得html形成一棵DOM树,包含文档、元素、节点

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

1、创建

​ 1.1、document.write

​ 1.2、innerHTML

​ 1.3、createElement

2、增

​ 2.1、appendChild

​ 2.2、insertBefore

3、删

​ 3.1、removeChild

4、改

主要修改DOM的元素属性,DOM元素的内容、属性、表单的值等

​ 4.1、修改元素属性:src、href、title等

​ 4.2、修改普通元素内容:innerHTML、innerText

​ 4.3、修改表单元素:value、type、disabled等

​ 4.4、修改元素样式:style、className

5、查

主要获取查询DOM的元素

​ 5.1、DOM提供的API方法:getElementById、getElementsByTagName古老用法不太推荐

​ 5.2、H5提供的新方法:querySelector、querySelectorAll提供

​ 5.3、利用节点操作获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling)提倡

6、属性操作

主要针对于自定义属性

​ 6.1、setAttribute:设置DOM的属性值

​ 6.2、getAttribute:得到DOM的属性值

​ 6.3、removeAttribute:移除属性

7、事件操作

给元素注册事件,采取事件源.事件类型 = 事件处理程序

十六、事件高级

1、注册事件(绑定事件)

​ 1.1、注册事件概述

​ 给元素添加事件,称为注册事件或者绑定事件

​ 注册事件有两种方式:传统方式和方法监听注册方式

​ 1.2、addEventListener事件监听方式(IE9以上版本支持 )

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

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

该方法接收三个参数:

(1)type:事件类型字符串,比如chilk、mouseover,注意这里不要带on

(2)listener:事件处理函数,事件发生时,会调用该监听函数

(3)useCaptrue:可选参数,是一个布尔值,默认是false

​ 1.3、attachEvent事件监听方式(IE9以下版本使用,不提倡使用)

eventTarget.attachEvent(eventNameWithOn,callback)

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

该方法接收两个参数:

​ (1)eventNameWithOn:事件类型字符串,比如:onclick、onmouseover,这里要带on

​ (2)callback:事件处理函数,当目标触发事件时回调函数被调用

<button>传统注册事件</button>
<button>事件侦听注册事件</button>
<script>
    var btns = document.querySelectorAll('button');
    console.log(btns);
    // 1.传统方式注册事件
    btns[0].onclick = function () {
        alert('hi');
    }
    btns[0].onclick = function () {
        alert('how are you');
    }
	// 执行结果:系统只弹出how are you弹框
    // 2.事件侦听注册事件
    btns[1].addEventListener('click',function () {
        alert(11);
    });
    btns[1].addEventListener('click',function () {
        alert(22);
    })
	// 执行结果:系统先弹出11弹框,后弹出22弹框
	// 3.attachEvent事件监听方式
    btns[2].attachEvent('onclick',function() {
        alert(11);
    });
	// 执行结果:系统弹出11弹框
</script>

​ 1.4、注册事件兼容性解决方法

兼容性处理的原则:首先照顾大多数浏览器,再处理特殊浏览器

2、删除事件(解绑事件)

​ 2.1、删除事件的方式

​ 1、传统注册方式

eventTarget.onclick = null;

​ 2、方法监听注册方式

1.eventTarget.removeEventListener(type,listener[,useCapture]);
2.eventTarget.detachEvent(eventNameWithOn,callback);
// 案例:点击块元素弹出弹框,再次点击不再弹框
<div>1</div>
<div>2</div>
<div>3</div>
<script>
    var divs = document.querySelectorAll('div');
    divs[0].onclick = function () {
        alert(11);
        // 1.传统方式删除事件
        divs[0].onclick = null;
    }
    divs[1].addEventListener('click',fn);
    function fn(){
        alert(22);
        // 2.removeEventListener()事件侦听方式删除事件
        divs[1].removeEventListener('click',fn);
    }
    divs[2].attachEvent('onclick',fn1);
    function fn1(){
        alert(33);
        // 3.detachEvent()事件侦听方式删除事件
        divs[2].detachEvent('onclick',fn1);
    }
</script>
3、DOM事件流

事件流描述的是从页面中接受事件的顺序

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流

​ 比如我们给div注册了点击事件:

​ DOM事件流分为3个阶段:

​ 1、捕获阶段

​ 2、当前目标阶段

​ 3、冒泡阶段

(1)事件冒泡:IE最早提出,时间开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程

(2)事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接受的过程

注意:

1.JS代码中只能执行捕获或者冒泡其中的一个阶段。

2.onclick和attachEvent只能得到冒泡阶段

3.addEventListener(type,listener[,useCaptrue])第三个参数如果是true,表示在时间捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在时间冒泡阶段调用事件处理程序。

4.实际开发中我们很少使用事件捕获,我们更关注事件冒泡

5.有些事件是没有冒泡的,比如onblur、onfocus、onmouseenter、onmouseleave

6.事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件

// css代码
<style>
    .father{
        position: relative;
        width: 200px;
        height: 200px;
        background-color: red;
    }
    .son{
        position: absolute;
        left: 50px;
        top: 50px;
        width: 100px;
        height: 100px;
        background-color: blue;
    }
</style>
//HTML和js代码
<div class="father">
    <div class="son">
        </div>
</div>
<script>
        // 获取元素
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // 捕获阶段
        father.addEventListener('click',function (){
            alert(11);
        },true);
        son.addEventListener('click',function (){
            alert(22);
        },true);
        // 冒泡阶段
        son.addEventListener('click',function (){
            alert(22);
        },false);
        father.addEventListener('click',function (){
            alert(11);
        },false);
</script>
4、事件对象

​ 4.1、事件对象概念

eventTarger.onclick = function (event) {}
eventTarger.addEventListener('click',function (event) {})
// 这个event就是事件对象,我们还喜欢写成e或者evt

官方解释:event对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态

简单解释:事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象event,它有很多属性和方法

比如:

1.谁绑定了这个事件

2.鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置

3.键盘触发事件的话,会得到键盘的相关信息,如按了哪个键

​ 4.2、事件对象的使用语法

eventTarger.onclick = function (event) {
    // 这个event就是事件对象,我们还喜欢写成e或者evt
}
eventTarger.addEventListener('click',function (event) {
    // 这个event就是事件对象,我们还喜欢写成e或者evt
})

​ 4.3、事件对象的兼容性方案

​ 事件对象本身的获取存在兼容问题:

​ 1.标准浏览器中是浏览器给方法传递的参数,只需要定义形参e就可以获取到

​ 2.在IE6~8中,浏览器不会给方法传递参数,如果需要的话,需要到window.event中获取查找

// css代码
<style>
        div{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
</style>
<div>123</div>
<script>
        var div = document.querySelector('div');
        div.onclick = function (event) {
            // event就是事件对象
            console.log(event);
        }
        div.addEventListener('click',function (event) {
            // event就是事件对象
            console.log(event); 
            // 解决兼容性问题
            event = event || window.event;
        })
</script>

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

<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
    <input type="submit" value="提交" name="sub">
        </form>
<script>
        // 1.获取元素
        var div = document.querySelector('div');
        var a = document.querySelector('a');
        var input = document.querySelector('input');
        // 2.返回事件类型
		// 点击123控制台输出click
        div.addEventListener('click',fn);
		// 点击123控制台输出mouseover
        div.addEventListener('mouseover',fn);
		// 点击123控制台输出mouseout
        div.addEventListener('mouseout',fn);
        function fn(event){
            console.log(event.type);
        }
        // 3.阻止默认行为(事件),让链接不跳转或者让提交按钮不提交
        var a = document.querySelector('a');
        a.addEventListener('click',function (e) {
            e.preventDefault();     // DOM标准写法,但有兼容性
        })
        // 传统注册方式
        a.onclick = function (e) {
            // 普通浏览器e.preventDefault(); 使用的是方法
            e.preventDefault();
            // 低版本浏览器IE6~8 returnValue,使用的是属性
            e.returnValue;
            // 我们可以使用return false,也能组织默认行为,不存在兼容性问题
            // 特点: return后面的代码不会再执行
            return false;
            alert(11);
        }
</script>
5、阻止事件冒泡

​ 5.1、阻止事件冒泡的两种方式

​ 事件冒泡:开始是由最具体的元素接收,然后逐级向上传播到DOM最顶层节点

​ 事件冒泡本身的特性,会带来的坏处,也会带来的好处,需要我们灵活掌握

阻止事件冒泡

​ (1)标准写法:利用事件对象里面的stopPropagation()方法

e.stopPropagation()

​ (2)非标准写法:IE6~8利用事件对象canceBubble属性

​ 5.2、阻止事件冒泡的兼容性解决方案

if(e && e.stopPropagation){
    e.stopPropagation();
}else{
    window.event.cancelBubble = true;
}
// 案例:阻止事件冒泡
<div class="father">
    <div class="son">
    </div>
</div>
<script>
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
		 son.addEventListener('click',function (e){
            alert(22);
            // 阻止冒泡DOM推荐的标准:stopPropagation()
            e.stopPropagation(); // stop停止 Propagation传播
            e.cancelBubble = true; // 非标准cancel 取消 bubble泡泡
        },false);
        father.addEventListener('click',function (e ){
            alert(11);
            e.stopPropagation(); 
        },false);
</script>
6、事件委托(代理、委派)

事件委托

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

事件委托的原理(重点)

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

​ 以上案例:给ul注册点击事件,然后利用事件对象的target来找当前点击的li,因为点击li,事件会冒跑到ul上,ul有注册事件,就会触发事件监听器。

事件委托的作用

​ 我们只操作一次DOM,提高了程序的性能。

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>
<script>
    var ul = document.querySelector('ul');
    ul.addEventListener('click',function (e) {
        // e.target 这个可以得到我们点击的对象
        e.target.style.backgroundColor = 'pink';
    })
</script>
7、常用的鼠标事件

​ 7.1、常用的鼠标事件

​ 1.禁止鼠标右键菜单

​ contextmenu主要控制应该何时显示上下文菜单,主要用于程序员取消默认的上下文菜单

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

好喜欢你
    <script>
    	// contextmenu 我们可以禁用右键菜单
        document.addEventListener('contextmenu',function(e) {
            e.preventDefault();
        })
		// selectstart 禁止选中文字
        document.addEventListener('selectstart',function(e) {
            e.preventDefault();
        })
    </script>

​ 7.2、鼠标事件对象

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

<script>
        document.addEventListener('click',function (e) {
            // 1.client 鼠标可视区的x和y坐标
            console.log(e.clientX);
            console.log(e.clientY);
            console.log('------------------------');
            
            // 2.page 鼠标在页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            console.log('------------------------');

            // 3.screen 鼠标在电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);
            
        })
    </script>
8、常用的键盘事件

​ 8.1、常用的键盘事件

​ 事件除了使用鼠标触发,还可以使用键盘触发。

注意:

1.如果使用addEventListener不需要添加on

2.onkeypress和前面2个的区别是:它不识别功能键,比如左右箭头,shift等

3.三个事件的执行顺序是:keydown->keypress->keyup

<script>
        // 1.keyup 按键弹起的时候触发
        // 传统注册方式
        document.onkeyup = function () {
            console.log('我弹起了');
        }
        // 事件侦听方式
        document.addEventListener('keyup',function () {
            console.log('我弹起了');
        })
        // 2.keydown 按键按下的时候触发
        // 传统注册方式
        document.onkeydown = function () {
            console.log('我按下了down');
        }
        // 事件侦听方式
        document.addEventListener('keydown',function () {
            console.log('我按下了down');
        })
        // 3.keypress 按键按下的时候触发
        document.addEventListener('keypress',function () {
            console.log('我按下了press');
        })
</script>

​ 8.2、键盘事件对象

注意:

1.onkeydown和onkeyup不区分字母大小写,onkeypress区分字母大小写

2.在我们实际开发中,我们更多的使用keyup和keydown,它能识别所有的键(包括功能键)

3.Keypress不识别功能键,但是keyCode属性能区分大小写,返回不同的ASCII值

<script>
        // 我们可以利用keyCode返回的ASCII码值来判断用户按下哪个键
        document.addEventListener('keyup',function (e) {
            console.log('up:' + e.keyCode);
        });
        document.addEventListener('keypress',function (e) {
            console.log('press:' + e.keyCode);
        });
</script>

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

// css代码
<style>
        .search{
            position: relative;
            width: 178px;
            margin: 100px;
        }
        .con{
            /* display: none; */
            position: absolute;
            top: -40px;
            width: 171px;
            border: 1px solid rgba(0, 0, 0, 2);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 2);
            padding: 5px 0;
            font-size: 18px;
            line-height: 20px;
            color: #333;
        }
        .con::before{
            content: '';
            width: 0;
            height: 0;
            position: absolute;
            top: 28px;
            left: 18px;
            border: 8px solid dashed;
            border-color: #fff transparent transparent;
        }
</style>
// HTML和JS代码
<div class="search">
<div class="con">123</div>
	<input type="text" placeholder="请输入您的快递单号" class="jd">
</div>
<script>
        // 快递单号输入内容时,上面的大号字体盒子(con)显示(这里面的字号更大)
        // 表单检测用户输入,给表单添加键盘事件
        // 同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)作为内容
        // 如果快递单号里面内容为空,则隐藏大号字体盒子(con)盒子
        var jd_input = document.querySelector('.jd');
        var con = document.querySelector('.con');
        jd_input.addEventListener('keyup',function () {
            // console.log('输入内容:');
            if (this.value == ''){
                con.style.display = 'none';
            }else{
                con.style.display = 'block';
                con.innerHTML = this.value;
            }
        })
        // 当我们失去焦点,就隐藏这个con盒子
        jd_input.addEventListener('blur',function() {
            con.style.display = 'none';
        })
        // 当我们获得焦点,就显示这个con盒子
        jd_input.addEventListener('focus',function() {
            if(this.value !== ''){
                con.style.display = 'block';
            }
        })
</script>

十七、BOM浏览器对象模型

1、BOM

​ 1.1、BOM

​ BOM(Browser Object Mode)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window。

​ BOM是由一系列相关的对象构成,并且每个对象都提供了很多方法与属性

​ BOM缺乏标准,javaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C,BOM最初是Netscape浏览器标准的一部分。

​ 1.2、BOM的构成

​ BOM比DOM更大,它包含DOM

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

​ 1.它是JS访问浏览器窗口的一个接口。

​ 2.它是一个全局对象,定义在全局作用域中的变量,函数都会变成window对象的属性和方法。

​ 在调用的时候可以省略window,前面学习的对话框都属于window对象方法,如alert()、prompt()等

注意:window下的一个特殊属性window.name

<script>
        function fn(){
            console.log(11);    
        }
        window.fn();
        console.log(window.name);
</script>
2、window对象的常见事件

​ 2.1、窗口加载事件

window.onload = function(){
    或者
    window.addEventListener("load",function() {})
};

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

注意:

​ 1.有了window.onload就可以把JS代码写到页面元素的上方,因为onclick是等页面内容全部加载完毕,再去执行处理函数。

​ 2.window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准。

​ 3.如果使用addEventListener则没有限制。

document.addEventListener('DOMContentLoaded',function () { })

​ DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。

​ IE9以上才支持

​ 如果页面的图片很多的话,从用户访问到onload触发可能需要较长时间,交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded事件比较合适。

​ 2.2、调整窗口大小事件

window.onresize = function(){}
window.addEventListener("resize",function() {});

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

​ 注意:

​ 1.只要窗口大小发生像素变化,就会触发这个事件。

​ 2.我们经常利用这个事件完成响应式布局,window.innerWidth当前屏幕的宽度

// css代码
<style>
    div{
        width: 200px;
        height: 200px;
        background-color: red;
    }
</style>
// HTML和JS代码
<script>
    window.addEventListener('load',function () {
        var div = document.querySelector('div');
        window.addEventListener('resize',function () {
            console.log(window.innerWidth);
            console.log('变化了');
            if(window.innerWidth <= 800){
                div.style.display = 'none';
            }else{
                div.style.display = 'block';
            }
         })
	})
</script>
<div></div>
3、定时器

​ 3.1、两种定时器

​ window对象给我们提供了2个非常好用的方法------定时器

​ (1)setTimeout()

​ (2)setInterval()

​ 3.2、setTimeout()定时器

window.setTimeout(调用函数,[延迟的毫秒数]);

​ setTimeout()方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。

​ setTimeout()这个调用函数我们也称为回调函数callback

​ 普通函数是按照代码顺序直接调用

​ 而这个函数,需要等待时间,时间到了才去调用这个函数,因此称为回调函数

​ 简单理解:回调,就是回头调用的意思,上一件事干完,再回头在调用这个函数

​ 以前我们讲的element.onclick = function(){}或者element.addEventListener("click",fn());里面的函数也是回调

注意:

​ 1.window可以省略

​ 2.这个调用函数可以直接写函数或者写函数名或者采取字符串,‘函数名()’三种形式,第三种不推荐

​ 3.延迟的毫秒数省略默认是0,如果写,必须是毫秒

​ 4.因为定时器可能有很多,所以我们经常给定时器赋值一个标识符

<script>
        // 综合写法
        window.setTimeout(function () {
            console.log('时间到了');
        },2000);
        // 分解写法
        function callback() {
            console.log('爆炸了');
        }
        // 页面中有很多定时器,我们经常给定时器加标识符(名字)
        // 这个调用函数可以直接写函数,还可以写函数名
        var time1 = setTimeout(callback,3000);
        // 还有一个写法'函数名()'
        var time2 = setTimeout('callback()',4000);  //我们不提倡这个写法
</script>
// 案例:5秒钟关闭照片
<img src="image/2.jpg" alt="">
    <script>
    var img = document.querySelector('img');
    setTimeout(function () {
        img.style.display = 'none';
    },5000);
</script>

​ 3.3、停止setTimeout()定时器

window.clearTimeout(timeout ID)

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

注意:

​ 1.window可以省略

​ 2.里面的参数就是定时器的标识符

// 案例:在定时器爆炸之前,点击按钮停止爆炸
<button>点击停止定时器</button>
<script>
    var btn = document.querySelector('button');
    var timer = setTimeout(function () {
        console.log('爆炸了');
    },5000);
    btn.addEventListener('click',function (){
        clearTimeout(timer);
    });
</script>

​ 3.4、setInterval()定时器

window.setInterval(回调函数,[间隔的毫秒数]);

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

// 语法规范:window.setInterval(回调函数,延迟时间);
setInterval(function() {
    console.log('继续输出');
},1000);
// setTimeout()与setInterval()区别:
// setTimeout()延时时间到了,就去调用这个回调函数,只调用一次,就结束了这个定时器
// setInterval()每隔这个延时时间,就去调用这个回调函数,会调用很多次,重复调用这个函数
// 案例:倒计时效果
// css代码
<style>
    div{
        float: left;
    }
	.hour,
    .minute,
    .second{
        display: block;
        float: left;
        width: 50px;
        height: 50px;
        margin: 5px;
        color: #fff;
        text-align: center;
        line-height: 50px;
        background-color: #000;
    }
</style>
//HTML和JS代码
<div>
    <span class="hour">1</span>
    <span class="minute">2</span>
    <span class="second">3</span>
</div>
<script>
        var hour = document.querySelector('.hour');
        var minute = document.querySelector('.minute');
        var second = document.querySelector('.second');
        var inputTime = +new Date('2021-01-01 18:00:00');
        // 我们先调用一次这个函数,防止第一次刷新页面有空白
        countDown();
        // 开启定时器
        setInterval(countDown,1000);
        function countDown(){
            var nowTime = +new Date();  //返回的是当前时间总得毫秒数
            var times = (inputTime - nowTime) / 1000;   //times是剩余总的毫秒数
            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>

执行结果:

​ 3.5、停止setInterval()定时器

window.setInterval(intervalID);

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

注意:

​ 1.window可以省略

​ 2.里面的参数就是定时器的标识符

// 案例:点击开启定时器按钮,控制台每一秒显示how are you,点击关闭定时器按钮,控制台停止运行
<button class="begin">开启定时器</button>
<button class="stop">关闭定时器</button>
<script>
    var begin = document.querySelector('.begin');
    var stop = document.querySelector('.stop');
    var timer = null;	//全局变量,null是一个空对象
    begin.addEventListener('click',function () {
        timer = setInterval(function () {
            console.log('how are you');
        },1000);
    });
    stop.addEventListener('click',function () {
        clearInterval(timer);
    });
</script>

​ 3.6、this

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

​ 现阶段,我们先了解一下几个this指向

​ 1.全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)

​ 2.方法调用中谁调用this指向谁

​ 3.构造函数中this指向构造函数的实例

<button></button>
<script>
        // this 指向问题:一般情况下this的最终指向的是那个调用它的对象
        // 1.全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
        function fn () {
            console.log(this);
            // 执行结果:this指向Window
        }
        window.fn();
        window.setTimeout(function () {
            console.log(this);
            // 执行结果:this指向Window
        },1000);
        // 2.方法调用中谁调用this指向谁
        var o = {
            sayHi: function () {
                console.log(this);
                // 执行结果:this指向o这个对象
            }
        }
        o.sayHi();
        var btn = document.querySelector('button');
        // btn.onclick = function () {
        //     console.log(this);
        //     // 执行结果:this指向btn这个按钮对象
        // }
        btn.addEventListener('click',function () {
            console.log(this);
            // 执行结果:this指向btn这个按钮对象
        })
        // 3.构造函数中this指向构造函数的实例
        function fun(){
            console.log(this);
            // 执行结果:this指向fun这个实例对象
        }
        var fun = new Fun();
</script>
5、JS执行机制

​ 5.1、JS是单线程

​ JavaScript语言的一大特点就是单线程,也就是说,同一时间只能做一件事,这是因为javasript这门脚本语言诞生的使命所致------JavaScript是为处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行,应该先进行添加,之后再删除。

​ 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

​ 5.2、同步和异步

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

同步

​ 前一个任务结束后再执行后一个任务,程序的执行顺序与人物的排列是一致的、同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

异步

​ 你在做一件事件时,因为这件事情会花费很长时间,在做这件事的同时,你还可以处理其他事情,比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

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

​ 5.3、同步任务和异步任务

同步任务

​ 同步任务都在主线程上执行,形成一个执行栈。

异步任务

​ JS的异步是通过回调函数实现的。

​ 一般而言,异步任务有三种类型:

​ 1.普通事件,如click、resize等

​ 2.资源加载,如load、error等

​ 3.定时器,包括setInterval、setTimeout等

​ 5.4、JS执行机制

​ 1.先执行执行栈中的同步任务。

​ 2.异步任务(回调函数)放入任务队列中。

​ 3.一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进行执行栈,开始执行。

!(C:\Users\钟敏华\Pictures\网络图片\chrome_EF7FbbXZac.png)

chrome_CTy7JmiTZg

<script>
        // 第一个问题
        console.log(1);
        setTimeout(function () {
            console.log(3);
            
        },1000);
        console.log(2);
        // 执行结果:123,输出3需要等1秒,所以先执行输出2的代码
        // 第二个问题
        console.log(1);
        setTimeout(function () {
            console.log(3);
            
        },0);
        console.log(2);
        // 执行结果:123,程序首先执行主线程执行栈(同步任务),因为function ()属于异步任务,最后在执行
</script>

console.log(1);
setTimeout(function () {
    console.log(3);

},0);
console.log(2);
// 执行结果:123,程序首先执行主线程执行栈(同步任务),因为function ()属于异步任务,最后在执行
console.log(1);
document.onclick = function () {
    console.log('click');
    // 该程序执行需要通过异步进程处理,只有点击才可以加入异步任务,然后传递到同步任务中
}
console.log(2);
setTimeout(function () {
    console.log(3);
    // 该程序执行需要通过异步进程处理,只有经过3秒之后才可以加入异步任务,然后传递到同步任务中
},3000);

</script>

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

6、location对象

​ 6.1、URL

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

​ URL的一般语法格式为:

​ 6.1、location对象的属性

​ 重点记住:href 和search

// 案例:5秒之后跳转到百度页面
<button>点击</button>
<div></div>
<script>
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.addEventListener('click',function () {
            location.href = 'http://www.baidu.com';
        })
        var time = 5
        setInterval(function () {
            if(time == 0){
                location.href = 'http://www.baidu.com';
            }else{
                div.innerHTML = '您在' + time + '秒钟之后跳转到首页';
                time --;
            }
            
        }, 1000);
</script>

案例:点击登陆按钮,跳转到主界面,显示用户登陆

// login.html代码
<form action="index.html">
        用户名:<input type="text" name="uname">
        <input type="submit" value="登陆">
</form>
// index.html代码
<div></div>
 <script>
            console.log(location.search);   //?uname = andy
            // 1.先去掉? substr('起始的位置',截取几个字符)
            var parms = location.search.substr(1);
            console.log(parms);
            // 2.利用=把字符串分割为数组split('=')
            var arr = parms.split('=');
            console.log(arr);
            var div = document.querySelector('div');
</script>

​ 6.2、location对象的方法

<button>点击</button>
<script>
        var btn = document.querySelector('button');
        btn.addEventListener('click',function () {
            // 记录浏览历史,所以可以实现后退功能
            location.assign('http://www.baidu.com');
            // 不记录浏览历史,所以不可以实现后退功能
            location.replace('http://www.baidu.com');
            // 重新加载页面,如果参数true,强制刷新ctrl+F5
            location.reload(true);
        })
</script>
7、navigator对象

​ navigator对象包含有关浏览器的信息,它有很多属性,我们最常用的是userAgent,该属性可以返回由客户机发送服务器的use-agent头部的值。

​ 下面前端代码可以判断用户那个终端打开页面,实现跳转

8、history对象

​ window对象给我们提供了一个history对象,与浏览器历史记录进行交互,该对象包含用户(在浏览器窗口中)访问过的URL。

十八、PC端网页特效

1、元素偏移量offset系列

​ 1.1、offset概述

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

​ (1)获得元素距离带有定位父元素的位置

​ (2)获得元素自身的大小(宽度高度)

​ (3)注意:返回的数值都不带单位

​ offset系列常用属性:

// css代码
<style>
        *{
            margin: 0;
            padding: 0;
        }
        .father{
            position: relative;
            width: 200px;
            height: 200px;
            background-color: red;
            margin: 100px;
        }
        .son{
            width: 100px;
            height: 100px;
            background-color: blue;
            margin: 50px;
        }
		.w{
            width: 200px;
            height: 200px;
            background-color: pink;
            margin: 100px;
        }
</style>
// HTML和JS代码
<div class="father">
	<div class="son"></div>
</div>
<script>
        // offset系列
        var father = document.querySelector('.father');
        var son = document.querySelector('.son');
        // 可以得到元素的偏移位置,返回的不带单位的数值
        console.log(father.offsetTop);	// 执行代码:100
        console.log(father.offsetLeft);	// 执行代码:100
        // 它以带有定位的父亲为准,如果没有父亲或者父亲没有定位,则以body为准
        console.log(son.offsetLeft);	// 执行代码:50
		// 2.可以得到的元素的大小宽度和高度,是包含padding + border + width
        var w = document.querySelector('.w');
        console.log(w.offsetWidth);
        console.log(w.offsetHeight);
        // 3.返回带有定位的父亲,否则返回的是body
        console.log(son.offsetParent);  // 返回带有定位的父亲,否则返回的是body
        console.log(son.parentNode);    // 返回父亲是最近一级的父亲,不管父亲有没有定位
</script>

​ 1.2、offset与style区别

案例:在盒子中移动鼠标显示鼠标在盒子中的位置

// css代码
<style>
        *{
            margin: 0;
            padding: 0;
        }
        .box{
            width: 200px;
            height: 200px;
            background-color: red;
            margin: 100px;
        }
</style>
// HTML和JS代码
<div class="box"></div>
<script>
        var box = document.querySelector('.box');
        box.addEventListener('mousemove',function (e) {
            var x = e.pageX - box.offsetLeft;
            var y = e.pageY - box.offsetTop;
            this.innerHTML = 'x坐标是' + x +'y坐标是' + y;
            
        })
</script>
2、元素可视区client系列

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

// css代码
<style>
    div{
        width: 200px;
        height: 200px;
        background-color: red;
        border: 2px solid blue;
        padding: 10px;
    }
</style>
// HTML和JS代码
<div></div>
<script>
        var div = document.querySelector('div');
        // 输出元素的宽度
        console.log(div.clientWidth);	// 执行结果:220
        // 输出元素上边框的大小
        console.log(div.clientTop);	// 执行结果:2
</script>

​ 2.1、立即执行函数

​ 写法:(1)(function () {})()

​ (2)(function() {} ())

​ 主要作用:创建一个独立的作用域,避免了命名冲突问题。

<script>
        // 1.立即执行函数:不需要调用,立马能够自己执行的函数
        // 第一种写法:
        (function (a,b) {
            console.log(a+b);
            
        })(1,2); // 第二个小括号可以看作调用函数
        // 第二种写法:
        (function (a,b) {
            console.log(a+b);
            
        }(2,3))
</script>
3、元素滚动scroll系列

​ 3.1、元素scroll系列属性

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

// css代码
<style>
    div{
        width: 200px;
        height: 200px;
        background-color: red;
        border: 10px solid blue;
        padding: 10px;
        overflow: auto;
    }
</style>
// HTML和JS代码
<div>
            我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容 我是内容
</div>
<script>
    // scroll 系列
    var div = document.querySelector('div');
    console.log(div.scrollHeight);	//执行结果:311
    console.log(div.clientHeight);	//执行结果:220
    // scroll滚动事件当我们滚动条发生变化会触发的事件
    div.addEventListener('scroll',function () {
        // 滑动滚动条内容移上去的多少
        console.log(div.scrollTop);		// 执行结果:91
    })
</script>

​ 3.2、页面被卷去的头部兼容性解决方案

​ 需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

​ 1.声明了DTD,使用document.documentElement.scrollTop

​ 2.未声明DTD,使用document.body.scrollTop

​ 3.新方法window.pageYoffset和window.pageXoffset,IE9开始支持

4、三大系列总结

​ 他们主要用法:

​ 1.offset系列经常用于获得元素位置 offsetLeft offsetTop

​ 2.client经常用于获取元素大小 clientWidth clientHeight

​ 3.scroll经常用于获取滚动距离 srcollTop scrollLeft

​ 4.注意页面滚动的距离通过window.pageXoffset获得

5、mouseenter和mouseover的区别

mouseenter鼠标事件

​ (1)当鼠标移动到元素上时就会触发mouseenter事件

​ (2)类似mouseover,它们两者之间的差别是:mouseover鼠标经过自身盒子会触发,经过自盒子还会触发,mouseenter只会经过自身盒子触发

​ (3)之所以这样,就是因为mouseenter不会冒泡

​ (4)跟mouseenter搭配鼠标离开mouseleave同样不会冒泡

6、动画函数封装

​ 6.1、动画实现原理

​ 核心原理:通过定时器setInterval()不断移动盒子位置。

​ 实现步骤:

​ 1.获得盒子当前位置

​ 2.让盒子在当前位置加上1个移动距离

​ 3.利用定时器不断重复这个操作

​ 4.加一个结束定时器的条件

​ 5.注意此元素需要添加定位,才能使用element.style.left

// css代码
<style>
    div{
        position: absolute;
        left: 0;
        width: 100px;
        height: 100px;
        background-color: yellow;
    }
</style>
// HTML和JS代码
<div></div>
<script>
    var div = document.querySelector('div');
	var timer = setInterval(function () {
        // 当盒子当前的位置大于等于400px时
        if (div.offsetLeft >= 400){
            // 停止动画本质是停止定时器
            clearInterval(timer);
        }
        // 将获得的盒子位置+1px
        div.style.left = div.offsetLeft + 1 + 'px';
	},30);
</script>

​ 6.2、动画函数简单封装

​ 注意函数需要传递2个参数,动画对象移动到的距离

// css代码
<style>
    div{
        position: absolute;
        left: 0;
        width: 100px;
        height: 100px;
        background-color: yellow;
    }
    span{
        position: absolute;
        left: 0;
        top: 200px;
        width: 200px;
        height: 200px;
        background-color: blue;
    }
</style>
// HTML和JS代码
<div></div>
<span></span>
<script>
    var div = document.querySelector('div');
    var span = document.querySelector('span');
	// obj是动画对象参数,target是移动到的距离参数
    function fn(obj, target){
        var timer = setInterval(function () {
            // 当盒子当前的位置大于等于400px时
            if (obj.offsetLeft >= target){
                // 停止动画本质是停止定时器
                clearInterval(timer);
            }
            // 将获得的盒子位置+1px
            obj.style.left = obj.offsetLeft + 1 + 'px';
        },30);
    }
fn(div,300);
fn(span,200);

​ 6.3、缓动效果原理

​ 缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来

​ 思路:

​ 1.让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。

​ 2.核心算法:(目标值 - 现在的位置) / 10 作为每次移动的距离步长。

​ 3.停止的条件是:让当前盒子位置等于目标位置就停止定时器。

// css代码
<style>
    span{
        position: absolute;
        left: 0;
        top: 200px;
        width: 200px;
        height: 200px;
        background-color: blue;
    }
</style>
// HTML和CSS代码
<button class="btn1">点击开始span</button>
<button class="btn2">点击开始span</button>
<span></span>
<script>
    var span = document.querySelector('span');
    var btn1 = document.querySelector('.btn1');
    var btn2 = document.querySelector('.btn2');
    function fn(obj, target){
        // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器
        // 解决方法就是:让我们元素只有一个定时器执行
        // 先清除以前的定时器,只保留当前的一个定时器执行
        clearInterval(obj.timer);
        obj.timer = setInterval(function () {
            // 将步长值写到定时器的里面
            // 把步长值改为整数,不要出现小数的问题
            var step = (target - obj.offsetLeft) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            // 当盒子当前的位置大于等于400px时
            if (obj.offsetLeft == target){
                // 停止动画本质是停止定时器
                clearInterval(timer);
            }
            // 把每次+1这个步长值改为一个慢慢变小的值,步长公式:(目标值 - 现在的位置) / 10
            obj.style.left = obj.offsetLeft + step + 'px';
        },15);
    }
    btn1.addEventListener('click',function () {
        fn(span,500);
    })
    btn2.addEventListener('click',function () {
        fn(span,800);
    })
    // 匀速动画就是盒子是当前的位置 + 固定的值 10
    // 匀速动画就是盒子当前的位置 + 变化的值(目标值 - 现在的位置) / 10

</script>

十九、移动端网页特效

1、触屏事件

​ 1.1、触屏事件概述

​ 移动端浏览器兼容性较好,我们不需要考虑以前JS的兼容性问题,可以放心使用原生JS书写效果,但是移动端也有自己独特的地方。比如触屏事件touch(也称触摸事件),Android和IOS都有。

​ touch对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。

// css代码
<style>
    div{
        width: 100px;
        height: 100px;
        background-color: red;
    }
</style>
// HTML和CSS代码
<div></div>
<script>
        // 1.获取元素
        var div = document.querySelector('div');
        // 2.手指触摸DOM元素事件
        div.addEventListener('touchstart',function () {
            console.log('我点了你');
            // 当手指点击盒子控制台输出我点了你
        })
        // 3.手指在DOM元素身上移动事件
        div.addEventListener('touchmove',function () {
            console.log('我继续点');
            // 当手指继续点击盒子控制台输出我继续点
        })
        // 4.手指离开DOM元素事件
        div.addEventListener('touchend',function () {
            console.log('轻轻的我走了');
            // 当手指松开点击的盒子控制台输出轻轻的我走了
        })
</script>

​ 1.2、触屏事件对象(TouchEvent)

​ TouchEvent是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发可以检测触点的移动,触电的增加和减少,等等

​ touchstart、touchmove、touchend三个事件都会各自有事件对象。

​ 触摸事件对象重点我们看三个常见对象列表:

注意:因为平时我们都是给元素注册触摸事件,所以重点记住targetTouches

// css代码
<style>
    div{
        width: 100px;
        height: 100px;
        background-color: red;
    }
</style>
// HTML和CSS代码
<div></div>
<script>
    // 1.获取元素
    var div = document.querySelector('div');
    // 2.手指触摸DOM元素事件
    div.addEventListener('touchstart',function (e) {
        console.log(e);
        // touches 正在触摸屏幕的所有手指的列表
        // targetTouches 正在触摸当前DOM元素的手指列表
        // 如果侦听的是一个DOM元素,他们两个是一样的 
        // changedTouches 手指状态发生改变的列表 从无到有 或 从有到无
        // 因为我们一般都是触摸元素,所以经常使用的是targetTouches
        console.log(e.targetTouches[0]);
        // targetTouches[0]就可以得到正在触摸dom元素的第一个手指的相关信息比如:手指的坐标
    })
    // 3.手指在DOM元素身上移动事件
    div.addEventListener('touchmove',function () {


    })
    // 4.手指离开DOM元素事件
    div.addEventListener('touchend',function (e) {
        console.log(e);
        // 当我们手指离开屏幕的时候,就没有了touches和targetTouches列表
        // 但是会有changedTouches
})
</script>

​ 1.3、移动端拖动元素

​ 1、touchstart、touchmove、touchend可以实现拖动元素

​ 2、但是拖动元素需要当前手指的坐标值我们可以使用targetTouches[0]里面的pageX和pageY

​ 3、移动端拖动的原理:手指移动中,计算出手指移动的距离,然后用盒子原来的位置 + 手指移动的距离

​ 4、手指移动的距离:手指滑动中的位置减去手指刚开始触摸的位置

拖动元素的三部曲:

​ (1)触摸元素touchstart:获取手指初始坐标,同时获得盒子原来的位置

​ (2)移动手指touchmove:计算手指的滑动距离,并且移动盒子

​ (3)离开手指touchend

注意:手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动e.preventDafault();

posted @ 2020-12-16 21:37  因为遇见你  阅读(44)  评论(0编辑  收藏  举报