前端:JavaScript基础

87 阅读12分钟

一、JavaScript 运行过程及组成

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

\

JavaScript 的组成:

ECMAScript(简称 ES) :JavaScript 语法。

DOM: 页面文档对象模型,对页面中的元素进行操作。

BOM: 浏览器对象模型,对浏览器窗口进行操作。

\

二、JavaScript 的书写形式

2.1 行内式

直接嵌入到 html 元素内部:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    
</head>
<body>
    <input type="button" value="点击我" onclick="alert('prprpr!')">
</body>
</html>

运行结果:

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

HTML 中推荐使用双引号, JS 中推荐使用单引号

2.2 内嵌式

写到 script 标签中:

<body>
    <script>
        alert('prprpr!');
    </script>
</body>

运行结果:

\

2.3 外部式

写到单独的 .js 文件

alert('prprpr~~~');
<body>
    <script src="demo7.js"></script>
</body>

运行结果:

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

适合代码多的情况.

\

三、输入输出

3.1 输入:prompt

弹出一个输入框

<body>
    <script>
        prompt("请输入你的姓名:");
    </script>
</body>

运行结果:

3.2 输出:alert

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

<body>
    <script>
        alert("hello world!");
    </script>
</body>

运行结果:

3.3 输出:console.log

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

<body>
    <script>
        console.log("这是一条日志");
    </script>
</body>

注意: 在 VSCode 中直接输入 "log" 再按 tab 键, 就可以快速输入 console.log

打开开发者工具(F12)==》Console(控制台) 标签页才能看到结果:

重要概念:console

console 是一个 js 中的 "对象"

. 表示取对象中的某个属性或者方法, . 可以直观理解成 "的"

console.log 就可以理解成: 使用 "控制台" 对象 "的" log 方法

四、语法介绍

4.1 变量的使用

JS 中创建的变量,不需要显式指定类型。

变量的实际类型,根据初始化/赋值来确定。

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

使用变量:

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

\

4.2 理解动态类型

  1. JS 的变量类型式程序运行过程中才确定的。
var a = 10; // 数字
var b = "hello"; // 字符串
  1. 随着程序运行,变量的类型可能会发送改变。
var a = 10; // 数字
a = "world"; // 字符串

\

4.3 基本数据类型

JS 中内置的几种类型

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

number 数字类型

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

数字进制表示

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('hello' - 10);

注意:

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

\

string 字符串类型

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

\

求长度

使用 string 的 length 属性即可:

var a = 'hello';
console.log(a.length);
var b = '啊啊';
console.log(b.length);

结果如下:

单位为 字符 的数量。

字符串拼接

使用 + 进行拼接:

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

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

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

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

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

boolean 布尔类型

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

console.log(true + 1); // 2
console.log(false + 1); // 1

这样的操作其实是不科学的. 实际开发中不应该这么写.

undefined 未定义数据类型

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

var a;
console.log(a);

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

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

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

console.log(a + 10);

\

null 空值类型

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

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

注意:

null 和 undefined 都表示取值非法的情况, 但是侧重点不同.

null 表示当前的值为空. (相当于有一个空的盒子)

undefined 表示当前的变量未定义. (相当于连盒子都没有)

\

运算符

JavaScript 中的运算符和 Java 用法基本相同. 此处不做详细介绍了。

注意:

  1. 比较运算符:
  • == 比较相等,会进行隐式类型转换
  • === 比较相等,不会进行隐式类型转换
var a = 10;
var b = '10'
console.log(a == b);
console.log(a === b);

  1. || 运算符:

JS 中的逻辑或 和 Java 差别很大。

java || 运算返回的一定式 boolean 值

JS 的或运算,a || b

  • 若 a 为 true,整个表达式的值就为 a 的值。
  • 若 b 为 true,整个表达式的值就为 b 的值。
// 若 b 值为非空,就把 a 设为 b
// 若 b 值为空(null/undefined),就把 a 设为 0
let a = b || 0;

\

\

数组

创建数组

Java / C 中,要求数组中的变量是相同类型的!

但是 JS 中不要求(其他动态类型语言都不要求)。

有两种创建数组的方式:

  1. 通过 new 关键字来创建,数组在 JS 中也就是一个 “对象”。
