一、什么是 NamedNodeMap?
NamedNodeMap 是 DOM 中表示属性节点集合的接口。它类似于一个由名称索引的节点列表,当通过元素的 attributes 属性访问时,会返回一个 NamedNodeMap 对象。这个集合是动态的(live),会实时反映元素属性的变化。
与 NodeList 或 HTMLCollection 不同,NamedNodeMap 专门存放 Attr 节点,而不是普通的文本或元素节点。每个属性在 NamedNodeMap 中都以一个 Attr 对象存在,可以通过名称或索引进行访问。
二、NamedNodeMap 的属性和方法
2.1 length 属性
返回集合中属性节点的数量。这是一个只读属性。
const div = document.querySelector('div');
console.log(div.attributes.length); // 输出 div 元素的属性个数
2.2 item(index) 方法
通过索引获取集合中指定位置的属性节点。返回值是一个 Attr 对象,如果索引无效则返回 null。
const attr = div.attributes.item(0); // 获取第一个属性
if (attr) {
console.log(attr.name, attr.value);
}
2.3 getNamedItem(name) 方法
通过属性名(字符串)获取对应的属性节点。返回 Attr 对象或 null。
const classAttr = div.attributes.getNamedItem('class');
if (classAttr) {
console.log(classAttr.value); // 输出 class 的值
}
2.4 setNamedItem(attr) 方法
向集合中添加或替换一个属性节点。参数必须是 Attr 对象,如果已存在同名属性,则用新的替换旧的,并返回被替换的节点;否则返回 null。注意该方法会直接修改元素的属性。
// 创建一个 Attr 节点
const newAttr = document.createAttribute('data-custom');
newAttr.value = 'example';
// 将其添加到 div 的 attributes 集合中
const oldAttr = div.attributes.setNamedItem(newAttr);
if (oldAttr) {
console.log('替换了旧的属性:', oldAttr.name);
} else {
console.log('新增了属性: data-custom');
}
2.5 removeNamedItem(name) 方法
通过属性名移除集合中的属性节点,并返回被移除的 Attr 对象。如果属性不存在,则抛出 NotFoundError 异常。
try {
const removedAttr = div.attributes.removeNamedItem('class');
console.log('已移除属性:', removedAttr.name, removedAttr.value);
} catch (e) {
console.error('属性不存在');
}
2.6 带命名空间的方法
对于 XML 文档或使用命名空间的属性(如 SVG 中的 xml:lang),可以使用以下三个方法:
- getNamedItemNS(namespaceURI, localName):根据命名空间 URI 和本地名获取属性节点。
- setNamedItemNS(attr):添加或替换带命名空间的属性节点。
- removeNamedItemNS(namespaceURI, localName):移除带命名空间的属性节点。
// 假设处理 SVG 元素
const svgElement = document.querySelector('svg');
if (svgElement) {
const xmlLang = svgElement.attributes.getNamedItemNS('http://www.w3.org/XML/1998/namespace', 'lang');
if (xmlLang) {
console.log('当前语言:', xmlLang.value);
}
}
三、实战示例:属性节点的管理
下面是一个结合 HTML 和 JavaScript 的综合示例,展示如何动态管理元素的属性集合。
HTML 结构
<div id="demo" class="container" title="示例区域">
这是一个演示元素
</div>
<button id="showBtn">显示所有属性</button>
<button id="renameBtn">修改 title 属性</button>
<button id="copyBtn">复制所有属性到新元素</button>
JavaScript 实现
const demoDiv = document.getElementById('demo');
const showBtn = document.getElementById('showBtn');
const renameBtn = document.getElementById('renameBtn');
const copyBtn = document.getElementById('copyBtn');
// 1. 遍历并显示所有属性
showBtn.addEventListener('click', () => {
const attrs = demoDiv.attributes;
let output = '当前元素属性:\n';
for (let i = 0; i < attrs.length; i++) {
const attr = attrs.item(i);
output += `${attr.name} = "${attr.value}"\n`;
}
alert(output);
});
// 2. 使用 setNamedItem 修改属性
renameBtn.addEventListener('click', () => {
const newTitleAttr = document.createAttribute('title');
newTitleAttr.value = '新的标题 - ' + new Date().toLocaleTimeString();
const old = demoDiv.attributes.setNamedItem(newTitleAttr);
alert(old ? `原 title 值: ${old.value}` : 'title 属性不存在,已新增');
});
// 3. 将所有属性复制到新元素
copyBtn.addEventListener('click', () => {
const newDiv = document.createElement('div');
const srcAttrs = demoDiv.attributes;
for (let i = 0; i < srcAttrs.length; i++) {
const clonedAttr = srcAttrs.item(i).cloneNode(true);
newDiv.attributes.setNamedItem(clonedAttr);
}
newDiv.textContent = '我是克隆体!';
document.body.appendChild(newDiv);
alert('已生成克隆元素,请查看页面底部');
});
四、NamedNodeMap 的动态特性与注意事项
动态性:NamedNodeMap 是“活”的,当底层元素的属性被添加、修改或删除时,集合会自动同步。例如,通过 element.setAttribute('id', 'new') 修改属性后,NamedNodeMap 的 length 和对应项会立即更新。因此,在循环中操作属性时需格外小心,避免因集合动态变化导致逻辑错误。
Attr 对象的特殊性:getNamedItem 返回的 Attr 节点虽然也是一个 DOM 节点,但它不是元素子节点树的一部分 (通过 childNodes 看不着它)。它的 parentNode 为 null,但依然可以调用 attr.value 或 attr.textContent 获取值。
与 getAttribute / setAttribute 的比较:虽然 NamedNodeMap 提供了直接操作 Attr 节点的方式,但在大多数日常场景中,使用 getAttribute / setAttribute 更为简洁。NamedNodeMap 的优势在于需要批量处理属性、精确操控属性节点(如命名空间)或复制属性集合时。
实例演示动态性:
const div = document.querySelector('div');
const map = div.attributes;
console.log(map.length); // 例如 2
div.setAttribute('data-test', '123');
console.log(map.length); // 变成 3,且 map.getNamedItem('data-test') 可用
div.removeAttribute('class');
console.log(map.length); // 又变回 2
五、总结
NamedNodeMap 为我们提供了一个操作元素属性节点的底层接口,虽然平时使用频率不高,但在需要精确控制属性节点、处理 XML 或 SVG 命名空间、以及进行属性批量迁移时,它的价值就体现出来了。记住它是一个动态的 Attr 节点集合,并合理运用其方法,可以写出更健壮的 DOM 操作代码。

733

被折叠的 条评论
为什么被折叠?



