web謎の入力フォームの作り方(白猫式)

更新

web謎の入力フォームの作り方(白猫式)タイトル画像

web謎を作ろうと思ったときに障壁となるものの1つが正解判定のフォームの作成だと思います。

正解判定フォームの形式には、多くの種類があるのですが、どれも使用できるサイトが限られていたり、アラート文が出せなかったりと強み弱みがあります。

ここでは、筆者が用いている方法(白猫式)とその使い方について書いていきます。

白猫さん(@amigurumicats)に作成していただいたjsを使用するため、jsやhtmlを触れる人であることが条件です。

(この場を借りて作成していただいた白猫さんにお礼申し上げます。ありがとうございました。)


この記事はGithubで公開しているツールを筆者が使いやすいように改良したツールを説明している記事です。

情報が最新でない場合がありますので、Githubも合わせて確認しておくと安心です。

サンプル

まずはサンプルです。

フォーム1


フォーム2


  • フォーム1に「とっぷ」と入力するとこのサイトのトップページに飛ぶ
  • フォーム1に「りんく」と入力するとこのサイトのweb謎リンク集ページに飛ぶ
  • フォーム2に「あらーと」と入力するとアラートが出る
  • フォーム2に「はいけい」と入力すると背景の色が変わる
  • それ以外のキーワードでは「『{入力ワード}』は不正解です。」とフォーム下に出る

ようになっています。

実装方法

jquery版とplain版がありますが、この記事ではjquery版を使用していきます。

ファイル構成・パス設定やcssに関しては、都合の良いように変更してください。

ファイル構成

nazo/
  ├─sample.html
  ├─nazo_checker_jquery.js
  ├─nazo_checker.css
  └─answer/
      ├─sample1とっぷ.txt
      ├─sample1りんく.txt
      ├─sample2あらーと.txt
      └─sample2はいけい.txt

htmlファイル

最低限必要なソースは以下です。

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script><!-- jquery -->
  <script src="./nazo_checker_jquery.js"></script><!-- nazo_checker.js -->
  <link href="./nazo_checker.css" rel="stylesheet" type="text/css"><!-- nazo_checker.css -->
</head>

<body>
  <div class="nazoform">
    <form name="sample1" onsubmit="AnswerCheck(this);return false;">
      <input type="text" name="input" placeholder="ひらがなで入力">
      <button type="submit">送信</button>
    </form>
    <div class="result"></div>
  </div>
</body>

nazo_checker_jquery.js

function AnswerCheck(form){
  try{
    // 連打を防止するために、送信ボタンを無効化する
    $(form).find('button[type=submit]').prop('disabled', true);
    // 入力欄に入っている文字列を拾う
    send_text = $(form).children("input[type=text]")[0].value;
    // console.log("./answer/"+form.name+send_text+".txt");
    // 入力欄が空のとき、送信せず、エラーメッセージを出す
    if(send_text.length == 0){
      // $(form).next(".result").text("入力欄が空です");
      return false;
    }

    $.ajax({
      url: "./answer/"+form.name+send_text+".txt",
      type: "GET",
      dataType: "json",
      timeout: 3000,
    })
    .done(function(resp){
      // answers/{send_text}が存在する
      $(form).next(".result").text('');
      if(resp["type"] == "move"){  // 遷移
        window.location.href = resp["value"];
      }else if(resp["type"] == "alert"){  // アラート
        alert(resp["value"]);
      }else if(resp["type"] == "uftext"){  // フォーム下テキスト
        $(form).next(".result").text(resp["value"]);
      }else if(resp["type"] == "function"){  // 関数実行
        tmpFn[resp["value"]](form);
      }
    })
    .fail(function(){
        // ./answer/{form.name}{send_text}.txtが存在しない
        $(form).next(".result").text("「"+send_text+"」は不正解です。");
    })
    .always(function(){
        // 送信が完了したら、送信ボタンを有効化する
        $(form).find('button[type=submit]').prop('disabled', false);
    });
  } catch(error){
    console.error(error);
  }
}

nazo_checker.css

