1. 浏览器环境

JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经发展成为一种具有多种用途和平台的语言。


下面是 JavaScript 在浏览器中运行时的鸟瞰示意图:

在这里插入图片描述

windows 是根对象,它充当 JavaScript 的全局对象以及代表着浏览器窗口,提供控制浏览器窗口的方法。

1.1 DOM 文档对象模型

DOM 将所有页面内容表示为可以修改的对象,document 是页面的入口,可以利用它修改创建 HTML 的内容,可以参考 DOM

document.bodyDOM.style.background = "pink";
let timer = setTimeout(() => {
    document.body.style.background = "orange";
}, 5000);

1.2 BOM 浏览器对象模型

BOM表示由浏览器提供的用于处理 document 之外的所有内容的其他对象,如 navigatorlocation。利用 location 可以读取当前 URL 和 重定向到新的 URL:

console.log(location.href);
if (confirm("go to baidu.com")) {
    location.href = "https://git-jhy.github.io";
}

此外,常用的 alertconfirmprompt 都是 BOM 的一部分。

2. DOM 树

2.1 DOM 树

看一个简单的 DOM 例子:

<!DOCTYPE HTML>
<html>
<head>
  <title>document</title>
</head>
<body>
  This is text.
</body>
</html>
  • html 是根节点,headbody 是它的字节点;

  • 元素内的文本形成 文本节点,被标记为 #text,如 title 中的 “document”,包括换行、缩进和空格。

  • 若浏览器遇到不正确的 HTML,会在形成 DOM 时自动更正。

上面的 HTML 的 DOM 标签树形结构如下,可以在这个网站上查看 DOM 树结构 Clck me

在这里插入图片描述

一共有 12 种节点类型。实际上,我们通常用到的是其中的 4 种:

  1. doxument —— DOM 入口
  2. 元素节点 —— HTML 标签
  3. 文本标签 —— 包含文本
  4. 注释 —— 读取注释信息

2.2 遍历 DOM

顶层

html = document.documentElement
body = document.body
head = document.head

注意:若脚本放在 <head>,则脚本访问不到 docment.body 元素,因为浏览器还未读到它。

子节点

使用 childNodesfirstChildlastChild访问子节点,如访问 body 中的所有子元素:

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

<head>
    <title>Document</title>
</head>

<body>
    <li>a</li>
    <li>b</li>
</body>

<script>
    for (let i = 0; i < document.body.childNodes.length; i++) {
        console.log(document.body.childNodes[i]);
    }
</script>

</html>

结果为:text、li、text、p、text、script、text、script

注意:显示的节点只包含 script 之前的所有 body 子节点。

DOM 集合

DOM 集合是可迭代对象,可以使用 for...of 迭代它,若要使用数组方法,则需要通过Array.from 将集合转化为数组:

console.log(Array.from(document.body.childNodes).filter);

注意

  1. DOM 集合是只读的
  2. 不要用 for…in 遍历集合

兄弟节点和父节点

// <body> 的父节点是 <html>
alert(document.body.parentNode === document.documentElenent);	// true

// <head> 的下一个兄弟节点
alert(document.head.nextSibing);	// body

// <body> 的前一个兄弟节点
alert(document.body.previousSibling);	// HTMLHeadElement

2.3 纯元素导航

一般不希望得到文本节点和注释节点,只读取元素节点:

在这里插入图片描述

children	// 子元素
firstElementChild	// 第一个子元素
previousElementSibling	// 前一个兄弟元素
parentElement	// 父元素

document.documentElement的父节点为 document,但父元素节点为 null

若要从任意节点到达 <html> 元素节点:

let elem = document.getElementById("a");
while (elem = elem.parentElement) {
    console.log(elem);
}

遍历元素:

for (let elem of document.body.children) {
    console.log(elem);
}

应用例子

<html>
<body>
  <div>Users:</div>
  <ul>
    <li>John</li>
    <li>Pete</li>
  </ul>
</body>
</html>
  1. 从上面的 HTML 中获取 div;
  2. 获取第二个 li 元素
document.body.firstElementChild;
document.body.children[1].lastElementChild

3. 搜索元素

getElementById

通过 document.getElementById('id') 方法可以获取具有 id 属性的元素,如:

<div id="elem">
    <p id="text">text</p>
</div>

