Amahoro Imagineer!

初心者エンジニアが日々の学びをアウトプットするブログ。

jQueryで現在のページのリンクの背景色などを変える

背景

僕は現在、TECH::EXPERTというプログラミングスクールでRubyRuby on Railsを用いたアプリケーションの作り方を勉強していて、
最終課題としてメルカリのクローンアプリの開発をチームで行っています。

ちなみに、開発環境は以下の通り。

HTMLはhamlを使って、CSSはSCSS記法を使って記述しています。

この記事で伝えたいこと

みなさん、こんにちは。ゴールデンウィークいかがお過ごしでしょうか?

僕は図書館に籠ってプログラミングの勉強をしたり、エントリーシートを書いたりしています。

さて、記事のタイトルのつけ方が下手すぎて、自分でも驚きです。
この記事のタイトルを見ただけで、何のことを言っているかわかった方は驚異的な読解力の持ち主と言って良いでしょう。

言葉だけではうまく説明できないので、GIFを使って説明します。

クローン版メルカリ Before
クローン版メルカリ Before

現時点では、どのページを開いても左にあるナビゲーションの「支払い方法」の背景色がグレーっぽくなっています。

この部分のコードはこんな感じです。

<li class="mypage-sidebar-list__item">
  <a class="mypage-sidebar-list__link js-mypage-sidebar-current-page" href="/users/1/mypages/card">
    支払い方法
    <div class="mypage-sidebar-list__icon">
      <i class="fas fa-chevron-right mypage-sidebar-list__icon"></i>
    </div>
  </a>
</li>
<!-- 省略 -->
<li class="mypage-sidebar-list__item">
  <a class="mypage-sidebar-list__link" href="/users/1/mypages/logout">
    ログアウト
    <div class="mypage-sidebar-list__icon">
      <i class="fas fa-chevron-right mypage-sidebar-list__icon"></i>
    </div>
  </a>
</li>

.js-mypage-sidebar-current-page{
  background: #eee;
  font-weight: 600;
  font-size: 14px;
  .mypage-sidebar-list__icon{
    color: #333;
  }
}

現在のコードでは、「支払い方法」のaタグに直接、js-mypage-sidebar-current-pageというclass名を与えているので、どのページに飛んでもこの部分の色がグレーっぽくなっているのです。

それを今回は、jQueryを使って、以下のように、現在開いているページのリンクの色が変わるようにしたいと思います。

クローン版メルカリ After
クローン版メルカリ After

結論

結論を先取りすれば、jQueryを使って以下のように書けば実装できます。

$(function(){
  var link = $('.mypage-sidebar-list__link');
  var currentUrl = location.pathname;

  link.each(function() {
    if ($(this).attr("href") == currentUrl) {
      $(this).addClass("js-mypage-sidebar-current-page");
    }
  })
});

eachメソッドを使って、a class="mypage-sidebar-list__link"1つ1つのhref属性の値を確認し、現在開いているページのパスと一致すれば、
そのaタグにjs-mypage-sidebar-current-pageというclass名を与える、というとてもシンプルな処理です。

jQueryを使って現在のページのURLやパスを取得する方法

Locationオブジェクトというものを使うと、現在開いているページのURLやらパスやらを取得することができます。

■URLを取得する


location.href

=> "http://localhost:3000/users/1/mypages"

■パスを取得する


location.pathname

=> "/users/1/mypages"

プロトコルを取得する


location.protocol

=> "http:"

ドメイン名を取得する


location.host

=> "localhost:3000"


参考サイト
jQueryでURLとパラメータを取得する方法:location | UX MILK


今回はaタグのhrefの値と比較したいのでlocation.pathnameを使っています。

$(this)の使い方が肝

仮に以下のようなコードにしたとします。

$(function(){
  if ($('.mypage-sidebar-list__link').attr("href") == currentUrl) {
    $('.mypage-sidebar-list__link').addClass("js-mypage-sidebar-current-page");
  }
});

これだと、すべてのリンクの色が変わってしまいます。

$('.mypage-sidebar-list__link').addClass("js-mypage-sidebar-current-page");のような書き方だと、
addClassメソッドの対象となる要素がすべてのa class="mypage-sidebar-list__link"になってしまうからです。

失敗した例
すべてのリンクの色が変わってしまった(汗)


こうならないようにするには、eachメソッドと$(this)を使う必要があります。

eachメソッドの中で$(this)を使った場合、$(this)は現在処理されている要素ということになります。

eachメソッドを使って複数あるmypage-sidebar-list__linkのhref属性の値を1つ1つ確認し、現在開いているページのパスと一致したものがあったら、
『その"mypage-sidebar-list__link"』js-mypage-sidebar-current-pageというclass名を与えるという処理にする必要があります。

『その"mypage-sidebar-list__link"』というのを表現するのに、$(this)を使うのです。

今日のポイント


◎ location.pathnameで、現在開いているページのパスが取得できる。

◎ eachメソッド内での$(this)は、現在処理中の要素を指す。

BEMのBlockとElement どう使い分ける?

BEMって?

BEMはCSS設計の一種です。

BEMでは、Webページを構成する要素をBlock、Element、Modifierの3つに分けて考えます。それぞれの頭文字をとってBEMなんですね。

この記事ではBEMの基本的な説明はしません。ご興味がある方はググれば、たくさん記事が見つかると思います。

BEMの公式ページは以下のサイトです。

en.bem.info

また、この記事ではMindBEMdingという記法でBEMを書いています。

この記事のテーマ = BlockとElementをどう使い分けるか?

BEMを用いたことがある人なら、「あれ、これってBlockかな?それともElementの方がいいのかな?」と悩んだことがあるのではないでしょうか。

実際、僕自身も毎回悩みながら書いています。

そこで本記事では公式のドキュメントを元にBlockとElementをどのように使いこなせばいいのかを考えてみたいと思います!

公式ドキュメントの確認

Block

公式ドキュメントによれば、Blockは以下のような特徴を持った構成要素と言えます。

  • 機能的に独立したページの構成要素で、再利用が可能である。
  • Block名はその要素が何なのかを示す必要があり("menu"や"button"など)、状態を表すものであってはいけない("red"や"big"など)。
  • Blockはお互いにネストしてもよい = Blockの中にBlockがあっても大丈夫。

BEMにおけるBlock
出典:https://github.com/juno/bem-methodology-ja/blob/master/definitions.md


<div class="head"> <!-- Block -->
  <div class="search"> <!-- Block -->
    <input type="text" class="search__input"> <!-- Element -->
    <button type="submit" class="search__button"></button> <!-- Element -->
  </div>
</div>

Blockの中にBlockがあっても良いというのは、上のようにheadブロックの中にsearchブロックがネストして入っても良いよということですね。ただし、CSSではBlockを入れ子にしてスタイルを指定してはいけないとされています。

なぜなら、Blockは独立していることがとても重要だからです。つまり、Blockはどこに移動してもきちんと表示されるようにしなければいけません。
仮に、あるBlockのCSSを他のBlockの入れ子にして記述してしまうと、そのBlockは親となるBlockにCSSを依存してしまい、独立性が損なわれてしまいます。

またBlock名はid属性ではなく、class属性を用いて指定する必要があります。これは重複が許されないid属性を使ってしまうと、Blockが再利用しにくくなってしまうからです。

Element

公式ドキュメントにはElementは以下のように説明されています。

  • Blockを構成する要素で、所属するBlockから切り離して使うことはできない。

BEMにおけるElement
出典:https://github.com/juno/bem-methodology-ja/blob/master/definitions.md

  • Elementの名前も、Blockと同様に、その要素が何なのかを示す必要があり("item"や"text"など)、状態を表すものであってはいけない("red"や"big"など)
  • Elementも互いにネストしてもよい = Elementの中にElementがあっても大丈夫。
  • ElementのElementは作ってはいけない。
  • Elementを1つも持っていないBlockがあっても大丈夫。


「Blockを構成する要素で、所属するBlockから切り離して使うことはできない」というのは、以下のようにBlockの外でそのBlockのElementを単独で用いてはいけないということです。

<form class="search-form">
</form>

<!-- "search-form" Blockの"input" Element -->
<input class="search-form__input">

<!-- "search-form" Blockの"button" Element -->
<button class="search-form__button">Search</button>

つまり、Elementは所属するBlockの中でのみ使って良いのです。

<form class="search-form">
  <input class="search-form__input">
  <button class="search-form__button">Search</button>
</form>

BEMにおけるElement
出典:https://github.com/juno/bem-methodology-ja/blob/master/definitions.md

「ElementのElementを作っていけない」というのは、
search-form__content__inputのようにBlock__Element1__Element2みたいな書き方はダメだよ、ということです。

公式ドキュメントでは、このような場合は、search-form__inputsearch-form__content-inputのようにBlock__Elementの形に書き直すように推奨されています。

<form class="search-form">
    <div class="search-form__content">
        <!-- 下の場合は "search-form__input" か "search-form__content-input"にする -->
        <input class="search-form__content__input">

        <!-- 下の場合は "search-form__button" か "search-form__content-button"にする -->
        <button class="search-form__content__button">Search</button>
    </div>
</form>

BlockとElementのどっちを作るべき?

公式ドキュメントには"Should I create a block or an element?"というセクションがあり、どのような場合にはBlockを作るべきで、どのような場合にはElementを作るべきかが説明されています。

https://en.bem.info/methodology/quick-start/#should-i-create-a-block-or-an-element

Blockを作るべき場合

If a section of code might be reused and it doesn't depend on other page components being implemented.


「コードの塊が再利用される可能性があり、ページの他の構成要素に依存しない場合」はBlockを作るべき、となるでしょうか。

Elementを作るべき場合

If a section of code can't be used separately without the parent entity (the block).

The exception is elements that must be divided into smaller parts – subelements – in order to simplify development. In the BEM methodology, you can't create elements of elements. In a case like this, instead of creating an element, you need to create a service block.


「コードの塊が親となるエンティティー(つまり、Block)から切り離して使えない場合」はElementを作るべき、ということでしょう。

正直、先ほど紹介したBlockとElementの特徴からすれば、当たり前のことしか言っておらず、あまり参考になりませんよね...

ただ、個人的にとても重要なのは、"The exception is..."以下の箇所です。
補足を含めて、意訳すると下記のようになるでしょうか。

開発を簡素化するために、Elementをより小さなパーツ(ここではsubelementと呼んでいる)に分けなければいけない場合は、この限りではありません。BEMの方法論では、ElementのElementは作ってはいけません。このような場合(ElementのElementを作りたい衝動に駆られてしまった場合)は、Elementを作る代わりに、service blockを作る必要があります。

"service block"という用語が新たに出てきました。ただ、これが何なのかは僕が見た限り、公式ドキュメントの中では説明されていません。なので、ググって見ました。

service blockとは?

ググった結果、以下のサイトが参考になりました。このサイトを元にservice blockが何なのかを解説してみたいと思います。

en.bem.info


例えば、下記のようなコードがあったとします。先ほども言った通り、BEMの方法論ではElementのElementは作ってはいけないことになっています。

しかしながら、下記のコードでは、
menuブロックのElementであるmenu__itemがあり、さらにそのElementであるmenu__item__iconmenu__item__textが登場してしまっており、良い書き方とは言えません。

<ul class="menu"> <!-- Block -->
    <li class="menu__item"> <!-- Element -->
        <span class="menu__item__icon"></span> <!-- ElementのElement-->
        <span class="menu__item__text">アイテム</span> <!-- ElementのElement-->
    </li>
</ul>


こんなときに使うのがservice blockです。service blockを使って、上記のコードを書き直すとこんな感じになります。

<ul class="menu"> <!-- Block -->
    <li class="menu-item"> <!-- Block -->
        <span class="menu-item__icon"></span> <!-- Element-->
        <span class="menu-item__text">アイテム</span> <!-- Element-->
    </li>
</ul>

元のコードではElementとして書かれたliタグが、service blockを使ったコードではmenu-itemというBlockに変わっています。
その結果、spanタグはmenu-itemのElementになり、ElementのElementが作られてしまっている状態が解消されました。

つまり、service blockとは、役割的にはElementなんだけど、Elementとして扱うとElementのElementを作る必要が出てきてしまうので、
便宜上、Blockとして扱う『なんちゃってBlock』なのです。

上で散々、「Blockは独立して使える必要があって、Elementは親となるBlockから切り離して使えない...」とか言いましたが、
上記のmenu-itemのように「本当はElementなんだけど、便宜上Blockにしちゃう」という裏の手が許されるのです。

このことを知っているだけで、随分、BEMが扱いやすくなるのではないでしょうか?

実際、僕はこれを知ってから、Blockにするか、Elementにするか悩みすぎて、夜も眠れない生活から抜け出すことができました。それだけでなく、肌荒れが治り、ご近所さんとの人間関係も良好になりました(言い過ぎ)。


また、service blockを使わずに、下記のような形に書き直すのもありです。

<ul class="menu"> <!-- Block -->
    <li class="menu__item"> <!-- Element -->
        <span class="menu__icon"></span> <!-- Element-->
        <span class="menu__text">アイテム</span> <!-- Element-->
    </li>
</ul>

まとめ


Blockにするか、Elementにするか迷った際、過度にそれぞれの定義に固執する必要はなく、
ElementのElementを作らなきゃいけないような場面などでは、
本来はElementである要素を、Blockとして扱ってもよい。

BEMについてチームで考えてみた

背景

僕は現在、TECH::EXPERTというプログラミングスクールでRubyRuby on Railsを用いたアプリケーションの作り方を勉強していて、
最終課題としてメルカリのクローンアプリの開発をチームで行っています。

ちなみに、開発環境は以下の通り。

チームでCSS設計を考える大切さ

TECH::EXPERTでは、最初に個人開発でアプリケーションの作り方を学んだ後に、上記のチーム開発に取り組みます。
チーム開発をしてわかったことは、個人でやるのとは全然違うということです(当たり前ですけど)。


チーム開発で個人個人が好き勝手に作業をしてしまったら、そのプロジェクトは破綻してしまうでしょう。
コミュニケーションをいかに上手くとるかが、チーム開発においては重要だと思います。


今回、チーム開発でメルカリのクローンアプリを作るにあたって、僕たちは仕様書でBEMを使うように指示がされていました。一方で、本家メルカリではBEMは使われていません。


そして、当初はBEMを使うメリットについてチームメンバー全員が理解できていたわけではありませんでした。
結果的に以下のような状況が発生してしまいました。

  • AさんはBEMに沿ってクラス名をつけてマークアップをしている。
  • Bさんは、検証ツールを使って、BEMを使っていない本家メルカリをほとんどそのまま真似てマークアップしている。
  • そのため、意図しない形で複数のCSS設計が混在してしまっている。


一応、僕はスクラムマスターをやっており、このままではよくないぞ、と思い、先日、チームメンバーに向けてBEMについてプレゼンをさせていただきました。
今回は、せっかくなのでその時に使ったパワポをシェアしたいと思います!



SlideShareを使うと少し画質が劣化してしまうのが残念です...

<後編> owlcarousel-railsを使って自動でスライドするバナーを実装してみた!

前編はこちら

この記事は以下の記事の続きです!

amahoro-imagineer.hatenablog.jp

この記事で説明すること

owl-carousel
クローン版メルカリ

前編ではowlcarousel-railsを使って、メルカリの自動でスライドするバナーの実装に挑戦しました。ある程度、本家メルカリを再現できたものの、いくつか足りない部分があります。

そこで本記事では以下の実装に挑戦したいと思います。

  • 次へボタン(owl-next)、戻るボタン(owl-prev)を表示させる。
  • owl-dotを表示させる。
    • owl-dotにホバーしただけでスライドが切り替わるようにする。

次へボタン&戻るボタンを表示させる

クローン版メルカリのowl-nav
クローン版メルカリ

現時点では、owl-navやowl-next・owl-prevがバナーの下に来てしまっているので、position: absoluteを使って位置を調整します。

デフォルトで表示される「‹」「›」だと少し見にくいので、今回は矢印の画像*1を表示させます。

.slide-banner {
  // 省略
  &__link {
  // 省略
  }
  & .owl-prev {
    @include slide-banner-owl-nav-btn("carousel-prev.png");
    left: 60px;
  }
  & .owl-next {
    @include slide-banner-owl-nav-btn("carousel-next.png");
    right: 60px;
  }
  //省略
}
// 以下省略

@mixin slide-banner-owl-nav-btn($background-image) {
  background-image: image_url($background-image) !important;
  cursor: pointer;
  height: 46px;
  width: 31px;
  position: absolute;
  top: 50%;
  -webkit-transform: translateY(-23px);
  transform: translateY(-23px);
}

owl-nav
クローン版メルカリ

きちんと表示されました。しかしよく見ると、デフォルトで表示される「‹」「›」も表示されてしまっています。

owl-nav
クローン版メルカリのowl-nav

なので、.owl-navに対してcolor: transparentを適用することで見えなくします。

