javascript

参考自后盾人 链接

1、变量提升问题

结论:变量声明升,函数整体提升

 console.log(a);
 var a= 10;

这里并不会报错,由于变量提升,它相当于

var a;
console.log(a);
a=10;

再举个例子

test();
function test() {
 console.log(a);
   if (false) {
       var a = '123123';
   }
}

输出结果为undefined 就算不执行if语句但是也会有变量提升,函数也不会报错。

为了避免变量提升带来的问题,我们可以使用es6的新特性 const let。
用const let 声明的变量不会出现这样的问题。

这里可以正常输出a的值

 let a = 1
 function test(){
    console.log(a);
 }
  test()

下面报错,这是TDC临时死区的特点。必须先声明再使用

 let a = 1;
 function test(){
    console.log(a);
    let a=2;
 }
  test()

在这里插入图片描述

2、块作用域的先进特性

引入了别的js,会造成变量的污染。为了解决这个问题以往都采用下面的方法。

1.js的内容

在这里插入图片描述
利用函数作用域来避免变量的污染。
在这里插入图片描述
1.js还可以采用块作用域的方法
在这里插入图片描述
结果相同,但是注意使用块作用域时。要使用let或者const声明变量。

3、window全局对象污染与重复声明

使用var变量声明时,会添加到window下面
例如

var a = 1;
console.log(window.a); //打印结果为 1
var screenLeft = 88;
console.log(window.screenLeft); //本来screenLeft会输出窗口距离屏幕左侧的距离,但是这样不管怎么拖拽窗口改变,它都会输出8。因为它被var给覆盖了。

这是var的缺点 但是let 和 const不会出现这种问题

const a = 1;
console.log(window.a) // undefined

再来讨论重复声明的问题
在同一作用域 var 可被多次声明,let 和const 则会报错

var a=1;
var a=2;//浏览器不会报错

let b=1;
let b=2;//浏览器会报错

在这里插入图片描述

4、typeof 和 instanceof

typeof是判断类型的,instanceof是判断是否在原型链上

 	   const array =[1,2,3];
       console.log(typeof array); //object
       const obj = {name: 'libai'}; 
       console.log(typeof obj); //object
       console.log(array instanceof Array); //true
       console.log(array instanceof Object); //true
       console.log(obj instanceof Array); //false

5、 for in 和 for of

       const str ='我是无敌的';
       for( let value of str){
            console.log(value);
       }
       for(let key in str){
           console.log(str[key]);
       }

在这里插入图片描述
for in 遍历出的可以为索引,for of遍历出的值为内容。字符串,数组,对象同理。

6、字符串的截取、搜索、替换与数组转换

字符串的截取

      const str = 'abcdefghijklmn'
      console.log(str.slice(0,2)); //从第0位开始截取到第2位(不包括第二位)
      console.log(str.substring(0,2));//同上
      console.log(str.substr(1,2)); //从第一位开始 截取2个

字符串的搜索

      const str = 'abcdefghijklmbn'
      console.log(str.indexOf('b')); //1 从前面开始找 第几位 找不到返回-1
      console.log(str.lastIndexOf('b'));//13 从后开始找 找不到返回-1
      console.log(str.includes('abc')); //true 判断是否包含'abc'返回布尔值

字符串的替换

把 我喜欢在b站学习js和css 中的js和css替换成链接

        const word = ['js','css'];
        const str = '我喜欢在b站学习js和css';
		//pre为前一个值(这里是指 '我喜欢在b站学习js和css'),word为遍历的值(这里为'js','css')
        const replaceString= word.reduce((pre,word)=>{ 
          console.log(pre,word);
          return pre.replace(word,`<a href='#'>${word}</a>`)
        },str) //这里的str是初始值
        document.body.innerHTML = replaceString
      </script>

下面的截图为输出的结果 可以清楚的看出pre和word在循环中的变化
在这里插入图片描述
字符串转换为数组,数组转换为字符串

        const str = "abcd,efg";
        console.log(str.split(",")); //从哪里切割
        const array = ['abc','zdsf'];
        console.log(array.join('|'));//用什么隔开

在这里插入图片描述

7、隐式boolean转换问题

空字符串和空数组转换为布尔值都为真

const a =[];
const b ={};
Boolean(a); //true
Boolean(b); //true

const c = 99;
if(c){ console.log("进入了循环");} //可以进入
console.log(c==true) //false 这里会把true转换为数值型为1 99==1 所以false

const d = [1];
console.log(d==true); //true 这里同样会都转换为数值型 Number(d)的值为1 true为1 所以相同 
const e= [2]; //Number的值为2
console.log(e==true) //false 
if(e){}//会进入循环

const f = [1,2] //Number(d)为NaN

由此可以得知,判断和转换的值不一定相同

8、Math常用函数

        const number = 99;
        console.log(Number.isInteger(number)); //true 判断是否为整数
        const number1 = 99.65;
        console.log(number1.toFixed(1)); //99.7 保留几位小数四舍五入 得到的值为字符串类型
        console.log(Math.round(number1)); // 100 四舍五入取整
        console.log(Math.ceil(number1)); //向上取整 100
        console.log(Math.floor(number1));//向下取整99

        Math.random(); //随机数取值为 0<= ... <1
        //随机取值 1<= x <=10 min+Math.floor(Math.random()*(max-min+1))
        console.log(1+Math.floor(Math.random()*(10-1+1)))

9、日期类型

        const date1 = Date();//object
        const date = new Date(); //string
        console.log(date.getMonth()+1); //得到的月份从0开始
        console.log(date.getFullYear()); //得到年份
        console.log(date.getDay()); //得到日
        console.log(date.getHours()+"  "+date.getMinutes()+ " " +date.getSeconds())
        console.log(date*1) //得到的是时间戳
        console.log(date.valueOf()) //得到的是时间戳
        console.log(date.getTime()) //得到的是时间戳
        console.log(Number(date)) //得到的是时间戳
        const timeStamp = date.getTime();
        console.log(new Date(timeStamp));//时间戳转换为标准时间

10、数组的常用方法

向数组中添加元素、删除元素、截取。。。

push方法

        const array = [1,2,3];
        array.push(1,3,4)//向数组的末尾追加,可以追加多个
        console.log(array); //[ 1, 2, 3, 1, 3, 4 ]

unshift方法

        const array = [1,2,3];
        array.unshift(7,8,9)//向数组前面追加,同样可以追加多个
        console.log(array); //[ 7, 8, 9, 1, 2, 3 ]

pop方法 删除最后一个元素

        const array = [1,2,3];
        array.pop();
        console.log(array);//[1,2]

slice方法 截取数组元素 不改变原数组

        const array = [1,2,3];
        const array2 = array.slice(1,2) //从第几个截取到第几个
        console.log(array2); //[2]
        console.log(array); //[1,2,3]

splice方法 同样是截取数组元素 但是它会改变原数组。因此可以利用它来实现数组的增删改。非常的强大。

        const array = [1,2,3];
        const array2 = array.slice(1,2) //从第几个开始 截取几个
        console.log(array2); //[2]
        console.log(array); //[1,2,3]

上面的例子就等同于删除操作,从下标为一个元素开始删除两个。下面的例子演示增加和修改

        const array = [1,2,3];
        array.splice(1,0,1000)//增加操作 在下标为1的地方增加一个元素1000
        console.log(array); //[1,1000,2,3];
        //接下来把1000改成 2000
        array.splice(1,1,2000)
        console.log(array); //[ 1, 2000, 2, 3 ]
        //最后删除2000
        array.splice(1,1);
        console.log(array); //[ 1, 2, 3 ]

es6的新特性

结构赋值简化了赋值操作

        const array = ['张三','李四','王五'];
        const [name1,name2,name3] = array;
        console.log(name1,name2,name3); //张三 李四 王五

结构赋值与…结合使用

        const array = ['张三','李四','王五'];
        const [name1,...names] = array;
        console.log(name1,names); //张三  Array [ "李四", "王五" ]
//把数组的第一个元素为name1,由于...的存在 它会将剩余的所有元素都塞给names

…展开符使用

        const array = ['张三','李四','王五'];
        const array2 = ['赵柳'];
        array3 = [...array,...array2]//相当于 array3 = ['张三','李四','王五','赵柳'] 就是将数组或对象展开
        console.table(array3);

        array3 = [...array,array2]
        console.log(array3);//Array(4) [ "张三", "李四", "王五", (1) […] ] 

数组的查找 和字符串类似。indexOf lastIndexOf includes 等

find方法查找 传入一个函数 返回值为true则返回 该元素 只查找第一个符合条件的元素

       const array = [{id:1,name:'张三'},{id:2,name:'李四'},{id:3,name:'王五'}];
       const obj =  array.find(item=> item.id===3); //find 如果函数值为true则返回
       console.log(obj);

如果要查找符合条件的元素的索引 则可使用 findIndex使用方法与find类似只是返回结果不同。

数组的排序

sort方法

升序

       const array = [
           {price:1000, product: '手机'},
           {price:500, product: '电风扇'},
           {price:25000, product: '电脑'},
           {price:300, product: '鼠标'},
           {price:400, product: '键盘'},
        ];
       const newArray = array.sort(function(a,b) {
           return a.price-b.price
       })
       console.log(newArray);

数组的迭代器

keys() 返回的是 索引和当前是否迭代完成
values() 返回的是 当前的值和是否迭代完成
entries() 返回的是 [索引 ,值] 和是否迭代完成

       const array = [
           {price:1000, product: '手机'},
           {price:500, product: '电风扇'},
           {price:25000, product: '电脑'},
           {price:300, product: '鼠标'},
           {price:400, product: '键盘'},
        ];
       console.log(array.keys().next()); // {value: 0, done: false}
       console.log(array.values().next()); // {value: {price:1000, product:'手机'}, done: false}
       console.log(array.entries().next());// {value: [0,{price:1000 , product: '手机'}], done: false}

可以利用迭代器进行循环搭配数组的结构赋值

       const array = [
           {price:1000, product: '手机'},
           {price:500, product: '电风扇'},
           {price:25000, product: '电脑'},
           {price:300, product: '鼠标'},
           {price:400, product: '键盘'},
        ];
       for(let [key,value] of array.entries()){
           let {price, product} = value
           console.log(key,value);
           console.log(price, product);
       }

在这里插入图片描述
foreachmap就省略了。
filter 过滤 只返回为true的内容

reduce方法

//找出大于200块钱的商品
       const array = [
           {price:1000, product: '手机'},
           {price:500, product: '电风扇'},
           {price:25000, product: '电脑'},
           {price:300, product: '鼠标'},
           {price:400, product: '键盘'},
        ];
      const mun = array.reduce((pre, cur)=>{
            if(cur.price>200){
               return [...pre,cur]
            }else{
                return pre
            }
      },[])
      console.log(mun);

some 和 every
every和some比较类似 像是 &&和||
every是只要函数返回值有一个false就返回false 只有全部为true才返回true
some是只要有一个true就返回true

every

       const array = [
           {name:'张三', score: 88},
           {name:'李四', score: 98},
           {name:'王五', score: 55},
           {name:'赵柳', score: 96},
           {name:'宋琪', score: 67},
           {name:'张久', score: 74},
        ];
        const flag= array.every((item)=> item.score>=60);
        flag? console.log("全都及格了") : console.log("有人没及格");
        //结果是有人没及格 ,因为王五的成绩是55 判断到王五时返回false 不继续判断后面的成绩

every是&& some是|| some不写例子了

11、Symbol是什么

Symbol 本质上是一种唯一标识符,可用作对象的唯一属性名,这样其他人就不会改写或覆盖你设置的属性值。
声明方法:

let id = Symbol("id“);

Symbol 数据类型的特点是唯一性,即使是用同一个变量生成的值也不相等。

let id1 = Symbol(‘id’);
let id2 = Symbol(‘id’);
console.log(id1 == id2); //false

 let id1 = Symbol('id');
 let id2 = Symbol('id');
 console.log(id1 == id2);  //false

Symbol 数据类型的另一特点是隐藏性,for···in,object.keys() 不能访问

let id = Symbol(“id”);
let obj = {
[id]:‘symbol’
};
for(let option in obj){
console.log(obj[option]); //空
}

 let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
 for(let option in obj){
     console.log(obj[option]); //空
 }

但是也有能够访问的方法:Object.getOwnPropertySymbols
Object.getOwnPropertySymbols 方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

let id = Symbol(“id”);
let obj = {
[id]:‘symbol’
};
let array = Object.getOwnPropertySymbols(obj);
console.log(array); //[Symbol(id)]
console.log(obj[array[0]]); //‘symbol’

 let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
let array = Object.getOwnPropertySymbols(obj);
 console.log(array); //[Symbol(id)]
 console.log(obj[array[0]]);  //'symbol'

虽然这样保证了Symbol的唯一性,但我们不排除希望能够多次使用同一个symbol值的情况。
为此,官方提供了全局注册并登记的方法:Symbol.for()

let name1 = Symbol.for(‘name’); //检测到未创建后新建
let name2 = Symbol.for(‘name’); //检测到已创建后返回
console.log(name1 === name2); // true

 let name1 = Symbol.for('name'); //检测到未创建后新建
 let name2 = Symbol.for('name'); //检测到已创建后返回
 console.log(name1 === name2); // true

通过这种方法就可以通过参数值获取到全局的symbol对象了,反之,能不能通过symbol对象获取到参数值呢?
是可以的 ,通过Symbol.keyFor()

let name1 = Symbol.for(‘name’);
let name2 = Symbol.for(‘name’);
console.log(Symbol.keyFor(name1)); // ‘name’
console.log(Symbol.keyFor(name2)); // ‘name’

 let name1 = Symbol.for('name');
 let name2 = Symbol.for('name');
 console.log(Symbol.keyFor(name1));  // 'name'
 console.log(Symbol.keyFor(name2)); // 'name'

最后,提醒大家一点,在创建symbol类型数据 时的参数只是作为标识使用,所以 Symbol() 也是可以的。

12、Set weakSet Map

set与数组很相似,但是set里面不能放入相同的数据

	//set的两种声明方式
        const set = new Set();
        const set2 = new Set([1,2,3,'1']);
        set.add("张三"); //向set中添加元素
        set.add("李四");
        
        set2.delete(2) //返回值为 boolean
        set2.clear(); //清空set
        set2.has(1); //检查是否存在某元素 返回值为boolean
        set2.size //长度
        console.log(set);
        console.log(set2);
        

有些情况数组能处理,而set处理起来很麻烦。有时候set能处理的,数组有不太容易。这种情况我们就需要让数组和set进行互相帮助,完成需求再转换回来。完成任务。

比如说要把set中工资大于10000的人给筛选出来

        let set2 = new Set([
            {name: '张三', money: 100000},
            {name: '李四', money: 10500},
            {name: '王五', money: 7190},
            {name: '赵柳', money: 5010},
        ]);
        let setArray = Array.from(set2).filter(set=> set.money>10000);
        set2 = new Set(setArray);
        console.log(set2);
        

在这里插入图片描述

在比如说,要把数组中的重复元素去除。这里就可以运用set的元素不能重复的特性

        let array = [1,2,415,1,2,3,58,9];
        const set = new Set(array);
        array = [...set];
        console.log(array);
        //Array(6) [ 1, 2, 415, 3, 58, 9 ]

遍历可以用forEach 和 for of
weakSet
添加add 删除delete 是否存在 has 与Set类型类似

1、WeakSet的成员只能是对象,不能是其他数据类型,否则会报错;

2、WeakSet的成员都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。

3、由于WeakSet的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。

4、没有clear()方法

5、不能遍历
Map

map的键可以采用任何类型,和对象不同。对象的键只能是字符串。

//第一种声明方式 
        const map = new Map();
        const obj = {
            id: 1,
            name:'杜甫'
        }
        map.set('姓名','李白'); //键为字符串
        map.set(1,'13214'); //键为数值型
        map.set(obj, '苏轼'); //键为对象
//也可以采用map的构造函数
        const map2 = new Map([['name','张飞'],['name2','刘备']]);
//通过get方法取值
		 console.log(map2.get('name')); //张飞

与set也颇为类似 has delete clear它都有

13、this的指向 和 bind apply call

全局环境下this就是window对象的引用

使用严格模式时在全局函数内this为undefined

bind apply call都用于改变this的指向。其中apply call会自动调用。

        function show (car){
            console.log(this.name,car);
        }
        let wangwu ={
            name: '王五'
        }
        let lisi ={
            name :  '李四'
        }
        show.apply(wangwu,['奔驰'])//前面是this的指向,后面是参数apply要用数组传 
        show.call(lisi, '宝马')//call与apply 第二个参数不用数组

call改变函数this的指向

    <button message='诗仙'>李白</button>
    <button message='诗圣'>杜甫</button>
    <script>
        function show (){
            console.log(this.getAttribute('message'));//此时的this指向window
        }
        const btns = document.querySelectorAll('button');
        btns.forEach(function(btn){
            btn.addEventListener('click',function(){
                show.call(btn); 
            })
        })
    </script>
    //这样就改变了this的指向,点击李白打印诗仙。点击杜甫,打印诗圣

bind
bind()是将函数绑定到某个对象,比如 a.bind(hd) 可以理解为将a函数绑定到hd对象上即 hd.a()。

与 call/apply 不同bind不会立即执行
bind 是复制函数形为会返回新函数

        const obj = {
            name: '李明'
        }
        function test(score){
            this.score = score;
            console.log(this.name);
        }
        func = test.bind(obj);
        func(99);
        console.log(obj.score);

在回调函数中使用

        let phone = {
            name: 'apple',
            price: 8000
        }
        setInterval(function(){
            console.log(this.name);
        }.bind(phone),500)

14、作用域

全局作用域只有一个,每个函数又都有作用域(环境)。

编译器运行时会将变量定义在所在作用域

使用变量时会从当前作用域开始向上查找变量

作用域就像攀亲亲一样,晚辈总是可以向上辈要些东西

作用域链只向上查找,找到全局window即终止,应该尽量不要在全局作用域中添加变量。

函数每次调用都会创建一个新作用域

        let site = '后盾人';

		function a() {
		  let hd = 'houdunren.com';
		
		  function b() {
		      let cms = 'hdcms.com';
		      console.log(hd);
		      console.log(site);
		  }
		  b();
		}
		a();

如果子函数被使用时父级环境将被保留

		function hd() {
		  let n = 1;
		  return function() {
		    let b = 1;
		    return function() {
		      console.log(++n);
		      console.log(++b);
		    };
		  };
		}
		let a = hd()();
		a(); //2,2
		a(); //3,3

构造函数也是很好的环境例子,子函数被外部使用父级环境将被保留

		function User() {
		  let a = 1;
		  this.show = function() {
		    console.log(a++);
		  };
		}
		let a = new User();
		a.show(); //1
		a.show(); //2
		let b = new User();
		b.show(); //1

15、闭包

函数能访问到其他作用域的函数就称为闭包
下面这个例子是给数组排序,可以通过向term中传送排序的条件。

        function term(attr){ //这个函数就是闭包 子函数使用了父函数中的attr
            return (a,b)=>{
                return a[attr]>b[attr]?1:-1; //因为参数为字符串所以只能用[]来获取,不能用a.attr的方式
            }
        }
        const products = [
            {
                name: '苹果',
                price: 5,
                count: 560
            },{
                name: '西瓜',
                price: 3.5,
                count:98
            },{
                name: '香蕉',
                price: 4.5,
                count: 500
            },{
                name: '橘子',
                price: 6,
                count: 1000
            }
        ];
        console.log( products.sort(term('count')) )

在这里插入图片描述

16、对象的复制

        const phone ={
            name: '三星',
            price: 2000
        };
        const flag=phone.hasOwnProperty("name"); //true 判断对象中是否存在该属性
        console.log(phone.hasOwnProperty("toString")); //false 此方法是检查自身有没有该属性
        console.log('toString' in phone); //true 这样是写包括它的原型

Object.assign()
从一个或多个对象复制属性

        const phone ={
            name: '三星',
            price: 2000
        };
        const newPhone =  Object.assign(phone,{count: '600'});
        console.log(newPhone);

Object.key() 获取对象中所有的键
Object.values() 获取对象中所有的值

浅拷贝
使用浅拷贝时如果被拷贝的对象中含有引用类型时,则不能完全复制引用类型到新对象中,只能传址。当修改其中一个对象中的引用类型时,另一个对象中的该值也同样会变化。

        const phone ={
            name: '三星',
            price: 2000,
            style:{
                color: 'red',
                size: 'small'
            }
        };
        const newPhone = {};
        // const newPhone= Object.assign({},phone);
        // const newPhone = {...phone}
        for(let key of Object.keys(phone)){
            newPhone[key] = phone[key]
        }
        console.log(newPhone);

在这里插入图片描述
当修改newPhone的引用类型时

        const phone ={
            name: '三星',
            price: 2000,
            style:{
                color: 'red',
                size: 'small'
            }
        };
        const newPhone = {};
        // const newPhone= Object.assign({},phone);
        // const newPhone = {...phone}
        for(let key of Object.keys(phone)){
            newPhone[key] = phone[key]
        }

        newPhone.style.color = 'blue'; //这里改变newPhone的引用类型的值
        console.log(phone); //phone中的值也变了

在这里插入图片描述

深拷贝
深拷贝则不会出现浅拷贝的问题,它会把对象中的引用类型也完完全全的拷贝一份。修改一个对象的引用类型时,另一个对象不受影响。

        function copy(object) {
           let obj = object instanceof Array ? []:{};
           for(let key of Object.keys(object)){
               if( typeof object[key] =='object'){
                    obj[key] = copy(object[key]);
               }else{
                    obj[key] = object[key];
               }
           }
           return obj;
        }
        const phone = {
            name: '三星',
            price: 2000,
            style: {
                color: 'red',
                size: 'small'
            }
        };
        const newPhone = copy(phone)
        newPhone.style.color = 'blue'
     
        console.log(phone);//改变newPhone的引用类型 phone的内容不变化

17、内置构造

为什么定义一个字符或者数值都能使用方法或者属性?
因为每当你const num = 1 或者 const str ='abc’时,都是通过构造器定义的。
都相当于

const num = new Number(99);
console.log(num.valueOf());

const string = new String("后盾人");
console.log(string.valueOf());

const boolean = new Boolean(true);
console.log(boolean.valueOf());

const date = new Date();
console.log(date.valueOf() * 1);

const regexp = new RegExp("\\d+");
console.log(regexp.test(99));

let hd = new Object();
hd.name = "后盾人";
console.log(hd);

字面量创建的对象,内部也是调用了Object构造函数

const hd = {
  name: "后盾人"
};
console.log(hd.constructor); //ƒ Object() { [native code] }

//下面是使用构造函数创建对象
const hdcms = new Object();
hdcms.title = "开源内容管理系统";
console.log(hdcms);

18、对象的属性特征

使用 Object.getOwnPropertyDescriptor查看对象属性的描述。

        const phone = {
            name: '三星',
            price: 2000,
            style: {
                color: 'red',
                size: 'small'
            }
        };
        console.log(Object.getOwnPropertyDescriptor(phone,'name'));

在这里插入图片描述
使用 Object.getOwnPropertyDescriptors查看对象所有属性的描述

        const phone = {
            name: '三星',
            price: 2000,
            style: {
                color: 'red',
                size: 'small'
            }
        };
        console.log(Object.getOwnPropertyDescriptors(phone));

在这里插入图片描述
在这里插入图片描述
设置特性
使用Object.defineProperty 方法修改属性特性,通过下面的设置属性name将不能被遍历、删除、修改。
使用 Object.defineProperties 可以一次设置多个属性,具体参数和上面介绍的一样。

Object.preventExtensions 禁止向对象添加属性
Object.isExtensible 判断是否能向对象中添加属性

Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为 configurable: false
Object.isSealed 如果对象是密封的则返回 true,属性都具有 configurable: false。

Object.freeze 冻结对象后不允许添加、删除、修改属性,writable、configurable都标记为false
Object.isFrozen()方法判断一个对象是否被冻结

详细可见后盾人
https://houdunren.gitee.io/note/js/10%20%E5%AF%B9%E8%B1%A1.html#%E8%AE%BE%E7%BD%AE%E7%89%B9%E5%BE%81

19、get set 属性访问器

getter方法用于获得属性值,setter方法用于设置属性,这是JS提供的存取器特性即使用函数来管理属性。

  • 用于避免错误的赋值
  • 需要动态监测值的改变
  • 属性只能在访问器和普通属性任选其一,不能共同存在
        const data = Symbol();
        const phone = {
            
            [data]:{
                name: '华为nova3',
                price: 2000
            },
            set name(value){
                this[data].name = value
            },
            get name(){
                return this[data].name
            }
        };
        phone.name= 'ipone';
        console.log(phone.name); //iphone 
        console.log(phone.data); //undefined

20、proxy代理

代理(拦截器)是对象的访问控制,setter/getter 是对单个对象属性的控制,而代理是对整个对象的控制。

对象的代理

        "use strict";
        const hd = { name: "后盾人" };
        const proxy = new Proxy(hd, {
            get(obj, property) {
                return obj[property];
            },
            set(obj, property, value) {
                obj[property] = value;
                return true;
            }
        });
        proxy.age = 10;
        console.log(hd);  

函数的代理

如果代理以函数方式执行时,会执行代理中定义 apply 方法。

参数说明:函数,上下文对象,参数

下面使用 apply 计算函数执行时间

function factorial(num) {
  return num == 1 ? 1 : num * factorial(num - 1);
}
let proxy = new Proxy(factorial, {
  apply(func, obj, args) {
    console.time("run");
    func.apply(obj, args);
    console.timeEnd("run");
  }
});
proxy.apply(this, [1, 2, 3]);

vue的双向绑定

        "use strict";
        const obj = {};
        const proxy = new Proxy(obj, {
            get(obj, property) {
            },
            set(obj, property, value) {
            	obj[property] = value;
                document.querySelectorAll(`[v-model=${property}]`).forEach(el=>{
                    el.value = value
                })
                document.querySelectorAll(`[v-bind=${property}]`).forEach(el=>{
                    el.innerHTML = value
                })
                return true
            }
        });
        const els = document.querySelectorAll('[v-model]');
        els.forEach(el => {
            el.addEventListener('keyup', function () {
                proxy[el.getAttribute('v-model')] = el.value;
            })
        })

21、JSON

  • json 是一种轻量级的数据交换格式,易于人阅读和编写。
  • 使用json 数据格式是替换 xml 的最佳方式,主流语言都很好的支持json 格式。所以 json 也是前后台传输数据的主要格式。
  • json 标准中要求使用双引号包裹属性,虽然有些语言不强制,但使用双引号可避免多程序间传输发生错误语言错误的发生。

js转json JSON.stringify()

  • 第一个是目标数组或对象
  • 根据第二个参数指定保存的属性
  • 第三个是参数用来控制TAB数量,如果字符串则为前导字符。
    <script>
        const obj = {
            name: '张三',
            score: 99,
            hobby:[
               '足球','篮球','打游戏'
            ]
        }
        const json = JSON.stringify(obj,null,2);
        console.log(json);

在这里插入图片描述
可以为数据添加 toJSON 方法来自定义返回格式
例子来自后盾人

let hd = {
    "title": "后盾人",
    "url": "houdunren.com",
    "teacher": {
        "name": "向军大叔",
    },
    "toJSON": function () {
        return {
            "title": this.url,
            "name": this.teacher.name
        };
    }
}
console.log(JSON.stringify(hd)); //{"title":"houdunren.com","name":"向军大叔"}

使用 JSON.parse 将字符串 json 解析成对象

        const obj = {
            name: '张三',
            score: 99,
            hobby:[
               '足球','篮球','打游戏'
            ]
        }
        const json = JSON.stringify(obj,null,2);//转换为json是字符串类型
        console.log(json);
        const data= JSON.parse(json);//转回js对象类型
        console.log(data);

可使用第二个参数函数来对返回的数据二次处理

let hd = {
  title: "后盾人",
  url: "houdunren.com",
  teacher: {
    name: "向军大叔"
  }
};
let jsonStr = JSON.stringify(hd);
console.log(
  JSON.parse(jsonStr, (key, value) => {
    if (key == "title") {
      return `[推荐] ${value}`;
    }
    return value;
  })
);

22、原型与继承

let array= ["a"];
console.log(array.concat("b"));
console.log(array);

在这里插入图片描述
这就是使用了数组的原型方法。
默认情况下创建对象都会有原型。

创建一个默认原型为空的对象

        const car = {
            name: '奔驰'
        }
        //创建的对象默认原型为Object 原型的方法hasOwnProperty判断对象对是否含有某属性
        console.log(car.hasOwnProperty('name'));//true
        //接下来创建一个新对象,默认原型为空 第一个参数为默认原型,第二个为对象的特征描述
        const car2 = Object.create(null, {
            name: {
                value: "宝马",
                enumerable: true //默认不可遍历
            }
        });
        console.log(car2.hasOwnProperty('name'));//报错 car2.hasOwnProperty is not a function
        //Object.keys是静态方法,不是原型方法所以是可以使用的
        console.log(Object.keys(car2));

函数拥有多个原型,prototype 用于实例对象使用,__proto__用于函数对象使用
我们随便创建一个构造函数,再用它实例化一个对象。输出一下它的结构

		function Car(name,price){
            this.name=name
            this.price = price

        }
        const benchi = new Car();
        console.dir(Car);
        console.dir(benchi);

在这里插入图片描述
我们可以看到Car函数有prototype和__proto__,其中__proto__是供Car本身来使用的。比如说bind(),call()等方法,都是在__proto__原型中的。而prototype则在它创建对象时,把prototype的内容作为__proto__中的内容传送给实例化出来的对象。
也就是说 console.log(benchi.proto===Car.prototype);

		function Car(name,price){
            this.name=name
            this.price = price

        }
        Car.__proto__.run=function(){
            console.log('Car跑起来了');
        }
        Car.run(); //本Car是没有run的方法的,但我们可以在它的原型上添加。添加之后Car就可以用run方法了
        const benchi = new Car();
        benchi.run()//报错 由于run方法是添加到__proto__上的,所以实例化出来的benchi不具备run方法
        Car.prototype.view=function(){//但是方法添加到Car的prototype 
            console.log('车可供人欣赏');
        }
        benchi.view();// 可调用,benchi实例化之后__propto__会获得Car的prototype中所有的方法,因为方法添加到了原型上,所以和示例化的顺序无关。

下面使用 setPrototypeOf 与 getPrototypeOf 获取与设置原型,操作的是__proto__而不是prototype

    const obj = {};
    const parent = {name: '父亲'};
    Object.setPrototypeOf(obj,parent);
    // obj.__proto__= parent //这个写法与方面相同
    console.dir(obj);

在这里插入图片描述
constructor存在于prototype原型中,用于指向构建函数的引用。

        function Car(){
            this.run= function(){
                console.log("跑起来了");
            }
        }
        const benchi = new Car();
        console.dir(benchi); //发现__proto__中包含constructor
        //那么我们是不是可以用 benchi对象来构建另一个对象呢
        const constructor = benchi.__proto__.constructor;//这个__proto__可以省略。因为它会自动去原型中查找方法
        const baoma = new constructor();
        baoma.run(); //运行成功

通过引用类型的原型,继承另一个引用类型的属性与方法,这就是实现继承的步骤。

对象的检查 hasOwnPropertyin
对象设置属性,只是修改对象属性并不会修改原型属性,使用hasOwnProperty 判断对象本身是否含有属性并不会检测原型。
使用 in 会检测原型与对象,而 hasOwnProperty 只检测对象,所以结合后可判断属性是否在原型中

    function Parent(){

    }
    const zhangsan = new Parent();
    const lisi = new Parent();
    zhangsan.name= '张三';
    zhangsan.__proto__.name = '中国人';//在zhangsan的原型上添加属性
    console.log(zhangsan.hasOwnProperty('name'));//检查zhangsan是否有name属性
    console.log(lisi.name); //因为lisi里面没有name属性,所以它去原型中查找
    console.log(lisi.__proto__===zhangsan.__proto__);//true
    delete zhangsan.name;
    console.log(zhangsan.hasOwnProperty('name'));//false 
    console.log('name' in zhangsan) //true
    console.log(zhangsan.name); //中国人

方法定义在函数中,每次实例化都会产生一个相同的方法。浪费空间。

        function User(name) {
            this.name = name;
            this.get = function () {
                return this.name;
            };
        }
        let lisi = new User("小明");
        let wangwu = new User("王五");
        console.log(lisi.get == wangwu.get); //false

为了解决这个问题,我们可以把方法添加到原型上面。

function User(name) {
  this.name = name;
}
User.prototype.get = function() {
  return  this.name;
};
let lisi = new User("小明");

let wangwu = new User("王五");
console.log(lisi.get == wangwu.get); //true
//通过修改原型方法会影响所有对象调用,因为方法是共用的
lisi.__proto__.get = function() {
  return "啦啦啦" + this.name;
};
console.log(lisi.get());  //啦啦啦小明
console.log(wangwu.get()); //啦啦啦王五

我们希望调用父类构造函数完成对象的属性初始化,但像下面这样使用是不会成功的。因为此时 this 指向了window,无法为当前对象声明属性。

function User(name) {
  this.name = name;
  console.log(this);// Window
}
User.prototype.getUserName = function() {
  return this.name;
};

function Admin(name) {
  User(name);
}
Admin.prototype = Object.create(User.prototype);
Admin.prototype.role = function() {};

let zhangsan = new Admin("张三");
console.log(zhangsan.getUserName()); //undefined

解决上面的问题是使用 call/apply 为每个生成的对象设置属性

function User(name) {
  this.name = name;
  console.log(this); // Admin
}
User.prototype.getUserName = function() {
  return this.name;
};

function Admin(name) {
  User.call(this, name);
}
Admin.prototype = Object.create(User.prototype);

let zhangsan = new Admin("zhangsan ");
console.log(zhangsan.getUserName()); //zhangsan 

23、Class类的静态属性和静态方法

类的本质就是在操作原型

静态属性 与 静态属性可以直接用类型调用,它是定义到对象本身上面的。普通方法和普通属性是定义到实例化之后的对象上面

用构造函数的方法声明静态方法和静态属性

        function Car(name){
            this.name = name;
            this.run= function(){
                console.log("普通方法----跑起来了");
            }
        }
        Car.name='车'
        Car.run = function(){
            console.log("静态方法----跑起来了");
        }
        console.dir(Car);

现在有了类 能更方便的定义类了

        class Car{
            constructor(name){
                this.name=name
            }
            color = 'red'
            run=()=>{
                console.log('普通方法 跑起来了');
            }
            static run=function(){
                console.log('静态方法 跑起来了');
            }
            static name= '静态方法 名字'
        }
        console.dir(Car);
        Car.run();
        console.log(Car.name);
        const benchi = new Car('奔驰');
        console.log(benchi);

在这里插入图片描述

24、类的私有属性和方法

属性名和方法名之前加#就可把它变成私有属性,外部不能随意访问和修改,子类也不行。

        class Car{
            #name= '张三'
        }
        const benchi = new Car();
        console.log(benchi.#name='李四');

在这里插入图片描述
运用get set对私有属性进行操作

        class Car{
            #name= '奔驰'
            #run = () =>{
                console.log('跑起来');
            }
            get name(){
                return this.#name;
            }
            set name(name){
                this.#name = name;
                return true
            }
        }
        const benchi = new Car();
        // benchi.#run() //报错
        console.log(benchi.name='宝马');

25、构造函数属性继承原理

        function Parent(name,age){
            this.name =name
            this.age =age
        }
        function Son(...args){
            Parent.apply(this,args)
        }
        Son.prototype= Object.create(Parent.prototype);
        const zhangsan = new Son('张三',18)
        console.log(zhangsan);

用class继承构造函数

        class Parent{
            constructor(name,age){
                this.name=name;
                this.age = age;
            }
			height = 180 //这里直接添加到prototype上了
        }
        class Son extends Parent{ 
            constructor(...args){ //这个可以不写,系统默认写
                super(...args);
            }
        }
        const zhangsan = new Son('张三',18);
        console.log(zhangsan);

在这里插入图片描述

26、方法的继承

原始的方法继承

        function Parent(){
            
        }
        Parent.prototype.show= function(){
            console.log('show');
        }
        function Son(){

        }
        Son.prototype= Object.create(Parent.prototype);
        Son.prototype.constructior = Son; 
        const zhangsan = new Son();
        zhangsan.show(); //成功打印show

class的方法继承

        class Parent{
           show = ()=>{ //添加直接添加到prototype属性上面
               console.log('show');
           }
        }
        class Son extends Parent{
           
        }
        const zhangsan = new Son();
        zhangsan.show()

静态方法的继承
原理

        function Parent (){
            
        }
        Parent.name = '张三';
        Parent.show= function(){
            console.log('我叫张三');
        }
        function Son(){
            
        }
        Son.__proto__ = Parent;
        Son.show();
        console.log(Son.name);

class类 同理

        class Parent{
            static name = '张三'
            static show= function(){
                console.log('wo shi zhang san');
            }
        }
        class Son extends Parent{

        }
        console.log(Son.name);
        Son.show();

27、instanceof和isPrototypeOf检测继承关系

        class Parent{

        }
        class Son extends Parent{

        }
        const zhangsan = new Son();
        console.log(zhangsan instanceof Son);
        console.log(zhangsan instanceof Parent);
        console.log(Son.prototype.isPrototypeOf(zhangsan));
        console.log(Parent.prototype.isPrototypeOf(zhangsan));

同步异步优先级问题

同步方法先执行 > promise完成后放入微观任务队列中 > 定时器之类的异步方法执行完毕之后放入宏观任务队列最后执行
promise 创建时即立即执行即同步任务,then 会放在异步微任务中执行,需要等同步任务执行后才执行。
promise 的 then、catch、finally的方法都是异步任务
程序需要将主任务执行完成才会执行异步队列任务

    setTimeout(()=>{
        console.log('定时器'); //宏观任务
    },0)
    new Promise(resolve=>{ 
        resolve();  //发送通知,执行成功方法 微观任务
        console.log('任务执行中'); //promise中的任务为同步

    }).then(values=>console.log('成功'));
    console.log('同步');

在这里插入图片描述

Promise

Promise
状态被改变后就不能再修改了,下面先通过resolve 改变为成功状态,表示promise 状态已经完成,就不能使用 reject 更改状态了

        const p1 = new Promise((resolve, reject) => {
            resolve("fulfilled");
            reject("rejected");
        });
        const p2 = new Promise(resolve => {
            resolve(p1);
        }).then(
            value => {
                console.log(value);
            },
            reason => {
                console.log(reason);
            }
        );

当promise做为参数传递时,需要等待promise执行完才可以继承,下面的p2需要等待p1执行完成。
因为p2 的resolve 返回了 p1 的promise,所以此时p2 的then 方法已经是p1 的了
正因为以上原因 then 的第一个函数输出了 p1 的 resolve 的参数

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("操作成功");
  }, 2000);
});
const p2 = new Promise((resolve, reject) => {
  resolve(p1);
}).then(
  msg => {
    console.log(msg);
  },
  error => {
    console.log(error);
  }
);

