Установить или изменить атрибут: jQuery и нативный JS
Содержание
О проверке кода. Примеры не запускались в браузере — в отличие от материалов по Node.js, где приводится фактический вывод. Код дан по документации jQuery и MDN. Говорим прямо, а не ставим бейдж «проверено» без проверки.
Установка атрибута — та же задача, что и чтение, с той же главной ловушкой и одной дополнительной, уже про безопасность.
Как установить
// jQuery
$('#img').attr('src', '/media/schema.jpg');
$('#link').attr('href', '/about');
// нативно
document.querySelector('#img').setAttribute('src', '/media/schema.jpg');
document.querySelector('#img').src = '/media/schema.jpg'; // через свойство-ярлык
У популярных атрибутов есть свойства-ярлыки (src, href, alt, id), и через них короче. Разница есть, и она неочевидна:
img.setAttribute('src', '/a.jpg');
img.getAttribute('src'); // "/a.jpg" — как записали
img.src; // "https://site.ru/a.jpg" — абсолютный URL!
Свойство src возвращает разрешённый абсолютный адрес, атрибут — исходную строку. Для сравнения путей это принципиально.
Несколько атрибутов разом
Единственное место, где jQuery объективно короче:
// jQuery: объектом
$('#img').attr({
src: '/media/schema.jpg',
alt: 'Схема работы',
width: 800,
});
// нативно: цикл
Object.entries({ src: '/media/schema.jpg', alt: 'Схема работы', width: 800 })
.forEach(([k, v]) => img.setAttribute(k, v));
Разница — три строки против одной. Если ради этого в проекте держат jQuery, размен сомнительный, но признаем: тут он удобнее.
Та же ловушка: формы меняются через свойство
// ❌ не сработает так, как ждёте
$('#agree').attr('checked', true);
$('#name').attr('value', 'Аня');
// ✅ правильно
$('#agree').prop('checked', true);
$('#name').prop('value', 'Аня');
document.querySelector('#agree').checked = true;
Причина та же, что при чтении: атрибут — это разметка, свойство — живое состояние. Установка атрибута checked меняет значение по умолчанию, а не текущее. После того как пользователь тронул поле, атрибут на него уже не влияет вовсе.
-
1
Это поле формы?
checked,value,disabled,selected→ только свойство:.prop()илиel.value -
2
Это класс или стиль?
classList.add()иel.style.x, а неsetAttribute— иначе затрёте всё разом -
3
Значение от пользователя?
href,src,action→ белый список протоколов.javascript:выполнит код -
4
Ничего из этого
setAttribute(name, value)— обычный путь
setAttribute затирает всё значение
Неочевидная деталь, на которой теряют классы:
// ❌ снесёт ВСЕ классы, оставит один
el.setAttribute('class', 'active');
$el.attr('class', 'active');
// ✅ добавит, не трогая остальные
el.classList.add('active');
$el.addClass('active');
Атрибут — одна строка целиком, и вы её заменяете. Для классов почти всегда нужен classList, для стилей — style, а не attr('style', ...), который так же снесёт все инлайновые правила.
Безопасность: не всякий атрибут безобиден
Раздела, которого не было в оригинале, а он тут важнее синтаксиса.
// ❌ дыра, если url пришёл от пользователя
$link.attr('href', userUrl);
Если userUrl равен javascript:alert(document.cookie), клик по ссылке выполнит код. То же с src у iframe и data:-URL.
// ✅ белый список протоколов
function safeUrl(raw) {
try {
const url = new URL(raw, location.origin);
return ['http:', 'https:', 'mailto:'].includes(url.protocol) ? url.href : '#';
} catch {
return '#';
}
}
link.setAttribute('href', safeUrl(userUrl));
Логика та же, что у параметризованных запросов в SQL и execFile вместо exec: не фильтруйте плохое, а разрешайте только известное хорошее. Чёрные списки обходят, белые — нет.
Опасные атрибуты, куда нельзя подставлять пользовательские данные без проверки: href, src, action, formaction, любые on* (onclick и подобные), style.
Атрибуты доступности: не забывать при динамике
Про этот класс атрибутов вспоминают в последнюю очередь, а устанавливать их нужно вместе с каждым изменением состояния. Меняете вид элемента из кода — обновляйте и то, что он сообщает скринридеру:
// раскрываем меню — и говорим об этом ассистивным технологиям
function toggleMenu(button, menu) {
const open = menu.hidden;
menu.hidden = !open;
button.setAttribute('aria-expanded', String(open)); // "true" / "false"
}
Ключевая деталь: aria-* принимает строку "true"/"false", а не булево. setAttribute('aria-expanded', true) запишет строку "true" (атрибуты всегда строки), но полагаться на автопревращение не стоит — приводите явно через String(...).
Типичный набор, который забывают обновлять при динамике:
aria-expanded— раскрыт ли аккордеон, меню, выпадашка;aria-hidden— скрыт ли элемент от скринридера (не то же, чтоhiddenилиdisplay: none);aria-pressed— нажата ли кнопка-переключатель;aria-busy— идёт ли загрузка.
Правило: меняете состояние — меняйте и его ARIA-отражение в той же функции. Разъехались визуальное состояние и aria-* — интерфейс работает для зрячих и врёт для тех, кто пользуется скринридером.
Класс и стиль — тоже «установка», но других инструментов
Короткое напоминание, потому что через setAttribute их и пытаются ставить:
| Задача | Не через setAttribute |
А через |
|---|---|---|
| добавить класс | attr('class', ...) затрёт всё |
classList.add |
| поменять стиль | attr('style', ...) затрёт всё |
el.style.x |
| поле формы | attr('checked', ...) не сработает |
.prop() / el.checked |
data-* |
работает, но лучше | el.dataset.x |
setAttribute заменяет всё значение атрибута целиком — для class и style, которые устроены как списки, это почти всегда не то, что нужно.
Что было на этой странице в 2017 году
Первая версия вышла 10 августа 2017 года, разбирала .attr() с одним и двумя аргументами и передачу объекта. Синтаксис описан верно и не изменился ни на символ.
Чего в ней не было — двух вещей, которые сегодня и составляют половину ответа:
- Разделения атрибут/свойство. Установка
attr('checked', true)подавалась как рабочий способ отметить чекбокс. Он работает ровно до момента, когда пользователь первый раз кликнул по нему сам. - Безопасности. Про подстановку данных пользователя в
hrefне думали вообще. Тогда фронтенд редко работал с чужими данными напрямую — сегодня это норма, и вопрос из теоретического стал ежедневным.
Смежные темы: получить значение атрибута; удалить атрибут; изменение стилей; уроки JavaScript.
Частые вопросы
Как установить сразу несколько атрибутов?
$el.attr({ src: '/a.jpg', alt: 'Схема' }). Нативно готового способа нет, но Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v)) решает задачу в одну строку.Почему изменение `value` через `.attr()` не работает?
.prop('value', ...) или el.value — иначе визуально ничего не изменится.Можно ли ставить атрибуты через функцию?
$el.attr('title', (i, old) => old + '!') — колбэк получает индекс и старое значение и вызывается для каждого элемента. Нативного аналога нет, нужен цикл.Опасно ли подставлять данные пользователя в атрибут?
href, src или обработчик события. Значение вида javascript:... в href выполнит код. Проверяйте протокол по белому списку, а не фильтруйте строку.