js基础

我们省略到最基本的语法内容,像数据类型,循环,函数,数组等,直接从对象开始。

第一章 对象

1.1 对象概述

什么是对象(object)? 简单来说,对象就是一组键值对(key-value)的集合,是一种无序的复合数据集合。

大括号:定义一个对象
person:定义的对象被赋值给person,person指向这个对象
name:henry 键值对(key:value)键值之间用:隔开
一共对象中可以包含多个键值对,每个键值对之间用逗号隔开,最后一个键值对可以不加逗号

注意这个person里面保存的是对象的内存地址,而不是对象,我们把这种赋值称为引用,上面的定义对象的方法叫做字面量
键名:
对象的键名基本都是字符串,可以加或者不加引号

let person = {
  name: 'henry',
  age: 18
}

// 和上面的写法意思一样
let person = {
  'name': 'henry',
  'age': 18
}

方法 :键名又称为属性,它的键值可以是任何的数据类型,当键值为一个函数时,我们把属性(键名)称为方法

let person = {
  name: 'henry',
  age: 18,
  run: function() {
    console.log('running');
  }
}

person.run();

对象的创建: 除了字面量的方法,我们还可以通过构造函数创建新对象。
分成两步 ,第一步 创造一个构造函数,构造函数的名称根据大驼峰的方式命名
第二步 通过new创建对象实例
其中构造函数可以声明对象的名称属性方法

// 第一步:创建构造函数
function People(name, age) {
  this.name = name;
  this.age = age;
}

// 第二步:通过 new 创建对象实例
let person = new People('henry', 18);
console.log(person);

注意 :我们先用为people创建了一个构造函数 People,然后用构造函数People创建了对象实例 person.
构造函数中的this是函数运行时所在的环境对象,它是在函数体内部自动生成的对象,只能在函数体内部使用。
我们用构造函数来创建一个对象实例,那么构造函数的this.name = name;就表示将传入的参数name赋值给对象实例中对应的属性name.

1.2 自定义属性的属性操作

调用方法和调用函数的写法是相同的

person.run();

属性的读取:js有两种方法读取对象的属性 点运算符和方括号运算符

let person = {
  name: 'henry',
  age: 18
}

console.log(person.name);
console.log(person['name']);

不同的是方括号里面可以是一个变量

let person = {
  name: 'henry',
  age: 18
}

let variable = 'name';               
console.log(person[variable]);       //可以是变量

variable = 'age';
console.log(person[variable]);      //可以是变量

如果对象的属性的值还是一个对象那么我们还是可以使用点运算符或者方括号运算符

let person = {
  name: 'henry',
  age: 18,
  parents: {                    //大括号里面是一个对象
    papa: 'jack',
    mama: 'mary'
  }
}

console.log(person.parents.papa);
console.log(person['parents']['mama']);  //链式引用

属性的赋值:也可以通过点运算符或者方括号运算符完成

let person = {
  name: 'henry',
  age: 18
}

person.name = 'tom';          //赋值
person['age'] = 10

console.log(person.name);
console.log(person.age);

属性的查看:查看所有的属性可以使用Object.keys方法

let person = {
  name: 'henry',
  age: 18
}

console.log(Object.keys(person)); // object是基本对象,java中所有的其他的对象都是继承于object对象,即都是object实例
                                   keys是object对象的一个静态方法

属性的删除和增加
删除:使用delete命令

let person = {
  name: 'henry',
  age: 18
}
delete person.name;
console.log(person);             //{ age: 18 }

添加属性

let person = {
  name: 'henry',
  age: 18
}
person.gender = 'male';      // { name: 'henry', age: 18, gender: 'male' }

1.3 遍历对象属性

在js中,我们可以通过for…in或者object.keys来实现对象属性的遍历
用for…in遍历属性 :返回的是键名

let person = {
  name: 'henry',
  age: 18,
}

for (var key in person) {                   //for ...in
  console.log('键名:' + key + ';键值:' + person[key]);
}

用Object.keys遍历属性:返回的是一个由对象所有 属性名 组成的数组

let person = {
  name: 'henry',
  age: 18,
}

let keys = Object.keys(person);

for (let i = 0; i < keys.length; i++) {
  console.log('键名:' + keys[i] + ';键值:' + person[keys[i]]);
}

1.4 对象的继承

除了字面量和自定义的构造方法创建对象外,我们还可以通过js提供的构造函数object()或者继承来创建对象

// 字面量
let o1 = {
  name: 'alice',
};

// 构造函数
let o2 = new Object();
let o3 = new Object();

// 继承 
let o4 = new o1();                //04对象就是继承于01  01就是原型

原型:一个对象,它所继承的上一级对象是它的原型,它也是继承它的下一级对象的原型
属性是否存在:in
我们可以通过in来判断对象的属性是否存在

let person = {
  name: 'henry',
  age: 18,
};

'name' in person;              //true
'gender' in person;           // false
'toString' in person;        //true   toString属性是object对象的属性person继承了object所以person也有tostring属性

由上面可以看出对象拥有的属性包括自己的属性和继承的属性

我们如何判断自身属性是否存在:hasOwnProperty可以帮我们解决这个问题

let person = {
  name: 'henry',
  age: 18,
};

person.hasOwnProperty('name');         //true
person.hasOwnProperty('gender');       //false
person.hasOwnProperty('toString');     //false  可以看出tostring属性不是自身的属性

