人人都会AI编程

13.3 属性与样式操作:原生属性、自定义属性、行内样式、类名操作

更新时间:2026-07-11

如果说 DOM 节点是页面的骨架,那属性与样式就是赋予它们外貌与行为的血肉。无论是修改按钮的禁用状态、读取输入框的当前值,还是动态改变一个元素的颜色、大小、显示隐藏,都离不开这一节的内容。

13.3.1 原生属性(标准属性)

每个 DOM 元素都对应一个特定的 HTML 标签,而标签上定义的属性(如 idsrchreftypevalue)会在 DOM 对象上以属性(property)的形式存在。JavaScript 提供了多种方式操作它们,最常用也最直接的就是“点操作”和 setAttribute/getAttribute 方法。

直接属性访问(推荐)

对于标准属性,直接通过 . 操作符读写是最简单、性能也最好的方式:

const link = document.querySelector('a');
console.log(link.href);         // 获取完整解析后的 URL
link.href = '/new-page';        // 设置新地址

const input = document.querySelector('input');
input.value = '新内容';          // 修改输入框内容
input.disabled = true;           // 禁用输入框

注意事项:

  • 标准属性在 DOM 对象上都有对应的 property,名称大多与 HTML 属性相同,但有几个特殊情况,例如 class 在 JavaScript 中对应 className(因为 class 是保留字),for 对应 htmlFor(label 的 for 属性)。
  • 对于布尔类型的属性(disabledcheckedselected),直接赋 truefalse 即可,无需移除整个属性。
  • 属性值类型不一定是字符串:style 返回一个 CSSStyleDeclaration 对象,onclick 返回一个函数或 null。

getAttribute / setAttribute / removeAttribute

这套方法直接操作的是 HTML 标签上的特性(attribute),得到的始终是字符串(或 null)。它与 DOM property 在大部分情况下同步,但并非完全等价。

const el = document.querySelector('.box');
el.setAttribute('data-id', '123');
console.log(el.getAttribute('data-id')); // '123'
el.removeAttribute('data-id');

property 与 attribute 的关键差异:

  • 同步性:对于标准的、非布尔属性,二者通常保持双向同步。比如你改了 input.value 的 property,getAttribute('value') 返回的仍然是标签上的初始值,而不是当前值。value property 代表实时值,而 attribute 保持默认值。
  • 类型不同:property 可以是任意类型,attribute 始终是字符串。
  • 大小写:HTML 属性不区分大小写,在 JavaScript 中通常用小写,自定义属性建议全小写并用连字符分隔(data-xxx)。

何时用 attribute 方法?

  • 需要获取或设置自定义 data-* 属性时(虽然也可以用 dataset)。
  • 需要移除某个属性,而不只是将其值置空或置为 false 时。
  • 操作一些不常用、没有直接映射 property 的属性时(如 aria-*colspan)。

13.3.2 自定义属性(data-*)

HTML5 引入了 data-* 属性,允许开发者在标签上存储一些不用于视觉呈现的、与元素相关的自定义数据。比如商品 ID、用户编号、配置项等。

<div id="product" data-product-id="1024" data-price="99.9" data-in-stock="true">
  商品名称
</div>

通过 dataset 进行操作(推荐):

const product = document.getElementById('product');
// 读取
console.log(product.dataset.productId);  // "1024"
console.log(product.dataset.price);      // "99.9"
console.log(product.dataset.inStock);    // "true"

// 设置
product.dataset.discount = '0.8';
// 对应生成 data-discount="0.8" 属性

// 删除
delete product.dataset.inStock;

dataset 的优势:

  • 自动进行驼峰转换:data-product-id 变成 dataset.productId
  • 直接返回属性值,无需手动调用 getAttribute
  • 设置后立即可在 DevTools 中看到 HTML 标签上的变化。

注意事项:

  • dataset 的值永远是字符串(或 undefined),即使你存的是 true,读回来也是 "true",需要自行转换类型。
  • 如果属性名中包含多个连字符,如 data-order-status-code,会转换为驼峰:dataset.orderStatusCode
  • 不建议在 data- 中存储大量数据或敏感信息,因为它在 HTML 中是完全公开可见的。大规模数据应使用 JavaScript 对象管理,或借助 data- 存放标识符再映射。

13.3.3 行内样式(Inline Styles)

每个 DOM 元素都有一个 style 属性,它是一个 CSSStyleDeclaration 对象,用于读写元素的行内样式(即写在标签 style 属性中的 CSS)。

const box = document.querySelector('.box');
box.style.backgroundColor = 'red';
box.style.fontSize = '20px';
box.style.display = 'none';

