JavaScriptで外部関数が読み込まれた後に処理を実行する

JavaScriptは良い意味でも悪い意味でも自由度が高いです。

基本的にhtmlはソースの上から順に読み込んでいきますが、外部ファイルなどは取得にタイムラグが発生して処理実行時に必要な関数が見つからない場合が稀にあります。

 

そんなときに、外部関数が読み込まれたことを確認してから処理を実行するスクリプトを書いてみました。

ニッチすぎるのでどうかと思ったのですが、需要がありそうな気配がしたので記事にしてみます。

関数の存在チェック

typeof hoge == "function"

hogeというオブジェクトが関数かどうかを判定します。

関数が見つからない場合はhogeundefinedなどになったりするので、trueが返ってくれば関数が存在する=読み込まれたと判断できます。

関数が読み込まれたら処理を実行

function checkLoadScript() {
if
(typeof hoge == "function") {
  // 実行したい処理
  hoge();
} else {
  setTimeout('checkLoadScript()', 100);
}
}

先ほどの存在チェックを再帰的に呼び出し、読み込まれたら処理を実行してループを抜けます。

あまりスマートなやり方ではないですが、窮地の際にすがる藁くらいの一助にはなるといいなと思います。

JavaScriptでQRコード生成

昨日の記事に連なります。

JavaScriptでバーコード生成

 

jQueryプラグインにバーコードを生成してくれるものがありますが、二次元コードであるQRコードを生成してくれるプラグインもありました。

http://jeromeetienne.github.io/jquery-qrcode/

 

QRコードは一次元のバーコードよりもはるかに多くの情報量を持つことができます。携帯電話のカメラからの読み取りもあって、爆発的に普及しました。

その恩恵が評価され、開発者であるデンソーウェーブの原昌宏氏と同氏のチームには、日本人で初となる欧州発明家賞が付与されています。

デンソーウェーブの QR コード開発者、原氏が日本人初の欧州発明家賞を受賞 - インターネットコム

雑記(2014/06/18)

 

というわけで、JavaScriptQRコード生成を行ってみました。


QRコードは文章も表現可能ですので、テキストエリアにしました。

文字数に応じて出力されるQRコードの大きさと粒度が変わります。

試してみたところ、10,208文字以上はスクリプトエラーとなりました。

また、大きすぎるとディスプレイ越しでiPhoneのカメラをかざしても認識できませんでした。

それ以外では普通に出力されると思います。

JavaScriptでバーコード生成

以前、講談社コミックスからバーコードが消えていた件で、書籍バーコードの生成を行う記事を書きました。

そのときはバーコードの生成によそ様のサイトを紹介したのですが、調べてみたらjQueryプラグインが公開されていました。

http://barcode-coder.com/

 

というわけで、JavaScriptでバーコード生成を行ってみました。

今回は書籍バーコードに特化して、13桁の数字に対してのみ生成を行っています。

なお、1111111111111のように入力しても1111111111116というバーコードが生成されますが、これはチェックディジットという仕組みです。

バーコードはいくつかの規格があり、それぞれに計算式が用意されています。

「JAN/EAN 13」では13桁の数字を以下のように計算し、バーコードとしての正当性をチェックしています。

チェックデジット計算方式 |JANコード登録│ 一般財団法人流通システム開発センター

  1. すべての偶数位置の数字を加算する。
  2. 1の結果を3倍する。
  3. すべての奇数位置の数字を加算する。
  4. 2の答えと3の答えを加算する。
  5. 最後に"121"*1の下1桁の数字を"10"から引く。この場合は"10"から"1"を引き算した答えの"9"がチェックデジットである。
  6. 下1桁が"0"となった場合は、チェックデジットはそのまま"0"となる。

つまり、最初の12桁が決まると最後の1桁は自動的に決まります。この13桁目をチェックディジットと呼びます。

 

 

これだけだと味気ないので、最近買った書籍のバーコードを載せてみます。

新約 とある魔術の禁書目録 (11)
 

オティヌス編が決着したあとの新刊ですが、どのようになるのか想像も付きませんでした。

今回は科学オンリーで過去に重きを置いた構成となっており、小休止+第5位の本格参戦の前振りといった感じでした。

 

六畳間の侵略者!? (17)
 

ここ数冊はバトルメインだったため、久しぶりの日常てんこもりの1冊でした。

アニメも無事終了し、一段落といったところです。

2期があるとすれば、いよいよ「白銀の姫と青き騎士」です。やってほしいなぁ。

関連:JavaScriptでQRコード生成

*1:項番1の99と項番3の22の合算値

iframeのクロスドメイン事情

iframe越しの別ドメインでのcookieのやりとりに四苦八苦しました。

前提条件

  • iframeの呼び出し元と先でサーバドメインが異なる
  • 2つのサーバは両方編集可能 (わるいことしたいわけではない)
  • サーブレットにおいてcookieはsubmit時に送信される [参考]
  • サーバ1のアプリケーションにサーバ2の画面のパラメータを渡したい
    …が、クロスドメイン制約に引っかかる

やりたいこと

  • サーバ1 (srv1.example.co.jp)
  • iframeの呼び出し元
  • アプリケーション処理する
  • サーバ2 (srv2.example.co.jp)
  • iframeの呼び出し先
  • 画面にパラメータ入力する

サーバ1側からiframeタグでサーバ2側の画面を呼び出しています。

クロスドメイン制約により、基本的にサーバ1⇔サーバ2のパラメータはやりとりできません。

が、親フレームから子フレームのJavaScriptを呼び出すことは可能です。数少ない例外にあたります。

もっとも、ChromeFirefoxでは普通に参照できないエラーとなります。IEだけなぜか通ります。hostsとかいじる必要がありますが。

 

本来であれば、サーバ1側にsubmit処理を書き、JavaScriptを通じてパラメータを拾うほうが楽かもしれませんが、諸事情によりサーバ2側のファイルは複数人でメンテナンスされる可能性があるので、なるべくシンプルにしたいです。

そこで、サーバ2側のsubmit時にサーバ1側の特定の関数を呼んでもらうルールだけ設定し、サーバ1側でパラメータチェックしたりcookie書いたりすることにしました。

起こったこと

  1. サーバ2でsubmitする。その際にサーバ1の関数hoge()を呼ぶ
  2. 関数hoge()でdocument.cookieに送りたいパラメータを書き込む
  3. submitを行いサーバ1のアプリケーションの処理を実行する

このときに、document.cookieに書いたパラメータが渡ってきませんでした。

アプリケーションでcookie情報の取得を試した際は受け取れていたのですが、そのときはサーバ1側のファイルからsubmitしていたようです。

 

IEの開発者ツール(F12)でcookie情報を確認してみると、確かにhoge()内で書いたパラメータがcookieに書き込まれているのですが、ドメインsrv2.example.co.jpとなっていました。

つまり、サーバ1側の関数でcookieを書いたとしても、関数を呼び出した側のドメインで書き込まれるということになります。

…まぁ、jQueryなどの外部スクリプトを使ってcookieを書いたときに、jQueryが置かれているサーバのドメインで書かれても困りますね。確かに。

 

悩みましたが、cookieの書き込みをちゃんと調べてみると、パラメータの他にドメインを指定して書き込めるようです。

Cookieの書き込み - とほほのCookie入門

document.cookie = "param=fuga; domain=.example.co.jp"という感じで、cookieのスコープを少し広げて渡すことで上手く取得できました。

JavaScriptで簡単にグラフが作成できる「Google Charts」

以前からhtmlとJavaScriptでグラフを描画できまいかと考えていました。

jQueryを使うようになってからプラグインを探してみましたが、ノーマルなJavaScriptでも描けそうなライブラリがありました。


Google Charts — Google Developers

 

Googleより提供されている開発ツールの1つです。

この「Google Charts」を使って簡単なグラフを描いてみました。

円グラフ

 

線グラフ

 

データは以前に調査したものを使用しています。[携帯電話契約数の今後]

棒グラフ

 

他にも様々なパターンのグラフが作成できます。

グラフの種類とそのサンプルコードは以下のページに掲載されています。

Chart Gallery - Google Charts — Google Developers

ブログ内にも描けるようなので、何らかのデータを載せる際にはわかりやすいグラフもセットにできそうです。

jQueryで遊んでみましたの3

前回

昨日に比べると軽いコードです。

クリックするたびにテーブル上の文字を切り替えるだけです。

一応、ついでに背景とかtitleとかも替えてますが、パターンの判定は単純なswitch文です。

クリックするたびに文字を切り替える

デモ

グー チョキ パー
グー チョキ パー

コード

$('#tableのid').find('td').click(function() {
  var txt;
  var color;
  switch($(this).text()) {
    case 'グー':
      txt = 'チョキ';
      color = '#9f9';
      break;
    case 'チョキ':
      txt = 'パー';
      color = '#f99';
      break;
    case 'パー':
      txt = 'グー';
      color = '#99f';
      break;
  }
  $(this).text(txt);
  $(this).attr('title', txt);
  $(this).css('background-color', color);
});

$.children()だと孫要素を追うのが面倒なようなので$.find()を使ってます。

<td>タグそのものをthisで参照していても、$.text()は<td>内の文字まで辿れるのがナイスですね。<span>タグとかあると更に子要素を見ないと駄目かもですが。

jQueryプラグイン「jqGrid」を勉強しました

必要に迫られたので。

 

