JAVASCRIPT 基础 DOM元素,MAP方法,获取输入值
从输入框获取数据的一般写法是:
```javascript
const w = parseFloat(document.getElementById("weight").value);
const h = parseFloat(document.getElementById("height").value);
```
而从弹窗获取数据一般写法是:
```javascript
const w = parseFloat(prompt("输入体重:"));
const h = parseFloat(prompt("输入身高:"));
```
下面是几种常见的浏览器弹窗示例:
1. 只展示信息的“警告弹窗”:
```html
<!-- HTML 中不需要额外代码,JS 可以直接写:-->
<script>
alert("这是一个只展示信息的弹窗");
</script>
```
它不会让用户输入内容,只是单纯显示文本。
2. 带“确认/取消”的弹窗:
```javascript
if (confirm("确认要删除吗?")) {
console.log("用户确认");
} else {
console.log("用户取消");
}
```
它只显示信息并提供“确定/取消”按钮,没有输入框。
3. 带输入框的弹窗 (prompt):
```javascript
const name = prompt("请输入你的名字:");
console.log(name);
```
它会自动出现一个能让用户输入文本的对话框,用户点击“确定”后将所输入的内容返回给 name 变量。没有办法在这个对话框里添加自定义标签,它是浏览器原生实现的输入弹窗。
---
## 1. innerHTML 的概念
innerHTML 不是分级的概念,而是一个属性,代表一个 DOM 元素的内部 HTML 内容。它不是"相对的"概念,而是指特定 DOM 元素的所有子内容(以 HTML 字符串形式)。当你访问 `element.innerHTML` 时,你获取的是该元素内部的所有 HTML 代码,不包括元素自身的标签。
## 2. 以给定的 HTML 片段为例
```html
<section>
<img
src="dinosaur.png"
alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth." />
<p>
Here we will add a link to the
<a href="https://www.mozilla.org/">Mozilla homepage</a>
</p>
</section>
```
对于 `<section>` 元素:
```html
- **innerHTML** 是:
<img
src="dinosaur.png"
alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth." />
<p>
Here we will add a link to the
<a href="https://www.mozilla.org/">Mozilla homepage</a>
</p>
```
- **outerHTML** 是:
```html
<section>
<img
src="dinosaur.png"
alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth." />
<p>
Here we will add a link to the
<a href="https://www.mozilla.org/">Mozilla homepage</a>
</p>
</section>
```
## 3. innerHTML 与 DOM 节点的关系
innerHTML
是 DOM 节点的一个属性,提供节点内部 HTML 的字符串表示。而 DOM 节点是文档对象模型中的元素,代表 HTML 文档的结构。
### 代码示例:
```javascript
// 获取 section 元素
const section = document.querySelector('section');
// 查看 DOM 节点本身
console.log("DOM 节点:", section);
// 输出: [object HTMLSectionElement] (浏览器中会显示更详细的对象信息)
// 查看 innerHTML (HTML 字符串)
console.log("innerHTML:", section.innerHTML);
// 输出: 内部的 HTML 内容字符串
// 查看 outerHTML (包含自身的 HTML 字符串)
console.log("outerHTML:", section.outerHTML);
// 输出: 包含自身在内的 HTML 内容字符串
// 查看 DOM 节点的子节点 (实际的 DOM 节点对象数组,而非 HTML 字符串)
console.log("子节点:", section.childNodes);
// 输出: NodeList [img, text, p]
// 修改 innerHTML (会替换所有子内容)
section.innerHTML = "<h1>新内容</h1>";
// 现在 section 只包含一个 h1 元素
```
DOM 节点是对象,而 innerHTML 是一个返回或设置 HTML 字符串的属性。当你修改 innerHTML 时,浏览器会解析这个字符串并创建新的 DOM 节点来替换原有内容。
---
把 <script> 放在 body 底部的策略对内联脚本和外部脚本都有效。当浏览器加载 HTML 时,直到解析到 <script> 标签时才会加载并执行这段脚本;因此,无论脚本代码内联还是通过 src 引入,都能确保在页面的元素加载完毕后再执行脚本。
---
给定一个包含学生成绩的字典数组,使用 map 修改数组,其中包含学生姓名和他们的成绩等级(>=90 为 "A", >=80 为 "B", >=70 为 "C", 其他为 "F")。
const students = [
{ name: 'Alice', score: 92 },
{ name: 'Bob', score: 76 },
{ name: 'Charlie', score: 85 },
{ name: 'Dave', score: 63 }
];
const passingScore = {
A: 90,
B: 80,
C: 70,
D: 60
}
students = students.map(onestudent => {
const score=onestudent.score;
;
for( const attribute in passingScore){
const threshold = passingScore.attribute;
if(score>=threshold){
return onestudent.grade>=threshold? onestudent.grade=`${attribute}` :`${attribute}`
}
}
});
console.log(students);
我认为return onestudent.grade>=threshold? onestudent.grade=${attribute}
:${attribute}
能保证“不断随着最大能达到的等级来更新,最终只留下最大匹配”
这段代码存在几个严重问题,**无法**实现"不断随着最大能达到的等级来更新,最终留下最大匹配":
1. **属性访问错误**:
```javascript
const threshold = passingScore.attribute;
```
应该用方括号访问:`passingScore[attribute]`,因为attribute是变量
2. **提前返回**:
```javascript
if(score>=threshold){
return onestudent.grade>=threshold? onestudent.grade=`${attribute}` :`${attribute}`
}
```
当找到第一个符合条件的阈值时就立即返回,无法比较所有等级
3. **遍历顺序问题**:`for...in`循环的遍历顺序不确定,可能先检查D再检查A
4. **返回值混乱**:
- 正在返回字符串值而不是学生对象
- 三元表达式逻辑不明确
// 正确的实现方式
const students = [
{ name: 'Alice', score: 92 },
{ name: 'Bob', score: 76 },
{ name: 'Charlie', score: 85 },
{ name: 'Dave', score: 63 }
];
const gradedStudents = students.map(student => {
const score = student.score;
if (score >= 90) {
return { name: student.name, grade: 'A' };
} else if (score >= 80) {
return { name: student.name, grade: 'B' };
} else if (score >= 70) {
return { name: student.name, grade: 'C' };
} else {
return { name: student.name, grade: 'F' };
}
});
console.log(gradedStudents);
---
# JavaScript map() 方法的参数解释
map()方法接受一个回调函数作为唯一必需的参数。实际语法是:
```javascript
array.map(callback)
```
其中 `callback` 是一个函数,会被应用于数组的每个元素。这个回调函数可以接收三个参数:
1. 当前元素
2. 当前索引(可选)
3. 原数组(可选)
## 两种常见的写法:
### 1. 内联定义函数:
```javascript
students = students.map(function(onestudent) {
// 这里的代码会应用到每个 student
return /* 某个值 */;
});
```
### 2. 使用预定义函数:
```javascript
function changeLevel(onestudent) {
// 处理逻辑
return /* 某个值 */;
}
students = students.map(changeLevel);
```
## 错误示例(不正确的语法):
```javascript
// 这是错误的!map 不接受两个参数
students = students.map(onestudent, changeLevel);
```
所以 students.map(function(onestudent) {...})是完全正确的语法,其中 `function(onestudent) {...}` 就是传递给 map()的回调函数。在这里,onestudent 是回调函数的参数,代表当前正在处理的数组元素。
在 JavaScript 的 map()函数中,参数是在函数定义时声明的。onestudent不需要在外部预先定义,它是作为回调函数的参数自动创建的。
这个概念可能对初学者来说有点抽象,让我用一个简单的例子来解释:
```javascript
// 普通的函数调用
function greet(person) {
console.log("Hello, " + person);
}
greet("John"); // person = "John"
// 类似地,map 自动为每个数组元素调用你的函数
students.map(onestudent => {
// 这里 onestudent 自动等于当前正在处理的数组元素
// 第一次循环:onestudent = { name: 'Alice', score: 92 }
// 第二次循环:onestudent = { name: 'Bob', score: 76 }
// 以此类推...
});
```
map() 方法会自动:
1. 遍历数组中的每个元素
2. 对每个元素调用你提供的函数
3. 将当前元素作为参数传递给函数
4. 收集函数的返回值组成一个新数组
所以 onestudent是在函数参数位置上定义的变量,它会自动接收当前正在处理的学生对象。这是 JavaScript 函数式编程的标准模式,你不需要预先声明这个变量。
---
1. DOM 节点层级判断标准
在 DOM 中,每个 HTML 元素都是一个节点,但节点不仅限于元素。节点类型包括:
- 元素节点(如 `<p>`、`<span>`)
- 文本节点(元素内的文本)
- 注释节点(HTML 注释)
- 属性节点等
2. DOM 节点和 CSS 选择器的对比
- DOM 包含文本节点,CSS 选择器不直接处理文本节点
- CSS 选择器有特殊语法(如 `>`、`+`、`~` 等组合器)
### 比较例子:
```html
<div id="container">
<p class="intro">Hello <span>world</span></p>
<p>Another paragraph</p>
</div>
```
**DOM 访问方式:**
```javascript
// 获取元素节点
const container = document.getElementById("container");
const paragraphs = container.getElementsByTagName("p");
const firstPara = paragraphs[0];
const span = firstPara.getElementsByTagName("span")[0];
// 访问子节点(包括文本节点)
console.log(firstPara.childNodes); // [文本节点"Hello ", span元素节点]
```
**对应的 CSS 选择器:**
```css
/* 选择container下的所有p */
#container p { }
/* 选择container下的直接p子元素 */
#container > p { }
/* 选择intro类的p内部的span */
p.intro span { }
```
CSS 选择器聚焦于元素的选择,而 DOM API 更关注节点的操作和遍历,包括文本节点、注释等非元素节点。
# DOM 选择器与节点关系
## 1. `document.querySelectorAll()` 的行为
`document.querySelectorAll()` 会选择文档中所有匹配 CSS 选择器的元素,无论它们在哪个层级,返回一个 NodeList。
### 1.1 对于示例文件中的选择器:
```javascript
// 选择所有 <p> 元素
const allPs = document.querySelectorAll("p");
console.log(allPs);
// 结果: NodeList(2) [p, p]
// 包含 box1 中的 "小猪课堂" 和 box2 中的 "box2p1"、"box2p2"
// 选择所有 <div> 元素
const allDivs = document.querySelectorAll("div");
console.log(allDivs);
// 结果: NodeList(2) [div#box1, div#box2]
// 包含两个 div,一个 id 为 box1,一个 id 为 box2
```
### 1.2 只选择 box2 中的第二个 span (hhh2):
```javascript
// 方法1:使用 CSS 选择器
const secondSpan = document.querySelector("#box2 span:nth-child(4)");
// 或
const secondSpan = document.querySelectorAll("#box2 span")[1];
secondSpan.textContent = "iii2";
// 方法2:使用 DOM 遍历
const box2 = document.getElementById("box2");
const spans = box2.getElementsByTagName("span");
spans[1].textContent = "iii2";
```
### 1.3 选择所有 span 元素并修改:
```javascript
// 选择所有 span 元素
const allSpans = document.querySelectorAll("span");
// 遍历并修改每一个
allSpans.forEach(span => {
span.textContent = "iii2";
});
// 或使用 for 循环
for (let i = 0; i < allSpans.length; i++) {
allSpans[i].textContent = "iii2";
}
```
## 2. DOM 节点关系与选择方法
针对你的文件,以下是访问各种节点关系的代码示例:
### 子节点 (Child Nodes)
```javascript
// 获取 box1 的所有子节点(包括文本节点和注释)
const box1 = document.getElementById("box1");
const box1Children = box1.childNodes;
console.log(box1Children);
// NodeList 包含所有子节点,包括文本节点和注释
// 只获取元素子节点(不包括文本节点和注释)
const box1ElementChildren = box1.children;
console.log(box1ElementChildren);
// HTMLCollection 只包含元素节点:p, span, em
```
### 后代节点 (Descendant Nodes)
```javascript
// 获取 body 下所有的 span 元素(后代,不限于直接子元素)
const allBodySpans = document.querySelectorAll("body span");
console.log(allBodySpans);
// NodeList 包含 body 下所有 span,无论层级深度
// 另一种获取特定后代的方式
const spanInBox2 = document.querySelectorAll("#box2 span");
console.log(spanInBox2);
```
### 父节点 (Parent Node)
```javascript
// 获取第一个 p 元素的父节点
const firstP = document.querySelector("p");
const parentOfP = firstP.parentNode;
console.log(parentOfP); // 是 box1 div
// 或者
console.log(firstP.parentElement);
```
### 兄弟节点 (Sibling Nodes)
```javascript
// 获取 box1 中 p 元素的下一个兄弟元素
const pInBox1 = document.querySelector("#box1 p");
const nextSibling = pInBox1.nextElementSibling;
console.log(nextSibling); // 是 span 元素
// 获取 box1 中 em 元素的上一个兄弟元素
const emInBox1 = document.querySelector("#box1 em");
const prevSibling = emInBox1.previousElementSibling;
console.log(prevSibling); // 是 span 元素
// 注意:nextSibling 和 previousSibling 会包含文本节点
// 而 nextElementSibling 和 previousElementSibling 只包含元素节点
```
这些方法允许你在 DOM 树中精确导航,根据节点之间的关系选择特定元素。与 CSS 选择器不同,这些 DOM 属性提供了更细粒度的控制和访问。