Sass combinant le parent en utilisant une esperluette (&) avec des sélecteurs de type

101

J'ai du mal à nicher à Sass. Disons que j'ai le code HTML suivant:

<p href="#" class="item">Text</p>
<p href="#" class="item">Text</p>
<a href="#" class="item">Link</a>

Lorsque j'essaye d'imbriquer mes styles dans ce qui suit, j'obtiens une erreur de compilation:

.item {
    color: black;
    a& {
        color:blue;
   }
}

Comment référencer un sélecteur de type avant le sélecteur parent lorsqu'il fait partie du même élément?

McShaman
la source

Réponses:

157

Comme le souligne Kumar , cela est possible depuis Sass 3.3.0.rc.1 (Maptastic Maple).

La directive @ at-root provoque l'émission d'une ou plusieurs règles à la racine du document, plutôt que d'être imbriquées sous leurs sélecteurs parents.

Nous pouvons combiner la @at-rootdirective avec l' interpolation#{} pour arriver au résultat escompté.

TOUPET

.item {
    color: black;
    @at-root {
        a#{&} {
            color:blue;
        }
    }
}

// Can also be written like this.
.item {
    color: black;
    @at-root a#{&} {
        color:blue;
    }
}

Sortie CSS

.item {
    color: black;
}
a.item {
    color: blue;
}
Blaine
la source
3
Cela devrait être la solution à cette question.
Kayhadrin
Cela ne fonctionne pas pour moi ... la sortie sort .item a.itempour une raison quelconque. J'ai essayé de faire moi a#{&}-même aussi et toujours le même résultat.
jaminroe
@jaminroe utilisez-vous Libsass? Il sera corrigé dans 3.3 .
Blaine
1
@jaminroe semble utiliser libsass (node-sass) sous le capot. Si tel est le cas, vous devrez attendre la version 3.3.
Blaine
1
J'ai une version légèrement plus difficile à résoudre, similaire, mais avec plusieurs classes, qui doit être ajoutée à une balise - n'importe qui?
Frank Nocke
29

La @at-rootméthode -only ne résoudra pas le problème si vous avez l'intention d'étendre le sélecteur le plus proche de la chaîne. Par exemple:

#id > .element {
    @at-root div#{&} {
        color: blue;
    }
}

Compilera pour:

div#id > .element {
    color: blue;
}

Que faire si vous devez joindre votre balise au .elementlieu de #id?

Il y a une fonction dans Sass appelée selector-unify()qui résout ce problème. En utilisant ceci avec @at-rootil est possible de cibler .element.

#id > .element {
    @at-root #{selector-unify(&, div)} {
        color: blue;
    }
}

Compilera pour:

#id > div.element {
    color: blue;
}
Ben Fleming
la source
1
Cela ne m'a pas donné le résultat escompté. Dans Sass 3.4.11, cette sortie vers #id > .element #id > div.element { color: blue; }. Une autre approche serait d'utiliser selector_replace, #id { > .element { @at-root #{selector-replace(&,'.element', 'div.element')} { color: blue;}}}. Voici un résumé pour une meilleure lisibilité.
Blaine
@Blaine Vous avez attrapé un bug flagrant, merci. J'ai modifié ma réponse avec une solution fonctionnelle.
Ben Fleming
1
Cela dit, selector-replacec'est aussi une autre façon de le faire, mais cela nécessite de savoir ce qu'est le sélecteur. La selector-unifyméthode a l'avantage d'être utilisée dans les mixins.
Ben Fleming
Réponse géniale.
Senthe
8

Pour commencer, (au moment de la rédaction de cette réponse), il n'y a pas de syntaxe sass qui utilise le sélecteur & . Si vous deviez faire quelque chose comme ça, vous auriez besoin d'un espace entre le sélecteur et l'esperluette. Par exemple:

.item {
    .helper & {

    }
}

// compiles to:
.helper .item {

}

L'autre façon d'utiliser l'esperluette est probablement ce que vous recherchez (à tort):

.item {
    &.helper {

    }
}

// compiles to:
.item.helper {

}

