「jQueryへ依存せずにBootstrapを使いたい」そんなときは、ネイティブJavaScript化する「Native JavaScript for Bootstrap」プロジェクトをチェックしてみるといかもしれません。
BootstrapのJavaScriptコンポーネントを使うよりも、実はVanilla JavaScript(編注:素のJavaScriptのこと)が好きだという人がいます。もしそうなら、ネイティブJavaScriptのBootstrapプロジェクトがおすすめです。このプロジェクトは、コンポーネントのうちjQueryで書かれている部分を素のJavaScriptに移植して、jQueryへの依存をなくすことを目指しています。
目的
移植する狙いのほとんどはパフォーマンスのためです。
数多くのベンチマークが示すとおり素のJavaScriptはjQueryより高速に実行できるので、パフォーマンスの向上が望めます。
加えて、ページが軽量になるのでパフォーマンスが向上します。簡単な比較をしてみましょう。次に示す数字はすべてgzipに圧縮したKB単位のファイルサイズです。bootstrap.jsはもとのBootstrapスクリプトで、bsn.jsはBootstrapネイティブのスクリプト、jqはjQueryです。ここではすべてのコンポーネントをバンドルしていますが、どちらのライブラリーもモジュラー構造で、実際には必要なコンポーネントとその依存オブジェクトだけを読み込みます。
Bootstrap.js:
- jq 3.1.0 + bootstrap.js = 34.5 + 11.2 = 45.7
- jq 3.1.0 slim + bootstrap.js = 27.2 + 11.2 = 38.4
- jq 2.2.4 + bootstrap.js = 34.3 + 11.2 = 45.5
- jq 1.12.4 + bootstrap.js = 38.8 + 11.2 = 50.0
BootstrapネイティブのJavaScript:
- minifill + bsn.js = 2.4 + 7.8 = 10.2
- polyfill.io(on chrome 54) + bsn.js = 1.1 + 7.8 = 8.9
- polyfill.io(on IE 8) + bsn.js = 12.1 + 7.8 = 19.9
(IE8用polyfill.ioサイズはここから引用しました. polyfillについては次のセクションで取り上げます)
Bootstrapコンポーネントのファイルサイズは38.4〜50.0KBである一方、Bootstrapネイティブのファイルサイズは8.9〜19.9KBにまで小さくなります。
対応ブラウザー
対応しているブラウザーはもとのjQueryベースのBootstrapスクリプトと同じく、主要なモバイルとデスクトップに搭載される最新のブラウザーとIE8以降です。polyfillにより対応しており、使い方は2つあります。
1つめはPolyfill.ioサービスを使うものです。次のとおり、相対パスのscriptタグをドキュメント内に挿入するだけで、ブラウザーに応じたpolyfill一式を取得できます。
<script src="https://cdn.polyfill.io/v2/polyfill.js"></script>
そのWebサイトで本当に使用される機能に応じたレスポンスを返すよう設定できます。詳しくはPollyfill.ioドキュメントを参照してください。
代わりにプロジェクトの作者が自ら提供しているminifillも使え、より軽量のカスタムpolyfillになる可能性があります。
使い方
使い方はもとのBootstrapスクリプトに似ていますが、jQueryを削除してBootstrapスクリプトをプロジェクトが用意したスクリプトで置き換え、必要ならpolyfillを含めることが相違点です。
閉じタグ</body>の前に、コンポーネントのスクリプトを挿入します。
<script src="https://cdn.jsdelivr.net/bootstrap.native/1.0.4/bootstrap-native.js"></script>
ドキュメントページに掲載されているほかのCDN URLも使えます。また代わりにファイルをダウンロードしてローカルからも利用できます。
polyfillが必要なら、<head>タグに含めてください。
<script src="https://cdn.jsdelivr.net/minifill/0.0.3/minifill.min.js"> </script>
<!--[if IE]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<![endif]-->
このスニペットではminifillを使います。
使い方の詳細はプロジェクトドキュメントページを参照してください。
移植について
Bootstrapネイティブは、厳密には、もとのスクリプトが持つ機能をすべて複製するという文字通りの移植ではありません。作者は主にパフォーマンス向上と開発工程の簡略化のために使用頻度が低いものなど重要ではない機能を意図的に移植していません。
そのために、生じる問題を取り上げます。
カスタムイベント
多くのBootstrapコンポーネントがライフサイクルの中で発信するイベントです。たとえば、モーダルは開いたときと閉じたときにイベントが発生します(実際には'show'の前に1つ、'show'のあとに1つ、計2つのイベントが開いたときと閉じたときです)。
同様のイベントがタブ切り替えの通知にも使われています。hideイベントが現在のタブに発信され、showイベントが表示対象のタブに発信されます。
一方、Bootstrapネイティブはカルーセルとボタンにだけイベントが発生します。もとのカルーセルはスライドの遷移時にカスタムイベントを起こします。1つめのイベントは'slide'で、遷移開始直前に発生します。もう1つのイベント'slid'は遷移完了後に発生します。イベントハンドラ―に渡されたオブジェクトには遷移の情報を持っているdirectionとrelatedTargetの2つのプロパティがあります。
次のjQueryスニペットを見てください。
$carousel
.on('slide.bs.carousel', function(e) {
var caption = $(e.relatedTarget).find('.carousel-caption').text();
console.log('About to slide to the ' + e.direction + ' to slide ' + caption);
})
.on('slid.bs.carousel', function(e) {
var caption = $(e.relatedTarget).find('.carousel-caption').text();
console.log('Slid to the ' + e.direction + ' to slide ' + caption);
});
Bootstrapネイティブは両方のイベントに対応していますが、イベントオブジェクトにはdirectionとrelatedTargetプロパティがありません。先ほどのスニペットを次のようにしてVanilla JavaScriptへ書き換えます。
carousel.addEventListener('slide.bs.carousel', function(e) {
console.log('About to slide');
});
carousel.addEventListener('slid.bs.carousel', function(e) {
console.log('Slid');
});
ほかのコンポーネントにカスタムイベントが必要な場合はどのようにすればよいでしょうか。自分で実装することは難しくありません。Bootstrapネイティブのカルーセルコードを参照して、CustomEvent APIを使います。
最初はイベントオブジェクトを作成します。
if (('CustomEvent' in window) && window.dispatchEvent) {
slid = new CustomEvent("slid.bs.carousel");
slide = new CustomEvent("slide.bs.carousel");
}
スライドが遷移する直前に、「slide」イベントが発生します。
if (slide) {
this.carousel.dispatchEvent(slide);
}
そして遷移完了後に、「slid」イベントが発生します。
if (slid) {
self.carousel.dispatchEvent(slid);
}
このモデルを使って、ほかのコンポーネントに類似のコードを簡単に付け加えられます。
CustomEvent APIに対応しているブラウザーは一部だけですが、あとで取り上げるpolyfillでカバーできます。
プログラミングできるAPI
BootstrapコンポーネントからアクセスできるAPIを使うと、JavaScriptで初期化と操作ができるようになります。たとえば、つぎの3つのメソッドでモーダルエレメントの表示を変更できます。
$('#mymodal').modal('show')
$('#mymodal').modal('hide')
$('#mymodal').modal('toggle')
一方、Bootstrapネイティブは通常このようにプログラミングによる制御はできません。先ほどのメソッドはモーダルには使えず、ドロップダウンやタブ、アラート、カルーセルにも使えません。
そのほかの違い
コンポーネントを1つ1つ比較すると、Bootstrapネイティブが文字通りの移植ではなく、機能を削除したり追加したりしていることが分かります。
たとえばBootstrapでTooltipを使うときは明示的に初期化する必要があります。相対位置のData APIをオプトインにしてパフォーマンスを高めているためです。一方、BootstrapネイティブではData APIの属性が適切に設定されていれば、自動的に初期化されます。さらに、Bootstrapネイティブは追加オプションなしで自動的にtooltipを配置できます。しかし、Bootstrapが持つテンプレートシステムは備えていません。
ドロップダウンもBootstrapコンポーネントを移植するときに異なった実装を選択した一例です。jQueryのドロップダウンはメニューをクリックしたあとに閉じますが、Bootstrap Nativeのメニューは開いたままです。
キーボード入力への対応も不完全です。タブナビゲーションには対応していますが、ほかの操作は部分的に実装されています。
モーダルでは、コンポーネントに読み込まれるソースを指定するremoteオプションは使用できませんが、ダイナミックコンテンツのテンプレートシステムは用意されています。
カルーセルに関しては、jQueryコンポーネントはデフォルトでキーボード入力に反応しますが、Vanilla JavaScriptバージョンでは次のようにdata-keyboard属性で使用を可能にします。
<div id="carousel" class="carousel slide" ... data-keyboard="true">
コンポーネントのdurationオプションをカスタマイズする方法も異なります。これは、現在のスライドから次のスライドに遷移する時間を指定するものです。どちらのライブラリーもデフォルト値は600msで、多くの場合は適切に動作する値です。
この値を変更するには、デフォルトのduration値をオーバーライドするCSSルールを追加します。どちらのライブラリーでもCSSでアニメーションが実行されることを前提にしています。
BootstrapではjQueryコードを使ってJavaScriptに直接コーディングされている値を変更します。
$carousel.data()['bs.carousel'].constructor.TRANSITION_DURATION = 2000;
一方、Bootstrap Nativeではコンポーネントのルート要素にdata-duration属性があるので、変更は簡単です。
<div id="carousel" class="carousel slide" data-ride="carousel" data-interval="false" data-duration="2000">
同じようにしてほかのコンポーネント(例:モーダルとTooltip)でも遷移のdurationを変更できます。
これ以外の問題はドキュメントページとプロジェクトの問題の管理録に掲載されています。
最後に
Bootstrapネイティブは確かに興味深いプロジェクトだと思いますが、私ならもとのjQuery版を捨て去りはしません。結局のところ、ほかの「jQuery vs Vanilla JavaScript」も示すように、実際の使用例に応じて変わるためです。
前のセクションで検討した問題は大きなハードルではないでしょう。完全に移植されたBootstrap JavaScriptコンポーネントを求めてはおらず、かつ多少の違いは乗り越えられると考えているのなら、Bootstrapネイティブが正しい選択でしょう。
なお、このプロジェクトは開発途上であり、GitHub trackerに投稿された問題には迅速にフィードバックされています。
次のBootstrapプロジェクトを立ち上げるときに試してみてください。
※本記事はJoan Yinが査読を担当しています。最高のコンテンツに仕上げるために尽力してくれたSitePointの査読担当者のみなさんに感謝します。
(原文:Quick Tip: Use Bootstrap Components without jQuery)
[翻訳:内藤夏樹/編集:Livit]