AlexZ33 / lessions

自己练习的各种demo和课程
12 stars 2 forks source link

Javascript教程 :DOM基础 #81

Open AlexZ33 opened 4 years ago

AlexZ33 commented 4 years ago

DOM基础

我们在js部分第一节课 JavaScript发展史 中有简单的说过DOM

Dom知识结构.xmind在线查看

什么是 DOM ?

DOM(文档对象模型)是针对于xml但是扩展用于HTML的应用程序编程接口,定义了访问和操作HTML的文档的标准。
W3C文档对象模型是中立于平台和语言之间的接口,它允许程序和脚本动态的访问和更新文档的内容、结构、样式。总之HTML是关于如何获取、修改、添加和删除HTML元素的标准。

DOM 分层节点

DOM的分层节点一般被称作是DOM树,树中的所有节点都可以通过脚本语言例如JS进行访问,所有HTMlL元素节点都可以被创建、添加或者删除。
在DOM分层节点中,页面就是用分层节点图表示的。

当咱们访问一个web页面时,浏览器会解析每个HTML元素,创建了HTML文档的虚拟结构,并将其保存在内存中。接着,HTML页面被转换成树状结构,每个HTML元素成为一个叶子节点,连接到父分支。 考虑以下 Html 结构:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>A super simple title!</title>
</head>
<body>
<h1>A super simple web page!</h1>
</body>
</html>

在这个结构的顶部有一个document,也称为根元素,它包含另一个元素:htmlhtml元素包含一个head,而 head 又有一个title。 然后body 包含一个h1。 每个HTML元素都由特定类型(也称为接口)表示,并且可能包含文本或其他嵌套元素:

document (HTMLDocument)
  |
  | --> html (HTMLHtmlElement)
          |  
          | --> head (HtmlHeadElement)
          |       |
          |       | --> title (HtmlTitleElement)
          |                | --> text: "A super simple title!"
          |
          | --> body (HtmlBodyElement)
          |       |
          |       | --> h1 (HTMLHeadingElement)
          |              | --> text: "A super simple web page!"

每个HTML元素都来自Element,但其中很大一部分都是专用的。 咱们可以检查原型以查找元素所属的“种类”。 例如,h1元素是HTMLHeadingElement

document.querySelector('h1').__proto__
// Output: HTMLHeadingElement

HTMLHeadingElement则是HTMLElement的后代:

document.querySelector('h1').__proto__.__proto__
// Output: HTMLElement

此时(特别是初学者)可能会对documentwindow之间的区别产生一些混淆。接下来看看它们有何不同!

document 和 window 之间的区别

