Définition de règles de pseudo-classe CSS à partir de JavaScript

125

Je cherche un moyen de changer les règles CSS pour les sélecteurs de pseudo-classes (tels que: link,: hover, etc.) à partir de JavaScript.

Donc un analogue du code CSS: a:hover { color: red }en JS.

Je n'ai trouvé la réponse nulle part ailleurs; si quelqu'un sait que c'est quelque chose que les navigateurs ne prennent pas en charge, ce serait également un résultat utile.

user39882
la source

Réponses:

192

Vous ne pouvez pas styliser une pseudo-classe uniquement sur un élément particulier, de la même manière que vous ne pouvez pas avoir une pseudo-classe dans un attribut inline style = "..." (car il n'y a pas de sélecteur).

Vous pouvez le faire en modifiant la feuille de style, par exemple en ajoutant la règle:

#elid:hover { background: red; }

en supposant que chaque élément que vous souhaitez affecter a un ID unique pour lui permettre d'être sélectionné.

En théorie, le document que vous voulez est http://www.w3.org/TR/DOM-Level-2-Style/Overview.html, ce qui signifie que vous pouvez (étant donné une feuille de style intégrée ou liée préexistante) en utilisant une syntaxe telle que:

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

IE, bien sûr, nécessite sa propre syntaxe:

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

Les navigateurs plus anciens et mineurs ne prendront probablement pas en charge l'une ou l'autre syntaxe. Le violon dynamique des feuilles de style est rarement fait car il est assez ennuyeux de bien faire, rarement nécessaire et historiquement gênant.

bobince
la source
32
Pourquoi cela n'a-t-il pas été choisi comme réponse?
SZH
2
Firefox: "Erreur: l'opération n'est pas sécurisée."
8128
@fluteflute l'opération est considérée comme non sécurisée si vous essayez de manipuler un fichier CSS d'un domaine différent (je suppose que c'est un type de politique de même origine). La honte! Fonction simple à vérifier conforme à la politique de même origine: 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
Il n'est pas recommandé de manipuler la feuille de style uniquement pour appliquer des styles à un élément spécifique car cela oblige le navigateur à redistribuer tout le document à chaque fois que la feuille de style est modifiée. stubbornella.org/content/2009/03/27/…
Johannes Ewald
29

J'ai rassemblé une petite bibliothèque pour cela car je pense qu'il existe des cas d'utilisation valides pour manipuler des feuilles de style dans JS. Les raisons étant:

  • Définition des styles qui doivent être calculés ou récupérés - par exemple, définir la taille de police préférée de l'utilisateur à partir d'un cookie.
  • Définition de styles comportementaux (non esthétiques), en particulier pour les développeurs de widgets / plugins d'interface utilisateur. Les onglets, les carrousels, etc., nécessitent souvent du CSS de base simplement pour fonctionner - ne devraient pas exiger une feuille de style pour la fonction principale.
  • Mieux que les styles en ligne, car les règles CSS s'appliquent à tous les éléments actuels et futurs et n'encombrent pas le HTML lors de la visualisation dans Firebug / Developer Tools.
David Tang
la source
17

Une fonction pour faire face aux trucs multi-navigateurs:

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

la source
7

Placez simplement le css dans une chaîne de modèle.

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

Créez ensuite un élément de style et placez la chaîne dans la balise de style et attachez-la au document.

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

La spécificité s'occupera du reste. Ensuite, vous pouvez supprimer et ajouter des balises de style de manière dynamique. C'est une alternative simple aux bibliothèques et à jouer avec le tableau de feuilles de style dans le DOM. Bon codage!

sites d'enchevêtrement
la source
insertAdjacentElementnécessite 2 arguments dans les principaux navigateurs (Chrome, Firefox, Edge), dont le premier établissant la position par rapport à l'élément de référence ( voir ici ). Est-ce un changement récent? (Ajouté 'beforeend' à la réponse).
collapsar le
6

Mon truc utilise un sélecteur d'attribut. Les attributs sont plus faciles à configurer par 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
la source
4
Cette solution utilise jQuery - introduire une dépendance de la taille de jQuery pour quelque chose d'aussi simple que cela, lorsque le questionneur a demandé du Javascript pur, est mauvais.
Tom Ashworth
Bien que pratiquement tous les sites Web utilisent aujourd'hui jquery, je vais le modifier pour utiliser du javascript pur.
Sergio Abreu
1
Et exactement comment cette méthode change-t-elle les attributs CSS du .class [special]: après le pseudo élément, comme, la couleur d'arrière-plan ou autre chose?
andreszs
5

Il existe une autre alternative. Au lieu de manipuler directement les pseudo-classes, créez des classes réelles qui modélisent les mêmes choses, comme une classe "survolée" ou une classe "visitée". Style les classes avec le "." Habituel. syntaxe et vous pouvez ensuite utiliser JavaScript pour ajouter ou supprimer des classes d'un élément lorsque l'événement approprié se déclenche.

Brandon Ramirez
la source
2
Cela ne fonctionne pas pour: avant et: après les pseudo classes.
jbyrd
Et ce n'est pas applicable pour changer l'image d'arrière-plan avec une valeur récupérée via AJAX.
andreszs
1
@jbyrd, :before& :aftersont des pseudo éléments, pas des classes.
Roy Ling
@royling - oui, merci pour la correction! Je n'arrive pas à modifier mon commentaire.
jbyrd
4

Au lieu de définir directement des règles de pseudo-classe avec javascript, vous pouvez définir les règles différemment dans différents fichiers CSS, puis utiliser Javascript pour désactiver une feuille de style et en activer une autre. Une méthode est décrite dans A List Apart (qv. Pour plus de détails).

Configurez les fichiers CSS comme,

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

Et puis basculez entre eux en utilisant 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;
     }
   }
}
Trigonométrie
la source
Que faire si vous avez besoin de changer de façon dynamique la classe en une valeur récupérée par une requête AJAX? Vous ne pouvez pas créer de fichier CSS maintenant ...
andreszs
2