var arr = new Array();
  1. 实际开发中,更常用的是,通过 字面量 的方式来创建。
let arr = [];
//也可以在创建数组的时候,填入内容
let arr2 = [1,2,'hello',true,undefined];

// 查看数组
console.log(arr);
console.log(arr2);

获取数组元素

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

var arr = ['艾希','诺克萨斯','盖伦'];
console.log(arr);
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);

arr[2] = '塞拉斯';
console.log(arr);
console.log(arr[2]);

注意:

  1. 如果尝试 读取 数组中不存在的下标,就会出现 undefined 的结果:
var arr = ['艾希','诺克萨斯','盖伦'];
console.log(arr[3]);

  1. 如果尝试 写入 数组中不存在的下标,就会往这个下标插入元素,同时可能就会修改数组的长度,但是中间元素的值,相当于没有定义,仍然是 undefined的:
var arr = ['艾希','诺克萨斯','盖伦'];
arr[5] = '劫';
console.log(arr);

console.log(arr[3]); // 同时如果读取元素不存在的下标,还是会 undefined

  1. JS 可以给数组指定一个 字符串 类型的 “下标” 。此处的 “下标” 更应该理解成是 “属性” ,也就相当于 “键值对”:
let arr = [];
arr['hello'] = 'world'; // 跟 arr.hello = 'world' 等价
console.log(arr);
console.log(arr['hello']);
console.log(arr.hello);

对于上例,本质上是在创建一个 hello 属性,类似的,也可以通过 [] 或者 . 的方式都能访问属性!!

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

相当于本来 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]);

  1. 通过下标新增

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

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

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

  1. 使用 push 进行追加元素(常用)

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

var arr = [1,2,3,4,5,6,7,8,9];
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 = [1,2,3,4,5,6];
// 第一个参数表示从下标为 2 的位置开始删除,
// 第二个参数表示要删除的元素个数为 3 个。
arr.splice(2,3);
console.log(arr);

\

\

4.4 函数

语法格式

// 创建函数/函数声明/函数定义 
function 函数名(形参列表) { 
  函数体 
  return 返回值;
}

// 函数调用 
函数名(实参列表)					 // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值

\

  • 函数定义并不会执行函数体内容, 必须要调用才会执行. 调用几次就会执行几次.
function hello() {
  console.log("hello world!!");
}
// 如果不调用函数,则没有执行打印语句
hello();

\

  • 调用函数的时候进入函数内部执行, 函数结束时回到调用位置继续执行. 可以借助调试器来观察.
  • 函数的定义和调用的先后顺序没有要求. (这一点和变量不同, 变量必须先定义再使用)
// 调用函数 
hello(); 
// 定义函数 
function hello() { 
  console.log("hello");
}

\

参数个数

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

  1. 如果实参个数比形参个数多,则多出的参数不参与函数运算:
function add(a,b) {
  return a+b;
}
console.log(add(10,20,30,40));

  1. 如果实参个数比形参个数少, 则此时多出来的形参值为 undefined:
function add(a,b) {
  return a+b;
}
console.log(add(10));

\

函数表达式

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 变量来调用函数了.

作用域链

背景:

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

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

var num = 1;
function test1() {
  var num = 10;

  function test2() {
    var num = 20;
    console.log(num);
  }
  test2();
}
test1();

\

4.5 对象

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

\

  1. 使用 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 = "蔡徐坤"; 这样的方式来新增属性

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

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

例如: 创建几个猫咪对象:

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

\

五、JS 的 DOM API

5.1 DOM

DOM =》Document 0bject Model - 文档对象模型

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

DOM 树结构形如:

重要概念:

文档: 一个页面就是一个 文档, 使用 document 表示.

元素: 页面中所有的标签都称为 元素. 使用 element 表示.

节点: 网页中所有的内容都可以称为 节点(标签节点, 注释节点, 文本节点, 属性节点等). 使用 node表示.

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

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

\

5.2 DOM API:获取元素

一个 DOM 树上有很多对象,到底要操作谁呢?

先选中这个对象!然后再操作对象!

DOM API 在提供了很多能够用来选中对象/元素的函数,此处介绍功能比较强大的两个API:

  • querySelector
  • querySelectorAll

