Перейти к содержимому

Как добавить класс чтобы прежние классы остались js

  • автор:

Стили и классы

До того, как начнёте изучать способы работы со стилями и классами в JavaScript, есть одно важное правило. Надеемся, это достаточно очевидно, но мы всё равно должны об этом упомянуть.

Как правило, существует два способа задания стилей для элемента:

  1. Создать класс в CSS и использовать его:
  2. Писать стили непосредственно в атрибуте style : .

JavaScript может менять и классы, и свойство style .

Классы – всегда предпочтительный вариант по сравнению со style . Мы должны манипулировать свойством style только в том случае, если классы «не могут справиться».

Например, использование style является приемлемым, если мы вычисляем координаты элемента динамически и хотим установить их из JavaScript:

let top = /* сложные расчёты */; let left = /* сложные расчёты */; elem.style.left = left; // например, '123px', значение вычисляется во время работы скрипта elem.style.top = top; // например, '456px'

Мы также можем установить сразу несколько стилей для элемента, используя свойство cssText . Но с ним нужно быть осторожнее, т.к оно может как добавить стили к существующим, так и полностью перезаписать их на новые:

let top = /* сложные расчёты */; let left = /* сложные расчёты */; // полная перезапись стилей elem, используем = elem.style.cssText = ` top: $; left: $; `; // добавление новых стилей к существующим стилям elem, используем += elem.style.cssText += ` top: $; left: $; `; // если элементу уже заданы стили, которые мы хотим добавить (+=), // они будут перезаписаны на новые.

В других случаях, например, чтобы сделать текст красным, добавить значок фона – описываем это в CSS и добавляем класс (JavaScript может это сделать). Это более гибкое и лёгкое в поддержке решение.

className и classList

Изменение класса является одним из наиболее часто используемых действий в скриптах.

Когда-то давно в JavaScript существовало ограничение: зарезервированное слово типа «class» не могло быть свойством объекта. Это ограничение сейчас отсутствует, но в то время было невозможно иметь свойство elem.class .

Поэтому для классов было введено схожее свойство «className» : elem.className соответствует атрибуту «class» .

   

Если мы присваиваем что-то elem.className , то это заменяет всю строку с классами. Иногда это то, что нам нужно, но часто мы хотим добавить/удалить один класс.

Для этого есть другое свойство: elem.classList .

elem.classList – это специальный объект с методами для добавления/удаления одного класса.

   

Так что мы можем работать как со строкой полного класса, используя className , так и с отдельными классами, используя classList . Выбираем тот вариант, который нам удобнее.

  • elem.classList.add/remove(«class») – добавить/удалить класс.
  • elem.classList.toggle(«class») – добавить класс, если его нет, иначе удалить.
  • elem.classList.contains(«class») – проверка наличия класса, возвращает true/false .

Кроме того, classList является перебираемым, поэтому можно перечислить все классы при помощи for..of :

   

Свойство style

Свойство elem.style – это объект, который соответствует тому, что написано в атрибуте «style» .

Свойства из одного слова записываются так же, с маленькой буквы:

background => elem.style.background top => elem.style.top opacity => elem.style.opacity

Для свойств из нескольких слов используется camelCase:

background-color => elem.style.backgroundColor z-index => elem.style.zIndex border-left-width => elem.style.borderLeftWidth
document.body.style.backgroundColor = prompt('background color?', 'green');

Свойства с префиксом

Стили с браузерным префиксом, например, -moz-border-radius , -webkit-border-radius преобразуются по тому же принципу: дефис означает заглавную букву.

button.style.MozBorderRadius = '5px'; button.style.WebkitBorderRadius = '5px';

Сброс стилей

Иногда нам нужно добавить свойство стиля, а потом, позже, убрать его.

Например, чтобы скрыть элемент, мы можем задать elem.style.display = «none» .

Затем мы можем удалить свойство style.display , чтобы вернуться к первоначальному состоянию. Вместо delete elem.style.display мы должны присвоить ему пустую строку: elem.style.display = «» .

// если мы запустим этот код, "мигнёт" document.body.style.display = "none"; // скрыть setTimeout(() => document.body.style.display = "", 1000); // возврат к нормальному состоянию