object 与 map,json的区别
json:一种轻量级的文本数据交换格式,可以看出编程语言之间用于传递数据的的一种数据格式
json格式和js对象的转化

1 json=>js对象:JSON.parse()

// 一个 JSON 字符串
const jsonStr =
  '{"sites":[{"name":"Runoob", "url":"www.runoob.com"},{"name":"Google", "url":"www.google.com"},{"name":"Taobao", "url":"www.taobao.com"}]}';

// 转成 JavaScript 对象
const obj = JSON.parse(jsonStr);

2 js对象转化为json:JSON.stringify()

const jsonStr2 = JSON.stringify(obj)

map :它和object有些相似,都可以保存键值对,但是他们任然有一些重大的区别
1 object的键通常是字符串,map可以是任意类型(函数 对象等),因此map会比较方便
2 object的键是无序的,而map中的键是有序的
3 object的键值对数目需要使用object.keys()计算得到,而map可以直接得到
4 Object需要借助object.keys()进行迭代,而map可以直接进行迭代
5 object存在键名和原型键名冲突的问题,而map不存在,可以直接覆盖
但是map不能直接转化为json格式进行通讯,可以把把map看作object的补充

1.5 内置对象,math-storage

math对象:math也是js的原生对象,不过它不是构造函数,不能生成实例,所有的属性方法都必须在math对象上面调用。
我们来看看math提供的一些用途
常量

Math.E // 常数e。
Math.LN2 // 2 的自然对数。
Math.LN10 // 10 的自然对数。
Math.LOG2E // 以 2 为底的e的对数。
Math.LOG10E // 以 10 为底的e的对数。
Math.PI // 常数π。
Math.SQRT1_2 // 0.5 的平方根。
Math.SQRT2 // 2 的平方根。

静态方法

Math.abs() // 绝对值
Math.ceil() // 向上取整
Math.floor() // 向下取整
Math.round() // 四舍五入取整
Math.max() // 最大值
Math.min() // 最小值
Math.pow() // 指数运算
Math.sqrt() // 平方根
Math.log() // 自然对数
Math.exp() // e的指数
Math.random() // 随机数

storge对象:用于脚本在浏览器保存数据,两个对象部署了这个接口 window.sessionStorage和window.localStorage
sessionStorage:保存的数据用于浏览器的一次会话,当会话结束(通常是窗口被关闭),数据被清空
localStorage:保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。
我们来主要看一下localStorage的用法:
数据的写入setItem

window.localStorage.setItem('myLocalStorage', 'storage Value');  //参数 键名和键值 都是字符串,
                   不是字符串也要转化为字符串再存入浏览器  JSON.stringify()可以将对象转化为json字符串

读取数据:getItem()

window.localStorage.getItem('myLocalStorage');  //参数是键名

我们可以通过f12 application storage查看储存的数据

清除缓存 :window.localStorage.clear();

1.6 内置对象 string

包装对象:原生对象可以把原始类型的值包装成对象

let v2 = new String('abc');

js提供了三大包装对象: string number和boolean

包装对象的好处:1 使js对象涵盖所有的值
2 使原始数据的值能够很方便的调用一些方法 例如
字符串长度 length

let len = 'here is an apple'.length;

查找字符串:indexOf()

let str = 'here is an apple';
const index = str.indexOf('an');                    //查找an字符串,返回下标(从0开始)  不存在返回-1
console.log(index);

去掉两端空格:trim() //不改变原字符串,只是返回处理后的结果

// 'here' 之前有一个空格,'apple' 之后有三个空格
let str = ' here is an apple   ';
const trimedStr = str.trim();         //去掉首尾的空格
console.log(str.length);
console.log(trimedStr.length);

截取字符串:substring/substr //不改变原字符串,只是返回处理后的结果

let url = 'https://www.youkeda.com/userhome#collect';

// 首先找到 # 后第一个字母的下标
const index = url.indexOf('#');

// 有 hash 才能进行截取,没有就直接提示不存在
if (index) {
  // 用 substring 截取字符串
  const hash1 = url.substring(index + 1, url.length); //参数为开始时的下标和结束时的下标,如果第二个参数不设置,会截取到字符串的最后面

  // 计算 hash 的长度
  const lenHash = url.length - index - 1;
  // 用 substr 截取字符串
  const hash2 = url.substr(index + 1, lenHash);     //参数为开始时的下标,和要截取的长度,如果第二个参数不设置,会截取到字符串的最后面

  console.log(hash1);
  console.log(hash2);
} else {
  console.log('不存在 hash');
}

分割字符串:spilt 返回一个数组

const splitedStr = 'a|b|c'.split('|');   //按照/来分割
console.log(splitedStr);

几个方法汇总:

属性/方法	                作用
str.length	            返回字符串长度
str.indexOf(sub)	    返回子字符串 sub 的开始下标,不存在则返回 -1   注意:这里的参数 sub 是个字符串变量
                        
str.trim()	            字符串前后去空格
str.substring(s, e)	    截取下标从 s 到 e 的子字符串       注意:这里的参数 s 和 e是个数字变量

str.substr(s, len)	    截取下标从 s 开始,长度为 len 的子字符串  注意:这里的参数 s 和 len 是个数字变量

str.split(pattern)	    按规格 pattern 分割字符串     注意:这里的参数 pattern 是个字符串变量

1.7 内置对象 array