Comme déjà indiqué, ce n'est pas quelque chose que les navigateurs prennent en charge.

Si vous ne trouvez pas les styles de manière dynamique (c'est-à-dire en les extrayant d'une base de données ou autre), vous devriez être en mesure de contourner ce problème en ajoutant une classe au corps de la page.

Le css ressemblerait à quelque chose comme:

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

Et le javascript pour changer cela serait quelque chose comme:

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

document.body.className += " theme1";  
Nathaniel Reinhart
la source
Par curiosité, n'est element.classList.addpas bien pris en charge? Je continue de voir des gens faire element.className +=.
Joel Cornett
2
classList est une fonctionnalité plus récente et ne semble pas avoir un bon support jusqu'à assez récemment (voir caniuse.com/classlist )
Nathaniel Reinhart
-1

Dans jquery, vous pouvez facilement définir des pseudo-classes de survol.

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

voici une solution comprenant deux fonctions: addCSSclass ajoute une nouvelle classe css au document, et toggleClass l'allume

L'exemple montre l'ajout d'une barre de défilement personnalisée à 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
la source
-2

Si vous utilisez REACT, il existe quelque chose qui s'appelle le radium . C'est tellement utile ici:

  • Ajouter des gestionnaires aux accessoires si des styles interactifs sont spécifiés, par exemple onMouseEnter pour: hover, encapsulant les gestionnaires existants si nécessaire

  • Si l'un des gestionnaires est déclenché, par exemple en survolant, Radium appelle setState pour mettre à jour un champ spécifique à Radium sur l'objet d'état des composants

  • Lors du nouveau rendu, résolvez tous les styles interactifs qui s'appliquent, par exemple: hover, en recherchant la clé ou la référence de l'élément dans l'état spécifique au Radium

Abdennour TOUMI
la source