Изменение стилей и классов: jQuery .css() и нативный JS

Содержание

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

Как менять стили

// jQuery
$('#box').css('color', 'red');
$('#box').css({ color: 'red', fontSize: '16px' });

// нативно
box.style.color = 'red';
Object.assign(box.style, { color: 'red', fontSize: '16px' });
Задача jQuery Нативно
Задать стиль $el.css('color', 'red') el.style.color = 'red'
Несколько разом $el.css({...}) Object.assign(el.style, {...})
Прочитать вычисленный $el.css('color') getComputedStyle(el).color
Убрать инлайновый $el.css('color', '') el.style.removeProperty('color')
CSS-переменная $el.css('--x', '5px') el.style.setProperty('--x', '5px')

Свойства с дефисом в JavaScript пишутся camelCase: font-sizefontSize, background-colorbackgroundColor. Исключение — CSS-переменные: они всегда с дефисами и только через setProperty.

Ловушка первая: читаете не то, что писали

box.style.color = 'red';
$('#box').css('color');            // "rgb(255, 0, 0)"
getComputedStyle(box).color;       // "rgb(255, 0, 0)"

Браузер отдаёт вычисленное значение в канонической форме. red, #f00, rgb(255,0,0) — для него одно и то же, и наружу пойдёт последнее.

То же с размерами: записали width: 50% — прочитаете width: 320px. Браузер посчитал.

Отсюда правило: сравнивать стили строками нельзя. Проверка if ($el.css('color') === 'red') не сработает никогда. Состояние держат в классах и проверяют classList.contains.

Ловушка вторая: style видит не всё

<style> #box { color: blue; } </style>
<div id="box"></div>
box.style.color;                   // "" — пусто!
getComputedStyle(box).color;       // "rgb(0, 0, 255)" — вот оно

el.style — это только атрибут style в разметке. Правила из CSS-файла туда не попадают. Прочитать реальный стиль можно только через getComputedStyle.

jQuery это прячет: .css() на чтение сам вызывает getComputedStyle. Удобно — и именно поэтому многие не знают о различии, пока не переходят на нативный код.

Главное: не меняйте стили из JavaScript

Совет, которого не было в оригинале, а он важнее всего синтаксиса выше.

// ❌ оформление растеклось по коду
$('#box').css({ background: '#e34948', color: '#fff', padding: '12px' });

// ✅ состояние в JS, оформление в CSS
box.classList.add('box--error');
.box--error { background: #e34948; color: #fff; padding: 12px; }

Почему второй вариант лучше:

  • Инлайновый стиль перебивает таблицу стилей. Написали из JS — переопределить это можно теперь только через !important. Вы своими руками сломали каскад.
  • Оформление оказывается в двух местах. Дизайнер меняет цвет ошибки в CSS, а он не меняется, потому что настоящий цвет зашит в JS.
  • Тема и адаптив ломаются. Тёмная тема и медиазапросы живут в CSS; инлайновый стиль их не видит и остаётся прежним.

JavaScript должен отвечать на вопрос «в каком состоянии элемент», а не «какого он цвета».

Кто за что отвечает

Оформление из JavaScript

.css() / el.style на каждый чих

  • Инлайн перебивает таблицу стилей
  • Переопределить только через !important
  • Цвета в двух местах: в CSS и в коде
  • Тёмная тема и медиазапросы не работают
  • Дизайнер не может ничего поменять

Состояние из JavaScript

classList.toggle() + правила в CSS

  • JS говорит что происходит, CSS — как это выглядит
  • Каскад цел, !important не нужен
  • Тема и адаптив работают сами
  • Правишь оформление, не трогая код
  • Переходы и анимации бесплатно

Когда значение всё-таки динамическое

Прогресс-бар, позиция по курсору, высота от контента — значение известно только в рантайме. Прямая запись в style кажется единственным вариантом, но есть лучше:

// ❌ оформление в JS
bar.style.width = percent + '%';
bar.style.background = percent > 80 ? 'red' : 'green';

// ✅ JS отдаёт данные, CSS решает вид
bar.style.setProperty('--progress', percent + '%');
.bar::after {
  width: var(--progress);
  background: green;
  transition: width .2s;
}
.bar[data-danger]::after { background: red; }

CSS-переменная — это способ передать в CSS число, не описывая в JS оформление. Код сообщает «прогресс 70%», а решение про цвет, ширину и анимацию остаётся в стилях, где ему и место.

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

Первая версия вышла 18 августа 2017 года — самая большая статья цикла про jQuery. Она подробно разбирала .css(), .addClass(), .removeClass(), .toggleClass() и была для своего времени исчерпывающей.

Синтаксис не устарел ни на символ. Устарела рамка, в которой он подавался: .css() и классы описывались как два равноправных способа поменять внешний вид, а выбор — как дело удобства.

Сегодня понятно, что это не так. Инлайновый стиль ломает каскад и растаскивает оформление между CSS и JS — и это не вопрос вкуса, а причина, по которой в проекте потом появляется !important на каждом втором правиле.

Чего в 2017-м не было и быть не могло: CSS-переменных в этой роли. Они появились раньше, но привычка отдавать значение в CSS вместо записи стиля сложилась позже. А ведь именно она решает единственный законный случай для .css() — динамическое значение.


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

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

Почему `.css('color')` возвращает `rgb(255, 0, 0)`, а не `red`?
Потому что браузер отдаёт вычисленное значение, приведённое к канонической форме. Так же ведёт себя нативный getComputedStyle. Сравнивать стили строками поэтому ненадёжно: red, #f00 и rgb(255,0,0) — одно и то же значение в трёх записях.
Чем `el.style.color` отличается от `getComputedStyle(el).color`?
el.style читает только инлайновые стили — то, что стоит в атрибуте style. Правило из CSS-файла там не появится, вернётся пустая строка. getComputedStyle показывает итог всех правил.
Как менять стили правильно?
Через классы: classList.toggle('active'), а сами правила — в CSS. Прямая запись в style перебивает таблицу стилей и создаёт код, который нельзя переопределить без !important.
Что такое CSS-переменные и чем они удобнее?
Это способ отдать в CSS значение из JavaScript, не описывая там оформление: el.style.setProperty('--progress', '70%'). JS передаёт данные, CSS решает, как их показать.