[Javascript] タグ登録機能サンプル「TagRegist」

インターネットを使ったサービスがスマートフォンの普及と合わせて爆発的に増えてきました。
色々な便利なサービスを安価や無料で便利に使えて、今では生活事態が激変する世の中になってきましたが、それぞれのサービスに個人情報を預けていたにも関わらず、簡単に「漏洩しました」というメールをもらって、対応策が「パスワードを変更してください」とだけ明記されていると、非常にイラッとしてしまうのは僕だけでしょうか?
無料で使えるサービスは、その裏で、個人情報以外でも、それぞれのユーザーの各種データを利用したデータ販売や、別会社とのデータ連携などで、マネタイズしているパターンがほとんどで、ユーザーは自分のデータが金になるというイメージはないですが、ユーザーの行動履歴などが100万件ぐらい貯まれば、それをお金を出してまで利用したいと考えている企業は多数あるという事なんですね。
データが命
「データは宝物」という事がわかったところで、システム開発を行っている人は設計に苦労している人も少なくないはずです。
それぞれのサービスによって、データの中身も違えば、データベース設計も全く異なりますが、利用するユーザーにとってはそんな事はどうでもいいのです。
ここで多くのサービスで悩ましい事として、「データ検索」というジャンルがあります。
データを沢山貯め込む事を目的にしているが、そのデータを呼び出す仕組みが乏しいサイトもまた山のようにあります。
データ検索って、一言で言っても、様々なアプローチがあり、UI/UXと密接に関係してくるほど、ユーザビリティに関連しているので、安易に考えているサービス構築者がいたとしたらそのサービスは恐らく「使いにくいサービス」として出来上がってしまうでしょう。
何故データ検索が難しいかと言うと、データは、「登録」→「蓄積」→「保持」→「検索」→「再利用」という流れでシステム内部で活用されていきますが、「検索」の部分がシステムの裏側で行う場合はユーザーは何も意識しなくてもいいのですが、これをユーザーが検索を行なう時によく問題が起きがちです。
データ検索あれこれ
データ検索で、非常によく出来ているサービスでいうと、個人的にはGmailが思い出されます。
意識して使っている人も少ないかもしれませんが、Gmailはスマートフォンのメールアプリで使っているだけでは、他のメールサービスと何にも変わらない機能しか使うことができませんが、ブラウザで利用すると、便利な検索入力欄が画面上部にあって、ここでの検索機能というのが「流石Google!!!」というレベルの品質で毎回感動させられてしまいます。
スマホでもやろうと思えば検索できますが、PCブラウザほどの利便性を感じられないのが残念ですね。
恐らくほとんどの人がキーワード検索として、メールアドレスや、何かの単語を検索しているのがほとんどだと思いますが、日付による期間検索や、既読済みと未読の振り分け、もっと特殊な内部データの絞り込みも可能になるため、知らない人はリファレンスページを一度読んで置くと、仕事などでの効率化に繋がりますよ。
https://support.google.com/mail/answer/7190?hl=ja
他にも、不動産業界や旅行業界のホームページも、非常に複雑で、多くの項目を対象とした検索機能を搭載しています。
検索UI/UXについて研究をしたいと考えている人は、是非とも業界別の特色と、EFOについての関連を考えてみるといいでしょう。
そんな中、Wordpressなどでも手軽に利用できる「タグ」という機能がどうやら安易にデータ管理をする仕組みとして標準とも言えるかもしれません。
そういえば、Gmailでも「ラベル」という機能が「タグ」と同じ要素にあたりますね。
今回は、ホームページで利用できる簡単な機能スニペットを用意しておいたので、プログラマーの方は気に入ってもらえたら利用してみてください。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!DOCTYPE html> <html> <head> <title>jsLoad</title> <link rel="stylesheet" href="tagRegist.css"> <script src="tagRegist.js"></script> </head> <body> <div class="tagRegist type-1"></div> <div class="tagRegist type-2"> <span class="tag">tag-3</span> <span class="tag">tag-4</span> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
.tagRegist{ width:300px; min-height:50px; border:1px solid #666; border-radius:4px; padding:8px; margin:8px; overflow:hidden; } .tagRegist .tag{ display:inline-block; white-space:nowrap; font-size:12px; background-color:red; color:white; padding:4px 2px 4px 8px; border-radius:2px; margin:2px; line-height:16px; } .tagRegist .tag .tagDel{ position:relative; display:inline-block; width:16px; height:16px; vertical-align:middle; background-color:white; margin-left:4px; margin-bottom:2px; background-color:transparent; cursor:pointer; /* border:1px solid white; */ border-radius:50%; } .tagRegist .tag .tagDel:before, .tagRegist .tag .tagDel:after{ content:""; position:absolute; top:50%; right:25%; display:block; width:50%; height:1px; background-color:white; transform-origin:center; } .tagRegist .tag .tagDel:before{ transform:rotate(45deg); } .tagRegist .tag .tagDel:after{ transform:rotate(-45deg); } .tagRegist .tag .tagDel:hover{ opacity:0.5; } .tagRegist .tagInput{ font-size:12px; min-width:80px; width:80px; padding:4px; outline: 0; background-color:transparent; /* border:1px solid red; */ border:0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
;(function(){ // イベントライブラリ var $$event = function(target, mode, func){ //other Browser if (typeof target.addEventListener !== "undefined"){ target.addEventListener(mode, func, false); } else if(typeof target.attachEvent !== "undefined"){ target.attachEvent('on' + mode, function(){func.call(target , window.event)}); } }; // check-onload var $$ = function(){ switch(document.readyState){ case "complete": this.loaded(); break; case "interactive": $$event(window , "DOMContentLoaded" , (function(e){this.loaded(e)}).bind(this)); break; default: $$event(window , "load" , (function(e){this.loaded(e)}).bind(this)); break; } }; // proc-loaded $$.prototype.loaded = function(){ this.setTagRegist_input(); this.setDeleteIcon(); }; $$.prototype.setTagRegist_input = function(){ var tagRegists = document.querySelectorAll(".tagRegist"); for(var i=0; i<tagRegists.length; i++){ var input = document.createElement("input"); input.type = "text"; input.className = "tagInput"; input.placeholder = "add a tag."; // input.onkeyup = (function(e){this.tagInputWidth(e)}).bind(this); $$event(input , "keyup" , (function(e){this.tagInputWidth(e)}).bind(this)); $$event(input , "keypress" , (function(e){this.tagInputEnter(e)}).bind(this)); tagRegists[i].appendChild(input); $$event(tagRegists[i] , "click" , (function(e){$$.prototype.clickBase(e)}).bind(this)); } }; $$.prototype.setDeleteIcon = function(){ var tags = document.querySelectorAll(".tagRegist .tag"); for(var i=0; i<tags.length; i++){ var delIcon = tags[i].querySelector(".tagDel"); if(delIcon){continue;} var delIcon = document.createElement("span"); delIcon.className = "tagDel"; $$event(delIcon , "click" , (function(e){ var tag = e.currentTarget.parentNode; this.delTag(tag); }).bind(this)); tags[i].appendChild(delIcon); } }; $$.prototype.clickBase = function(e){ var target = e.target; if(target.className.indexOf("tagRegist") === -1){return;} var input = target.querySelector(".tagInput"); if(!input){return;} input.focus(); }; $$.prototype.tagInputEnter = function(e){ var target = e.currentTarget; // enter if(e.keyCode == 13){ var base = target.parentNode; this.addTag(base , target.value); // target.style.setProperty("width" , "auto" , ""); } }; $$.prototype.addTag = function(base , value){ if(!base || !value){return;} var input = base.querySelector(".tagInput"); if(!input){return;} // check if(this.checkTagValue(base,value)){return;} // add var span = document.createElement("span"); span.className = "tag"; span.textContent = value; base.insertBefore(span , input); input.value = ""; this.setDeleteIcon(); this.saveData_add(value); }; $$.prototype.delTag = function(tagElement){ console.log(tagElement); var value = tagElement.textContent; tagElement.parentNode.removeChild(tagElement); this.saveData_del(value); }; // return @ [true:重複有り : false:重複無し] $$.prototype.checkTagValue = function(base , value){ if(typeof value === "string" && value === ""){return false;} var tags = base.querySelectorAll(".tag"); var flg = false; for(var i=0; i<tags.length; i++){ if(tags[i].textContent === value){ flg = true; } } return flg; }; $$.prototype.tagInputWidth = function(e){ var input = e.currentTarget; input.style.setProperty("width" , "" , ""); if(input.value !== ""){ input.style.setProperty("width" , input.scrollWidth + "px" , ""); } }; $$.prototype.loadData = function(){ }; $$.prototype.saveData_add = function(value){ // console.log("add"); }; $$.prototype.saveData_del = function(value){ // console.log("del"); }; new $$; })(); |
class=”tagRegist”が付いている箇所を設置すれば、ページ内に複数箇所のタグ登録を設置できるようにしていますが、サーバー側の登録などはシステム連携の部分になるので、jsファイルの”saveData_add”と”saveData_del”にajaxでサーバーにデータ送信をする記述をそれぞれで行ってください。
今回は、見た目のタグ登録部分だけにしておきます。
登録ができたタグは、それを付随するデータ登録と連携して、検索の時に登録されているタグを表示して任意の検索を可能にしてあげるといいでしょう。
色々なシステムで毎回個別に作っていたのですが、今回テンプレート版としてコピペして少し変更すれば使えるバージョンを作っておきました。
こういうマイクロシステムは重要ですね〜