Получить значение атрибута: jQuery .attr() и нативный JS

Содержание

О проверке кода. Примеры не запускались в браузере — в отличие от материалов по Node.js, где приводится фактический вывод. Код дан по документации jQuery и MDN. Говорим прямо, а не ставим бейдж «проверено» без проверки.

Прочитать атрибут — задача на одну строку. Но именно здесь живёт самое устойчивое заблуждение фронтенда, из-за которого чекбоксы «не работают».

Как прочитать

// jQuery
$('#link').attr('href');        // "/about"
$('#img').attr('alt');          // "Схема"

// нативно
document.querySelector('#link').getAttribute('href');
document.querySelector('#img').alt;      // у популярных атрибутов есть свойство-ярлык
Задача jQuery Нативно
Прочитать атрибут $el.attr('href') el.getAttribute('href')
Установить $el.attr('href', '/x') el.setAttribute('href', '/x')
Удалить $el.removeAttr('href') el.removeAttribute('href')
Проверить наличие $el.attr('x') !== undefined el.hasAttribute('x')

Отличие в мелочи: jQuery на отсутствующем атрибуте вернёт undefined, нативный getAttributenull. А если атрибут есть, но пустой (<div data-x>), вернётся пустая строка. Три разных состояния, и их стоит различать:

el.hasAttribute('data-x');   // true  — атрибут есть
el.getAttribute('data-x');   // ""    — но он пустой
el.getAttribute('data-нет'); // null  — атрибута нет вовсе

Главная ловушка: атрибут ≠ свойство

Вот из-за чего сюда приходят чаще всего.

Атрибут — это то, что написано в HTML. Он не меняется, когда пользователь что-то делает. Свойство — текущее состояние DOM-объекта. Оно живое.

<input type="checkbox" id="agree">

Пользователь поставил галочку. Что мы прочитаем?

$('#agree').attr('checked');    // undefined — в разметке атрибута НЕТ
$('#agree').prop('checked');    // true      — а состояние изменилось
document.querySelector('#agree').checked;   // true

Атрибут показывает исходное значение, свойство — актуальное. Галочку поставили мышью, и в HTML от этого ничего не изменилось.

То же с полем ввода: пользователь напечатал текст, а attr('value') вернёт то, что стояло в разметке изначально.

Атрибут или свойство: что читать

Атрибут — что написано в HTML

.attr() / getAttribute()

  • href, src, alt, class, data-*
  • Не меняется от действий пользователя
  • Для checked и value введёт в заблуждение
  • Показывает исходное состояние

Свойство — состояние DOM сейчас

.prop() / el.checked

  • checked, selected, disabled, value
  • Меняется вместе с действиями пользователя
  • Возвращает настоящий тип (boolean, а не строку)
  • Для форм читайте только его

Правило: всё, что меняется от действий пользователя, — читайте свойством. Всё, что задано в разметке и там же и остаётся, — атрибутом.

Кстати, до jQuery 1.6 метода .prop() не было вообще, и .attr('checked') возвращал то свойство, то атрибут в зависимости от версии. Разделение появилось именно потому, что путаница ломала код.

data-атрибуты: dataset вместо .data()

<article id="post" data-user-id="42" data-is-draft="true">
// нативно
post.dataset.userId;    // "42"   — строка, дефисы превратились в camelCase
post.dataset.isDraft;   // "true" — тоже строка!

// jQuery
$('#post').data('userId');   // 42   — число! jQuery приводит типы
$('#post').data('isDraft');  // true — boolean!

Здесь у jQuery есть особенность, о которой стоит знать: .data() приводит типы и кэширует значение. Строка "42" превращается в число, "true" — в булево. Иногда удобно, иногда неожиданно.

Хуже другое: .data() кэширует. Первое чтение запоминается, и если потом изменить data-* через setAttribute, jQuery отдаст старое значение из кэша. Нативный dataset всегда читает разметку.

Правило: пишете и читаете через один инструмент — либо только dataset, либо только .data(). Смешивать нельзя.

Что читать в 2026

Для атрибутов jQuery не нужен: getAttribute и dataset покрывают всё. Разница в записи — пара символов, а поведение нативных методов предсказуемее (нет кэша, нет приведения типов).

Если jQuery уже в проекте — используйте, разница несущественная. Но помните про .prop() для форм: это не вопрос вкуса, а вопрос корректности.

Чтение ARIA и доступности

Отдельный класс атрибутов, который читают чаще, чем думают, — aria-* и role. Они не про внешний вид, а про то, что элемент сообщает скринридеру, и работают как обычные атрибуты:

button.getAttribute('aria-expanded');   // "true" или "false" — строка!
button.getAttribute('role');            // "button", "tab", "dialog"...
button.getAttribute('aria-label');      // текст для озвучивания

Одна ловушка: aria-* возвращает строку, а не булево. aria-expanded="false" при чтении даст "false" — непустую строку, которая в условии истинна:

// ❌ ветка выполнится всегда: "false" — истинная строка
if (button.getAttribute('aria-expanded')) { /* ... */ }

// ✅ сравниваем со строкой явно
if (button.getAttribute('aria-expanded') === 'true') { /* ... */ }

Это та же природа, что у чтения data-* через getAttribute: всё, что приходит из разметки без приведения типов, — строка. Число, булево, null — только после явного преобразования.

Атрибут против свойства: полная таблица

Свести всё воедино, потому что именно эта пара — тема всего цикла:

Читаете Атрибутом Свойством
href, src исходная строка абсолютный URL
value (после ввода) значение из разметки что напечатал пользователь
checked (после клика) из разметки (обычно нет) текущее состояние
class строка целиком — (используйте classList)
data-x строка приведённый тип (.data())
aria-expanded строка "true"/"false" нет свойства-ярлыка
id строка строка (совпадают)

Простая мнемоника на весь цикл: атрибут — то, что в HTML; свойство — что в DOM сейчас. Совпадают они только у «статичных» атрибутов вроде id и href, которые пользователь не меняет.

Что было на этой странице в 2017 году

Первая версия вышла 10 августа 2017 года и разбирала только .attr() — как получить href, src, class. Разделения атрибут/свойство в ней не было, .prop() не упоминался.

Это типично для туториалов того времени: .attr() подавался как универсальный способ «прочитать что угодно у элемента». Работало — пока дело не доходило до формы, и тогда начиналось необъяснимое: чекбокс отмечен, а код считает иначе.

Сегодня мы вынесли это различие в отдельный раздел, потому что оно и есть настоящий ответ на вопрос. Синтаксис .attr() за девять лет не изменился ни на символ — изменилось понимание того, где он не работает.


Смежные темы: установить или изменить атрибут; удалить атрибут; узнать класс элемента; уроки JavaScript.

Частые вопросы

Чем `.attr()` отличается от `.prop()`?
.attr() читает атрибут из разметки — то, что написано в HTML. .prop() читает свойство DOM-объекта — текущее состояние. Для checked, selected, disabled и value нужен .prop(): атрибут покажет исходное значение, а не то, что сейчас.
Почему `.attr('checked')` возвращает undefined?
Потому что в разметке атрибута нет — пользователь поставил галочку мышью, и изменилось только свойство DOM. Читать надо .prop('checked').
Как читать `data-*` атрибуты?
Нативно — через el.dataset: data-user-id становится dataset.userId. В jQuery есть .data(), но он приводит типы и кэширует значение, из-за чего может разойтись с разметкой.
`getAttribute` возвращает null или пустую строку?
null, если атрибута нет вовсе. Пустую строку — если атрибут есть, но пустой: <div data-x>. Это разные ситуации, и их стоит различать.