JavaScript的学习

1.JavaScript的基本内容

JavaScript (简称 JS)

  • 是世界上最流行的编程语言之一
  • 是一个脚本语言, 通过解释器运行
  • 主要在客户端(浏览器)上运行, 现在也可以基于 node.js 在服务器端运行.

JavaScript 最初只是为了完成简单的表单验证(验证数据合法性), 结果后来不小心就火了.

当前 JavaScript 已经成为了一个通用的编程语言

JavaScript 的能做的事情:

  • 网页开发(更复杂的特效和用户交互)
  • 网页游戏开发
  • 服务器开发(node.js)
  • 桌面程序开发(Electron, VSCode 就是这么来的)
  • 手机 app 开发

1.JavaScript 和 HTML 和 CSS 之间的关系

  • HTML: 网页的结构(骨)
  • CSS: 网页的表现(皮)
  • JavaScript: 网页的行为(魂)

我们说,Web前端三剑客:HTML、CSS、JavaScript,它们看上去是三种不同的技术,但在实际中却是相互搭配使用的。

HTML是用来标记内容的(重在内容组织上)

HTML是超文本标记语言的简称,它是一种不严谨的、简单的标识性语言。它用各种标签将页面中的元素组织起来,告诉浏览器该如何显示其中的内容。

为什么说HTML是不严谨的呢?因为HTML标签即使不闭合,也并不会影响页面内容的组织。

CSS是用来修饰内容样式的(重在内容样式美化展示上)

CSS是层叠样式表的简称,它用来表现HTML文件样式的,简单说就是负责HTML页面中元素的展现及排版。

JavaScript是用来做交互的

JavaScript是一种脚本语言,即可以运行在客户端也能运行在服务器端。JavaScript的解释器就是JS引擎,JS引擎是浏览器的一部分。而JavaScript主要是用来扩展文档交互能力的,使静态的HTML具有一定的交互行为(比如表单提交、动画特效、弹窗等)。

HTML与CSS及JS的关系

这三者99%的情况下都是搭配使用的,但也不是绝对的,具体关系是:

  • HTML与CSS、JS是不同的技术,可以独立存在;

  • HTML一般需要CSS和JS来配合使用,否则单一HTML文档无论是功能还是展示上效果都不理想;

  • CSS一般是不能脱离HTML或XML的,如果CSS脱离了HTML和XML,那就没有存在的必要的;

  • JS可以脱离HTML和CSS而独立存在;

  • JS可以操作HTML和CSS。

2.JavaScript 运行过程

  • 编写的代码是保存在文件中的, 也就是存储在硬盘(外存上).
  • 双击 .html 文件浏览器(应用程序)就会读取文件, 把文件内容加载到内存中(数据流向: 硬盘 => 内存)
  • 浏览器会解析用户编写的代码, 把代码翻译成二进制的, 能让计算机识别的指令(解释器的工作)
  • 得到的二进制指令会被 CPU 加载并执行(数据流向: 内存 => CPU)

在这里插入图片描述

浏览器分成渲染引擎 + JS 引擎.

  • 渲染引擎: 解析 html + CSS, 俗称 “内核”
  • JS 引擎: 也就是 JS 解释器. 典型的就是 Chrome 中内置的 V8

JS 引擎逐行读取 JS 代码内容, 然后解析成二进制指令, 再执行

3.JavaScript 的组成

  • ECMAScript(简称 ES): JavaScript 语法
  • DOM: 页面文档对象模型, 对页面中的元素进行操作
  • BOM: 浏览器对象模型, 对浏览器窗口进行操作

光有 JS 语法, 只能写一些基础的逻辑流程.

但是要想完成更复杂的任务, 完成和浏览器以及页面的交互, 那么久需要 DOM API 和 BOM API.

这主要指在浏览器端运行的 JS. 如果是运行在服务端的 JS , 则需要使用 node.js 的 API, 就不太需要关注 DOM 和BOM

2.JavaScript 的书写形式

1. 行内式

直接嵌入到 html 元素内部

<input type="button" value="点我一下" onclick="alert('haha')">

注意, JS 中字符串常量可以使用单引号表示, 也可以 使用双引号表示.
HTML 中推荐使用双引号, JS 中推荐使用单引号.

2. 内嵌式

写到 script 标签中

<script>
    alert("haha");
</script>

3. 外部式

写到单独的 .js 文件中

<script src="hello.js"></script>
alert("hehe");

注意, 这种情况下 script 标签中间不能写代码. 必须空着(写了代码也不会执行). 适合代码多的情况.

3.JavaScript的注释和输入输出

1.注释

单行注释 // [建议使用]
多行注释 /* */

// 我是单行注释
/*
   我是多行注释
   我是多行注释
   我是多行注释
*/

使用 ctrl + / 切换注释.
多行注释不能嵌套. 形如这种代码就会报错

/*
/*
   我是多行注释
   我是多行注释
   我是多行注释
*/
*/

2.输入和输出

输入: prompt

弹出一个输入框

// 弹出一个输入框
prompt("请输入您的姓名:");

输出: alert

弹出一个警示对话框, 输出结果

// 弹出一个输出框
alert("hello");

输出: console.log

在控制台打印一个日志(供程序员看)

// 向控制台输出日志
console.log("这是一条日志");

4.JavaScript的语法

1.变量的使用

1.基本用法

创建变量(变量定义/变量声明/变量初始化)

var name = 'zhangsan';
var age = 20;

