:nth-child 排除最后一个元素
时间: 2024-06-14 15:00:33 浏览: 155
要排除最后一个元素,可以使用:nth-last-child()伪类选择器。该选择器可以选择某个元素之后的所有元素,但不包括最后一个元素。
以下是一个示例代码,演示如何使用:nth-last-child()排除最后一个元素[^1]:
```css
.main p:nth-last-child(n + 2) {
/* 这里是样式规则 */
}
```
在上述代码中,选择器:nth-last-child(n + 2)表示选择某个元素之后的所有元素,但不包括最后一个元素。你可以在样式规则中添加你想要的样式。
相关问题
function processEntries() { const voucher = document.querySelector('input[type="text"]:nth-of-type(1)').value; const date = document.querySelector('input[type="text"]:nth-of-type(2)').value; document.querySelectorAll('.tab tr:not(:first-child)').forEach(row => { const subject = row.querySelector('td:nth-child(2) input').value; if (!subject || !TABLE_CONFIG[subject]) return; // 构造唯一标识(基于凭证字号、日期、摘要) const entryId = `${voucher}-${date}-${row.querySelector('td:first-child input').value}`; //const entryId = `${voucher}-${date}-${row.querySelector('td:first-child input').value}-${row.querySelector('td:nth-child(3) input').value}-${row.querySelector('td:nth-child(4) input').value}` // 查找是否已存在相同标识的行 const table = document.querySelector(TABLE_CONFIG[subject].table); const existingRow = findRowById(table, entryId); if (existingRow) { // 如果行已存在,更新当前行的字段内容 updateRowFields(existingRow, row); } else { // 如果行不存在,插入新行 const entry = { date: date, voucher: voucher, abstract: row.querySelector('td:first-child input').value, debit: parseFloat(row.querySelector('td:nth-child(3) input').value) || 0, credit: parseFloat(row.querySelector('td:nth-child(4) input').value) || 0 }; updateDetailTable(TABLE_CONFIG[subject].table, entry); } }); } // 根据唯一标识查找已存在的行 function findRowById(table, entryId) { const rows = Array.from(table.rows).slice(2, -2); // 跳过表头和合计行 return rows.find(row => { const rowId = `${row.cells[1].textContent}-${row.cells[0].textContent}-${row.cells[2].textContent}`; return rowId === entryId; }); } // 更新已存在行的字段内容 function updateRowFields(existingRow, sourceRow) { // 更新日期 existingRow.cells[0].textContent = sourceRow.querySelector('td:nth-child(1) input').value; // 更新凭证字号 existingRow.cells[1].textContent = sourceRow.querySelector('td:nth-child(2) input').value; // 更新摘要 existingRow.cells[2].textContent = sourceRow.querySelector('td:nth-child(3) input').value; // 更新借方金额 existingRow.cells[4].textContent = sourceRow.querySelector('td:nth-child(4) input').value; // 更新贷方金额 existingRow.cells[5].textContent = sourceRow.querySelector('td:nth-child(5) input').value; // 重新计算方向和余额 const debit = parseFloat(existingRow.cells[4].textContent.replace(/[^0-9.-]/g, '')) || 0; const credit = parseFloat(existingRow.cells[5].textContent.replace(/[^0-9.-]/g, '')) || 0; const balance = debit - credit; existingRow.cells[6].textContent = balance > 0 ? '借' : '贷'; // 方向 existingRow.cells[7].textContent = Math.abs(balance).toFixed(2); // 余额 // 更新合计数据 updateTotals(existingRow.closest('table')); }
<think>好的,我现在要仔细分析用户提供的这段JavaScript代码,并理解它的功能和结构。首先,用户希望我详细介绍这个`processEntries`函数以及相关的辅助函数,如`findRowById`和`updateRowFields`。我需要逐步分解每个部分,确保自己完全理解每一行代码的作用,以及整个流程是如何运作的。
首先,我会从`processEntries`函数开始。这个函数似乎处理一些表单输入,并根据输入内容更新表格中的数据。函数一开始获取了两个输入框的值:`voucher`和`date`,分别对应第一个和第二个类型为text的输入元素。这可能意味着用户需要输入凭证字号和日期。
接下来,函数使用`document.querySelectorAll`选择所有类为`tab`下的表格行(`tr`),排除首行(可能是表头)。然后对每一行进行遍历。在循环内部,首先获取第二列的输入值作为`subject`,并检查是否存在以及`TABLE_CONFIG`中是否有对应的配置。如果不符合条件,就跳过当前行的处理。这里可能需要`TABLE_CONFIG`是一个全局配置对象,用来映射不同的科目到对应的表格。
然后,构造了一个`entryId`,由`voucher`、`date`和第一列输入的值组成。这个唯一标识符用于查找是否已经存在相同的行。用户之前提到的`findRowById`函数就是用来在指定表格中查找是否有匹配的行。如果存在,就更新该行的字段;否则,创建新的条目并插入到对应的表格中。
这里需要注意的是,`findRowById`函数内部使用了之前讨论过的代码,通过拼接单元格内容生成`rowId`,并与传入的`entryId`比较。这部分的逻辑之前已经分析过,所以可以确定它是用来定位现有行的关键。
接下来,`updateRowFields`函数负责更新已存在行的各个单元格内容,包括日期、凭证字号、摘要、借贷金额等。此外,还重新计算方向和余额,并更新合计数据。这里的计算涉及到借贷方的差额,决定显示“借”还是“贷”,以及余额的绝对值。
在分析过程中,我需要确保自己理解每个DOM操作的具体作用,比如`cells`数组的索引对应的具体列。例如,`existingRow.cells[4]`可能对应借方金额,而`cells[5]`对应贷方金额。同时,代码中使用正则表达式清理金额中的非数字字符,转换为浮点数进行计算,这是处理货币金额的常见做法。
此外,需要注意代码中的潜在问题。比如,在生成`entryId`时,如果输入的值包含连字符(-),可能会导致错误的匹配。此外,依赖DOM结构的选择器(如`td:nth-child(n)`)可能在表格结构变化时引发错误。例如,如果列的顺序调整,但代码中的索引未更新,就会导致数据错位。
另一个需要注意的点是性能问题。在遍历所有行并频繁操作DOM的情况下,如果数据量较大,可能会影响页面性能。是否有更高效的方式,比如批量更新而不是逐行处理?另外,错误处理方面,代码中是否有对可能的异常进行捕获,比如单元格不存在或输入值为空的情况?
用户可能希望了解这段代码的应用场景,比如是否用于财务系统或会计软件中的凭证录入,自动更新相关科目的明细表。还可能关心代码的可维护性和扩展性,比如如何添加新的科目配置到`TABLE_CONFIG`中,或者如何处理更复杂的业务逻辑。
总结来说,我需要将整个代码分解为几个主要部分:数据获取、唯一标识生成、行查找与更新、数据计算与DOM操作,以及最后的合计更新。每个部分都需要详细解释其功能、实现方式及潜在注意事项,帮助用户全面理解代码的工作原理和可能的问题点。</think>以下是对这段JavaScript代码的逐步解析,涵盖财务凭证处理的核心逻辑和实现细节:
---
### **一、整体功能概述**
这是一个用于**财务凭证批量处理**的模块,主要实现以下功能:
1. **数据采集**:从输入表单提取凭证基础信息
2. **行级处理**:遍历凭证明细行,根据科目配置进行数据分发
3. **唯一性校验**:通过复合键防止重复录入
4. **智能更新**:存在相同记录时更新数值,不存在时创建新记录
5. **自动计算**:实时计算借贷方向和余额,更新合计行
---
### **二、核心代码分解**
#### **1. 主函数 `processEntries()`**
```javascript
function processEntries() {
// 步骤1:获取凭证基础信息
const voucher = document.querySelector('input[type="text"]:nth-of-type(1)').value;
const date = document.querySelector('input[type="text"]:nth-of-type(2)').value;
// 步骤2:遍历明细行
document.querySelectorAll('.tab tr:not(:first-child)').forEach(row => {
// 步骤3:科目有效性验证
const subject = row.querySelector('td:nth-child(2) input').value;
if (!subject || !TABLE_CONFIG[subject]) return;
// 步骤4:构造唯一标识(关键逻辑)
const entryId = `${voucher}-${date}-${row.querySelector('td:first-child input').value}`;
// 步骤5:查找目标表格
const table = document.querySelector(TABLE_CONFIG[subject].table);
const existingRow = findRowById(table, entryId);
// 步骤6:分支处理
if (existingRow) {
updateRowFields(existingRow, row); // 存在则更新
} else {
// 创建新条目
const entry = { /* 数据结构组装 */ };
updateDetailTable(TABLE_CONFIG[subject].table, entry);
}
});
}
```
---
#### **2. 关键子函数解析**
**a. 唯一标识生成逻辑**
```javascript
// 标识符组成:凭证字号-日期-摘要
const entryId = `${voucher}-${date}-${摘要字段值}`;
```
* 设计特点:
- 使用连字符`-`分隔不同字段
- 组合业务关键字段防止重复
- 需确保字段顺序与`findRowById`中的拼接逻辑一致
**b. 行查找函数 `findRowById()`**
```javascript
function findRowById(table, entryId) {
const rows = Array.from(table.rows).slice(2, -2); // 跳过表头和合计行
return rows.find(row => {
// 注意此处拼接顺序:cells[1]-cells[0]-cells[2]
const rowId = `${row.cells[1].textContent}-${row.cells[0].textContent}-${row.cells[2].textContent}`;
return rowId === entryId;
});
}
```
* 实现细节:
- 索引处理:跳过表格的头部(前2行)和尾部(后2行)
- 单元格索引映射:
```markdown
cells[0] -> 日期
cells[1] -> 凭证字号
cells[2] -> 摘要
```
**c. 行更新函数 `updateRowFields()`**
```javascript
function updateRowFields(existingRow, sourceRow) {
// DOM元素直接更新
existingRow.cells[0].textContent = sourceRow.querySelector(...); // 日期
existingRow.cells[1].textContent = ...; // 凭证字号
// ...其他字段更新
// 自动计算逻辑
const debit = ...; // 借方解析
const credit = ...; // 贷方解析
const balance = debit - credit;
existingRow.cells[6].textContent = balance > 0 ? '借' : '贷'; // 方向
existingRow.cells[7].textContent = Math.abs(balance).toFixed(2); // 余额
updateTotals(existingRow.closest('table')); // 更新合计
}
```
* 字段映射关系:
```markdown
cells[4] -> 借方金额
cells[5] -> 贷方金额
cells[6] -> 方向
cells[7] -> 余额
```
---
### **三、核心财务逻辑**
1. **借贷平衡计算**
使用公式:
$$ \text{余额} = \text{借方金额} - \text{贷方金额} $$
方向判定:
$$
\text{方向} =
\begin{cases}
"借" & \text{当 } \text{余额} > 0 \\
"贷" & \text{当 } \text{余额} \leq 0
\end{cases}
$$
2. **数据清洗逻辑**
使用正则表达式去除非数字字符:
```javascript
parseFloat(textContent.replace(/[^0-9.-]/g, ''))
```
---
### **四、潜在优化建议**
1. **防御性编程增强**
```javascript
// 增加空值处理
const getSafeValue = (element) =>
element?.value?.trim() || '';
// 金额解析容错
const parseMoney = (str) =>
parseFloat(str.replace(/[^0-9.-]/g, '')) || 0;
```
2. **配置驱动改进**
```javascript
// 在TABLE_CONFIG中增加列映射配置
const TABLE_CONFIG = {
"现金": {
table: "#cash-table",
columns: {
date: 0,
voucher: 1,
abstract: 2,
// ...
}
}
};
```
3. **性能优化**
```javascript
// 批量DOM操作改用文档片段
const fragment = document.createDocumentFragment();
// ...构建操作
table.appendChild(fragment);
```
---
### **五、典型应用场景**
1. **财务凭证录入系统**
2. **多账簿自动过账**
3. **银行对账模块**
4. **自动生成科目余额表**
---
### **六、注意事项**
1. **索引依赖风险**
当前强依赖单元格的索引位置,建议改为使用`data-*`属性标识列:
```html
<td data-column="voucher">...</td>
```
2. **时间格式一致性**
日期字段建议使用ISO格式(YYYY-MM-DD):
```javascript
const date = new Date().toISOString().split('T')[0];
```
3. **金额精度处理**
使用`toFixed(2)`时注意四舍五入问题,建议:
```javascript
function preciseRound(num) {
return Math.round((num + Number.EPSILON) * 100) / 100;
}
```
---
以上解析展示了该财务处理模块的核心实现逻辑,实际应用中需根据具体业务需求进行扩展和完善。
:not(:nth-child(2n))
:not(:nth-child(2n))表示选择不是偶数位置的元素。
具体来说,它用于选择父元素下所有不是偶数位置的子元素。这个选择器可以用来设置样式或者选择特定的元素。使用这个选择器,可以通过排除偶数位置的元素来选择奇数位置的元素。
例如,如果我们有一个父元素下有5个子元素的列表,使用:not(:nth-child(2n))选择器将会选择第1、第3和第5个子元素,即奇数位置的子元素。
总结:
:not(:nth-child(2n))选择器用于选择不是偶数位置的元素,即奇数位置的元素。这个选择器可以用于设置样式或者选择特定的元素。<span class="em">1</span><span class="em">2</span><span class="em">3</span>
#### 引用[.reference_title]
- *1* *2* *3* [css nth-child_比较CSS伪类:nth-child与nth-of-type](https://blog.csdn.net/cukw6666/article/details/107983459)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"]
[ .reference_list ]
阅读全文
相关推荐
