then返回的也是一个promise,所以可以链式操作。
每次的 then 都是一个全新的 promise,默认 then 返回的 promise 状态是 fulfilled

        const p = new Promise((resolve,reject)=>{
            // resolve("成功");
            resolve("失败");
        })
        p.then(success=>{
            console.log(success);
        },(msg)=>{
            console.log(msg);
        }).then(success=>{//默认返回成功
            console.log('success');
        },(msg)=>{
            console.log('msg');
        })

DOM开始了

文档渲染

当把script写在外部时,浏览器会自动把script移入body中

<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>

</body>
<script>
    console.log('张三');
</script>

</html>

在这里插入图片描述

id没有加引号,浏览器也会自动补齐

 <div id=div></div>

在这里插入图片描述
表格tabel中不允许有内容,浏览器在渲染过程中会进行处理

<body>
    <table>
        zhangsan
        <tr>
          <td>lisi</td>
        </tr>
      </table>
</body>

渲染后会添加tbody标签并将table中的字符移出
在这里插入图片描述

操作时机

获取元素时,元素必须已经渲染到页面上。否则就向下例一样获取不到

<body>
    <script>
        const div = document.querySelector('#div')
        console.log(div);
    </script>
   <div id="div"></div>
</body>

解决方法,把script移到div下面 或者 用onload等待页面渲染完之后执行onload里面的代码。也可以采用定时器异步等方法。

<body>
    <script>
       window.onload = ()=>{
        const div = document.querySelector('#div')
        console.log(div);
       }
    </script>
   <div id="div"></div>
</body>

节点对象

JS中的内容称为DOM节点对象(node),即然是对象就包括操作NODE的属性和方法

  • 包括12种类型的节点对象
  • 常用了节点为document、标签元素节点、文本节点、注释节点
  • 节点均继承自Node类型,所以拥有相同的属性或方法
  • document是DOM操作的起始节点
<body id="hdcms">
  <!-- 后盾人 -->
</body>
<script>
	// document节点 noteType为9
  console.log(document.nodeType)
  
  // 第一个子节点为<!DOCTYPE html>,且nodetype为10
  console.log(document.childNodes.item(0).nodeType)
  
  // body 是标签节点 nodeType为1
  console.log(document.body.nodeType) 
  
  // body的属性节点nodeType 为2
  console.log(document.body.attributes[0].nodeType)
  
	// body的第一个节点为文本节点,nodeType为3
  console.log(document.body.childNodes.item(0).nodeType)
  
  // body的第二个节点为注释,nodeType类型为8
  console.log(document.body.childNodes[1].nodeType)
</script>

原型链

在浏览器渲染过程中会将文档内容生成为不同的对象

  • 不同类型节点由专有的构造函数创建对象
  • 节点也是对象所以也具有js对象的特征
  • 使用console.dir可以打印出DOM节点对象结构

遍历出h1的原型链

<body>
  <h1 id="title">大家好</h1>
  <script>
    function prototype(el) {
      const parent = Object.getPrototypeOf(el)
      console.log(parent);
      parent ? prototype(parent) : null
    }
    const title = document.querySelector('#title');
    prototype(title);
    console.log(title.__proto__ === HTMLHeadingElement.prototype);
    console.log(HTMLHeadingElement.prototype.__proto__ === HTMLElement.prototype);
  </script>
</body>

在这里插入图片描述
最终得到的节点的原型链为

原型说明
Object根对象
EventTarget提供事件支持
Node提供parentNode等节点操作方法
Element提供getElementsByTagName、querySelector等方法
HTMLElement所有元素的基础类,提供className、nodeName等方法
HTMLHeadingElementHead标题元素类

利用原型,添加一个截取字符串方法

<body>
  <input type="text" value="Hello everyone" />
  <div id="div">Hello World</div>
  <script>
    //  console.log(document.querySelector('[type=text]').__proto__);//HTMLInputElement 
    class Util {
      static isForm = function (node) {
        return node instanceof HTMLInputElement
      }
    }

    Node.prototype.substr = function (start, num) {
      if (Util.isForm(this)) {
        return this.value.substr(start, num)
      } else {
        return this.textContent.substr(start, num)
      }
    }
    //div
    console.log(document.querySelector('#div').substr(1, 2))
    //input
    console.log(document.querySelector('[type=text]').substr(3, 5))
  </script>
</body>

在这里插入图片描述

对象特征

既然DOM与我们其他js创建的对象特征相仿,所以也可以为DOM对象添加属性或方法。

对于系统应用的属性,应明确含义不应该随意使用,比如ID是用于标识元素唯一属性,不能用于其他目的

下面的直接修改ID属性是不建议的

  <div id="div">Hello World</div>
  <script>
    const el = document.querySelector('#div');
    el.id= '张三'
    const el2 = document.querySelector('#div');//空
  </script>

在这里插入图片描述
利用对象合并,给元素增加单击事件。

<body>

  <div id="div">Hello World</div>
  <script>
    const el = document.querySelector('#div');
    console.dir(el);
    Object.assign(el,{
      onclick(){
        this.style.color = 'red'
      },
      innerHTML: '你好世界'
    })

  </script>
</body>

使用对象特性更改样式属性

<body>

  <div id="div">Hello World</div>
  <script>
    const el = document.querySelector('#div');
    console.dir(el);
    Object.assign(el.style,{
      color: 'red',
      fontSize: '30px'
    })

  </script>
</body>

DOCUMENT

document是window对象的属性,是由HTMLDocument类实现的示例。
原型链中也包含Node,所以可以使用有关节点操作的方法如nodeType/NodeName等

<body>
  <div id="div">Hello World</div>
  <script>
  console.dir(document.nodeName)
  console.dir(document.nodeType)
  function prototype(el) {
      const parent = Object.getPrototypeOf(el)
      console.log(parent);
      parent ? prototype(parent) : null
    }
    prototype(document)
  </script>
</body>

在这里插入图片描述Html
下面通过节点的nodeType来获取元素,因为标签的nodeType为1

<body>
  <div id="div">Hello World</div>
  <script>
    window.onload = function () {
      let html = [...document.childNodes].filter((node) => {
        if (node.nodeType === 1) {
          return node
        }
      })[0]
      console.log(html)
    }
  </script>
</body>

在这里插入图片描述

系统提供了简单的方式来获取html元素,效果与上面相同

console.log(document.documentElement)

文档信息

    //设置文档标签
    document.title = 'hello world'
    //获取文档标题
    console.log(document.title);
    //获取当前URL
    console.log(document.URL)
    //获取域名
    document.domain
    //获取来源地址
    console.log(document.referrer)

节点属性

不同类型的节点拥有不同属性,下面是节点属性的说明与示例

nodeType指以数值返回节点的类型

nodeType说明
1元素节点
2属性节点
3文本节点
8注释节点
9document对象

下面是节点nodeType的示例

<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
    <div class="xiangjun"><!-- 向军大叔 --></div>
  </div>
  <script>
    const node = document.querySelector(`#app`)
    console.log(node.nodeType) //1
    console.log(node.firstChild.nodeType) //3 空格文本
  
    const xj = document.querySelector('.xiangjun')
    console.log(xj.childNodes[0].nodeType) //8 注释
  </script>
</body>

递归获取所有标签元素

<body>
  <div id="div">
    <ul>
      <li>
        <h2><strong>hello world</strong></h2>
      </li>
    </ul>
    <div id='div2'>
      小飞象
    </div>
  </div>
  <script>
    const array = [];
    function getAll (el){
      [...el.childNodes].map((e)=>{
          if(e.nodeType==1){
            array.push(e)
            getAll(e)
          }
      })
    }
    getAll(document.body)
    console.log(array);
  </script>
</body>

nodeName
nodeName指定节点的名称
获取值为大写形式

nodeTypenodeName
1元素名称如DIV
2属性名称
3#text
8#comment

下面来操作 nodeName

<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
    <div class="xiangjun"><!-- 向军大叔 --></div>
  </div>
  <script>
    const node = document.querySelector(`#app`)
    console.log(node.nodeName) //DIV
    console.log(node.firstChild.nodeName) //#text
  
    const xj = document.querySelector('.xiangjun')
    console.log(xj.childNodes[0].nodeName) //#comment
  </script>
</body>

** tagName**

nodeName可以获取不限于元素的节点名,tagName仅能用于获取标签元素节点名称。

  • tagName存在于Element类的原型中
  • 元素上使用tagName与nodeName没有区别
  • 获取值为大写形式

用tagName遍历元素,因为它只能获取标签,可以利用这一特性。

<body>
  <div id="div">
    <ul>
      <li>
        <h2><strong>hello world</strong></h2>
      </li>
    </ul>
    <div id='div2'>
      小飞象
    </div>
  </div>
  <script>
    const array = [];
      function getAll(el){
        
        [...el.childNodes].forEach(e=>{
          if(e.tagName){
            array.push(e.tagName)
            getAll(e)
          }
        })
      }
      getAll(document.querySelector('#div'))
      console.log(array);
  </script>
</body>

在这里插入图片描述
nodeValue
使用nodeValue或data函数获取节点值,也可以使用节点的data属性获取节点内容

nodeTypenodeValue
1null
2属性值
3文本内容
8注释内容

下面来看nodeValue的示例,直接采用后盾人的例子

<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
  <div class="xiangjun"><!-- 向军大叔 --></div>
</div>
<script>
  const node = document.querySelector(`#app`)
  console.log(node.nodeValue) //null

  const hdcms = document.querySelector('.hdcms')
  console.log(hdcms.firstChild.nodeValue) //hdcms.com

  const xj = document.querySelector('.xiangjun')
  console.log(xj.childNodes[0].nodeValue) // 向军大叔
</script>

使用data属性获取节点内容

<div id="app">
  houdunren.com
</div>

<script>
  const app = document.querySelector('#app')
  console.log(app.childNodes[0].data) //houdunren.com
</script>

