Tanabebe Blog Environment

Goやりたい願望強め

reveal-ck×Markdownで美しいプレゼン資料を作成する

f:id:tanabebe:20190915222923p:plain

社内プレゼンの資料作成時、いつもパワポを使用しているのですが、個人的には以下の課題があったので脱却したいと考えていました。

課題

  • パワポをダウンロードするのが面倒だ
    • 資料展開後の操作する手間を減らしたい
  • 資料の透明化
  • Markdownでもプレゼン用にスライド作成が出来るようにしたい
  • Gitで管理したい
  • スライドの見た目が綺麗

調べたところ、この課題を満たすためにreveal.jsというモノを見つけたので今回はこちらについて紹介していきます。

reveal.jsとは

HTMLを使用して美しいプレゼンテーションを簡単に作成するためのフレームワークです。公式を見ると確かに美しく納得です。

revealjs.com

reaveal.jsを単体で使うにあたって問題はないのですが、公式のREADMEを読むとhtmlとMarkdownが混在している作法でした…これは正直気持ち悪いなと感じたので調査したところ…以下記事内にreveal-ckとなるモノがあり、解決出来そうです。

qiita.com

こちらはreveal-ckの公式
jedcn.github.io

コンセプトページなどを見ると、1ファイルで完結することが出来るとのこと。非常にシンプルですね。早速reveal-ckを導入していきましょう。
また、以降のreveal-ckインストール手順からスライド作成までの手順を、GitHub Pagesにアップしました。

reveal-ck×Markdownで実際に作成したスライドはこちら
tanabebe.github.io

GitHubはこちら
GitHub - tanabebe/reveal-ck-install-manual at gh-pages

reveal-ck インストール手順

Rubyがインストールされているか確認する

私は以下のVersionでしたが、問題なくインストール可能でした。

❯❯❯ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]

reveal-ckをインストールする

sudoしないとエラーになるので注意を。

❯❯❯ sudo gem install reveal-ck

reveal-ckがインストールされているか確認する

❯❯❯ reveal-ck -v
reveal-ck version 3.9.2

問題なくインストールされました。これでMarkdownで心置きなく書いていくことができます。早速スライドの作成に取り掛かって行きましょう。

スライドを作成していく

ディレクトリの作成

❯❯❯ mkdir reveal-sample
❯❯❯ cd reveal-sample/

Markdownファイルの作成

ファイル名がslides.*でないと後のreveal-ck gen時にエラーとなるので注意が必要です。

# OKパターン
❯❯❯ touch slides.md
❯❯❯ touch slides.sample.md
# NGパターン
❯❯❯ touch index.md

ここでは一旦ファイル内へ何かしら書き込んでおきます。

❯❯❯ echo "# Hello!" > slides.md

スライド用ファイルの自動生成を行う

❯❯❯ reveal-ck gen
Generating slides for 'slides.md'..

ローカル環境で動作を確認する

❯❯❯ reveal-ck serve
[ reveal-ck ] Serving up slide content in 'slides/'.
[ reveal-ck ] Open your browser to 'http://localhost:10000'.
[ reveal-ck ] Press CTRL-C to stop.

http://localhost:10000にアクセスするとスライドにアクセス出来ます。reveal-cs serveを止めずにファイル編集をするとリロードが走りますが、ページが複数あったとしても1番最初のページからスタートします。

まとめ

Markdownのみでプレゼン資料を作成することがこれで出来たので、私が抱えている課題は全て解決することが出来ました。mdファイル1つで完結出来るのでとてつもなく膨大なスライドではない限り、Markdownでのスライド作成が捗ります。GitHub Pagesにも問題なくアップが出来るのでこれを気にパワポ資料作成から脱却するのも1つの手段と考えてはどうでしょうか?

躓いた点など

reveal.jsにはThemeが複数用意されていますが、各Theme毎にCSSが設定されており、不要と思ったCSSの除去や上書きが必要でした。見出しはデフォルトで中央寄せになっているのと、画像をはめ込む時は画像サイズをカスタムするが必要があり。一旦は時間がなかったため、無理やり作成しましたが、今後はもう少し掘り下げる必要性を私は感じました。

参考

qiita.com

qiita.com

Publishing Slides · jedcn/reveal-ck Wiki · GitHub

qiita.com

CodeSandboxを使って必要最小限なReactコンポーネントを作成する

