Definindo regras de pseudo-classe CSS a partir do JavaScript

125

Estou procurando uma maneira de alterar as regras CSS para seletores de pseudo-classe (como: link,: hover, etc.) do JavaScript.

Então, um análogo do código CSS: a:hover { color: red }em JS.

Não consegui encontrar a resposta em nenhum outro lugar; se alguém souber que isso é algo que os navegadores não suportam, também seria um resultado útil.

user39882
fonte

Respostas:

192

Você não pode estilizar uma pseudo-classe apenas em um elemento específico, da mesma maneira que não pode ter uma pseudo-classe em um atributo inline style = "..." (como não há seletor).

Você pode fazer isso alterando a folha de estilo, por exemplo, adicionando a regra:

#elid:hover { background: red; }

supondo que cada elemento que você deseja afetar tenha um ID exclusivo para permitir a seleção.

Em teoria, o documento que você deseja é http://www.w3.org/TR/DOM-Level-2-Style/Overview.html, o que significa que você pode (com uma folha de estilo incorporada ou vinculada pré-existente) usando sintaxe como:

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

O IE, é claro, requer sua própria sintaxe:

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

É provável que navegadores antigos e secundários não ofereçam suporte a nenhuma sintaxe. A manipulação dinâmica de folhas de estilo raramente é feita porque é muito chato acertar, raramente necessário e historicamente problemático.

bobince
fonte
32
Por que isso não foi escolhido como resposta?
SZH 24/03
2
Firefox: "Erro: a operação é insegura."
8128
@fluteflute a operação é considerada insegura se você estiver tentando manipular um arquivo CSS de um domínio diferente (acho que é um tipo de política de mesma origem). Vergonha! A função simples de verificar está em conformidade com a política de mesma origem: 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 7/07
3
Manipular a folha de estilo apenas para aplicar estilos a um elemento específico não é recomendado, pois faz com que o navegador reflita todo o documento toda vez que a folha de estilo é alterada. stubbornella.org/content/2009/03/27/…
Johannes Ewald
29

Criei uma pequena biblioteca para isso, pois acho que existem casos de uso válidos para manipular folhas de estilo em JS. Razões para ser:

  • Definir estilos que devem ser calculados ou recuperados - por exemplo, definir o tamanho da fonte preferido do usuário a partir de um cookie.
  • Definir estilos comportamentais (não estéticos), especialmente para desenvolvedores de widgets / plug-ins da interface do usuário. Guias, carrosséis etc. geralmente exigem CSS básico para funcionar - não devem exigir uma folha de estilo para a função principal.
  • Melhor que os estilos embutidos, pois as regras de CSS se aplicam a todos os elementos atuais e futuros e não sobrecarregam o HTML ao serem visualizados nas Firebug / Developer Tools.
David Tang
fonte
17

Uma função para lidar com o material entre navegadores:

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);
    }
  }
};

fonte
7

Basta colocar o css em uma string de modelo.

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

Em seguida, crie um elemento de estilo e coloque a sequência na marca de estilo e anexe-a ao documento.

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

A especificidade cuidará do resto. Em seguida, você pode remover e adicionar tags de estilo dinamicamente. Essa é uma alternativa simples para bibliotecas e bagunça com a matriz de folhas de estilo no DOM. Feliz codificação!

sites emaranhados
fonte
insertAdjacentElementrequer 2 argumentos nos principais navegadores (Chrome, Firefox, Edge), o primeiro dos quais estabelece a posição em relação ao elemento de referência ( veja aqui ). Esta é uma mudança recente? (Adicionado 'beforeend' na resposta).
Collapsar
6

Meu truque é usar um seletor de atributos. Os atributos são mais fáceis de configurar pelo 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)"> ...  
Sergio Abreu
fonte
4
Essa solução usa o jQuery - a introdução de uma dependência do tamanho do jQuery para algo tão simples como esse, quando o solicitante solicita Javascript puro, é ruim.
Tom Ashworth
Embora praticamente todos os sites hoje em dia USE jquery, vou modificá-lo para usar javascript puro.
Sergio Abreu
1
E exatamente como esse método consegue alterar os atributos CSS da classe .class [special]: após o pseudo elemento, como a cor do plano de fundo ou qualquer outra coisa?
precisa saber é o seguinte
5

Existe outra alternativa. Em vez de manipular diretamente as pseudo-classes, crie classes reais que modelem as mesmas coisas, como uma classe "pairar" ou uma classe "visitada". Estilize as aulas com o "." Usual sintaxe e, em seguida, você pode usar JavaScript para adicionar ou remover classes de um elemento quando o evento apropriado for acionado.

Brandon Ramirez
fonte
2
Isso não funciona para: pseudo-classes antes e depois.
Jbyrd
E isso não é aplicável para alterar a imagem de plano de fundo com um valor recuperado via AJAX.
andreszs
1
@jbyrd, :before& :aftersão pseudo-elementos, não classes.
Roy Ling
@royling - sim, obrigado pela correção! Não consigo editar meu comentário.
jbyrd
4

Em vez de definir diretamente regras de pseudo-classe com javascript, você pode definir as regras de maneira diferente em arquivos CSS diferentes e usar o Javascript para desativar uma folha de estilo e ativar outra. Um método é descrito em A List Apart (qv. Para mais detalhes).

Configure os arquivos CSS como,

<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 -->

E então alterne entre eles usando 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;
     }
   }
}
TRiG
fonte
E se você precisar alterar dinamicamente a classe para um valor recuperado por uma solicitação AJAX? Você não pode criar um arquivo CSS agora ...
andreszs 21/12
2

Como já foi dito, isso não é algo compatível com os navegadores.

Se você não está criando os estilos dinamicamente (ou seja, retirando-os de um banco de dados ou algo assim), você pode contornar isso adicionando uma classe ao corpo da página.

O CSS seria algo como:

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

E o javascript para alterar isso seria algo como:

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

document.body.className += " theme1";  
Nathaniel Reinhart
fonte
Por curiosidade, element.classList.addnão é bem suportado? Eu continuo vendo pessoas fazendo element.className +=.
Joel Cornett
2
classList é uma característica mais nova e não se parece com ele teve um bom suporte até muito recentemente (ver caniuse.com/classlist )
Nathaniel Reinhart
-1

No jquery, você pode facilmente definir pseudo classes suspensas.

$("p").hover(function(){
$(this).css("background-color", "yellow");
}, function(){
$(this).css("background-color", "pink");
});
vasanth
fonte
-1

aqui está uma solução que inclui duas funções: addCSSclass adiciona uma nova classe css ao documento e toggleClass a ativa

O exemplo mostra como adicionar uma barra de rolagem personalizada a uma 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
fonte
-2

Se você usa o REACT, existe algo chamado rádio . É tão útil aqui:

  • Adicione manipuladores aos objetos, se estilos interativos forem especificados, por exemplo, onMouseEnter para: pairar, agrupando manipuladores existentes, se necessário

  • Se algum dos manipuladores for acionado, por exemplo, ao passar o mouse, o Radium chamará setState para atualizar um campo específico do Radium no objeto de estado dos componentes

  • Na renderização, resolva quaisquer estilos interativos que se apliquem, por exemplo: pairar, procurando a chave do elemento ou ref no estado específico do Radium

Abdennour TOUMI
fonte