.nazoform {
  text-align: center;
  position: relative;
  width: 100%;
  max-width: 500px;
  margin: 3.0rem auto;
}
.nazoform form {
  display: flex;
  justify-content: center;
  align-items: center;
}
.nazoform input {
  position: relative;
  display: block;
  outline: none;
  border-radius: 0.5rem;
  border: solid 0.1rem #999999;
}
.nazoform input[type="text"] {
  padding: 0 0 0 0.5rem;
  height: 3.6rem;
  line-height: 3.6rem;
  font-size: 1.8rem;
  background-color: #ffffff;
}
.nazoform input[type="text"]:disabled {
  background-color: rgba(255,255,255,0.9);
}
.nazoform button {
  color: #ffffff;
  background-color:#00b500;
  font-size: 1.6rem;
  font-weight: bold;
  line-height: 1.5;
  position: relative;
  display: inline-block;
  padding: 0.5rem 2.0rem;
  margin-left: 0.5rem;
  cursor: pointer;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-transition: all 0.3s;
  transition: all 0.3s;
  text-align: center;
  vertical-align: middle;
  text-decoration: none;
  letter-spacing: 0.1em;
  border-radius: 0.5rem;
}

解答テキストファイル

jsファイルの15行目を見ていただくと分かるのですが、テキストファイルのファイル名は「{formの名前}{解答ワード}.txt」という名前にしてください。

(このページの例では、フォーム名が「sample1」、解答ワードが「とっぷ」なので、テキストファイル名が「sample1とっぷ.txt」となっています。)

以下はそれぞれのtxtファイルの記述例です。comment部分は何を書いても大丈夫です。

解答テキストファイルはいくつ設置しても良いため、複数ファイルを設置することで表記揺れ等別解や、処理の振り分けに対応ができます!

move例


{
  "type": "move",
  "value": "./sample.html",
  "comment": "moveの場合は遷移先を記述してください。絶対パスでも相対パスでも大丈夫です。"
}

alert例


{
  "type": "alert",
  "value": "アラートです!",
  "comment": "alertの場合はアラートで出したい文章を記述してください。"
}

uftext例


{
  "type": "uftext",
  "value": "フォーム下テキストです!",
  "comment": "uftextの場合はフォーム下に出したい文章を記述してください。"
}

function例


{
  "type": "function",
  "value": "sampleFn",
  "comment": "functionの場合は(疑似)関数名を記述してください。"
}

この際、実行するjs関数は以下のように記述してください。

let nazoformFn={};
nazoformFn.sampleFn = function(form){
  //ここに実行する関数を記述
  alert("関数を実行しました!");
};

>functionで別のjsに処理を記載してしまったらせっかく解答ワードを解析できなくした意味が無いのでは?

白猫式を使用する強み

白猫式を使用したときの強みは以下の通りです。

  • 入力形式を問わない
  • 複数の入力方式・複数ワードに対応可能
  • 遷移先の振り分けやアラート,js関数実行に対応可能
  • スクリプトから解答ワードがバレない
入力形式を問わない

wordpressでは記事自体に鍵を掛けることができるため、擬似的に同様の仕組みを利用することができますが、パスワードが半角英数字でないといけないという制約があります。

白猫式はJavaScriptで正誤判定を行なっているため、入力方式を問いません。

複数の入力方式・複数ワードに対応可能

公式LINEアカウントと同様に白猫式では複数の入力方式・入力ワードに対応することができます。

そのため、大文字と小文字、ひらがなとカタカナといった表記揺れに対応することが可能です。

遷移先の振り分けやアラートに対応可能

複数ワードに対応しているということは、答えによって遷移先のページを変えることができるということです。

また、JavaScriptのif文で判定をしてるため惜しいときに「もう少し考えてみましょう!」といったアラートメッセージを表示したり、その他独自のjs関数を実行することもできます。URL代入式の様に、答えが異なった際に404ページに飛んでしまうこともありません。

スクリプトから解答ワードがバレない

白猫式は解答ワードに対応するファイルの有無によって判定するため、ティーポット式と異なりデコードによって解答ワードがバレる心配がありません。

そのため、コンテストや速解き等の不正対策が必要な場面でも用いやすいです。

よくある質問

どういう仕組みなの?

ajaxを用いて入力ワードと同じ名前のファイルがあるかを判定しています。


ファイルがあれば中身を取得して処理を行う、ファイルが無ければ不正解としています。

どんな名前のファイルがどこにあるかが事前には分からないため、スクリプトから答えがバレません。