** 树状节点**
下面获取标签树状结构即多级标签结构,来加深一下nodeType/nodeName等知识
document.documentElement 是获取全部的标签

<!DOCTYPE html>
<html lang="en">
<head >
</head>

<body>
  <div id="app">
    hello world
  </div>
  <script>
    function getAll(el){
      if(el.nodeType!=1) return //如果传入的不是标签直接退出

      return [...el.childNodes].filter(e=> e.nodeType==1) //判断子节点是否为标签节点。如果是就进入遍历
      .map(e=>{
        return {
          name: e.tagName, //e.nodeName
          children: getAll(e) //遍历子标签
        }
      })
    }
    const nodes=getAll(document.documentElement);
    console.log(nodes);
  </script>
</body>
</html>

在这里插入图片描述

节点集合

Nodelist与HTMLCollection都是包含多个节点标签的集合,大部分功能也是相同的。

  • getElementsBy…等方法返回的是HTMLCollection
  • querySelectorAll 返回的是NodeList
  • NodeList节点列表是动态的,即内容添加后会动态获取

length
Nodelist与HTMLCollection包含length属性,记录了节点元素的数量

<body>
  <div name="app">
    <div id="div">小飞象</div>
    <div name="feixiang">xiaofeixiang</div>
  </div>
  <script>
    const nodes = document.getElementsByTagName('div')
    const nodes2 = document.querySelectorAll('div')
    for (let i = 0; i < nodes.length; i++) {
      console.log(nodes[i])
    }
    console.log(nodes);
    console.log(nodes2);
  </script>
</body>

在这里插入图片描述
转换数组

有时候使用数组方法来操作节点集合,这就需要将集合节点转化为数组类型,有以下几种方法可以实现。
使用call调用原型方法 array.form …展开语法

<body>
  <div name="app">
    <div id="div">小飞象</div>
    <div name="feixiang">xiaofeixiang</div>
  </div>
  <script>
    const nodes = document.getElementsByTagName('div')
    console.log([...nodes]);
    console.log(Array.from(nodes));
    console.log(Array.prototype.slice.call(nodes,0));
  </script>
</body>

item
Nodelist与HTMLCollection提供了item()方法来根据索引获取元素

<body>
  <div name="app">
    <div id="div">小飞象</div>
    <div name="feixiang">xiaofeixiang</div>
  </div>
  <script>
    const nodes = document.getElementsByTagName('div')
    console.log(nodes.item(1));
    console.log(nodes[2]); //使用数组索引获取更方便
  </script>
</body>

namedItem

HTMLCollection具有namedItem方法可以按name或id属性来获取元素

<body>
  <div name="app">
    <div id="div">小飞象</div>
    <div name="feixiang">xiaofeixiang</div>
  </div>
  <script>
    const nodes = document.getElementsByTagName('div')
    console.log(nodes.namedItem('app')); //按name属性查找
    console.log(nodes.namedItem('div')); //按div查找
    console.log(nodes['feixiang']); //也可以使用数组或属性方式获取
    console.log(nodes.feixiang);
    
  </script>
</body>

数组检索时使用item方法,字符串索引时使用namedItem方法

<body>
  <div name="app">
    <div id="div">小飞象</div>
    <div name="feixiang">xiaofeixiang</div>
  </div>
  <script>
    const nodes = document.getElementsByTagName('div')
    console.log(nodes[0]); //按照索引
    console.log(nodes['feixiang']); //按照name或id
  </script>
</body>

常用元素
系统针对特定标签提供了快速选择的方式
比如获取所有的超链接

<body>
  <div name="app">
    <a href="#">小飞象</a>
    <a href="#">xiaofeixiang</a>
  </div>
  <script>
   console.log(document.links);
  </script>
</body>
方法说明
document.documentElement文档节点即html标签节点
document.bodybody标签节点
document.headhead标签节点
document.links超链接集合
document.anchors所有锚点集合
document.formsform表单集合
document.images图片集合

节点关系

节点是父子级嵌套与前后兄弟关系,使用DOM提供的API可以获取这种关系的元素。

  • 文本和注释也是节点,所以也在匹配结果中

基础知识
节点是根据HTML内容产生的,所以也存在父子、兄弟、祖先、后代等节点关系

节点属性说明
childNodes获取所有子节点
parentNode获取父节点
firstChild子节点中第一个
lastChild子节点中最后一个
nextSibling下一个兄弟节点
previousSibling上一个兄弟节点

子节点集合与首、尾节点获取

  • 文本也是node所以也会在匹配当中
<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
  <div class="xiangjun">向军大叔</div>
</div>
<script>
  const node = document.querySelector(`#app`)
  console.log(node.childNodes) //所有子节点
  console.log(node.firstChild) //第一个子节点是文本节点
  console.log(node.lastChild) //最后一个子节点也是文本节点
</script>

在这里插入图片描述
下面通过实例操作节点关联

  • 文本也是node所以也会在匹配当中
<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
    <div class="xiangjun">向军大叔</div>
  </div>
  <script>
    const node = app.querySelector(`.hdcms`)
    console.log(node.parentNode) //div#app
    console.log(node.childNodes) //文本节点
    console.log(node.nextSibling) //下一个兄弟节点是文本节点
    console.log(node.previousSibling) //上一个节点也是文本节点
  </script>
</body>

在这里插入图片描述
document是顶级节点html标签的父节点是document

<script>
  console.log(document.documentElement.parentNode === document)
</script>

元素关系
使用childNodes等获取的节点包括文本与注释,但这不是我们常用的,系统也提供了只操作元素的关系方法。

节点属性说明
parentElement获取父元素
children获取所有子元素
childElementCount子标签元素的数量
firstElementChild第一个子标签
lastElementChild最后一个子标签
previousElementSibling上一个兄弟标签
nextElementSibling下一个兄弟标签
contains返回布尔值,判断传入的节点是否为该节点的后代节点

以下实例展示怎样通过元素关系获取元素

<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
  <div class="xiangjun"><!-- 向军大叔 --></div>
</div>

<script>
  const app = document.querySelector(`#app`)
  console.log(app.children) //所有子元素
  console.log(app.firstElementChild) //第一个子元素 div.houdunren
  console.log(app.lastElementChild) //最后一个子元素 div.xiangjun

  const hdcms = document.querySelector('.hdcms')
  console.log(hdcms.parentElement) //父元素 div#app

  console.log(hdcms.previousElementSibling) //上一个兄弟元素 div.houdunren
  console.log(hdcms.nextElementSibling) //下一个兄弟元素 div.xiangjun
</script>

html标签的父节点是document,但父标签节点不存在

<script>
  console.log(document.documentElement.parentNode === document) //true
  console.log(document.documentElement.parentElement) //null
</script>

选取节点

系统提供了丰富的选择节点(NODE)的操作方法

getElementById

使用ID选择是非常方便的选择具有ID值的节点元素,但注意ID应该是唯一的
getElementById只能通过document访问,不能通过元素读取拥有ID的子元素

  • 只能通过document对象上使用

拥有ID的元素可做为WINDOW的属性进行访问
如果声明了变量这种访问方式将无效,所以并不建议使用这种方式访问对象

<body>
  <div id="app">
    123
  </div>
  
  <script>
    app.innerText= '456'
    console.log(app.innerText); //456
  </script>
</body>

getElementByName

使用getElementByName获取去设置了name属性的元素,虽然在DIV等元素上同样有效,但一般用来对表单元素进行操作时使用。

  • 返回NodeList节点列表对象
  • NodeList顺序为元素在文档中的顺序
<body>
  <div name="houdunren">houdunren.com</div>
  <input type="text" name="username" />
  
  <script>
    const div = document.getElementsByName('houdunren')
    console.dir(div)
    const input = document.getElementsByName('username')
    console.dir(input)
  </script>
</body>

getElementsByTagName

使用getElementsByTagName用于按标签名获取元素

  • 返回HTMLCollection节点列表对象
  • 是不区分大小的获取
<div name="houdunren">houdunren.com</div>
<div id="app"></div>
<script>
  const divs = document.getElementsByTagName('div')
  console.dir(divs)
</script>

通配符

可以使用通配符 ***** 获取所有元素

<div name="houdunren">houdunren.com</div>
<div id="app"></div>

<script>
  const nodes = document.getElementsByTagName('*')
  console.dir(nodes)
</script>

某个元素也可以使用通配置符 ***** 获取后代元素,下面获取 id为houdunren的所有后代元素

<div id="houdunren">
  <span>houdunren.com</span>
  <span>hdcms.com</span>
</div>

<script>
  const nodes = document.getElementsByTagName('*').namedItem('houdunren').getElementsByTagName('*')
  console.dir(nodes) //打印的是两个span标签
</script>

getElementsByClassName

getElementsByClassName用于按class样式属性值获取元素集合

  • 设置多个值时顺序无关,指包含这些class属性的元素
<body>
  <div class="houdunren hdcms xiangjun">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
  <script>
    const nodes = document.getElementsByClassName('hdcms')
    console.log(nodes.length) //2
  
    //查找包含class属性包括 hdcms 与 houdunren 的元素
    const tags = document.getElementsByClassName('hdcms houdunren')
    console.log(tags.length) //1
    </script>
</body>

遍历节点

<body>
  <div id="app">
    <div>小飞象</div>
    <div>
      <ul>
        <li>js</li>
        <li>css</li>
        <li>react</li>
        <li>实战</li>
        <li>找工作</li>
      </ul>
    </div>
  </div>
  <script>

    const nodes = document.querySelector('#app');
    const els = []
    function getAll(el){
      [...el.children].map(e=>{
        if(e.children){
          els.push(e)
          getAll(e);
        }
      })
    }
    getAll(nodes)
    console.log(els);
    for(let i=0 ; i<els.length ; i++){
      console.log(els[i]);
    }
  </script>
</body>

length

根据节点列表的length属性,使用for遍历

forEach

Nodelist节点列表也可以使用forEach来进行遍历,但HTMLCollection则不可以

map

节点集合对象原型中不存在map方法,但可以借用Array的原型map方法实现遍历

<div id="app">
    <div>小飞象</div>
    <div>
      <ul>
        <li>js</li>
        <li>css</li>
        <li>react</li>
        <li>实战</li>
        <li>找工作</li>
      </ul>
    </div>
  </div>

<script>
  const nodes = document.querySelectorAll('div')
  Array.prototype.map.call(nodes, (node, index) => {
    console.log(node, index)
  })
</script>

也可以转为数组进行操作

<body>
  <div id="app">
    <div>小飞象</div>
    <div>
      <ul>
        <li>js</li>
        <li>css</li>
        <li>react</li>
        <li>实战</li>
        <li>找工作</li>
      </ul>
    </div>
  </div>

<script>
  const nodes = document.querySelectorAll('div');
  [...nodes].map( (node, index) => {
    console.log(node, index)
  })
</script>
</body>

结果相同
在这里插入图片描述

for Of

节点集合是类数组的可迭代对象所以可以使用for…of进行遍历

<body>
  <div id="app">
    <div>小飞象</div>
    <div>
      <ul>
        <li>js</li>
        <li>css</li>
        <li>react</li>
        <li>实战</li>
        <li>找工作</li>
      </ul>
    </div>
  </div>

<script>
  const nodes = document.querySelectorAll('div');
  for(node of nodes){
    console.log(node);
  }
</script>
</body>

样式选择器

在css中可以通过样式选择器修饰元素样式,在DOM操作中也可以使用这种方式查找元素。
使用getElementsByTagName等方式选择元素不够灵活,使用样式选择器,更加灵活方便。

querySelectorAll

使用querySelectorAll根据css选择器获取Nodelist节点列表
获取的Nodelist节点列表是静态的,添加或删除元素后不变

querySelector

querySelector使用CSS选择器获取一个元素,下面是根据属性获取单个元素

matches

用于检测元素是否是指定的样式选择器匹配,下面过滤掉所有name属性的li元素

<body>
  <div id="app">
    <li>houdunren</li>
    <li>向军大叔</li>
    <li name="hdcms">hdcms.com</li>
  </div>
  <script>
    const app = document.getElementById('app')
    const nodes = [...app.querySelectorAll('li')].filter((node) => {
      return !node.matches(`[name]`); //返回的是布尔值
    })
    console.log(nodes)
  </script>
</body>

closest

查找最近的符合选择器的祖先元素(包括自身),下例查找父级拥有 .comment类的元素

