変数宣言の場所と値の代入
JavaScriptのソースコードで、変数宣言とそれに値を代入する場所が整然と整理されていると、後で困らないで済みます。 検索・置換ツールの「S-R in Editor」は、かなりコードが複雑に肥大化して来たので、この際に変数関連の整理をしました。
以下は変数の整理に関する模式図です。
JavaScriptは「let」を変数の前に付けると変数を宣言したことになりますが、図の上半分の様に、関数Aと関数Bで同じ事を意味する変数「variableN」が重複する状態が生じがちです。 動作上は殆どの場合は問題がなく、多くのエラーは「変数の情報が欠損」している場合に生じるので、つい冗長なコードを書く傾向になります。
これを整理するのですが、変数を電話番号データに例えると、最初はAの電話機とBの電話機で別々に作っていたのを整理して、オンラインの電話番号データにしてしまうという感じです。 オンライン化により、A / B どちらの電話機でデータを登録や変更をしても、別の電話機でもその変更が反映されます。
プログラムの変数も、個別の場所で変数宣言をすると値が伝達されず、他の関数で値を取り込む手間が生じます。 この状態は、無駄なコード行が増えると同時に、実際と異なる旧い値を扱う間違いも生じ得ます。(緻密なコードを書いていれば、間違いは無くせますが)
こういう整理をすると、私はコードを省略し過ぎて、エラーでスクリプトが動作しない事が良くあります。 DevToolsのエラー報告を何度も見て、なんとかスムーズに動作する状態に整理が出来ました。
「HTML置換チェック」の問題
先日から苦心していた問題は、「置換チェック画面」で他のスクリプトのショートカットが実行されてしまう点でした。 Chromeは良いが Firefoxはかなり危ない状態でしたが、両ブラウザでこの問題についてはクリアーしました。
最終的に行き着いたのは、他のスクリプト用のショートカットをブラウザが受け取らない様に、このツールの側で先に受け取ってしまうという方法です。
想定されるショートカットは大変な数になりますが、多くのショートカットは「Ctrl」「Shift」「Alt」「Esc」等で始まり(私は「Pause」も使っていますが)、これらのキーを受け取ったら、「置換チェック画面」をOFFにして「UNDO」と同様の置換え前に戻します。 最初の「機能キー」をこのツールが消費してしまうので、テストした限りではそれらのキーで起動するツール等はありませんでした。
但し、これらの「機能キー」で「置換文字入力」の状態に戻るので、そこで再びショートカット(長押しを含む)を打てば、起動するツールが出て来ます。 とはいえ「置換」は「UNDO」されているので安心できます。
唯一、「Ctrl+F4」のブラウザウインドウを閉じる、「F5」のページのリロードは、どんな場合でも編集内容を失います。 これはツール以前の問題で注意が要ります。
入力枠の移動に「Tab」「Esc」を使う
テストをしていると、「検索文字」を入力から「置換文字」へ移動するのにマウスで「置換ボタン」を押す操作が入り、操作のもたつきを感じます。「HTML処理」が確定すると「置換ボタン」が押せる様になりますが、「Tab」キーで「置換文字」の入力に移動できる様にしました。 下が操作体系です。
最後に「置換チェック画面」が表示されますが、先に書いた様に「Tab」「Esc」「Ctrl」「Shift」「Alt」「Pause」を押すと「置換文字入力」段に戻り、そこで「Tab」「Esc」を押すと「検索文字入力」の最初の状態に戻ります。
「Tab」「Esc」「Enter」だけで全段階を移動できて、覚え易く便利です。
「S-R in Editor」 ver. 0.8
以下の「S-R in Editor」は、「検索語入力」~「TEXTのヒット文字列の巡回チェック」~「HTML置換・置換チェックとUNDO」が可能な、制作過程のツールです。
Chrome版 / Firefox版の「Tampermonkey」の新規作成編集枠に、以下のスクリプトコードをコピー&ペーストする事で、このテストコードを試すことが出来ます。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
〔 S-R in Editor 〕 ver. 0.8
// ==UserScript== // @name S-R in Editor // @namespace http://tampermonkey.net/ // @version 0.8 // @description 通常編集枠で実行できる 検索 / 置換 ツール // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/entry/srventry* // @grant none // ==/UserScript== window.addEventListener('load', function(){ let t_flag, h_flag; let buffer; let editor_iframe; let iframe_doc; let iframe_html; let iframe_body; let js_cover; let search_box; let search_word; let replace_box; let replace_word; let result_box; let r_select; let s_1; let s_2; let s_3; let s_4; let s_radio; let t_select; let ua=0; // Chromeの場合のフラグ let agent=window.navigator.userAgent.toLowerCase(); if(agent.indexOf('firefox') > -1){ ua=1; } // Firefoxの場合のフラグ let cke_1_contents=document.querySelector('#cke_1_contents'); // 監視 target let monitor=new MutationObserver(catch_key); monitor.observe(cke_1_contents, {childList: true}); // ショートカット待受け開始 catch_key(); function catch_key(){ search_box=document.querySelector('#search_box'); editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ //「通常表示」の場合 search_box=document.querySelector('#search_box'); if(search_box){ search_box.disabled=false; } document.addEventListener("keydown", check_key); // documentは先に指定 iframe_doc=editor_iframe.contentWindow.document; iframe_doc.addEventListener("keydown", check_key); // iframeは後に指定 function check_key(event){ if(event.ctrlKey==true){ if(event.keyCode==123){ event.preventDefault(); if(h_flag!=2){ // HTML置換時でなければ「Ctrl+F12」でON/OFF search_replace(); }}} if(event.keyCode==9 || event.keyCode==16 || event.keyCode==17 || event.keyCode==18 ||event.keyCode==19 || event.keyCode==27){ if(h_flag==2){ // HTML置換時に「Tab/Shift/Ctrl/Alt/Pause/Esc」を無効化 event.preventDefault(); out_h_flag_2();}}}} else{ //「HTML表示」の場合 if_html(); } function out_h_flag_2(){ js_cover.style.display='none'; cke_1_contents.style.zIndex='0'; if(ua==0){ editor_iframe.style.pointerEvents='auto'; } // 編集可能にする Chrome if(iframe_body){ iframe_body.contentEditable='true'; } // 編集可能にする iframe_body.innerHTML=buffer; // 置換処理をUNDO ⏹ s_2.style.cursor='pointer'; r_select.disabled=false; search_box.disabled=false; replace_box.disabled=false; s_3.style.display='none'; s_4.style.display='none'; replace_box.focus(); h_flag=1; } // 1=処理開始 function if_html(){ if(search_box){ search_box.disabled=true; } result_box.textContent=' '; s_1.style.display='none'; s_2.style.display='none'; replace_box.style.display='none'; replace_box.value=''; s_3.style.display='none'; s_4.style.display='none'; s_radio.style.display='none'; } } // catch_key function search_replace(){ let i_body=document.querySelector('body.l-body'); editor_iframe=document.querySelector('.cke_wysiwyg_frame'); if(editor_iframe){ //「通常表示」の場合 iframe_doc=editor_iframe.contentWindow.document; iframe_html=iframe_doc.querySelector('html'); iframe_body=iframe_doc.querySelector('.cke_editable'); } let css= '#s_container {position: fixed; top: 12px; left: calc(50% - 490px); '+ 'background: #fff; border: 1px solid #aaa; border-radius: 4px; '+ 'padding: 6px 15px; min-width: 948px; z-index: 10;}'+ '#search_box {width: 215px;} #replace_box {width: 215px; display: none;}'+ '::placeholder {font-size: 15px; color: #bbb;}'+ '#s_container input:disabled {color: #000; background: #eef1f3;}'+ '#s_container input {font-size: 16px; padding: 2px 6px 0; -moz-appearance: none;}'+ '#result {display: inline-block; min-width: 12px; padding: 4px 6px 2px; '+ 'margin-left: 5px; border: 1px solid #aaa; font-size: 16px;}'+ '.s_sw {display: inline-block; vertical-align: -9px; font-size: 15px; '+ 'padding: 5px 8px 2px; border: 1px solid #aaa; overflow: hidden;}'+ '.s_1 {margin-left: 15px; min-width: 4em; display: none;}'+ '.s_2 {margin: 0 5px 0 15px; cursor: pointer; display: none; position: relative;}'+ '.s_3, .s_4 {margin-left: 5px; color:#fff; background: #1e88e5; '+ 'cursor: pointer; display: none;}'+ '#r_select {position: absolute; top: -15px; opacity: .6; box-shadow: 0 0 0 6em #fff;}'+ '#r_select:checked {opacity: 1; box-shadow: 0 0 0 6em #d2eff4; z-index: -1;}'+ '.s_radio {font-size: 15px; line-height: 16px; padding-left: 10px; display: none;}'+ '.s_radio input{vertical-align: -2px; margin-left: 12px;}'+ '.js_cover {position: fixed; top: 0; width: 100%; height: 100%; '+ 'background: rgba(0, 0, 0, .6); z-index: 10; display: none;}'; let style_tag=document.createElement("style"); // css設定styleタグ style_tag.type="text/css"; style_tag.setAttribute("class", "ep"); style_tag.appendChild(iframe_doc.createTextNode(css)); if(i_body.querySelector('.ep')){ i_body.querySelector('.ep').remove(); } i_body.appendChild(style_tag); js_cover=document.createElement("div"); // クリック操作のブロックカバー js_cover.setAttribute("class", "js_cover"); if(i_body.querySelector('.js_cover')){ i_body.querySelector('.js_cover').remove(); } document.querySelector('#js-container').appendChild(js_cover); let css_iframe='.cke_editable m {background: #ffcc00;}'; // mタグの背景色指定 let style_tag_iframe=iframe_doc.createElement("style"); style_tag_iframe.type="text/css"; style_tag_iframe.setAttribute("class", "ep"); style_tag_iframe.appendChild(document.createTextNode(css_iframe)); if(iframe_html.querySelector('.ep')){ iframe_html.querySelector('.ep').remove(); } iframe_html.appendChild(style_tag_iframe); let s_container=document.querySelector('#s_container'); if(!s_container){ //#s_containerが無い場合 生成して開始 let insert_node_d=document.createElement('div'); insert_node_d.setAttribute('id', 's_container'); i_body.appendChild(insert_node_d); insert_node_d.innerHTML= '<input id="search_box" placeholder="検索文字" autocomplete="off">'+ '<span id="result"> </span>'+ '<span class="s_sw s_1"> </span>'+ '<label for="r_select" class="s_sw s_2">'+ '<input id="r_select" type="checkbox">置換</label>'+ '<input id="replace_box" placeholder="置換文字" autocomplete="off">'+ '<span class="s_radio"><input name="t_select" type="radio">一括処理'+ '<input name="t_select" type="radio">順次</span>'+ '<span><span class="s_sw s_3">OK</span>'+ '<span class="s_sw s_4">UNDO</span></span>'; search_box=document.querySelector('#search_box'); result_box=document.querySelector('#result'); r_select=document.querySelector('#r_select') replace_box=document.querySelector('#replace_box'); s_1=document.querySelector('.s_1'); s_2=document.querySelector('.s_2'); s_3=document.querySelector('.s_3'); s_4=document.querySelector('.s_4'); s_radio=document.querySelector('.s_radio'); t_select=document.getElementsByName('t_select'); t_select[0].checked=true; let count_t, count_h, t_index; let native_line=iframe_html.scrollTop; // 通常表示のスクロール位置を記録 search_box.focus(); search_box.onkeydown=function(event){ // 🔽 検索ツール操作の開始点 if(event.keyCode==13){ t_flag=0; // 0=検索前 h_flag=0; // 0=検索前 search_word=search_box.value; get_search(); // bufferを取得する rbox_disp(); } // bufferをリセットする if(event.keyCode==9){ //「Tab」で置換入力 event.preventDefault(); if(t_flag>0 || h_flag>0){ s_2.style.display='inline-block'; r_select.checked = true; replace_box.style.display='inline-block'; setTimeout(function(){ replace_box.focus();},10); }}} search_box.onchange=function(){ search_box.value=search_word; } //「Enter」を押さずに移動した場合は正確な検索語を表示 function get_search(){ editor_iframe=document.querySelector('.cke_wysiwyg_frame'); // ここで取得 if(editor_iframe){ //「通常表示」が実行条件 iframe_doc=editor_iframe.contentWindow.document; iframe_body=iframe_doc.querySelector('.cke_editable'); buffer=iframe_body.innerHTML; // ハイライト表示のためソースコードを保存 🟦 let childs=searchNodes(iframe_body); count_t=0; // テキストノードのヒット数 let result_t for(let k=0; k<childs.length; k++){ if(childs[k].nodeType==3){ result_t=childs[k].textContent.match(new RegExp(search_word, 'g')); if(result_t){ count_t+=result_t.length; }}} count_h=0; // HTMLソース全体のヒット数 let result_h=iframe_body.innerHTML.match(new RegExp(search_word, 'g')); if(result_h){ count_h=result_h.length; }}} function searchNodes(root){ // 全子孫ノードリストを作成 var list=[]; var search=function (node){ while (node !=null){ list.push(node); search(node.firstChild); node=node.nextSibling; }} search(root.firstChild); return list; } function rbox_disp(){ r_select.checked=false; search_box.disabled=false; replace_box.disabled=false; replace_box.value=''; if(count_t!=0 && count_h-count_t==0){ s_1.textContent='TEXT処理'; s_1.style.display='inline-block'; s_1.style.color='#000'; s_2.style.display='inline-block'; replace_box.style.display='none'; s_radio.style.display='none'; s_3.style.display='none'; s_4.style.display='none'; t_flag=1; // 1=処理開始 highlight(); } if(count_t!=0 && count_h-count_t!=0){ result_box.textContent='T:'+count_t+' H:'+(count_h-count_t); s_1.textContent='処理不能'; s_1.style.display='inline-block'; s_1.style.color='red'; s_2.style.display='none'; replace_box.style.display='none'; s_radio.style.display='none'; s_3.style.display='none'; s_4.style.display='none'; } if(count_t==0 && count_h-count_t!=0){ result_box.textContent='H:'+(count_h-count_t); s_1.textContent='HTML処理'; s_1.style.display='inline-block'; s_1.style.color='#000'; s_2.style.display='inline-block'; replace_box.style.display='none'; s_radio.style.display='none'; s_3.style.display='none'; s_4.style.display='none'; h_flag=1; // 1=処理開始 html_replace(); } if(count_t==0 && count_h-count_t==0){ result_box.textContent='T:'+count_t+' H:'+(count_h-count_t); s_1.textContent=' - - - '; s_1.style.display='inline-block'; s_1.style.color='#000'; s_2.style.display='none'; replace_box.style.display='none'; s_radio.style.display='none'; s_3.style.display='none'; s_4.style.display='none'; }} function highlight(){ replace_word='<m>'+ search_box.value +'</m>'; iframe_body.innerHTML= iframe_body.innerHTML.replace(new RegExp(search_word, 'g'), replace_word); let mark=iframe_body.querySelectorAll('m'); iframe_body.focus(); let k=0; view(0); result_box.textContent='T:'+count_t+'│1'; t_flag=2; // 2=巡回 while(k<mark.length && t_flag==2){ k=next(k); } function next(k){ iframe_doc.addEventListener("keydown", check_key); function check_key(event){ if(t_flag==2){ if(event.keyCode==37 || event.keyCode==38){ //「←」「↑」 event.preventDefault(); if(k>0){ k=k-1; } result_box.textContent='T:'+count_t+'│'+(k+1); view(k); return k; } if(event.keyCode==39 || event.keyCode==40){ //「→」「↓」 event.preventDefault(); if(k<mark.length -1){ k+=1; } result_box.textContent='T:'+count_t+'│'+(k+1); view(k); return k; } if(event.keyCode==13 || event.keyCode==27 ){ //「Enter」「Esc」 event.preventDefault(); search_box.focus(); } else{ event.preventDefault(); search_box.focus(); setTimeout( function(){ let keyEvent=new KeyboardEvent('keydown', {keyCode: 8}); search_box.dispatchEvent(keyEvent); },200); setTimeout( function(){ let keyEvent=new KeyboardEvent('keydown', {keyCode: 27}); search_box.dispatchEvent(keyEvent); },300); setTimeout( function(){ search_box.value=search_word; },400); }}} search_box.onfocus=function(){ if(t_flag==2){ result_box.textContent='T:'+count_t+'│-'; iframe_html.scrollTop=native_line; // 検索開始位置に戻る iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹ t_flag=1; }}} // 1=検索開始 function view(k){ mark[k].scrollIntoView(); i_body.scrollIntoView(); } iframe_doc.addEventListener("click", stop_out); // 編集画面のクリックで巡回終了 function stop_out(){ if(t_flag==1){ // 1=検索開始 native_line=iframe_html.scrollTop; } // クリックした場所をスクロール位置に指定 else if(t_flag==2){ // 巡回ループ内の場合 result_box.textContent='T:'+count_t+'│-'; native_line=iframe_html.scrollTop; // クリックした場所をスクロール位置に指定 iframe_body.innerHTML=buffer; // highlight を抜ける時はリセット ⏹ t_flag=1; }} // 1=検索開始 } // highlight() function html_replace(){ // HTML置換処理 r_select.onclick=function(){ if(r_select.checked){ replace_box.style.display='inline-block'; replace_box.focus(); } else{ replace_box.style.display='none'; replace_box.value=''; }} buffer=iframe_body.innerHTML; // HTML置換の処理前のソースコード保存 🟦 replace_box.focus(); html_roop(); function html_roop(){ replace_box.onkeydown=function(event){ // 🔽 置換操作の開始点 if(event.keyCode==13 && r_select.checked){ replace_word=replace_box.value; iframe_body.innerHTML= iframe_body.innerHTML.replace(new RegExp(search_word, 'g'), replace_word); js_cover.style.display='block'; cke_1_contents.style.zIndex='11'; if(ua==0){ editor_iframe.style.pointerEvents='none'; } // 編集不可にする Chrome iframe_body.contentEditable='false'; // 編集不可にする s_2.style.cursor='default'; r_select.disabled=true; search_box.disabled=true; replace_box.disabled=true; s_3.style.display='inline-block'; s_4.style.display='inline-block'; h_flag=2; } // 2=置換時 if(event.keyCode==9 || event.keyCode==27){ //「Tab」「Esc」で処理前に戻る event.preventDefault(); result_box.textContent=' '; s_1.style.display='none'; s_2.style.display='none'; r_select.checked=false; replace_box.style.display='none'; replace_box.value=''; search_box.focus(); h_flag=0; }} // 処理前 s_3.onclick=function(){ js_cover.style.display='none'; cke_1_contents.style.zIndex='0'; if(ua==0){ editor_iframe.style.pointerEvents='auto'; } // 編集可能にする Chrome iframe_body.contentEditable='true'; // 編集可能にする s_2.style.cursor='pointer'; r_select.disabled=false; search_box.disabled=false; replace_box.disabled=false; result_box.textContent=' '; // 検索結果は変更される s_1.style.display='none'; s_2.style.display='none'; r_select.checked=false; replace_box.style.display='none'; replace_box.value=''; s_3.style.display='none'; s_4.style.display='none'; search_box.focus(); h_flag=0; } // 検索前 s_4.onclick=function(){ js_cover.style.display='none'; cke_1_contents.style.zIndex='0'; if(ua==0){ editor_iframe.style.pointerEvents='auto'; } // 編集可能にする Chrome iframe_body.contentEditable='true'; // 編集可能にする s_2.style.cursor='pointer'; r_select.disabled=false; search_box.disabled=false; replace_box.disabled=false; s_3.style.display='none'; s_4.style.display='none'; iframe_body.innerHTML=buffer; // 置換処理をUNDO ⏹ replace_box.focus(); h_flag=1; } // 1=処理開始 } // html_roop() } // html_replace() } // #s_container が無い場合「Ctrl+F12」で開始 else{ // #s_container がある場合は「Ctrl+F12」で終了 document.querySelector('#s_container').remove(); } } // search_replace() })
「S-R in Editor ⭐」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。
●「S-R in Editor ⭐」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。