ラジオボタンの実装コード
ラジオボタンは複数の選択肢の中で「1個」だけを選択するインターフェイスに適したinput要素です。 コードの書き方は色んなページにサンプルがありますが、私は少し判っていなかった事があり、饒舌なコードを書いていました。
ラジオボタンを実装したのは下の「コメント設定」のメニュー画面です。
コードの制作時は、このページで DevTools を起動して、
「Application → Storage → Cookies → https://blog.ameba.jp」
を選択すると、このページのCookie一覧が表示されます。
「Comment C-O」は 「approved」「pending」のNameでボタン選択を「0 or 1」の値で記録しています。 この変化を観測するとスクリプトの動作が判ります。 ただし、Cookieの一覧表の「 」ボタンを押さないと、ラジオボタン操作で更新された値が反映しないので、この点は注意が要ります。
ファットなコードとスマートなコード
下は要らない記述(問題は生じない不要コード)を沢山含んだファットなコードです。 ラジオボタンが判っておらず、なんとか使える動作にしようとした結果で、出来上がったコードです。
このコードの動作を調べると、下線の部分が不要でした。 不要な記述を実際に削除すると、下の様にとてもシンプルになります。
ラジオボタンのコードから判った事
上の様な不要な記述の多いコードを作ってしまったのは、ラジオボタンの特質の理解がとてもあいまいだったからです。
上記コードは「onchange」を状態変化(ユーザーの選択操作)を調べて動作していますが、これに関しては正解。「checked」は状態を取得したり変更できますが、ユーザー操作をトリガーにする事は出来ません。「addEventListener」と同じ動的なコードには「onchange」が必要です。(onclick等を使うコードも可能です)
● ラジオボタンは選択肢のボタンが複数あるが、全体で1個のボタンと考えるべき
● 押されたラジオボタンは必ず選択となり、これは「onchange」で取得できる。
(既に選択されたボタンをクリックしても「onchange」で取得できないはず)
● 他のボタンが押されて自動的に非選択になったボタンの場合、そのボタンに設定された「onchange」で「非選択」に変化した事をトリガーにできない。 つまり、ラジオボタンの「onchange」は、「選択」されたイベントのみを取得する。
以上から、押されなかった場合についての記述は意味がない。 全てのボタンについて、押された事を「onchange」で取得した記述が必要です。
この例で、片側のボタンを取得して、それを「OFF」にする事は「checked=false」の指定で可能ですが、そのスクリプトは反対側のボタンを「ON」にした事になりません。 あくまで、反対のボタンを取得して「checked=true」の指定が必要です。
要するに
ラジオボタンでユーザーの操作に動的に反応するには、全てのボタンを取得した上で、押した事に「onchange」で対応するコードを各ボタンで書く必要があります。
そして、そのコードで「chexked==true」を判定する必要なく、「onchange」でトリガーされた場合は「true」は自明という事になります。
リストの初期設定に全文表示を追加
前ページでは、リスト初期表示の設定で「コメント表示」を選択した場合は、本文の高さの上限を 300pxとしていました。 しかし、ユーザーが「コメント表示枠」のリサイズ操作を好まない場合があり得るので、初期表示が「コメント全文表示」の選択肢を設けました。 この設定は、アメブロのデフォルト仕様と同じになります。
● これらの「初期表示」の設定は、「承認待ち」「公開済み」の画面を開いた初期のリストと、「▼」ボタンでリストのコメント本文を開いた場合に適用されます。
●「返信する」ボタンでコメントを開いた場合は、常に「制限表示」になります。 これは返信の編集時の枠のレイアウトを想定しています。
●「▲」ボタンは、常にコメントを最小の状態に閉じます。
「Comment C-O」 ver. 0.4
「Comment C-O」は「コメント管理」ページのコメントリストの操作性を改善するツールです。
◎ このツールの使用には「Ameblo Management ver.2020.10.30」以降の適用が必須です。「Ameblo Management」の導入方法は以下のページを参照ください。
ツールを導入するには
「Comment C-O」は Chrome / Edge / Firefox の拡張機能の「Tampermonkey」上で動作します。
❶「Tampermonkey」を導入します
◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。
既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。
拡張機能の導入については、以下のページに簡単な説明があるので参照ください。
❷「Tampermonkey」にスクリプトを登録します
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 Comment C-O 〕 ver. 0.4
// ==UserScript== // @name Comment C-O // @namespace http://tampermonkey.net/ // @version 0.4 // @description コメント管理画面のリスト開閉機能 // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/comment/comment* // @noframes // @run-at document-start // @grant none // ==/UserScript== window.addEventListener('DOMContentLoaded', function(){ environ(); set_items(); list_mode(); function environ(){ let css= '.userList__item { position: relative; } '+ '.userList__item:focus { outline: 2px solid #2196f3; } '+ '.userList__text { display: none; } '+ '.userList__information { top: 42px; right: 330px; } '+ '.userList__buttons { top: 38px; right: 76px; } '+ '.co_view { width: 29px; height: 29px; margin-left: 16px; color: #888; '+ 'font: 16px/30px Meiryo; padding: 0; border: 1px solid #aaa; '+ 'border-radius: 4px; background: #fff; cursor: pointer; } '+ '.item_close { position: absolute; margin: 0 !important; } '+ '.userList__tooltip { z-index: 1; }'; let style=document.createElement('style'); style.setAttribute('id', 'co_list'); style.innerHTML=css; let target=document.querySelector('body'); if(!target.querySelector('#co_list')){ target.appendChild(style); }} function set_items(){ let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ items[k].setAttribute('tabindex', '0'); let information=items[k].querySelector('.userList__information'); let buttons=items[k].querySelector('.userList__buttons'); set_view(buttons); information.classList.add('item_close'); buttons.classList.add('item_close'); } function set_view(elem){ let view=document.createElement('button'); view.setAttribute('type', 'button'); view.setAttribute('class', 'co_view'); view.textContent='▼'; if(!elem.querySelector('.co_view')){ elem.appendChild(view); }}} function list_mode(){ let tab_item=document.querySelectorAll('.tabs .tabs__item'); if(tab_item[0] && tab_item[0].classList.contains('is-active')){ // 公開済み画面 let approved=get_cookie('approved'); if(approved!=0){ // approved: 1:制限表示 2:全文表示 let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let view_button=items[k].querySelector('.co_view'); let text=items[k].querySelector('.userList__text'); text.style.display='block'; height_g(text, approved); view_button.textContent='▲'; }} document.cookie='approved='+approved+'; Max-Age=2592000'; } if(tab_item[1] && tab_item[1].classList.contains('is-active')){ // 承認待ち画面 let pending=get_cookie('pending'); if(pending!=0){ // pending: 1:制限表示 2:全文表示 let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let view_button=items[k].querySelector('.co_view'); let text=items[k].querySelector('.userList__text'); text.style.display='block'; height_g(text, pending); view_button.textContent='▲'; }} document.cookie='pending='+pending+'; Max-Age=2592000'; } } // list_mode() }); // window.addEventListener window.addEventListener('load', function(){ open_edit(); open_read(); close(); text_focus(); read_memo(); memo(); function open_edit(){ let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let reply_button=items[k].querySelector('.js-comment-reply-button'); if(reply_button){ reply_button.addEventListener('click', function(){ let text=items[k].querySelector('.userList__text'); let information=items[k].querySelector('.userList__information'); let buttons=items[k].querySelector('.userList__buttons'); text.style.display='block'; information.classList.remove('item_close'); buttons.classList.remove('item_close'); height_g(text, 1); items[k].scrollIntoView({block: "center"}); items[k].setAttribute('tabindex', ''); }); }}} function open_read(){ let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let view_button=items[k].querySelector('.co_view'); if(view_button){ view_button.addEventListener('click', function(){ toggle(items[k], view_button); }); }} function toggle(item, sw){ let open_limit; let tab_item=document.querySelectorAll('.tabs .tabs__item'); if(tab_item[0] && tab_item[0].classList.contains('is-active')){ // 公開済み画面 open_limit=get_cookie('approved'); } else if(tab_item[1] && tab_item[1].classList.contains('is-active')){ // 承認待ち画面 open_limit=get_cookie('pending'); } let text=item.querySelector('.userList__text'); if(sw.textContent=='▼'){ text.style.display='block'; sw.textContent='▲'; height_g(text, open_limit); } else{ text.style.display='none'; sw.textContent='▼'; item.focus(); }}} function close(){ let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let reply_cancel=items[k].querySelector('.js-comment-reply-cancel'); if(reply_cancel){ reply_cancel.addEventListener('click', function(){ let text=items[k].querySelector('.userList__text'); let information=items[k].querySelector('.userList__information'); let buttons=items[k].querySelector('.userList__buttons'); text.style.display='none'; information.classList.add('item_close'); buttons.classList.add('item_close'); let view_button=items[k].querySelector('.co_view'); if(view_button.textContent=='▲'){ view_button.textContent='▼'; } items[k].setAttribute('tabindex', '0'); items[k].focus(); }); }}} function text_focus(){ let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let text=items[k].querySelector('.userList__text'); let resizeObserver=new ResizeObserver(recover_scroll); resizeObserver.observe(text); function recover_scroll(){ let textarea=items[k].querySelector('.formGroup__textarea'); if(textarea){ textarea.blur(); }}}} function memo(){ let sendbutton=document.querySelectorAll('.commentList .sendButton'); for(let k=0; k<sendbutton.length; k++){ sendbutton[k].addEventListener('mousedown', function(){ let comm_id=sendbutton[k].getAttribute('data-comment-id'); document.cookie='comm_id='+comm_id + '; Max-Age=20'; }); }} function read_memo(){ let comm_id=get_cookie('comm_id'); let items=document.querySelectorAll('.userList__item'); for(let k=0; k<items.length; k++){ let sendbutton=items[k].querySelector('.commentList .sendButton'); if(sendbutton){ let id=sendbutton.getAttribute('data-comment-id'); if(id==comm_id){ items[k].focus(); }}}} }); // window.addEventListener function height_g(elem, limit){ let s_height=elem.scrollHeight; if(s_height<300){ elem.style.height='auto'; } else{ if(limit!=2){ elem.style.height='300px'; } else if(limit==2){ elem.style.height='auto'; }}} function get_cookie(name){ let cookie_req=document.cookie.split('; ').find(row=>row.startsWith(name)); if(cookie_req){ return cookie_req.split('=')[1]; } if(!cookie_req){ return 0; }} window.addEventListener('load', function(){ let SettingsList=document.querySelector('.commentSettingsList'); if(SettingsList){ // コメント設定メニューの画面でのみ動作 let list1=document.createElement('li'); list1.setAttribute('id', 'list1'); list1.classList.add('commentSettingsList__item'); list1.innerHTML= '<div class="commentSettingsList__link" style="justify-content: normal">'+ '公開済みリスト 初期表示のコメント内容: '+ '<input name="approved" type="radio" id="approved0"> 非表示 '+ '<input name="approved" type="radio" id="approved1"> 制限表示 '+ '<input name="approved" type="radio" id="approved2"> 全文表示</div>'; if(!SettingsList.querySelector('#list1')){ SettingsList.appendChild(list1); } let list2=document.createElement('li'); list2.setAttribute('id', 'list2'); list2.classList.add('commentSettingsList__item'); list2.innerHTML= '<div class="commentSettingsList__link" style="justify-content: normal">'+ '承認待ちリスト 初期表示のコメント内容: '+ '<input name="pending" type="radio" id="pending0"> 非表示 '+ '<input name="pending" type="radio" id="pending1"> 制限表示 '+ '<input name="pending" type="radio" id="pending2"> 全文表示</div>'; if(!SettingsList.querySelector('#list2')){ SettingsList.appendChild(list2); } set_radio(); function set_radio(){ let approved=get_cookie('approved'); let approved_radio0=document.querySelector('#approved0'); let approved_radio1=document.querySelector('#approved1'); let approved_radio2=document.querySelector('#approved2'); if(approved==0){ // approved: 0:コメント非表示 1:制限表示 2:全文表示 approved_radio0.checked=true; } else if(approved==1){ approved_radio1.checked=true; } else if(approved==2){ approved_radio2.checked=true; } document.cookie='approved='+approved+'; Max-Age=2592000'; let pending=get_cookie('pending'); let pending_radio0=document.querySelector('#pending0'); let pending_radio1=document.querySelector('#pending1'); let pending_radio2=document.querySelector('#pending2'); if(pending==0){ // pending: 0コメント非表示 1:制限表示 2:全文表示 pending_radio0.checked=true; } else if(pending==1){ pending_radio1.checked=true; } else if(pending==2){ pending_radio2.checked=true; } document.cookie='pending='+pending+'; Max-Age=2592000'; } radio_select(); function radio_select(){ let approved_radio0=document.querySelector('#approved0'); approved_radio0.onchange=function(){ document.cookie='approved=0; Max-Age=2592000'; } let approved_radio1=document.querySelector('#approved1'); approved_radio1.onchange=function(){ document.cookie='approved=1; Max-Age=2592000'; } let approved_radio2=document.querySelector('#approved2'); approved_radio2.onchange=function(){ document.cookie='approved=2; Max-Age=2592000'; } let pending_radio0=document.querySelector('#pending0'); pending_radio0.onchange=function(){ document.cookie='pending=0; Max-Age=2592000'; } let pending_radio1=document.querySelector('#pending1'); pending_radio1.onchange=function(){ document.cookie='pending=1; Max-Age=2592000'; } let pending_radio2=document.querySelector('#pending2'); pending_radio2.onchange=function(){ document.cookie='pending=2; Max-Age=2592000'; }} }}); // window.addEventListener
「Comment C-O」最新版について
旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。 最新バージョンへのリンクは、以下のページのリンクリストから探せます。