jQuery Grid Plugin – jqGrid [日本語マニュアルサイト:jqGrid]

jqGridデモ

jqGrid Demo

※TOK2で404となっていました。すみません…

主な機能

  • jqGridを使用したグリッドテーブル
  • jqGridでのヘッダ2段組
  • jqGridでの横固定スクロール
  • jqGridで動的にカラム追加 (無理矢理)
  • 各種jQuery UIのテーマを動的に変更可能 (おまけ)

コード

<link rel="stylesheet" type="text/css" href="ui.jqgrid.css" /> <!-- jqGrid css -->
<link rel="stylesheet" type="text/css" href="jquery-ui.css" /> <!-- jQuery UI css -->
<script type="text/javascript" src="jquery-1.11.1.min.js"></script> <!-- jQuery js -->
<script type="text/javascript" src="jquery-ui.min.js"></script> <!-- jQuery UI js -->
<script type="text/javascript" src="jquery.jqGrid.min.js"></script> <!-- jqGrid js -->
<script type="text/javascript" src="i18n/grid.locale-ja.js"></script> <!-- jqGrid locale js -->
<script type="text/javascript">
<!--
// グリッドに表示するデータ
var jsonData = [
  {no:'003', name:'メガフシギバナ', hp:80, a:100, b:123, c:122, d:120, s:80, sp:'あついしぼう'}
 ,{no:'006', name:'メガリザードンX', hp:78, a:130, b:111, c:130, d:85, s:100, sp:'かたいツメ'}
 ,{no:'006', name:'メガリザードンY', hp:78, a:104, b:78, c:159, d:115, s:100, sp:'ひでり'}
 ,{no:'009', name:'メガカメックス', hp:79, a:103, b:120, c:135, d:115, s:78, sp:'メガランチャー'}
// ~省略~
];
// カラム名データ
var columnNameData = ['No', 'Name', 'HP', '攻撃', '防御', '特攻', '特防', '素早', '備考'];
// カラム設定データ
var columnParamData = [
  {name:'no', index:'no', width:25, align:'center', sorttype:'text', resizable:false, frozen:true}
 ,{name:'name', index:'name', width:100, align:'left', sorttype:'text', frozen:true}
 ,{name:'hp', index:'hp', width:30, align:'right', sorttype:'int', frozen:false}
 ,{name:'a', index:'a', width:30, align:'right', sorttype:'int'}
 ,{name:'b', index:'b', width:30, align:'right', sorttype:'int'}
 ,{name:'c', index:'c', width:30, align:'right', sorttype:'int'}
 ,{name:'d', index:'d', width:30, align:'right', sorttype:'int'}
 ,{name:'s', index:'s', width:30, align:'right', sorttype:'int'}
 ,{name:'notes', index:'notes', width:100, align:'left', sortable:false, hidden:true}
]
var isLoaded = false;
$(function(){
  createGrid = function () {$('#hogeGridTable').jqGrid({
    datatype : 'local' // データタイプ
    , colNames : columnNameData // カラム名
    , colModel : columnParamData // カラム設定
    , data : jsonData // 読み込むデータ
    , rowNum : 10 // 1ページの表示件数
    , rowList : [5, 10, 15] // rowNumの選択リスト
    , caption : 'メガシンカ一覧' // キャプション
    , height : 150 // グリッドテーブルの高さ
    , width : 480 // グリッドテーブルの幅
    , pager : 'hogePager' // ページャ
    , shrinkToFit : true // カラム幅の自動調整
    , viewrecords: true // フッタの右下に件数情報を表示
    , gridComplete : function() { // 読み込み完了時に呼ばれる関数
      if (!isLoaded) {
        $('#hogeGridTable').jqGrid('setGroupHeaders', {
          useColSpanStyle : true // カラム縦結合
          , groupHeaders : [ // ヘッダを2段にする
            {
              startColumnName : 'hp' // 下段の開始カラム
              , numberOfColumns : 6 // 結合するカラム数
              , titleText : 'ステータス' // 結合後の上段カラム名
            }
          ]
        });
        $('#hogeGridTable').jqGrid('setFrozenColumns'); // カラムの固定化(固定するカラムはcolModelのfrozen)
        isLoaded = true;
      }
    }
  })};
  createGrid();

  $('#hogeAddCol').click(function() {
isLoaded = false
    $("#hogeGridTable").GridUnload(); // グリッドを削除
    columnNameData.push("特性"); // カラムの追加
    columnParamData.push({name:'sp', index:'sp', width:200, align:'left', sortable:false}); // 追加したカラムの設定を追加
    createGrid(); // 列を追加してグリッド再作成
  });
});
//-->
</script>