Если мы установим в style.display пустую строку, то браузер применит CSS-классы и встроенные стили, как если бы такого свойства style.display вообще не было.

Следите за единицами измерения

Не забудьте добавить к значениям единицы измерения.

Например, мы должны устанавливать 10px , а не просто 10 в свойство elem.style.top . Иначе это не сработает:

   

Пожалуйста, обратите внимание, браузер «распаковывает» свойство style.margin в последних строках и выводит style.marginLeft и style.marginTop из него.

Вычисленные стили: getComputedStyle

Итак, изменить стиль очень просто. Но как его прочитать?

Например, мы хотим знать размер, отступы, цвет элемента. Как это сделать?

Свойство style оперирует только значением атрибута «style» , без учёта CSS-каскада.

Поэтому, используя elem.style , мы не можем прочитать ничего, что приходит из классов CSS.

Например, здесь style не может видеть отступы:

  body Красный текст  

…Но что, если нам нужно, скажем, увеличить отступ на 20px ? Для начала нужно его текущее значение получить.

Для этого есть метод: getComputedStyle .

getComputedStyle(element, [pseudo])

element Элемент, значения для которого нужно получить pseudo Указывается, если нужен стиль псевдоэлемента, например ::before . Пустая строка или отсутствие аргумента означают сам элемент.

Результат вызова – объект со стилями, похожий на elem.style , но с учётом всех CSS-классов.

  body   

Вычисленное (computed) и окончательное (resolved) значения

Есть две концепции в CSS:

  1. Вычисленное (computed) значение – это то, которое получено после применения всех CSS-правил и CSS-наследования. Например, height:1em или font-size:125% .
  2. Окончательное (resolved) значение – непосредственно применяемое к элементу. Значения 1em или 125% являются относительными. Браузер берёт вычисленное значение и делает все единицы измерения фиксированными и абсолютными, например, height:20px или font-size:16px . Для геометрических свойств разрешённые значения могут иметь плавающую точку, например, width:50.5px .

Давным-давно getComputedStyle был создан для получения вычисленных значений, но оказалось, что окончательные значения гораздо удобнее, и стандарт изменился.

Так что, в настоящее время getComputedStyle фактически возвращает окончательное значение свойства, для геометрии оно обычно в пикселях.

getComputedStyle требует полное свойство!

Для правильного получения значения нужно указать точное свойство. Например: paddingLeft , marginTop , borderTopWidth . При обращении к сокращённому: padding , margin , border – правильный результат не гарантируется.

Например, если есть свойства paddingLeft/paddingTop , то что мы получим вызывая getComputedStyle(elem).padding ? Ничего, или, может быть, «сгенерированное» значение из известных внутренних отступов? Стандарта для этого нет.

Стили, применяемые к посещённым :visited ссылкам, скрываются!

Посещённые ссылки могут быть окрашены с помощью псевдокласса :visited .

Но getComputedStyle не даёт доступ к этой информации, чтобы произвольная страница не могла определить, посещал ли пользователь ту или иную ссылку, проверив стили.

JavaScript не видит стили, применяемые с помощью :visited . Кроме того, в CSS есть ограничение, которое запрещает в целях безопасности применять к :visited CSS-стили, изменяющие геометрию элемента. Это гарантирует, что нет обходного пути для «злой» страницы проверить, была ли ссылка посещена и, следовательно, нарушить конфиденциальность.

Итого

Для управления классами существуют два DOM-свойства:

  • className – строковое значение, удобно для управления всем набором классов.
  • classList – объект с методами add/remove/toggle/contains , удобно для управления отдельными классами.

Чтобы изменить стили:

  • Свойство style является объектом со стилями в формате camelCase. Чтение и запись в него работают так же, как изменение соответствующих свойств в атрибуте «style» . Чтобы узнать, как добавить в него important и делать некоторые другие редкие вещи – смотрите документацию.
  • Свойство style.cssText соответствует всему атрибуту «style» , полной строке стилей.