简单来说,documentwindow的一个对象属性。window 对象表示浏览器中打开的窗口。如果文档包含框架(frameiframe 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象。所有的全局函数和对象都属于 window 对象的属性和方法。
区别:

  1. window 指窗体。document指页面。documentwindow的一个子对象。
  2. 用户不能改变 document.location(因为这是当前显示文档的位置)。但是,可以改变window.location (用其它文档取代当前文档)window.location本身也是一个对象,而document.location不是对象。

document接口有许多实用方法,比如querySelector(),它是用于查找给定页面内HTML元素的方法:

document.querySelector('h1');

window表示当前的浏览器,下面代码与上面等价:

window.document.querySelector('h1');

当然,更常见的是用第一种方式。
window是一个全局对象,可以从浏览器中运行的任何JS代码直接访问。 window暴露了很多属性和方法,如:

window.alert('Hello world'); // Shows an alert
window.setTimeout(callback, 3000); // Delay execution
window.fetch(someUrl); // make XHR requests
window.open(); // Opens a new tab
window.location; // Browser location
window.history; // Browser history
window.navigator; // The actual user agent
window.document; // The current page

因为这些属性和方法也是全局的,所以也可以这样访问它们

alert('Hello world'); // Shows an alert
setTimeout(callback, 3000); // Delay execution
fetch(someUrl); // make XHR requests
open(); // Opens a new tab
location; // Browser location
history; // Browser history
navigator; // The actual user agent
document;// The current page

其中有些咱们都已经很熟悉了,如setTimeout() 的方法。 例如,当咱们想要得知当前用户的浏览器语言时,window.navigator就非常有用:

if (window.navigator) {
  var lang = window.navigator.language;
  if (lang === "en-US") {
    // show something
  }
  if (lang === "it-IT") {
    // show something else
  }
}

DOM 常用方法

获取节点

// 通过id号来获取元素,返回一个元素对象
document.getElementById(idName) 

// 通过name属性获取id号,返回元素对象数组 
document.getElementsByName(name)  

// 通过class来获取元素,返回元素对象数组
document.getElementsByClassName(className)   
// 通过标签名获取元素,返回元素对象数组
document.getElementsByTagName(tagName)       

查找拥有指定id的第一个元素节点。
例如我们查找一个id属性值为“id”的元素。

var id  = documentElementById('id');
console.log(id);

查找所有带有class指定属性值的元素。
例如我们查找class属性值为“class”的元素。

var className = document.getElementsByClassName('class');
console.log(className);

查找所有指定标签名的元素,例如我们查找标签名为div的元素。

var tag = document.getElementsByTagName('div');
console.log(tag);

查找所有指定name属性值的元素,例如我们查找name属性值为name的元素。

var name = document.getElementsByName('name');
console.log(name);

  - 查找所有指定css选择器的元素。<br />例如我们查找css选择器为#id的元素。<br />

var selectors = document.querySelectorAll('#id'); console.log(selectors);


<a name="a9oVx"></a>
#### 获取/设置元素的属性值:

// 括号传入属性名,返回对应属性的属性值 element.getAttribute(attributeName) // 传入属性名及设置的值 element.setAttribute(attributeName,attributeValue)

<a name="MvQVj"></a>
#### 创建节点Node

// 创建一个html元素,这里以创建h3元素为例 document.createElement("h3") // 创建div元素 var div = document.createElement('div'); cnosole.log(div);

// 创建一个文本节点; document.createTextNode(String);

var textNode = document.createTextNode('大家好'); console.log(textNode);

// 创建一个属性节点,这里以创建class属性为例 // 属性节点包含两类,一类是DOM元素的基本属性,还有一类是自定义属性。 // 基本属性既可以通过如下方式设置,也可以通过 createAttribute 方式设置。 document.body.id = 'container'; document.createAttribute("class");

// 注意:自定义属性是一定要使用 createAttribute 方式来创建的。 // 比如创建一个自定义属性 custom 。 var dataAttribute = document.createAttribute('custom'); cnosole.log(dataAttribute);

// 创建一个内容为“这一个标签”的注释节点 var comment = document.createComment('这是一个标签'); console.log(comment);

拓展: [如何创建一个DOM树](https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model/How_to_create_a_DOM_tree)
<a name="dE2sj"></a>
#### 增添(插入)节点

// 往element内部最后面添加一个节点,参数是节点类型 element.appendChild(Node); // 在element内部的中在existingNode前面插入newNode elelment.insertBefore(newNode,existingNode);


- 在父节点的最后一个子节点后面插入新的节点<br />例如我们创建一个新节点插入到 body 节点中。

var newNode = document.createElement('div'); document.body.appendChild(newNode);


- 在指定元素节点之前插入一个新元素节点。<br />例如我们在 id 属性值为 id 的元素前面添加一个新元素。

var newNode = document.createElement('div'); var div = document.getElementById('id'); document.body.insertBefore(newNode, div);


- 在指定元素中添加一个属性节点,如果元素中已有该属性改变属性值。例如我们在 id 属性值为 id 的元素中添加一个属性名为  class  的属性节点。

var div = document.getElementById('id') div.setAttribute('class', 'white');//第一个参数属性名,第二个参数属性值。


<a name="JBa8m"></a>
#### 删除节点

- 删除元素节点<br />删除父元素中的一个子节点。<br />例如我们删除 body 元素下 id 属性值为 id 的元素。

var div = document.getElementById('id'); document.body.removeChild(div);


- 也可以直接删除,谁调用删除谁。

var div = document.getElementById('id'); div.remove();


- 删除属性节点<br />删除属性节点,无返回值。<br />例如我们删除 id 属性值为 id 的元素中 class 属性。

var div = document.getElementById('id'); div.removeAttribute('class');


- 删除属性节点,有返回值。例如我们删除 id 属性值为 id 的元素中 class 属性。

var div = document.getElementById('id'); var attribute = div.getAttributeNode('class'); div.removeAttributeNode(attribute);

//删除当前节点下指定的子节点,删除成功返回该被删除的节点,否则返回null element.removeChild(Node)

<a name="rg3b8"></a>
#### 替换元素

- 替换元素

创建一个新节点替换父元素已有的节点。<br />例如我们把id属性值span的span标签替换成p标签。

var newElement = document.createElement('p'); var span = document.getElementById('span'); //查找被替换元素的父元素。 var parent = span.parentNode; //替换旧元素为新元素,返回值是被替换掉的旧元素。 var replace = parent.replaceChild(newElement, span);


<a name="jB0pF"></a>
## DOM常用属性
<a name="wx7v6"></a>
#### 获取当前元素的父节点

// 返回当前元素的父节点对象 element.parentNode

<a name="RY3zj"></a>
#### 获取当前元素的子节点

// 返回当前元素所有子元素节点对象,只返回HTML节点 element.chlidren // 返回当前元素多有子节点,包括文本,HTML,属性节点。(回车也会当做一个节点) element.chilidNodes // 返回当前元素的第一个子节点对象 element.firstChild // 返回当前元素的最后一个子节点对象 element.lastChild

<a name="4PdHs"></a>
#### 获取当前元素的同级元素

// 返回当前元素的下一个同级元素 没有就返回null element.nextSibling // 返回当前元素上一个同级元素 没有就返回 null element.previousSibling

<a name="NkNij"></a>
#### 获取当前元素的文本

// 返回元素的所有文本,包括html代码 element.innerHTML // 返回当前元素的自身及子代所有文本值,只是文本内容,不包括html代码 element.innerText

<a name="bihQA"></a>
#### 获取当前节点的节点类型

// 返回节点的类型,数字形式(1-12) // 常见几个1:元素节点,2:属性节点,3:文本节点。 node.nodeType

<a name="ac6Jk"></a>
#### 设置样式

// 设置元素的样式时使用style element.style.color=“#eea”;

<a name="rqTYK"></a>
## DOM 操作
DOM中的每个HTML元素也是一个节点,可以像这样查找节点:

document.querySelector('h1').nodeType;

上面会返回`1`,它是`Element`类型的节点的标识符,还可以检查节点名称:

document.querySelector('h1').nodeName; "H1"

上面的示例返回大写的节点名。但是需要理解的最重要的概念是,咱们主要使用DOM中的两种类型的节点:

- 元素节点
- 文本节点

创建元素节点,可以通过 `createElement`方法:

var heading = document.createElement('h1');

创建文本节点,可能通过 `createTextNode` 方法:

var text = document.createTextNode('Hello world');

接着将两个节点组合在一起,然后添加到 `body` 上:

var heading = document.createElement('h1'); var text = document.createTextNoe('Hello world'); heading.appendChild(text); document.body.appendChild(heading)

在学习Dom操作时候,这些方法需要牢记并熟练使用的。<br />目前像咱们用这种方式创建和操作元素,是属于命令式DOM操作。现代前端库通过支持声明性方法来解决这个问题,如 JQuery,咱们可以声明需要什么HTML元素,其它就由库来完成。
<a name="urKVS"></a>
## DOM 操作和 jQuery
大部分可能会想,咱们直接使用 JQ 不就行了,为啥还要用如`createElement`这些原生的方法,多费劲。<br />请注意jQuery正在渐渐消失。Bootstrap 5将把它从依赖项中删除,还有很多项目也在删除它。这背后有一个合理的原因:原生DOM API提供了大量像JQ这样操作DOM的简便方法,足以替代jQuery一些常用的DOM操作。<br />如果只是想进行简单的交互和操作,请使用普通的JS。咱们甚至可以创建自己的迷你框架来抽象最常见的操作:创建元素、追加、创建文本。
<a name="1cwTi"></a>
## 总结
**DOM**是浏览器创建并保留在内存中的网页的虚拟副本。创建、修改、删除 HTML 元素,这些属于 “DOM 操作”。在过去即使对于更简单的任务,咱们也要依赖于 jQuery,但今天原生 API 已经互相兼容并且足够成熟足以替代 jQuery 了。<br />jQuery不会很快消失,但是每个JS开发人员都必须知道如何使用原生API操作DOM。这样做有很多原因,额外的库增加了JS应用程序的加载时间和大小,更不用说DOM操作在技术面试也经常出现。<br />操作 **DOM** 最常用的方法是 `document.createElement()` 用于创建新的 HTML 元素,`document.createTextNode()` 用于在 DOM 内创建文本节点。需要注意的是 `.appendChild()` 用于将新的 HTML 元素或文本节点附加到现有元素。<br />虽然很好的了解本机 API 是很好的,但是现代前端库也提供了无可置疑的好处。尽管用“原生” JS 去构建大型JS 程序确实是可行的,但有时 Angular、React、Vue可以提供很多帮助。仅使用 JavaScript 来处理更简单的原型和中小型应用也是明智之举。

<a name="q60hR"></a>
# 作业练习
<a name="78Tl7"></a>
### 任务一:

在你的IDE(Visual Studio Code或其它)中新建一个js_dom_task01.html(也可以其它名字)的文件,然后把下面代码复制粘贴到文件中:

<!DOCTYPE html>

IFE ECMAScript

Demo

Hello World


然后使用 Chrome 浏览器打开这个网页。并打开 Chrome 的开发者工具(不知道如何打开的请自行百度)。<br />这时候你可以发现在开发者工具的 Console 中应该有输出`Hello World`了。在这个例子中,`script`标签中的内容就是 JavaScript 代码,在这个代码中,我们只有一行代码,我们执行力`console.log`一个函数,用于在Chrome中的Console中输出一个内容,这将是我们今后写 JavaScript 代码中最常用的调试手段之一,当然你也可以尝试一下十多年前前端工程师的调试方法,把`console.log`换成`alert`。<br />在这一行代码中,还可以关注的是 `document.getElementById` 和 `innerHTML`,可以通过自行搜索了解他们是什么意思。<br />接下来,我们给这个代码加一些内容:<br />

<!DOCTYPE html>

IFE ECMAScript

Demo

你是谁?

把刚才的代码替换一下,然后尝试在浏览器中运行一下,在输入框中输入一些文字,然后点按钮。看看发生了什么。<br />在这个短小的例子中,我们简单演示了 JavaScript 用得最多的场景**,从提供给用户的表单组件中获取输入内容,然后做一些处理,并在页面中做出对应的呈现**。好,接下来,需要你大量的进行阅读和练习
<a name="5AgnE"></a>
### 任务二:

<a name="JftWI"></a>
#### 编码

<!DOCTYPE html>

IFE ECMAScript

运算结果

复制以上代码到你的IDE中,然后在`script`标签中编写代码,实现以下需求:

- 点对应加减乘除按钮的时候,将两个输入框中的数字做对应的算术,并将结果显示在id为result的p标签内。
- 暂时不用做任何异常处理
<a name="wl1WC"></a>
#### 常见错误

- 最初代码

document.getElementById('add-btn').onclick = function () { var result = document.getElementById('first-number').value + document.getElementById('second-number').value; document.getElementById('result').innerHTML = result; }

此时输出的结果:0 + 0 = 00,显然直接从Input里面获取到的数据是String类型<br />学习使用parseInt(string, radix)函数<br />`parseInt() 函数解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。`

- [parseInt(string, radix);](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt)

**变量**

string 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。 radix 一个介于2和36之间的整数(数学系统的基础),表示上述字符串的基数。比如参数"10"表示使用我们通常使用的十进制数值系统。始终指定此参数可以消除阅读该代码时的困惑并且保证转换结果可预测。当未指定基数时,不同的实现会产生不同的结果,通常将值默认为10。

**返回值**<br />`返回解析后的整数值。 如果被解析参数的第一个字符无法被转化成数值类型,则返回 NaN。`<br />注意:radix参数为n 将会把第一个参数看作是一个数的n进制表示,而返回的值则是十进制的。例如:<br />parseInt('123', 5) // 将'123'看作5进制数,返回十进制数38 => 1_5^2 + 2_5^1 + 3*5^0 = 38

- 改正后代码

document.getElementById('add-btn').onclick = function () { var result = parseInt(document.getElementById('first-number').value) + parseInt(document.getElementById('second-number').value); document.getElementById('result').innerHTML = result; }


其实我们可以用简便的方法写  `+ (document.getElementById('first-number').value)` 
<a name="wEUG4"></a>
### 
<a name="eKXc9"></a>
### 任务三:让你和页面对话
[https://gitee.com/turingitclub/javascript-learning/tree/dev/03-%20DOM%E5%9F%BA%E7%A1%80](https://gitee.com/turingitclub/javascript-learning/tree/dev/03-%20DOM%E5%9F%BA%E7%A1%80)

<a name="IB3km"></a>
### 任务四:
[https://gitee.com/turingitclub/javascript-learning/tree/dev/03-%20DOM%E5%9F%BA%E7%A1%80/task04](https://gitee.com/turingitclub/javascript-learning/tree/dev/03-%20DOM%E5%9F%BA%E7%A1%80/task04)
<a name="L1byx"></a>
# 拓展阅读

- [W3C  JavaScript HTML DOM  快速入门](https://www.w3school.com.cn/js/js_htmldom.asp)
- [文档对象模型 (DOM) MDN](https://developer.mozilla.org/zh-CN/docs/Web/Events)
- [《JavaScript 高级程序设计》](https://book.douban.com/subject/10546125/)第三版 或 第四版, 第10,11,12章节 (立志从事前端开发必看)
- [操作DOM MDN](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents)