Sassでネストされたセレクタの直前の親セレクタを取得する関数

Sassでmixinを作成しているときにネストされたセレクタの直前の親セレクタが欲しいときがあったので、それを取得する関数を作ってみました。

Sassでネストされたセレクタの直前の親セレクタを取得する関数

今回実装するものを以下のHTMLとCSSを例に簡単に説明します。

やりたいこと
.box .box__inner.box__innerだけ取得したい。

<div class="box">
  <div class="box__inner">
    <p class="box__text">さんぷるテキストさんぷるテキスト</p>
  </div>
</div>
.box {
    .box__inner {
    background: #eee;
        &:hover {
          background: #ccc;
        }
    }
}

.box .box__innerのようなネストされたセレクタの中で直前までのセレクタを指定するにはSassでは&を使用することで取得できますが、取得されるセレクタは.box .box__innerとなります。

今回は.box__innerだけを取得したいのでその関数の紹介になります。

関数の紹介

//ネストされたセレクタの直前の親セレクタを取得する関数
//.hoge .hoge__box だと .hoge__box を取得
@function parent($value) {
  $selector: $value;
  $array: "";
  $space: if(str-index($selector, " "), str-index($selector, " "), 0);//セレクタのスペースの位置を保存※スペースがなければ0

  //セレクタにスペースがあれば繰り返し
  @while $space > 0 {
    $selector: str-insert($selector, ',', $space);//スペースの位置に,(カンマ)を挿入
    $array: selector-parse($selector);//配列に変換
    //  // $l: length($array);
    $selector: #{nth($array, length($array))};//配列の最後を取得
    $space: if(str-index($selector, " "), str-index($selector, " "), 0);//セレクタのスペースの位置を保存※スペースがなければ0
  }
  @return "#{$selector}";
}

使い方

parent('.box .box__inner')
//返り値 .box__inner

parent('.box .box__inner .box__text')
//返り値 .box__text

.box {
    .box__inner {
        parent(#{&})
        //返り値 .box__inner
    }
}

解説

$selector: $value;
$array: "";
$space: if(str-index($selector, " "), str-index($selector, " "), 0);

初期値を設定

  • 引数からセレクタを受け取り、$selectorにセット
  • 配列を入れる変数を作成
  • セレクタの最初のスペース(ネスト)の位置を取得

str-index($string, $substring)
指定した文字列($substring)が最初に出現する位置の番号を返す。

スペース(ネスト)の数だけループして直前の親セレクタ以外を削除

@while $space > 0 {
  $selector: str-insert($selector, ',', $space);//スペースの位置に,(カンマ)を挿入
  $array: selector-parse($selector);//配列に変換
  // $l: length($array);
  $selector: nth($array, length($array));//配列の最後を取得
  $space: if(str-index($selector, " "), str-index($selector, " "), 0);//セレクタのスペースの位置を保存※スペースがなければ0
}

ループのイメージ

以下のセレクタの場合
.box .box__inner .box__text

1, 最初のスペース(ネスト)の位置に,(カンマ)を挿入
2, 1を配列に変換
3, 2の配列の最後を取得※
4, 3のセレクタの最初のスペース(ネスト)の位置を取得

.box, .box__inner .box__text
↓
[.box, .box__inner .box__text]
↓
.box__inner .box__text
↓
.box__inner, .box__text
↓
[.box__inner, .box__text]
↓
.box__text

このループで最後に直前の親セレクタが残りますので

@return $selector;

で終わりです。

そもそも何に使うのか?

直前の親セレクタなんてそんなのどこで使うのか?
&で十分じゃないかと思われたと思います。

今回冒頭で述べたようにmixin作成中に直前の親セレクタが必要になったことがありました。

そのmixinは以下の記事となります。

特定の親セレクタの状態を変更するmixin ネスト外にも対応

最後に

僕の調べた限りではSassの組み込み関数で直前の親セレクタを取得するものがありませんでしたので、今回のようなものを作成しました。

僕自身Sassの関数やmixinの作成はあまり得意ではありませんのでもっといい方法があるのではないかと思います。
誰か優しい方教えてくれるとすごく嬉しいです。

参考にさせて頂いた記事

https://sass-lang.com/documentation/modules

https://blog.yuhiisk.com/archive/2015/07/14/sass-built-in-functions.html