フラッシュメッセージをhtml/css/jqueryで実装する（コピペOK）

#完成像
下記gifのようなフラッシュメッセージををhtml, css, jqueryで実装したのでコードと利用方法の備忘録を残しておきたいと思います。コードも解説しているのでご参考になれば幸いです。
![flash_message.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/370f0977-1533-9c6c-c01d-9943ff942318.gif)

# Codeと解説
## html

```html:html
<button class="js-show-flash-success">成功メッセージ</button>
<button class="js-show-flash-fail">失敗メッセージ</button>
<div id="flash-msg-area"></div>
```
メッセージ表示用のボタン（成功/失敗メッセージ用）とフラッシュメッセージエリア

## css

```css:css
#flash-msg-area {
    opacity: 0;
    position: absolute;
    left: 50%;
    top: -20px;
    transform: translateX(-50%);
    transition: all 200ms ease-out;
}
#flash-msg-area.show {
    opacity: 1;
    top: 20px;
}
#flash-msg-area .flash_message {
    display: inline-block;
    width: 200px;
    text-align: center;
    font-size: 15px;
    padding: 10px 20px;
    color: #fff;
    background-color: #86a3ff;
    box-shadow: 1px 1px 2px 1px #ddd;
}
#flash-msg-area .flash_message.error {
    background-color: #ff8e86;
}

```
このcssでやっていることは、
・メッセージの表示位置を中央にする
・transitionをつけることでopacity=0～1、top=-20px～20pxの変化にグラデーションをつける(徐々にメッセージを表示しつつ、スライドインする動作)
⇒transitionは動作の変化を少しずつつけることができるプロパティです。[ここ](https://shu-naka-blog.com/css/transition/#btn01)が参考になります。
・メッセージエリアはあらかじめ透明（opacity=0）として非表示にして、.showが追加されたときに不透明（opacity=1）として表示する(⇒transitionの効果と組み合わさって、.showが追加されると徐々に表示されて、.showが除かれると徐々に消えていく感じになる)
・メッセージエリアは非表示(opacity=0)のときに基準位置よりも20px上に配置しておき、表示(opacity=1)のときに基準位置よりも20px下に配置する(⇒transitionの効果と組み合わさって、.showが追加されると上から下スライドインし、.showが除かれると下から上にスライドアウトする)


図で示すと下記のような感じで、初期位置を上にずらして透明にしておいて、表示するときに不透明にしつつ元の位置に戻してあげれば、transitionの効果で最初に見せたフラッシュメッセージのような動作になります。
![flash_area.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/1373f485-39f7-e2e4-fa53-dc096719d176.png)

## javascript

```js:javascript
$(function(){
    //フラッシュメッセージ用の関数をjqueryに登録
    $.fn.flash_message = function(options) {
        //デフォルト値
        options = $.extend({
            text: '完了しました',
            time: 3000,
            how: 'before',
            class_name: ''
        }, options);

        return $(this).each(function() {
            //指定したセレクタを探して取得
            if ($(this).find('.flash_message').get(0)) {
                $(this).find('.flash_message').remove();
            }

            var message = $('<span />', {
                'class': 'flash_message ' + options.class_name,
                text: options.text
            //フェードイン表示
            });

            $(this)[options.how](message).addClass('show');
            //delayさせてからフェードアウト
            message.delay(options.time).queue(function(){
                $(this).parents('#flash-msg-area').removeClass('show');
            });
        });
    };

    // ボタンクリックでフラッシュメッセージ（成功）を表示
    $('.js-show-flash').click(function() {
        $('#flash-msg-area').flash_message({
            text: '登録に成功しました',
            how: 'append'
        });
    });

    // ボタンクリックでフラッシュメッセージ（失敗）を表示
    $('.js-show-flash-fail').click(function() {
        $('#flash-msg-area').flash_message({
            text: '失敗しました',
            how: 'append',
            class_name: 'error'
        });
    })
})
```
javascriptでやっていることは、
・下記関数(flash_message)をjqueryに登録
　- 表示するときにメッセージエリア(#flash-msg-area)にshowクラスをつけ、ある時間が経過したら外す
・ボタンクリックされたら、flash_messageを動作させる
ということです。
ひとつひとつコードを説明します。

### ① `$.fn.flash_message()`
```js
$.fn.flash_message = function(options) {
```
flash_messageをjqueryに登録します。登録しておくことで、通常のjqueryの関数を呼び出す感じでflash_messageを呼び出すことができます。`$('#flash-msg-area').flash_message` のような感じです。
このように記載すれば、通常の関数みたく呼び出せるんだ、という理解でいいのですが、より正確に理解するためには`$.fnとは？`というところから学ぶ必要があります。詳しく知りたい方は[jQuery拡張の仕組み 〜 JSおくのほそ道 #013](https://qiita.com/hosomichi/items/29b19ed3ebd0df9361ae)を参照ください。

### ② `$.extend()`
```js
options = $.extend({
    text: '完了しました',
    time: 3000,
    how: 'before',
    class_name: ''
}, options);
```
ここではフラッシュメッセージのオプションを設定しています。

| オプション | 初期値 | 説明 |
|:-:|:-:|:-:|
| text  | 完了しました | メッセージ内容  |
| time  | 3000  | メッセージ表示時間  |
| how  |  before |  メッセージテキストエリアの追加方法 |
| class_name  |  なし |  メッセージテキストエリア（.flash_message）に追加したいclass名 |

`$.extend`関数は、複数のオブジェクトをマージして返してくれるメソッドです。（第一引数に対し、第二引数をマージする）
参考サイト：[jQuery.extend() メソッドの使い方まとめ](https://www.tam-tam.co.jp/tipsnote/javascript/post3853.html)

例えば、下記optionsを第二引数に与えた場合、

```js
options = {
    time: 5000,
    class_name: 'warning'
}
```
返却されるoptionsは、下記のようになります。

```js
options = {
    text: '完了しました',
    time: 5000,
    how: 'before',
    class_name: 'warning'
}
```

つまり、第二引数で指定したものはその値に置き換わり、ないものは初期値が使用されるということです。

### ③ `$(this).each(function(index, element))`
`each`関数は、`$()`にマッチした要素をひとつずつ取得して実行します。

例えば、下記HTML要素があって、javascriptを実行した場合、

```html:html
<ul>
    <li>html</li>
    <li>css</li>
    <li>jquery</li>
</ul>
```
```javascript:javascript
$('li').each(function(index, element){
    console.log(element);
})
```
ログには下記のように出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/5d57cc59-29e0-5d51-2c22-0b524b26319e.png)
`li`要素にマッチするHTML要素が一つずつ順番に取得されているのがわかると思います。

ここで改めて下記コードを眺めてみます。

```js
$(this).each(function() {})
```
`each`は`$(this)`にマッチする要素を一つずつ取得して実行するはずです。では、ここでいう`$(this)`とはなんでしょうか。
$(this)をログに出力した結果、下記が1度だけ出力されていました。
![this.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/4d73c258-35d8-80b5-c080-d2f74313f065.jpeg)

つまり、$(this)にマッチするhtml要素は`div#flash-msg-area`だけで、この要素に対して`.each(function(){})`のなかでshowクラスの付け外しを実施したということです。よくよくかんがえれば、id要素なのでひとつしかないのは当然で、あえてeachする必要はありませんが、class要素の場合は複数取得する可能性があるので、そのときも動作するようにケアしている感じです。

### ④ `$(this).find('.flash_message').remove()`

```js
if ($(this).find('.flash_message').get(0)) {
    $(this).find('.flash_message').remove();
}
```
`flash_message`関数内でメッセージ表示エリア(.flash_message)をフラッシュメッセージエリア(#flash-msg-area)に追加しています。なので、はじめに.flash_messageの要素を削除してから、新規の.flash_messageを追加しています。`$(this).find('.flash_message').remove()`を削除してコードを実行するとわかりますが、.flash_messageをremoveしておかないと下記のようにメッセージが重なって表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/6fb54004-9c8e-82af-2401-de347dfa71c3.png)

### ⑤ `$('<span />', { } )`

```js
var message = $('<span />', {
    'class': 'flash_message ' + options.class_name,
    'text': options.text
});
```
この記述をすることで、下記html要素を作成することができます。

```html
<span class="flash_message ">（options.textの内容）</span>
```
この記述はちょっと見慣れないですが、`$()`は第一引数に何を指定するかによって返却されるものが大きくかわってくることを頭にいれておきましょう。下記を見れば、いろいろなケースがあるのがわかると思います。
[jQueryセレクタの仕組み 〜 JSおくのほそ道 #011](https://qiita.com/hosomichi/items/25064020f199e35d65db#jquery%E9%96%A2%E6%95%B0%E3%81%AE%E5%AE%9F%E8%A1%8C%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)

上記参考サイトでいうと、今回のケースはパターン7に相当します。仕様についてより詳しく知りたいかたはjqueryの公式リファレンスを参照ください。

https://api.jquery.com/jQuery/#entry-examples-1

### ⑥ `$(this)[options.how](message).addClass('show');`

```js
$(this)[options.how](message).addClass('show');
```
フラッシュメッセージエリア(#flash-msg-area)内にフラッシュメッセージテキストエリア(.flash_message)を追加し、さらにshowクラスを追加しています。
このコードは下記と同義です。

```js
$(this).before(message).addClass('show')
```
なぜ`$(this)[options.how]`のような書き方をしているかというと、関数名を変数として扱っているので、`$(this).options.how(message).addClass('show')`と記述すると、まったく別の意味で解釈されてしまうからです。これを実行すると「Cannot read property 'how' of undefined」となります。そもそも$(this)にoptions関数（optionsオブジェクト）なんてないので当たり前です。
関数を変数内にいれて扱いたい場合はこのように記述することは覚えていてもよいかもしれません。

### ⑦ `message.delay(options.time).queue(function(){})`

```js
message.delay(options.time).queue(function(){
    $(this).parents('#flash-msg-area').removeClass('show');
});
```

options.timeミリ秒後にメッセージエリア(#flash-msg-area)からshowクラスを外す動作をしています。これによりメッセージがoptions.timeミリ秒だけ表示されて消えるわけです。delayの後に実行したい関数をqueueにより独自に設定しています（ここではshowクラスを除く動作）。もし設定したい動作がjqueryオブジェクト内の関数でよいならqueueを使う必要はなく、`message.delay(options.time).removeClass('show')`のような感じ（メソッドチェーンと呼ばれます）で記載すればいいです
`queue`関数について補足します。
まずは"キュー"の用語説明です。
>キューとは、最も基本的なデータ構造の一つで、要素を入ってきた順に一列に並べ、先に入れた要素から順に取り出すという規則で出し入れを行うもの。順番を待つ人の行列と同じ仕組みであるため「待ち行列」とも訳される。

行列ができてるお店に並ぶことを想像するとわかりやすい(?)かもです。さきにならんでいる人から先にお店に入りますよね。そういうイメージです。
下記のようなjqueryのコードがあるとき、show⇒animate⇒slideToggle⇒slideToggle⇒animate⇒hide⇒show⇒slideUpの順にキューに登録されます。

```js
function runIt() {
  $(div)
    .show( "slow" )
    .animate({ left: "+=200" }, 2000 )
    .slideToggle( 1000 )
    .slideToggle( "fast" )
    .animate({ left: "-=200" }, 1500 )
    .hide( "slow" )
    .show( 1200 )
    .slideUp( "normal", runIt );
}
```

上記のケースは、jqueryオブジェクトが持っている関数を登録していますが、もし独自の関数を登録したい場合は、`queue(function(){})`に実装します。

```js
function runIt() {
$( "div" )
    .show( "slow" )
    .animate({ left: "+=200" }, 2000 )
    .queue(function() {
      $( this ).addClass( "newcolor" ).dequeue();
    })
    .animate({ left: "-=200" }, 500 )
    .queue(function() {
      $( this ).removeClass( "newcolor" ).dequeue();
    })
    .slideUp();
}
```

少し話はそれますが、、
はじめのコードに戻ると、、

```js
message.delay(options.time).queue(function(){
    $(this).parents('#flash-msg-area').removeClass('show');
});
```
このコードって、

```js
message.delay(options.time).parents('#flash-msg-area').removeClass('show');
});
```
このように記載できそうじゃないですか？でもこの書き方はできません。
理由はメソッドチェーンは、戻り値がjqueryオブジェクトである必要があるからです。上記のparentsの返り値はjqueryオブジェクトではないのでメソッドチェーンで記載できないのです。
関数の戻り値がjqueryオブジェクトかどうか（メソッドチェーン可能かどうか）は、下記のサイトで関数を検索して、”戻り値”のところを確認してください。ここに”jqueryオブジェクト”とあればメソッドチェーンとして利用可能です。

http://semooh.jp/jquery/

## おわりに
単純なコードでもいろいろと調べてみると勉強になりますね。
どなたかの参考になればうれしいです！