以前社内システムにてReactを使用する機会があり、その際、勉強がてらではありますが、自由に遊んで良い砂場として使用していました。 個人的にReactは始めてからも始める前もそこそこ障壁が高いと考えていますが、CodeSandboxを使うと簡単に始める事が出来るので紹介したいと思います。

CodeSandBoxとは?

Webアプリケーションプロジェクトの作成が可能なオンラインコードエディタです。簡単な登録をすましたらすぐに開始が可能で、GitHub連携やURLの共有なども出来るのでかなり強力です。無料枠だけで30個ほどのプロジェクトを作成が可能です。

codesandbox.io

私の場合ですと、業務上の関係でReactとは別で複数のnode versionを管理する必要がありました。versionを切り替えを忘れていた状態でnpm installした時に時間を潰してしまっていたり、非常に面倒でした…
PCの環境に依存しないでとにかくReactを触りたいという考えにマッチするモノはないかと探していた時にCodeSandboxに辿り着きました。私のケースとは別で実際にReactを始めるにあたって以下のように障壁の高さを感じた人もいるのではないでしょうか。

Reactを始める上での障壁の高さ

環境面

  • Node.jsのインストール
  • npm によるパッケージのインストール
  • BabelによるJSX 等の JavaScript へのトランスコンパイル

また、環境面とは別でReact自体の障壁の高さも以下にあげてみます。

React自体

  • 動かしてから内容を理解するまでにそれなりの努力を要する
  • 社内システムを作るにあたってReactを採用したが、失敗した
  • babelとかwebpackやら覚える事がやたらと多い
  • 数年前にフューチャーされてたjqueryと比べると段違いにシステマチック
  • そもそもJavaScriptの仕様を理解する必要がある などなど…

CodeSandboxの世界へ入る

環境面で躓いてReactまで辿り着かないとなってはストレスもかかりますし、場合によっては数日かかるかもしれないです。CodeSandboxを使い、Reactの事だけを考えられるようスタートしていきましょう。

Create Sandboxでミニマムスタートを切る!

CodeSandBoxへアクセスしCreate Sandboxを選択して次画面へ進みます。 f:id:tanabebe:20190912103122p:plain プリセットを見ると選択肢が多く、非常に強力な事が見て取れます。ここではReactを選択します。 f:id:tanabebe:20190912103148p:plain すると自動でプロジェクトが生成されます!(内部的にcreate-react-appとかしてくれているとのことです) f:id:tanabebe:20190912103154p:plain

最小限のclass componentを作成する

index.jsの中身を以下に書き換えます。

