【脱jQueryのすゝめ】ShopifyのjQueyレス化と代替案

【脱jQueryのすゝめ】ShopifyのjQueyレス化と代替案

世はまさに脱jQuery時代。

長年ウェブ制作に携わっていると、以前は当たり前のように使用していたのに、ブラウザやCSSの進化、SEOの変化、何よりIEの開発終了で不要になったものが多くありますが、その中でもjQueryは過去の遺物となりつつあります。

というのも、以前はおまじないのよう設置し、CDNで読み込むか、サーバーに入れるか、<head>内で読み込むか、</body>直前に読み込むかなど様々な議論も行われてきましたが、便利な反面デメリットも多くウェブ業界では脱jQueryが推奨されています。

はじめに

 Shopifyでも以前のデフォルトテーマ「Debut」はjQueryで制御されておりましたが、「Dawn」ではjQueryレスとなりました。

jQueryのデメリットは何より「重い」の一言につきます。

いくらCDNで高速通信したところで、jQueryを読み込む分サイトパフォーマンスは遅くなります。「PageSpeed Insights」で計測すると一目瞭然。jQueryが足を引っ張ってるのが一目瞭然です。

加えてjQueryライブラリを読み込んで使用するので、パフォーマンスはどんどん落ちていきます。

他にもjQueryライブラリのバッティングによって機能しなくなったり、jQueryバージョンに依存するライブラリがあったりするので、はっきり言って使い勝手が悪いです。

また、jQueryのAjax(非同期通信)はセキュリティ的にも良くないと言われています。

それでは、どうすればよいか?
jQueryを使って行う代表的なものと代替案をご紹介します。

スライドショー

メインページのファーストビューに使われることが多いスライドショー。
jQueryのメジャーなスライドショーライブラリに「Slick」がありますが、個人的にこういった演出はあまり好きではありません。

というのも、動きがあって豪華に見えるくらいで、切り替わらない1枚画像リンクに比べてクリック率も低いと言われています。

CSSだけで実装することも可能ですが難易度が高いため、どうしても必要な場合にお勧めするのは「Swiper」です。
多機能で実装しやすく軽量なのがポイントで、jQueryに依存せず快適に動作します。

Swiper

Swiperの設置例

{{ 'swiper-bundle.min.css' | asset_url | stylesheet_tag }}
<script src="{{ 'swiper-bundle.min.js' | asset_url }}" defer="defer"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
  const swiper = new Swiper('.swiper', {
    loop: true,
    pagination: {
      el: '.swiper-pagination',
    },
    navigation: {
      nextEl: '.swiper-button-next',
      prevEl: '.swiper-button-prev',
    },
    scrollbar: {
      el: '.swiper-scrollbar',
    },
  });
});
</script>

Shopifyではdefer属性をつけるのを推奨しているので、DOMContentLoadedも併用して使うと良い。

モーダルウィンドウ

以前はサムネイルをクリックで拡大するのが一般的でしたが、時代はモバイルファーストになり、スマホでタップして拡大したところで、表示サイズは変わらないどころか小さくなったりすることもあるので、存在自体が不要になってきています。

jQueryで最も有名なライブラリは「Lightbox2」で、「FancyBox」も有名です。

単純にモーダルウィンドウだけであればHTMLタグ「dialog」で代用できます。

dialog

アニメーション付きのモーダルにするにはCSS、JSとの合せ技が必要になります。

Dialogの設置例

<button type="button" class="show-button">開く</button>
<dialog id="dialog">
	<div class="dialog-content">
		<p>ダイアログのコンテンツ</p>
		<button type="button" class="close-button">閉じる</button>
	</div>
</dialog>
#dialog {
	position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    place-content: center;
    display: grid;
    max-width: unset;
    max-height: unset;
    width: 100%;
    height: 100%;
    border: none;
    transition: .3s linear;
}
#dialog.is-show {
    z-index: 1000;
    opacity: 1;
}
#dialog:not(.is-show) {
    z-index: -1000;
    opacity: 0;
}
#dialog .dialog-content {
    display: grid;
    grid-template-rows: auto 1fr auto;
    transform: scale(0);
    transition: .3s linear;
    overflow: hidden;
}
#dialog.is-show .dialog-content {
    transform: scale(1);
}
#dialog:not(.is-show) .dialog-content {
    transform: scale(0);
}
const showButton = document.querySelector('.show-button');
const closeButton = document.querySelector('.close-button');
const dialog = document.querySelector('#dialog');
showButton.addEventListener('click', function(){
	dialog.clasList.add('is-show');
	dialog.showModal();
});
closeButton.addEventListener('click', function(){
	dialog.clasList.remove('is-show');
	setTimeout(() => {
		dialogclose();
	}, 300);
});

<dialog>のデフォルト使用は「open」属性を「display: none;」で切り替えるため、アニメーションに不向きです。
予めCSSで「display: grid;」で指定しておき、「transition」と「z-index」と「opacity」でアニメーションを実装しています。

切り替えはクラス「is-show」の有無で判定しており、アニメーション終わってから閉じるようCSSの「transition」とJSの「setTimeout」でアニメーション時間を合わせています。

画像拡大に関してはCSSとJSだけで実装することも可能ですが、難易度が高くなってしまうため、jQuery非依存のライブラリ「Luminous」がお勧めです。

Luminous

Luminousの設置例

