• 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
  • 【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

©️ 2024 yori3 All rights reserved.