import React, { Component } from "react";
import { render } from "react-dom";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "Tanabebe!!",
      word: "Hello!!"
    };
  }

  render() {
    return (
      <div>
        <h2>
          {this.state.word} {this.state.name}
        </h2>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

ホットリロードも自動で走るので書き換えると以下が表示され完成です。 f:id:tanabebe:20190912105743p:plain

まとめ

CodeSandboxを使う事でReactの開発開始時に必要なものをCodeSandBoxが提供してくれるので、素早くReact 開発を始めることが出来ます。通常であれば慣れていない方にとっては環境構築は難易度が高いと思うので、このハードルを取り除く事が出来ます。Reactに限らず、フロントエンドの開発フレームワークのサポートも多いのでとても魅力的で強力なツールです。環境構築で躓いた方はこれを気にこの砂場で遊んでみてはいかがでしょうか。

おまけ

CodeSandbox上からReactを使用し、Task追加アプリを作成しました。こちらはCodeSandbox上から作成したモノとなります。

デモ

f:id:tanabebe:20190912112552g:plain

GoからGoogle Cloud SQLへ接続を行いデータを登録する

f:id:tanabebe:20190908221629p:plain GoでDB接続をして何かしらの命令を実行する、という事はやった事がないので勉強がてらではありますが、今更ながらGoからCloud SQLへの接続を行い、対象のテーブルへレコードを追加するという事をやってみます。

Cloud SQLインスタンスの作成

まずはデータベースが無いと始まらないので接続先のデータベースとしてGoogle Cloud Platform(以降GCP)にログインをし、作成していきます。

対象プロジェクトへ移動し、Cloud SQLインスタンスを作成する。 f:id:tanabebe:20190908200705p:plain

PostgreSQLを選択する。 f:id:tanabebe:20190908200747p:plain

Cloud SQLインスタンスに必要な情報を入力する。 f:id:tanabebe:20190908200818p:plain

Cloud SQLに接続可能なIPを指定する。 f:id:tanabebe:20190908200846p:plain

インスタンスを作成する。 f:id:tanabebe:20190908200851p:plain

ここまで出来たらデータベースタブを選択しデモ用にデータベースを作成する。 f:id:tanabebe:20190908204744p:plain

demoというデータベースを作成。 f:id:tanabebe:20190908204801p:plain

Cloud Shellを使用して接続してみる

Cloud SQLの対象インスタンスを選択し、Cloud Shellを使用して接続を選択。

# 以下のコマンド後にパスワードを入力する
$ gcloud sql connect sample
Whitelisting your IP for incoming connection for 5 minutes...done.        
Connecting to database with SQL user [postgres].Password for user postgres:
psql (9.6.15, server 9.6.14)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES128-GCM-SHA256, bits: 128, compression: off)
Type "help" for help.
# データベースを指定
postgres=>\c demo
# テーブルを作成する
demo=> CREATE TABLE demo (
demo(>   id SERIAL PRIMARY KEY,
demo(>   name TEXT NOT NULL,
demo(>   hobby TEXT NOT NULL);
CREATE TABLE

テーブルの作成が出来たので、データがない事を確認する

demo=> SELECT * FROM demo;
 id | name | hobby
----+------+-------
(0 rows)

ここまで確認出来たらデータベース側の事前準備は終了です。

Go側の準備

PostgreSQL用のドライバをインストールする

go get github.com/lib/pq

Go側のプログラムを書いていく

コードの内容としては薄いので今回はこれのみです。

// main.go
package main

import (
    "database/sql"
    // 直接使わないので無名インポートしておく
    _ "github.com/lib/pq"
    "log"
)

// 接続文字列(hoge部分は自身の環境に合わせて下さい)
const connectionString = "host=hoge port=5432 user=postgres password=hoge dbname=demo"
func main() {

    db, err := sql.Open("postgres", connectionString)
    // main関数を抜けたら実行
    defer db.Close()

    // errがあったらlogへ書き込み
    if err != nil {
        log.Fatal(err)
    }

    ret, err := db.Exec(`INSERT INTO demo(name, hobby) VALUES('tanabe', 'programming')`)
    // errがあったらlogへ書き込み
    if err != nil {
        log.Fatal(err)
    }

    // logへ結果を書き込み
    log.Println(ret)
}

結果が反映されているかを確認する

cloud shellからSQLを実行してテーブルにデータが書き込まれたかを確認する。

demo=> SELECT * FROM demo;
 id |  name  |    hobby
----+--------+-------------
  1 | tanabe | programming
(1 row)

これでGoからテーブルへデータの書き込みが出来ました。

まとめ

どうだったでしょうか。Go側の実装はとてもシンプルで全く難しいと思った事はないかと思います。今回の例で言うとCloud SQL側の事前準備の方が時間がかかったかと思います。SQLを実行する前にデータベースへの疎通確認はいいの?と思うかも知れませんがExecを実行する際に事前に実行されるのであえて実装する必要はないと考えており今回は行っておりません。個人的な感覚ではありますが、ORMを使用せずに、ここまでシンプルに接続と命令が可能なので本当にこれで良いの?と思うくらい取っ付きやすかったです。

参考URL

github.com Cloud SQL for PostgreSQL のクイックスタート  |  Cloud SQL for PostgreSQL  |  Google Cloud

Go×WebAssemblyを使ってDOM操作を試してみる

今回はWebAssemblyについて試してみました。以下目次で進めて行きます。

WebAssembyとは

WebAssemblyは、ブラウザーで実行できるバイナリコードです。WebではJavaScriptが必須ですが、リッチな処理が実現出来るようになるにつれ性能が不足するシーンが増えてきています。この問題解決のために実行が高速になるよう設計され誕生したのがWebAssemblyです。JavaScript以外の言語からコンパイルされていることを前提としており、調査したところ代表的な言語としてはC、C++、Rustです。ただし、WebAssemblyをロードして、JavaScriptから呼出すのでJavaScriptの代替えというわけではないことには留意しておきましょう。

WebアプリケーションにおけるWebAssemblyイメージ
f:id:tanabebe:20190901111756p:plain

Goでは1.11以降のVersionであれば、コードをWebAssemblyとしてコンパイル出来る機能が試験的ではありますがリリースされているため、今回はGoから試してみたいと思います。

Go 1.11 added an experimental port to WebAssembly. Go 1.12 has improved some parts of it, with further improvements expected in Go 1.13.

今回のコード

こちらに公開しております。
github.com

作成した画面


console.log

f:id:tanabebe:20190901100719g:plain


HTML書き換え

f:id:tanabebe:20190901100814g:plain


alertの表示 f:id:tanabebe:20190901100940g:plain


text-boxの入力値を別のtext-boxへ

f:id:tanabebe:20190901101301g:plain

前提条件

  • OS:macOS Majave
  • IDE:GoLand
  • GoVersion:1.12.9

Goのコードを書いていく

console.logに出力するためのGo側のコードを書いていきます。

// main.go

package main

import (
    "fmt"
    "syscall/js"
)

// documentオブジェクト取得用
var document = js.Global().Get("document")

// bodyのDOM取得
var body = document.Get("body")

func main() {
    // goからbuttonのDOMを作成する
    cLogBtn := createElement("button")
    // cLogBtnボタンのテキストを設定
    cLogBtn.Set("textContent", "console log!!")
    // buttonをbodyへ追加
    body.Call("appendChild", cLogBtn)
    // cLogBtnにclickのEventLisnerを設定
    cLogBtn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} {
        fmt.Println("Hello Webassembly!")
        return nil
    }))
    // プログラムが終了しないように待機
    select {}
}

// 使用頻度が高そうなので、対象DOMを作成する関数を用意
func createElement(elementName string) js.Value {
    return document.Call("createElement", elementName)
}

HTMLの作成

HTMLを作成してWebAssembly実行に必要なコードを記述します。console.log出力用のボタンはGoから作成していますのでHTMLには記述なしです。

<!-- index.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>wasam-demo</title>
    <script src="./wasm_exec.js"></script>
    <script>
        (async () => {
            const go = new Go();
            const { instance } = await WebAssembly.instantiateStreaming(
                fetch('main.wasm'),
                go.importObject
            );
            await go.run(instance);
        })();
        async function run() {
            console.clear();
            await go.run(inst);
            inst = await WebAssembly.instantiate()
        }

    </script>
</head>
<body>
    <div id="message">Hello, World</div>
</body>
</html>

WebAssemblyへコンパイルする

ここまで来たら、Goのプログラムをコンパイルします。以下のコマンドを打ちます。

❯❯❯ GOOS=js GOARCH=wasm go build -o main.wasm

実行するとmain.wasmというファイルが生成されます。macOSWindowsLinux向けのバイナリをクロスコンパイル出来るのでGOOSGOARCH環境変数を変更すれば他OSの実行環境へのコンパイルも可能です。ここではJavaScriptの実行環境をOSとし、WebAssemblyとしてコンパイルしています。

GOROOTからwasm_exec.jsをコピーしてくる

ブラウザで実行するためにwasm_exec.jsをコピーしてきます。wasm_exec.jsはGoから JavaScriptへリンクするための実行命令が含まれているスクリプトになります。では以下のコマンドを打ってコピーしてきます。

❯❯❯ cp $GOROOT/misc/wasm/wasm_exec.js .

HTTPサーバーを実行し、確認する

ローカル環境で動作させるために以下をgo getしてから実行していきます。

❯❯❯ go get https://github.com/mattn/serve
❯❯❯ serve -a :8080

これでhttp://localhost:8080/にアクセスすると画面の確認が出来ます。読み込み時はDOMの生成をGoで行っているので少し遅れてボタン表示がされます。 f:id:tanabebe:20190901105707g:plain

次は表示されているHTMLのテキストを書き換えていきます。

HTMLのテキストを書き換え

main.goに以下を追記していきます。func main()に追記。

// buttonDOMを作成する
textChangeBtn := createElement("button")
// textChangeBtnのテキストを設定
textChangeBtn.Set("textContent", "text change!!")
// buttonをbodyへ追加
body.Call("appendChild", textChangeBtn)
// textChangeにclick時のEventLisnerを設定
textChangeBtn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} {
    message := getElementByID("message")
    message.Set("innerHTML", "Hello, WebAssembry!!")
    return nil
}))

func mainの外に以下を追記

// 使用頻度が高そうなので、対象のDOMのIDを取得する関数を用意
func getElementByID(targetID string) js.Value {
    return document.Call("getElementById", targetID)
}

今回もDOMをGoから作成しているのでHTML側の修正は行いませんが、Go側を修正したのでコンパイルした後、HTTPサーバーを実行します。

❯❯❯ GOOS=js GOARCH=wasm go build -o main.wasm
❯❯❯ serve -a :8080

※以降Go側の修正があり、再実行時はこの手順を実施します

画面から確認するとテキストの変更が出来ることが確認出来ました! f:id:tanabebe:20190901110555g:plain

alertのダイアログを表示させる

main.goグローバル変数として以下を追記します。

// windowオブジェクトを取得
var window = js.Global()

main.gofunc main内に以下を追記します。

// buttonのDOMを生成
alertBtn := createElement("button")
// alertBtnのテキストを設定
alertBtn.Set("textContent", "alert!!")
// buttonをbodyへ追加
body.Call("appendChild", alertBtn)
// alertBtnにclick時のEventLisnerを設定
alertBtn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} {
    window.Call("alert", "Hello!!")
    return nil
}))

コンパイルした後、HTTPサーバーを実行して画面を確認していきます。無事alertのダイアログが表示されました! f:id:tanabebe:20190901111423g:plain