命名规则:

  • CSS 属性名中带连字符的,转换为驼峰写法:background-colorbackgroundColormargin-topmarginTop
  • 可以使用 cssText 一次性设置多个样式:
  box.style.cssText = 'color: white; background: black; padding: 10px;';
  

cssText完全覆盖元素已有的行内样式,合并时需谨慎,可以在末尾添加 ; 后累加,或使用工具函数合并。

获取计算后的样式:
行内样式只反映直接写在 style 属性上的值,无法获得从 CSS 规则层叠而来的最终样式。要获取元素当前实际应用的样式(包括继承自样式表的),使用 getComputedStyle()

const computed = getComputedStyle(box);
console.log(computed.backgroundColor); // 返回最终的 rgb/rgba 字符串

这个方法返回的对象是只读的。

性能提示:

  • 频繁的逐个修改 style.xxx 会导致浏览器多次重排/重绘。如果需要批量修改样式,建议使用 cssText 或切换类名来减少重排次数。
  • 避免在循环中读写样式属性而引起“强制同步布局”(例如先读取 offsetWidth,再修改样式),这会在第 18 章渲染原理中详细解释。

13.3.4 类名操作(classList)

样式管理最推荐的方式不是直接操作行内样式,而是通过 类名 来控制。JavaScript 提供了 classList 对象,它提供了一套优雅的 API 来增删改查元素的类名,无需手动拼接字符串。

const el = document.querySelector('.item');
el.classList.add('active', 'highlight');     // 添加一个或多个类
el.classList.remove('hidden');                // 移除类
el.classList.toggle('dark-mode');             // 切换类(有则移除,无则添加)
el.classList.toggle('dark-mode', isDark);     // 第二个参数控制强制添加/移除
el.classList.contains('active');              // 检查是否包含某类,返回布尔值
el.classList.replace('old', 'new');           // 替换类名

与传统 className 的对比:

  • className 是一个字符串,操作类时需手动拆分、添加、去重、拼接,容易出错、冗余。
  • classList 自动处理空格分隔、重复检查等问题,代码更健壮,可读性更高。

应用场景示例:

// 点击切换菜单的展开/收起
button.addEventListener('click', () => {
  menu.classList.toggle('open');
});

// 根据数据动态设置状态样式
if (item.isActive) {
  itemEl.classList.add('active');
} else {
  itemEl.classList.remove('active');
}

性能与最佳实践:

  • 尽量在 CSS 中定义好不同状态的样式,然后用 JavaScript 切换类名来改变外观,而不是直接写行内样式。这样样式与逻辑解耦,更易维护,也更利于浏览器优化渲染(类名变更通常只引起重绘或最小幅度重排)。
  • 若需要在多个元素间切换同一个类名,可以使用循环或结合事件委托。

13.3.5 综合运用与常见陷阱

结合前面几种操作,我们来做一个小结示例,展示如何优雅地、同时使用属性和样式来构建一个常见的交互效果——表单实时校验。

<input type="text" id="username" data-min-length="3" placeholder="请输入用户名">
<span id="error" class="error-msg hidden">长度至少3个字符</span>

<script>
  const input = document.getElementById('username');
  const error = document.getElementById('error');
  
  input.addEventListener('input', function() {
    const minLength = parseInt(this.dataset.minLength, 10);
    const isValid = this.value.length >= minLength;
    
    // 操作类名切换错误提示的显示
    error.classList.toggle('hidden', isValid);
    
    // 操作原生属性修改输入框状态
    input.style.borderColor = isValid ? '#ccc' : 'red';
    
    // 甚至可以操作自定义属性来记录校验结果
    input.dataset.valid = isValid;
  });
</script>

常见易错点:

  • 混淆 property 和 attribute:例如,想获取复选框是否选中,应该用 checkbox.checked 而不是 getAttribute('checked'),因为后者返回的是 HTML 初始状态字符串("checked" 或 null),不会随着用户操作改变。
  • classList.toggle 在老旧浏览器中不支持第二个参数,在需要强制指定时可通过 add/remove 替代。
  • style.float 是保留字冲突,应使用 cssFloat
  • 忘记数字加单位:设置 style.width 时,不能只给数字,必须带上 'px' 等单位,除非属性本身是无单位的(如 opacityzIndex)。
  • 读取计算样式后的字符串解析getComputedStyle 返回的颜色通常是 rgb()rgba(),尺寸是 '100px',需要解析数字时使用 parseIntparseFloat 并注意单位。

掌握属性与样式的操作是成为高效前端开发者的基本功。良好的习惯是:dataset 存数据,用 classList 管外观,用 property 改状态。这不仅让代码清晰易读,也能帮你在日后的性能优化和状态管理中避开许多坑。