var 是 JS 中的关键字, 表示这是一个变量.
= 在 JS 中表示 “赋值”, 相当于把数据放到内存的盒子中. = 两侧建议有一个空格
每个语句最后带有一个 ; 结尾. JS 中可以省略 ; 但是建议还是加上.
注意, 此处的 ; 为英文分号. JS 中所有的标点都是英文标点.
初始化的值如果是字符串, 那么就要使用单引号或者双引号引起来.
初始化的值如果是数字, 那么直接赋值即可.

使用变量

console.log(age); // 读取变量内容
age = 30;         // 修改变量内容

例题展示: 弹框提示用户输入信息, 再弹框显示

var name = prompt("请输入姓名:");
var age = prompt("请输入年龄:");
var score = prompt("请输入分数");
alert("您的姓名是: " + name);
alert("您的年龄是: " + age);
alert("您的分数是: " + score);

也可以把三个输出内容合并成一次弹框

var name = prompt("请输入姓名:");
var age = prompt("请输入年龄:");
var score = prompt("请输入分数");
alert("您的姓名是: " + name + "\n" + "您的年龄是: " + age + "\n" + "您的分数是: " + score + "\n");
    • 表示字符串拼接, 也就是把两个字符串首尾相接变成一个字符串.
  • \n 表示换行

2.动态类型

  1. JS 的变量类型是程序运行过程中才确定的(运行到 = 语句才会确定类型)
var a = 10;     // 数字
var b = "hehe"; // 字符串
  1. 随着程序运行, 变量的类型可能会发生改变.
var a = 10;    // 数字
a = "hehe";    // 字符串

2.基本数据类型

JS 中内置的几种类型

  • number: 数字. 不区分整数和小数.
  • boolean: true 真, false 假.
  • string: 字符串类型.
  • undefined: 只有唯一的值 undefined. 表示未定义的值.
  • null: 只有唯一的值 null. 表示空值.

1.number 数字类型

JS 中不区分整数和浮点数, 统一都使用 “数字类型” 来表示.

数字进制表示

计算机中都是使用二进制来表示数据, 而人平时都是使用十进制.

因为二进制在使用过程中不太方便(01太多会看花眼).

所以在日常使用二进制数字时往往使用 八进制 和 十六进制 来表示二进制数字.

var a = 07;      // 八进制整数, 以 0 开头
var b = 0xa;     // 十六进制整数, 以 0x 开头
var c = 0b10;    // 二进制整数, 以 0b 开头

注意:

  • 一个八进制数字对应三个二进制数字
  • 一个十六进制数字对应四个二进制数字. (两个十六进制数字就是一个字节)

各种进制之间的转换, 不需要手工计算, 直接使用计算器即可.

特殊的数字值

  • Infinity: 无穷大, 大于任何数字. 表示数字已经超过了 JS 能表示的范围.
  • -Infinity: 负无穷大, 小于任何数字. 表示数字已经超过了 JS 能表示的范围.
  • NaN: 表示当前的结果不是一个数字.
var max = Number.MAX_VALUE;
// 得到 Infinity
console.log(max * 2);
// 得到 -Infinity
console.log(-max * 2);
// 得到 NaN
console.log('hehe' - 10);

注意:

  1. 负无穷大 和 无穷小 不是一回事. 无穷小指无限趋近与 0, 值为 1 / Infinity
  2. ‘hehe’ + 10 得到的不是 NaN, 而是 ‘hehe10’, 会把数字隐式转成字符串, 再进行字符串拼接.
  3. 可以使用 isNaN 函数判定是不是一个非数字.
console.log(isNaN(10));  // false
console.log(isNaN('hehe' - 10));  // true

2.string 字符串类型

基本规则

字符串字面值需要使用引号引起来, 单引号双引号均可.

var a = "haha";
var b = 'hehe';
var c = hehe;    // 运行出错

如果字符串中本来已经包含引号

var msg = "My name is "zhangsan"";    // 出错
var msg = "My name is \"zhangsan\"";  // 正确, 使用转义字符. \" 来表示字符串内部的引号. 
var msg = "My name is 'zhangsan'";    // 正确, 搭配使用单双引号
var msg = 'My name is "zhangsan"';    // 正确, 搭配使用单双引号

转义字符

有些字符不方便直接输入, 于是要通过一些特殊方式来表示.

\n , \ , ’ , " , \t

求长度

使用 String 的 length 属性即可

var a = 'hehe';
console.log(a.length);//4
var b = '哈哈';
console.log(b.length);//2

字符串拼接

使用 + 进行拼接

var a = "my name is ";
var b = "zhangsan";
console.log(a + b);

注意, 数字和字符串也可以进行拼接

var c = "my score is ";
var d = 100;
console.log(c + d);

注意, 要认准相加的变量到底是字符串还是数字

console.log(100 + 100);     // 200
console.log('100' + 100);   // 100100

3.boolean 布尔类型

表示 “真” 和 “假”

boolean 原本是数学中的概念 (布尔代数).
在计算机中 boolean 意义重大, 往往要搭配条件/循环完成逻辑判断.

Boolean 参与运算时当做 1 和 0 来看待.

4.undefined 未定义数据类型

如果一个变量没有被初始化过, 结果就是 undefined, 是 undefined 类型

var a;
console.log(a)

undefined 和字符串进行相加, 结果进行字符串拼接

console.log(a + "10");  // undefined10

undefined 和数字进行相加, 结果为 NaN