<ul>
  <li>
    <a class="gallery-demo" href="https://assets.imgix.net/unsplash/coyote.jpg?w=1600">
      <img src="https://assets.imgix.net/unsplash/coyote.jpg?w=100" alt="Coyote">
    </a>
  </li>
  <li>
    <a class="gallery-demo" href="https://assets.imgix.net/unsplash/motorbike.jpg?w=1600">
      <img src="https://assets.imgix.net/unsplash/motorbike.jpg?w=100" alt="Motorbike">
    </a>
  </li>
  <li>
    <a class="gallery-demo" href="https://assets.imgix.net/unsplash/hotairballoon.jpg?w=1600">
      <img src="https://assets.imgix.net/unsplash/hotairballoon.jpg?w=100" alt="Hot air balloon">
    </a>
  </li>
</ul>
new LuminousGallery(document.querySelectorAll(".gallery-demo"));

アコーディオンメニュー

クリックして開閉するコンテンツや、折りたたみメニューによく使われていましたが、「details / summary」の登場によりjQuery無しでも開閉できるようになりました。

何より非表示中のコンテンツは隠しコンテンツ扱いされて、サイト評価が下がる可能性もありますので、jQueryでの開閉は推奨されていません。

また、CSSやJSを使えば、開閉時にスムーズなアニメーションを表現することも可能です。

details / summary

details / summaryの設置例

<details class="accordion">
  <summary class="accordion-title">タイトル</summary>
  <div class="accordion-content">
  	<p>アコーディオンコンテンツ</p>
  </div>
</details>

スムーズスクロール

ページ上部へ移動したり、アンカータグへ移動したり、ページ内をスムーズに移動させることのみにjQueryが使われてるケースも多々あります。

こちらはCSSの「scroll-behavior」の登場で不要になりました。

CSSにたった1行、html{}内に「scroll-behavior: smooth;」と加えるだけでページ内移動がスムーズになります。

現在は全てのモダンブラウザが対応しています。

scroll-behavior

scroll-behaviorの設置例

html {
	scroll-behavior: smooth;
}

ブラウザのURL欄にアンカーリンクが付くのが嫌な方は、VanillaJSでも実装できます。

VanillaJSでの設置例

document.querySelectorAll('a[href^="#"]').forEach(anchor => {
  anchor.addEventListener('click', function (e) {
    e.preventDefault();

    const targetId = this.getAttribute('href').substring(1);
    const targetElement = document.getElementById(targetId);

    if (targetElement) {
      targetElement.scrollIntoView({
        behavior: 'smooth'
      });
    } else {
      document.body.scrollIntoView({
        behavior: 'smooth'
      });
    }
  });
});

ヘッダーをFixedやStickyで固定していて移動先の見出しが隠れてしまうのを回避する場合は以下のコードになります。

document.querySelectorAll('a[href^="#"]').forEach(anchor => {
    anchor.addEventListener('click', function (e) {
      e.preventDefault();

      const headerHeight = document.querySelector('.header').offsetHeight;
      const targetId = this.getAttribute('href').substring(1);
      const targetElement = document.getElementById(targetId);

      if (targetElement) {
        const targetPosition = targetElement.getBoundingClientRect().top + window.scrollY;
        window.scrollTo({
          top: targetPosition - headerHeight,
          behavior: 'smooth'
        });
      } else {
        document.body.scrollIntoView({
            behavior: 'smooth'
        });
      }
    });
});

Ajax通信

jQueryの場合はAjax通信が簡単に実装できるのがメリットといえますが、「Fetch API」を使えばjQueryレスでも実装可能です。

Fetch API

日本の祝日を取得する設置例

const holidays_jp = 'https://holidays-jp.github.io/api/v1/date.json';
fetch(holidays_jp)
  .then((response) => response.json())
  .then((holidaysData) => {
    const holidays = Object.keys(holidaysData);
    holidays.forEach((holiday) => {
	    const targets = document.querySelectorAll(`[data-date="${holiday}"]`);
	    targets.forEach((target) => {
	        target.classList.add('holiday');
	    });
    });
  });

VanillaJSを使おう

VanillaJSと言われていますが、ネイティブなJavascriptのことです。

基本的には、クリックで要素にクラスを付与してアニメーションを展開させるだけのものがほとんどなので、jQueryなんて使わなくてもVanillaJSとCSSでほとんどのことに対応できます。

以下は、ボタンをクリックしてコンテナにクラスを付与させるアクションの比較コードです。

jQuery

$('#hoge').click(function(){
	$('#fuga').toggleClass('is-open');
});

VanillaJS

const button = document.querySelector('#hoge');
button.addEventListener('click', function(){
	const target = document.querySelector('#fuga');
	target.classList.toggle('is-open');
});

jQueryの方が簡単に見えますが、覚えてしまえば大差はありません。

とはいえ、Shopifyアプリによっては未だにjQuery依存のアプリが多く、せっかくテーマをjQueryレスにしたところで、3つも4つもjQueryを読み込んでたりします。

次回の「脱jQueryのすゝめ」では、上記コードの様によく使うJavascriptを紹介していきます。

この記事を書いた人

モリタオウ

株式会社テンカ 代表取締役 / ウェブクリエイター / グラフィックデザイナー

1977年12月20日生まれ。広告代理店や企業広報を経て、2007年12月にデザイン事務所「モリタ・クリエイト」を創業。2022年12月に「株式会社テンカ」を設立。