Для чтения окончательных стилей (с учётом всех классов, после применения CSS и вычисления окончательных значений) используется:

  • Метод getComputedStyle(elem, [pseudo]) возвращает объект, похожий по формату на style . Только для чтения.

Задачи

Создать уведомление

важность: 5

Напишите функцию showNotification(options) , которая создаёт уведомление: с заданным содержимым. Уведомление должно автоматически исчезнуть через 1,5 секунды.

Пример объекта options :

// показывает элемент с текстом "Hello" рядом с правой верхней частью окна. showNotification(< top: 10, // 10px от верхней границы окна (по умолчанию 0px) right: 10, // 10px от правого края окна (по умолчанию 0px) html: "Hello!", // HTML-уведомление className: "welcome" // дополнительный класс для div (необязательно) >);

Используйте CSS-позиционирование для отображения элемента в заданных координатах. Исходный документ имеет необходимые стили.

Как добавить класс к элементу js?

Для добавления в элемент класса существует метод classList.add() . В качестве параметра он принимает имя класса, который нужно добавить. Ещё существует простой способ задать классы элементу через свойство className через обычное присваивание. Имена классов должны разделяться пробелами.

const div1 = document.createElement('div'); div1.className = 'foo'; console.log(div1.outerHTML); // => const div2 = document.createElement('div'); div2.className = 'foo bar'; console.log(div2.outerHTML); // => const div3 = document.createElement('div'); div3.classList.add('foo'); console.log(div3.outerHTML); // => const div4 = document.createElement('div'); div4.classList.add('foo', 'bar', 'baz'); console.log(div4.outerHTML); // => 

Неожиданный порядок инициализации наследованных классов в JavaScript

Сегодня у меня была небольшая задачка на рефакторинг JS кода, и я натолкнулся на неожиданную особенность языка, о которой на протяжении 7 лет своего опыта программирования на этом ненавистном многими языке не задумывался и не сталкивался.

К тому же, я ничего не смог найти ни в русскоязычном, ни в английском интернете, в связи с чем решился опубликовать эту не очень длинную, не самую интересную, но полезную заметку.

Чтобы не пользоваться традиционными и бессмысленными константами foo/bar , покажу непосредственно на примере, который был у нас в проекте, но всё же без кучи внутренней логики и с фейковыми значениями. Помните, что всё равно примеры получились довольно синтетические

Наступаем на грабли

Итак, у нас есть класс:

class BaseTooltip < template = 'baseTemplate' constructor(content) < this.render(content) >render(content) < console.log('render:', content, this.template) >> const tooltip = new BaseTooltip('content') // render: content baseTemplate

А потом нам понадобилось создать другой тип тултипов, в котором изменяется поле template

class SpecialTooltip extends BaseTooltip

И вот тут меня ждал сюрприз, потому что при создании объекта нового типа происходит следующее

const specialTooltip = new SpecialTooltip('otherContent') // render: otherContent baseTemplate // ^ СТРАННО

Метод render вызвался со значением BaseTooltip.prototype.template , а не с SpecialTooltip.prototype.template , как я ожидал.

Наступаем на грабли внимательнее, снимая на видео

Поскольку chrome DevTools не умеют дебажить присваивание полей класса, то чтобы разобраться в происходящем, приходится прибегать к ухищрениям. С помощью небольшого хелпера логируем момент присваивания переменной

function logAndReturn(value) < console.log(`set property=$`) return value > class BaseTooltip < template = logAndReturn('baseTemplate') constructor(content) < console.log(`call constructor with property=$`) this.render(content) > render(content) < console.log(content, this.template) >> const tooltip = new BaseTooltip('content') // set property=baseTemplate // called constructor BaseTooltip with property=baseTemplate // render: content baseTemplate

И когда мы применим этот подход к наследуемому классу, получим следующее странное:

class SpecialTooltip extends BaseTooltip < template = logAndReturn('otherTemplate') >const tooltip = new SpecialTooltip('content') // set property=baseTemplate // called constructor SpecialTooltip with property=baseTemplate // render: content baseTemplate // set property=otherTemplate

Я был уверен, что сначала инициализируются поля объекта, а потом вызывается остальная часть конструктора. Оказывается, что всё хитрее.