text-boxの入力値を別のtext-boxへ反映させる

main.goに以下を追記していきます。func main()に追記。

// textエリアの入力値を取得
getElementByID("in").Call("addEventListener", "keyup", js.FuncOf(func(js.Value, []js.Value) interface{} {
    getElementByID("out").Set("value", getElementByID("in").Get("value"))
    return nil
}))

index.htmlbodyタグ内に以下を追加します。

<div><input type="text" id="in"></div>
<div><input type="text" id="out"></div>

コンパイルした後、HTTPサーバーを実行して画面を確認します。こちらも問題なくDOM操作が出来ました。
f:id:tanabebe:20190901115717g:plain

躓いた点

ビルドが出来ない問題

main.goにimportしているsyscall/jsを認識してくれずビルドが不可な状態になりました。調査したところ以下で同じエラーが再現しており、私も同じようにGoLand上で設定を変更したところ動作するようになりました。
Go × WebAssemblyで電卓のWebアプリを作ってみた - Sansan Builders Box

GoLandのBuild設定 f:id:tanabebe:20190901123021p:plain

js.NewCallbackには変更がある

js.NewCallbackという記述をちらほら見ますが、こちらは現在FuncOf()に変更されているので注意が必要です。

まとめ

WebAssemblyを使用して、GoからJavaScriptと同じ振る舞いが出来る事が確認できました。WebAssemblyはスピードにフォーカスを当てているので今回のようなケースでは実感しづらいですが、実際のシチュエーションとしてはゲーム、大規模なWebアプリ制作などが考えられます。フロントエンド側の技術が苦手な私でもDOM操作がGoから行えるのは魅力的です。まだ比較的新しい技術ではありますが、今後のWebプログラマーとして関わる方には動向が気になる技術ではないでしょうか。 WebAssemblyでデモ的に作成されたゲームもあるので気になる方はこちらもチェックしてはいかがでしょう。

参考

js - The Go Programming Language WebAssembly: 「なぜ」と「どうやって」 [翻訳記事] - DEV Community 👩‍💻👨‍💻 GoでWebAssemblyに触れよう ドキュメントオブジェクトモデル (DOM) - Web API | MDN

Javaで構築されたWebアプリのsubmit時にローディング画面を表示する

既に運用しているレガシーなシステムだとローディング画面をgifやアニメーション表示をしたいという場面がそこそこあります。もちろん案件にもよりますが。蓋を開けてみると少し遠回りして実装しないといけなかったり、ボタンを非活性として対応したり、やり方は様々かと思います。今回はJavajQueryを使ってローディング画面をどのように実装したか、以下の目次で書いていきます。

1. 前提条件

  1. jquery-cookieの導入(以下をダウンロード)
    GitHub - carhartl/jquery-cookie: No longer maintained, superseded by JS Cookie:
  2. ローディング用のgifは作成済み

2. 要件

  1. ファイルダウンロードの時はsubmit時にクライアント側で検知する
  2. クライアント側でローディング画面を表示させた後にサーバー側でcookieの値を設定してクライアント側で検知する
  3. 画面遷移がある場合はsubmitのactionに任せる
  4. ローディング画面を表示する処理は明示的に修正可能であること

