◆ replaceChild でダミーと交換して元に戻す
◆ 兄弟要素なしか最後なら remove して appendChild もあり
◆ getClientRects 使えば display を none して元に戻すことでも可能 

css のアニメーションって最初に勝手に実行しますけど ボタンをクリックした時など好きなタイミングで実行させたいですよね

先にサンプルページ置いておきます

もともとこの記事用に作ってたので for 文だと動かないー のようなのがついてます
黄緑のボタンだけみてくれればいいです


ボタンを押すと上から下へと流れるようにアニメーションが続いていくものが目的です

display:block

display を none から block のように非表示から表示に切り替えるとアニメーションします
animB は display を none にして即 block にしています
function animStartB(elem){
if(!elem) return
elem.style.display = "none"
elem.style.display = "block"
}
押してみるとわかりますが 何も起きません
none にしてもすぐ block にしているので 何も起きなかったのと一緒です
つまり何もなかったことにされるので アニメーションはしません

display:block + setTimeout

即 block に戻してもダメです
なら わずかに時間を置いて none に一瞬でもなればいけるはずです
http://var.blog.jp/archives/46166953.html

ということで setTimeout を使います

animBS
function animStartBS(elem){
if(!elem) return
elem.style.display = "none"
setTimeout(function(){elem.style.display = "block"},1)
}
問題なさそうですが 一部しかアニメーションしません
原因はよくわからないですが 1つアニメーションが実行中に block が切り替わっても無意味なようです
Firefox だと稀にうまくいくので内部実装がどうこうありそうです
複雑な最適化とか関わってきてそうなのでとりあえずこれはダメということだけわかればいいとします

それと今回は 非表示に切り替える要素の外側の要素で領域を確保してるので大丈夫ですが そういうことをしていない場合は一瞬要素が消えて違和感を感じることになります

class

期待せずに block の切り替えをクラスでやってみました
直接要素の style を書き換えるのじゃなくて class を付け替えてという間接的なアピールです

animC
function animStartC(elem){
if(!elem) return
elem.classList.remove("anim")
elem.classList.add("anim")
}
anim クラスは animation の css がついてるだけです

予想通り直接 style のと一緒で動きません

class + setTimeout

timeout 版も一応

animCS
function animStartCS(elem){
if(!elem) return
elem.classList.remove("anim")
setTimeout(function(){elem.classList.add("anim")},1)
}
直接 style 変えたのと一緒で飛び飛びでアニメーションがされてますね

replace element

ぐぐってみると cloneNode して replaceChild を使うのが良い方法とのこと
とりあえずやってみます

animR
function animStartR(elem){
if(!elem) return
elem.parentElement.replaceChild(elem.cloneNode(true), elem)
}
ちゃんと動いてます!
綺麗に上から順番にアニメーションしてくれます

ですが 問題点もいくつかあります

まず 要素を入れ替えているので elems のような配列に要素への参照を保存しているとそこも置き換えないとダメです
簡単に置き換えれるといいのですが アニメーションする部分からは読み出し専用になってるような作りだとどうしようもないですし アニメーションのために構造を変えるというのも違う気がします

次に cloneNode ではイベントリスナはついてきません
jQuery だと内部でリスナを保存してるのでクローン時につけてくれますが ただの JavaScript では自分でどうにかしないとダメです


実は 0~5 にはクリックリスナがついています
リロードしてすぐにクリックするとアラートが出ますが animR を押した後は出なくなっています

アニメーションはできても 問題点も多いので実用性的に使いづらいです

replace して戻す

replace はしますが その後すぐにまた replace して元に戻します
もとに戻すなら わざわざ cloneNode せずにシンプルなダミー要素でいいので cloneNode はしないようにしてます

animRU
function animStartRU(elem){
if(!elem) return
var p = elem.parentElement
var dummy = document.createElement("div")
p.replaceChild(dummy, elem)
p.replaceChild(elem, dummy)
}

block と同じで 元に戻してるなら最適化で動かないかなと思ったのですが ちゃんとアニメーションしてくれます
ただ この先ずっとアニメーションしてくれる保証はないです

remove して append する

わざわざ ダミーをつくって replace で置き換えなくても remove して append するだけでいいなじゃないかと思うのでやってみます

animRA
function animStartRA(elem){
if(!elem) return
var p = elem.parentElement
p.removeChild(elem)
p.appendChild(elem)
}

こっちでもちゃんと動いてます
ですが appendChild は最後に追加になるので 要素が複数あると ちょっと面倒なので replaceChild を使ったほうがいいかもしれません

まとめ

replaceChild でダミーに入れ替えて元に戻す方法がよさそう
要素が 1つだけなら remove + appendChild でもOK
「今のところ」なので ちゃんと animation させるメソッドが出てくることが期待です





裏ワザ

display:block をいったん none にするだけでは動かなかったですが それは最適化のおかげでいったん none にしたときに 表示部分は書き換えないからです
なら 表示部分を更新させればいいんじゃないかと getClientRects を使ってみます
getClientRects は表示部分の位置を返すので 表示部分の計算が行われます
これを多用すると 表示部分を毎回計算しない最適化が無意味になるので普段は使わないことが推奨されてますね

ですがあえてこれを使うことで none の状態に更新させます

animRA
function animStartCR(elem){
if(!elem) return
elem.style.display = "none"
elem.getClientRects()
elem.style.display = "block"
}

おぉ! 動きます!!
こんなシンプルなコードでできてます
replace でダミー作ったり元に戻してという操作がいらないです


私としてはこれが一番いい方法だと思ってます
表示部分の計算が必要な操作を入れるので 今後 replace でも最適化されて最終的に一緒ならアニメーションしなくなったとしても大丈夫ですし

でもまぁ裏ワザ感がすごいのでどうなんでしょうね