また、答えワードファイルの中にアラート内容や遷移先URLが記載されているため、答えが分からないうちはアラート内容や遷移先URLもバレません。(とはいえGitHub Pagesで公開すると、{アカウント名}.github.io/じゃなくて、github.io/{アカウント名}.github.io/の方にアクセスすればファイルやらフォルダやら全部見えるので留意しておいてください)

日本語を含むファイルが作れない(fc2とか)

適当なエンコードを挟んで日本語を数字列や英数字列にしましょう。


エンコードは以下の手順で行えます。

  • 予め、手元で解答ワードをエンコードした文字列を出しておき、解答ワード.txtの名前を全てその形式にする
    • ブラウザで新規タブを開いて、JavaScriptコンソールを出す
    • 「Array.prototype.map.call("ねこ", c => c.charCodeAt()).join(",")」と打って実行すると、12397,12371のような文字列が得られるので、答えワードファイルの名前を"12397,12371"にする
  • send_text取得後、チェックする前にsend_textをエンコードする処理を挟む
    • req.open("GET", ...の前の行にsend_text = Array.prototype.map.call(send_text, c => c.charCodeAt()).join(",");を追加する
    • また、どのファイルがどのワードかがかなり分かりにくくなるので、答えワードファイルのcomment欄(スクリプト側の処理では使っていないため、メモみたいな使い方ができます)に元のワードが何だったかを書いておくと多少ましになります。
macで作業しているんですが、ちゃんと行っているはずなのに上手くいかない。

macだと文字コードの関係で全角文字ファイルをアップロードすると上手く動作しない可能性があります。


実際にtxtファイルのURLを叩いていただき、404エラーになったら正しくアップされていません。

対処法としましては、1つ上の方法を用いてエンコードしていただくことになるかと思います。

jquery版+jquery.slimでうまく動作しない($.ajax is not function的なエラーが出る)

jqueryのスリムビルド版はajaxが使えないのが原因です。


スリム版ではない("slim"が入っていない)jqueryを使用するか、plain版を使用してください。

個人でカスタマイズしたい。

ご自由にどうぞ!


そもそもこの記事で公開しているスクリプトも、筆者が使用しやすいようにカスタマイズしたものです。公式にも「自由にカスタマイズができます」と書いてあるので、使用者が使いやすいようカスタマイズして良いのだと思います。

demo/index.htmlをローカルでそのまま開いた状態(URLがfile://とかになっている状態)だと、CORSにひっかかってうまく動作しない

本番環境に思い切って投げちゃうか、それでも不安ならローカルで一時的に開発サーバを動かして確認しましょう。


ローカルで一時的に開発サーバを動かすのは、例えば、お使いのPCにpythonが入ってるなら、ターミナル(コマンドプロンプト)を立ち上げて、適切なディレクトリまで移動して、python -m http.serverをすると、簡易開発サーバが立ちます。

その状態で、ブラウザのURL欄にlocalhost:8000/index.htmlを入力すれば、さっき立てた簡易開発サーバ経由で色々動かせます。 (立てた開発サーバはCtrl+CやCtrl+Dで止める(終了する)ことができます。)

functionで別のjsに処理を記載してしまったらせっかく解答ワードを解析できなくした意味が無いのでは?

functionの中身をvalueに直接書くことで解決できます。


nazo_checker_jquery.jsの//関数実行にあたる部分を以下のように書き換えることで、.txtファイルのtype=="function"の場合に、valueの文字列を関数として認識して実行してくれます。

else if(resp["type"] == "function"){ // 関数実行
  Function(resp["value"])();
}

上記修正をした場合の.txtファイルの中身例は以下です。

{
  "type": "function",
  "value": "alert('正解!!'); $('.main').addClass('seikai');",
  "comment": "ここはコメントです。処理には使わないので、メモ的な使い方をしてください。"
}

関数の記述が長い場合にテストや修正を行うのが面倒にはなりますが、この方法を使用することでソースから解答やその後の展開を推測されるのを防ぐことができます。


また、その他の質問がある場合には、公式リファレンスに解答があるかもしれません。

まとめ

  • 比較的簡単にweb謎の入力フォームは作成できる!
  • 使用する際は誤作動に注意して使用してください。

「ajaxって何だろう?」という状態のまま作成・使用ができているので、あまりホームページ作成に詳しくない人でも実装できるのではないでしょうか?

ソースを見ても答えがバレないというのは非常に大きな利点だと思いますので、web謎に限らず使ってみてください!

↓↓↓この記事をシェア↓↓↓