console.log(a + 10);

5.null 空值类型

null 表示当前的变量是一个 “空值”.

var b = null;
console.log(b + 10);    // 10
console.log(b + "10");  // null10

注意:
null 和 undefined 都表示取值非法的情况, 但是侧重点不同.
null 表示当前的值为空. (相当于有一个空的盒子)
undefined 表示当前的变量未定义. (相当于连盒子都没有)

3.运算符

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.条件语句

if 语句

基本语法格式

条件表达式为 true, 则执行 if 的 { } 中的代码

// 形式1
if (条件) {
    语句
}
// 形式2
if (条件) {
    语句1
} else {
    语句2
}
// 形式3
if (条件1) {
    语句1
} else if (条件2) {
    语句2   
} else if .... {
    语句...
} else {
    语句N
} 

三元表达式

是 if else 的简化写法.

条件 ? 表达式1 : 表达式2

条件为真, 返回表达式1 的值. 条件为假, 返回表达式2 的值.

注意, 三元表达式的优先级是比较低的.

switch

switch (表达式) {
    case1:
        语句1;
        break;
    case2:
        语句2:
        break;
    default:
        语句N;
}

5.循环语句

重复执行某些语句

while 循环

while (条件) {
    循环体;
}

执行过程:

  • 先执行条件语句
  • 条件为 true, 执行循环体代码.
  • 条件为 false, 直接结束循环

continue :结束这次循环
break:结束整个循环

for 循环

for (表达式1; 表达式2; 表达式3) {
 循环体
}
  • 表达式1: 用于初始化循环变量.
  • 表达式2: 循环条件
    -表达式3: 更新循环变量.

执行过程:

  • 先执行表达式1, 初始化循环变量
  • 再执行表达式2, 判定循环条件
  • 如果条件为 false, 结束循环
  • 如果条件为 true, 则执行循环体代码.
  • 执行表达式3 更新循环变量

6.数组

创建数组

使用 new 关键字创建

// Array 的 A 要大写
var arr = new Array();

使用字面量方式创建

var arr = [];
var arr2 = [1, 2, 'haha', false]; // 数组中保存的内容称为 "元素"

注意: JS 的数组不要求元素是相同类型.

获取数组元素

使用下标的方式访问数组元素(从 0 开始)

var arr = ['小猪佩奇', '小猪乔治', '小羊苏西'];
console.log(arr);
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);
arr[2] = '小猫凯迪';
console.log(arr);

如果下标超出范围读取元素, 则结果为 undefined

console.log(arr[3]);   // undefined
console.log(arr[-1]);  // undefined

注意: 不要给数组名直接赋值, 此时数组中的所有元素都没了.

相当于本来 arr 是一个数组, 重新赋值后变成字符串了

var arr = ['小猪佩奇', '小猪乔治', '小羊苏西'];
arr = '小猫凯迪';

新增数组元素

1. 通过修改 length 新增

相当于在末尾新增元素. 新增的元素默认值为 undefined

var arr = [9, 5, 2, 7];
arr.length = 6;
console.log(arr);
console.log(arr[4], arr[5]);

在这里插入图片描述

2. 通过下标新增

如果下标超出范围赋值元素, 则会给指定位置插入新元素

var arr = [];
arr[2] = 10;
console.log(arr)

在这里插入图片描述

此时这个数组的 [0] 和 [1] 都是 undefined

3. 使用 push 进行追加元素

代码示例: 给定一个数组, 把数组中的奇数放到一个 newArr 中.

var arr = [9, 5, 2, 7, 3, 6, 8];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
    if (arr[i] % 2 != 0) {
        newArr.push(arr[i]);
   }
}
console.log(newArr);

删除数组中的元素

使用 splice 方法删除元素

var arr = [9, 5, 2, 7];
// 第一个参数表示从下表为 2 的位置开始删除. 第二个参数表示要删除的元素个数是 1 个
arr.splice(2, 1);
console.log(arr);
// 结果
[9, 5, 7]

目前咱们已经用到了数组中的一些属性和方法.
arr.length, length 使用的时候不带括号, 此时 length 就是一个普通的变量(称为成员变量, 也叫属性)
arr.push(), arr.splice() 使用的时候带括号, 并且可以传参数, 此时是一个函数 (也叫做方法)

7.函数

语法结构

// 创建函数/函数声明/函数定义
function 函数名(形参列表) {
    函数体
    return 返回值;
}
// 函数调用
函数名(实参列表)           // 不考虑返回值
返回值 = 函数名(实参列表)   // 考虑返回值
  • 函数定义并不会执行函数体内容, 必须要调用才会执行. 调用几次就会执行几次
function hello() {
    console.log("hello");
}
// 如果不调用函数, 则没有执行打印语句
hello();
  • 调用函数的时候进入函数内部执行, 函数结束时回到调用位置继续执行. 可以借助调试器来观察.
  • 函数的定义和调用的先后顺序没有要求. (这一点和变量不同, 变量必须先定义再使用)
// 调用函数
hello();
// 定义函数
function hello() {
    console.log("hello");
}

参数个数

实参和形参之间的个数可以不匹配. 但是实际开发一般要求形参和实参个数要匹配

  1. 如果实参个数比形参个数多, 则多出的参数不参与函数运算
sum(10, 20, 30);    // 30
  1. 如果实参个数比形参个数少, 则此时多出来的形参值为 undefined
sum(10);    // NaN, 相当于 num2 为 undefined.

