1. 程式人生 > 其它 >Debug日記:為什麼神經網路測試精度遠低於驗證精度??都是模式“P”惹的禍!!

Debug日記:為什麼神經網路測試精度遠低於驗證精度??都是模式“P”惹的禍!!

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)

<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();