ボタンタップで一時的に要素を量産する

# 作ったもの
![emoteAnim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602012/0253dd7c-db44-d076-26b0-4322bd85017e.gif)


インスタライブのような同期チャットにエモート機能を載せるということで
JSとCSSアニメーションでひな形の挙動を作ってみました。

## コード

<details><summary>HTML</summary>

```html:html
<link rel="stylesheet" href="style.css">

<div class="contentsAll">
    <!-- なにもないエリア(表示用) -->
    <div class="none-area"></div>
    <!-- エモートリスト  -->
    <ul class="emotes">
        <li class="emotes__box left">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box upper">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box right">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
        <li class="emotes__box under">
            <button class="emotes__box-select"></button>
            <div class="floatBox">
                <span class="floatBox__item"></span>
            </div>
        </li>
    </ul>
</div>

<script type="text/javascript" src="index.js"></script>
```

</details>


<details><summary>CSS</summary>

```css:css
ul li {
    list-style: none;
}
button{
    background-color: transparent;
    border: none;
    cursor: pointer;
    width: 100%;
    height:100%;
}


.none-area{
    height:70%;
    background-color: lightgray;
}


.emotes{
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding:5px 10px;
    background-color: #FFF;
}
.emotes__box , .floatBox {
    height:30px;
    width:30px;
}
.emotes__box{
    position:relative;
}
.floatBox{
    position: absolute;
    bottom:70px;
    z-index:3;
}


.floatBox__item{
    display: none;
    width:100%;
    height:100%;
}
.floatBox__item:nth-of-type(n+2){
    display: block;
    position:absolute;
}
.floatBox__item:nth-of-type(odd){
    animation:oddemote 1.5s linear forwards;
}
.floatBox__item:nth-of-type(even){
    animation:evenemote 1.5s linear forwards;
}

.left button , .left .floatBox__item{
    background: url('img/left-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.upper button , .upper .floatBox__item{
    background: url('img/upper-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.right button , .right .floatBox__item{
    background: url('img/right-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}
.under button , .under .floatBox__item{
    background: url('img/under-arrow.png');
    background-size: contain;
    background-repeat: no-repeat;
}


@keyframes oddemote{
    0%{
        transform:rotate(0deg) scale(1.2);
    }
    25%{
        transform: translate(10px, -25px) rotate(-15deg) scale(1.2);
    }
    35%{
        transform: translate(0px, -35px) rotate(-20deg) scale(1);
    }
    55%{
        transform: translate(-10px, -55px) rotate(15deg) scale(1);
        opacity:0.9;
    }
    75%{
        transform: translate(0px , -75px) rotate(-5deg) scale(0.8);
        opacity:0.8;
    }
    95%{
        transform: translate(-10px , -100px) rotate(-15deg) scale(0.7);
        opacity:0.6;
    }
    100%{
        transform: translate(-12px , -105px) rotate(-15deg) scale(0.2);
        opacity:0;
    }
}
@keyframes evenemote{
    0%{
        transform:rotate(0deg) scale(1.2);
    }
    25%{
        transform: translate(-10px, -25px) rotate(15deg) scale(1.2);
    }
    35%{
        transform: translate(0px, -35px) rotate(20deg) scale(1.1);
    }
    55%{
        transform: translate(10px, -55px) rotate(-15deg) scale(1.1);
        opacity:0.9;
    }
    75%{
        transform: translate(0px , -75px) rotate(5deg) scale(0.9);
        opacity:0.8;
    }
    95%{
        transform: translate(10px , -100px) rotate(15deg) scale(0.7);
        opacity:0.6;
    }
    100%{
        transform: translate(11px , -102px) rotate(-15deg) scale(0.2);
        opacity:0;
    }
}
```

</details>

<details><summary>javascript</summary>

```javascript:javascript
// エモート表示
const selectedEmote = document.getElementsByClassName('emotes__box-select');
const floatEmote = document.getElementsByClassName('floatBox')
for(let i = 0; i < selectedEmote.length; i++){
    selectedEmote[i].addEventListener('click' , displayEmote);
    function displayEmote(){
        const arrayEmote = Array.prototype.slice.call(selectedEmote);
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);

        //表示が終わったエレメントを消す
        setTimeout(function(){clonedEmote.style.display = 'none'},2000);
        setTimeout(function(){clonedEmote.remove()},6000);

    }
};
```

</details>


## 概要

#### 大まかな挙動のゴール
1.エモートボタンを連打できる
2.表示が終わったらエモート（エレメントそのもの）を消す

#### 大まかな仕様解説
1. エモート表示用のボタンを基準値にして、エモート本体の座標を決める
2. デフォルトはdisplay:none;としつつ、2つ目以降をdisplay:block;とする。
`○○:nth-of-type(n+2){display: block;}`
3. ボタンを押下する度にエモート本体(span)要素を量産
4. `setTimeOut`で時間差を作り、表示が終わったエモートを順次削除

#### コアな挙動
JSの部分ですね。
```javascript:javascript
const selectedEmote = document.getElementsByClassName('emotes__box-select');
const floatEmote = document.getElementsByClassName('floatBox')
for(let i = 0; i < selectedEmote.length; i++){
    selectedEmote[i].addEventListener('click' , displayEmote);
    function displayEmote(){
        const arrayEmote = Array.prototype.slice.call(selectedEmote);
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);

        //表示が終わったエモートを消す
        setTimeout(function(){clonedEmote.style.display = 'none'},2000);
        setTimeout(function(){clonedEmote.remove()},6000);

    }
};
```

まずはボタン要素をクラスで取得、for文を使っておなじみの処理。（いずれmap等に置換え）
`getElementsByClassName`で取得した要素は配列でなく
HTMLCollectionという配列っぽい配列でない何か　になり
このままだと後に使う「indexOf」が使えないので
```javascript:javascript
        const arrayEmote = Array.prototype.slice.call(selectedEmote);
```
`Array.prototype.slice.call`で配列化します。

```javascript:javascript
        const emoteIndex = arrayEmote.indexOf(this);
        const clone = floatEmote[emoteIndex].firstElementChild.cloneNode(true);
        const clonedEmote = floatEmote[emoteIndex].appendChild(clone);
```
配列化した後、クリックされたエモートの**index番号**を取得。
`floatEmote`で取得した表示用エモートの親要素のindex番号に**照らし合わせる**。
そして、その子要素に現存する表示用エモートの雛形(非表示になっている)をクローン。
`appendChild`で一番最後にクローンした要素をくっつける。

`○○:nth-of-type(n+2){display: block;}`で
2つ目以降のspan要素は表示される指定になっているので
動的なスタイルの変更やクラスの付替は不要です。

表示が終わった後の削除処理は、もうちょっとうまいことやれそうなのでアドバイスください..