<div class="comment">
  <ul class="comment">
    <li>houdunren.com</li>
  </ul>
</div>

<script>
  const li = document.getElementsByTagName('li')[0]
  const node = li.closest(`.comment`)
  console.log(node)
</script>

动态与静态

通过getElementsByTagname等getElementsBy… 函数获取的Nodelist与HTMLCollection集合是动态的,即有元素添加或移动操作将实时反映最新状态。

  • 使用getElement…返回的都是动态集合
  • 使用querySelectorAll 返回的是静态集合

动态特性

下例中通过按钮动态添加元素后,获取的元素集合是动态的

<h1>houdunren.com</h1>
<h1>hdcms.com</h1>
<button id="add">添加元素</button>

<script>
  let elements = document.getElementsByTagName('h1')
  console.log(elements)
  let button = document.querySelector('#add')
  button.addEventListener('click', () => {
    document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>向军大叔</h1>')
    console.log(elements) //每添加一个标题,elements也跟着变化
  })
</script> 

document.querySelectorAll获取的集合是静态的

<h1>houdunren.com</h1>
<h1>hdcms.com</h1>
<button id="add">添加元素</button>

<script>
  let elements = document.querySelectorAll('h1')
  console.log(elements.length)
  let button = document.querySelector('#add')
  button.addEventListener('click', () => {
    document.querySelector('body').insertAdjacentHTML('beforeend', '<h1>向军大叔</h1>')
    console.log(elements.length) //不管添加多少个标题,长度始终是2
  })
</script>

使用静态

如果需要保存静态集合,则需要对集合进行复制

<body>
  <div id="houdunren">houdunren.com</div>
  <div name="hdcms">hdcms.com</div>
  <script>
    const array1 = document.getElementsByTagName('div');
    const clone = [...array1];
    console.log('array1.length   ' + array1.length);
    console.log('clone.length    ' + clone.length);
    document.body.appendChild(document.createElement('div'))
    console.log('array1.length   ' + array1.length);
    console.log('clone.length    ' + clone.length);
  </script>
</body>

在这里插入图片描述

标准属性

元素的标准属性具有相对应的DOM对象属性

  • 操作属性区分大小写
  • 多个单词属性命名规则为第一个单词小写,其他单词大写
  • 属性值是多类型并不全是字符串,也可能是对象等
  • 事件处理程序属性值为函数
  • style属性为CSStyleDeclaration对象
  • DOM对象不同生成的属性也不同

属性别名

有些属性名与JS关键词冲突,系统已经起了别名

属性别名
classclassName
forhtmlFor

操作属性

使用属性设置图片的路径
使用hidden隐藏元素


<body>
  <img src="" alt="">
  <script>
    const img = document.querySelector('img');
    img.src= 'https://i-blog.csdnimg.cn/blog_migrate/42e466e24a4ad2b65ac38b21f6c8e005.png';
    img.alt= '小飞象'
    img.addEventListener('click',function(){
      this.hidden = true
    })
  </script>
</body>

多类型

大部分属性值都是字符串,但并不是全部。比如说表单的checked就是布尔型。

<label for="hot"> <input id="hot" type="checkbox" name="hot" />热门 </label>
<script>
  const node = document.querySelector(`[name='hot']`)
  node.addEventListener('change', function () {
    console.log(this.checked)
  })
</script>

属性值并都与HTML定义的值一样

元素特征

对于标准的属性可以使用DOM属性的方式进行操作,但对于标签的非标准的定制属性则不可以。但JS提供了方法来控制标准或非标准属性
可以理解为元素的属性分两个地方保存,DOM属性中记录标准属性,特征中记录标准和定制属性。

方法说明
getAttribute获取属性
setAttribute设置属性
removeAttribute删除属性
hasAttribute属性检测

特征是可迭代对象

<body>
  <div id="app" content="小飞象" color="red">hdcms.com</div>
  <script>
    const node = document.querySelector(`#app`)

    for (const a of node.attributes) {
      console.log(a);
    }
  </script>
</body>

特征值与HTML定义是一致的,这和属性值是不同的

<a href="#houdunren" id="home">后盾人</a>
<script>
  const node = document.querySelector(`#home`)
  
  // http://127.0.0.1:5500/test.html#houdunren
  console.log(node.href)
  
  // #houdunren
  console.log(node.getAttribute('href'))
</script>

attributes

元素提供了attributes属性可以只读的获取元素的属性

<div class="hdcms" data-content="后盾人">hdcms.com</div>
<script>
  let hdcms = document.querySelector('.hdcms')
  console.dir(hdcms.attributes['class'].nodeValue) //hdcms
  console.dir(hdcms.attributes['data-content'].nodeValue) //后盾人
</script>

自定义特征

虽然可以随意定义特征并使用getAttribute等方法管理。但很容易造成与标签的现在或未来属性重名。建议使用以data-为前缀的自定义特征处理,针对这种定义方式JS也提供了接口方便操作。

  • 元素中以data-为前缀的属性会添加到属性集中
  • 使用元素的dataset可扩区属性集中的属性
  • 改变dataset的值也会影响到元素上
<body>
  <div id="app" data-content="小飞象" data-el-color="red">hdcms.com</div>
  <script>
    const node = document.querySelector(`#app`);
    let color=node.dataset.elColor; //采用驼峰的方式
    console.log(color);
    color = node.dataset.elColor = 'blue';
    console.log(color);
  </script>
</body>

属性同步

特征和属性是记录元素属性的两大不同场所。大部分更改会进行同步操作。
下面使用属性更改了className,会自动同步到了特征集中,反之亦然。

<body>
  <div id="app" class="blue" data-content="小飞象" data-el-color="red">hdcms.com</div>
  <script>
    const node = document.querySelector(`#app`);
    node.className = 'red';
    console.log(node.getAttribute('class'));
    node.setAttribute('class','green');
    console.log(node.className);
  </script>
</body>

但是input的value是单项的,修改特征会同步到属性。修改属性,不会影响特征

创建节点

创建节点就是构建出DOM对象,然后根据需要添加到其他节点中

createTextNode

创建文本对象并添加到元素中

<body>
  <div id="app">你的名字是:</div>
  <script>
    const node = document.querySelector(`#app`);
    const text =document.createTextNode('张三');
    console.log(typeof text); //object
    node.append(text)
  </script>
</body>

createElement

使用该方法可以创建标签节点对象,创建span标签新节点并添加到div中

<body>
  <div id="app">你是谁:</div>
  <script>
    const node = document.querySelector(`#app`);
    let span =document.createElement('span');
    span.innerHTML='我是新创建的标签';
    node.append(span)
  </script>
</body>

使用PROMISE结合节点操作来加载外部JAVASCRIPT文件

function js(file) {
  return new Promise((resolve, reject) => {
    let js = document.createElement('script')
    js.type = 'text/javascript'
    js.src = file
    js.onload = resolve
    js.onerror = reject
    document.head.appendChild(js)
  })
}

js('1.js')
  .then(() => console.log('加载成功'))
  .catch((error) => console.log(`${error.target.src} 加载失败`))

cloneNode&importNode

使用cloneNode和document.importNode用于复制节点对象操作

  • cloneNode是节点方法
  • cloneNode参数为true时递归复制子节点即深拷贝
  • importNode是document对象方法
<body>
  <div id="app"><h1>我是个标题 <span> 深拷贝能复制我 </span></h1></div>
  <script>
    const node = document.querySelector(`#app`);
    const title = document.querySelector(`h1`);
    const cloneTitle = title.cloneNode(true);
    node.append(cloneTitle)
  </script>
</body>

document.importNode方法是部分IE浏览器不支持的,也是复制节点对象的方法
第一个参数为节点对象
第二个参数为true时递归复制

<body>
  <div id="app"><h1>我是个标题 <span> 深拷贝能复制我 </span></h1></div>
  <script>
    const node = document.querySelector(`#app`);
    const title = document.querySelector(`h1`);
    const cloneTitle = document.importNode(title,true);
    node.append(cloneTitle)
  </script>
</body>

节点内容

innerHtml

innerHTML用于向标签中添加html内容,同时触发浏览器的解析器重绘DOM
下例使用innerHTML获取和设置div内容
innerHTML中只解析HTML标签语法,所以其中的script不会作为js处理

<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
</div>
<script>
  let app = document.querySelector('#app')
  console.log(app.innerHTML)

  app.innerHTML = '<h1>后盾人</h1>'
</script>

重绘节点
使用innerHTML操作会重绘元素,下面在点击第二次就没有效果了

  • 因为对#app内容进行了重绘,即删除原内容然后设置新内容
  • 重绘后产生的button对象没有事件
  • 重绘后又产生了新img对象,所以在控制台中可看到新图片在加载
<body>
  <div id="app">
    <button>houdunren.com</button>
    <img src="https://img0.baidu.com/it/u=3225163326,3627210682&fm=26&fmt=auto&gp=0.jpg" alt="" />
  </div>
  <script>
    const app = document.querySelector('#app')
    app.querySelector('button').addEventListener('click', function () {
      alert(this.innerHTML)
      this.parentElement.innerHTML += '<hr/>向军大叔'
    })
  </script>
</body>

outerHTML

outerHTML与innerHTML的区别是包含父标签

  • outerHTML不会删除原来的旧元素
  • 只是用新内容替换就内容,就内容依旧存在
    下面将div#app替换为新内容,使用outerHTML是保留旧内容,页面中使用新内容
<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
  </div>
  <script>
    let app = document.querySelector('#app')
    
    
    app.outerHTML = '<h1>后盾人</h1>'
    console.log(app.outerHTML)
  </script>
</body>

在这里插入图片描述
使用innerHTML内容是被删除然后使用新内容

<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
  </div>
  <script>
    let app = document.querySelector('#app')
    
    
    app.innerHTML = '<h1>后盾人</h1>'
    console.log(app.innerHTML)
  </script>
</body>

在这里插入图片描述

textContent与innerText

textContent与innerText是访问或添加文本内容到元素中

  • 获取时忽略所有标签,只获取文本部分
  • 设置时将内容中的标签当文本对待不进行标签解析
    获取时忽略内容中的所有标签
<body>
  <div id="app">
    <h1>houdunren.com</h1>
  </div>
  <script>
    let app = document.querySelector('#app')
    console.log(app.textContent)
  </script>
</body>

在这里插入图片描述
设置时将标签当文本对待,即转为HTML实体内容

<body>
  <div id="app">
    <div class="houdunren" data="hd">houdunren.com</div>
    <div class="hdcms">hdcms.com</div>
  </div>
  <script>
    let app = document.querySelector('#app')
    app.textContent="<h1>后盾人</h1>"
  </script>
</body>

在这里插入图片描述

insertAdjacentText

将文本插入到元素指定位置,不会对文本中的标签进行解析,包括以下位置

选择说明
beforebegin元素本身前面
afterend元素本身后面
afterbegin元素内部前面
beforeend元素内部后面
<body>
  <div id="app" style="display:inline-block;line-height: 50px;text-align: center;background-color: pink; width: 300px; height: 50px;">
    我是div
  </div>
  <script>
    let app = document.querySelector('#app')
    app.insertAdjacentText('beforebegin','元素本身前面')
  </script>
</body>

在这里插入图片描述

节点管理

推荐方法

方法说明
append节点尾部添加新节点或字符串
prepend节点开始添加新节点或字符串
before节点前面添加新节点或字符串
after节点后面添加新节点或字符串
replaceWith将节点替换为新节点或字符串

使用remove方法可以删除节点

insertAdjacentHTML

将html文本插入到元素指定位置,浏览器会对文本进行标签解析,包括以下位置
在这里插入图片描述
在div#app前添加HTML文本

<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
</div>
<script>
  let app = document.querySelector('#app')
  let span = document.createElement('span')
  app.insertAdjacentHTML('beforebegin', '<h1>后盾人</h1>')
</script>

insertAdjacentElement

insertAdjacentElement() 方法将指定元素插入到元素的指定位置,包括以下位置
第一个参数是位置
第二个参数为新元素节点
在这里插入图片描述
在div#app 前插入span标签

<div id="app">
  <div class="houdunren" data="hd">houdunren.com</div>
  <div class="hdcms">hdcms.com</div>
</div>
<script>
  let app = document.querySelector('#app')
  let span = document.createElement('span')
  span.innerHTML = '后盾人'
  app.insertAdjacentElement('beforebegin', span)
