bootstrap-carouselタグを実装 の変更点

更新


[[公開メモ]]

* 目次 [#ce63a3b6]

#contents

* 概要 [#c7be18af]

Riot.js というライブラリがあって最近話題です。

- Riot.js http://riotjs.com/ja/
- Qiita の記事 http://qiita.com/search?q=riot

これを使って bootstrap の carousel を簡単に実装するためのカスタムタグを作ってみました。

https://gist.github.com/osamutake/f800cb0a7c638f058bded6702bc31356

確かにすごく簡単で、便利に使えそうです。

* Bootstrap の carousel [#v03edf07]

Bootstrap を使うと、次のように画像が次々にスクロールする carousel を簡単に実装できます。

&attachref(bootstrap-carousel.gif);

実働サンプルはこちらです。

http://jsbin.com/peqipi/2/edit?html,output

* ただ簡単とはいえ [#f605dba1]

普通にやると、この程度のコードを書く必要があります。

 LANG:html(linenumber)
   <div id="carousel-example-generic" class="carousel slide" data-ride="carousel" data-interval="1500">
       <!-- Indicators -->
       <ol class="carousel-indicators">
           <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
           <li data-target="#carousel-example-generic" data-slide-to="1"></li>
           <li data-target="#carousel-example-generic" data-slide-to="2"></li>
       </ol>
 
       <!-- Wrapper for slides -->
       <div class="carousel-inner">
           <div class="item active">
               <img src="http://placehold.it/800x400" alt="Caption #1">
               <div class="carousel-caption">
                   <h2>Caption #1</h2>
               </div>
           </div>
           <div class="item">
               <img src="http://placehold.it/800x400" alt="Caption #2">
               <div class="carousel-caption">
                   <h2>Caption #2</h2>
               </div>
           </div>
           <div class="item">
               <img src="http://placehold.it/800x400" alt="Caption #3">
               <div class="carousel-caption">
                   <h2>Caption #3</h2>
               </div>
           </div>
       </div>
 
       <!-- Controls -->
       <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev">
           <span class="fa fa-angle-left"></span>
       </a>
       <a class="right carousel-control" href="#carousel-example-generic" data-slide="next">
           <span class="fa fa-angle-right"></span>
       </a>
   </div>

- 11-28 行目に画像とキャプションが羅列されています
- 4-6 行目は中央の白丸にあたるもので、画像の数だけ並べます
- 32-37 行目は手動でスクロールするための左右のコントロールにあたります

画像を1枚追加したければ、11-28 行目と、4-6 行目を両方変えなければならず面倒ですし、
何をやっているか見通しも悪いです。

* Riot.js を使うと [#p50c77b1]

上と同じ carousel を

 LANG:html(linenumber)
 <bootstrap-carousel interval="1500">
   <img src="http://placehold.it/800x400" alt="Caption #1">
   <img src="http://placehold.it/800x400" alt="Caption #2">
   <img src="http://placehold.it/800x400" alt="Caption #3">
 </bootstrap-carousel>

と書けるようになります!

画像の url と caption を列挙するだけ、
これ以上シンプルにはならない形です。
* Riot.js のカスタムタグ [#sdc5a684]

 LANG:html
 <bootstrap-carousel interval="1500"> ... </bootstrap-carousel>

の部分がいわゆる「カスタムタグ」です。

Riot.js は html ソースに埋め込まれたこのようなカスタムタグを、
別に与えた変換規則を用いて html に変換してくれるライブラリです。

** _innerHTML の利用 [#s46c6cec]

よくある Riot.js のカスタムタグの利用例では、タグの中身を空にして、

 LANG:html
 <bootstrap-carousel interval="1500" />

とか、

 LANG:html
 <bootstrap-carousel interval="1500"></bootstrap-carousel>

のような形で使っています。

しかし、ドキュメントにもあるとおり
Riot.js は通常の html をカスタムタグで囲って使うことも許しています。

 LANG:html
 <bootstrap-carousel interval="1500">
   <!-- 任意の html -->
 </bootstrap-carousel>

カスタムタグに囲われた innerHTML は通常 <yield /> を呼び出して利用します。

ただその方法では、出力する html のどこかに、innerHTML をそのままの形で取り込むことしかできず、
利用目的が限られてしまいます。

実はこの内容には javascript からも this.root._innerHTML としてアクセス可能なため、
下記コードではこれを利用しています。(innerHTML ではなく _innerHTML であることに注意して下さい)

* カスタムタグの実装 [#rdb9cbb7]

以下のコードを bootstrap-carousel.tag として用意し、
riot.js で javascript へコンパイルして使います。

実動サンプルはこちら: http://jsbin.com/qoduteguli/edit?html,output

&attachref(bootstrap-carousel2.gif);

 LANG:html(linenumber)
 <bootstrap-carousel>
 
   <div id="carousel-example-generic" class="carousel slide" data-ride="carousel"
     data-interval={ this.interval } data-pause={ this.pause }
     data-wrap={ this.wrap } data-keyboard={ this.keyboard }>
       <!-- Indicators -->
       <ol class="carousel-indicators">
           <li each={ this.items } data-target="#carousel-example-generic" data-slide-to={ index } class={ active: index==0 }></li>
       </ol>
 
       <!-- Wrapper for slides -->
       <div class="carousel-inner">
           <div each={ this.items } class={ item: true, active: index==0 }>
               <img src={ src } alt={ caption }>
               <div class="carousel-caption">
                   <h2>{ caption }</h2>
               </div>
           </div>
       </div>
 
       <!-- Controls -->
       <a class="left carousel-control" href="#carousel-example-generic" data-slide="prev">
           <span class="fa fa-angle-left"></span>
       </a>
       <a class="right carousel-control" href="#carousel-example-generic" data-slide="next">
           <span class="fa fa-angle-right"></span>
       </a>
   </div>
   
   <style scoped>
     .carousel-inner > .item {
       text-align: center;
     }
     .carousel-inner > .item > img {
       display: inline;
     }
     .carousel-inner .carousel-caption h2 {
       color: rgba(0,0,0,0.6);
       font-size: 40px;
       text-shadow: 0px 0px 5px rgba(255, 255, 255, 0.6),
                    0px 0px 10px rgba(255, 255, 255, 0.6);
     }
     .carousel-indicators li {
       background-color: rgba(0,0,0,0.4);
       box-shadow: 0 0 5px rgba(0,0,0,0.6);
       border-color: rgba(255,255,255,0.8);
       margin-left: 6px;
       margin-right: 6px;
     }
     .carousel-indicators li.active {
       border-color: rgba(255,255,255,0.8);
       background-color: rgba(255,255,255,0.8);
       margin-left: 6px;
       margin-right: 6px;
     }
     img {
       opacity: 0.8;
     }
   </style>
 
   <script type="coffee">
     @on 'update', ->
       @interval = opts.interval ? 5000
       @pause    = opts.pause    ? "hover"
       @wrap     = opts.wrap     ? true
       @keyboard = opts.keyboard ? true
 
       @items = for img, i in $(@root._innerHTML) when img.tagName == 'IMG'
         caption: img.alt
         src:     img.src
         index:   i
   </script>
 </bootstrap-carousel>

- 3-28 行目が html ソース
-- { } で囲まれた部分が javascript として解釈されて、結果に置き換えられる
-- { value: expr } の部分は、expr の評価結果が true なら value に、さもなければ空になる
-- each={ } のついたタグは与えられた配列と同じ数だけ繰り返される
- 30-59 行目が css ソース
-- scoped としておくと、カスタムタグの下の要素にのみ適用される
-- type="scss" などを指定して、scss などで書くこともできる
- 61-73 行目が javascript ソース
-- 変換前に一回呼ばれる
-- on('update', function(){ ... }); を呼ぶことで、値の更新時に処理を行える
-- 基本は this.variable に値を設定して、javascript で { this.variable } を参照する形
-- this.root._innerHTML でカスタムタグに囲まれた html ソースが得られる

* コンパイルとマウント [#ff697abb]

上記のようなタグの定義は Riot.js で javascript に変換して呼び出すか、
riot.compile にソースを渡してやることでタグライブラリに取り込み、
riot.mount でカスタムタグを含んだ html へ適用します。

 LANG:javascript
   riot.compile(tag_source);
   riot.mount('*');

* イベントやオブザーバブル、ルートなど [#a72f29a9]

ここでは使いませんでしたが、Riot には他にもいろいろ機能があるようです。

今後も徐々に使いながら慣れていこうと思います。

* カスタムタグを TinyMCE editor で入力したい [#q89f512d]

こちらで試し中:

http://codepen.io/anon/pen/dMLXBy?editors=1010

+ custom_elements でタグを許可する
+ extended_valid_elements でプロパティを許可する
+ カスタムタグ入力用のメニューを作成する
-- toolbar1 か toolbar2 に custom_tags というツールグループを作成して、
-- setup で editor.addButton を読んでメニューを追加
-- メニューの onclick で editor.iinsertContent する
+ 編集中にタグが見えるようにスタイルシートを追加する


* 質問・コメント [#saad47c4]

#article_kcaptcha
**無題 [#vc3f6936]
> (&timetag(2016-10-15T04:42:06+09:00, 2016-10-15 (土) 13:42:06);)~
~
面白い記事でした。しかしoptsで渡したほうが汎用性高いような・・・~

//
- おっしゃる通り、riot べったりで行く方針なら opts で渡すのが良いですね。今回は html を拡張するようなイメージでいたため、こんな実装になりました。 -- [[武内(管理人)]] &new{2016-10-16 (日) 20:38:33};
- 今回は html を拡張するようなイメージでいたため、こんな実装になりました。おっしゃる通り、riot べったりで行く方針なら opts で渡すのが良いですね。 -- [[武内(管理人)]] &new{2016-10-16 (日) 20:38:33};

#comment_kcaptcha

Counter: 6083 (from 2010/06/03), today: 1, yesterday: 2