<script>
    let elem = document.getElementById('text');
    elem.style.color = 'orange';
</script>

补充:实际上直接使用 id 作为变量名也可以实现,但不建议!

querySelectorAll

最常用的方法是:elem.querySelectorALL(css),css 是一个 css 选择器:

<body>
    <div>
        <ol>
            <li>a</li>
            <li>b</li>
        </ol>
        <p>b</p>
    </div>
</body>

<script>
    let element = document.querySelectorAll('ol > li:last-child');
    element[0].style.color = 'red';
</script>

querySelector

elem.querySelector(css)也很常用,返回第一个与给定的 CSS 选择器匹配的元素。它等价于 elem.querySelectorAll(css)[0]

matches

使用 elem.matches(css) 可以检查 elem 是否与给定的 CSS 匹配,返回 truefalse

在遍历是可以利用 matches来进一步筛选元素:

<body>
    <ol>
        <li id="a.cn">a</li>
        <li id="b.com">b</li>
        <li id="c.org">c</li>
        <li id="d.com">d</li>
    </ol>
</body>

<script>
    let element = document.querySelectorAll('ol > li');
    for (let elem of element) {
        // 匹配 id 属性以 com 结尾的 li 标签
        if (elem.matches('li[id$="com"]')) {
            console.log(elem);
        }
    }
</script>

getElementsBy*

有以下三种方法:

  1. getElementsByTagName(tag):查找具有给定标签的元素;
  2. getElementsByClassName(className):返回具有给定 CSS类 的元素;
  3. getElementsByName(name):返回具有 name 属性的元素,不常用。

注意:

  1. 不能漏了s
  2. 返回值为一个 集合

3.1 实时性

看一个小例子:

<div>first</div>

<script>
    let divs = document.getElementsByTagName('div');
    console.log(divs.length);   // 1
</script>

<div>second</div>

<script>
    console.log(divs.length);   // 2
</script>

从上面的例子可以看到:getElementsByTagName 是动态实时的;querySelectorquerySelectorAllgetElementById是静态的。

应用例子

// 筛选 name="search" 的表单
document.querySelectorAll('form[name="search"]');

// 筛选 id 以 com 结尾的 a 标签
document.querySelectorAll('a[id$="com"]');

4. 节点属性

4.1 DOM 节点类

在这里插入图片描述

  1. Node —— 抽象类,充当 DOM 基础,提供树的核心功能:parentNode、nextSibling,childNodes 等;
  2. Element —— DOM 元素的基本类,提供元素级的导航,如 children;
  3. HTMLElement —— 最终是 HTML 元素的基本类,包括 HTMLInputElement,htmlBodyElement 和 HTMLAnchorElement 等;
alert(document.body.constructor.name);	// HTMLBodyElement

nodeName、tagName 和 nodeType

  1. nodeName:节点名,有 undefined, #comment 和 标签名
  2. nodeType:元素节点为 1, 文本节点为 3, document对象为 9
  3. tagName:标签名
console.dir(document.body.nodeType); // 1
console.log(document.body.tagName); // BODY

4.2 innerHTML

innerHTML 属性可以将元素内的 HTML 获取为字符串的形式(不包含外面的标签,但包括换行),也可以设置内容:

<body>
    <div id="text">
        Hello
        <b>World</b>
    </div>

</body>

<script>
    elem = document.getElementById('text');
    console.log(elem.innerHTML);
</script>

注意

下面的语句是对 HTML 的重写(移除旧的内容写入新的内容),两个语句等价:

elem.innerHTML += "...";
elem.innerHTML = elem.innerHTML + "...";

4.3 outerHTML

outerHTML可以获取完整的 HTML (包含外面的标签):

<body>
    <div id="text">
        Hello
        <b>World</b>
    </div>

</body>

<script>
    elem = document.getElementById('text');
    console.log(elem.outerHTML);
</script>

4.4 textContent

textContent 可以获取纯文本:

<div id="news">
  <h1>Headline!</h1>
  <p>Martians attack people!</p>
</div>

<script>
  // Headline! Martians attack people!
  alert(news.textContent);
</script>

假设有一个用户输入的字符串,我们希望将其显示出来。

  • 使用 innerHTML,我们将其“作为 HTML”插入,带有所有 HTML 标签。
  • 使用 textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理。
