【iPhone / iPad】続・Ohajiki Web ブラウザでねじれ天国のUIをそこそこ快適にする

はじめに

前回記事では検索窓の幅調整だけというショボい内容だったが、今回大幅にUI改善を加えてみた。Ohajiki Web ブラウザの導入は前回記事を参考にし、貼り付けるコードをこの記事の最後のものに置き換えてほしい。

2021/03/26追記

コードを修正し、発言ボタンの2度押しを防止する機能を追加。また、各機能の有効/無効をフラグでコードの先頭に追加した。

追加内容

文学ブロッカー

規定文字数以上の発言を途中で省略する。コード内の BLK_MAX_LENGTH の値を変えれば何文字以上で省略するかを変更可能。デフォルトは800文字。

サイドバー格納

左の参加者リストおよび検索窓をデフォルトで非表示にし、ログを画面幅最大で表示できるように。サイドバーの中身は「リスト・検索窓を開く▼」ボタンで開閉可能(表示位置が適当なのはご愛敬)。

発言欄のフォントサイズを調整

これによって入力時にズームされないようになり、発言の全体が見やすくなった(はず)。

「村を出る」ボタンタップ時に確認ダイアログ表示

誤タップによる退村を防げる。

発言ボタン2度押し防止 2021/03/26追加

そのまま。通常の発言欄だけでなく、すべてのフォームに適用される。

その他

最初にねじ天の村かどうかを判定しているので、他のサイトで悪さをする可能性はありません。

修正版コード

タップでコピーされます。適用手順は前回記事を確認してください。このコードより下に文章はありません。

