弊学最大の課題を解決するユーザースクリプト【LiveCampusのブラウザバック】

# まえがき

## 記事について

記事中にLiveCampusと書きましたが、この記事は全く架空のシステムに関するものであり株式会社NTTデータ九州による大学に特化した[学務支援ソリューションLiveCampusシリーズ](https://www.nttdata-kyushu.co.jp/products/corporate/detail.php?type=1&class=21)とは一切無関係**(察してください)**です。

## ユーザースクリプトの導入について

ユーザースクリプトと言えば聞こえはいいですが要は人様のサイトを改変するわけであり、事務の根幹に関わるシステムでそういったことを認めている大学は無いでしょう。導入は自己責任でお願いします。

# 概要

総合ポータルシステム LiveCampusという架空のシステムでブラウザの「戻る」ボタンが使えるようにするユーザースクリプトを作りました。
[GreasyForkで公開中](https://greasyfork.org/ja/scripts/411257-fix-livecampus-browser-back)です。

# はじめに

日本全国津々浦々様々な大学に導入されている、ある基幹システムがあります。

このシステムは主には事務の効率化のために存在するようですが、利用するのは事務の人たちばかりではありません。
例えば教員はこのシステムを通じてレポートを課したり授業連絡を行いますし、学生も同様に履修登録からレポートの提出まで行います。

このシステムを導入している大学は全国で37校[^1]に登ります。大学に在学している限り頻繁に利用するシステムですから、認知度は高いでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507909/9812cf52-b4cc-42cc-7733-d0af9169efd2.png)
<font color="Gray">(↑筆者がTwitterとSlackで独自に集計。調査としての妥当性はない)</font>

しかし一方でこのシステムは別の側面からも広く知られています。それは尽く直感に反する極めて独特な操作性という側面です。

むやみに子ウィンドウが開く、時刻のフォーマットが悪く0:00が実際のところいつを指しているのかわからない、ナビゲーションが滅茶苦茶で求める機能にどこからアクセスするのかわからない、「一時保存」で落ちる、大事な時に限って極端に重い、たまにNullPointerExceptionを出す…その操作性の独特さはビジネス計画の講義となれば毎年必ずと言っていいほど学生から代替システムの企画を作られ、ユーザビリティの講義となれば教員からも悪い例として取り上げられるほど広範かつ多岐に渡るものですが、

>不正な操作が行われました。
>システムエラーにより利用を継続することができなくなりました。処理を続ける場合はログインしなおして下さい。

中でも最も大きいのはブラウザの「戻る」ボタンを押すと「不正な操作が行われました」として即座にエラー落ちする動作でしょう。

以降ブラウザの「戻る」ボタンを押すことをブラウザバックと言うことにしますが、一般的に人間がブラウザバックを使うときには前のページに戻るという動作を期待します。エラー落ちではありません。
しかしエラーで落ちるとわかっていても戻りたいときにブラウザバックという癖は抜けないのです。これによって弊学では一体どれだけの時間的資源が失われてきたでしょうか。もはや一刻の猶予もありません。せめてこの点だけでもなんとか改善できないでしょうか。

# 手法

[Tampermonkey](https://www.tampermonkey.net/)/[Greasemonkey](https://www.greasespot.net/)で利用できるユーザースクリプトとして実装します。その流れとしては以下のとおりです。

1. 規定のブラウザバックの動作を無効化する
2. ブラウザバックの際にページ内にある「戻る」ボタン(ブラウザの「戻る」ボタンとは別)を探してクリックするようにする

わざわざページ内の「戻る」ボタンを探してクリックする処理は回りくどいようですが、[Window.location](https://developer.mozilla.org/ja/docs/Web/API/Window/location)などを使った通常のページ遷移では即座にエラー落ちするのでこの方法を使わざるを得ません。
またページ内に「戻る」ボタンが無いこともありますから、そういう場合にはトップページに戻ることにします。ブラウザバックに期待される動作ではないですがエラー落ちよりはマシでしょう。

# 実装

## 1. 規定のブラウザバックの動作の無効化

JavaScriptで規定の動作の無効化といえば[Event.preventDefault()](https://developer.mozilla.org/ja/docs/Web/API/Event/preventDefault)ですが、これはブラウザバックの際などに起こる[WindowEventHandlers.onpopstate](https://developer.mozilla.org/ja/docs/Web/API/WindowEventHandlers/onpopstate)イベントでは使えないようです。

しかし偉大なる先駆者[^2][^3][^4][^5][^6][^7][^8]によれば[History.pushState()](https://developer.mozilla.org/ja/docs/Web/API/History_API/Working_with_the_History_API#pushState_%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)で適当に1つ履歴を追加する=ブラウザバックしたときに戻る先のページが現在のページを指し示すようにすることで規定のブラウザバックを無効化できるとのこと。
「同じページに戻る」わけなので再読み込みなども発生しないようです。

したがってこの実装はたった1行で成ります。空の履歴を追加しているだけです。

```javascript
// 通常のブラウザバックを無効化する
history.pushState(null, null, null);
```

## 2. ブラウザバック時にページ内の戻るボタンを探してクリック

規定のブラウザバックの動作は無効化できたので、次に[WindowEventHandlers.onpopstate](https://developer.mozilla.org/ja/docs/Web/API/WindowEventHandlers/onpopstate)イベント時にページ内の「戻る」ボタンを探してクリックする処理を作ります。

これは単純に[Document.querySelector()](https://developer.mozilla.org/ja/docs/Web/API/Document/querySelector)で「戻る」ボタンの要素を検索し、[HTMLElement.click()](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/click)でその要素にクリックイベントを起こせば良いでしょう。

検索の際、「戻る」ボタンに振られている属性は一貫しないのでそれぞれに対応した複数のCSSセレクタを用います。
それでも「戻る」ボタンが1つも見つからないような場合にはヘッダメニューにあるLiveCampusのロゴをクリックしてトップページに戻ることにします。

またさらに、そのロゴも見つからないような場合にはLiveCampusにログインしていない(例えばログイン前のトップページにいる)と考えられます。そういう場合には普通にブラウザバックできるはずですから、[History.back()](https://developer.mozilla.org/ja/docs/Web/API/History/back)を使って先ほど無効化した規定のブラウザバックの動作を蘇らせましょう。

実装は次のようになりました。

```javascript
window.onpopstate = () => {
  // DOM中の「戻る」ボタンを探す
  const buttons = [
    document.querySelector(".icon-back"),
    document.querySelector("img[alt='戻る']"),
    document.querySelector("img[src$='modoru.gif']"),
    document.querySelector("h1 a")
  ].filter(button => button);

  // ボタンが見つかればクリック、見つからなければ通常のブラウザバックを行う
  if (buttons.length) {
    buttons[0].click();
  } else {
    history.back();
  }
}
```

# 最終的に出来上がったコード

[Github](https://github.com/ia15076/fix-livecampus-browser-back)と[GreasyFork](https://greasyfork.org/ja/scripts/411257-fix-livecampus-browser-back)でも公開中です。

```javascript
// ==UserScript==
// @name         fix livecampus browser back
// @description  総合ポータルシステム LiveCampusでブラウザバックを使えるようにする
// @author       fukuchan
// @license      GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt
// @match        *://a-portal.aichi-u.ac.jp/*
// @match        *://access.sit.ac.jp/*
// @match        *://campus.kyushu-ns.ac.jp/*
// @match        *://gakujo.shizuoka.ac.jp/*
// @match        *://idp.idm.kyutech.ac.jp/*
// @match        *://jlc.jumonji-u.ac.jp/*
// @match        *://ksuweb.kyusan-u.ac.jp/*
// @match        *://lc-nue.naruto-u.ac.jp/*
// @match        *://lc.brs.nihon-u.ac.jp/*
// @match        *://lc.nagoya-cu.ac.jp/*
// @match        *://lc.okiu.ac.jp/*
// @match        *://lc.s.kaiyodai.ac.jp/*
// @match        *://lc.sgk.ac.jp/*
// @match        *://lc.sun.ac.jp/*
// @match        *://livecampus.adb.fukushima-u.ac.jp/*
// @match        *://portal.bgu.ac.jp/*
// @match        *://siweb.iuk.ac.jp/*
// @match        *://tgulc.u-gakugei.ac.jp/*
// @match        *://vos-lc-web01.nagaokaut.ac.jp/*
// @match        *://www.lc.nishogakusha-u.ac.jp/*
// ==/UserScript==

// 通常のブラウザバックを無効化する
history.pushState(null, null, null);
window.onpopstate = () => {
  // DOM中の「戻る」ボタンを探す
  const buttons = [
    document.querySelector(".icon-back"),
    document.querySelector("img[alt='戻る']"),
    document.querySelector("img[src$='modoru.gif']"),
    document.querySelector("h1 a")
  ].filter(button => button);

  // ボタンが見つかればクリック、見つからなければ通常のブラウザバックを行う
  if (buttons.length) {
    buttons[0].click();
  } else {
    history.back();
  }
}

```

# あとがき

このシステムで「戻る」ボタンを使えたほうが良いということは誰もが認識していることだとは思いますが、機能の追加には莫大な予算が必要とされているようです(追加する機能の内容に依ると思うが、参考として年9000万円程度[^9])。そのほうが良いからと言ってそれだけの予算を組むのも厳しいものがあるのでしょう。

もちろんだからといってユーザースクリプトで対処するというのもまえがきに書いたような理由できっと褒められたものではありません。しかし利用者側で出来る場当たり的対処としてはこのようなユーザースクリプトの導入も選択肢に入るかもしれない…なんだかんだ言っても便利だし…そう思ってこの記事を書きました。便利だよ。

~~つまり何が言いたいかって各大学は9000万円の1/10でいいから私にくれてもいいのよ♡~~

# 参考

[^1]: https://www.nttdata-kyushu.co.jp/news/files/BunkyoCloud.pdf
[^2]: https://qiita.com/maruo327/items/a0502c8b508fed4ec169
[^3]: https://hidari-lab.hatenablog.com/entry/2015/03/02/212818
[^4]: https://qiita.com/szaizen/items/91e652392825b90ef14a
[^5]: https://qiita.com/donkey-maru/items/c5cd17007f8a7ca8fb18
[^6]: https://qiita.com/MikihiroSaito/items/1ea57c06aa88e9e67c3d
[^7]: https://qiita.com/windows222/items/3af92fb7a81624a9927f
[^8]: https://qiita.com/masashi68/items/d53f2195dde0712e641f
[^9]: https://www.musabi.ac.jp/wp-content/uploads/2014/08/keiri_2012_yosan.pdf