补充:关于 document

document就表示 “文档”,是每个页面都自带的一个默认的对象,这个相当于是一个 “全局变量”,可以在代码的任意位置来访问到 document。

querySelect

var element = document.querySelector(selectors);
  • querySelector的参数是一个 “字符串”,这个字符串是一个 CSS 选择器。如果不是一个选择器,则引发SYNTAX_ERR异常。
  • querySelector会返回多个被选中元素中的第一个出现的元素。
  • querySelectorAll就会把选中的多个元素以类似数组的形式,全都返回回来。

注意:

  • 如果要是在 document 上调用这两个方法,相当于从这个页面的全局来查找符合要求的元素。
  • 如果是在某个具体的 DOM 对象上调用这两个方法,相当于从这个元素里面来查找符合要求的元素。

\

举例1:使用单个选择器

<body>
    <!-- 这就是一个 DOM 对象 -->
    <div class="one">
        hello
    </div>
    <div id="two">
        hello world!!!
    </div>
    <script>
        // 使用 document.querySelector('CSS选择器')来获取元素
        let div = document.querySelector('.one');
        console.log(div);

        let id = document.querySelector('#two');
        console.log(id);
    </script>
</body>

运行结果:

举例2:使用复合选择器

<body>
    <ul>
        <li>
            aaa
        </li>
    </ul>
    <script>
        let obj = document.querySelector('ul li');
        console.log(obj);
    </script>
</body>

运行结果:

如果上述代码中,无序列表中的列表项不止一个会怎么样?

<body>
    <ul>
        <li>aaa</li>
        <li>bbb</li>
        <li>ccc</li>
    </ul>
    <script>
        let obj = document.querySelector('ul li');
        console.log(obj);
    </script>
</body>

运行结果“

通过 querySelector来选择第一个元素。

querySelectorAll

<body>
    <ul>
        <li>aaa</li>
        <li>bbb</li>
        <li>ccc</li>
    </ul>
    <script>
        let obj = document.querySelectorAll('ul li');
        console.log(obj);
    </script>
</body>

\

5.3 事件初识

基本概念

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

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

行更复杂的交互操作。

事件三要素

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

举例:点击事件

<body>
    <button>这是个按钮</button>

    <script>
        // 将获取到的对象 赋予 button
        let button = document.querySelector('button');

        // 通过 .onclick ,给 button 添加点击事件
        // 事件源:button 对象/标签
        // 事件类型:鼠标点击事件
        // 事件处理程序:匿名函数里面执行:弹出一个对话框,显示 ”hello“
        button.onclick = function() {
            alert("hello");
        }
    </script>
</body>

点击按钮之后,就会弹出对话框:

  • button 按钮就是事件源。
  • 点击就是事件类型
  • function 这个匿名函数就是事件处理程序。
  • 其中 button.onclick = function() {} 这个操作称为 注册事件/绑定事件

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

5.4 操作元素

通过 .innerHTML

获取/修改元素内容

<body>
    <div id="screen">hello</div>
    <button id="button">这是一个按钮</button>
    <script>
        let button = document.querySelector('#button');
        button.onclick = function() {
            let screen = document.querySelector('#screen');
            console.log(screen.innerHTML);
        }
    </script>
</body>

还未点击按钮的状态:

点击按钮之后,得到了 <div id="screen">对象里面的内容:

我们还可以修改元素的内容,比如下面,我们将 按钮 改成 一个文本框

<body>
    <div id="screen">hello</div>
    <button id="button">这是一个按钮</button>
    <script>
        let button = document.querySelector('#button');
        // 读取页面内容
        console.log(button.innerHTML);
        // 修改页面内容
        button.innerHTML = '<input type="text">';
    </script>
</body>

\

\

示例:自制数字自增器:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>手动计数器</title>
    