array也是js的原生对象,它为数组提供了很多实用的方法,这里我们介绍几个比较常用的
连接数组 :join() 以指定参数作为分隔符,将数组成员连接为一个字符串返回

let arr = [1, 2, 3, 4];

arr.join(' ') // '1 2 3 4'
arr.join(' | ') // "1 | 2 | 3 | 4"
arr.join() // "1,2,3,4"

可以看出和spilt的作用相反

倒序排列:reverse()
将数组元素颠倒排列然后返回数组

let arr = ['a', 'b', 'c'];
arr.reverse() // ["c", "b", "a"]
arr // ["c", "b", "a"]

排序:sort()
将数组成员默认按照字典顺序排序,如果自定义排序,只需要传入一个函数作为参数

let arr = [
  { name: 'jenny', age: 18 },
  { name: 'tom', age: 10 },
  { name: 'mary', age: 40 }
];

arr.sort(function(a, b) {
  return a.age - b.age;         //返回值大于0 将第一个成员放在第二个成员后面
});

console.log(arr);

遍历:map和forEach
有返回值的遍历 map
它接受一个函数作为参数,然后将所有数组成员传入函数中,再将结果处理为数组返回

let arr = [
  { name: 'jenny', age: 18 },
  { name: 'tom', age: 10 },
  { name: 'mary', age: 40 }
];

// elem: 数组成员
// index: 成员下标
// a: 整个数组
const handledArr = arr.map(function(elem, index, a) {
  elem.age += 1;
  console.log(elem, index, a);
  return elem.name
});

console.log(arr);
console.log(handledArr);

无返回值的遍历数组:foreach

const handledArr = arr.forEach(function(elem, index, a) {
  elem.age += 1;
  console.log(elem, index, a);
  return elem.name
});

console.log(handledArr);            //无返回值 undefined

array的几个方法总结

属性/方法	             作用
arr.join(pattern)	按规则 pattern 连接数组,返回字符串
arr.reverse()	    将原数组倒序排列
arr.sort(func)	    自定义排序,根据传入的参数函数 func 将数组成员排序
arr.map(func)	     根据传入的参数函数 func 对数组进行遍历操作,返回操作后的数组
                      函数有三个参数,依次为:数组成员、对应下标、整个数组
arr.forEach(func)	 根据传入的参数函数 func 对数组进行遍历操作,无返回值
                     函数有三个参数,依次为:数组成员、对应下标、整个数组

1.8 内置对象 date

js提供了一个原生的时间库:date对象 它是以国际标准时间1970.01.01.00.00.00为时间零点。单位是毫秒,前后各一亿天。

获取当前时间:new date()
可以把date看作一个构造函数,然后获取它的实例,在不加参数的情况下,返回的就是当前的时间

let now = new Date();
console.log(now);                  //返回当前时间

传入一些参数,就会生成特定的时间对象

// 传入表示“年月日时分秒”的数字
let dt1 = new Date(2020, 0, 6, 0, 0, 0);
console.log(dt1);

// 传入日期字符串
let dt2 = new Date('2020-1-6');
console.log(dt2);

// 传入距离国际标准时间的毫秒数
let dt3 = new Date(1578240000000);
console.log(dt3);

日期运算
时间差:毫秒数 两个时间对象是可以直接相减的,返回的结果是二者的毫秒数差

let dt1 = new Date(2020, 2, 1);
let dt2 = new Date(2020, 3, 1);

// 求差值
let diff = dt2 - dt1;

// 一天的毫秒数
let ms = 24 * 60 * 60 * 1000;
console.log(diff / ms); // 31

早晚比较:大于小于符号
比较两个时间的早晚,可以使用大于小于符号,返回boolean值。

let dt1 = new Date(2020, 2, 1);
let dt2 = new Date(2020, 3, 1);

console.log(dt1 > dt2); // false
console.log(dt1 < dt2); // true

解析日期字符串:date.parse()
解析字符串,返回的是距离时间零点的毫秒数

let dt = Date.parse('2020-1-6');
console.log(dt); // 1578240000000

时间对象转时间字符串:to方法

let dt = new Date();
let dtStr = dt.toJSON();        //toJson方法

console.log(dtStr); //  我们是东八区,所以会快8个小时

获取时间对象的的年月日:get方法

let dt = new Date();
dt.getTime(); // 返回实例距离1970年1月1日00:00:00的毫秒数。
dt.getDate(); // 返回实例对象对应每个月的几号(从1开始)。
dt.getDay(); // 返回星期几,星期日为0,星期一为1,以此类推。
dt.getFullYear(); // 返回四位的年份。
dt.getMonth(); // 返回月份(0表示1月,11表示12月)。
dt.getHours(); // 返回小时(0-23)。
dt.getMilliseconds(); // 返回毫秒(0-999)。
dt.getMinutes(); // 返回分钟(0-59)。
dt.getSeconds(); // 返回秒(0-59)。

设置时间对象的年月日 :set方法

let dt = new Date();
dt.setTime(ms); // 设置实例距离1970年1月1日00:00:00的毫秒数。
dt.setDate(date); // 设置实例对象对应每个月的几号(从1开始)。
dt.setFullYear(year); // 设置四位的年份。
dt.setMonth(month); // 设置月份(0表示1月,11表示12月)。
dt.setHours(hour); // 设置小时(0-23)。
dt.setMilliseconds(ms); // 设置毫秒(0-999)。
dt.setMinutes(min); // 设置分钟(0-59)。
dt.setSeconds(sec); // 设置秒(0-59)。