.slide-banner {
  // 省略
  &__link {
  // 省略
  }
  // 以下を追加。
  & .owl-nav {
    color: transparent;
  }
  & .owl-prev {
  // 以下省略
}
// 以下省略

owl-nav
クローン版メルカリのowl-nav

見えなくなりました!

owl-dotを表示させる

おさらいすると本家メルカリでは、button.owl-dotはこんな感じで表示されています。

owl-dot
本家メルカリのowl-dot

owl-dotsに関しても、現時点ではバナーの下にあるため、上に移動させる必要があります。

owl-dots
クローン版メルカリ


.slide-banner {
  // 省略
  &__link {
  // 省略
  }
  // 以下省略
  & .owl-dots {
    // ネガティブマージンで表示位置を調整。
    margin: -30px;
    text-align: center;
  }
  & .owl-dot {
    position: relative;
    background: white !important;
    opacity: 0.4;
    border-radius: 50%;
    height: 12px;
    width: 12px;
    // min-heightの値を上書き
    min-height: 12px;
    margin-right: 15px;
    &.active {
      opacity: 1;
      background: rgba(0,0,0,0.5) !important;
    }
  }
}
// 省略
// ブラウザの幅が768px未満のときは、.owl-dotsを表示させない
@media screen and (max-width: 768px) {
  .slide-banner .owl-dots {
    display: none;
  }
}

owl-dots
クローン版メルカリ owl-dots

デフォルトではbutton.owl-dotに対してbackground: noneが適用されているので、backgroundには!importantを付けて、強制的にスタイルを適用しています。

また、今回使っているsanitize.css(リセットCSSの一種)にはbutton要素に対してmin-height: 1.5emというスタイルが当てられているせいで、heightに12pxを適用しても、button.owl-dotのheightが24pxになってしまい、いびつな形になってしまいます。

f:id:potato_life:20190417181651p:plain
width 12px, height 24pxのいびつな形をしたowl-dot

そこで、min-height: 12pxを記述して、min-heightの値を上書きしています。


また、本家メルカリに倣って、メディアクエリを使って、ブラウザの幅が768pxを下回るときはbutton.owl-dotが表示されないようにしています。

owlcarousel-railsの仕様で、現在表示されているスライドに対応するドットには"active"というクラス名が付くので、&.active {...}の部分で色が変わるようにしています。

本家メルカリではowl-dotsに対してネガティブマージンを使って、ドットの位置を調整しているところが興味深いですね。ちなみに以下のようなCSSでも同じようなレイアウトを再現できます。

.slide-banner {
  position: relative;
  // 以下省略
  & .owl-dots {
    position: absolute;
    width: 100%;
    bottom: 10px;
    text-align: center;
  }
// 以下省略

owl-dotにホバーしたらバナーが切り替わるようにする

現時点では、owl-dotをクリックするとそれに対応するスライドに表示が切り替わります。しかし、本家メルカリではドットにホバーするだけでスライドが切り替わるようになっているので、このあたりも再現してみたいと思います。

結論から言えば、ホバーしたらclickイベントを駆動させればいいだけです。

$(function(){
  $(".slide-banner.owl-carousel").owlCarousel({
    //省略
  });
  // owl-dotにホバーしたら表示が切り替わる
  $('.slide-banner .owl-dot').hover(function() {
    $(this).click();
  }, function() {});
});

コードおさらい

完全にではないですが、大まかに本家メルカリの自動スライドするバナーを再現できました。
随分、長い記事になって恐縮です。最後にowl-carouselの部分のコードをまとめておきます。

%section.slide-banner.owl-carousel
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--natsukashi"
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--new-life"
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--bokurano-heisei"

.slide-banner {
  height: 200px;
  overflow: hidden;
  position: relative;
  &__link {
    display: block;
    &--natsukashi {
      @include slide-banner-owl-item-background("banner-natsukashi.jpg");
    }
    &--new-life {
      @include slide-banner-owl-item-background("banner-new-life.jpg");
    }
    &--bokurano-heisei {
      @include slide-banner-owl-item-background("banner-bokurano-heisei.jpg");
    }
  }
  & .owl-nav {
    color: transparent;
    display: block;
  }
  & .owl-prev {
    @include slide-banner-owl-nav-btn("carousel-prev.png");
    left: 60px;
  }
  & .owl-next {
    @include slide-banner-owl-nav-btn("carousel-next.png");
    right: 60px;
  }
  & .owl-dots {
    margin: -30px;
    text-align: center;
  }
  & .owl-dot {
    position: relative;
    background: white !important;
    opacity: 0.4;
    border-radius: 50%;
    height: 12px;
    width: 12px;
    min-height: 12px;
    margin-right: 15px;
    &.active {
      opacity: 1;
      background: rgba(0,0,0,0.5) !important;
    }
  }
  & .owl-stage-outer, .owl-stage, .owl-item, .owl-item>a {
    height: 100%;
  }
}

@media screen and (min-width: 1068px) {
  .slide-banner {
    height: 390px;
  }
}

@media screen and (min-width: 768px) and (max-width: 1068px) {
  .slide-banner {
    height: 290px;
  }
}

@media screen and (max-width: 768px) {
  .slide-banner .owl-dots {
    display: none;
  }
}

@mixin slide-banner-owl-nav-btn($background-image) {
  background-image: image_url($background-image) !important;
  cursor: pointer;
  height: 46px;
  width: 31px;
  position: absolute;
  top: 50%;
  -webkit-transform: translateY(-23px);
  transform: translateY(-23px);
}

@mixin slide-banner-owl-item-background($background-image) {
  background: image_url($background-image);
  background-size: auto 100%;
  background-position: center;
}

$(function(){
  $(".slide-banner.owl-carousel").owlCarousel({
    loop:true,
    nav:true,
    items:1,
    autoplay:true,
    autoplayTimeout:5000,
    autoplayHoverPause:true,
    smartSpeed: 1000;
  });
  $('.slide-banner .owl-dot').hover(function() {
    $(this).click();
  }, function() {});
});

最後までお読みいただきありがとうございました。

*1:これらの画像は本家メルカリからゲットできます

<前編> owlcarousel-railsを使って自動でスライドするバナーを実装してみた!

背景

僕は現在、TECH::EXPERTというプログラミングスクールでRubyRuby on Railsを用いたアプリケーションの作り方を勉強していて、
最終課題としてメルカリのクローンアプリの開発をチームで行っています。

ちなみに、開発環境は以下の通り。

HTMLはhamlを使って、CSSはSCSS記法を使って記述しています。

自動で横にスライドするバナーを作りたい

※最終的なコードは後編記事の最後にまとめて載せてあります。

本家メルカリではヘッダーの下に自動でスライドするバナーがあります。これはボタンを押したりドラッグすることでも表示を切り替えることができます。

本家メルカリ owl-carousel
本家メルカリの自動スライダー

いったい、これはどのように実装しているのでしょうか?この部分のHTMLを検証ツールを使って確認してみると以下のようになっていました。

<section class="owl-carousel top-carousel owl-loaded owl-drag">
  <div class="owl-stage-outer">
    <div class="owl-stage" ...>
       ...
    </div>
  </div>
</section>

"owl"と付くクラス名を持つ要素がたくさん出てきました。試しに"owl-carousel"でググってみると、owlcarousel-railsというgemがあることがわかりました。

github.com

どうやらこれを使えばスライダーが実装できるみたいです!

使うための準備

それではこのGithubページを参考にして、owlcarousel-railsを使う準備をしていきたいと思います。

まずはgemfileに"owlcarousel-rails"を追加し、bundle installを実行します。

gem 'owlcarousel-rails'

次にapplication.jsに以下を加えます。

//= require owl.carousel

Githubページにはapplication.cssに以下の2行を追記するように書いてあります。

*= require owl.carousel
*= require owl.theme

しかし、僕たちはSCSS記法を使っているので、代わりにapplication.scssに以下の行を追加しました。

@import "owl.carousel";
@import "owl.theme.default";

参考:Sass support · acrogenesis/owlcarousel-rails Wiki · GitHub


あとはJavaScriptファイルで以下の記述を追加してプラグインを呼び出せば、ひとまず準備は完了です。僕は"owl-carousel.js"というファイルをassets/javascripts/channelsに手動で作成してコードを書きました。

$(function(){
  $(".slide-banner.owl-carousel").owlCarousel();
});

※ "slide-banner"というクラス名は下で出てきます。$(".owl-carousel")でも大丈夫です。

実際に使ってみよう

それではスライドさせたい部分のHTMLを書いてみましょう。

owlcarousel-railsを使うのにHTML部分ですることは、スライドさせたい要素を包む親要素に"owl-carousel"というクラス名を与えるだけです。他の必要な要素は自動生成されます。

今回はscssでスタイルを記述しやすいように"slide-banner"というクラス名も付けています。リンク先はとりあえずroot_pathにしておきます。

%section.slide-banner.owl-carousel
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--natsukashi"
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--new-life"
  = link_to "", root_path, class: "slide-banner__link slide-banner__link--bokurano-heisei"

画像はbackgroundプロパティとimage_urlを使って指定します。また、本家メルカリに倣って、メディアクエリを使いブラウザの幅に応じてsection class="side-banner owl-carousel..."のheightが変わるようにしました。

.slide-banner {
  height: 200px;
  overflow: hidden;
  position: relative;
  &__link {
    &--natsukashi {
      @include slide-banner-owl-item-background("banner-natsukashi.jpg");
    }
    &--new-life {
      @include slide-banner-owl-item-background("banner-new-life.jpg");
    }
    &--bokurano-heisei {
      @include slide-banner-owl-item-background("banner-bokurano-heisei.jpg");
    }
  }
}

@media screen and (min-width: 1068px) {
  .slide-banner {
    height: 390px;
  }
}

@media screen and (min-width: 768px) and (max-width: 1068px) {
  .slide-banner {
    height: 290px;
  }
}

@mixin slide-banner-owl-item-background($background-image) {
  background: image_url($background-image);
  background-size: auto 100%;
  background-position: center;
}

一旦、現在どのように表示されるか確認してみると...

f:id:potato_life:20190405153913p:plain
クローン版メルカリ 画像が表示されていない

全く表示されてない!(舌打ち)

background-imageを指定している要素の高さは明示的に指定する

検証ツールでHTMLの構造を確認してみるとsection class="slide-banner ..."内に"owl-stage-outer"というクラス名を持つdivタグなどの色々な要素が自動で追加されているのがわかります。

owl-carouselのHTML構造
クローン版メルカリのHTML構造

ざっくりした構造は以下のようになっています。

ざっくりしたowl-carouselの構造

owl-itemやowl-dotは表示させたいスライドの数によって変わります。

owl-prevやowl-nextはいわゆる「前へ」「次へ」ボタンで、「‹」「›」が表示されます。設定(後述)をすれば、これらのボタンを使ってスライドを切り替えられるようになります。

owl-dotは本家メルカリでいうと以下の箇所です。このドットを押すとスライドが切り替わります。

owl-dot
本家メルカリのowl-dot

これら新しく追加された要素を検証ツールで見てみると"owl-stage-outer", "owl-stage", "owl-item"の高さがほとんどなく、"owl-item"内のaタグの高さは0になっています。高さが0なので、表示されなくて当然ですね。。

f:id:potato_life:20190405161608p:plain

それでは、なぜ高さが0になってしまっているのか?原因はbackground-imageを指定しているaタグのheightを指定してないからです。これは別にowl-carouselの問題という訳ではなく、そもそも論になるのですが、backgroundに画像を指定した要素の高さは明示的に指定する必要があるみたいです(要素の大きさ=背景画像の大きさには勝手にならない)。widthやheightの指定がなくても表示されるimageタグとは挙動が異なります。

また、aタグはinline要素で高さが指定できないので、displayプロパティの値をblockにする必要があります。

.slide-banner {
  //省略
  &__link {
    // 以下の1行を追加
    display: block;
    // 以下省略
  }
  // 以下を追記
  & .owl-stage-outer, .owl-stage, .owl-item, .owl-item>a {
    height: 100%;
  }
}
// 以下省略


今回はメディアクエリでブラウザの幅によってsection class="side-banner owl-carousel..."の高さが変わるようにしているので、aタグの大きさもそれに合わせて変わるようにパーセンテージで指定しています。

要素の高さをパーセンテージで指定した場合、親要素の高さに対する割合で自身の高さが決まるので、owl-itemの高さをsection class="side-banner owl-carousel..."の高さに合わせるにはowl-itemの親要素であるowl-stage、その親要素であるowl-stage-outerに対してもheight: 100%を指定し、section class="side-banner owl-carousel..."までたどっていく必要があります。

もう一度、表示を確認してみると...

クローン版メルカリ
クローン版メルカリ
とりあえず画像は表示されました!

jQueryを使ってカスタマイズ

スライドの表示をカスタマイズするには、先ほど作ったowl-carousel.jsを編集します。

$(function(){
  $(".slide-banner.owl-carousel").owlCarousel({
    // 1枚目→2枚目→3枚目→1枚目→2枚目→3枚目→1枚目...みたいにループさせる。
    loop:true,
    //次へボタン(owl-next)、前へボタン(owl-prev)をクリックすると画像が切り替わるようにする。
    nav:true,
    // 一度に表示するowl-itemの数を1つにする。
    items:1,
    // 自動でスライドが切り替わるようにする。
    autoplay:true,
    // 5秒ごとにスライドが自動で切り替わるように設定。
    autoplayTimeout:5000,
    //画像にホバーしている間はスライドが自動で切り替わらないようにする。
    autoplayHoverPause:true,
    //画像の切り替わりにかかる秒数を1秒に設定。
    smartSpeed: 1000
  });
});

参考:Demos | Owl Carousel | 2.3.4


とりあえず、現時点でこんな感じになりました。gifを作るのにGyazo GIFを使っているのですが、無料版だと7秒までしか撮れない関係で、ループしている様子が撮影できてませんが、きちんとループ(3枚目の画像の次は1枚目の画像に戻る)もしています。

owl-carousel
クローン版メルカリ

ただ、現時点ではowl-next/owl-prevやowl-dotが表示されていません。これらを表示させる方法については別の記事で紹介したいと思います!

Font Awesomeがうまく表示されないので、font-awesome-sassを導入してみた!

背景

僕は現在、TECH::EXPERTというプログラミングスクールでRubyRuby on Railsを用いたアプリケーションの作り方を勉強していて、
最終課題としてメルカリのクローンアプリの開発をチームで行っています。

ちなみに、開発環境は以下の通り。

Font Awesomeがきちんと表示されない問題

Font Awesomeを使いたい!
ということで、該当する箇所を抜粋するとこんな感じに書いてみました。

%i.far.fa-bell.header-notification__icon
%span お知らせ

%i.fas.fa-check.header-todos__icon
%span やることリスト

確認してみると、

f:id:potato_life:20190324171210p:plain
font-awesome-railsを使った場合

あれれ、なんか四角になっちゃってる(汗)。

原因はfont-awesome-railsがFont Awesome 5に対応していないこと

今回、Font Awesomeを使うのに、"font-awesome-rails"というgemを使っています。また、Font Awesomeのアイコンを表示させるのに"far"や"fas"といったclass名を使っています。

実はこれらのclass名はFont Awesome 5から使えるようになったもので、この部分を変えることでアイコンのスタイルを選ぶことができます。

class名(Prefix) アイコンスタイル
fab Brands
fas Solid
far Regular
fal Light


詳しくは以下のサイトが参考になります。
www.webdesignleaves.com


しかしながら、"font-awesome-rails"は今のところ、これらの新しいclass名には対応していないため、先ほどのように表示が崩れてしまっていたのです。

font-awesome-sassを代わりに導入する

じゃあ、どうすればいいのか?僕が見つけた解決策は"font-awesome-rails"の代わりに"font-awesome-sass"というgemを使うというものです。

font-awesome-sassのGithubページを見てみると、しっかりと新しいスタイル表記に対応していることがわかります。

icon('fas', 'flag')
# => <i class="fas fa-flag"></i>
GitHub - FortAwesome/font-awesome-sass: Font-Awesome Sass gem for use in Ruby/Rails projects

ってことで、導入していきます。

gemfileの修正

まずはgemfileから"font-awesome-rails"を削除し、代わりに"font-awesome-sass"を追加。そして、bundle install。

gem 'font-awesome-sass', '~> 5.8.1'

application.scssを編集

インストールしただけではまだ使えません。下記のようにapplication.scssで"font-awesome"と"font-awesome-sprockets"をインポートする必要があります。

@import "font-awesome-sprockets";
@import "font-awesome";

このとき、注意しなければならないのは"font-awesome-sprockets"を"font-awesome"より上に書かなければならないということです。この順番を間違えるとアイコンがうまく表示されません。

viewファイルで実際にアイコンを使ってみる

さて、ここまで来たら準備完了。viewファイルで実際にFont Awesomeのアイコンを使ってみましょう。

= icon('far', 'bell', class: 'header-notification__icon')
%span お知らせ

= icon('fas', 'check', class: 'header-todos__icon')
%span やることリスト

Font Awesomeのアイコンがうまく表示された場合
font-awesome-sassを使った場合

今度はきちんと表示されました!