一文带你了解HTML Attribute与DOM Property的差异

369 阅读5分钟

前言

在Web开发中,我们经常遇到"Attributes"和"Properties"这两个术语,它们虽然在日常对话中经常被互换使用,但实际上代表着两个完全不同的概念。正确理解它们之间的区别对于编写有效、可靠的代码至关重要。本文将帮助大家深入解析Attributes与Properties之间的根本差异。

定义与关联

定义

Attribute

Attribute 可扩展 HTML 或 XML 元素,改变其行为或提供元数据。

Property

JavaScript 属性是一个对象的成员,它将键与值联系起来。JavaScript 对象是一种数据结构,它存储了属性的集合。

官方定义看不懂?记住一点:DOM 中的属性是Attribute,JS对象中的属性是Property

<input type="text"> 为例:

  • input 标签里的 type 是 attribute,读取、修改 DOM 中的属性时使用 getAttributesetAttribute等方法;
  • input.type:这里的input 是HTMLInputElement实例后的一个JS对象,type 是JS对象的 propterty;

那这两者之间有什么关联?

我们在编码过程中,需要查询 html 标签的 attribute 通常的做法是先通过document.getElementById等方法获取标签元素,再通过input.type的形式读取属性值。其内部是attribute反射机制,通过这个机制将 html 的 attribute 与 js 对象的 property 进行关联,使得我们能够通过 js 对象操作 html 标签的 attribute。

以下是一个反射id的例子帮助理解反射过程:

class HTMLInputElement extends HTMLElement {
    get id() { return this.getAttribute('id') ?? ''; }
    set id(str) {
        this.setAttribute('id', String(str));
    }
}

Attribute 与 Property 的差异

序列化

var input = document.createElement('input');
input.setAttribute('age', 18);
input.age2 = '19';
input.name = 'age';

//<input age="18" name="age">

通过以上例子可知:

  • 当 property 能与关联到标签元素的 attribute 时,通过input.name='age'的方式设置属性,最终 name 属性会序列化成 html;
  • 当 property 能与无法关联到标签元素的 attribute 时,通过input.age2='19'的方式设置属性,age2 属性不会序列化成 html;
  • 使用setAttribute 设置属性则一定会序列化为 html;

大小写敏感性

attribute 大小写不敏感, property 敏感

使用getAttribute方法时会将参数自动转为小写。

<div id="test" HeLlO="world"></div>
var div = document.getElementById('test');
console.log(div.getAttributeNames()); // ['id', 'hello']

div.age = 1;
div.Age = 2;
console.log(div.age, div.Age); // 1 2

数据类型类型

attribute 的值主要是 string, property 的值可以是任意类型;

例如以下例子将bool attribute 设置布尔类型true,通过getAttribute获取到的实际是字符串'true'.

var div = document.createElement('div');
div.setAttribute('bool', true);
console.log(div.getAttribute('bool')); // 'true'

命名差异

attribute 与 property 命名存在差异,常见的 attribute 如crossorigin,class,aria-label,对应的 property 名分别为crossOrigin,className,ariaLabel

默认值

html 的部分属性实际是存在默认值的,例如 input 标签的 type 默认为 textproperty 反射内部对于一些属性做了默认值判断,使得通过 input.typegetAttribute 方式获取的结果存在差异。

var input = document.createElement('input');
console.log(input.type); // 'text'
console.log(input.getAttribute('type')); // 'foo'

值校验

通过 property 即 input.type 的方式赋值会对值进行校验,而通过 setAttribute的方式赋值则没有校验;

var input = document.createElement('input');
input.type ='foo';
console.log(input.type);
input.setAttribute('type', 'foo');
console.log(input.type, input.getAttribute('type'));

attribute、property 操作结果的差异

property 赋值

var input = document.createElement('input');
input.type = '';
input.value = '';
input.readOnly = true;
input.myname = 'tom';
input.name = 'jim';

console.log(input.type, input.getAttribute('type')); // 'text', ''
console.log(input.value, input.getAttribute('value')); // '', null
console.log(input.readOnly, input.getAttribute('readonly')); // true, ''
console.log(input.myname, input.getAttribute('myname')); // 'tom', null
console.log(input.name, input.getAttribute('name')); // 'jim', null

document.body.appendChild(input); // <input type="" readonly="" name="jim">

以上代码属性均使用 property 的赋值操作,分析代码的输出结果得出以下结论。

property 有2种情况:

  • 如果属性存在默认值,例如 type 属性,property 为默认值,attribute 为空字符串;

  • 如果属性无默认值, property 为空字符串,attributenull或空字符串;

attribute 也有两种情况:

  • type等特殊属性,property赋值时会直接修改attribute的值,attributeproperty一致;

  • 一般属性,反射无特殊逻辑,attributenull;

  • 如果使用 setAttribute 为标签元素自定义属性,property 获取的为 undefinedattribute 为设置的值;

attribute 赋值

var input = document.createElement('input');
input.setAttribute('type', '');
input.setAttribute('value', '');
input.setAttribute('readonly', true);
input.setAttribute('name', 'jim');
input.setAttribute('myname', 'tom');

console.log('type: ', input.type, input.getAttribute('type')); // 'text', ''
console.log('value: ', input.value, input.getAttribute('value')); // '', ''
console.log('readonly: ', input.readOnly, input.getAttribute('readonly')); // true, 'true'
console.log('name: ', input.name, input.getAttribute('name')); // 'jim', 'jim'
console.log('myname: ', input.myname, input.getAttribute('myname')); // undefined, 'tom'

document.body.appendChild(input); 
// <input type="" value="" readonly="true" name="jim" myname="tom">

以上代码属性均使用 attribute 的赋值操作,分析代码的输出结果得出以下结论。

attribute 值为设定值的字符串形式;

property 有2种情况:

  • 如果属性为标签自带属性,值与设定值一致;
  • 如果属性为自定义属性,值为 undefined

总结

在深入探讨了HTML attribute与DOM property之后,我们可以清晰地看到,尽管它们在某些情况下可以相互映射,但它们在本质上服务于不同的目的,并具有不同的特性。Attribute是定义在HTML标签中的,它们为元素提供了元数据或者改变了元素的行为。而Property则是JavaScript中的对象特性,可以是任何类型的值,并且受JavaScript类型转换规则的约束。

通过对Attribute和Property的比较,我们了解到了它们在序列化、大小写敏感性、数据类型、命名规则以及默认值和值校验方面的差异。这些差异不仅影响着我们如何访问和修改元素的特性,也决定了元素如何在网页上呈现和行为。

最终,理解这些概念的细微差别对于前端开发者来说至关重要。它不仅帮助我们避免潜在的bug和问题,还让我们能够更精确地控制网页元素的表现和交互。随着Web技术的不断进步,对这些基础知识的掌握将成为构建更复杂、更动态网页应用的坚实基石。