<div id="elem1"></div>
<div id="elem2"></div>

<script>
    let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");

    elem1.innerHTML = name;
    elem2.textContent = name;
</script>

在大多数情况下,我们期望来自用户的文本,并希望将其视为文本对待。我们不希望在我们的网站中出现意料不到的 HTML。

4.5 hidden

hidden 属性可以设置元素是否隐藏,下面的代码使用定时函数设置了标签是否可见,实现闪烁的效果:

<body>
    <div id="blink">I'm blinking</div>
</body>

<script>
    let timer = setInterval(() => {
        let elem = document.querySelector('#blink');
        elem.hidden = !elem.hidden;
    }, 1000);
</script>

其他属性

  1. value —— input,select,textarea 标签的 value
  2. href —— a 标签的 href
  3. id —— 所有元素的 id 属性
<body>
    <input type="text" name="fruit" id="fruit">
    <input type="submit" name="submit" id="submit" onclick="show()">
</body>

<script>
    let show = () => {
        let elem = document.getElementById('fruit');
        console.log(elem.value);
    }
</script>

此时点击提交按钮可以显示出框内的文字内容(value)

5. 修改文档

DOM 修改是创建“实时”页面的关键。

5.1 创建元素

document.createELement(tag) —— 创建元素节点;

document.createTextNode(text) —— 创建文本节点;

let div = document.createElement('div');
let textNOde = document.createTextNode('text');

举个栗子

创建一个 div 标签:

let div = document.createElement('div');
div.id = 'alert';
div.innerHTML = '<strong>Hello</strong> guys!';

此时元素不能在浏览器中显示出来,需要插入。

5.2 插入方法

有以下常用方法:

  1. node.append(nodes or strings) :在 node 的末尾插入(放在内部)
  2. node.prepend(nodes or strings) :在 node 的开头插入(放在内部)
  3. node.before(nodes or strings):在 node 前面插入
  4. node.after(nodes or strings):在 node 后面插入
  5. node.replaceWith(nodes or strings):替换 node 的内容

如在 ol 标签插入:

在这里插入图片描述

将上面的 div 标签插入到 document.body

document.body.append(div);

5.3 节点移除

使用 node.remove() 移除节点:

下面的代码在 ol 的尾部追加了一个 li 标签,1s 后再删除 li 标签:

<body>
    <ol>
        <li>a</li>
        <li>b</li>
        <li>c</li>
    </ol>
</body>

<script>
    let li = document.createElement('li');
    li.innerHTML = 'd';
    document.getElementsByTagName('ol')[0].append(li);
    let timer = setTimeout(() => li.remove(), 1000);
</script>

5.4 克隆节点

使用 elem.cloneNode(true) 创建元素的一个 “深” 克隆(复制所有特性和子元素);

使用 elem.cloneNode(false) 创建元素的一个 “深” 克隆;

let div = document.getElementsByName('div')[0];
let div2 = div.cloneNode(true); // 克隆消息

6. 样式和类

可以用 javascript 设置 style 属性:

let circle = document.querySelector('#circle');
let le = 0;
let timer = setInterval(() => {
    le += 1;
    circle.style.left = le + 'px';
    if (circle.style.left == '650px') {
        clearInterval(timer);
    }
}, 5);

上面的 HTML 可以让绿色的圆匀速滚动,通过设置 elem.stle.xx 设置样式元素的属性。

可以查看 style:

console.log(document.body.style);

6.1 classList & className

使用 className 可以查看标签的所有类名;

使用 classList 可以增加 / 删除标签类名;

举个栗子:

<body>
    <div class="class1 class2"></div>
</body>

<script>
    let div = document.querySelector('div');
    // 两个类: class1 class2
    console.log(div.className);

    div.classList.add('class3');
    // 三个类: class1 class2 class3
    console.log(div.className);
</script>

classList 常用方法:

  1. elem.classList.add/remove(class) —— 添加 / 移除
  2. elem.classList.toggle(class) —— 类不存在就添加,否则移出
  3. elem.classList.contains(class) —— 检查是否有给定类

6.2 应用例子

创建一个 div ,添加内容为 Hello,设置颜色为红色,背景为粉色:

let div = document.createElement('div');
div.innerHTML = 'Hello';
document.body.append(div);
div.style.color = 'red';
div.style.backgroundColor = 'pink';
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