Motomichi Works Blog

その日学習したことについて書いている日記です。誰かの役に立ったらそれはそれで嬉しいです。

display:flexの学習 その0001-03 flexアイテムの伸縮比率とベース幅 flex-grow、flex-shrink、flex-basis、flexについて

参考にさせて頂いたページ

MDN

その他の記事

はじめに

今回は、以下のようなことについてです。flexアイテムに設定するプロパティです。

  • flexコンテナに空いた領域があるときに各flexアイテムが伸びる比率
  • flexコンテナの大きさにflexアイテムが入りきらないときに各flexアイテムが縮む比率
  • 伸縮していない状態のflexアイテムの幅または高さ

flex-growについて

flexコンテナに空いた領域があるときに各flexアイテムが伸びる比率です。 0を設定すると伸びません。

flex-shrinkについて

flexコンテナの大きさにflexアイテムが入りきらないときに各flexアイテムが縮む比率です。 flex-shrinkに0を設定すると、flex-basisで設定した値よりも縮まなくなります。 1以上の数値を設定したとき、flex-basisで設定した値よりも縮むようになります。

flexコンテナにflex-wrap: nowrap;を設定している場合は、コンテナよりもはみ出していきます。

flex-basisについて

伸縮していない状態のflexアイテムの幅または高さの設定です。

flex-basis - CSS | MDN」によると、設定できる値の種類は非常に豊富なようです。

autoだとコンテンツが入りきる最小値になります。

flexについて

上記した3つのプロパティを設定できるショートハンドです。

それぞれの値の初期値は以下の通りです。

flex-grow: 0
flex-shrink: 1
flex-basis: auto

flexコンテナにrowが設定されているとき、flexアイテムにflex-basisを設定すると幅に適用されるようです。 flexコンテナにcolumnが設定されているとき、flexアイテムにflex-basisを設定すると高さに適用されるようです。

display:flexの学習 その0001-02 並べる方向と改行 flex-direction、flex-wrap、flex-flowについて

参考にさせて頂いたページ

MDN

その他の記事

はじめに

今回は、大雑把にいうと、以下のようなことについてです。

  • flexアイテムを縦に並べるか横に並べるか
  • flexアイテムを改行するかしないか

flex-directionについて

flexコンテナにこのプロパティを設定します。

子要素であるflexアイテムを縦に並べるか、横に並べるかを設定できます。

また、flex-direction: row-reverse;を設定することで、右から左へ並べることができ、flex-direction: column-reverse;を設定することで、下から上へ並べることができます。

flex-wrapについて

flexコンテナにこのプロパティを設定します。

flexコンテナの幅または高さに、flexアイテムが入りきらなくなったとき、改行を許可するかしないかを設定します。

以下のような設定から選べるようです。このプロパティでもreverseがあります。

flex-wrap: nowrap;
flex-wrap: wrap;
flex-wrap: wrap-reverse;

flex-flowについて

上記した、flex-direction、flex-wrapをまとめて設定できるショートハンドです。

display:flexの学習 その0001-01 ほんとに基礎のこと

参考にさせて頂いたページ

flexible box の利用

CSS flexible box の利用 - CSS | MDN

flexプロパティについて

flex - CSS | MDN

用語について

まずは「CSS flexible box の利用 - CSS | MDN」に書いてある用語を理解しておくとよさそうです。 その中でも以下の用語は、flexに関連するCSSプロパティの説明などを理解するのに特に必要そうです。

  • flexコンテナ
  • flexアイテム
  • main axis(主軸)
  • cross axis(交差軸)
  • main start(主軸方向のスタート側)
  • main end(主軸方向のエンド側)
  • cross start(交差方向のスタート側)
  • cross end(交差方向のエンド側)

今日試している環境

windows10で以下のブラウザで試しています。

サンプルコード

以下のように書いてみました。

