riot.jsで外部からクラス名を操作する時の注意点
アドベントカレンダー2021 Riot.js 1日目の記事です。
今年はRiot.jsのカレンダーが無くなってしまいそうでしたが、せっかく作成いただいたようなので参加させていただきます。
ちなみにriot.jsはv3が好きです。
本題
riot.jsは他のライブラリとも使用できるとされていますが、注意しないと理想の動作を得られない場合があります。今回は外部からクラス名を変更する場合について語りたいと思います。
例として下記のようなタグを作成しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<my-tag> <p class="message {color}">テスト</p> <style> .red{ color: red; } .blue{ color: blue; } </style> <script> tag = this; tag.color = 'red'; </script> </my-tag> |
“テスト”と表示されて、文字色が変わるだけのタグです。
p要素のクラス名に”message”とテンプレート変数”{color}”を指定しています。
実行結果
{color}の初期値は”red”なので、クラス名redが追加されています。
例えばこの要素に、外部のライブラリなどから”selected”というクラス名が追加されたとします。
クラス名”selected”はriotのテンプレートタグ内に記載されていないのため、将来的には削除される可能性がありますが、どのような時に削除されるかご存知でしょうか?
例えば、この状態でtag.update()
を実行します。
変化はありません。
次にtag.color = 'blue'
を実行しcolorの値を変えた後で、再びtag.update()
を実行します。
クラス名のred
がblue
に変わり、selected
が削除されました。
何が起きて削除されたのでしょうか?
動作原理
- テンプレート変数{}が使用された属性値はテンプレート変数部分だけではなく全体が書き換えられる。
- 前回のupdate時と評価結果が違う場合にのみDOMに反映される
[1]の部分については”message {color}”という書き方から{color}の部分のみを書き換えているのかと思っていましたが、属性値全体を書き換えています。つまり、テンプレート変数{}が使用された属性に外部から追加や削除を行うと、将来的に意図しない変更が起きる可能性があります。
逆に{}の使われていない部分は、更新によって書き換えられることはありません。
他のコードで示すと赤で囲まれ部分のみ変更され、他の部分には動的にjsで追加を行っても問題ありません。
[2]については元の値が”message red”、1度目のupdate後も”message red”で変化なし。2度目のupdate後は”message blue”と変更されたため、結果がdomに適用され”selected”も削除されました。
では、このような削除が起きないようにするにはどうしたら良いでしょうか。
解決策
別の属性でテンプレート変数を使用し、class属性ではテンプレート変数を使用しないようにします。こうすれば今後classの属性値がriotによって書き換えられることはありません。文字色の変更は、CSSを書く時に属性セレクターを使用することで行なえます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<my-tag> <p class="message" data-color="{color}">テスト</p> <style> [data-color="red"]{ color: red; } [data-color="bule"]{ color: blue; } </style> <script> tag = this; tag.color = 'red'; </script> </my-tag> |
属性で指定する場合、セレクターの記載が長くなってしまいますが、外部のライブラリ等とバッティングしてしまう場合は、このような対応をしてみてはいかがでしょうか。