(function() {
  // 各機能ごとの有効フラグ。不要なものは true を false にすると無効になります。
  const USE_BLK_LTR = true;    // 文学ブロッカー
  const USE_ADJ_SEARCH = true; // 検索窓幅調整
  const USE_RMV_SIDE = true;   // 左リスト畳み込み&発言入力欄ズームなし
  const USE_CFM_EXT = true;    // 村を出るボタンに確認ダイアログ追加
  const USE_BLK_DBL = true;    // 発言ボタン2度押し抑止

  // BLK_MAX_LENGTH 以上の文字数を短縮表示する
  const BLK_MAX_LENGTH = 800;

  const url = window.location.href;
  if (!url.match(/nejiten\.halfmoon\.jp\/index\.cgi\?vid=/)) {
    return;
  }

  if (USE_BLK_LTR) {
    blockLiterature();
  }
  if (USE_ADJ_SEARCH) {
    adjustSearchBox();
  }
  if (USE_RMV_SIDE) {
    removeSidebar();
  }
  if (USE_CFM_EXT) {
    confirmExit();
  }
  if (USE_BLK_DBL) {
    disableSubmit();
  }

  // 文学ブロッカー
  function blockLiterature() {
    // 表示変更リンクのテキスト
    const OPEN_MES = '▼全文表示';
    const CLOSE_MES = '▲短縮表示';

    function createOpenDiv(mes_num) {
      let div = document.createElement('div');
      div.style.color = 'blue';
      let a = document.createElement('a')
      a.id = 'open' + mes_num;
      a.innerText = OPEN_MES;
      a.style.cursor = 'pointer';
      div.append(a);
      return div;
    }

    function createCloseDiv(mes_num) {
      let div = document.createElement('div');
          div.style.color = 'blue';
      let a = document.createElement('a')
      a.id = 'close' + mes_num;
      a.innerText = CLOSE_MES;
      a.style.cursor = 'pointer';
      div.append(a);
      return div;
    }

    function createMsgDiv(msg, id) {
      let div = document.createElement('div');
      div.id = id;
      div.innerText = msg;

      return div;
    }

    let mes = document.querySelectorAll('[class$=body1]');
    for (let i = 0; i < mes.length; i++) {
      if (BLK_MAX_LENGTH < mes[i].innerText.length) {
        let msg = mes[i].innerText;
        let shortMsg = msg.slice(0, BLK_MAX_LENGTH) + "...\n\n";
        mes[i].innerText = '';

        // 長文を退避
        let msgDiv = createMsgDiv(msg, 'msg' + i);
        let closeDiv = createCloseDiv(i);
        msgDiv.append(closeDiv);
        msgDiv.style.display = 'none';

        // 短縮文を作成
        let shortMsgDiv = createMsgDiv(shortMsg, 'shortMsg' + i);
        let openDiv = createOpenDiv(i);
        shortMsgDiv.append(openDiv);

        // クリック時に開閉を呼ぶようにイベントリスナ設定
        closeDiv.addEventListener('click', function() {
          toggle(i, true);
        });
        openDiv.addEventListener('click', function() {
          toggle(i, false);
        });

        // 追記
        mes[i].append(shortMsgDiv);
        mes[i].append(msgDiv);

      }
    }

    function toggle(i, isOpened) {
      const msgDivId = 'msg' + i;
      const shortMsgDivId = 'shortMsg' + i;
      let msgDiv = document.getElementById(msgDivId);
      let shortMsgDiv = document.getElementById(shortMsgDivId);

      if (isOpened) {
        // 全文表示から短縮表示に切り替える
        msgDiv.style.display = 'none';
        shortMsgDiv.style.display = 'inline';

        // 短縮時に発言が詰まってしまうので元発言のトップにスクロール
        shortMsgDiv.scrollIntoView(true);
      } else {
        // 短縮表示から全文表示に切り替える
        shortMsgDiv.style.display = 'none';
        msgDiv.style.display = 'inline';
      }
    }
  }

  // 検索窓幅調整
  function adjustSearchBox() {
    var target = document.querySelector('input#cse-search-box');
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            target.style.width = "auto";
        });
    });

    var config = { attributes: true, childList: true, characterData: true }
    observer.observe(target, config);
  }

  // 左リスト削除
  function removeSidebar() {
    var side = document.querySelector('#side');
    var side2 = document.querySelector('#side2');
    var sides = document.createElement('div');
    sides.id = 'sides';
    sides.style.display = 'none';
    sides.appendChild(side);
    sides.appendChild(side2);
    var btn = document.createElement('span');
    var btnMsg = document.createTextNode('リスト・検索窓を開く▼');
    btn.id = 'btn';
    btn.classList.add('hope_toggle');
    btn.appendChild(btnMsg);
    btn.onclick = () => {
      if (sides.style.display === 'none') {
        btn.innerText = btn.innerText.replace('開く▼', '閉じる▲');
        sides.style.display = 'block';
      } else {
        btn.innerText = btn.innerText.replace('閉じる▲', '開く▼');
        sides.style.display = 'none';
      }
    }
    var content = document.querySelector('#content');
    content.appendChild(btn);
    content.appendChild(sides);
  
    document.querySelector('body > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr:nth-child(2) > td:nth-child(1)').style.display = 'none';
    document.querySelector('#main > td:nth-child(1)').style.display = 'none';
    document.querySelector('body > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr:nth-child(5) > td:nth-child(1)').style.display = 'none';
    document.querySelector('body > table > tbody > tr > td > table').style.width = 'auto';
    
    // 発言フォームのフォントサイズ変更(フォーカス時にズームされないようにする)
    let textAreas = document.querySelectorAll('textarea');
    (Array.from(textAreas)).forEach((ta) => { ta.style.fontSize = '24px' });
  }

  // 村を出るボタンに確認ダイアログ追加
  function confirmExit() {
    var exit = document.querySelector('input[value="村を出る"]');
    if (exit) {
      var form = exit.closest('form');
      form.onsubmit = () => {
        if (window.confirm('村を出ますか?')) {
          return true;
        } else {
          return false;
        }
      };
    }
  }

  // 発言ボタン2度押し防止
  function disableSubmit() {
    let forms = Array.from(document.querySelectorAll('form'));
    let btn = Array.from(document.querySelectorAll('input[type="submit"]'));
    
    forms.forEach((form) => {
        form.onsubmit = () => {
            btn.forEach((b) => {
                b.disabled = true;
            })
        }
    });
  }
})();

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です