<style>
.flex{
  display: flex;
  height: 600px;
  background-color: #eeeeee;
}
.flex :nth-child(1){background-color: #ffeeee;}
.flex :nth-child(2){background-color: #eeffee;}
.flex :nth-child(3){background-color: #eeeeff;}
.flex :nth-child(4){background-color: #ffffee;}
.flex :nth-child(5){background-color: #eeffff;}
</style>

<div class="flex">
  <div>flexアイテム nth-child(1)</div>
  <div>flexアイテム nth-child(2)<br>テキスト</div>
  <div>flexアイテム nth-child(3)<br>テキスト<br>テキスト</div>
  <div>flexアイテム nth-child(4)<br>テキスト<br>テキスト<br>テキスト</div>
  <div>flexアイテム nth-child(5)<br>テキスト<br>テキスト<br>テキスト<br>テキスト</div>
</div>

サンプルコードについて

.flexdisplay:flex;を設定しています。これが「flexコンテナ」です。 これを設定すると、子要素は「flexアイテム」となるため、div要素は横に並びます。

その他のcssプロパティはflexとは関係ないのですが、flexアイテムがどのような領域を占めるのか、自分自身が理解するためにつけています。

5個のflexアイテムはそれぞれ、親要素であるflexコンテナの高さいっぱいに600pxで表示され、横幅はテキストが入る最小幅となりました。

試した3つのブラウザはベンダープレフィックス無しで、同じように表示されました。

初期値の変更について

今回のサンプルコードでは、親要素にdisplay:flex;を設定するだけで最小幅のdivを横並びにできて、高さはflexコンテナいっぱいに広がる。というものになりましたが、フレキシブルなレイアウトを可能にする為に以下のようなプロパティがあるようです。

W3Cのページなどで最新の仕様を確認するとよさそうです。

display:inline-flexについて

今回はdisplay:flex;を使用したため、「flexコンテナ」は横幅いっぱいに広がりましたが、display:inline-flex;を使用すると、「flexコンテナ」の幅は最小幅にできます。

vue.js 2.x その0007 v-forのindexを使って連番のclassを付与する

参考にさせて頂いたページ

サンプルコード

以下のように書いてみました。

<div id="vue-app">
  <ul>
    <li v-for="(item, index) in items" v-bind:class="['hoge-' + index]">
      {{item}}
    </li>
  </ul>
</div>

<script src="js/vue.js"></script>
<script>
  new Vue({
    el: '#vue-app',
    data: {
      items: [
        'item-0',
        'item-1',
        'item-2',
      ]
    }
  });
</script>

サンプルコードについて

v-bind:classにはオブジェクト構文と配列構文があります。

今回のサンプルでは配列構文を使用しています。

公式ページの「クラスとスタイルのバインディング - Vue.js」の配列構文の項にはdata.activeClassに格納されている文字列を適用する方法と、三項演算子の判定結果を適用する例が書かれています。

配列構文では、この他に、文字列をreturnするmethodsの実行や、静的な文字列、文字列の結合などをカンマ区切りで入れることができるようです。

今回のサンプルコードでは'hoge-‘とindexを結合して文字列にしています。

vue.js 2.x その0006 refで要素を参照する

参考にさせて頂いたページ

Vue.js

サンプルコード

以下のように書いてみました。

<div id="vue-app">
  <button v-on:click="clicked">click here</button>
  <div>
    <my-component ref="hogeElm" />
  </div>
  <div ref="fooElm">
    foo<br>foo
  </div>
</div>

<script src="js/vue.js"></script>
<script>
  new Vue({
    el: '#vue-app',
    data: {
    },
    components: {
      'my-component': {
        template: '<div><p ref="hogeChild">pタグ</p></div>',
      },
    },
    mounted: function(){
      console.log('mounted');
      // hogeElmの高さを取得します
      console.log(this.$refs.hogeElm.$el.clientHeight);
      // componentsの中にあるタグの高さを取得します
      console.log(this.$refs.hogeElm.$refs.hogeChild.clientHeight);
      // componentsにしていない要素ももちろん参照できます
      console.log(this.$refs.fooElm.clientHeight);
    },
    methods: {
      clicked: function(){
        // クリックイベントで要素を参照します
        console.log(this.$refs.fooElm.clientHeight);
      }
    }
  });
</script>

サンプルコードについて

createdではまだ参照できませんが、mountedではthis.$refsから参照できるようになります。

vue.js 2.x その0005 lodashのdebounce()とthrottle()で処理の実行を遅らせる

参考にさせて頂いたページ

はじめに

vue.jsの0.xと1.xではdebounce属性がありましたが、2.xからは無くなったことが公式ドキュメントに書いてあります。

Vue 1.x からの移行 - Vue.js

代わりにlodash.jsのdebounce()またはthrottle()を使用して、処理の実行を遅らせるサンプルを書いてみます。

サンプルコード

CDNでlodashを読み込んで、throttle()を使用するサンプルを書いてみました。

公式ドキュメントのサンプルではmethods内でdebounceを適用していますが、watch内でthrottleを適用するサンプルを書いてみました。

<div id="vue-app">
  <h1>Vue.jsの練習 静的なページ</h1>
  <input type="text" v-model="valText">
  <div>{{lateText}}</div>
<!--/#vue-app--></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="js/vue.js"></script>
<script>
  new Vue({
    el: '#vue-app',
    data: {
      valText: '',
      lateText: '',
    },
    watch: {
      valText: _.throttle(function(){
        console.log('changed');
        this.lateText = this.valText;
      }, 500)
    }
  });
</script>

vue.js 2.x その0004 アコーディオンを作る(jQueryのslideToggleみたいなものを作る)

サンプルコード(v-forで出力した要素にそれぞれ)

v-forで出力した要素それぞれにアコーディオンを入れるイメージで、以下のように書いてみました。

<style>
.accordion{}
.accordion__trigger{}
.accordion__contents-outer-wrap{
  transition: max-height 2s;
  max-height: 0;
  overflow: hidden;
  background: #eeeeff;
}
.accordion__contents-inner-wrap{
  padding: 100px 10px;
}
</style>

<div id="accordion-app">
  <ul>
    <li class="accordion" v-for="(item, index) in items">
      <button class="accordion__trigger" v-on:click="slideToggle(index)">
        accordion-trigger-{{index}}
      </button>
      <div class="accordion__contents-outer-wrap" v-bind:style="{ maxHeight: items[index].contents.maxHeight + 'px' }">
        <div class="accordion__contents-inner-wrap" ref="contentsInner">
          {{item.title}}<br>
          {{item.contents.lead}}
        </div>
      </div>
    </li>
  </ul>
</div>

<script src="js/vue.js"></script>
<script>
  var items = [
    {
      title: 'content-0',
      contents: {
        lead: 'lead-0',
        isActive: false,
        maxHeight: 0,
      }
    },
    {
      title: 'content-0',
      contents: {
        lead: 'lead-0',
        isActive: false,
        maxHeight: 0,
      }
    },
  ];

  var vueApp = new Vue({
    el: '#accordion-app',
    data: {
      items: items,
    },
    methods: {
      slideToggle: function(index){
        this.items[index].contents.isActive = !this.items[index].contents.isActive;
        if(this.items[index].contents.isActive){
          this.items[index].contents.maxHeight = this.$refs.contentsInner[index].clientHeight;
        }else{
          this.items[index].contents.maxHeight = 0;
        }
      },
    },
  });
</script>

サンプルソース(table-rowを開閉)

trタグの開閉も書いてみましたがHTMLタグが多くなりますね。 display: table-rowでも同じですね。

<style>
.container{
  width: 600px;
}
table{
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
}
.contents-wrap{
  padding: 10px;
  background: #eeeeff;
}
tr + tr .contents-wrap{
  border-top: 1px solid #aaaaaa;
}
th,td{
  border: 0;
  padding: 0;
}
.accordion{
  transition: max-height 2s;
  max-height: 0;
  overflow: hidden;
}

</style>

<div id="vue-app">
  <div class="container">
    <table>

      <tr>
        <th>
          <div>
            <div class="contents-wrap">
              ラジオボタンrow
            </div>
          </div>
        </th>
        <td>
          <div>
            <div class="contents-wrap">
              <span class="controll">
                <input type="radio" name="hoge-radio" value="true" v-model="hoge">
                <label for="">開く</label>
              </span>
              <span class="controll">
                <input type="radio" name="hoge-radio" value="false" v-model="hoge">
                <label for="">閉じる</label>
              </span>
            </div>
          </div>
        </td>
      </tr>

      <tr>
        <th>
          <div class="accordion" :style="{'max-height': state.hoge.maxHeight + 'px'}">
            <div class="contents-wrap" ref="hoge">
              トグルrow
            </div>
          </div>
        </th>
        <td>
          <div class="accordion" :style="{'max-height': state.hoge.maxHeight + 'px'}">
            <div class="contents-wrap">
              トグルコンテンツ
            </div>
          </div>
        </td>
      </tr>

      <tr>
        <th>
          <div>
            <div class="contents-wrap">
              ラジオボタンrow
            </div>
          </div>
        </th>
        <td>
          <div>
            <div class="contents-wrap">
              <span class="controll">
                <input type="radio" name="foo-radio" value="true" v-model="foo">
                <label for="">開く</label>
              </span>
              <span class="controll">
                <input type="radio" name="foo-radio" value="false" v-model="foo">
                <label for="">閉じる</label>
              </span>
            </div>
          </div>
        </td>
      </tr>

      <tr>
        <th>
          <div class="accordion" :style="{'max-height': state.foo.maxHeight + 'px'}">
            <div class="contents-wrap" ref="foo">
              トグルrow
            </div>
          </div>
        </th>
        <td>
          <div class="accordion" :style="{'max-height': state.foo.maxHeight + 'px'}">
            <div class="contents-wrap">
              トグルコンテンツ
            </div>
          </div>
        </td>
      </tr>

    </table>
  </div>
</div>

<script src="js/vue.js"></script>
<script>
  var vueApp = new Vue({
    el: '#vue-app',
    data: {
      hoge: 'false',
      foo: 'false',
      piyo: 'false',
      state: {
        hoge: {
          isVisible: false,
          maxHeight: 0,
        },
        foo: {
          isVisible: false,
          maxHeight: 0,
        },
      }
    },
    watch: {
      hoge: function(val){
        this.slideToggle('hoge', val);
      },
      foo: function(val){
        this.slideToggle('foo', val);
      },
    },
    methods: {
      slideToggle: function(key, val){
        this.state[key].isVisible = !this.state[key].isVisible;

        if(this.state[key].isVisible){
          this.state[key].maxHeight = this.$refs[key].clientHeight;
        }else{
          this.state[key].maxHeight = 0;
        }
        console.log(this.state[key]);
      },
    },
  });
</script>