jQueryで作った診断コンテンツをVue.jsで作り直してみた

以前案件で、診断コンテンツのあるサイトを制作することになり、jQueryでjsonを読み込んで作成したのですが、もしかしてVue.jsでできるんじゃないかと思い、Vue.jsの練習がてら作り直してみることにしました。

作ったコンテンツはこちらです。
demo
※見た目はどちらも同じになってます。

まずは読み込み用のjson。

json(質問項目)

[
  {
    "id":"1",
    "num": "1",
    "question": "吾輩は猫である。<strong>名前はまだ無い。</strong><br>どこで生れたかとんと見当がつかぬ。",
    "links": [
      {
        "link": "2",
        "select": "Yes"
      },{
        "link": "result_1",
        "select": "No"
      }
    ],
    "notes": ""
  },{
    "id":"2",
    "num": "2",
    "question": "吾輩は猫である。<strong>名前はまだ無い。</strong><br>どこで生れたかとんと見当がつかぬ。",
    "links": [
      {
        "link": "3",
        "select": "Yes"
      },
      {
        "link": "result_2",
        "select": "No"
      }
    ],
    "notes": ""
  },{
    "id": "3",
    "num": "3",
    "question": "吾輩は猫である。<strong>名前はまだ無い。</strong>。<br>どこで生れたかとんと見当がつかぬ。",
    "links": [
      {
        "link": "4",
        "select": "Yes"
      },
      {
        "link": "result_3",
        "select": "No"
      }
    ],
    "notes": ""
  },{
    "id": "4",
    "num": "4",
    "question": "吾輩は猫である。<strong>名前はまだ無い。</strong><br>どこで生れたかとんと見当がつかぬ。",
    "links": [
      {
        "link": "5",
        "select": "選択肢1"
      },
      {
        "link": "result_4",
        "select": "選択肢2"
      },
      {
        "link": "result_5",
        "select": "選択肢3"
      }
    ],
    "notes": "※選択肢の数が違うバージョンです。"
  },
  {
    "id": "5",
    "num": "5",
    "question": "吾輩は猫である。<strong>名前はまだ無い。</strong><br>どこで生れたかとんと見当がつかぬ。",
    "links": [
      {
        "link": "result_4",
        "select": "No"
      },
      {
        "link": "result_5",
        "select": "No"
      }
    ],
    "notes": ""
  }
]

json(結果項目)

[
  {
    "id":"1",
    "title":"結果01",
    "text":"ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。<br>この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。"
  },{
    "id":"2",
    "title":"結果02",
    "text":"ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。<br>この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。"
  },{
    "id":"3",
    "title":"結果03",
    "text":"ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。<br>この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。"
  },{
    "id":"4",
    "title":"結果04",
    "text":"ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。<br>この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。"
  },{
    "id":"5",
    "title":"結果05",
    "text":"ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。<br>掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。<br>この時妙なものだと思った感じが今でも残っている。第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。"
  }
]

jQueryでつくったもの

はじめにjQueryで作ったときのソースを載せておきます。

html

<section class="check" id="check">
    <div class="check_box is-show" id="start">
      <h2 class="check_ttl">診断テスト</h2>
      <div class="check_startBtn">
        <div class="btn"><button data-box="q1" class="btn_body check_btn startbtn">診断をはじめる</button></div>
      </div>
    </div>

    <div class="question_wrap">
      <!-- 設問 -->
    </div>


    <div class="check_box result_box">

      <!-- 結果出し分け部分 -->
    </div>

  </section>

JS(jQuery)

/* チェック項目 */
var html = "";
var htmlResult = "";
var data;
$.ajax({
  type: "GET",
  url: 'js/question.json',
  dataType: 'json',
  data: data,
}).done(function (json) {
  for (var i = 0; i < json.length; i++) {
    html += '<div class="check_box question_box" id="q' + json[i]["id"] + '">';
    html += '<h3 class="check_qNum">Q' + json[i]["num"] + '</h3>';
    html += '<p class="check_question">' + json[i]["question"] + '</p>';
    html += '<div class="check_questionBtn">';
    var selectLen = json[i]["links"].length;
    for (var j = 0; j < selectLen; j++) {
      html += '<div class="btn"><a href="" data-box="q' + json[i]["links"][j]["link"] + '" class="btn_body check_btn">' + json[i]["links"][j]["select"] + '</a></div>';
    }
    if (json[i]["notes"] != "") {
      html += '<p class="check_notes">' + json[i]["notes"] + '</p>';
    }
    html += '</div>';
    html += '</div>';

  }
  $('.question_wrap').append(html);
}).fail(function () {
  console.log('ERROR')
});

//結果
$.ajax({
  type: "GET",
  url: 'js/result.json',
  dataType: 'json',
  data: data,
}).done(function (jsonResult) {
  for (var resultNum = 0; resultNum < jsonResult.length; resultNum++) {
    htmlResult += '<div class="result_box_inner result_' + jsonResult[resultNum]["id"] + '" id="qresult_' + jsonResult[resultNum]["id"] + '">';
    htmlResult += '<h3 class="result_ttl">';
    htmlResult += '<strong>' + jsonResult[resultNum]["title"] + '</strong><br>';
    htmlResult += '</h3>';
    htmlResult += '<p class="result_text">' + jsonResult[resultNum]["text"] + '</p>';
    htmlResult += '</div>';
    htmlResult += '</div>';
  }
  $('.result_box').prepend(htmlResult);
  $('.result_box_inner').hide();
}).fail(function () {
  console.log('ERROR')
});

