Установка правил псевдокласса CSS из JavaScript

125

Я ищу способ изменить правила CSS для селекторов псевдоклассов (таких как: link,: hover и т. Д.) Из JavaScript.

Итак, аналог кода CSS: a:hover { color: red }в JS.

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

user39882
источник

Ответы:

192

Вы не можете стилизовать псевдокласс только для определенного элемента, точно так же, как вы не можете создать псевдокласс во встроенном атрибуте style = "..." (поскольку здесь нет селектора).

Вы можете сделать это, изменив таблицу стилей, например, добавив правило:

#elid:hover { background: red; }

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

Теоретически вам нужен документ http://www.w3.org/TR/DOM-Level-2-Style/Overview.html, что означает, что вы можете (с учетом уже существующей встроенной или связанной таблицы стилей) использовать синтаксис, например:

document.styleSheets[0].insertRule('#elid:hover { background-color: red; }', 0);
document.styleSheets[0].cssRules[0].style.backgroundColor= 'red';

IE, конечно, требует своего синтаксиса:

document.styleSheets[0].addRule('#elid:hover', 'background-color: red', 0);
document.styleSheets[0].rules[0].style.backgroundColor= 'red';

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

bobince
источник
32
Почему это не было выбрано в качестве ответа?
SZH
2
Firefox: «Ошибка: операция небезопасна».
8128
@fluteflute операция считается небезопасной, если вы пытаетесь манипулировать файлом CSS из другого домена (я предполагаю, что это тип политики одного и того же происхождения). Позор! Простая функция проверки соответствует политике одинакового происхождения: function sameOrigin(url) { var loc = window.location, a = document.createElement('a'); a.href = url; return a.hostname === loc.hostname && a.port === loc.port && a.protocol === loc.protocol; }
WickyNilliams,
3
Манипулировать таблицей стилей только для применения стилей к определенному элементу не рекомендуется, так как это заставляет браузер перекомпоновывать весь документ при каждом изменении таблицы стилей. stubbornella.org/content/2009/03/27/…
Йоханнес Эвальд,
29

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

  • Установка стилей, которые должны быть вычислены или извлечены - например, установка предпочтительного размера шрифта пользователя из файла cookie.
  • Установка поведенческих (не эстетических) стилей, особенно для разработчиков UI-виджетов / плагинов. Вкладки, карусели и т. Д. Часто требуют некоторого базового CSS просто для работы - не должны требовать таблицы стилей для основной функции.
  • Лучше, чем встроенные стили, поскольку правила CSS применяются ко всем текущим и будущим элементам и не загромождают HTML при просмотре в Firebug / Developer Tools.
Дэвид Танг
источник
17

Функция, справляющаяся с кроссбраузерностью:

addCssRule = function(/* string */ selector, /* string */ rule) {
  if (document.styleSheets) {
    if (!document.styleSheets.length) {
      var head = document.getElementsByTagName('head')[0];
      head.appendChild(bc.createEl('style'));
    }

    var i = document.styleSheets.length-1;
    var ss = document.styleSheets[i];

    var l=0;
    if (ss.cssRules) {
      l = ss.cssRules.length;
    } else if (ss.rules) {
      // IE
      l = ss.rules.length;
    }

    if (ss.insertRule) {
      ss.insertRule(selector + ' {' + rule + '}', l);
    } else if (ss.addRule) {
      // IE
      ss.addRule(selector, rule, l);
    }
  }
};

источник
7

Просто поместите CSS в строку шаблона.

const cssTemplateString = `.foo:[psuedoSelector]{prop: value}`;

Затем создайте элемент стиля, поместите строку в тег стиля и прикрепите ее к документу.

const styleTag = document.createElement("style");
styleTag.innerHTML = cssTemplateString;
document.head.insertAdjacentElement('beforeend', styleTag);

Об остальном позаботится специфика. Затем вы можете динамически удалять и добавлять теги стиля. Это простая альтернатива библиотекам и возням с массивом таблиц стилей в DOM. Удачного кодирования!

путать сайты
источник
insertAdjacentElementтребует 2 аргумента в основных браузерах (Chrome, Firefox, Edge), первый из которых устанавливает позицию относительно ссылочного элемента ( см. здесь ). Это недавнее изменение? (К ответу добавлено слово beforeend).
collapsar
6

Моя уловка заключается в использовании селектора атрибутов. Атрибуты легче настроить с помощью javascript.

CSS

.class{ /*normal css... */}
.class[special]:after{ content: 'what you want'}

Javascript

  function setSpecial(id){ document.getElementById(id).setAttribute('special', '1'); }

HTML

<element id='x' onclick="setSpecial(this.id)"> ...  
Серхио Абреу
источник
4
Это решение использует jQuery - введение зависимости размера jQuery для чего-то столь же простого, как это, когда спрашивающий просил чистый Javascript, плохо.
Tom Ashworth
Хотя практически все веб-сайты в настоящее время ИСПОЛЬЗУЮТ jquery, я изменю его, чтобы использовать чистый javascript.
Серхио Абреу
1
И как именно этот метод может изменить атрибуты CSS псевдоэлемента .class [special]: after, например, цвет фона или что-то еще?
andreszs
5

Есть еще одна альтернатива. Вместо того, чтобы напрямую манипулировать псевдоклассами, создайте реальные классы, моделирующие одни и те же вещи, такие как класс «зависания» или «посещаемый» класс. Стилизуйте классы обычным "." синтаксис, а затем вы можете использовать JavaScript для добавления или удаления классов из элемента при возникновении соответствующего события.

Брэндон Рамирес
источник
2
Это не работает для псевдоклассов: before и: after.
jbyrd
И это не применимо к изменению фонового изображения со значением, полученным через AJAX.
andreszs
1
@jbyrd, :before& :after- это псевдоэлементы, а не классы.
Рой Линг
@royling - да, спасибо за исправление! Не могу отредактировать свой комментарий.
jbyrd
4

Вместо того, чтобы напрямую устанавливать правила псевдокласса с помощью javascript, вы можете установить правила по-разному в разных файлах CSS, а затем использовать Javascript для отключения одной таблицы стилей и включения другой. Метод описан в A List Apart (см. Подробнее).

Настройте файлы CSS как,

<link rel="stylesheet" href="always_on.css">
<link rel="stylesheet" title="usual" href="preferred.css"> <!-- on by default -->
<link rel="alternate stylesheet" title="strange" href="alternate.css"> <!-- off by default -->

А затем переключаться между ними с помощью javascript:

function setActiveStyleSheet(title) {
   var i, a, main;
   for(i=0; (a = document.getElementsByTagName("link")<i>); i++) {
     if(a.getAttribute("rel").indexOf("style") != -1
        && a.getAttribute("title")) {
       a.disabled = true;
       if(a.getAttribute("title") == title) a.disabled = false;
     }
   }
}
наряжать
источник
Что делать, если вам нужно динамически изменить класс на значение, полученное с помощью запроса AJAX? Вы не можете создать файл CSS сейчас ...
andreszs
2

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

Если вы не придумываете стили динамически (т. Е. Извлекаете их из базы данных или что-то в этом роде), вы сможете обойти это, добавив класс в тело страницы.

CSS будет выглядеть примерно так:

a:hover { background: red; }
.theme1 a:hover { background: blue; }

И javascript для изменения этого будет примерно так:

// Look up some good add/remove className code if you want to do this
// This is really simplified

document.body.className += " theme1";  
Натаниэль Рейнхарт
источник
Из любопытства element.classList.addне поддерживается? Я продолжаю видеть, как люди поступают element.className +=.
Joel Cornett
2
classList - более новая функция и не похоже, что она имела хорошую поддержку до недавнего времени (см. caniuse.com/classlist )
Натаниэль Рейнхарт
-1

В jquery вы можете легко установить псевдоклассы при наведении курсора.

$("p").hover(function(){
$(this).css("background-color", "yellow");
}, function(){
$(this).css("background-color", "pink");
});
Взант
источник
-1

вот решение, включающее две функции: addCSSclass добавляет в документ новый класс css, а toggleClass включает его

В примере показано добавление настраиваемой полосы прокрутки в div.

// If newState is provided add/remove theClass accordingly, otherwise toggle theClass
function toggleClass(elem, theClass, newState) {
  var matchRegExp = new RegExp('(?:^|\\s)' + theClass + '(?!\\S)', 'g');
  var add = (arguments.length > 2 ? newState : (elem.className.match(matchRegExp) === null));

  elem.className = elem.className.replace(matchRegExp, ''); // clear all
  if (add) elem.className += ' ' + theClass;
}

function addCSSclass(rules) {
  var style = document.createElement("style");
  style.appendChild(document.createTextNode("")); // WebKit hack :(
  document.head.appendChild(style);
  var sheet = style.sheet;

  rules.forEach((rule, index) => {
    try {
      if ("insertRule" in sheet) {
        sheet.insertRule(rule.selector + "{" + rule.rule + "}", index);
      } else if ("addRule" in sheet) {
        sheet.addRule(rule.selector, rule.rule, index);
      }
    } catch (e) {
      // firefox can break here          
    }
    
  })
}

let div = document.getElementById('mydiv');
addCSSclass([{
    selector: '.narrowScrollbar::-webkit-scrollbar',
    rule: 'width: 5px'
  },
  {
    selector: '.narrowScrollbar::-webkit-scrollbar-thumb',
    rule: 'background-color:#808080;border-radius:100px'
  }
]);
toggleClass(div, 'narrowScrollbar', true);
<div id="mydiv" style="height:300px;width:300px;border:solid;overflow-y:scroll">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus
  a diam volutpat, ullamcorper justo eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit
  nec sodales sodales. Etiam eget dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui. Lorem ipsum dolor sit amet, consectetur
  adipiscing elit. Sed a eros metus. Nunc dui felis, accumsan nec aliquam quis, fringilla quis tellus. Nulla cursus mauris nibh, at faucibus justo tincidunt eget. Sed sodales eget erat consectetur consectetur. Vivamus a diam volutpat, ullamcorper justo
  eu, dignissim ante. Aenean turpis tortor, fringilla quis efficitur eleifend, iaculis id quam. Quisque non turpis in lacus finibus auctor. Morbi ullamcorper felis ut nulla venenatis fringilla. Praesent imperdiet velit nec sodales sodales. Etiam eget
  dui sollicitudin, tempus tortor non, porta nibh. Quisque eu efficitur velit. Nulla facilisi. Sed varius a erat ac volutpat. Sed accumsan maximus feugiat. Mauris id malesuada dui.
</div>

kofifus
источник
-2

Если вы используете РЕАКТ, там есть что-то под названием радий . Здесь так полезно:

  • Добавьте обработчики в свойства, если указаны интерактивные стили, например onMouseEnter для: hover, при необходимости обертывая существующие обработчики

  • Если какой-либо из обработчиков запускается, например, при наведении указателя мыши, Radium вызывает setState для обновления поля, специфичного для Radium, в объекте состояния компонентов.

  • При повторном рендеринге разрешите любые применяемые интерактивные стили, например: наведение, путем поиска ключа или ссылки элемента в специфическом для Radium состоянии

Абденнур Туми
источник