憂しと見し世ぞ今は恋しき

HTMLでオンラインテストを作ってみた話②

javascriptから別のjavascriptを呼び出してみる

オンラインテストの出題部分の作成を続けます。
https://haruyou-blog.com/2019/05/28/post-5126/

前回で問題の表示→解答のサイクルはできたので、次に手がけるのは問題のデータを分けたファイルの作成です。
問題だけ別ファイルにできれば、あとで問題数を増やすときに楽ですからね。

ということで必要になるのは、「javascriptから別のjavascriptを呼ぶ」という処理になります。
ググってみるといろいろな方法が出てきましたが、実際に動かしてみて一番しっくりきたのがこのページで出てきた「jQueryのAjax」でした。

http://cly7796.net/wp/javascript/to-get-the-javascript-files-in-ajax/って、未だにそれが何を意味してる言葉なのかわかってませんが。

jQueryって何?

よくわかりませんが、どうやらjQueryってのはjavascriptのプラグイン的なもののようで。
こいつがあると追加コマンドが使用できる的な。
で、html内でgoogleから読み込んでおくと使えるらしいことがわかりました。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

こんなの書いとけば勝手に読み込んでくれるみたいです。
で、jQueryを導入したら、次にそいつのコマンドです。
どうやら$から始まるみたいで、あとはさっきのサイトにあったものを元に。

    $.ajax({
      type : 'GET',
      url: './js/Testdata.js',
      dataType: 'script',
      success: function(data) {
        Qdatacall();
      }
    });

前回作ったquestioncontrol.jsの関数next()内で、こんな感じで実行してみます。
Testdata.jsという別ファイルを作って、そこで関数Qdatacallを実施して、問題データの呼び出しをします。

で、関数next()内で、問題の番号を乱数で指定、その番号によってQdatacall内で問題を出力、という流れです。
で、できたのがこんな感じです。
前回とHTML、CSSに変更はありません。

See the Pen MdBPWG by Haru-You (@HaruYou) on CodePen.


見てわかる?ように、問題データは格納されていません。
haruyou-blog.com/NP/1/jsから拾ってきてくれています。

ただ、コンソールを見てると実行時に「XML パースエラー: 構文エラーです。」って表示されてるのが気になります。
けど、動いてるからいいや。

javascriptでCSVのデータを読み込む

とはいえ、javascriptじゃ問題の編集に手間がかかってしょうがありません。
次に目指すのは、問題文のデータをスプレッドシートで編集できるCSV化することです。

javascriptで配列を読み込ませて、セルごとに区切って問題文と解答を拾えるかな?というイメージです。

ひとまずググってみると、またもやAjaxを利用してできるようです。
ありがとうAjax。
アジャクスなのかエイジャックスなのか、野上彰と飯塚高史のタッグなのか知らないけど。
https://algorithm.joho.info/programming/javascript/csv-to-array/
ということで、こんな感じにquestioncontrolの中に組み込みます。
必要なときだけ関数csv();で呼びだすイメージですね。

function csvToArray(path) {
  var csvData = new Array();
  data = new XMLHttpRequest();        
  data.open("GET", path, false);//ここをtrueにすると読み込みが間に合わない  
  data.send(null);
  var LF = String.fromCharCode(10);
  var lines = data.responseText.split(LF);
  for (var i = 0; i < lines.length;++i) {
          var cells = lines[i].split(",");
          if( cells.length != 1 ) {
                  csvData.push(cells);
          }
  }
  return csvData;
}

function csv(){
  DataCSV = csvToArray("https://haruyou-blog.com/NP/1/js/QuestionDATA.csv");
}

で、ここの4行目のdata.openのところで3番目の引数にtrueを指定すると非同期読み込み、falseにすると同期読み込みってことで、同期読み込みにすると読み込み終了までフリーズしちゃうみたいです。
しかし、trueにすると読み込みが間に合わないのか、csvのデータが来ません。
どっかで先に読んどく仕組みが必要ですかね。
とりあえずfalseにしても動作に支障はないので、このまま進めます。

次に必要になるのが問題番号によって必要な配列だけを拾う処理です。
QuestionDATA.csvの1列目に問題番号を入力して、これにフィルターすればよさそうです。
で、CSVのフィルターについてググったらでてきたのがこんな感じのコード。

function CSVArray(p){
    CSVCell = _.filter( DataCSV, function(get) {
    var r = _.filter( get, function( number ) {
    return number === p;
    } );
    return r.length > 0;
    } );
}

underscoreって何?

なんか、またもや見たことない記号から始まるコマンドです。
調べてみると、underscoreって代物のようで。
Jqueryと同じように使えばいいっぽいです。

<script type="text/javascript" src="https://underscorejs.org/underscore-min.js"></script>

だもんで、HTMLでこいつを読み込み。
理由はわかりませんが、このコマンドでcsvから取り出した配列を問題番号でフィルターできてます。

最後に、フィルターして取り出した中から、さらに問題をとりだします。
2列目に問題、3列目に解答を入力してあるので、これをそれぞれ変数に代入します。

        case 3://csv問題 社会科
        q_title="鎌倉時代";
        CSVArray(3);
        a=Math.floor(Math.random()*CSVCell.length);
        question=CSVCell[a][1];
        answer=CSVCell[a][2];
        break;

問題番号3に設定された問題の数だけaに乱数振って、その中から一問出題されるイメージです。
で、こんな感じになりました。

ソースを見てもTestdataとQuestionDATAが入っていませんが、確かにデータを読んでくれてます。

この状態でのTestdata.jsはこんな感じ。

function Qdatacall(){
    //問題switch
    switch(QID){
        case 1://1けたのたし算
        c= 9;
        while (c <=9){
            a =(Math.floor( Math.random() * 9)+1);
            b =(Math.floor( Math.random() * 9)+1);
            c = a + b;
        }//計算ループ
        q_title="くりあがりの たしざん";
        question = a+"+"+b+"=";
        answer=c;
        break;
        
        case 2://1けたの引き算
        c= 10;
        while (c >=10){
            a =(Math.floor( Math.random() * 9)+10);
            b =(Math.floor( Math.random() * 9)+1);
            c=a-b;
        } //計算ループ
        q_title="くりさがりの ひきざん";
        question =a+"-"+b+"=";
        answer=c;
        break;

        case 3://csv問題 社会科
        q_title="鎌倉時代";
        CSVArray("3");
        a=Math.floor(Math.random()*CSVCell.length);
        question=CSVCell[a][1];
        answer=CSVCell[a][2];
        break;

        case 4://csv問題 ???
        q_title="なんかよくわからない問題";
        CSVArray("4");
        a=Math.floor(Math.random()*CSVCell.length);
        question=CSVCell[a][1];
        answer=CSVCell[a][2];
        break;
    
    
}// switchの終わり
    
    document.getElementById("q_title").innerHTML=q_title;
    document.getElementById("text_question").innerHTML=question;
}

function CSVArray(p){
    CSVCell = _.filter( DataCSV, function( get ) {
    var r = _.filter( get, function( number ) {
    return number === p;
    } );
    return r.length > 0;
    } );
}

でもって、csvファイルの内容がこんな感じです。

QID,問題,解答
3,源頼朝が守護・地頭の任命権を得たのは何年のことか答えなさい,1185
3,源頼朝の妻で、のちに「尼将軍」と呼ばれた人物の名を答えなさい,北条政子
3,鎌倉幕府第2代将軍の名前を答えなさい,源頼家
4,木村健吾といえば?,稲妻レッグラリアート
4,愛ってなんだ?,ためらわないことさ
4,西武ライオンズ史上最高の外国人選手は?,スティーブ・オンティベロス

4-1は「トライアングル・スコーピオン」でも正解ですし、4-2は「躊躇わないことさ」と漢字で書かれた場合どうするか。
4-3は「テリー・ウィットフィールド」「郭泰源」も別解として適用したい。デストラーデは×です。
なんてことを考えると、別解の枠をCSVに入れ、判定システムでも入力したinputansが解答および別解の配列内に含まれるかどうかの判定にする必要がありそうです。
まあ、それくらいならなんとかなりそうなんで後回し。


今回はランダムで問題番号(QID)を振りましたが、これを自由に選べるようにするのが次の作業ということになります。
なので、次回は「教科・単元を選択するシステム」を作ることにします。

出題画面と判定部分の改良

前の段落で別解について考えましたが、あれこれと改良・機能追加したい点が見えてきます。

  1. 解答の全角/半角
  2. 4択型の出題
  3. 複数解答を必要とする問題(分数とか)
  4. 問題内での画像の表示

この辺はまあ解決済みなのですが、こういった小ネタをどうやって解決していったかは、大筋が終わってから書きます。
いつの日かわかりませんが。
そんなん書く暇あったら問題作るほうが先なんでしょうけどね。