3. プログラムの実装

3-1. サーバー側のJava実装

// ※クラスは省略しています
// クライアントへ返却するresponseの作成
HttpServletResponse response = ServletActionContext.getResponse();

// ダウンロード処理の前にクッキーを設定する(定数は好みでkeyとvalueを設定)
Cookie resultCookie = new Cookie(COOKIE_NAME_DOWNLOAD, COOKIE_NAME_DOWNLOAD_SUCCESS);

// cookieのパス
resultCookie.setPath("/");

// 作成したcookieをresponseに設定
response.addCookie(resultCookie);

3-2. ローダー用JavaScriptの実装

  • 作成したjsはどこかの共通処理に読み込みます
  • 読み込むgifはどこかで作成しておきます
  • cssは色々あるのでここでは割愛しています(gifは中央の背景画像として表示させる前提)
/***
 * ローディング検知用のonLoadingHandler
 * ファイルダウンロードが発生する場合はsubmit後のcookie値を監視して明示的にローディング画面を閉じる
 * @param action : submit時のform action
 */
function onLoadingHandler(action) {

  // ローディング画面の生成
  dispLoading("ローディング画面の文言");

  // ファイルダウンロード系のactionを全て判断して、cookie値からの監視を行い、ローディング画面の制御を行い
  // 該当しないactionならsubmitの動作に委譲する
  if(action === '対象のaction' ) {
    // setInterval用の変数
    var intervalId;
    // 一定時間ごとに特定の処理を繰り返す
    intervalId = setInterval(function() {
      // サーバー側から受け取ったcookie値を取得出来た場合 
      if ($.cookie('download')) {
        // ローディング画面を止める
        removeLoading();
        // intervalの処理を止める
        clearInterval(intervalId);
        // 受け取ったcookieをクリア
        $.removeCookie('download', { path: '/' });
      }
    }, 1000);
  }
}

/***
 * ローディング画面表示用のdispLoading
 * dispLoading('表示させたいメッセージ');で単独でも呼び出せる
 * @param msg : 表示させたいメッセージ
 */
function dispLoading(msg) {
  // メッセージがない場合も許容しておく
  if( msg === undefined || msg === null ){
    msg = "";
  }
  // 画面表示メッセージ用DOM生成
  var dispMsg = "<div class='loadingMsg'>" + msg + "</div>";

  // ローディング画像が表示されていない場合のみ出力する
  if($("#loading").length === 0){
    $("body").append("<div id='loading'>" + dispMsg + "</div>");
    // Webブラウザのキャッシュ対策でgifにパラメータを付与して表示させる
    var timestamp = new Date().getTime();
    var filePath = "../css/images/loader.gif?" + timestamp; // ファイルのパスは環境に合わせて
    // 対象の要素にcss付与(gifはセンター寄せ)
    $('#loading .loadingMsg').css('background', 'url("' + filePath + '") center center no-repeat');
  }
}

/***
 * ローディング画面停止用のremoveLoading
 */
function removeLoading(){
  $("#loading").remove();
}

3-3. ローダー用JavaScriptの呼び方

// 画面遷移する場合
function fileNoneLoaded() {
  $("#form").attr("action", "対象のactionパス");
  $("#form").submit();
  // actionを空文字で渡す
  onLoadingHandler('');
}

// ファイルのダウンロードの場合
function fileDownLoaded() {
  $("#form").attr("action", "対象のactionパス");
  $("#form").submit();
  // action名にfileExportを渡す、onLoadingHandlerで検知出来るように対象actionは記述しておく
  onLoadingHandler('fileExport');
}

4. まとめ

このようにサーバー側でcookie値を設定してクライアントで検知することで、ローディングの可否について判定させることが出来ます。html部分もjavaScriptで生成しているので、画面全体にwrap出来るように工夫する事で画面全体を覆う事が可能なのでhtmlも不要です。「4. ローディング画面を表示する処理は明示的に修正可能であること」の通り、必ずしも画面遷移毎にローディング画面を表示させるわけではないという点には気を使いました。

5. あとがき

使用しているブラウザが特殊なブラウジングアプリ(DoCANブラウザなど)だと、CSSJavaScriptがうまく効かないという現象があるためお気をつけ下さい。