はじめに
今回は、趣味としてちびちびスキルアップしてきたプログラミングで、何か作ってみようと考えた末に作った、自分用のツールです。
やはり形になるものを1つ作ると、考案から完成までの流れが理解しやすくなるなぁと思いました。
これまで、バッチファイルから始めて、HTML/CSS/JavaScript/Pythonを触ってきましたが、今回はHTML/CSS/JavaScriptを使いました。とりあえず、作ったものと参考にしたURLとかを備忘録的に書いておこうかと思います。
きっかけ
- 趣味としてかじっていたプログラミングを使って何か作りたかった
- 連番が出てくるコードはたくさんあるが、for文やEmmetのような便利なツールを使えない/使いたくない状況で、いちいちコピペして数字を打つのが面倒だと感じた場面が最近あった
目標とするもの
例えば、
example.css
bodyinput[num="1"]>span#index_1bodyinput[num="2"]>span#index_2bodyinput[num="3"]>span#index_3bodyinput[num="4"]>span#index_4bodyinput[num="5"]>span#index_5
こういうのを作るのを楽にしたい。
プラン立て・HTML/CSSで骨組み作り
プランを立てる大切さ
完成形のイメージを持って開発を行うことはとても大切。今回はHTML/CSS/JavaScriptを使用したが、しっかりとしたプランを立てておくことで、ID名やクラス名、属性名を分かりやすいものにし、その後のメンテナンスもしやすくなるようにできる。
今回のプラン
[共通の文字列]{連番}[共通の文字列]{連番}・・・のような構造になっているので、それにあわせて設計する。
[文字列]{連番}[文字列]という形を基本として、必要な分だけ[文字列]{連番}の組み合わせを追加できるようにする。
骨組みの作り上げ
完成体の体が見えてきたため、HTML/CSSを書き上げる。基本的にすべてのパーツをJavaScriptでの処理に使用するので、ID名などは分かりやすいものをつけることを心がけた。
参考 : CSS クラス名リスト
ID/クラス名の名付け方がとても参考になる。
また、フリーのアイコン素材を配布しているサイトはたくさんあるので、とりあえず探せば見つかる。(今回はHTMLファイルに埋め込んだ)
リップルエフェクト(クリックしたら波紋が出るあれ)の導入も行ったので、それについてもいつかまとめたい。
index.html
index.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>連番サポート</title><linkrel="stylesheet"href="main.css"><linkrel="icon"href="./img/icon16.png"sizes="16x16"type="image/png"><linkrel="icon"href="./img/icon32.png"sizes="32x32"type="image/png"><linkrel="icon"href="./img/icon48.png"sizes="48x48"type="image/png"><linkrel="icon"href="./img/icon64.png"sizes="64x64"type="image/png"></head><body><divid="content"><divid="primary"><textareaname="String_0"id="String_0"></textarea><divid="Number_1"><inputtype="number"name="Number_1_start"value="1">=><inputtype="number"name="Number_1_end"value="1"></div><textareaname="String_1"id="String_1"></textarea><div><svgid="plus"x="0px"y="0px"viewBox="0 0 512 512"tabindex="0"><g><pathclass="st0"d="M359.244,224.004h-59.988c-6.217,0-11.258-5.043-11.258-11.258v-59.992c0-6.215-5.039-11.254-11.256-11.254
h-41.486c-6.217,0-11.258,5.039-11.258,11.254v59.992c0,6.215-5.039,11.258-11.256,11.258h-59.988
c-6.219,0-11.258,5.039-11.258,11.258v41.484c0,6.215,5.039,11.258,11.258,11.258h59.988c6.217,0,11.256,5.039,11.256,11.258
v59.984c0,6.219,5.041,11.258,11.258,11.258h41.486c6.217,0,11.256-5.039,11.256-11.258v-59.984
c0-6.219,5.041-11.258,11.258-11.258h59.988c6.217,0,11.258-5.043,11.258-11.258v-41.484
C370.502,229.043,365.461,224.004,359.244,224.004z"></path><pathclass="st0"d="M256,0C114.613,0,0,114.617,0,256c0,141.387,114.613,256,256,256c141.383,0,256-114.613,256-256
C512,114.617,397.383,0,256,0z M256,448c-105.871,0-192-86.129-192-192c0-105.867,86.129-192,192-192c105.867,0,192,86.133,192,192
C448,361.871,361.867,448,256,448z"></path></g></svg><svgid="minus"x="0px"y="0px"viewBox="0 0 512 512"class="disabled"tabindex="0"><g><pathclass="st0"d="M359.77,224.004H152.228c-5.928,0-10.732,4.804-10.732,10.73v42.535c0,5.926,4.805,10.734,10.732,10.734
H359.77c5.928,0,10.732-4.809,10.732-10.734v-42.535C370.502,228.808,365.697,224.004,359.77,224.004z"></path><pathclass="st0"d="M256,0C114.613,0,0,114.617,0,256c0,141.387,114.613,256,256,256c141.383,0,256-114.613,256-256
C512,114.617,397.383,0,256,0z M256,448c-105.871,0-192-86.129-192-192c0-105.867,86.129-192,192-192c105.867,0,192,86.133,192,192
C448,361.871,361.867,448,256,448z"></path></g></svg></div></div><divid="secondary"><divclass="submit_form"><buttonid="apply"class="submit">実行</button><buttonid="reset"class="submit">リセット</button></div><dl><dt>結果</dt><dd><textareaname="result"id="result"></textarea></dd><dt>結果をクリップボードにコピー</dt><dd><buttonid="clip">コピー</button></dd></dl></div></div></body><script src="main.js"></script></html>
main.css
main.css
@importurl('https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap');body{background-color:#222222;color:white;margin:20px0020px;display:flex;justify-content:center;}textarea,input[type="number"]{background-color:#333333;border:1pxsolid#666;color:white;margin:15px0;}textarea{padding:10px;width:20em;height:5em;display:block;font-family:'Source Code Pro',monospace;}input[type="number"]{width:7em;padding:3px;margin:015px;}textarea:hover,input[type="number"]:hover{border:1pxsolid#ffa6008e;outline:none;}textarea:focus,input[type="number"]:focus{border:1pxsolidorange;outline:none;}svg{fill:white;width:20px;height:20px;cursor:pointer;}svg:hover{fill:#ffa6008e;}#plus{margin:1em1em06.5em;}#minus{margin:1em6.5em01em;}#minus.disabled,#minus.disabled:hover{fill:gray;cursor:inherit;}.submit_form{margin:00030px;}.submit{color:white;width:80px;height:80px;background-color:transparent;border:1pxsolid#666;border-radius:50%;outline:none;margin:20px030px20px;display:inline-block;position:relative;z-index:1;overflow:hidden;}.submit:hover{border:1pxsolid#ffa6008e;}.submit:focus{border:1pxsolidorange;}#secondary{margin:40px000;}dl{margin-top:40px;}dd{margin-left:0;}#result{width:20em;height:15em;}@mediascreenand(min-width:768px){body{display:block;}#content{display:flex;}#secondary{margin:00080px;}#result{width:40em;height:20em;}}.ripple{position:absolute;width:0;height:0;border:none;border-radius:50%;background-color:#ffa600e1;z-index:5;pointer-events:none;animation-name:fadeOut;animation-duration:.5s;animation-fill-mode:none;}@keyframesfadeOut{0%{opacity:1;width:0px;height:0px;border:none;}40%{opacity:.3;}100%{opacity:0;width:100px;height:100px;transform:translateX(-50px)translateY(-50px);}}#clip{background-color:transparent;color:white;border:1pxsolid#666;border-radius:5px;outline:none;width:64px;height:24px;margin:10px0015px;position:relative;z-index:1;overflow:hidden;}#clip:hover{border:1pxsolid#ffa6008e;}#clip:focus{border:1pxsolidorange;}
JavaScriptでメイン処理を書く
アイコン周りのプラン
+アイコンで[文字列]{連番}のブロックを一つ追加
-アイコンで削除 (ただし初期状態のみこれ以上削除できないよう、クリックできないようにしている)
HTML要素の追加には、.appendChild()等を使っても良いのだが、今回は.insertAdjacentHTML()を使用した。パフォーマンス面で優秀らしい。(innerHTML より insertAdjacentHTML を使うより)今回は特にパフォーマンスが要求される場面ではないが、今後はよく使用することになりそう。
main_前半.js
letelement_count=1;document.querySelector('svg#plus').addEventListener('click',function(){if(element_count===1){document.getElementById('minus').classList.remove('disabled');}element_count+=1;letadding_element='<span class="block_content" id="Number_'+element_count+'"><input type="number" name="Number_'+element_count+'_start" value="1">=><input type="number" name="Number_'+element_count+'_end" value="1"></span><textarea name="String_'+element_count+'" id="String_'+element_count+'" class="block_content"></textarea>'document.querySelector('#String_'+(element_count-1)).insertAdjacentHTML('afterend',adding_element);})document.querySelector('svg#minus').addEventListener('click',function(){if(element_count!==1){document.querySelector('span#Number_'+element_count).remove();document.querySelector('#String_'+element_count).remove();if(element_count===2){document.getElementById('minus').classList.add('disabled');}element_count-=1;}})
メイン処理のプラン
まず、例えば1-10の連番を振る際に、すべての欄を1-10にセットして・・・とするのは面倒なので、指定された数値範囲が最も大きいものに合わせて連番を振るようにした。
つまり、下のように、1カ所のみ1-10の範囲指定をすれば、それに合わせて連番の範囲が自動的に判断され、あとは左側の数字に合わせてそれぞれ連番を振っていけるようにした。
こんな感じでコーディング。。。
main.js
window.addEventListener('load',function(){letelement_count=1;document.querySelector('svg#plus').addEventListener('click',function(){if(element_count===1){document.getElementById('minus').classList.remove('disabled');}element_count+=1;letadding_element='<span class="block_content" id="Number_'+element_count+'"><input type="number" name="Number_'+element_count+'_start" value="1">=><input type="number" name="Number_'+element_count+'_end" value="1"></span><textarea name="String_'+element_count+'" id="String_'+element_count+'" class="block_content"></textarea>'document.querySelector('#String_'+(element_count-1)).insertAdjacentHTML('afterend',adding_element);})document.querySelector('svg#minus').addEventListener('click',function(){if(element_count!==1){document.querySelector('span#Number_'+element_count).remove();document.querySelector('#String_'+element_count).remove();if(element_count===2){document.getElementById('minus').classList.add('disabled');}element_count-=1;}})document.getElementById('apply').addEventListener('click',(ev)=>{document.getElementById('apply').insertAdjacentHTML('afterbegin','<span class="ripple" style="left: '+ev.offsetX+'px; top: '+ev.offsetY+'px;"></span>');letwidth_loop=[];for(leti=1;i<=element_count;i++){width_loop.push(Math.abs((document.querySelector('input[name="Number_'+i+'_start"]').value-document.querySelector('input[name="Number_'+i+'_end"]').value)));}letloopNumber=Math.max.apply(null,width_loop);setResult(loopNumber);})document.getElementById('reset').addEventListener('click',(ev)=>{document.getElementById('reset').insertAdjacentHTML('afterbegin','<span class="ripple" style="left: '+ev.offsetX+'px; top: '+ev.offsetY+'px;"></span>');letallTextarea=document.querySelectorAll('#primary textarea');for(textareaofallTextarea){textarea.value='';}})functionsetResult(loopNumber){letresult=[];for(letn=0;n<=loopNumber;n++){letresult_string='';for(leti=0;i<=element_count;i++){if(i!==element_count){result_string=result_string+document.querySelector('textarea[name="String_'+i+'"]').value+(n+Math.min(document.querySelector('input[name="Number_'+(i+1)+'_start"]').value,document.querySelector('input[name="Number_'+(i+1)+'_end"]').value)).toString();}else{result_string=result_string+document.querySelector('textarea[name="String_'+i+'"]').value;result.push(result_string);}}}document.getElementById('result').innerHTML=result.join('\n');}document.getElementById('clip').addEventListener('click',(ev)=>{document.getElementById('clip').insertAdjacentHTML('afterbegin','<span class="ripple" style="left: '+ev.offsetX+'px; top: '+ev.offsetY+'px;"></span>');document.getElementById('result').select();document.execCommand("copy");})})
完成
そんな感じでできたのがこれです。
連番サポート (on GitHub)