</script>

表单控制

JS为表单的操作提供了单独的集合控制

  • 使用document.forms获取表单集合
  • 使用form的name属性获取指定的form元素
  • 根据表单项的name属性使用form.elements.title获取表单项
  • 也可以直接写成form.name形式,不需要form.elements.title
  • 针对radio/checkbox获取的表单项是一个集合
<body>
  <form action="" name="hd">
    <input type="text" name="title" />
  </form>
  <script>
    const form = document.forms.hd
    console.log(form.title) //  console.log(form.elements.title)
  </script>
</body>

通过表单项可以反向查找FORM

<form action="" name="hd">
  <input type="text" name="title" />
</form>
<script>
  const form = document.forms.hd
  console.log(form.title.form === form) //true
</script>

样式管理

通过DOM修改样式可以通过修改元素的class属性或通过style对象设置行样式来完成
建议使用class控制样式,将任务交给css处理。

批量设置

使用JS的className可以批量设置样式

<head>
  <style>
    .pink{
      background-color: pink;
    }
    .fontSize{
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="text" class="pink">
    白日依山尽,黄河入海流
  </div>
  <script>
    const el = document.querySelector('#text');
    el.className += ' fontSize' //前面有空格 要形成class='pink fontSize'的样式
  </script>
</body>

也可以通过特征的方式修改

<head>
  <style>
    .pink{
      background-color: pink;
    }
    .fontSize{
      font-size: 30px;
    }
  </style>
</head>

<body>
  <div id="text" class="pink">
    白日依山尽,黄河入海流
  </div>
  <script>
    const el = document.querySelector('#text');
    el.setAttribute('class','pink fontSize')
  </script>
</body>

classList

如果对类单独进行控制使用classList属性操作

方法说明
node.classList.add添加类名
node.classList.remove删除类名
node.classList.toggle切换类名
node.classList.contains类名检测
<head>
  <style>
    .pink{
      background-color: pink;
    }
    .fontSize{
      font-size: 30px;
    }
  </style>
</head>

<body>
  <div id="text" class="pink">
    白日依山尽,黄河入海流
  </div>
  <script>
    const el = document.querySelector('#text');
    el.classList.add('fontSize')
  </script>
</body>

设置行样式

使用style对象可以对样式属性单独设置,使用cssText可以批量设置行样式
样式属性设置
使用节点的style对象来设置行样式
多个单词的属性使用驼峰进行命名

<div id="app" class="d-flex container">后盾人</div>
<script>
  let app = document.getElementById('app')
  app.style.backgroundColor = 'red'
  app.style.color = 'yellow'
</script>

批量设置行样式
使用 cssText属性可以批量设置行样式,属性名和写CSS一样不需要考虑驼峰命名

<div id="app" class="d-flex container">后盾人</div>
<script>
  let app = document.getElementById('app')
  app.style.cssText = `background-color:red;color:yellow`
</script>

也可以通过setAttribute改变style特征来批量设置样式

<div id="app" class="d-flex container">后盾人</div>
<script>
  let app = document.getElementById('app')
  app.setAttribute('style', `background-color:red;color:yellow;`)
</script>

获取样式

style
可以通过DOM对象的style属性读取行样式
style对象不能获取行样式外定义的样式

<style>
  div {
    color: yellow;
  }
</style>
<div id="app" style="background-color: red; margin: 20px;">后盾人</div>
<script>
  let app = document.getElementById('app')
  console.log(app.style.backgroundColor)
  console.log(app.style.margin)
  console.log(app.style.marginTop)
  console.log(app.style.color) //没有值,因为style只能获取行内定义的样式

getComputedStyle

使用window.getComputedStyle可获取所有应用在元素上的样式属性
函数第一个参数为元素
第二个参数为伪类
这是计算后的样式属性,所以取得的单位和定义时的可能会有不同

<body>
  <style>
    div {
      font-size: 35px;
      color: yellow;
    }
  </style>
  <div id="app" style="background-color: red; margin: 20px;">后盾人</div>
  <script>
    let app = document.getElementById('app')
    let fontSize = window.getComputedStyle(app).fontSize
    console.log(fontSize.slice(0, -2))//去掉px
    console.log(parseInt(fontSize))
  </script>
</body>

空间坐标

视口与文档

首先理解视口(窗口)与文档的含义

  • 网页很多都是多屛(通过滚动条显示看不见的内容),所以文档尺寸一般大于视口尺寸
  • 视口尺寸不包括浏览器工具条、菜单、标签、状态栏等
  • 当你打开控制台后,视口尺寸就相应变小额
  • position使用文档定位,fixed使用视口定位
  • 文档坐标在页面滚动时不发生变化
  • 视口坐标的操作需要考虑滚动条的位置

视口尺寸

方法说明注意
window.innerWidth视口宽度包括滚动条(不常用)
window.innerHeight视口高度包括滚动条(不常用)
document.documentElement.clientWidth视口宽度
document.documentElement.clientHeight视口高度

几何尺寸

元素在页面中拥有多个描述几何数值的尺寸
在这里插入图片描述
方法列表

方法说明备注
element.getBoundingClientRect返回元素在视口坐标及元素大小,包括外边距,width/height与offsetWidth/offsetHeight匹配窗口坐标
element.getClientRects行级元素每行尺寸位置组成的数组
element.offsetParent拥有定位属性的父级,或body/td/th/table对于隐藏元素/body/html值为null
element.offsetWidth元素宽度尺寸,包括内边距与边框和滚动条
element.offsetHeight元素高度尺寸,包括内边距与边框和滚动条
element.offsetLeft相对于祖先元素的x轴坐标
element.offsetTop相对于祖先元素的y轴坐标
element.clientWidth元素宽度,不包含边框,只包含内容和内边距,行元素尺寸为0
element.clientHeight元素高度,不包含边框,只包含内容和内边距,行元素尺寸为0
element.clientLeft内容距离外部的距离,滚动条在左侧时包括滚动条的尺寸
element.clientTop内容距离顶部的距离,滚动条在顶部时包括滚动条尺寸
element.scrollWidth元素宽度,内容+内边距+内容溢出的尺寸元素可滚动的最大距离
element.scrollHeight元素高度,内容+内边距+内容溢出的尺寸
element.scrollLeft水平滚动条左侧已经滚动的宽度
element.scrollTop垂直滚动条顶部已经滚动的高度

getComputedStyle

为什么不要使用getComputedStyle

  • 尺寸设置auto时获取结果不可用
  • 由于滚动条的存在,不同浏览器返回的结果不同
  • 当元素没有设置css尺寸时,获取不到相应的尺寸内容

getBoundingClientRect

使用 getBoundingClientRect获取元素相对于文档的几何坐标信息

<style>
    * {
        padding: 0;
        margin: 0;
    }
    main {
        padding: 200px;
        position: relative;
    }
    #app {
        width: 200px;
        background: #e34334;
        margin: 100px;
        padding: 50px;
        border: 20px solid #efbc0f;
        color: white;
        text-align: center;
    }
</style>
<main>
    <div id="app">houdunren.com</div>
</main>
<script>
    let app = document.getElementById('app')
    let info = app.getBoundingClientRect()
    console.table(info)
</script>

在这里插入图片描述

getClientRects

getClientRects用于返回多行元素所占的尺寸,下面示例将为每行返回所占的空间尺寸

<style>
	span {
    width: 200px;
    overflow: auto;
  }
</style>

<span>
网页很多都是多屏,所以文档尺寸一般大于视口尺寸,当打开控制台后,视口尺寸相应变小。网页很多都是多屏,所以文档尺寸一般大于视口尺寸,当打开控制台后,视口尺寸相应变小。网页很多都是多屏,所以文档尺寸一般大于视口尺寸,当打开控制台后,视口尺寸相应变小。
</span>
<script>
  let span = document.querySelector('span')
  let info = span.getClientRects()
  console.log(info)
</script>

在这里插入图片描述

坐标点元素

js提供了方法获取指定坐标上的元素,如果指定坐标点在视口外,返回值为null

  • 坐标都是从左上角计算,这与css中的right/bottom等不同
  • 窗口坐标类似于position:fixed
  • 文档坐标类似于position:absolute
方法说明
element.elementsFromPoint返回指定坐标点所在的元素集合
element.elementFromPoint返回指定坐标点最底层的元素

元素集合与底层元素

返回指定坐标点上的元素集合

<body>
  <style>
    div {
      width: 200px;
      height: 200px;
      background: pink;
    }
  </style>
  
  <div>

  </div>
  <script>
    let els = document.elementsFromPoint(100,100)
    console.log(els); //[div,body,html]
    let el = document.elementFromPoint(100,100)
    console.log(el); // 获取到div标签
  </script>
</body>

滚动控制

下方掌握文档或元素的滚动操作

方法列表

方法说明备注
element.scrollLeft获取和设置元素x轴滚动位置
element.scrollTop获取和设置元素y轴滚动位置
element.scrollBy()按偏移量进行滚动内容参数为对象,{top:垂直偏移量,left:水平偏移量,behavior:滚动方式}
element.scroll()或element.scrollTo()滚动到指定的具体位置参数为对象,{top:x轴文档位置,left:y轴文档位置,behavior:滚动方式}
element.scrollIntroView(boolean)定位到顶部或底部参数为true元素定位到底部,为false定位窗口底部

文档滚动位置

下例是查看文档滚动的X/Y坐标示例,请在控制台查看结果
也可以使用window.pageXOffset对象属性获取

<body>
  <style>
    div {
      width: 2000px;
      height: 2000px;
      background: pink;
    }
  </style>
  
  <div>

  </div>
  <script>
  //   window.onscroll = function () {
  //       console.log(document.querySelector('html').scrollTop)
  //       console.log(document.documentElement.scrollLeft)
  //  }
   window.onscroll = function () {
        console.log(window.pageXOffset)
        console.log(window.pageYOffset)
   }
  </script>
</body>

元素滚动位置

<body>
  <style>
    #parent {
      width: 200px;
      height: 200px;
      background: pink;
      overflow: auto;
    }
    #son{
      width: 400px;
      height: 400px;
    }
  </style>
  
  <div id="parent">
    <div id="son">

    </div>
  </div>
  <script>
    document.getElementById('parent').addEventListener('scroll',function(){
      console.log(this.scrollLeft);
      console.log(this.scrollTop);
    })
  </script>
</body>

控制滚动

下面介绍的是控制元素滚动的操作方法
scrollBy

behavior:smooth 为平滑滚动 按照偏移量进行移动

<body>
  <script>
    setTimeout(()=>{
      document.documentElement.scrollBy({top: 300, behavior: 'smooth'})
    },1000)
  </script>
</body>

scroll
使用scroll滚动到指定位置 移动到指定位置

<body>
  <script>
    setTimeout(()=>{
      document.documentElement.scrollBy({top: 300, behavior: 'smooth'})
    },1000)
  </script>
</body>

scrollIntoView
使用元素scrollIntoView方法实现滚动操作,参数可以是布尔值和对象

  • 参数为true时顶部对齐,相当于{block: ‘start’}
  • 参数为false时底部对齐,相当于{block: ‘end’}
  • 也可以定义{behavior: ‘smooth’}来进行平滑滚动
<head>
  <style>
    div {
        height: 2000px;
        background: red;
        border-top: solid 50px #efbc0f;
        border-bottom: solid 50px #1bb491;
    }
    span {
        border-radius: 50%;
        color: #fff;
        background: #000;
        width: 50px;
        height: 50px;
        display: block;
        text-align: center;
        line-height: 50px;
        position: fixed;
        top: 50%;
        right: 50px;
        border: solid 2px #ddd;
    }
</style>
<div id="app">hdcms.com</div>
<span>TOP</span>

<script>
    document.querySelector('span').addEventListener('click', () => {
        let app = document.querySelector('#app')
        app.scrollIntoView({ block: 'end', behavior: 'smooth' })
    })
</script>
</body>

事件

处理程序

事件的目的是要执行一段代码,我们称这类代码为事件处理程序。当在对象上触发事件时就会执行定义的事件处理程序

HTML绑定

可以在html元素上设置事件处理程序,浏览器解析后会绑定到DOM属性中

<button onclick="alert(`houdunren.com`)">后盾人</button>

往往事件处理程序业务比较复杂,所以绑定方法或函数会常见

  • 绑定函数或方法时需要加上括号
