ラジオボタンの実装コード

ラジオボタンは複数の選択肢の中で「1個」だけを選択するインターフェイスに適したinput要素です。 コードの書き方は色んなページにサンプルがありますが、私は少し判っていなかった事があり、饒舌なコードを書いていました。

 

ラジオボタンを実装したのは下の「コメント設定」のメニュー画面です。

 

 

コードの制作時は、このページで DevTools を起動して、

 「Application → Storage → Cookies → https://blog.ameba.jp」

を選択すると、このページのCookie一覧が表示されます。

 

 

「Comment C-O」は 「approved」「pending」のNameでボタン選択を「0 or 1」の値で記録しています。 この変化を観測するとスクリプトの動作が判ります。 ただし、Cookieの一覧表の「  」ボタンを押さないと、ラジオボタン操作で更新された値が反映しないので、この点は注意が要ります。

 

 

 

ファットなコードとスマートなコード 

下は要らない記述(問題は生じない不要コード)を沢山含んだファットなコードです。 ラジオボタンが判っておらず、なんとか使える動作にしようとした結果で、出来上がったコードです。

 

function radio_select(){
    let approved_radio1=document.querySelector('#approved1');
    approved_radio1.onchange=function(){
        if(approved_radio1.checked==true){
            document.cookie='approved=1; Max-Age=2592000'; }
        else{
            document.cookie='approved=0; Max-Age=2592000'; }}

    let approved_radio0=document.querySelector('#approved0');
    approved_radio0.onchange=function(){
        if(approved_radio0.checked==true){
            document.cookie='approved=0; Max-Age=2592000'; }
        else{
            document.cookie='approved=1; Max-Age=2592000'; }}

    let pending_radio1=document.querySelector('#pending1');
    pending_radio1.onchange=function(){
        if(pending_radio1.checked==true){
            document.cookie='pending=1; Max-Age=2592000'; }
        else{
            document.cookie='pending=0; Max-Age=2592000'; }}

    let pending_radio0=document.querySelector('#pending0');
    pending_radio0.onchange=function(){
        if(pending_radio0.checked==true){
            document.cookie='pending=0; Max-Age=2592000'; }
        else{
            document.cookie='pending=1; Max-Age=2592000'; }}}

 

このコードの動作を調べると、下線の部分が不要でした。 不要な記述を実際に削除すると、下の様にとてもシンプルになります。

 

function radio_select(){
    let approved_radio1=document.querySelector('#approved1');
    approved_radio1.onchange=function(){
        document.cookie='approved=1; Max-Age=2592000'; }

    let approved_radio0=document.querySelector('#approved0');
    approved_radio0.onchange=function(){
        document.cookie='approved=0; Max-Age=2592000'; }

    let pending_radio1=document.querySelector('#pending1');
    pending_radio1.onchange=function(){
        document.cookie='pending=1; Max-Age=2592000'; }

    let pending_radio0=document.querySelector('#pending0');
    pending_radio0.onchange=function(){
        document.cookie='pending=0; Max-Age=2592000'; }}

 

 

ラジオボタンのコードから判った事 

上の様な不要な記述の多いコードを作ってしまったのは、ラジオボタンの特質の理解がとてもあいまいだったからです。

 

上記コードは「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">&#8196;非表示  '+
            '<input name="approved" type="radio" id="approved1">&#8196;制限表示  '+
            '<input name="approved" type="radio" id="approved2">&#8196;全文表示</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">&#8196;非表示  '+
            '<input name="pending" type="radio" id="pending1">&#8196;制限表示  '+
            '<input name="pending" type="radio" id="pending2">&#8196;全文表示</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ツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。 最新バージョンへのリンクは、以下のページのリンクリストから探せます。