カテゴリー別アーカイブ: HTML Imports

customelements.io に Web Componentsを登録する

1. まずは, bowerが必要になるのでインストールしておきましょう.

$ npm install -g bower

2. 次に, (プロジェクトのディレクトリで) bower.jsonを生成します.

$ bower init

いろいろきかれると思いますが, あとから書き換えもできるので,
とりあえずわからない質問はenterキーでとばしましょう.

以下のようなjsonファイルが生成されます.

{
  "name": "x-piano",
  "version": "0.0.0",
  "homepage": "https://github.com/Korilakkuma/x-piano",
  "authors": [
    "Korilakkuma <rilakkuma.san.xjapan@gmail.com>"
  ],
  "description": "Piano illust with Polymer Web Components",
  "main": "x-piano.html",
  "keywords": [
    "Polymer",
    "web-components"
  ],
  "license": "MIT",
  "dependencies": {
    "core-component-page": "Polymer/core-component-page#^0.5",
    "webcomponentsjs": "~0.5.5"
  },
  "ignore" : [
  ],
  "private" : false
}

customelements.ioにWeb Componentsを登録する場合に特に重要なポイントは3つです.

  • kyewordsに “Polymer”と”web-components”を記述する
  • “ignore”キーを指定する (対象ファイルがなければ空配列の指定でOKです)
  • privateキーの値をfalseにする

3. customelements.ioに登録します

$ bower register [タグ名] [GitHubのリポジトリ]

例えば, x-pianoの場合,

$ bower register x-piano git://github.com/Korilakkuma/x-piano

となります.

4. 登録の確認

$ bower search [タグ名]

上記のコマンドでSearch resultsが表示されればOKです.

例えば, x-pianoだと,

$ bower search x-piano
Search results:

    x-piano git://github.com/Korilakkuma/x-piano.git

と表示されます.

ただ, customelements.ioには数時間経過しないと検索結果に反映されないので, ご注意してください. 適当に時間をおいて検索すれば, 自作したWeb Componentsがヒットするはずです.

Web Components x-pianoのレスポンシブ対応

前々から構想していた, x-pianoのレスポンシブレイアウト対応をついに実装しました.

<x-piano responsive></x-piano>

x-pianoタグにresponsive属性を記述することで, ブラウザ幅に応じてレイアウト (ピアノの鍵盤数が変化します)

デモはこちらです.

Web Components x-piano のアップデート

久々のアップデート

今回のアップデートでサウンド生成・停止のためのイベントをトリガーできるようになったので, ユーザーの操作だけでなくプログラムからもサウンド生成可能になりました. MMLによる自動演奏などに利用できると思います

HTML Importsでのdocumentオブジェクト

HTML Importsを利用すれば, HTMLを簡単に読み込むことができるわけですが, 1つ問題があります. 文書が複数存在することになるので, DOMツリーのルートであるdocumentオブジェクトが複数存在することになります.

もっとも, メイン文書側 (importする側のHTML) ではdocumentはメイン文書側のDOMを扱うオブジェクトとして機能します.

問題は, サブ文書側 (importされる側のHTML) のdocumentオブジェクトです. サブ文書のみで利用する場合は, documentオブジェクトは自身のDOM, つまり, サブ文書のDOMを扱うオブジェクトとして機能するのですが, このサブ文書がimportされると, importした側のDOMを扱うオブジェクトとなってしまいます.

importされる文書は多くの場合, Web Componentsです. サブ文書側で, Custom Elementsを定義して, ShadowRootを生成して, <template>の内容をShadow DOMに追加する処理が当たり前のようにされるわけですが…

<!-- サブ文書 (importされるHTML) -->
<template>
    <div>Sub Document</div>
</template>
<script>
    var xElement = document.registerElement('x-element', {
        prototype : Object.create(HTMLElement.prototype, {
            createdCallback : function() {
                 //importされた場合, documentはメイン文書側のdocumentオブジェクトとなるため, 意図したDOMを生成できない
                 var template     = document.querySelector('template');  //Maybe, null
                var shadowRoot = this.createShadowRoot();
                var clone             = document.importNode(template.content, true);
                shadowRoot.appendChild(clone);
            }
        }
    });
</script>

これを解決するためには, document.currentScriptを利用します.
document.currenScriptの仕様に関してはこちらを参照してください.

簡単に言えば, 現在のスクリプトが実行されている<script>のHTMLScriptElementを参照することができます.

これで, サブ文書側のDOMにアクセスする踏み台ができるわけです. あとは, ownerDocumentプロパティを参照することで, サブ文書のdocumentオブジェクトを参照できるようになるので, importされてもサブ文書側のDOMにアクセスすることができて, <template>の内容をShadow DOMに追加することができるようになります.

ただし, 1つ注意点として, currentScriptの仕様にもありますが,

スクリプト内のコードがコールバックまたはイベントハンドラとして呼び出されている場合、<script>要素を参照しないことに注意が必要です

したがって, メイン文書側でお決まりのように,

document.addEventListener('DOMContentLoaded', function() {
    //do something ...
}, true);

としてしまうと, メイン文書document.currentScriptがnullとなってしまうので, サブ文書側でサブ文書のdocumentオブジェクトを参照できなくなってしまいます.

HTML importsを利用する場合, importする側では, importされた文書のロードが完了した後に, メイン文書側で必要な処理を記述したいはずなので, HTMLLinkElementのonloadイベントハンドラにメイン文書側で必要な処理を設定します.

こんな感じ.

<script>
    var onload  = function(event) { //do something... };
    var onerror = funciton(event) { //do something... };
</script>
<link rel="import" href="sub.html" type="text/html" onload="onload(event)" onerror="onerror(event)" />

もしくは, 動的にを生成してもOKです.

<script>
(function() {
    var link = document.createElement('li');

    link.setAttribute('rel', 'import');
    link.setAttribute('href', 'sub.html');
    link.setAttribute('type', 'text/html');

    link.onload  = function(event) { //do something... };
    link.onerror = function(event) { //do something... };

    document.querySelector('head').appendChild(link);
})();
</script>

実は, 自分が作成しているWeb Components (x-piano) で, これまでのバージョンでは, サブ文書側のdocumentオブジェクトの参照方法がわからず, Shadow DOMの生成を邪道のinnerHTMLで実装していました….

しかし, これでサブ文書側のdocumentオブジェクトの参照法右方がわかったので, 現在のバージョンでは, <template>の内容をコピーすることで, スマートにShadow DOMを生成しています.

参考