タグ入力フォームを作る
Webサービスでよくタグを入力する機能がよくあると思います。Qiitaにも入力するところがあると思います。Qiitaではタグを作ると入力した言語の背景がかわってとてもいい感じです。
自分でも作ってみたいと思い試行錯誤してみました。Qiitaとか参考にすればいいとあとで気づいたのであまり筋のいい解では無いと思いますが、こういう実装もあるんだよ程度に思って頂けると幸いです。
動作
動作の様子です。スタイルが適当なのは勘弁してください。
少しわかりにくいのですがエンターキーでタグを確定しています。となりのx
を押すとタグが削除されます。
実装
<div class="text-area">
<div class="tag-list">
<span class="tag" v-for="tag in tags">
{{tag.name}}
<span class="delete" v-on:click="delete(this)">x</span>
</span>
</div>
<div class="input-area" contenteditable=true v-on:keydown.enter="decide_tag">
</div>
</div>
.text-area{
width: auto;
height: 20px;
background: white;
}
.tag-list{
display: inline-block;
margin-top: 0px;
}
.input-area{
display: inline-block;
width: 100px;
height: 20px;
background: white;
&:focus{
outline: 0;
}
}
.tag {
padding: 2px;
background: yellow;
}
.delete {
cursor: pointer;
}
Vue.jsを用いているのでディレクティブがついています。
キモはタグのリストと入力フォームを分けている点で、これでタグが増えたら入力フォームが自動的に右側にスライドします。
input
タグは使わずdiv
のcontenteditable
をtrue
にして入力を可能にしています。これだと入力フォームっぽくならないので全体を包んでいるタグの背景を白くしています。
$(document).ready(function() {
var tag = new Vue({
el: ".text-area",
data: {
tags: []
},
methods: {
decide_tag: function(e) {
this.tags.push({
name: e.target.textContent
});
e.target.textContent = "";
},
delete: function(vm) {
this.tags.splice(vm.$index,1);
}
}
});
});
Vueのほうは簡単に実装しています。タグの一覧をdata
のプロパティに配列としてもって、エンターキーを押した時に発火するdecide_tag
でpush
しています。タグを消すときはタグの配列をsplice
して削除しています。
あんまりいけてないのはinput
タグを使っていないのでv-model
が使えないことで、v-model
だとdecide_tag
でe.target.textContent
からデータをとらないですむはずです。本来はViewModelからViewを参照するのはよくないので設計がよくないと言えますね(ならやるなってね)。
どうしてinput
を使いたくなかったかというとinput
を使うならinput
を覆うオーバレイにタグを描画してinput
を隠す必要があるからで、何となくそれが気に入らなかったからです。とはいえそうすると設計的によくない実装になるので今となってはオーバレイする方法がよかったかもですね。
あとinputを使わなかったことでform
のsubmit
が使えないです。タグを入力し次第保存する方針でなければinput
を使った方が素直で良いですね。
おわり
以上Vue.jsで作るタグ入力フォームでした。自分で書いていてなんですけどあんまりいけてませんね。入力するところが右側にスライドするところなんかはうまくいったかなと思ったのですが。
もう少し筋の良いやり方でリベンジしたいところです。