Modificar la mitad de un selector en Sass (agregar/eliminar clases, etc.)

Resuelto cimmanon asked hace 9 años • 2 respuestas

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

cimmanon avatar Feb 19 '15 03:02 cimmanon
Aceptado

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

cimmanon avatar Feb 18 '2015 20:02 cimmanon