DOM: свойства VS атрибуты

У DOM-элементов в javascript есть свойства и атрибуты. И те и другие имеют имя и значение.
Поэтому иногда разработчики путают одно с другим.

Между ними есть соответствие, но оно не однозначное и его лучше понимать.

Свойства DOM-элементов

Узлы DOM являются объектами с точки зрения javascript. А у объектов есть свойства. Поэтому любому узлу можно назначить свойство, используя обычный синтаксис.

var elem = document.getElementById('MyElement')
elem.mySuperProperty = 5

Значением свойства может быть любой объект. Это же javascript.

elem.master = {
  name: vasya
}
alert(elem.master.name)

DOM-Атрибуты

Теперь посмотрим на DOM-элемент с другой стороны. Являясь элементом HTML,
DOM-элемент может иметь любое количество атрибутов.

В следующем примере элемент имеет атрибуты id, class и нестандартный (валидатор будет ругаться) атрибут alpha.

<div id="MyElement" class="big" alpha="omega"></div>

Атрибуты можно добавлять, удалять и изменять. Для этого есть специальные методы:

setAttribute(name, value)
Устанавливает значение атрибута
getAttribute(name)
Получить значение атрибута
hasAttribute(name)
Проверить, есть ли такой атрибут
removeAttribute(name)
Удалить атрибут

Имя атрибута является регистронезависимым.

// название маленькими буквами
document.body.setAttribute('test', 123)

// большими буквами
document.body.getAttribute('TEST') // 123

Значением атрибута может быть только строка. Это же HTML..

DOM-атрибуты VS DOM-свойства

Все, вроде бы, ясно. Есть свойства. Есть атрибуты.

Но создатели javascript решили (с лучшими намерениями) запутать ситуацию и создать искусственное соответствие между свойством и атрибутом.

Синхронизация

А именно, браузер синхронизирует значения ряда свойств с атрибутами. Если меняется атрибут, то меняется и свойство с этим именем. И наоборот.

Например:

document.body.id = 5
alert(document.body.getAttribute('id'))

А теперь - наоборот

document.body.setAttribute('id', 'NewId')
alert(document.body.id)

Такая синхронизация гарантируется для всех основных стандартных атрибутов.
При этом атрибуту с именем class соответствует свойство className, т.к. ключевое слово class зарезервировано в javascript.

Для "левых" атрибутов браузер ничего не гарантирует

document.body.setAttribute('cool', 'SomeValue')
alert(document.body.cool)  // undefined везде кроме IE (почему - см ниже)

Возможные значения

Название атрибута не зависит от регистра
Атрибуты с именами "abc" и "ABC" - один и тот же атрибут.
document.body.setAttribute('abc', 1)
document.body.setAttribute('ABC', 5)
alert(document.body.getAttribute('abc')) // => стало 5

Но свойства в разных регистрах - два разных свойства.

document.body.abc = 1
document.body.ABC = 5
alert(document.body.abc) // => все еще 1

Увы, в Internet Explorer версии до 8.0 с этим проблемы. Этот браузер старается по возможности уравнять свойства и атрибуты. Но как быть, если свойства - регистрозависимы, а атрибуты - нет?

Создатели IE поступили хитро: setAttribute ставит оба свойства (abc и ABC).. Ну а getAttribute возвращает первое попавшееся из них, с учетом регистронезависимости. Если таких свойств несколько, то невозможно сказать, какое именно он вернет.

document.body.setAttribute('abc', 1)
document.body.setAttribute('ABC', 5)

// IE пытается уравнять свойства и атрибуты
alert(document.body.abc) // => 1 
alert(document.body.ABC) // => 5
 
// но getAttribute выбирает первое попавшееся свойство
// за вычетом регистра букв
alert(document.body.getAttribute('abc')) // => 1
alert(document.body.getAttribute('ABC')) // => 1

Запустите этот пример в IE6/7 и, например, в Firefox, чтобы лучше понять различия.

Атрибут можно установить любой, а свойство - нет
Например, можно установить для тэга <body> атрибут tagName, но соответствующее свойство - только для чтения, поэтому оно не изменится:
document.body.setAttribute('tagName',1)
document.body.getAttribute('tagName') // 1 
document.body.tagName  // "BODY"

Вообще говоря, браузер не гарантирует синхронизацию атрибута и свойства.

Атрибуты и обработчики событий
В IE текстовое значение, присвоенное атрибуту onclick, не является функцией и не будет работать:
elem.setAttribute('onclick', 'alert(something)') // в IE не работает

Firefox корректно преобразовывает строку в функцию, поэтому там этот фрагмент работать будет. Но, вообще говоря, никакой браузер не обязан этого делать.

Атрибут - это строка (кроме IE)
Следующий код это наглядно демонстрирует:
document.body.setAttribute('v',{a:5})
alert(document.body.getAttribute('v'))  // "[object Object]" в Firefox
alert(document.body.getAttribute('v').a) // 5 в IE

В первой строке атрибуту присвоено значение-объект. Firefox тут же автоматически преобразовал его в строку. А IE, в нарушение стандартов, оставил атрибут объектом.

Исключение className
Как уже говорилось в разделе о синхронизации, атрибуту class соответствует свойство className. Так получилось из-за того, что class является зарезервированным словом javascript.

В IE также является исключением атрибут for, для него используется свойство forHtml.

Cвойство элемента и атрибут - это разные вещи. Не используйте одно вместо другого.

Вообще, обычно свойств хватает с головой.