JS 的函数传参比较灵活, 这一点和其他语言差别较大. 事实上这种灵活性往往不是好事

函数表达式

var add = function() {
 var sum = 0;
    for (var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
   }
    return sum;
}
console.log(add(10, 20));            // 30
console.log(add(1, 2, 3, 4));        // 10
console.log(typeof add);             // function

此时形如 function() { } 这样的写法定义了一个匿名函数, 然后将这个匿名函数用一个变量来表示.

后面就可以通过这个 add 变量来调用函数了

作用域

某个标识符名字在代码中的有效范围.

在 ES6 标准之前, 作用域主要分成两个

  • 全局作用域: 在整个 script 标签中, 或者单独的 js 文件中生效.
  • 局部作用域/函数作用域: 在函数内部生效
// 全局变量
var num = 10;
console.log(num);
function test() {
    // 局部变量
    var num = 20;
    console.log(num);
}
function test2() {
    // 局部变量
    var num = 30;
    console.log(num);
}
test();
test2();
console.log(num);
// 执行结果
10
20
30
10

创建变量时如果不写 var, 则得到一个全局变量

function test() {
 num = 100;
}
test();
console.log(num);
// 执行结果
100

作用域链

背景:

  • 函数可以定义在函数内部
  • 内层函数可以访问外层函数的局部变量.

内部函数可以访问外部函数的变量. 采取的是链式查找的方式. 从内到外依次进行查找

var num = 1;
function test1() {
    var num = 10;
    function test2() {
      var num = 20;
        console.log(num);
   }
    test2();
}
test1();
// 执行结果
20

执行 console.log(num) 的时候, 会现在 test2 的局部作用域中查找 num. 如果没找到, 则继续去 test1 中查找. 如果还没找到, 就去全局作用域查找.

在这里插入图片描述

8.对象

基本概念

对象是指一个具体的事物.

“电脑” 不是对象, 而是一个泛指的类别. 而 “我的联想笔记本” 就是一个对象.

在 JS 中, 字符串, 数值, 数组,函数都是对象.

每个对象中包含若干的属性和方法.

  • 属性: 事物的特征.
  • 方法: 事物的行为.

1. 使用字面量创建对象

使用 { } 创建对象

var a = {};  // 创建了一个空的对象
var student = {
    name: '蔡徐坤',
    height: 175,
    weight: 170,
    sayHello: function() {
        console.log("hello");
   }
};
  • 使用 { } 创建对象
  • 属性和方法使用键值对的形式来组织.
  • 键值对之间使用 , 分割. 最后一个属性后面的 , 可有可无
  • 键和值之间使用 : 分割.
  • 方法的值是一个匿名函数.

使用对象的属性和方法:

// 1. 使用 . 成员访问运算符来访问属性 `.` 可以理解成 "的"
console.log(student.name);
// 2. 使用 [ ] 访问属性, 此时属性需要加上引号
console.log(student['height']);
// 3. 调用方法, 别忘记加上 () 
student.sayHello();

2. 使用 new Object 创建对象

var student = new Object(); // 和创建数组类似
student.name = "蔡徐坤";
student.height = 175;
student['weight'] = 170;
student.sayHello = function () {
    console.log("hello");
}
console.log(student.name);
console.log(student['weight']);
student.sayHello();

注意, 使用 { } 创建的对象也可以随时使用 student.name = “蔡徐坤”; 这样的方式来新增属性

3. 使用 构造函数 创建对象

前面的创建对象方式只能创建一个对象. 而使用构造函数可以很方便 的创建 多个对象.

例如: 创建几个猫咪对象

var mimi = {
    name: "咪咪",
    type: "中华田园喵",
    miao: function () {
        console.log("喵");
   }
};
var xiaohei = {
    name: "小黑",
    type: "波斯喵",
    miao: function () {
        console.log("猫呜");
   }
}
var ciqiu = {
    name: "刺球",
    type: "金渐层",
    miao: function () {
        console.log("咕噜噜");
   }
}

此时写起来就比较麻烦. 使用构造函数可以把相同的属性和方法的创建提取出来, 简化开发过程.

基本语法

function 构造函数名(形参) {
    this.属性 =;
    this.方法 = function...
}
    
var obj = new 构造函数名(实参);

注意:

  • 在构造函数内部使用 this 关键字来表示当前正在构建的对象.
  • 构造函数的函数名首字母一般是大写的.
  • 构造函数的函数名可以是名词.
  • 构造函数不需要 return
  • 创建对象的时候必须使用 new 关键字.

this 相当于 “我”

使用构造函数重新创建猫咪对象

function Cat(name, type, sound) {
    this.name = name;
    this.type = type;
    this.miao = function () {
        console.log(sound); // 别忘了作用域的链式访问规则
   }
}
var mimi = new Cat('咪咪', '中华田园喵', '喵');
var xiaohei = new Cat('小黑', '波斯喵', '猫呜');
var ciqiu = new Cat('刺球', '金渐层', '咕噜噜');
console.log(mimi);
mimi.miao();

理解 new 关键字

