web-dev-qa-db-fra.com

sass css: classe parent cible de l'enfant

J'utilise Sass et j'ai trouvé un problème. Voici un exemple de ce que j'essaie de faire: 

.message-error {
    background-color: red;

    p& {
        background-color: yellow
     }
  }

Css attendu:

.message-error {
    background-color: red;
}
p.message-error {
    background-color: yellow ;
}

L'idée: tous les éléments avec .message-error seront rouges, sauf s'il s'agit de p.message-error. Ce n'est pas une situation réelle, juste pour montrer un exemple.

SASS n'est pas capable de compiler cela, j'ai même essayé la concaténation de chaînes. Y at-il un plugin qui fera exactement la même chose?

NOTE: Je sais que je peux mettre une autre définition CSS comme

p.message-error{....}

sous, mais je voudrais éviter cela et utiliser un seul endroit pour toutes les définitions .message-error.

Merci.

35
Zeljko

A partir de Sass 3.4, cela est maintenant supporté. La syntaxe ressemble à ceci:

.message-error {
    background-color: red;

    @at-root p#{&} {
        background-color: yellow
    }
}

Notez la directive @at-root et la syntaxe d'interpolation sur l'esperluette. Si la directive @at-root n'est pas incluse, un sélecteur du type .message-error p.message-error plutôt que p.message-error sera créé.

26
cimmanon

Natalie Weizenbaum (la concepteur en chef et développeur de Sass) dit que cela ne sera jamais supporté:

À l’heure actuelle, & est syntaxiquement identique à un sélecteur d’éléments. ne peut pas apparaître à côté d'un. Je pense que cela aide à clarifier où il peut être utilisé; Par exemple, foo&bar ne serait jamais un sélecteur valide (ou pourrait être équivalent à foo& bar ou foo &bar). Je ne pense pas que cette utilisation le cas est assez fort pour justifier de changer cela.

Source: # 282 - Sélecteur d'élément.parent

À ma connaissance, il n'y a pas de solution de contournement possible.

20
piouPiouM

La meilleure chose à faire serait probablement la suivante (en supposant que votre classe .message-error comporte un peu plus que la couleur de fond.

.message-error {
  background-color: red;
}

p.message-error {
  @extend .message-error;
  background-color: yellow
}

Cette approche n'offre pas ce regroupement proche, mais vous pouvez simplement les garder proches les uns des autres.

9
justnorris

Je pense que si vous voulez les garder groupés par sélecteur de parents, vous devrez peut-être ajouter un parent commun:

body {
    & .message-error {background-color: red;}
    & p.message-error {background-color: yellow}
}

Bien entendu, body peut être remplacé par un autre parent commun, tel que #Content ou un autre nom div qui contiendra tous les messages d'erreur.

UPDATE (une autre idée)

Si vous exploitez les listes @for et alors il semble que cela devrait fonctionner (ce que je ne sais pas, c'est si la liste permettra le . (point).

@for $i from 1 to 3 {
  nth(. p. ul., #{$i})message-error {
    background-color: nth(red yellow cyan, #{$i}));
  }
}

Devrait compiler quelque chose comme:

.message-error {
   background-color: red;}
p.message-error {
   background-color: yellow;}
ul.message-error {
   background-color: cyan;}
2
ScottS

@Zeljko Il n'est pas possible de faire ce que vous voulez via SASS.

Voir le commentaire Nex3: https://github.com/nex3/sass/issues/286#issuecomment-7496412

La clé est l'espace avant le '&':

.message-error {
    background-color: red;

    p & {
        background-color: yellow
     }
  }

au lieu de:

.message-error {
    background-color: red;

    p& {
        background-color: yellow
     }
  }
2
Tian

J'ai fait un mixin qui résout ce problème.

Github: https://github.com/imkremen/sass-parent-append

Exemple: https://codepen.io/imkremen/pen/RMVBvq


Utilisation (scss):

.ancestor {
  display: inline-flex;

  .grandparent {
    padding: 32px;
    background-color: lightgreen;

    .parent {
      padding: 32px;
      background-color: blue;

      .elem {
        padding: 16px;
        background-color: white;

        @include parent-append(":focus", 3) {
          box-shadow: inset 0 0 0 8px aqua;
        }

        @include parent-append(":hover") {
          background-color: Fuchsia;
        }

        @include parent-append("p", 0, true) {
          background-color: green;
        }
      }
    }
  }
}

Résultat (css):

.ancestor {
  display: inline-flex;
}
.ancestor .grandparent {
  padding: 32px;
  background-color: lightgreen;
}
.ancestor .grandparent .parent {
  padding: 32px;
  background-color: blue;
}
.ancestor .grandparent .parent .elem {
  padding: 16px;
  background-color: white;
}
.ancestor:focus .grandparent .parent .elem {
  box-shadow: inset 0 0 0 8px aqua;
}
.ancestor .grandparent .parent:hover .elem {
  background-color: Fuchsia;
}
.ancestor .grandparent .parent p.elem {
  background-color: green;
}
0
imkremen

J'ai eu le même problème alors j'ai fait un mixin pour ça.

@mixin tag($tag) {
  $ampersand: & + '';
  $selectors: simple-selectors(str-replace($ampersand, ' ', ''));

  $main-selector: nth($selectors, -1);
  $previous-selectors: str-replace($ampersand, $main-selector, '');

  @at-root {
     #{$previous-selectors}#{$tag}#{$main-selector} {
      @content;
    }
  }
}

Pour que cela fonctionne, vous aurez également besoin d'une fonction de remplacement de chaîne ( de Hugo Giraudel ):

@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);
  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }
  @return $string;
}

Comment ça marche:

SCSS 

.foo {
  color: blue;

  @include tag(p) {
    color: red;
  }
}

Sortie

.foo {
  color: blue;
}

p.foo {
  color: red;
}

Cas d'utilisation
Cette méthode fonctionne avec les sélecteurs imbriqués mais pas avec les composés.

0
Quentin Veron

Je l'ai déjà rencontré auparavant. Bootstrap 3 gère cela en utilisant un hack de sélecteur de parent. Je l'ai légèrement modifié pour mes propres besoins ...

@mixin message-error() {
  $class: '.message-error';
  #{$class} {
    background-color: red;
  }
  p#{$class} {
    background-color: yellow;
  }
}
@include message-error();

wheresrhys utilise une approche similaire ci-dessus, mais avec quelques erreurs sass. Le code ci-dessus vous permet de le gérer en un seul bloc et de le réduire dans votre éditeur. L'imbrication de la variable la rend également locale afin que vous puissiez réutiliser $ class pour toutes les instances où vous devez appliquer ce hack. Voir ci-dessous pour un échantillon de travail ...

http://sassmeister.com/Gist/318dce458a9eb3991b13

0
Adam Youngers