<button onclick="show()">后盾人</button>
<script>
  function show() {
    alert('houdunren.com')
  }
</script>

也可以使用方法作为事件处理程序

<input type="text" onkeyup="HD.show()" />
<script>
  class HD {
    static show() {
      console.log('houdunren')
    }
  }
</script>

可以传递事件源对象与事件对象

<body>
  <button onclick="show(this,'houdunren','hdcms','向军大叔',event)">后盾人</button>
<script>
    function show(...args) {
        console.log(args)
    }
</script>
</body>

DOM绑定

也可以将事件处理程序绑定到DOM属性中

  • 使用setAttribute方法设置事件处理程序无效
  • 属性名区分大小写
<div id="app">houdunren.com</div>
<script>
  const app = document.querySelector('#app')
  app.onclick = function () {
    this.style.color = 'red'
  }
</script>

无法为事件类型绑定多个事件处理程序,下面绑定了多个事件处理程序,因为属性是相同的所以只有最后一个有效

<div id="app">houdunren.com</div>
<script>
  const app = document.querySelector('#app')
  app.onclick = function () {
    this.style.color = 'red'
  }
  app.onclick = function () {
    this.style.fontSize = '55px'
  }
</script>

事件监听

通过上面的说明我们知道使用HTML与DOM绑定事件都有缺陷,所以我们可以使用事件监听的方法
使用addEventListener添加事件处理程序有以下几个特点

  • transtionenmd/DOMContentLoaded等事件类型只能使用addEventListener处理
  • 同意事件类型设置多个事件处理程序,按设置的顺序先后执行
  • 也可以对未来添加的元素绑定事件
方法说明
addEventListener添加事件处理程序
removeEventListener移除事件处理程序

*绑定多个事件

设置多个事件处理程序,按设置的顺序先后执行

<body>
  <div id="app">houdunren.com</div>
  <script>
    const app = document.querySelector('#app')
    app.addEventListener('click', function () {
      this.style.color = 'red'
    })
    app.addEventListener('click', function () {
      this.style.fontSize = '55px'
    })
  </script>
</body>

*通过对象绑定

若果事件处理程序可以使对象,对象的handleEvent方法会作为事件处理程序执行。

<body>
  <div id="app">houdunren.com</div>
<script>
  const app = document.querySelector('#app')
  class HD {
    handleEvent(e) { //e为鼠标事件
      console.log(e.type);//事件类型
      this[e.type](e) //this.click(e) 相同
    }
    click(e) {
      console.log('单击事件')
    }
    mouseover(e) {
      console.log('鼠标移动事件')
    }
  }
  app.addEventListener('click', new HD())
  app.addEventListener('mouseover', new HD())
</script>
</body>

###移除事件
使用removeEventListener删除绑定的事件处理程序
事件处理程序单独定义函数或方法,这可以保证事件处理程序是同一个

<div id="app">houdunren.com</div>
<button id="hd">删除事件</button>

<script>
  const app = document.querySelector('#app')
  const hd = document.querySelector('#hd')
  function show() {
    console.log('APP我执行了')
  }
  app.addEventListener('click', show)
  hd.addEventListener('click', function () {
    app.removeEventListener('click', show)
  })
</script>

*事件选项

addEventListener的第三个参数为定制的选项,可传递object或boolean类型
下面是传递对象时的说明

once可选参数说明
oncetrue/false只执行一次事件
capturetrue/false事件是在捕获/冒泡哪个阶段执行,true为捕获阶段 false为冒泡阶段
passivetrue/false声明事件里不会调用 preventDefault(),可减少系统默认行为的等待

下面使用once:true 来指定事件只执行一次

<body>
  <button id="app">houdunren.com</button>
<script>
    const app = document.querySelector('#app')
    app.addEventListener(
        'click',
        function () {
            alert('houdunren@向军大叔')
        },
        { once: true }
    )
</script>
</body>

设置{capture:true} 或直接设置第三个参数为true用来在捕获阶段执行事件
addEventListener的第三个参数传递true/false 和设置 {capture:true/false}是一样

<div id="app" style="background-color: red">
    <button id="bt">houdunren.com</button>
</div>
<script>
    const app = document.querySelector('#app')
    const bt = document.querySelector('#bt')
    app.addEventListener(
        'click',
        function () {
            alert('这是div事件 ')
        },
        { capture: true }
    )

    bt.addEventListener(
        'click',
        function () {
            alert('这是按钮事件 ') //点击按钮先出来div事件然后才是按钮事件
        },
        { capture: true }
    )
</script>

设置 { capture: false } 或直接设置第三个参数为false用来在冒泡阶段执行事件

<div id="app" style="background-color: red">
    <button id="bt">houdunren.com</button>
</div>
<script>
    const app = document.querySelector('#app')
    const bt = document.querySelector('#bt')
    app.addEventListener(
        'click',
        function () {
            alert('这是div事件 ')
        },
        { capture: false }
    )

    bt.addEventListener(
        'click',
        function () {
            alert('这是按钮事件 ')//点击按钮时,先触发按钮事件,然后才是div事件
        },
        { capture: false }
    )
</script>

事件对象

执行事件处理程序时,会产生当前事件相关信息的对象,即为事件对象。系统会自动做为参数传递给事件处理程序。

  • 大部分浏览器将事件对象保存到window.event中
  • 有些浏览器会将事件对象做为事件处理程序的参数传递
    事件对象常用属性如下
属性说明
type事件类型
currentTarget当前执行事件的对象
target事件目标对象,冒泡方式时父级对象可以通过该属性找到在哪个子元素上最终执行事件
x相对窗口的x坐标
y相对窗口的y坐标
clientX相对窗口的x坐标
clientY相对窗口的y坐标
screenX相对计算机屏幕的x坐标
screenY相对计算机屏幕的y坐标
pageX相对于文档的x坐标
pageY相对于文档的y坐标
offsetY相对于事件对象的x坐标

在这里插入图片描述

冒泡捕获

冒泡行为

标签元素是嵌套的,在一个元素上触发的事件,同时也会向上执行父级元素的事件处理程序,一直到HTML标签元素

  • 大部分事件都会冒泡,但像focus事件则不会
  • event.target可以在事件链中最底层的定义事件的对象
  • event.currentTarget == this 即当前执行事件的对象

阻止冒泡

冒泡过程中的任何事件处理程序中,都可以执行 event.stopPropagation() 方法阻止继续进行冒泡传递

  • event.stopPropagation() 用于阻止冒泡
  • 如果同一类型事件绑定多个事件处理程序 event.stopProagation()只能阻止当前的事件处理程序
  • event.stipImmediatePropagation()阻止事件冒泡并且阻止相同事件的其他事件处理程序被调用
<body>
  <style>
    #app {
      background: #34495e;
      width: 300px;
      padding: 30px;

    }
    #app h2 {
      background-color: #f1c40f;
    }
  </style>
  <div id="app">
    <h2>我是h2子元素</h2>
  </div>
  <script>
    const app = document.querySelector('#app')
    const h2 = document.querySelector('h2')
    app.addEventListener('click', (event) => {
      console.log(`event.currentTarget:${event.currentTarget.nodeName}`)
      console.log(`event.target:${event.target.nodeName}`)
      console.log('app event')
    })
    h2.addEventListener('click', (event) => {
      event.stopPropagation() //阻止冒泡
      //event.stopImmediatePropagation(); //下面h2绑定的点击事件和冒泡不会触发
      console.log(`event.currentTarget:${event.currentTarget.nodeName}`)
      console.log(`event.target:${event.target.nodeName}`)
      console.log(`h2 event`)
    })
     h2.addEventListener('click', (event) => {
      console.log('h2 的第二个事件处理程序')
     })
  </script>
</body>

事件捕获

事件执行顺序为 捕获>事件目标>冒泡,在向下传递到目标对象的过程即为事件捕获。
通过设置第三个参数为true或{capture:true}在捕获阶段执行事件处理程序
上面事件选择有例子

事件代理

借助事件冒泡思路,我们可以不为子元素设置事件,而将事件设置在父级。返回通过父级事件对象的event.target查找子元素,并对他做出处理。

  • 这在为多个元素添加相同事件时很方便
  • 会使添加事件变得非常容易
    下面是为父亲UL设置事件来控制子元素LI的样式切换
<style>
  .hd {
    border: solid 2px #ddd;
    background-color: red;
    color: white;
  }
</style>
<ul>
  <li>houdunren.com</li>
  <li>hdcms.com</li>
</ul>

<script>
  'use strict'
  const ul = document.querySelector('ul')
  ul.addEventListener('click', () => {
    if (event.target.tagName === 'LI') event.target.classList.toggle('hd')
  })
</script>

事件代理例子2

<body>
  <div class="tab">
    <dl>
      <dt data-action="toggle">在线教程</dt>
      <dd data-action="hidden">houdunren.com</dd>
    </dl>
    <dl>
      <dt data-action="toggle">开源软件</dt>
      <dd data-action="hidden">hdcms.com</dd>
    </dl>
  </div>
  
  <script>
    class HD {
      constructor(el) {
        this.el = el
        el.addEventListener('click', (event)=>{
          const action = event.target.dataset.action
        if (action) this[action]()
        })
      }
     
      hidden() {
        event.target.hidden = true
      }
      toggle() {
        this.el.querySelectorAll(`[data-action='hidden']`).forEach((e) => (e.hidden = true))
        event.target.nextElementSibling.hidden = false
      }
    }
    new HD(document.querySelector('.tab'))
  </script>
</body>

未来元素

下面使用事件代理来对未来元素进行事件绑定

<body>
  <button>
    添加一个小li
  </button>
  <ul id="list">

  </ul>
  
  <script>
    let i=0;
    const ul = document.querySelector('#list');
    document.querySelector('button').addEventListener('click',()=>{
      const li =  document.createElement('li');
      li.innerHTML= `我是${i++}`;
        ul.append(li);
    })
    ul.addEventListener('click',(e)=>{
      console.log(e.target.innerHTML);
    })
  </script>
</body>

默认行为

js中有些对象会设置默认事件处理程序,比如a标签点击时会进行跳转
一般默认处理程序会在用户自定义的处理程序后执行,所有我们可以在我们定义的事件处理中取消默认事件处理程序的执行。

  • 使用onclick绑定的事件处理程序,return false 可以阻止默认行为
  • 推荐使用 event.preventDefault()阻止默认行为
<a href="https://www.houdunren.com">后盾人</a>
<script>
  document.querySelector('a').addEventListener('click', () => {
    event.preventDefault()
    alert(event.target.innerText)
  })
</script>

窗口文档

事件类型

事件名说明
window.onload文档解析及外部资源加载后
DOMContentLoaded文档解析后执行,不需要等待图片/样式文件等外部资源加载,该事件只能通过addEventListener设置
window.beforeunload文档刷新或关闭时
window.unload文档卸载时
scroll页面滚动时

onload

window.onload事件在文档解析后及图片、外部样式文件等资源加载完后执行

<script>
  window.onload = function () {
    alert('houdunren.com')
  }
</script>
<div id="app">houdunren.com</div>

DOMContentLoaded

DOMContentLoaded事件在文档标签解析后执行,不需要等外部图片、样式文件、JS文件等样式资源加载

<script>
  window.addEventListener('DOMContentLoaded', (event) => {
    console.log('houdunren.com')
  })
</script>
<div id="app">houdunren.com</div>

beforeunload

当浏览器窗口关闭或者刷新时,会触发beforeunload事件,可以取消关闭或刷新页面

  • 返回值为非空字符串时,有些浏览器会做为弹出的提示信息内容
  • 部分浏览器使用addEventLIstener无法绑定事件
window.onbeforeunload = function (e) {
  return '真的要离开吗?'
}

unload

window.unload事件在文档资源被卸载时执行

  • 不能执行alert、confirm等交互指令
  • 发生错误也不能阻止页面关闭或更新
window.addEventListener('unload', function (e) {
  localStorage.setItem('name', 'houdunren')
})

鼠标事件

事件类型

在这里插入图片描述

事件对象

在这里插入图片描述

禁止复制

<body>
  houdunren.com
  <script>
    document.addEventListener('copy', () => {
      event.preventDefault()
      alert('禁止复制内容')
    })
  </script>
</body>

键盘事件

在这里插入图片描述

事件对象

键盘事件产生的事件对象包含相对应的属性
在这里插入图片描述

表单事件

下面是可以用在表单上的事件类型
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值