<table id="hogeGridTable"></table> <!-- グリッド部分 -->
<div id="hogePager"></div> <!-- ページャ部分 -->
<input type="button" id="hogeAddCol" value="列追加" />

解説

html部分は単純明快ですので、スクリプト部分を見ていきます。

必要なファイル

コードに記載していますが、必要なのは以下のファイルです。

  • jQuery : jquery-1.11.1.min.js (サイト)
  • jQuery UI : jquery-ui.min.js、jquery-ui.css (サイト)
  • jqGrid : jquery.jqGrid.min.js、grid.locale-ja.js、ui.jqgrid.css (サイト)

それぞれダウンロードして読み込みます。

なお、ファイル名の末尾に"min"とあるのは容量圧縮されたファイルです。

jqGridの基本コード

jqGridを使用するにあたって、基本となるのは以下のコードです。

$('#tableのid').jqGrid({
    datatype : 'local' // データタイプ
    , colNames : ['カラム1', 'カラム2', 'カラム3'] // カラム名
    , colModel : [
{name:'カラム1', id:'col1', width:100, align:'left'}
, {name:'カラム2', id:'col2', width:100, align:'center'}
, {name:'カラム3', id:'col3', width:100, align:'left'}
] // カラム設定
    , data : [
       {col1:'aaa', col2:'bbb', col3:'ccc'}
       , {col1:'ddd', col2:'eee', col3:'fff'}
       , {col1:'ggg', col2:'hhh', col3:'iii'}
     ] // 読み込むデータ
    , rowNum : 10 // 1ページの表示件数
    , rowList : [5, 10, 15] // rowNumの選択リスト
    , caption : 'gridのキャプション' // キャプション
    , height : 150 // グリッドテーブルの高さ
    , width : 480 // グリッドテーブルの幅
    , pager : 'ページャdivタグのid' // ページャ
    , shrinkToFit : true // カラム幅の自動調整
    , viewrecords: true // フッタの右下に件数情報を表示
});

以下のサイトを参考にさせてもらいました。

jqGrid関数に渡すプロパティの一覧は日本語マニュアルの以下のページに記載されています。

[プロパティ|基本のグリッド - jqGrid]

dataType : 'local'などは載っていなかったので全てを網羅しているわけではなさそうですが、非常に参考になりました。

jqGridでのヘッダ2段組、横固定スクロール

グリッドテーブルのヘッダ部分を2段組にしたり、一部のカラムを横スクロール時にも固定することができたりします。

$('#tableのid').jqGrid('setGroupHeaders', {
    useColSpanStyle : true // カラム縦結合
    , groupHeaders : [ // ヘッダを2段にする
      {
        startColumnName : 'col2' // 下段の開始カラム
        , numberOfColumns : 2 // 結合するカラム数
        , titleText : '結合名' // 結合後の上段カラム名
      }
    ]
  });
  $('#tableのid').jqGrid('setFrozenColumns'); // カラムの固定化(固定するカラムはcolModelのfrozen)
}

jqGrid関数を呼ぶ際に、第一引数で'setGroupHeaders'を指定することでヘッダを2段組にできます。

第二引数にuseColSpanStylegroupHeadersを持つオブジェクトを渡して、ヘッダの結合条件を指定します。

また、第一引数に'setFrozenColumns'を指定することで、横スクロール固定を有効にします。実際に固定するカラムは基本コードのcolModelプロパティでfrozenをtrueすることで設定可能です。
以下のサイトを参考にさせてもらいました。

jqGridで動的にカラム追加

動的にレコード(行)を追加するのはaddRowDataなどで比較的簡単に実現できますが、カラム(列)は参考になる情報がありませんでした。

無理矢理ながら追加してみたのが以下のコードです。

function() {
  $("#tableのid").GridUnload(); // グリッドを削除
  columnNameData.push("追加するカラム名"); // カラムの追加
  columnParamData.push({name:'追加するカラム名', index:'addCol', width:200, align:'right'}); // 追加したカラムの設定を追加
  createGrid(); // 列を追加してグリッド再作成
};

解説では触れていませんでしたが、jqGridは”createGrid"という関数として定義しています。

GridUnload()でグリッドをごそっと削除したあと、外部定義しているカラム名称の配列とカラム設定情報の配列に新しい列をpushしています。

その後はcreateGridを再度コールしてグリッドテーブルを再作成します。

実際のデータについては、本来はサーバから全て受信する想定をしているので、クライアント側で増やしたりしていません。最初から全て持たせています。

一部、以下のサイトを参考にさせてもらいました。

jqGridの備忘録 「グリッドを更新する方法」|Type2011おじさんの徒然日記

 

 

 

 

jqGridは他にもデータのフィルタリングなども可能ですが、今回は不要なので省きました。

 

 

関連:jQueryプラグイン「jqGrid」を勉強しました その2