Получить значение атрибута: 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, нативный getAttribute — null. А если атрибут есть, но пустой (<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?
.prop('checked').Как читать `data-*` атрибуты?
el.dataset: data-user-id становится dataset.userId. В jQuery есть .data(), но он приводит типы и кэширует значение, из-за чего может разойтись с разметкой.`getAttribute` возвращает null или пустую строку?
null, если атрибута нет вовсе. Пустую строку — если атрибут есть, но пустой: <div data-x>. Это разные ситуации, и их стоит различать.