$(document).on('click', '.check_btn', function (e) {
  e.preventDefault();
  const nextBox = $(this).data('box');
  $(this).parents('.check_box').removeClass('is-show').addClass('is-hide');
  if (!nextBox.match(/result/)) {
    $('#' + nextBox).addClass('is-show');
  } else {
    $('#' + nextBox).show();
    $('.result_box').addClass('is-show');
  }
});

htmlは非常にシンプルですが、jQuery側がやたらと長くなって、どこに何を書いてあるのかを探すのが大変です。
さらにJS内にhtmlのソースを書くので、修正もしづらいです。

Vue.jsで作り直す

次にVue.jsで書いたコードです。

html

<section class="check">
  <div class="check_box is-show" id="start">
    <h2 class="check_ttl">診断テスト</h2>
    <div class="check_startBtn">
      <div class="btn"><button data-box="q1" class="btn_body check_btn startbtn">診断をはじめる</button></div>
    </div>
  </div>

  <div id="question" class="question_wrap">
    <div class="check_box question_box" v-bind:class="'q'+item.id" v-bind:id="'q'+item.id" v-for="item in posts">
      <h3 class="check_qNum" v-text="'Q'+item.num"></h3>
      <p class="check_question" v-html="item.question"></p>
      <div class="check_questionBtn">
        <div class="btn" v-for="link in item.links">
          <button v-bind:data-box="'q'+link.link" class="btn_body check_btn" v-html="link.select" v-on:click="check(item.id,link.link)"></button>
        </div>
        <p class="check_notes" v-if="item.notes" v-text="item.notes"></p>
      </div>
    </div>
  </div>



  <div class="check_box result_box" id="result">

    <!-- 結果出し分け部分 -->
    <div class="result_box_inner" v-for="result in results" v-bind:class="'result_'+result.id" v-bind:id="'qresult_'+result.id">
      <h3 class="result_ttl">
        <strong v-html="result.title"></strong>
      </h3>
      <p class="result_text" v-html="result.text"></p>
    </div>
  </div>

</section>

JS(Vue.js)

/* チェック項目 */
const vm = new Vue({
  el: '#question',
  data: {
    posts: []
  },
  created() {
    axios.get('js/question.json')
    .then(response => { this.posts = response.data; })
    .catch(error => {
      window.alert(error);
    } );
  }, methods: {
    check: function (i,j) {
      const thisBoxNum = 'q' + i;
      const nextBoxNum = 'q' + j;
      const thisBoxObj = document.getElementsByClassName(thisBoxNum);
      const nextBoxObj = document.getElementById(nextBoxNum);
      console.log();
      thisBoxObj[0].classList.remove('is-show');
      thisBoxObj[0].classList.add('is-hide');
      const resultBox = document.getElementsByClassName('result_box');
      const clslist = String(nextBoxObj.classList);
      if (!clslist.match(/result/)) {
        nextBoxObj.classList.add('is-show');
      } else {
        nextBoxObj.style.display = 'block';
        resultBox[0].classList.add('is-show');
      }
    }
  }
});

//結果
const vm02 = new Vue({
  el: '#result',
  data: {
    results: []
  },
  created() {
    axios.get('js/result.json')
    .then(response => { this.results = response.data; })
    .catch(error => {
      window.alert(error);
    });
  }
});

//開始ボタン
const startbtn = document.getElementsByClassName('startbtn');
document.addEventListener('click',function(e){
  e.preventDefault();
  thisbtn = e.target;
  if (thisbtn === startbtn[0]){
    thisbtn.closest('.check_box').classList.remove('is-show');
    thisbtn.closest('.check_box').classList.add('is-hide');
    const checkBox = document.getElementById('q1');
    checkBox.classList.add('is-show');
  }
});

htmlのほうはさっきと比べて長くなりましたが、JSの方は少しシンプルな見た目になりました。
また、htmlも見慣れた形になっているので、修正が入ってもそこまで大変そうな印象はありません。

やったことまとめ

Vue.jsを使った方ではaxiosを利用してjsonを読み込むようにしました。

html側では、v-forでループさせて、各設問内容を呼び出しています。
v-bind:idでそれぞれにid属性を付与し、あとで操作しやすいようにしています。
回答の数が設問によって違うので、ボタンのところで再度ループさせています。

クリックイベントでは、クリックしたときにjsonから値を取得し、その値を渡して、設問の表示非表示を切り替えるようにしています。
is-showというクラスが付与されると表示されるようになっています。

苦労したところ

v-bindで文字列を追加したい

v-bindの値を、「文字列+jsonの値」としたいときにどうしたらいいかが、はじめわりませんでした。
いくつか調べると、文字列の方をクウォートで囲って繋げばいいとわかりましたが、調べ方が難しく時間がかかりました。

ボタンの動作が思い通りいかない

次の設問へ移るボタンの動作がなかなかうまくいきませんでした。イベントハンドラでmethodsを設定して・・・というところはなんとなくわかっていたのですが、そこからどうしたら考えていることができるのかがなかなかわからず苦戦しました。
結局、引数にjsonから取得した値を入れるようにして、そこから次に表示する要素のidなどを取得するようにしました。

この辺りは普通にJSで書いたので、Vue.jsの機能を使ってもう少し効率的に書けないか検討したいと思います。

まとめ

探り探りでしたが、なんとか動いてよかったです。
なんとなくVue.jsで動かせた感じはありますが、実際、もう少しこうした方がいいとかいうところがありそうなので、時間があるときに調べてみたいと思います。

jsonでコンテンツを吐き出すのは、Vue.jsで書く方がやりやすいかなと思いました。
クリックイベントの方は、これくらいの簡単な動作ならjQueryで十分なようにも思います。(書きなれてるからかもですが)

また、Vue.js使って何か作ってみたいので、次のネタ探しを始めます。

トップへ戻る