Cela vous permet d'étendre les sélecteurs avec d'autres classes, ID, pseudo-sélecteurs, etc. Malheureusement pour votre cas, cela se compilerait théoriquement en quelque chose comme .itema qui ne fonctionne évidemment pas.

Vous voudrez peut-être simplement repenser la façon dont vous écrivez votre CSS. Y a-t-il un élément parent que vous pourriez utiliser?

<div class="item">
    <p>text</p>
    <p>text</p>
    <a href="#">a link</a>
</div>

De cette façon, vous pouvez facilement écrire votre SASS de la manière suivante:

.item {
    p {
        // paragraph styles here
    }
    a {
        // anchor styles here
    }
}

(Note latérale: vous devriez jeter un oeil à votre html. Vous mélangez des guillemets simples et doubles ET mettez des attributs href sur les balises p.)

imjared
la source
@ Lübnah Je suis d'accord mais je n'essaye pas de définir les meilleures pratiques pour les gains de micro-performance. J'essaie de trouver quelqu'un qui met une hrefbalise sur un ppour écrire du CSS fonctionnel.
imjared le
Il n'y a absolument aucun support pour dire que & .selector est une mauvaise pratique du tout, il y a des tonnes de cas d'utilisation exactement comme celui que l'OP publiait.
Mike Mellor
@MikeMellor OP a écrit .item { a& }et il n'y a pas de syntaxe sass pour cela (pour autant que je sache. J'ai suggéré qu'il recherchait probablement quelque chose comme la syntaxe de l'esperluette que vous avez décrite et qui est utilisée assez régulièrement dans Sass. Cependant, en s'appuyant sur l'exemple d'OP, .item { &a }c'est non valide et je compilerais.itema . Comme je l'ai dit dans ma réponse. Y a-t-il quelque chose qui me manque?
imjared
OP voulait .item { a& { ... } }ce qui peut maintenant être fait comme le souligne @Blaine .item { @at-root a#{&} { ... } }. Le fait est que si vous avez une classe sur l'élément, il est facile de le faire, .item { &.class { ... } }alors pourquoi ne devriez-vous pas pouvoir faire la même chose avec un élément html brut. Ce n'est pas parce qu'il n'y avait aucun moyen de le faire au moment de la publication de l'OP qu'il avait tort de vouloir que la fonctionnalité le fasse réellement.
Mike Mellor
4

AFAIK Si vous souhaitez combiner une esperluette pour la classe et les balises en même temps, vous devez utiliser cette syntaxe:

.c1 {
  @at-root h1#{&},
    h2#{&}
    &.c2, {
    color: #aaa;
  }
}

compilera en

h1.c1,
h2.c1,
.c1.c2 {
  color: #aaa;
}

D'autres utilisations (comme utiliser la classe avant @at-rootou utiliser plusieurs @at-roots) entraîneront des erreurs.

J'espère que ça sera utile

Vahid
la source
1
Je viens de l'utiliser, très utile pour les mixins où vous souhaitez faire varier le comportement des éléments span ou div par exemple
santiago arizti
wow - très cool :-) Donc, #{&}c'est essentiellement duper le compilateur - ou cela fonctionnera toujours à l'avenir !?
Simon_Weaver
@Simon_Weaver dans sass, tout à l'intérieur #{}est évalué. Signifie également &le sélecteur de courant. Donc #{&}est évalué à .c1.
Vahid
Mais vous le trompez dans le sens où & seul ne fonctionne pas ;-)
Simon_Weaver
1
@Simon_Weaver voir ce lien pour plus d'exemples de&
Vahid
3

Cette fonctionnalité a atterri dans la dernière version de Sass, 3.3.0.rc.1(Maptastic Maple)

Les deux fonctionnalités étroitement liées que vous devrez utiliser sont le scriptable &, que vous pouvez interpoler dans un style imbriqué pour référencer les éléments parents, et la @at-rootdirective, qui place le sélecteur ou le bloc de css immédiatement suivant à la racine (il ne sera pas avoir des parents dans le css produit)

Voir ce problème Github pour plus de détails

Kumarharsh
la source