ご存知の通り、HTMLとCSS、JavaScriptには明確に役割が決まっています。 つまり外観を整えるCSSとHTML、何かしらの処理を行うJavaScriptからWebアプリケーションができています。 HTMLが持つマークアップはセマンティックWebからも重要であることが理解できます。
重要なCSS、HTMLですが、Webアプリケーションを作る上で幾つか問題も指摘されています。例えば
- 再利用性が悪い
- CSSスコープが基本的にはグローバル
ということが問題視されます。 重要なCSS定義では「!important」というキーワードを利用することで「スタイルの優先」が定義できます。 この「!important」は後勝問題を解決するために用いることが可能です。
具体的な例を見てみます。
(!important無し)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
body {background:#ffdff9}
</style>
</head>
<body style="background:#000000;">
</body>
</html>
(!importantあり)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
body { background:#ffdff9 !important ; }
</style>
</head>
<body style="background:#000000;">
</body>
</html>
「!important」としたCSS定義が優先されブラウザのレンダリングに影響を及ぼしていることが理解できます。 ただ、こうしたキーワードを利用してもWebアプリケーションを開発するための根本的な問題解決にはなっていません。
CSSを用いて開発する場合開発手法として幾つか提案されています。
- OOCSS: Object Oriented CSS
- BEM: Block、Element、Modifier
- SMACSS: Scalable and Modular Architecture for CSS
OOとは、「Object Oriented(オブジェクト指向)」のことで、設定ひとつひとつをCSSオブジェクトとして考え、Webページをレゴの集まりのように考えます。
BEMとは、Block、Element、Modifierの略語です。 Webサイトのコンポーネント化のためのフロントエンド設計方法のひとつで、厳格なclass名の命名ルールが特徴的な手法です。 そして、__や、_、-を使って単語と単語を区切ります。BEMではこれをセパレーターと呼んでいます。 Block、Element、Modifierのそれぞれの区切りにセパレーターを使用します。すなわち
- BlockとElementとの区切り
- Block(あるいはElement)とModifierとの区切りとModifierのkeyとvalueの区切り
- BlockやElement名を、2つ以上の単語で表す場合の単語と単語との区切り
BEMのデメリットは、次のことが上げられます:
- 独特な記法のため、BEMを知らない人から見ると、違和感を持つ人もいる
- ElementやModifierの名前が冗長になりがち
SMACSSとは、Scalable and Modular Architecture for CSSの略語で、「スマックス」と読みます。 SMACSSはCSSの設計手法のひとつで、CSSのルールを5種類にカテゴライズした上で、それぞれの考え方や記述ルールが取り決められているのが特徴的な手法です。
(SMACSSの5つのルール)
- ベース:要素そのもののデフォルトスタイル
- レイアウト:ページをエリアごとに分割
- モジュール:再利用可能なパーツ
- 状態(ステート):レイアウト,モジュールの特定状態を示す
- テーマ:サイトのルック&フィールを定義
CSS自体の問題や設計手法によって問題をどう解決しようとしたか具体的に見てきましたが、複雑になりがちなWebアプリケーションの解決に対するきれいな回答を得ることは難しいです。 こういった問題をWeb Componentsが解決してくれるのではという意味においても期待されています。
- 複雑になりがちなHTML+CSSによるWebページの構築を綺麗にしてくれる
- ライブラリや方法論に依存せずコンポーネントを再利用することができる
それではWeb Componentsについて見ていきます。
Web ComponentsはHTMLを部品化する仕組みです。 Web Componentsというのは概念であり、次の4つのWeb標準からできています。
- Custom Element
- HTML Templates
- HTML Imports
- Shadow DOM
Custom Elementsは独自タグを定義する仕組みです。 W3Cで定義されたタグを利用するだけでなく、自分たちでタグを定義することが可能になります。
> document.registerElement('my-app')
function my-app() { [native code] }
カスタムタグとして認められないケースももちろん存在します。
> document.registerElement('my_app')
> document.registerElement('myapp')
まとめると次のルールがあります。
- 二重登録できません
- 大文字小文字の判定はしません
- 「-」は必須です。文字と文字で挟む必要があります
このように、新しい要素を登録するためにはdocument.registerElement
を利用します。
var XFoo = document.registerElement('x-foo');
document.body.appendChild(new XFoo());
要素をインスタンス化するためにはいくつか方法があります。
- 宣言する
- JavaScript で DOM を生成する
- new オペレーター を使う
ひとつひとつ見ていきましょう
カスタムタグを通常のHTMLタグと同様に定義します
<x-foo></x-foo>
JavaScript で DOM を生成する
var xFoo = document.createElement('x-foo');
xFoo.addEventListener('click', function(e) {
alert('Thanks!');
});
new オペレーター を使う
var xFoo = new XFoo();
document.body.appendChild(xFoo);
JavaScript プロパティとメソッドを追加することも可能です。
var XFooProto = Object.create(HTMLElement.prototype);
// x-foo に foo() メソッドを追加
XFooProto.foo = function() {
alert('foo() called');
};
// 読み込み専用のプロパティ "bar" を定義
Object.defineProperty(XFooProto, "bar", {value: 5});
コールバックメソッドが幾つか定義されています。
コールバック名 | 呼び出されるタイミング |
---|---|
createdCallback | 要素のインスタンスが作られた時 |
attachedCallback | インスタンスがドキュメントに追加された時 |
detachedCallback | インスタンスがドキュメントから取り除かれた時 |
attributeChangedCallback(attrName, oldVal, newVal) | 属性が追加、削除、更新された時 |
次のように書くことができます。
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {...};
proto.attachedCallback = function() {...};
マークアップを追加します。
var XFooProto = Object.create(HTMLElement.prototype);
XFooProto.createdCallback = function() {
this.innerHTML = "<b>I'm an x-foo-with-markup!</b>";
};
var XFoo = document.registerElement('x-foo-with-markup', {prototype: XFooProto});
次に「HTML Templates」について見ていきます。 HTML 要素はマークアップ上のテンプレートを表わします。
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
テンプレートはアクティベートされ初めてブラウザで動作可能となります。
- コンテンツはアクティベートされるまで自律動作しません。
- スクリプトは動作しませんし、画像はロードされません。オーディオも再生されません
- コンテンツはドキュメント内に存在しないものとして扱われます
var t = document.querySelector('#mytemplate');
// src をランタイムに埋める
t.content.querySelector('img').src = 'logo.png';
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);
HTML ImportsはHTML Templatesで再利用可能にしたHTMLを取り込むための仕様です。 ページに を宣言してインポートします。サンプルを見てみましょう。
(import.html)
<template>
<h1>Hello World!</h1>
<!-- <template> が実際に使われるまで img はリクエストされない -->
<img src="world.png">
<script>alert("Executed when the template is activated.");</script>
</template>
(index.html)
<html>
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// import で <template> をクローンする
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector('#container').appendChild(clone);
</script>
</body>
</html>
上記3つでHTMLの部品化は出来ましたが、問題はCSSのスコープにあります。そのスコープを定義するためにShadow DOMという仕様があります。このShadow DOMのお陰で部品化されたコンポーネントの書式を隠蔽することができます。
次の具体的なサンプルを見てみましょう。
https://github.com/albatrosary/webcompoments
応用として次のサンプルを実行してみましょう。
https://github.com/albatrosary/x-business-card
Web ComponentsはWeb標準となる仕様でブラウザでのネイティブ実装になっています。webcomponents.jsというポリフィルライブラリをラッピングしています。これによりすべてのブラウザでPolymerが動くことを保証します。Web Componentsを利用しやすく実装されたライブラリがPolymerになります。Polymerは7つの機能から構成されます。
- Fe(Iron Elements)
- Md(Paper Elements)
- Go(Google Web Components)
- Au(Gold Elements)
- Ne(Neon Elements)
- Pt(a Elements)
- Mo(Molecules)
https://www.polymer-project.org/1.0/