• TOP
  • ブログ
  • プロフィール
  • お問い合わせ
  • 【Vue.js】selectタグのデザインをカスタマイズ

    【Vue.js】selectタグのデザインをカスタマイズ

    先日、jQueryを使ってselectタグをカスタマイズする方法をブログに書いたんですが、JSが長いし、なんかややこしいし、ということでVue.jsで作り直してみました。

    demo

    実装方法

    HTML

    <div id="select">
      <div class="selectBox">
    
    <!-- カスタマイズ用のdivタグ -->
        <div class="selectBox__output" v-on:click="selectorshow=!selectorshow" v-bind:class="[ selectorshow ? 'open' : '' ]">
          <span v-for="selector in selectors" v-show="selected == selector.value" v-bind:key="selector.id">{{selector.text}}</span>
        </div>
        <transition name="accodion">
        <div class="selectBox__selector" v-show="selectorshow">
          <div class="selectBox__selectorItem" v-for="selector in selectors" v-on:click="selected=selector.value,selectorshow=!selectorshow" v-bind:key="selector.id">{{selector.text}}</div>
        </div>
        </transition>
    <!-- カスタマイズ用のdivタグ -->
    
        <select name="" id="" v-model="selected">
          <option v-for="selector in selectors" v-bind:value="selector.value" v-bind:key="selector.id">{{selector.text}}</option>
        </select>
      </div>
    </div>

    CSS

    *{
      box-sizing: border-box;
    }
    ul,li{
      margin: 0;
      padding: 0;
      list-style: none;
    }
    
    .selectBox{
    position: relative;
    width: 10em;
    height: 60px;
    }
    .selectBox select{
      position: absolute;
      left: 100%;
      top: 100%;
      width: 100%;
      height: 100%;
    }
    .selectBox__output{
      display: flex;
      align-items: center;
      position: relative;
      width: 100%;
      height: 100%;
      padding: 1em;
      border: 1px solid #ccc;
      background-color: #fff;
      border-radius: 5px;
      z-index: 2;
    }
    .selectBox__output::after{
      display: block;
      position: absolute;
      right: 3%;
      top: 50%;
      font-family: "CONDENSEicon";
      transform: translateY(-50%);
      content: "û";
    }
    .selectBox__output.open::after{
      transform: translateY(-50%) rotate(180deg);
    }
    .selectBox__selector{
      position: absolute;
      left: 0;
      top: calc(100% - 1px);
      width: 100%;
      border: 1px solid #ccc;
      background-color: #fff;
      transform-origin: left top;
      z-index: 10;
    }
    .selectBox__selectorItem{
      width: 100%;
      padding: .75em;
    }
    .selectBox__selectorItem+.selectBox__selectorItem{
      border-top: 1px solid #ccc;
    }
    .selectBox__selectorItem:hover{
      background-color: #0d61ad;
      color:#fff;
    }
    
    .accodion-enter-active, .accodion-leave-active {
      transition: .5s;
      overflow: hidden;
    }
    .accodion-enter, .accodion-leave-to {
      transform: scaleY(0);
    }
    .accodion-leave, .accodion-enter-to {
      transform: scaleY(1);
    }

    Vue.js

    var tab = new Vue({
      el: '#select',
      data: {
        selected: 'cat1',
        selectorshow: false,
        selectors: [
          { id: 1, text: "カテゴリ1", value: "cat1" },
          { id: 2, text: "カテゴリ2", value: "cat2" },
          { id: 3, text: "カテゴリ3", value: "cat3" },
        ]
      }
    });
    

    簡単な説明

    まず、JSでselectorsという配列を用意し、optionタグと見た目カスタマイズ用のdivタグにv-forで展開します。

    HTMLでは、selectタグにv-modelディレクティブにselectedという値を持たせています。これは、selectタグの変更を変数に格納するためのもので、カスタマイズ用のdivタグと値を共有できるようになります。
    selectedには、カスタマイズ用のdivタグで選択した要素の値が格納されるようになっており、jQueryでやっていたJSで取得して格納して出力みたいなのをこれ一個でやってくれます。

    カスタマイズ用のdivタグは、.selectBox__outputという出力用のタグと、.selectBox__selectorという選択肢用のタグがあります。
    出力用のタグでは、クリック時に変数のbool値を操作しており、そのbool値によってクラス追加や選択肢用のタグの表示非表示を操作しています。v-bind:classに設定されてるのは三項演算子というもので、selectorshow変数がtrueならopenクラスをつけるというものです。

    選択肢用のタグでは、v-showディレクティブでselectorshow変数がtrueなら表示するというようになっています。
    それぞれの選択肢ではクリック時にselectedに配列のvalueの値を格納するようにしています。
    このvalueの値はoptionタグのvalueの値と一緒になっているので、選択されたものがselectedに格納され、v-modelディレクティブを通じてselectタグに反映されるようになっています。
    一緒にv-on:clickの値になっているのは、選択肢を閉じる為のものです。

    また、選択肢を囲むようにtransitionというタグがあります。
    これはv-showディレクティブで表示非表示を切り替えるときにアニメーションをつける為のもので、これで囲むとCSSでアニメーションを設定できます。
    CSSの最後の10行がそれになります。

    まとめ

    v-modelという便利なやつのおかげで簡単にselectタグとの連動ができました。
    これが双方向バインディングってやつです。
    「双方向」なので、今回はやってませんが、selectタグの値を変更してもdivタグの方に反映されます。
    selectタグ以外のform系要素で使えるそうなので、またいろいろやってみたいと思います。

    このくらいになると、だいぶVue.jsの方が便利という感じがしてきました。
    そろそろ規模が大きめのものにチャレンジしてみようかな。。。

    yori3

    2020年2月20日
    【Web】いろいろ試してみた話
    css, html, vue.js
  • 【jQuery】selectタグのデザインをカスタマイズ

    【jQuery】selectタグのデザインをカスタマイズ

    フォームをつくるとき、見た目を変えたいことがあるかと思います。
    だいたいの要素はCSSでカスタマイズできますが、selectタグのoption要素にはCSSが効きません。

    そこで、CSSとjQueryでselectタグを操作する方法をまとめました。

    demo

    実装方法

    HTML

    <div id="select">
      <div class="selectBox">
        <div class="selectBox__output"></div>
        <div class="selectBox__selector">
          <div class="selectBox__selectorItem" data-select="cat1">カテゴリ1</div>
          <div class="selectBox__selectorItem" data-select="cat2">カテゴリ2</div>
          <div class="selectBox__selectorItem" data-select="cat3">カテゴリ3</div>
        </div>
        <select name="" id="select01">
          <option value="cat1">カテゴリ1</option>
          <option value="cat2">カテゴリ2</option>
          <option value="cat3">カテゴリ3</option>
        </select>
      </div>
    </div>

    CSS

    *{
      box-sizing: border-box;
    }
    ul,li{
      margin: 0;
      padding: 0;
      list-style: none;
    }
    
    .selectBox{
      position: relative;
      width: 10em;
      height: 60px;
    }
    .selectBox select{
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
    }
    .selectBox__output{
      display: flex;
      align-items: center;
      position: relative;
      width: 100%;
      height: 100%;
      padding: 1em;
      border: 1px solid #ccc;
      background-color: #fff;
      border-radius: 5px;
      z-index: 2;
    }
    .selectBox__output::after{
      display: block;
      position: absolute;
      right: 3%;
      top: 50%;
      font-family: "CONDENSEicon";
      transform: translateY(-50%);
      content: "û";
    }
    .selectBox__output.open::after{
      transform: translateY(-50%) rotate(180deg);
    }
    .selectBox__selector{
      display: none;
      position: absolute;
      left: 0;
      top: calc(100% - 1px);
      width: 100%;
      border: 1px solid #ccc;
      background-color: #fff;
      z-index: 10;
    }
    .selectBox__selectorItem{
      width: 100%;
      padding: .75em;
    }
    .selectBox__selectorItem+.selectBox__selectorItem{
      border-top: 1px solid #ccc;
    }
    .selectBox__selectorItem:hover{
      background-color: #0d61ad;
      color:#fff;
    }

    jQuery

    //初期値
    $('.selectBox__output').each(function () {
      const defaultText = $(this).next('.selectBox__selector').children('.selectBox__selectorItem:first-child').text()
      $(this).text(defaultText);
    })
    
    //出力の枠をクリックした時の動作
    $('.selectBox__output').on('click', function (e) {
      e.stopPropagation();
      if ($(this).hasClass('open')) {
        $(this).next('.selectBox__selector').slideUp();
      } else {
        $(this).next('.selectBox__selector').slideDown();
      }
      $(this).toggleClass('open');
    });
    
    //選択肢をクリックした時の動作
    $('.selectBox__selectorItem').on('click', function () {
      const selectVal = $(this).data('select');
      const selectText = $(this).text();
      $(this).parent('.selectBox__selector').prev('.selectBox__output').text(selectText);
      $(this).parent('.selectBox__selector').slideUp();
      $(this).parents('.selectBox__output').slideDown();
      $(this).parent('.selectBox__selector').next('select').val(selectVal);
    });

    簡単な説明

    空のdivタグ(.selectBox__output)を用意し、そこに選択しているoption要素のテキストをjQueryで取得して出力するようにしています。(jQueryのselectText変数に値が入る)

    また、そのすぐ後に、option要素と同じ選択肢を入れたdivタグ(.selectBox__selector)を用意しています。この選択肢にはdata属性でoption要素のvalueと同じ値を持たせています。
    そうすることで、この選択肢を選択すると、同じ値のoption要素が選ばれた状態にできるようになっています。(jQueryのselectVal変数に選択した値が入る)

    こうして用意した要素はdivタグやspanタグなので、CSSで見た目をカスタマイズすることができます。
    selectタグはCSSで後ろに隠してしまえばOKです。
    (demoではわかりやすいように見える位置においています。)

    まとめ

    form系のタグはブラウザごとに見た目が異なるので、サイトのトンマナに合わせて変更したいということはよくあると思います。
    radioやcheckboxなどはCSSだけでも見た目カスタマイズできるのですが、selectタグはどうにもならず、jQueryでいろいろすることになってしまいました。。。

    もう少し手軽できればなあ、と思うのですが、optionにCSSが効かないのでどうしようもない。。。

    yori3

    2020年2月16日
    【Web】いろいろ試してみた話
    css, html, jQuery
  • 【Vue.js】タブで切り替えるコンテンツの実装

    【Vue.js】タブで切り替えるコンテンツの実装

    先日、jQeuryでタブ切り替えを実装する方法を書いたのですが、今度はそれをVue.jsで実装してみることにしました。

    作ったもの

    こちらです。

    demo

    実装方法

    HTML

    <div id="tab">
      <ul class="tabMenu">
        <li class="tabItem" v-for="(tab,index) in tabs" v-on:click="activetab=(index+1)" v-bind:class="[ activetab === (index+1) ? 'active' : '' ]">{{tab.tabitem}}</li>
      </ul>
    
      <div class="tabContent">
        <div class="tabBlock" v-for="(item,index) in contents" v-bind:class="[ activetab === (index+1) ? 'show' : '' ]">
          <div class="tabBlock__inner">
            <h3 class="tabBlock__title">{{item.title}}</h3>
            <p class="tabBlock__text">{{item.text}}</p>
          </div>
        </div>
      </div>
    </div>

    CSS

    *{
      box-sizing: border-box;
    }
    ul,li{
      margin: 0;
      padding: 0;
      list-style: none;
    }
    #tab{
      max-width: 640px;
      margin: 0 auto;
    }
    
    .tabMenu{
      display: flex;
      justify-content: space-around;
      align-items: flex-end;
      list-style: none;
      border-bottom: 1px solid #ffa594;
    }
    .tabItem{
      display: flex;
      justify-content: center;
      align-items: center;
      height: 60px;
      flex-basis: 32%;
      flex-shrink: 0;
      border-left: 1px solid #ffa594;
      border-right: 1px solid #ffa594;
      border-top: 1px solid #ffa594;
      border-radius: 10px 10px 0 0;
      color: #ffa594;
      font-weight: bold;
      text-align: center;
    }
    .tabItem.active{
      background-color: #ffa594;
      color: #fff;
    }
    
    .tabContent{
      position: relative;
      border: 1px solid #ffa594;
    }
    .tabBlock{
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      padding: 60px;
      opacity: 0;
      transition: .5s;
    }
    .tabBlock.show{
      position: static;
      opacity: 1;
    }

    Javascript(Vue.js)

    var tab = new Vue({
      el: '#tab',
      data: {
        activetab: 1,
    
        tabs: [
          { tabitem: "tab01" },
          { tabitem: "tab02" },
          { tabitem: "tab03" },
        ],
        contents: [
          {
            title: "コンテンツ01",
            text: "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト"
          },
          {
            title: "コンテンツ02",
            text: "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト"
          },
          {
            title: "コンテンツ03",
            text: "テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト"
          },
        ],
      }
    });

    簡単な説明

    v-forでタブメニューを生成し、その順番を利用して activetab という変数の値を変化させて、今どれがアクティブな状態なのかを判定しています。
    また、表示するコンテンツの方も、同じようにv-forで生成して、同じ順番のものをアクティブにするようにしています。

    まとめ

    jQueryのものと比べるとこちらの方がややコードがすっきりして見えるように思います。
    実際、編集するときはJSの配列の値を変更するだけなので、触るところも明白かと。
    HTMLが長くなるとどこ触っていいかわからなくなってくるので。。。

    引き続き勉強がてらVue.jsでいろいろやってみたいと思います。

    yori3

    2020年2月5日
    【Web】いろいろ試してみた話
    css, html, vue.js
  • 【jQuery】タブで切り替えるコンテンツの実装

    【jQuery】タブで切り替えるコンテンツの実装

    メモもかねてタブで切り替えるコンテンツをjQueryで作る方法を紹介します。

    これを書こうと思いながら改めてブログを振り返ってると以前にも書いてたんですが、id使ってたりちょっとまどろっこしいなと思ったので、もう少しシンプル目なやつにしました。

    demo

    実装方法

    html

    <div id="tab">
      <ul class="tabMenu">
        <li class="tabItem tabItem-current">tab01</li>
        <li class="tabItem">tab02</li>
        <li class="tabItem">tab03</li>
      </ul>
    
      <div class="tabContent">
        <div class="tabBlock tabBlock-show">
          <div class="tabBlock__inner">
            <h3 class="tabBlock__title">コンテンツ01</h3>
            <p class="tabBlock__text">テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト</p>
          </div>
        </div>
        <div class="tabBlock">
          <div class="tabBlock__inner">
            <h3 class="tabBlock__title">コンテンツ02</h3>
            <p class="tabBlock__text">テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト</p>
          </div>
        </div>
        <div class="tabBlock">
          <div class="tabBlock__inner">
            <h3 class="tabBlock__title">コンテンツ03</h3>
            <p class="tabBlock__text">テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト</p>
          </div>
        </div>
      </div>
    </div>

    CSS

    *{
      box-sizing: border-box;
    }
    ul,li{
      margin: 0;
      padding: 0;
      list-style: none;
    }
    #tab{
      max-width: 640px;
      margin: 0 auto;
    }
    
    .tabMenu{
      display: flex;
      justify-content: space-around;
      align-items: flex-end;
      list-style: none;
      border-bottom: 1px solid #ffa594;
    }
    .tabItem{
      display: flex;
      justify-content: center;
      align-items: center;
      height: 60px;
      flex-basis: 32%;
      flex-shrink: 0;
      border-left: 1px solid #ffa594;
      border-right: 1px solid #ffa594;
      border-top: 1px solid #ffa594;
      border-radius: 10px 10px 0 0;
      color: #ffa594;
      font-weight: bold;
      text-align: center;
    }
    .tabItem.tabItem-current{
      background-color: #ffa594;
      color: #fff;
    }
    
    .tabContent{
      position: relative;
      border: 1px solid #ffa594;
    }
    .tabBlock{
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      padding: 60px;
      opacity: 0;
      transition: .5s;
    }
    .tabBlock.tabBlock-show{
      position: static;
      opacity: 1;
    }

    jQuery

    const tabLen = $('.tabItem').length;
    for (i = 0; i < tabLen; i++) {
      tabChange(i);
    }
    
    function tabChange(index) {
      $('.tabItem:nth-child(' + (index + 1) + ')').on('click', function (e) {
        e.preventDefault();
        $('.tabItem.tabItem-current').removeClass('tabItem-current');
        $(this).addClass('tabItem-current');
        $('.tabBlock.tabBlock-show').removeClass('tabBlock-show');
        $('.tabBlock:nth-child(' + (index + 1) + ')').addClass('tabBlock-show');
      });
    }

    簡単な説明

    クリックしたタブが何番目かを取得して、それに対応するコンテンツエリアを表示するようにしています。
    htmlとしては共通のクラスをつけるだけなので、更新がとてもシンプルになるかなと思います。
    複数設置する場合はeach構文などを使えばできるかと思います。

    まとめ

    よく使うので、まとめておきました。
    コードまとめておいておくと使うときコピペして、必要なところだけいじれば使えるので、とても便利です。

    また、自分用にメモしてるやつをいくつかブログに出していくようにしたいと思います。

    同じものVue.jsでもつくりました。

    yori3

    2020年2月2日
    【Web】いろいろ試してみた話
    css, html, jQuery
  • サイトをブロックエディタ化した話【Kansai WordPress Meetup@KyotoのLTの補足】

    サイトをブロックエディタ化した話【Kansai WordPress Meetup@KyotoのLTの補足】

    先日のWordPress Meetup KyotoでLT枠でサイトをブロックエディター化した話をしたので、少し詳細をこちらにまとめたいと思います。

    その時のスライドはこちら

    スタイルの設定

    編集画面の右サイドに出てくる、「スタイル」という設定項目をカスタマイズしました。

    画像ブロックや引用ブロックなど一部のブロックにはデフォルトで入っているのですが、普通は「高度な設定」のところにクラス名を手動で入れないといけないところを、目で見て選択できるようになるというものです。
    あらかじめ設定されたクラスがつくだけなので、やっていることは高度な設定と同じですが、コーディング苦手な人にも優しいつくりになります。

    今回やったのはソースコードブロックに対してスタイルを設定しました。

    prism.jsというシンタックスハイライターを使って、記事に埋め込んだソースコードをハイライトできるようにクラス名をつけるようにしています。
    prism.jsでは「language-{言語名}」というクラスをpreタグにつけると、その中のソースコードをハイライトしてくれるという機能があるので、それにそってクラスがつくようになっています。

    エディター用のJSに下記のように書いて、functions.phpで読み込むだけです。

    //コードシンタックスブロック
    wp.blocks.registerBlockStyle( 'core/code', {
    	name: 'language-html',
    	label: 'HTMLシンタックス',
    	isDefault: false,
    } );
    wp.blocks.registerBlockStyle( 'core/code', {
    	name: 'language-css',
    	label: 'CSSシンタックス',
    	isDefault: false,
    } );
    wp.blocks.registerBlockStyle( 'core/code', {
    	name: 'language-js',
    	label: 'JSシンタックス',
    	isDefault: false,
    } );
    wp.blocks.registerBlockStyle( 'core/code', {
    	name: 'language-php',
    	label: 'phpシンタックス',
    	isDefault: false,
    } );

    こういった技術系の話をする記事ではソースコードを入れることがよくあるので、用意しておくと記事を書くスピードがだいぶ上がっていい感じです。

    固定ページもブロックエディターでつくる

    もともと静的サイトからWordPress化したこともあって、固定ページは静的なHTMLがほとんどの状態で書いていました。

    元々のソースコード(一部抜粋)

    <div class="about__block about__block-profile">
      <h2 class="about__ttl">Profile</h2>
      <div class="profile__img"><img src="<?php echo get_template_directory_uri(); ?>/images/img_mine.jpg" alt=""></div>
      <dl class="profile__content profileList">
        <dt>Name</dt>
        <dd>西村 依泰</dd>
        <dt>Birth/Age</dt>
        <dd>1988.04.01/30歳</dd>
        <dt>Live</dt>
        <dd>大阪府</dd>
        <dt>Work</dt>
        <dd>元小学校教諭(〜2015)<br>マークアップ・CSSコーディング(2016〜)<br>ディレクション(2018〜)</dd>
        <dt>hobby</dt>
        <dd>楽器(ピアノ・ギター)、推理小説、TVゲーム</dd>
        <dt>Like</dt>
        <dd>コーヒー、甘味</dd>
        <dt>Community</dt>
        <dd>Word Camp Kansai 2016 実行委員<br>Word Camp Kyoto 2017 実行委員<br>Word Camp Osaka 2018 実行委員</dd>
      </dl>
    </div>
    
    <div class="about__block about__block-skill">
      <h2 class="about__ttl">Skill</h2>
      <div class="skill__content skill__content-tools">
        <h3>Tools</h3>
        <ul>
          <li>Photoshop</li>
          <li>Illustrator</li>
          <li>Adobe XD</li>
          <li>Dreamweaver</li>
          <li>Atom</li>
          <li>Google Analytics</li>
          <li>Github</li>
        </ul>
      </div>
      <div class="skill__content skill__content-language">
        <h3>Language</h3>
        <ul>
          <li>HTML</li>
          <li>CSS</li>
          <li>SCSS</li>
          <li>Javascript</li>
          <li>jQuery</li>
          <li>Vue.js</li>
          <li>PHP</li>
          <li>WordPress</li>
        </ul>
      </div>
    </div>

    これをブロックエディターに置き換えてみました。

    基本はデフォルトのブロックをつかって、「高度な設定」にクラス名を設定することでレイアウトを作っていきました 。

    ただ、上記コードにもあるように、dlタグを使っているところがあったので、そこは勉強もかねて、ネット上の情報の参考に自分で作ってみることにしました。

    コードを入れると長くなるので省略しますが、下記のサイトを参考に進めました。

    Before Gutenberg – ブロックの入れ子InnerBlockの作成

    Gutenbergブロック開発

    カスタムブロックは割となんでも作れる感じですが、学習コストが高めなので、CSSを用意しておいて高度な設定でクラス名をつけることで対応する方がやりやすそうだと思いました。
    クライアントワークでは前述のスタイルをカスタマイズするか、再利用ブロックを活用することで扱いやすくなるかと思います。

    フロントとエディターの見た目を合わせる

    フロント(サイト側)の見た目とエディター(編集画面)の見た目があっている方が編集時に完成系がわかりやすくていいということがGutenbergハンドブックに書いてある(意訳)ので、それもやってみようと思います。

    こんな風に。

    フロント
    エディター

    やることとしては、エディター用のCSSをつくって、functions.phpで読み込むだけなのですが、厄介だったのはエディター側で生成されるhtmlが階層が深くてややこしかったということです。

    グループブロックなどを使うとdivが二重三重になる上に、独自のクラスがつくので、単純にフロント用のCSSを持ってくるだけではうまくレイアウトが整えられないということがありました。

    結局、枠やリストなどのスタイルをSCSSでmixinにして、それをフロント用とエディター用でそれぞれ読み込むようにしました。
    それでも割と複雑なファイルができてしまったので、その辺りをうまく設計してSCSSファイルをもう少し簡潔にできないか、今後考えていきたいです。

    まとめ

    コアのブロックとCSSの設定だけでもそれなりにページを作ることはできたと思います。
    とりあえず触ってみようと思う人は、まずはコアのブロックでいろいろ試してみるのがいいかなと思います。

    これまでカスタムフィールドでやってたことを置き換えられないかと試行錯誤していますが、なかなかそこまでやるのは骨が折れるなぁと感じています。
    今後の課題ということで。

    yori3

    2020年1月21日
    【Web】いろいろ試してみた話
    css, Javascript, WordPress, ブロックエディタ
  • 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使って何か作ってみたいので、次のネタ探しを始めます。

    yori3

    2020年1月15日
    【Web】いろいろ試してみた話
前のページ
1 2 3 4 5 … 11
次のページ

©️ 2024 yori3 All rights reserved.