WebComponents
Polymer

Polymer 1.0とBootstrap v3を組み合わせる

More than 3 years have passed since last update.

laco0416です。Polymer 1.0からBootstrapのテーマを自作Custom Elementsに適用するのが簡単になった話をします。


0.5から1.0で変わったこと

前回の記事に書いたとおり、Polymer 0.5系におけるShadow DOMの実装はW3Cの仕様に忠実に、Shadow DOMの外からのCSSの侵入を防ぐようになっていました。しかしPolymer 1.x系では実装を軽量で高速な Shady DOM に切り替えたことで、CSSのスコープは内から外への漏洩を防ぐのみとなりました(W3Cの仕様からはズレますが…)。

この変更によりCustom Elements内でグローバルのスタイルを柔軟に適用できるようになったので、Bootstrapのようなテーマと共に使うのがごくごく自然な書き方で実現できます。


PolymerとBootstrapを組み合わせる意義

WebComponentsのないHTMLはスタイリングのためだけにdivネスト地獄を容易に引き起こします。例えば名前の部分だけスタイルを変えるネームタグを作る次のようなHTMLはセマンティックでない構造をしています

<div class="outer">

<div class="boilerplate">
Hi! My name is
</div>
<div class="name">
Bob
</div>
</div>

我々がやりたいことは「ネームタグを描画する」ことです。そしてそのために必要な情報だけをもつ構造は次の形です

<div id="nameTag">Bob</div>

これでは#nameTagの要素にしか適用できないので、汎用的にDOM要素として配置できるようにするのがWebComponentsであり、Polymerです。

<name-tag name="Bob"></name-tag>

誰がどうみてもこのタグはネームタグに「Bob」を表示する要素ですね。

WebComponentsはこのように、divの入れ子構造による非セマンティックなスタイリングを取り払い、DOM構造に意味を持たせることが可能な技術です。

Bootstrapは御存知の通り、だれでも(デザインなんてわからないプログラマーでも!)簡単にレスポンシブで、それなりの見た目のWebページ、アプリケーションが作れるCSSテーマとJSのツールキットのセットです。そして、Bootstrapはとりわけdivネストが深くなりがちです。BootstrapとPolymerを組み合わせることで、少しでもセマンティックなindex.htmlを書くことは、三ヶ月後にそのコードを読む自分を少なからず幸せにするでしょう。


サンプル

Bootstrapにはいくつかのサンプルが用意されていますが、その中で今回は「Jumbotron」というサンプルの中にある小さなパーツをPolymerで作ってみました。

image

完成品がこちらの<row-item>。ドキュメントからheaderlink-toを属性として指定し、コンテンツ部分は分散ノードとして取り込みました

<!DOCTYPE html>

<html>
<head lang="ja">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap with Polymer</title>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
<link rel="import" href="elements/row-item.html"/>
</head>
<body>
<div class="container">
<div class="row">
<row-item class="col-md-4" header="ヘッダーA" link-to="#A">
<p>Content of A</p>
</row-item>
<row-item class="col-md-4" header="ヘッダーB" link-to="#B">
<p>Content of B</p>
</row-item>
<row-item class="col-md-4" header="ヘッダーC" link-to="#C">
<p>Content of C</p>
</row-item>
</div>
</div>
</body>
</html>

image


準備

bowerで必要なパッケージをインストールします

> bower install --save polymer PolymerElements/paper-elements bootstrap

エントリポイントになるHTML(ドキュメント)でWebComponentsのPolyfillとBootstrapのCSSファイルを読み込みます。


index.html

<!DOCTYPE html>

<html>
<head lang="ja">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap with Polymer</title>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
</head>
<body>
</body>
</html>


コンポーネント作成

element/row-item.htmlを作成します。今回はドキュメントからBootstrapのスタイルが降ってくるので、コンポーネント内からBootstrapを呼び出す必要はありません。もちろん広く配布したいコンポーネントであればコンポーネント自体をBootstrapに依存させて<link>でCSSファイルを読み込むことも可能です。(参考)

<link rel="import" href="../bower_components/polymer/polymer.html"/>

<link rel="import" href="../bower_components/paper-button/paper-button.html"/>

<dom-module id="row-item">
<template>
<h2>{{header}}</h2>

<content></content>

<paper-button class="btn btn-primary" on-click="goLink">View details &raquo;</paper-button>
</template>
<script>
Polymer({
is: 'row-item',
properties: {
header: {
type: String,
value: "Heading"
},
linkTo: {
type: String
}
},
goLink: function () {
window.location.href = this.linkTo;
}
});
</script>
</dom-module>


プロパティの宣言

コンポーネントを使用する側からAttributeとして値を渡すにはpropertiesの中でプロパティを宣言する必要があります。

    Polymer({

is: 'row-item',
properties: {
header: {
type: String,
value: "Heading"
},
linkTo: String
}
});

プロパティは名前をキーにオブジェクトもしくは型を指定します。オブジェクトを指定する場合もtypeプロパティは必須です。valueは初期値になります。

プロパティを文字列としてDOM中で展開するには{{}}を使います

<h2>{{header}}</h2>

イベントハンドラ内で使う場合はthisのプロパティとして呼び出せます

      goLink: function () {

window.location.href = this.linkTo;
}

イベントリスナは通常のHTML要素と同じように記述できます。ハンドラーはPolymer()に渡すオブジェクトのプロパティとして宣言します。

<paper-button class="btn btn-primary" on-click="goLink">View details &raquo;</paper-button>

    Polymer({

is: 'row-item',
properties: {
header: {
type: String,
value: "Heading"
},
linkTo: {
type: String
}
},
goLink: function () {
window.location.href = this.linkTo;
}
});


CSSテーマの適用

ここまでで気づいた方もいるかと思いますが、paper-buttonのクラスにbtn btn-primaryが指定されています。これはBootstrapで宣言されているボタン向けのスタイルです。

image

Bootstrap 3系はフラットデザインがベースになっているので、クリックした際のアニメーション等はありません。

この<row-item>ではPolymerのPaper Elementsに含まれる<paper-button>にこのクラスを設定しています。<paper-button>はマテリアルデザインのボタンスタイルになっていて、クリックした場所から波紋のようにアニメーションが広がります。

image

今回<paper-button>btn btn-primaryを設定したことで、<paper-button>のアニメーションはそのままに、カラーリングやシェイプはBootstrapのbtn btn-primaryを適用することができました

image


配置

作成した<row-item>をドキュメントに配置します。


index.html

<!DOCTYPE html>

<html>
<head lang="ja">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap with Polymer</title>
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"/>
<link rel="import" href="elements/row-item.html"/>
</head>
<body>
<div class="container">
<div class="row">
<row-item class="col-md-4" header="ヘッダーA" link-to="#A">
<p>Content of A</p>
</row-item>
<row-item class="col-md-4" header="ヘッダーB" link-to="#B">
<p>Content of B</p>
</row-item>
<row-item class="col-md-4" header="ヘッダーC" link-to="#C">
<p>Content of C</p>
</row-item>
</div>
</div>
</body>
</html>

<row-item>も他のHTML要素と変わらず、rowcol-md-4でレスポンシブに表示することができます。


まとめ

Polymer 1.0のShady DOMのおかげで特にShadow DOMのCSSスコープがあることを感じずに簡単にコンポーネント中でBootstrapが使えました。BootstrapにかぎらずほとんどのCSSテーマが同様に使えると思います。既存のWebアプリケーションを部分的にでもPolymerでコンポーネント化してみてはどうでしょうか?