【JavaScript / CSS】1行目だけ見えているアコーディオンの作り方

こんにちは、ウェブエンジニアのmasakichiです。
需要があるかわからない程度のtipsなどを書いています。

今回は1行目だけテキストが見えているアコーディオンに挑戦します。

#なにそれ？
こんなアコーディオンです。
テキストの１行目だけ表示されていて、タイトル部分をクリックすると全文が表示されます。

![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/4c73ce60-0c3d-baed-d60e-2d2fac8ad97d.jpeg)

#なぜ書いた？
実際、すごく簡単なコードなので記事にするほどでもないのですが、ググって見つけたサンプルが下記のようなコードが多かったので。

↓
↓
↓

こんなHTMLがあるとして、

```html:HTML
<dl class="accordion">
	<dt>
		TITLE
	</dt>
	<dd>
		<p>
			TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT
			TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT 
			TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT 
			TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT TEXT
		</p>
	</dd>
</dl>
```
↓
こんなCSSを記述して
↓

```scss:SCSS
.accordion{
	max-width: 300px;
	margin: 20px auto;
	dt{
		padding: 1em;
		background-color: #ccc;
	}
	dd{
		background-color: #eeeeee;
		height: 1em;
		overflow: hidden;
		transition: .3s;
	}
}
.accordion.active{
	dd{
		height: auto;
	}
}
```
↓
JavaScriptでクラスの付け替えを実装して完成！
↓

```javascript:JavaScript
$(function(){
    $('.accordion').on('click',function(e){
        if(!$(e.target).parents().is('dt') && e.target !== $(this).find('dt').get(0)){
            return;
        }
        $(this).toggleClass('active');
    });
});

```
いや、まぁいいんですけど...
お気づきの方もいらっしゃると思うのですが、アニメーションはガン無視なのですよ。

`height:auto`にしたところで`transition`はかからず。
中にはheightの数値を決め打ちで`◯◯px`としてるのもあって、これはたしかに`transition`かかるのですが。。。

数値決め打ちってなぁ。。。行数増えたらどうすんの？と思い、自作することにしました（最初から自作しろ）

#clip-pathを使ってみる
まず考えたのが、heightがダメなら、切り抜き範囲を変えてみたらどうかなと思い、`clip-path`を使うことにしました。

↓

以下HTML部分は以前のものと一緒です。

```scss:SCSS
.accordion{
	max-width: 300px;
	margin: 20px auto;
	dt{
		padding: 1em;
		background-color: #ccc;
	}
	dd{
		background-color: #eeeeee;
		clip-path: inset(0 0 calc(100% + -1em) 0);
		transition: .3s;
	}
}
.accordion.active{
	dd{
		clip-path: inset(0 0 0 0);
	}
}
```

```javascript:JavaScript
$(function(){
    $('.accordion').on('click',function(e){
        if(!$(e.target).parents().is('dt') && e.target !== $(this).find('dt').get(0)){
            return;
        }
        $(this).toggleClass('active');
    });
});
```

これで無事に`transition`もかかって、しっかりテキストの高さ分アコーディオンしてくれるようになりました。
しかし、問題が。。。

まず、下記のように`clip-path`で切り抜いたとしても高さがなくなるわけではないので、もし下に別の要素来るとしたら変な隙間が空いてしまいます。
![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1006362/d869cf86-75d9-a0b7-ea99-3af70cd4483d.jpeg)

そしてさらに、`clip-path`はIE1１に対応しておらず。。。
うーん。これだとかなり使い道が限定されてしまいます。

#JavaScriptで実装する
はい。結局、JS使って実装しました。

他にもいろいろCSSプロパティをググってみたのですが、いいものが見つからず。。。
もしCSSだけでいけるよ！ってかたがいらっしゃいましたら是非教えてください

m(_ _)m < なにとぞ

それでは実装例です。

↓

先ほど同様に、HTML部分は以前のものと一緒です。

```scss:SCSS
.accordion{
	max-width: 300px;
	margin: 20px auto;
	dt{
		padding: 1em;
		background-color: #ccc;
	}
	dd{
		position: relative;
		background-color: #eeeeee;
		transition: .3s;
		padding-bottom: 1em;
		overflow: hidden;
		p{
			position: absolute;
			top: 0;
			left: 0;
		}
	}
}
```

```javascript:JavaScript
$(function(){
    $('.accordion').on('click',function(e){
        if(!$(e.target).parents().is('dt') && e.target !== $(this).find('dt').get(0)){
            return;
        }
        $(this).toggleClass('active');
        let contentsH = $(this).find('p').height();

        if($(this).hasClass('active')){
            $(this).find('dd').css({
                "padding-bottom": contentsH + "px"
            });
        }else{
            $(this).find('dd').removeAttr('style');
        }
    });
});
```

特に説明が必要なくらいのコードではないかと思いますが、要点として下記です。

- クリックイベント時に親要素にactiveクラスを付け替えする
- クリックイベント時にアコーディオン箇所の高さを取得する
- activeクラスの有無によってアコーディオン箇所にスタイルをあてる


んー。なにかいいCSSはないものだろうか。。。

今日は以上です！

#追記　line-heightを使う
コメントにて@nagtkkさんからご紹介頂きました。
ありがとうございます。感謝しかありませんm(_ _)m

`line-height`の調整でアコーディオンが実現可能です。
ご好意でリンクを貼っても大丈夫とのことでしたので、下記codepenをご参考ください。

**内容物にボタンなどが入り込むともう少し工夫が必要ですが、テキストなどのあくまで単純なもの**であれば、しっかりとクラスの付け替えのみで実装可能です。


<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="XWepLjb" data-user="nagtkk" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nagtkk/pen/XWepLjb">
  line-height-accordion</a> by nagtkk (<a href="https://codepen.io/nagtkk">@nagtkk</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>