Удалить атрибут: jQuery .removeAttr() и нативный JS
Содержание
О проверке кода. Примеры не запускались в браузере — в отличие от материалов по Node.js, где приводится фактический вывод. Код дан по документации jQuery и MDN. Говорим прямо, а не ставим бейдж «проверено» без проверки.
Как удалить
// jQuery
$('#img').removeAttr('title');
$('#img').removeAttr('title alt'); // несколько через пробел
// нативно
img.removeAttribute('title');
['title', 'alt'].forEach((a) => img.removeAttribute(a));
Удаление несуществующего атрибута — не ошибка: обе версии молча ничего не сделают. Проверять заранее через hasAttribute не нужно, как не нужно проверять существование файла перед чтением — тот же принцип.
Пустое значение — это не удаление
Разница, которую стоит понимать:
img.setAttribute('alt', ''); // атрибут ЕСТЬ и пустой
img.removeAttribute('alt'); // атрибута НЕТ
img.hasAttribute('alt'); // false после remove, true после setAttribute('')
img.getAttribute('alt'); // null после remove, "" после setAttribute('')
Для большинства атрибутов разница чисто техническая. Но не для alt, и это тот случай, где ошибка стоит дорого:
| Разметка | Что понимает скринридер |
|---|---|
<img alt="Схема потоков"> |
описание картинки |
<img alt=""> |
картинка декоративная, пропустить |
<img> |
alt забыли — прочитает имя файла |
alt="" — осознанное решение: «здесь нечего озвучивать». Отсутствие alt — недоработка, и скринридер начнёт читать crud-annotated-mongodb-insertOne.bakedsvg.svg. Поэтому у декоративных картинок атрибут ставят пустым, а не удаляют.
setAttribute('alt', '')
Атрибут ЕСТЬ и пустой
hasAttribute('alt')→ truegetAttribute('alt')→""- Скринридер: «картинка декоративная, пропустить»
- Осознанное решение
removeAttribute('alt')
Атрибута НЕТ
hasAttribute('alt')→ falsegetAttribute('alt')→null- Скринридер прочитает имя файла
- Читается как недоработка
Ловушка: disabled и checked
Та же история, что при чтении и установке атрибутов:
// поле заблокировали из кода
$('#submit').prop('disabled', true);
// ❌ не поможет — атрибута в разметке и не было
$('#submit').removeAttr('disabled');
// ✅ снимаем свойство
$('#submit').prop('disabled', false);
document.querySelector('#submit').disabled = false;
Правило: если состояние ставили свойством — свойством и снимайте. Смешивать .prop() и .removeAttr() — гарантированный способ получить кнопку, которая «не разблокируется».
Оговорка: если атрибут disabled был в исходной разметке, то removeAttr сработает. Именно эта половинчатость и сбивает: иногда работает, иногда нет — в зависимости от того, откуда взялось состояние.
data-атрибуты
// нативно
delete post.dataset.userId; // убирает data-user-id
post.removeAttribute('data-user-id'); // то же самое
// jQuery
$('#post').removeAttr('data-user-id'); // атрибут убран...
$('#post').data('userId'); // ...но тут ещё лежит старое значение из кэша!
Здесь важный нюанс: .data() в jQuery кэширует. Удалили атрибут из разметки — кэш об этом не узнает. Чтобы очистить и его, нужен .removeData('userId').
Отсюда правило, общее для чтения и записи data-атрибутов: работайте одним инструментом. Либо только dataset, либо только .data() + .removeData().
Удалить все атрибуты
Задача редкая, но встречается при санитизации чужого HTML:
[...el.attributes].forEach((attr) => el.removeAttribute(attr.name));
Копия массива через spread здесь обязательна: el.attributes — живая коллекция, и удаление во время перебора собьёт индексы. Классическая ошибка — цикл for по живому списку, который пропускает каждый второй элемент.
Булевы атрибуты: удалять, а не ставить false
Отдельная категория, где удаление — единственный правильный способ выключить.
Булев атрибут работает по факту присутствия, а не по значению. disabled, required, readonly, checked, hidden — все они включены, если атрибут есть, независимо от того, что в нём написано:
<input disabled> <!-- выключен -->
<input disabled="disabled"> <!-- выключен -->
<input disabled="false"> <!-- ВСЁ РАВНО выключен! -->
<input disabled=""> <!-- выключен -->
Строка "false" — это непустое значение, а значит атрибут присутствует, а значит поле заблокировано. Логика «поставлю false, чтобы включить» здесь не работает вовсе.
Выключается такой атрибут только удалением:
// ❌ не включит поле — атрибут по-прежнему на месте
el.setAttribute('disabled', 'false');
// ✅ убрать атрибут целиком
el.removeAttribute('disabled');
// или через свойство:
el.disabled = false;
Отсюда практическое следствие для генерации HTML: булев атрибут либо выводят целиком, либо не выводят вовсе. disabled={условие} в шаблоне должно давать либо disabled, либо пустоту — именно так это и делают React и другие фреймворки, а не disabled="false".
ARIA-атрибуты — исключение из правила булевых
Ловушка внутри ловушки, на которой спотыкаются при работе с доступностью.
aria-* атрибуты выглядят как булевы, но таковыми не являются. Их значение — настоящая строка "true" или "false", и удаление меняет смысл:
el.setAttribute('aria-expanded', 'false'); // свёрнуто — валидно
el.setAttribute('aria-expanded', 'true'); // развёрнуто
el.removeAttribute('aria-expanded'); // ← НЕ «свёрнуто», а «свойства нет вообще»
Для скринридера aria-expanded="false" и отсутствие атрибута — разные состояния: первое сообщает «этот элемент можно развернуть, сейчас свёрнут», второе — «элемент не разворачивается». Удалив атрибут «чтобы выключить», вы не выключаете, а ломаете семантику.
Правило: обычные булевы атрибуты выключают удалением, aria-* — установкой "false" строкой. Это прямо противоположные подходы к похоже выглядящим атрибутам, и именно поэтому их путают.
Что было на этой странице в 2017 году
Первая версия вышла 10 августа 2017 года и описывала .removeAttr() — синтаксис, удаление нескольких атрибутов через пробел. Всё описано верно и работает по сей день.
Не хватало ровно того же, что и в соседних статьях цикла: разделения атрибут/свойство. Совет «уберите атрибут disabled, чтобы разблокировать кнопку» работает только в половине случаев — и понять, в какой именно, по той статье было невозможно.
Показательно, что три статьи про атрибуты 2017 года — чтение, запись и удаление — все обходили это различие стороной. Оно и есть настоящая тема цикла, а .attr() против .removeAttr() — просто синтаксис.
Смежные темы: получить значение атрибута; установить или изменить атрибут; узнать класс элемента; уроки JavaScript.
Частые вопросы
Почему `removeAttr('disabled')` не всегда разблокирует поле?
disabled — свойство, а не только атрибут. Если состояние меняли из кода через .prop(), удаление атрибута ни на что не повлияет. Ставьте .prop('disabled', false).Чем удаление атрибута отличается от установки пустого значения?
removeAttribute('alt') — атрибута нет, hasAttribute вернёт false. setAttribute('alt', '') — атрибут есть и пустой. Для alt это разные вещи: пустой означает «картинка декоративная», отсутствующий — «забыли подписать».Можно ли удалить несколько атрибутов сразу?
.removeAttr('title alt'). Нативно нужен цикл по массиву имён.Как удалить все data-атрибуты?
el.dataset и удалить каждый ключ через delete el.dataset.key, либо собрать имена из el.attributes и вызвать removeAttribute для каждого.