Наступаем на грабли, покрасив черенок

Усложним ситуацию, добавив в конструктор ещё один параметр, который присвоим нашему объекту

class BaseTooltip < template = logAndReturn('baseTemplate') constructor(content, options) < this.options = logAndReturn(options) // with property=$`) this.render(content) > render(content) < console.log(content, this.template, this.options) // > class SpecialTooltip extends BaseTooltip < template = logAndReturn('otherTemplate') >const tooltip = new SpecialTooltip('content', 'someOptions') // в результате вообще путаница: // set property=baseTemplate // set property=someOptions // called constructor SpecialTooltip with property=baseTemplate // render: content baseTemplate someOptions // set property=otherTemplate

И только такой способ дебага (хорошо что не алертами) немножко прояснил мне происходящее

Откуда взялась эта проблема:

Раньше этот код был написан на фреймворке Marionette и выглядел (условно) так

const BaseTooltip = Marionette.Object.extend(< template: 'baseTemplate', initialize(content) < this.render(content) >, render(content) < console.log(content, this.template) >, >) const SpecialTooltip = BaseTooltip.extend(< template: 'otherTemplate' >)

При использовании Marionette всё работало так, как я ожидал, то есть метод render вызывался с указанным в классе значением template , но при переписывании логики модуля на ES6 в лоб и вылезла описанная в статье проблема

Считаем шишки

При создании объекта наследованного класса порядок происходящего следующий:

  • Инициализация полей объекта из объявления наследуемого класса
  • Выполнение конструктора наследуемого класса (в том числе инициализация полей внутри конструктора)
  • Только после этого инициализация полей объекта из текущего класса
  • Выполнение конструктора текущего класса

Возвращаем грабли в сарай

Конкретно в моей ситуации проблему можно решить или через миксины или передавая template в конструктор, но когда логика приложения требует переопределять большое количество полей, это становится довольно грязным способом.

Было бы классно прочитать в комментариях ваши предложения о том, как элегантно решить возникшую проблему

Курсы javascript

Правильно ли я понял, что 2-й вариант работает не на всех браузерах, только на новых, и лучше воспользоваться первым?

Последний раз редактировалось newobject, 20.07.2014 в 22:21 .
20.07.2014, 22:38
Регистрация: 27.05.2010
Сообщений: 33,038
а типа в учебник заглянуть — это не по пацански
20.07.2014, 22:54
Регистрация: 30.04.2013
Сообщений: 92

newobject,
я новенькая в этом деле но все же)))

я делаю это так
если есть основной блок

$(".osnovnoy-div(название class или id основного дива) div(или за место div любой другой элемент span или что то свое)[id*=id-esli-est'']").addClass('new-name-class').html();

как выглядит это

 

после как скрипт обработает

Строго не судите пожалуйста я только учусь))

20.07.2014, 23:35
Регистрация: 10.07.2014
Сообщений: 145

рони,
Спасибо огромное за ответ. Я просто думаю: а нахрен нужны эти извраты с эмуляцией и прочим, если первый вариант работает везде и без костылей? Или не везде?

21.07.2014, 00:36
Регистрация: 19.01.2010
Сообщений: 354

ClassList — удобный интерфейс для работы со списком классов. Например, у тебя никогда не случиться такого:

Добавление класса — пол-дела, при помощи ClassList можно удобно удалять класс, делать toggle и проверять наличие заданного класса. Попробуй написать однострочники для всего указанного и поймешь нахрена оно надо.

« — слайдер | document.getElementById(‘qwe’) перевести $(‘#qwe’) »

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как добавить методы объекту после конструирования. DragorWW Общие вопросы Javascript 23 21.01.2013 19:39
хотел как лучше, а получилось как всегда. Вопрос оптимизации 9xakep Общие вопросы Javascript 13 21.01.2013 16:27
помогите добавить class/id элементу magnateg Events/DOM/Window 39 19.11.2011 22:41
Как добавить свой блог в раздел feeds этого сайта IzumeRoot Ваши сайты и скрипты 13 30.10.2008 21:11
Добавить контент к последнему элементу Viper jQuery 0 11.10.2008 20:04

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *