Modificar la mitad de un selector en Sass (agregar/eliminar clases, etc.)
Tengo el siguiente SCSS para diseñar enlaces en mi menú:
nav {
ul {
li {
a {
color: red
}
}
}
ul.opened {
li {
a {
color: green
}
}
}
}
Lo que genera el siguiente CSS (correcto):
nav ul li a {
color: red;
}
nav ul.opened li a {
color: green;
}
Intenté modificar mi JavaScript para aplicar la clase al elemento de navegación y usarlo selector-append()
en Sass para agregar la clase. Pero eso parece agregar en el orden incorrecto (y si los argumentos se invierten, ¡la clase se agrega al último elemento!):
nav {
ul {
li {
a {
color: red;
@at-root #{selector-append('.opened', &)} {
color: green;
}
}
}
}
}
Salida (¡incorrecta!):
nav ul li a {
color: red;
}
.openednav ul li a {
color: green;
}
¿Hay alguna manera de reescribir el SCSS para que la clase pueda agregarse correctamente sin tener que duplicar selectores (similar al selector-append()
método)?
La respuesta corta
Dado que el elemento que queremos reemplazar tiene un nombre único, lo que buscamos es esto:
nav {
ul {
li {
a {
color: red;
@at-root #{selector-replace(&, 'ul', 'ul.opened')} {
color: green;
}
}
}
}
}
la respuesta larga
Manipular selectores es extremadamente sucio y lo desaconsejaría a menos que sea absolutamente necesario. Si está sobrecalificando sus selectores especificando cosas como table tr td
o ul li
, comience simplificando: tr y ul son redundantes en estos selectores (a menos que esté tratando de evitar diseñar elementos en una lista ordenada). Ajuste su anidamiento para que sea más simple, etc.
A partir de la versión 3.4 de Sass, hay 2 características importantes que le permiten modificar los selectores.
- Funciones selectoras
- El selector principal se puede almacenar en una variable.
Ejemplo:
.foo ul > li a, .bar {
$sel: &;
@debug $sel;
}
Siempre obtendrá una lista de cadenas porque los selectores se pueden encadenar con una coma, incluso cuando solo tiene un selector.
.foo ul > li a, .bar { ... }
(1 2 3 4 5), (1)
Notarás que aquí se cuenta el selector de descendientes (las listas en Sass pueden estar delimitadas por espacios o comas). Es extremadamente importante recordar esto.
cuando selector-replace()
no funciona
La selector-replace()
función no funciona en los siguientes casos:
- El selector que desea reemplazar no es único (p. ej.
ul ul li
) - Desea insertar uno o más selectores (por ejemplo,
ul ul li
->ul ul ul li
) - Desea eliminar un selector (por ejemplo,
ul > li
->ul li
)
En este caso, deberá recorrer los selectores y necesitará saber qué posición desea modificar. La siguiente función tomará una función y la aplicará a una posición específica en su selector usando la magia de la función call() .
@function selector-nth($sel, $n, $f, $args...) {
$collector: ();
@each $s in $sel {
$modified: call($f, nth($s, $n), $args...);
$collector: append($collector, set-nth($s, $n, $modified), comma);
}
@return $collector;
}
Agregar una clase (cuando el selector no es único o no sabes su nombre)
La función que necesitamos aquí toma 2 argumentos: el selector original y el selector que le gustaría agregarle. Utiliza interpolación simple para hacer el trabajo.
@function append-class($a, $b) {
@return #{$a}#{$b};
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, append-class, '.baz')} {
color: blue;
}
}
}
Producción:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > li.baz a, .bar ul > li.baz a {
color: blue;
}
Insertar un selector
Esta función también toma 2 argumentos: el selector original y el selector que desea insertar antes.
@function insert-selector($a, $b) {
@return $b $a;
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, insert-selector, '.baz')} {
color: blue;
}
}
}
Producción:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > .baz li a, .bar ul > .baz li a {
color: blue;
}
Eliminar un selector
Eliminar un selector es tan simple como reemplazarlo con una cadena vacía.
@function remove-selector($sel) {
@return '';
}
.foo, .bar {
ul > li a {
color: red;
@at-root #{selector-nth(&, -2, remove-selector)} {
color: blue;
}
}
}
Producción:
.foo ul > li a, .bar ul > li a {
color: red;
}
.foo ul > a, .bar ul > a {
color: blue;
}
TL;DR
Los selectores son solo listas. Cualquier función de manipulación de listas funcionará en él y podrá recorrerlo para modificarlo según sea necesario.
Así que sí, no lo hagas a menos que realmente lo necesites. Si ha decidido que todavía lo necesita, he empaquetado estas funciones en la biblioteca selector-nth .