时间对象,毫秒数,日期字符串之间的转化

第二章 BOM

2.1 BOM

BOM:浏览器对象模型 它由一系列对象组成,每个对象都提供了很多方法和属性
我们以chrome为准,学习一些BOM共同的对象和api

BOM对象中比较重要的4个对象:
1 window窗口:window是整个网页的框架,每个网页的内容都是装载在window中的
2 navigator浏览器:navigator里面存储浏览器相关信息
3 history历史 我们知道每个网页都可以前进后端,history便是用来存储整个网页栈的。
4 screen显示屏幕 screen包含我们显示屏幕的信息,这个是硬件信息
5 location地址 location里面当前访问的地址信息(网址)

注意:
1 screen是整个电脑唯一的
2 navigator是浏览器唯一的,如果有多个浏览器就会有多个navigator
3 window是每个网页唯一的,每个网页都会有自己独立的window
4 histor和location是网页唯一的,是每个网页的信息

2.2 window

在html中嵌入js
我们在html body底部加入,嵌入执行脚本,然后在脚本中添加代码。

window对象中,有很多方法,比如alert, confirm ……
还有一些属性对象比如: console, screen, navigator, location……

官方对window对象的解释:1 window对象表示一个浏览器窗口或一个frame框架,它处于对象层次的最顶端,它提供了处理浏览器窗口的属性和方法
2 window对象是浏览器对象的默认对象,所以可以隐式地引用window对象的属性和方法。在浏览器环境中,添加到
window对象中的属性或者方法,其作用域都是全局的。
什么叫默认对象,什么叫隐式引用?

console.log('优课达');
window.console.log('优课达');  //可以看出二者等同

console.log(navigator);
console.log(window.navigator); //可以看出二者等同

function hello() {}
console.log(hello);
console.log(window.hello);   //甚至自定义的顶层函数,也是挂载在window上的。 包括Math对象,setTimeout函数,setInterval函数

总结:window是默认对象,如果调用window上的方法可以省略,也可以称为隐式调用window上面的属性和方法。

2.3 location/history

location常用的属性:


属性	                        值	                                                 解释
href	https://resource.youkeda.com/wss_test/5dc54e230f101ed7c2256d0d/
5e33d104680dca7ebc7a223b/index.html?time=1580454161498	                          href 是整个网页地址

hostname	           resource.youkeda.com	                                      hostname 是网页域名
host	               resource.youkeda.com	        host 是网页域名 + 端口信息,在这里端口默认 80 省略了,所有和 hostname 一样
protocol	           https	                                                  protocol 代表协议信息
origin	               https://resource.youkeda.com	origin                        页面来源的域名的标准形式
pathname	/5dc54e230f101ed7c2256d0d/5e33d104680dca7ebc7a223b/index.html	       pathname 包含 url 路径部分
search	               ?time=1580454161498	                                       search 表示 URL 参数

location方法:reload()方法

setTimeout(function () {
  window.location.reload();                //定时器延迟调用
}, 3000);

跳转到新的地址

window.location = 'https://www.youkeda.com';   //赋值网页地址即可

history:它会存储窗口的历史纪录,也允许操作浏览器曾经的标签页或者框架内的会话历史纪录。
举一个例子

如果原始网页为https://www.youkeda.com,
那 history 中存储为
['https://www.youkeda.com'];
如果我们在网页中点击某个链接,或者使用window.location = xxx跳转到https://www.baidu.com, 那 history 中存储为
 ['https://www.youkeda.com', 'https://www.baidu.com'];  //这是一个数组,或者说列表,也叫栈

history应该掌握两个方法back()和forward() 对应浏览器左上角的返回和前进按钮

2.4 navigator/screen

navigator:表示用户代理的标识和状态,也就是浏览器的基本信息。
userAgent:当前浏览器的用户代理

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/79.0.3945.130 Safari/537.36

Mozilla是一个基金会,表示这是一个主流浏览器
Intel Mac OS X 10_15_2表示电脑基本信息为mac
Chrome/79.0.3945.130 表示浏览器版本

第三章 DOM和DOM操作

3.1 初始dom

dom(文档对象模型)是js甚至是前端最核心的内容。
文档对象模型可以把web页面与脚本或者编程语言连接起来。

web页面:html和css绘制的页面,也称为文档
脚本或编程语言: dom是一种规范,一种约定,只要遵循这个规范,编程语言与文档就可以连接起来

DOM映射:像html和xml这种形式的文档都是树状结构,也对应数据结构中的数

<html>
  <head>
    <title>youkeda</title>
  </head>
  <body>
    <div>
      <h1>优课达</h1>
      <p>学的比别人好一点</p>
    </div>
  </body>
</html>

我们把上面这个html文档转化为树状结构

上面这棵树就是DOM树,我们来看看它的一些特性:
1 树根是 DOCUMENT,也可以称为整个页面文档
2 每个 HTML 标签我们称之为 DOM 节点,英文为Node或者ELement
3 每个 HTML 标签包裹的子标签,在树上体现为分支,称为儿子节点。比如上图,P和H1都是DIV的儿子节点。DIV同样也是BODY的儿子节点。
4 儿子节点类推可以得知P,H1是BODY的孙子节点。
5 所有P, H1的长辈,我们称为P和H1的祖先节点。
6 P, H1是亲兄弟,我们称为兄弟节点。