</head>
<body>
    <div class="parent">
        <div class="screen">0</div>
        <div class="ctrl">
            <button id="add">+</button>
            <button id="minus">-</button>
        </div>
    </div>

    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .parent {
            width: 500px;
            height: 300px;
            background-color:bisque;
            margin: 0 auto;
        }

        .parent .screen {
            width: 200px;
            height: 150px;
            font-size: 50px;
            line-height: 150px;
            color:purple;
            margin: 0 auto;
            text-align: center;
        }

        .parent .ctrl {
            width: 300px;
            height: 150px;
            margin: 0 auto;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .parent .ctrl button {
            width: 100px;
            height: 50px;
            font-size: 20px;
            line-height: 20px;
            text-align: center;
            background-color:cadetblue;
            border-radius: 10px;
            border: none;
        }

        .parent .ctrl button:active {
            color:aliceblue;
            background-color: black;
        }
    </style>

    <script>
        let addButton = document.querySelector('#add');
        addButton.onclick = function() {
            //1.选中 screen 元素
            let screen = document.querySelector('.screen');
            //2.取出其中的内容
            let content = screen.innerHTML;
            //3.把内容+1
            let result = parseInt(content)+1;
            //4.把内容写回
            screen.innerHTML = result;
        }

        let minusButton = document.querySelector('#minus');
        minusButton.onclick = function() {
            //1.选中 screen 元素
            let screen = document.querySelector('.screen');
            //2.取出其中的内容
            let content = screen.innerHTML;
            //3.把内容-1
            let result = parseInt(content)-1;
            //4.把内容写回
            screen.innerHTML = result;
        }
    </script>
</body>
</html>

获取/修改元素属性

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

<body>
    <img src="img/2.jpg" alt="这是一座山河">
    <script>
        let img = document.querySelector('img');
        console.dir(img);
    </script>
</body>

通过 console.dir() 可以看到元素很多的属性(此处只截取了一部分):

我们可以在代码中直接通过这些属性来获取属性的值:

<body>
    <img src="img/2.jpg" alt="这是一座山河">
    <script>
        let img = document.querySelector('img');
        console.log(img.src);
        console.log(img.alt);
    </script>
</body>

\

还可以直接修改属性:

通过点击图片,进行图片的切换

<body>
    <img src="img/2.jpg" alt="这是一座山河">
    <script>
        let img = document.querySelector('img');
        img.onclick = function() {
            if (img.src.lastIndexOf('2.jpg') !== -1) {
                img.src = 'img/1.jpg';
            } else {
                img.src = 'img/2.jpg';
            }
        }
    </script>
</body>

未点击图片:

点击图片之后:

\

获取/修改表单元素属性

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

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

代码示例: 切换按钮

一开始是个播放按钮,在 “播放” 和 “暂停” 之间切换

<body>
    <input type="button" value="播放">
    <script>
        let button = document.querySelector('input');
        button.onclick = function() {
            if (button.value == '播放') {
                button.value = '暂停';
            } else {
                button.value = '播放';
            }
        }
    </script>
</body>

\

获取/修改样式属性

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

行内样式操作

element.style.[属性名] = [属性值]; // 一般使用这种
element.style.cssText = [属性名+属性值];

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


代码示例: 点击文字则放大字体

注意:

script 中的属性都是使用 驼峰命名 的方式和 CSS 属性对应的.

例如: font-size => fontSize, background-color => backgroundColor 等

这种方式修改只影响到特定样式, 其他内联样式的值不变.

<body>
    <div style="font-size: 10px;">越来越帅了</div>

    <script>
        let fontSize = document.querySelector('div');
        fontSize.onclick = function() {
            let size = parseInt(fontSize.style.fontSize) + 10;
            fontSize.style.fontSize = size + 'px';
        }
    </script>
</body>
  • ,此处第一行 parseInt 之后,去掉了 style 中属性 font-size 的单位 px,因此需要在 第二行 把 单位 px 加回来。

运行结果:

类名样式操作

element.className = [CSS 类名];

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

由于 class 是 JS 的保留字, 所以名字叫做 className

\

代码示例: 开启夜间模式

<body>
    <div class="container light">
        这是一段话 <br>
        这是一段话 <br>
        这是一段话 <br>
        这是一段话 <br>
    </div>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        html,body {
            width: 100%;
            height: 100%;
        }

        .container {
            width: 100%;
            height: 100%;
        }

        .light {
            background-color: #f3f3f3;
            columns: #333;
        }

        .dark {
            background-color: #333;
            color: #f3f3f3;
        }
    </style>
    <script>
        let container = document.querySelector('div');
        container.onclick = function() {
            console.log(container.className);
            if (container.className.indexOf('light') != -1) {
                container.className = 'container dark';
            } else {
                container.className = 'container light';
            }
        }
    </script>
</body>

点击页面,实现日夜间的切换:

\

\

5.5 操作节点

新增节点

分成两个步骤

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

第一步相当于生了个娃, 第二步相当于给娃上户口

1. 创建元素节点

使用 createElement 方法来创建一个元素. options 参数暂不关注.

var element = document.createElement(tagName[, options]);

代码示例:

<body>
    <div class="container"></div>

    <script>
        let div = document.createElement('div');
        div.id = 'mydiv';
        div.className = 'box';
        div.innerHTML = 'hello!';
        console.log(div);
    </script>
</body>

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

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

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

我们以 createElement 为主即可.

\

2. 插入节点到 DOM 树中

这里有两种不同的方法来插入节点:

  1. 使用 appendChild将节点插入到指定节点的 最后一个孩子之后
element.appendChild(aChild)

代码示例:

<body>
    <div class="container"></div>

    <script>
        let div = document.createElement('div');
        div.id = 'mydiv';
        div.className = 'box';
        div.innerHTML = 'hello!';
        
        let container = document.querySelector('.container');
        container.appendChild(div);
    </script>
</body>

\

  1. 使用 insertBefore将节点插入到 指定节点之前
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
  • insertedNode 被插入节点 (newNode)
  • parentNode 新插入节点的父节点
  • newNode 用于插入的节点
  • referenceNode newNode 将要插在这个节点之前

如果 referenceNodenull newNode 将被插入到子节点的末尾.

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

\

代码示例:

<body>
    <div class="container">
        <div>11</div>
        <div>22</div>
        <div>33</div>
        <div>44</div>
    </div>

    <script>
        let newDiv = document.createElement('div');
        newDiv.innerHTML = '这是一个新的节点';

        let container = document.querySelector('.container');
        console.log(container.children);
        container.insertBefore(newDiv,container.children[0]);
    </script>
</body>

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

<body>
    <div class="container">
        <div>11</div>
        <div>22</div>
        <div>33</div>
        <div>44</div>
    </div>

    <script>
        let newDiv = document.createElement('div');
        newDiv.innerHTML = '这是一个新的节点';

        let container = document.querySelector('.container');
        console.log(container.children);

        // 此时插入后元素为:
        //此处为坐标:         0       1  2  3  4
        //            这是一个新的节点 11 22 33 44
        container.insertBefore(newDiv,container.children[0]);

        // 此时插入后元素为:
        //此处为坐标:  0        1        2  3  4     
        //            11 这是一个新的节点 22 33 44 
        container.insertBefore(newDiv,container.children[2]);
    </script>
</body>

\

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

<body>
    <div class="container">
        <div>11</div>
        <div>22</div>
        <div>33</div>
        <div>44</div>
    </div>

    <script>
        let newDiv = document.createElement('div');
        newDiv.innerHTML = '这是一个新的节点';

        let container = document.querySelector('.container');
        console.log(container.children);
        container.insertBefore(newDiv,container.children[0]);

        // 插入完毕后再次修改 newDiv 的内容
        newDiv.innerHTML = '这是新节点2号';
    </script>
</body>

\

删除节点

使用 removeChild 删除子节点

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

\

代码示例:

<body>
    <!-- 创建一个空的 div -->
    <div class="container"></div>

    <button>此处是删除 div 的按钮</button>

    <script>
        // 首先创建一个 节点
        let newDiv = document.createElement('div');
        newDiv.className = 'newDiv';
        newDiv.innerHTML = "hello world!!";

        // 将 newDiv 打印在控制台上
        console.log(newDiv);

        // 将 新节点 挂到 DOM 树上
        let container = document.querySelector('.container');
        container.appendChild(newDiv);

        // 让 按钮 实现删除功能
        let button = document.querySelector('button');
        button.onclick = function() {
            // 删除节点
            container.removeChild(newDiv);
        }
    </script>
</body>

未点击按钮之前,此时 新节点是挂在 DOM 树中的子节点上:

点击删除按钮之后,发现之前新创建的节点已经不在 DOM 树上了,即为删除了: