Establecer reglas de pseudo-clase CSS desde JavaScript

125

Estoy buscando una manera de cambiar las reglas de CSS para los selectores de pseudo-clase (como: link,: hover, etc.) desde JavaScript.

Entonces, un análogo del código CSS: a:hover { color: red }en JS.

No pude encontrar la respuesta en ningún otro lado; Si alguien sabe que esto es algo que los navegadores no admiten, también sería un resultado útil.

usuario39882
fuente

Respuestas:

192

No puede diseñar una pseudo-clase solo en un elemento en particular, de la misma manera que no puede tener una pseudo-clase en un atributo inline style = "..." (ya que no hay un selector).

Puede hacerlo alterando la hoja de estilo, por ejemplo agregando la regla:

#elid:hover { background: red; }

suponiendo que cada elemento que desea afectar tiene una ID única para permitir que se seleccione.

En teoría, el documento que desea es http://www.w3.org/TR/DOM-Level-2-Style/Overview.html, lo que significa que puede (dada una hoja de estilo incorporada o vinculada preexistente) utilizando una sintaxis como:

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

IE, por supuesto, requiere su propia sintaxis:

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

Es probable que los navegadores antiguos y menores no admitan ninguna de las sintaxis. Rara vez se realiza una dinámica de hoja de estilo porque es bastante molesto acertar, rara vez se necesita e históricamente problemático.

bobince
fuente
32
¿Por qué no se eligió esto como respuesta?
SZH
2
Firefox: "Error: la operación es insegura".
8128
@fluteflute, la operación se considera insegura si está intentando manipular un archivo CSS de un dominio diferente (supongo que es un tipo de política del mismo origen). ¡Vergüenza! La función simple para verificar se ajusta a la política del mismo origen: 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
No se recomienda manipular la hoja de estilo solo para aplicar estilos a un elemento específico, ya que hace que el navegador redistribuya todo el documento cada vez que se cambia la hoja de estilo. stubbornella.org/content/2009/03/27/…
Johannes Ewald
29

Creé una pequeña biblioteca para esto, ya que creo que hay casos de uso válidos para manipular hojas de estilo en JS. Las razones son:

  • Establecer estilos que deben calcularse o recuperarse, por ejemplo, configurar el tamaño de fuente preferido del usuario desde una cookie.
  • Establecer estilos de comportamiento (no estéticos), especialmente para desarrolladores de complementos / widgets de UI. Las pestañas, carruseles, etc., a menudo requieren un poco de CSS básico simplemente para funcionar, no deberían exigir una hoja de estilo para la función central.
  • Mejor que los estilos en línea, ya que las reglas CSS se aplican a todos los elementos actuales y futuros, y no saturan el HTML cuando se ven en Firebug / Developer Tools.
David Tang
fuente
17

Una función para hacer frente a las cosas 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);
    }
  }
};

fuente
7

Simplemente coloque el CSS en una cadena de plantilla.

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

Luego cree un elemento de estilo y coloque la cadena en la etiqueta de estilo y adjúntela al documento.

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

La especificidad se encargará del resto. Luego puede eliminar y agregar etiquetas de estilo dinámicamente. Esta es una alternativa simple a las bibliotecas y jugar con la matriz de hojas de estilo en el DOM. ¡Feliz codificación!

sitios de enredos
fuente
insertAdjacentElementrequiere 2 argumentos en los principales navegadores (Chrome, Firefox, Edge), el primero de los cuales establece la posición relativa al elemento de referencia ( ver aquí ). ¿Es este un cambio reciente? (Se agregó 'beforeend' a la respuesta).
collapsar
6

Mi truco es usar un selector de atributos. Los atributos son más fáciles de configurar mediante 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
fuente
44
Esta solución usa jQuery: la introducción de una dependencia del tamaño de jQuery para algo tan simple como esto, cuando el interlocutor solicitó Javascript puro, es malo.
Tom Ashworth
Aunque prácticamente todos los sitios web hoy en día usan jquery, lo modificaré para usar javascript puro.
Sergio Abreu
1
¿Y exactamente cómo este método cambia los atributos CSS de .class [especial]: después del pseudo elemento, como el color de fondo o cualquier otra cosa?
andreszs
5

Hay otra alternativa En lugar de manipular las pseudo-clases directamente, cree clases reales que modelen las mismas cosas, como una clase "flotante" o una clase "visitada". Diseñe las clases con el habitual "." sintaxis y luego puede usar JavaScript para agregar o eliminar clases de un elemento cuando se active el evento apropiado.

Brandon Ramirez
fuente
2
Eso no funciona para: before y: after pseudo classes.
jbyrd
Y eso no es aplicable para cambiar la imagen de fondo con un valor recuperado a través de AJAX.
andreszs
1
@jbyrd, :beforey :afterson pseudo elementos, no clases.
Roy Ling
@royling: sí, ¡gracias por la corrección! Parece que no puedo editar mi comentario.
jbyrd
4

En lugar de establecer directamente reglas de pseudo-clase con javascript, puede establecer las reglas de manera diferente en diferentes archivos CSS, y luego usar Javascript para desactivar una hoja de estilo y activar otra. Se describe un método en A List Apart (qv. Para más detalles).

Configure los archivos 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 -->

Y luego cambie entre ellos 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;
     }
   }
}
Trigonometría
fuente
¿Qué sucede si necesita cambiar dinámicamente la clase a un valor recuperado por una solicitud AJAX? No puede crear un archivo CSS ahora ...
andreszs
2

Como ya se dijo, esto no es algo que los navegadores admitan.

Si no está creando los estilos dinámicamente (es decir, sacándolos de una base de datos o algo así), debería poder solucionar esto agregando una clase al cuerpo de la página.

El CSS se vería algo así como:

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

Y el javascript para cambiar esto sería 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
fuente
Por curiosidad, ¿ element.classList.addno está bien respaldado? Sigo viendo gente haciendo element.className +=.
Joel Cornett
2
classList es una característica más nueva y no parece que haya tenido un buen soporte hasta hace poco (vea caniuse.com/classlist )
Nathaniel Reinhart
-1

En jquery puede configurar fácilmente las pseudo clases flotantes.

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

Aquí hay una solución que incluye dos funciones: addCSSclass agrega una nueva clase css al documento y toggleClass la activa

El ejemplo muestra cómo agregar una barra de desplazamiento personalizada a un 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
fuente
-2

Si usa REACT, hay algo llamado radio . Es muy útil aquí:

  • Agregue controladores a los accesorios si se especifican estilos interactivos, por ejemplo, onMouseEnter para: desplazar, envolver los controladores existentes si es necesario

  • Si alguno de los controladores se activa, por ejemplo, al pasar el mouse, Radium llama a setState para actualizar un campo específico de Radium en el objeto de estado de componentes

  • En el renderizado, resuelva los estilos interactivos que se apliquen, por ejemplo: pase el mouse, buscando la clave o la referencia del elemento en el estado específico de Radium

Abdennour TOUMI
fuente