3.2 访问DOM

如何获取document(DOM树的根部元素)?

window.document;  //得到一个HTMLDocument对象,其中body, head属性,分别对应 HTML 中的body,head内容。

选择器查询:
我们想要单独查询某一个节点该怎么办?例如查询一个subtitle节点。
我们可以使用选择器查询方法,querySelector() //查询第一个满足条件的节点

document.querySelector('main .core .subtitle');  //参数为选择器名称,为了防止查询到多个同名节点,我们加上一些筛选条件

迭代查询:当我们查询到一个节点后,还可以利用这个节点,继续查询内部的节点例如

let subtitle = document.querySelector('main .core .subtitle');
console.log(subtitle.querySelector('a'));     //查询subtitle节点内部的节点a

选择器全量查询:查询所有满足条件的节点querySelectorAll()方法

document.querySelectorAll('input');        //查询返回的是一个数组,可以用索引访问内部元素

其他的筛选方法:

getElementById():         根据 id 查询某个节点
getElementsByClassName(): 根据 class 查询多个节点
getElementsByTagName():   根据 标签名 查询多个节点

区别 :querySelector(All)和getElement()方法的区别在于,前者copy的是原始数据,不会随页面变化而变化,而后者会。
大多数时候,我们还是使用前者来查询节点。

3.3 DOM属性

DOM种类:我们发现基本每一种html标签都对应一种DOM类型

<!-- HTMLDocument 根文档 -->
<html>
  ……
</html>

<!-- HTMLDivElement DIV类型 -->
<div class="subtitle">
  ……
</div>

<!-- HTMLAnchorElement 超链接类型 -->
<a class="free-bright">免费靓号</a>

<!-- HTMLInputElement Input类型 -->
<input class="password" type="pasworkd" placeholder="请输入密码" />

DOM属性
DOM类别:大概可以归纳为4类 1 元素节点
2 特性节点
3 文本节点
4 其他的一些节点 不太重要可以忽略
我们来用例子来分析一下:

let divDom = document.querySelector('div#test');       // divDom是元素节点 
console.log(divDom.nodeType, divDom.nodeName, divDom.nodeValue);

// 获取DIV节点的第一个儿子节点,代表 '优课达' 这个字符串
let txtDom = divDom.firstChild;          // txtDom是文本节点           
console.log(txtDom.nodeType, txtDom.nodeName, txtDom.nodeValue);

// 获取DIV节点的id属性
let attDom = divDom.attributes.id;        // attDom表示特性节点 
console.log(attDom.nodeType, attDom.nodeName, attDom.nodeValue);

总结一下:
1整个html中,无论是标签,标签属性,还是纯文本字符串,都是element,不同的地方在于nodetype分为元素节点,文本节点和特性节点。
2html的标签都是元素节点,可以用nodeName来获得标签名称
3纯文本都是文本节点,可以用nodeValue来获得文本内容
4标签的每个属性都是特性节点,可以用nodeName获取属性key还可以用nodeValue来获得value
5attributes可以获取元素节点的所有属性,得到的结果是一个字典,通过属性key获取对应的特性节点。

let divDom = document.querySelector('div#test');
console.log(divDom.outerHTML, divDom.innerHTML, divDom.innerText);

分析 :
属性 值 内容
outerHTML

优课达

youkeda

学的比别人好一点

整个 DOM 的 HTML 代码
innerHTML 优课达

youkeda

学的比别人好一点

DOM 内部 HTML 代码
innerText 优课达 DOM 内部纯文本内容

DOM亲属:我们知道firstChild属性获取到元素的第一个儿子节点。

let divDom = document.querySelector('div#test');
console.log(divDom.firstChild, divDom.lastChild);    
console.log('-----');
console.log(divDom.childNodes);
console.log('-----');
console.log(divDom.parentNode);

分析 :
属性 值 总结
firstChild 优课达 指定节点的第一个子节点
lastChild

学的比别人好一点

指定节点的最后一个子节点
childNodes 优课达

youkeda

学的比别人好一点

指定节点的子节点的集合
parentNode
优课达

youkeda

指定节点在 DOM 树中的父节点

学的比别人好一点

DOM样式:通过dom我们可以访问到css特性

const h1Dom = document.querySelector('h1');
console.log(h1Dom.classList);
console.log(h1Dom.style);
console.log(h1Dom.style.color);

分析:

属性        类型                  值                                  总结       
classList  DOMTokenList类数组	['test', 'youkeda']	                classList 数组方式存储所有的 class 名称
style	   CSSStyleDeclaration	color属性为rgb(255, 51, 0)	        对象或字典的方法存储 CSSStyle 

DOM数据属性
网页设计的初衷是为了数据和特定的html标签相关联

<!DOCTYPE html>
<head>
  <meta charset="UTF-8" />
  <title>优课达</title>
</head>
<body>
  <article data-parts="3" data-words="1314" data-category="python"> //段落 字数 分类
    ...
  </article>
  <script src="./index.js"></script>
</body>

我们来看看上面的一段代码:html提供了一种数据属性的标准,利用data-*储存额外的信息,例如段落 字数 分类

const article = document.querySelector('article');
console.log(article.dataset);

我们可以用dom的.dataset属性来获取这些额外的数据属性。dataset是个map对象,它是data-整个的key value的集合

3.4DOM操作(一)

DOM样式修改:我们来看一个例子

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <link rel="stylesheet" type="text/css" href="./post.css" />
  <title>优课达</title>
</head>
<body>
  <section class="box">
    <img
      class="java"
      src="https://document.youkeda.com/new-learn-path/Bitmap.png"
    />
    <div class="title">Java</div>
    <div class="select"></div>
  </section>

  <script src="./post.js"></script>
</body>


我们要实现如图所示的效果,可以怎么做?
我们可以在点击的时候向select插入一个img节点,渲染选中的打勾图片,再次点击的时候清除内部的select节点,这样我们需要做到以下几点
1如何使用js创建节点(创建img节点)
2如何设置节点的样式和属性(Img 节点这里设置src属性)
3如何在已经存在的节点内部创建节点(Img节点需要添加到select中)
4如何清空节点内部子节点(再次点击的时候清除img节点)

// 保存当前是否选中的状态
let isSelected = false;

// 获取整个元素的节点
const box = document.querySelector('.box');

// 获取select框节点
const select = document.querySelector('.select');

// 给整个元素添加点击事件【大家可以先忽略这部分】
box.addEventListener('click', function () {
  // 点击以后触发这个函数

  // 修改当前选中状态,取反即可
  isSelected = !isSelected;

  // 如果当前是选中状态、则添加img到select中
  if (isSelected) {
    // 创建一个img标签节点
    const img = document.createElement('img');

    // 设置img的src属性和样式,让其撑满select框
    img.src = 'https://style.youkeda.com/img/sandwich/check.png';
    img.setAttribute('style', 'width: 100%; height: 100%;');

    // 将这个节点添加到select框中
    select.appendChild(img);
  } else {
    // 如果不是选择状态,则清空内部子元素
    select.innerHTML = '';
  }
});

1 创建标签节点
document.createElement(tagName),利用此方法可以创建一个标签名为tagName的元素节点
例如,创建一个div节点

const div = document.createElement('div');

document.creatTextNode(string),利用此方法我们可以在div标签内部添加纯文本内容

const div = document.createElement('div');
const txt = document.createTextNode('优课达-学的比别人好一点');
div.appendChild(txt);                     //把txt添加到div中
document.body.appendChild(div);           //把div添加到body中

2 添加新节点
appendChild()方法可以往节点中插入一个新节点 位置:在所有儿子节点之后添加
inserBefore(newNode,referenceNode)在某个目标儿子节点之前添加,两个参数,第一个参数新节点,第二个参数某个目标儿子节点
示例

function createDisease(txt) {                            //构造一个创建节点的方法
  const dom = document.createElement('li');
  const domTxt = document.createTextNode(txt);
  dom.appendChild(domTxt);
  return dom;
}

const root = document.querySelector('ul.root');
const sars = document.querySelector('li.sars');         //查询节点

// 创建 H1N1
const H1N1 = createDisease('H1N1');                      //调用方法创建节点
root.appendChild(H1N1);

// 创建 新型冠状病毒
const nCoV = createDisease('新型冠状病毒');
root.insertBefore(nCoV, sars);

3 设置样式属性、
可以直接设置样式

img.setAttribute('style', 'width: 100%; height: 100%;');  //setAttribute可以设置html的所有属性,包括id type src

还可以单独设置某些样式

dom.style.color = 'xxxx';

classList:classList能够获取到DOM上所有的类,我们可以把样式都写成css,然后再js中添加或者删除。

4 innerHtml:我们可以使用innerHtml给某个节点添加内容

function createDisease(txt) {
  const dom = document.createElement('li');
  dom.innerHTML = txt;                         // 我们可以直接用innerHTML设置其纯文本
  return dom;
}

3.5 DOM操作(二)

我们用一个案列来深入解析一下DOM的规范

我们要做出上图显示的效果,先分析一下
1 首先,我们要在不考虑鼠标交互的情况下,做出静态页面
2 监听搜索框的input事件
3 当输入内容为肺炎时,显示模糊的搜索结果
4 当输入内容不是肺炎时,显示登录查看历史

开发静态html页面 :可以分为3大板块 搜索框,登录查看历史和肺炎相关列表

<body>
  <div>
    <nav>
      <!-- 头部搜索框区域 -->
    </nav>
    <main>
      <!-- 输入非'肺炎'情况 -->
      <section class="login">
        登录查看历史
      </section>
      <!-- 输入'肺炎'情况 -->
      <ul class="search-result">
        <li>
          <i class="search"></i>
          <p><em>肺炎</em>疫情实时动态</p>
          <i class="edit"></i>
        </li>
        ……
      </ul>
    </main>
  </div>
</body>

监听input输入事件,处理区域显示隐藏

const input = document.querySelector('input');

// 监听键盘事件
input.addEventListener('keyup', function() {
  // this 是DOM节点,this.value可以获取input内输入的值
  console.log(this.value);
});

监听输入肺炎时,显示肺炎查询结果

main .search-result {              //先将登录查看历史设置为可见,将搜索结果设置为不可见
  padding: 0;
  display: none; 
}

const input = document.querySelector('input');

const login = document.querySelector('.login');
const searchResult = document.querySelector('.search-result');

// 监听键盘事件
input.addEventListener('keyup', function() {
  // this 是DOM节点,this.value可以获取input内输入的值
  if (this.value === '肺炎') {
    login.style.display = 'none';
    searchResult.style.display = 'block';
  } else {
    login.style.display = 'block';
    searchResult.style.display = 'none';
  }
});

肺炎搜索结果动态显示:一般搜索结果都是js发起网络请求,然后返回的数据,这里我们自己写一些数据来模拟

let data = [
  '<em>肺炎</em>实时疫情动态',
  '<em>肺炎</em>的症状有哪些症状',
  '<em>肺炎</em>武汉',
  '<em>肺炎</em>症状',
  '<em>肺炎</em>最新',
  '<em>肺炎</em>是怎么引起的',
  '<em>肺炎</em>最新消息',
  '<em>肺炎</em>实时',
  '<em>肺炎</em>症状及表现',
  '<em>肺炎</em>最新情况'
];

我们用这份数组数据,生成多个li标签内容,我们来封装一个函数,用来生成这个liDom节点

function createSearchItem(txt) {
  const item = document.createElement('li');
  item.innerHTML = `<i class="search"></i><p>${txt}</p><i class="edit"></i>`;//我们使用innerHTML和模板字符串来创建li内容
  return item;
}

最后我们遍历搜索结果和依次创建li标签并插入到页面中,代码如下

let data = [
  '<em>肺炎</em>实时疫情动态',
  '<em>肺炎</em>的症状有哪些症状',
  '<em>肺炎</em>武汉',
  '<em>肺炎</em>症状',
  '<em>肺炎</em>最新',
  '<em>肺炎</em>是怎么引起的',
  '<em>肺炎</em>最新消息',
  '<em>肺炎</em>实时',
  '<em>肺炎</em>症状及表现',
  '<em>肺炎</em>最新情况'
];

function createSearchItem(txt) {
  const item = document.createElement('li');
  item.innerHTML = `<i class="search"></i><p>${txt}</p><i class="edit"></i>`;
  return item;
}

const input = document.querySelector('input');

const login = document.querySelector('.login');
const searchResult = document.querySelector('.search-result');

// 监听键盘事件
input.addEventListener('keyup', function() {
  // this 是DOM节点,this.value可以获取input内输入的值
  if (this.value === '肺炎') {
    // 先把原始内容清空
    searchResult.innerHTML = '';
    for (let i = 0; i < data.length; i++) {
      searchResult.appendChild(createSearchItem(data[i]));
    }

    login.style.display = 'none';
    searchResult.style.display = 'block';
  } else {
    login.style.display = 'block';
    searchResult.style.display = 'none';
  }
});

分析:我们先写出静态的页面,然后利用js控制区的显示和隐藏达到动态的效果,最后根据静态页面模板和数据,动态创建dom节点

第四章 DOM事件

4.1 DOM事件

我们可以通过addEventListener(eventName, callback)绑定eventName事件

// 监听Input输入事件
dom.addEventListener('input', function () {});

// 监听鼠标放置,移动事件
dom.addEventListener('mouseover', function () {});

// etc...

也有两种不太恰当的写法,不建议使用
1 事件嵌套到html 代码中

<div onclick="console.log('xxx')"></div>     ///容易导致html代码过于庞大,不易于分离

2 事件方法替换

dom.onclick = function () {};                //只能绑定一个事件,下一个事件会置换它

而addEventListener()可以绑定多个监听事件,所以我们统一使用addEventListener()

DOM事件

const h1 = document.querySelector('h1');
h1.addEventListener('click', function (e) {
  console.log(e);
});


我们从结果来看,可以发现它是一个MouseEvent,我们来掌握这个对象的几个属性

属性  	      值	                         解释
target	      <h1>优课达-学的比别人好一点</h1>	点击事件触发的 DOM 节点
type	      click	                        事件名称
pageX/pageY	  92/47	                         鼠标事件触发的页面坐标

我们来介绍一些常用事件:

焦点事件
focus: 表单组件(Input, Textarea, etc…)获取焦点事件
blur: 表单组件(Input, Textarea, etc…)失去焦点事件

鼠标事件
click: 点击事件
dblclick: 双击事件
mousedown: 在元素上按下任意鼠标按钮。
mouseenter: 指针移到有事件监听的元素内。
mouseleave: 指针移出元素范围外(不冒泡)。
mousemove: 指针在元素内移动时持续触发。
mouseover: 指针移到有事件监听的元素或者它的子元素内。
mouseout: 指针移出元素,或者移到它的子元素上。
mouseup: 在元素上释放任意鼠标按键。

键盘事件
keydown: 键盘按下事件
keyup: 键盘释放事件

视图事件
scroll: 文档滚动事件
resize: 窗口放缩事件

资源
load: 资源加载成功的事件

我们来做个总结:我们对DOM的操作,一般都是监听DOM事件,使用DOM操作,修改DOM属性。

4.2 冒泡 捕获 委托

我们先来看一个例子:我们想在一个工程里面添加点击事件,使它跳转到对应的工程详情页面

//省略部分代码
const workspace = document.querySelector('.workspace');
workspace.addEventListener('click', function() {
  window.location.href = 'https://www.youkeda.com';       // 点击后跳转
});

我们会发现按钮先变红,然后跳转

冒泡:
因为事件先触发like的click事件,然后再触发workspace的click事件,这就是事件冒泡
会发生这样的情况:1 点击事件触发button.like-btn的监听事件
2 冒泡找到其父节点,触发父节点的监听事件
3 一直冒泡直到htmL的根元素
那么我们要如何组织冒泡呢? 很简单,添加 e.stopPropagation()即可

// ......省略
likeBtn.addEventListener('click', function(e) {
  // 点击事件
  e.stopPropagation()                   //阻止冒泡事件

// ......省略              

捕获
捕获和冒泡完全相反,冒泡从当前元素开始一直冒泡到根元素,捕获是从根元素开始依次移动到当前元素。
我们之前使用的addEventListener是在冒泡阶段监听事件,而如果想在捕获阶段监听事件,只需要添加一个参数即可

dom.addEventListener('click', function() {}, true); //添加参数true

委托
委托其实是冒泡事件的一种应用,当你想在大量子元素中单击任何一个都可以运行一段代码,
你可以将事件监听器设置在父节点上,而不用每个节点都单独设置监听器。
示例
设置在每一个字节点上

const box = document.querySelector('.box');
const imgArr = box.children;

for (let i = 0; i < imgArr.length; i++) {
  imgArr[i].addEventListener('click', function() {
    document.body.style.backgroundImage = `url(${imgArr[i].src})`;
  });
}

设置在父节点上

const box = document.querySelector('.box');

box.addEventListener('click', function(e) {
  // 注意box区域比img大,如果点击在空白间隔区域,那么返回的节点将不会是IMG,需要特殊处理一下
  if (e.target.nodeName === 'IMG') {
    document.body.style.backgroundImage = `url(${e.target.src})`;
  }
});

4.3 表单元素事件

焦点事件:获取焦点 focus 和失去焦点blur
示例

const nick = document.querySelector('input.nick');
nick.addEventListener('focus', function() {
  console.log('获取焦点');
});

nick.addEventListener('blur', function() {
  console.log('失去焦点');
});

内容值变化
有两种事件可以监听内容的变化 input和change

const nick = document.querySelector('input.nick');
nick.addEventListener('input', function() {
  console.log('-----input');
  console.log(nick.value);
});

nick.addEventListener('change', function() {
  console.log('-----change');
  console.log(nick.value);
});

我们发现只要在input里面输入值就能触发input事件,而只有当失去焦点才会触发change事件

事件             介绍                                                     案例
change	当用户提交对元素值的更改时触发; change 事件不一定会对元素值的每次更改触发	1. checkbox 值修改以后
                                                                        2. select 选择后
                                                                        3. input 内容修改并失去焦点
input	只要 value 值修改就会触发

4.4 滚动事件

哪些情况下需要用到滚动事件
1 无尽滚动:当每次滚动到底部的时候,就会加载新的内容
2 动态效果:给页面添加一些动态效果

滚动事件:scroll
滚动事件的添加和处理

window.addEventListener('scroll', function() {
  console.log(window.scrollY);
});

无尽滚动:当页面滚动到底部时,添加新的文章到body

window.addEventListener('scroll', function() {
  // 可以通过clientHeight获取内容高度
  const height = document.body.clientHeight;

  // 通过screen.height获取浏览器的高度
  const screenHeight = window.screen.height;

  // 当距离底部的距离小于500时,触发页面新增内容
  if (height - window.scrollY - screenHeight < 500) {
    console.log('加载新文章内容');
    // 在底部添加10张图片
    const div = document.createElement('div');
    let str = '';
    for (let i = 0; i < 10; i++) {
      str += `
       <img
        class="first"
        alt=""
        src="https://document.youkeda.com/P3-1-HTML-CSS/1.8/1.jpg?x-oss-process=image/resize,h_300"
      />
      `;
    }
    div.innerHTML = str;
    document.body.appendChild(div);
  }
});

分析一下运用到的技术:
1 内容高度document.body.clientHeight
2 浏览器高度 window.screen.height
3 滚动距离 window.scrollY
4 滚动距离底部距离 内容高度 - 浏览器高度 - 滚动距离

补充

最后额外来添加一些关于网络请求的知识,基本的协议和url这些就不讲了,我们来看看关于fetch
我们可以使用fetch方法获取api返回的数据,示例

fetch(                                       //使用fetch方法
  'https://www.fastmock.site/mock/b73a1b9229212a9a3749e046b1e70285/f4/f4-11-1-1'
)
  .then(function (response) {
    return response.json();
  })
  .then(function (myJson) {
    console.log(myJson);
  });

我们看到示例中有.then这个语法,要理解这个语法我们先来看看fetch返回的结果是什么?
fetch返回的是一个Promise对象

promise是异步编程的一种解决方案,之前的异步编程都是通过回调方法来实现的。


let oReq = new XMLHttpRequest();
oReq.addEventListener('load', function () {
  console.log(this.responseText);
});
oReq.open(
  'GET',
  'https://www.fastmock.site/mock/b73a1b9229212a9a3749e046b1e70285/f4/f4-11-1-1'
);
oReq.send();

这是老版的ajax调用,大家以后几乎用不到这种方法。
使用这种方法我们需要写一大段代码,然后通过addEventListener监听load事件,然后触发后面的function回调函数。如果在这个回调函数里面继续
加入setTimeout或者addEventListener监听代码,那么会出现多层嵌套,专业叫做回调地狱。

Promise对象可以通过.then触发回调函数。
因为response.json()返回的也是一个 Promise 对象,所有后续可以继续使用.then触发后续回调。
fetch把嵌套型的回调调整为了平埔型的回调,完美的解决了回调地狱的问题。
fetch默认发起的是get请求,如果要发起post请求,只需要添加一个参数 method: 'POST’即可

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值