new 的执行过程:

  1. 先在内存中创建一个空的对象 { }
  2. this 指向刚才的空对象(将上一步的对象作为 this 的上下文)
  3. 执行构造函数的代码, 给对象创建属性和方法
  4. 返回这个对象 (构造函数本身不需要 return, 由 new 代劳了

JavaScript 的对象和 Java 的对象的区别

1. JavaScript 没有 “类” 的概念

对象其实就是 “属性” + “方法” .

类相当于把一些具有共性的对象的属性和方法单独提取了出来, 相当于一个 “月饼模子”

在 JavaScript 中的 “构造函数” 也能起到类似的效果.

而且即使不是用构造函数, 也可以随时的通过 { } 的方式指定出一些对象

在 ES6 中也引入了 class 关键字, 就能按照类似于 Java 的方式创建类和对象了.

2. JavaScript 对象不区分 “属性” 和 “方法”

JavaScript 中的函数是 “一等公民”, 和普通的变量一样. 存储了函数的变量能够通过 ( ) 来进行调用执行.

3. JavaScript 对象没有 private / public 等访问控制机制.

对象中的属性都可以被外界随意访问.

4. JavaScript 对象没有 “继承”

继承本质就是 “让两个对象建立关联”. 或者说是让一个对象能够重用另一个对象的属性/方法.
JavaScript 中使用 “原型” 机制实现类似的效果.

例如: 创建一个 cat 对象和 dog 对象, 让这两个对象都能使用 animal 对象中的 eat 方法.

通过 proto 属性来建立这种关联关系 (proto 翻译作 “原型”)

在这里插入图片描述

当eat方法被调用的时候,先在自己的方法列表中寻找, 如果找不到,就去找原型中的方法, 如果原型中找不到, 就去原型的原型中去寻找… 最后找到Object那里, 如果还找不到, 那就是未定义了。

在这里插入图片描述

5. JavaScript 没有 “多态”

多态的本质在于 “程序猿不必关注具体的类型, 就能使用其中的某个方法”.

C++ / Java 等静态类型的语言对于类型的约束和校验比较严格. 因此通过 子类继承父类, 并重写父类的方法的方式 来实现多态的效果.

但是在 JavaScript 中本身就支持动态类型, 程序猿在使用对象的某个方法的时候本身也不需要对对象的类型做出明确区分. 因此并不需要在语法层面上支持多态.

例如:
在 Java 中已经学过 ArrayList 和 LinkedList. 为了让程序猿使用方便, 往往写作

List<String> list = new ArrayList<>()

然后我们可以写一个方法:

void add(List<String> list, String s) {
 list.add(s);   
}

我们不必关注 list 是 ArrayList 还是 LinkedList, 只要是 List 就行. 因为 List 内部带有 add 方法.
当我们使用 JavaScript 的代码的时候

function add(list, s) {
 list.add(s)
}

add 对于 list 这个参数的类型本身就没有任何限制. 只需要 list 这个对象有 add 方法即可. 就不必
像 Java 那样先继承再重写绕一个圈子.

5.WebAPI

JS 分成三个大的部分

  • ECMAScript: 基础语法部分
  • DOM API: 操作页面结构
  • BOM API: 操作浏览器

WebAPI 就包含了 DOM + BOM.

这个是 W3C 组织规定的. (和制定 ECMAScript 标准的大佬们不是一伙人).

1.什么是API

API 是一个更广义的概念. 而 WebAPI 是一个更具体的概念, 特指 DOM+BOM

所谓的 API 本质上就是一些现成的函数/对象, 让程序猿拿来就用, 方便开发.

相当于一个工具箱. 只不过程序猿用的工具箱数目繁多, 功能复杂.

6.DOM 基本概念

1.什么是 DOM

DOM 全称为 Document Object Model.

W3C 标准给我们提供了一系列的函数, 让我们可以操作:

  • 网页内容
  • 网页结构
  • 网页样式

2.DOM 树

一个页面的结构是一个树形结构, 称为 DOM 树.

树形结构在数据结构阶段会介绍. 就可以简单理解成类似于 “家谱” 这种结构

页面结构形如

在这里插入图片描述

重要概念:

  • 文档: 一个页面就是一个 文档, 使用 document 表示.
  • 元素: 页面中所有的标签都称为 元素. 使用 element 表示.
  • 节点: 网页中所有的内容都可以称为 节点(标签节点, 注释节点, 文本节点, 属性节点等). 使用 node 表示.

这些文档等概念在 JS 代码中就对应一个个的对象.

所以才叫 “文档对象模型” .

7.获取元素

1.querySelector

前面的几种方式获取元素的时候都比较麻烦. 而使用 querySelector 能够完全复用前面学过的 CSS 选择器知识, 达到更快捷更精准的方式获取到元素对象.

var element = document.querySelector(selectors);
  • selectors 包含一个或多个要匹配的选择器的 DOM字符串 DOMString ,该字符串必须是有效的CSS选择器字符串;如果不是,则引发 SYNTAX_ERR 异常
  • 表示文档中与指定的一组CSS选择器匹配的第一个元素的 html元素Element 对象.
  • 如果您需要与指定选择器匹配的所有元素的列表,则应该使用querySelectorAll()
  • 可以在任何元素上调用,不仅仅是 document。 调用这个方法的元素将作为本次查找的根元素
<div class="box">abc</div>
<div id="id">def</div>
<h3><span><input type="text"></span></h3>
<script>
    var elem1 = document.querySelector('.box');
    console.log(elem1);
    var elem2 = document.querySelector('#id');
    console.log(elem2);
    var elem3 = document.querySelector('h3 span input');
    console.log(elem3);
</script>

2.querySelectorAll

使用 querySelectorAll 用法和上面类似.

<div class="box">abc</div>
<div id="id">def</div>
<script>
    var elems = document.querySelectorAll('div');
console.log(elems);
</script>

8.事件

1.基本概念

JS 要构建动态页面, 就需要感知到用户的行为.

用户对于页面的一些操作(点击, 选择, 修改等) 操作都会在浏览器中产生一个个事件, 被 JS 获取到, 从而进行更复杂的交互操作.

浏览器就是一个哨兵, 在侦查敌情(用户行为). 一旦用户有反应(触发具体动作), 哨兵就会点燃烽火台的狼烟(事件),后方就可以根据狼烟来决定下一步的对敌策略

2.事件三要素

  1. 事件源: 哪个元素触发的
  2. 事件类型: 是点击, 选中, 还是修改?
  3. 事件处理程序: 进一步如何处理. 往往是一个回调函数.

3.简单示例

<button id="btn">点我一下</button>
<script>
    var btn = document.getElementById('btn');
    btn.onclick = function () {
        alert("hello world");
   }
</script>
  • btn 按钮就是事件源.
  • 点击就是事件类型
  • function 这个匿名函数就是事件处理程序
  • 其中 btn.onclick = function() 这个操作称为 注册事件/绑定事件

注意: 这个匿名函数相当于一个回调函数, 这个函数不需要程序猿主动来调用, 而是交给浏览器, 由浏览器自动在合适的时机(触发点击操作时) 进行调用.

9.操作元素

1.获取/修改元素内容

1. innerText

Element.innerText 属性表示一个节点及其后代的“渲染”文本内容

// 读操作
var renderedText = HTMLElement.innerText;
// 写操作
HTMLElement.innerText = string;

不识别 html 标签. 是非标准的(IE发起的). 读取结果不保留html源码中的 换行 和 空格

<div>
    <span>hello world</span>
    <span>hello world</span>
</div>
<script>
    var div = document.querySelector('div');
    // 读取 div 内部内容
    console.log(div.innerText);
    // 修改 div 内部内容, 界面上就会同步修改
    div.innerText = 'hello js <span>hello js</span>';
</script>

可以看到, 通过 innerText 无法获取到 div 内部的 html 结构, 只能得到文本内容.
修改页面的时候也会把 span 标签当成文本进行设置.

2. innerHTML

Element.innerHTML 属性设置或获取HTML语法表示的元素的后代.

// 读操作
var content = element.innerHTML;
// 写操作
element.innerHTML = htmlString;
  1. 先获取到事件源的元素
  2. 注册事件

识别 html 标签. W3C 标准的. 读取结果保留html源码中的 换行 和 空格

<div>
    <span>hello world</span>
    <span>hello world</span>
</div>
<script>
    var div = document.querySelector('div');
    // 读取页面内容
    console.log(div.innerHTML);
    // 修改页面内容
    div.innerHTML = '<span>hello js</span>'
</script>

可以看到 innerHTML 不光能获取到页面的 html 结构, 同时也能修改结构. 并且获取到的内容保留的空格和换行

2.获取/修改元素属性

可以通过 Element 对象的属性来直接修改, 就能影响到页面显示效果

<img src="rose.jpg" alt="这是一朵花" title="玫瑰花">
<script>
    var img = document.querySelector('img');
    console.dir(img);
</script>

此时可以看到 img 这个 Element 对象中有很多属性

在这里插入图片描述
我们可以在代码中直接通过这些属性来获取属性的值

<img src="rose.jpg" alt="这是一朵花" title="玫瑰花">
<script>
    var img = document.querySelector('img');
    // console.dir(img);
    console.log(img.src);
    console.log(img.title);
    console.log(img.alt);
</script>

在这里插入图片描述
还可以直接修改属性

<img src="rose.jpg" alt="这是一朵花" title="玫瑰花">
<script>
    var img = document.querySelector('img');
    img.onclick = function () {
        if (img.src.lastIndexOf('rose.jpg') !== -1) {
            img.src = './rose2.png';
       } else {
            img.src = './rose.jpg';
       }
   }
</script>

此时点击图片就可以切换图片显示状态.

3.获取/修改表单元素属性

表单(主要是指 input 标签)的以下属性都可以通过 DOM 来修改

  • value: input 的值.
  • disabled: 禁用
  • checked: 复选框会使用
  • selected: 下拉框会使用
  • type: input 的类型(文本, 密码, 按钮, 文件等)

input 具有一个重要的属性 value, 这个 value 决定了表单元素的内容
如果是输入框, value 表示输入框的内容, 修改这个值会影响到界面显式; 在界面上修改这个值也会影响到代码中的属性
如果是按钮, value 表示按钮的内容. 可以通过这个来实现按钮中文本的替换

4.获取/修改样式属性

CSS 中指定给元素的属性, 都可以通过 JS 来修改.

行内样式操作

element.style.[属性名] = [属性值];
element.style.cssText = [属性名+属性值];

“行内样式”, 通过 style 直接在标签上指定的样式. 优先级很高.
适用于改的样式少的情况

类名样式操作

element.className = [CSS 类名];

修改元素的 CSS 类名. 适用于要修改的样式很多的情况

10.操作节点

1.新增节点

分成两个步骤

  1. 创建元素节点
  2. 把元素节点插入到 dom 树中

1. 创建元素节点

使用 createElement 方法来创建一个元素

var element = document.createElement(tagName[, options]);
<div class="container">
</div>
<script>
    var div = document.createElement('div');
    div.id = 'mydiv';
    div.className = 'box';
 div.innerHTML = 'hehe';
    console.log(div);
</script>

此时发现, 虽然创建出新的 div 了, 但是 div 并没有显示在页面上. 这是因为新创建的节点并没有加入到DOM 树中.

上面介绍的只是创建元素节点, 还可以使用:

  • createTextNode 创建文本节点
  • createComment 创建注释节点
  • createAttribute 创建属性节点

我们以 createElement 为主即可.

2. 插入节点到 dom 树中

  1. 使用 appendChild 将节点插入到指定节点的最后一个孩子之后
element.appendChild(aChild)
<div class="container">
</div>
<script>
    var div = document.createElement('div');
    div.id = 'mydiv';
    div.className = 'box';
    div.innerHTML = 'hehe';
    var container = document.querySelector('.container');
    container.appendChild(div);
</script>
  1. 使用 insertBefore 将节点插入到指定节点之前
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
  • insertedNode 被插入节点(newNode)
  • parentNode 新插入节点的父节点
  • newNode 用于插入的节点
  • referenceNode newNode 将要插在这个节点之前

如果 referenceNode 为 null 则 newNode 将被插入到子节点的末尾.

注意: referenceNode 引用节点不是可选参数

<div class="container">
<div>11</div>
    <div>22</div>
    <div>33</div>
    <div>44</div>
</div>
<script>
    var newDiv = document.createElement('div');
    newDiv.innerHTML = '我是新的节点';
    var container = document.querySelector('.container');
    console.log(container.children);
    container.insertBefore(newDiv, container.children[0]);
</script>

在这里插入图片描述

注意1: 如果针对一个节点插入两次, 则只有最后一次生效(相当于把元素移动了)

<div class="container">
    <div>11</div>
    <div>22</div>
    <div>33</div>
    <div>44</div>
</div>
<script>
    var newDiv = document.createElement('div');
    newDiv.innerHTML = '我是新的节点';
    var container = document.querySelector('.container');
    console.log(container.children);
    // 此处的 children 里有 4 个元素
    container.insertBefore(newDiv, container.children[0]);
    // 此处的 children 里有 5 个元素(上面新插了一个), 0 号元素是 新节点, 
    // 1 号元素是 11, 2号节点是 22, 所以是插入到 22 之前. 
    container.insertBefore(newDiv, container.children[2]);
</script>

在这里插入图片描述

注意2: 一旦一个节点插入完毕, 再针对刚刚的节点对象进行修改, 能够同步影响到 DOM 树中的内容

<div class="container">
    <div>11</div>
    <div>22</div>
    <div>33</div>
    <div>44</div>
</div>
<script>
    var newDiv = document.createElement('div');
    newDiv.innerHTML = '我是新的节点';
    var container = document.querySelector('.container');
    console.log(container.children);
    container.insertBefore(newDiv, container.children[0]);
    
    // 插入完毕后再次修改 newDiv 的内容
    newDiv.innerHTML = '我是新节点2';
</script>

在这里插入图片描述

2.删除节点

使用 removeChild 删除子节点

oldChild = element.removeChild(child);
  • hild 为待删除节点
  • element 为 child 的父节点
  • 返回值为该被删除节点
  • 被删除节点只是从 dom 树被删除了, 但是仍然在内存中, 可以随时加入到 dom 树的其他位置.
  • 如果上例中的 child节点 不是 element 节点的子节点,则该方法会抛出异常

10.代码案例: 猜数字

预期效果

在这里插入图片描述
代码实现

<button type="button" id="reset">重新开始一局游戏</button>
<br>
请输入要猜的数字:<input type="text" id="number">
<button type="button" id="button"></button>
<br>
已经猜的次数:<span id="count">0</span>
<br>
结果:<span id="result"></span>
<script>
    var inputE = document.querySelector("#number");
    var countE = document.querySelector("#count");
    var resultE = document.querySelector("#result");
    var btn = document.querySelector("#button");
    var resetBtn = document.querySelector("#reset");
    // 随机生成一个 1-100 的数字
    var guessNumber = Math.floor(Math.random() * 100) + 1// 0 - 1 之间的数
    var count = 0;
    // on: 当
    // click: 点击
    // 事件驱动(Event-Drive):只要真正发生了点击事件时,才执行该函数
    btn.onclick = function() {
        count++;
        countE.innerText = count;
        var userGuess = parseInt(inputE.value);
        if (userGuess == guessNumber) {
            resultE.innerText = "猜对了";
            resultE.style = "color: gray;";
       } else if (userGuess < guessNumber) {
            resultE.innerText = "猜小了";
            resultE.style = "color: blue;";
       } else {
            resultE.innerText = "猜大了";
            resultE.style = "color: red;";
       }
   };
    resetBtn.onclick = function() {
        guessNumber = Math.floor(Math.random() * 100) + 1
        count = 0;
        countE.innerText = count;
        resultE.innerText = "";
        inputE.value = "";
   }
</script>

11.代码案例: 表白墙

预期效果

在这里插入图片描述

创建页面布局

创建 表白墙.html

<h1>表白墙</h1>
<p>输入后点击提交, 会将信息显示在表格中</p>
<span>: </span>
<input type="text">
<span>对谁: </span>
<input type="text">
<span>说什么: </span>
<input type="text">
<input type="button" value="提交">

此时效果形如

在这里插入图片描述

调整样式

<div class="container">
    <h1>表白墙</h1>
    <p>输入后点击提交, 会将信息显示在表格中</p>
    <div class="row">
        <span>: </span>
        <input class="edit" type="text">
        </div>
    <div class="row">
        <span>对谁: </span>
        <input class="edit" type="text">
    </div>
    <div class="row">
        <span>说什么: </span>
        <input class="edit" type="text">
    </div>
    <div class="row">
        <input type="button" value="提交" class="submit">
    </div>
</div>
<style>
   * {
        margin: 0;
        padding: 0;
   }
    .container {
        width: 400px;
        margin: 0 auto;
   }
    h1 {
        text-align: center;
        padding: 20px 0;
   }
    p {
        color: #666;
        text-align: center;
        font-size: 14px;
        padding: 10px 0;
   }
    .row {
        height: 40px;
        display: flex;
        justify-content: center;
        align-items: center;
   }
    span {
        width: 100px;
        line-height: 40px;
   }
    .edit {
        width: 200px;
        height: 30px;
   }
    .submit {
     width: 304px;
        height: 40px;
        color: white;
        background-color: orange;
        border: none;
   }
</style>

此时效果形如

在这里插入图片描述

实现提交

// 给点击按钮注册点击事件
var submit = document.querySelector('.submit');
submit.onclick = function () {
    // 1. 获取到编辑框内容
    var edits = document.querySelectorAll('.edit');
    var from = edits[0].value;
    var to = edits[1].value;
    var message = edits[2].value;
    console.log(from + "," + to + "," + message);
    if (from == '' || to == '' || message == '') {
        return;
   }
    // 2. 构造 html 元素
    var row = document.createElement('div');
    row.className = 'row';
    row.innerHTML = from + '对' + to + '说: ' + message;
    // 3. 把构造好的元素添加进去
    var container = document.querySelector('.container');
    container.appendChild(row);
    // 4. 同时清理之前输入框的内容
    for (var i = 0; i < 3; i++) {
        edits[i].value = '';
   }
}

此时效果形如
在这里插入图片描述

实现点击按钮的动画效果

需要使用伪类选择器.

伪类选择器种类有很多. 此处的 :active 表示选中被按下的按钮

.submit:active {
    background-color: #666;
}

12.代码案例: 待办事项

预期效果
在这里插入图片描述

创建页面布局

创建 待办事项.html

<div class="nav">
    <input type="text">
    <button>新建任务</button>
</div>
<div class="container">
    <div class="todo">
        <h3>未完成</h3>
        <div class="row">
            <input type="checkbox">
            <span>吃饭</span>
            <button>删除</button>
        </div>
    </div>
    <div class="done">
        <h3>已完成</h3>
    </div>
</div>

实现页面样式

<style>
   * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
   }
    .container {
        width: 800px;
        margin: 0 auto;
        display: flex;
   }
    .todo,
    .done {
        width: 50%;
        height: 100%;
   }
    .container h3 {
        height: 50px;
        text-align: center;
        line-height: 50px;
        background-color: #333;
        color: #fff;
   }
    .nav {
        width: 800px;
        height: 100px;
        margin: 0 auto;
        display: flex;
        align-items: center;
   }
    .nav input {
        width: 600px;
        height: 50px;
   }
    .nav button {
        width: 200px;
        height: 50px;
        border: none;
        background-color: orange;
        color: #fff;
   }
    .row {
        height: 50px;
        display: flex;
        align-items: center;
   }
    .row input {
        margin: 0 10px;
   }
    .row span {
        width: 300px;
   }
    .row button {
        width: 50px;
        height: 40px;
   }
</style>

实现页面行为

实现新增

// 实现新增任务
var addTaskButton = document.querySelector('.nav button');
addTaskButton.onclick = function () {
    // 1. 获取到任务内容的输入框
    var input = document.querySelector('.nav input');
    // 2. 获取到输入框内容
     var taskContent = input.value;
    // 3. 根据内容新建一个元素节点
    var row = document.createElement('div');
    row.className = 'row';
    var checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    var span = document.createElement('span');
    span.innerHTML = taskContent;
    var button = document.createElement('button');
    button.innerHTML = '删除';
    row.appendChild(checkbox);
    row.appendChild(span);
    row.appendChild(button);
    // 4. 把新节点插入到 todo 中
    var todo = document.querySelector('.todo');
    todo.appendChild(row);
}

点击复选框后将元素放到 “已完成”

注意:

  • 在事件回调函数中使用 this 能够获取到当前处理事件的元素.
  • 通过 this.parentNode 属性能够获取到当前元素的父元素.
  • 点击 checkbox 时, 会先修改 value , 再触发点击事件
// 修改 addTaskButton.onclick
addTaskButton.onclick = function () {
 // 上方的部分不变...
    
    // 5. 给 checkbox 注册点击事件
    checkbox.onclick = function () {
        // 
        var row = this.parentNode;
        // 注意! 是先触发 checked 为 true, 然后再调用 onclick 函数
        if (this.checked) {
            var target = document.querySelector('.done');
       } else {
            var target = document.querySelector('.todo');
       }
        target.appendChild(row);
   }
}

点击删除按钮删除该任务

// 修改 addTaskButton.onclick
addTaskButton.onclick = function () {
 // 上方的部分不变...
    // 6. 给删除按钮注册点击事件
    button.onclick = function () {
        var row = this.parentNode;
        var grandParent = row.parentNode;
        grandParent.removeChild(row);
   }
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值