2016年11月18日金曜日

改造版フィードガジェット! ~Blogger テンプレートを作るよ! (29)~

こんにちはー!

前回作成したフィードガジェットに、ついに 『一覧はこちら』 ボタンを追加することに成功しました!


というわけで、どういう風にやったかを公開します。

(前回:『フィードガジェットでラベルごとの最新記事取得 ~Blogger テンプレートを作るよ! (28)~』)




こんな風にして作りました

javascript をヘッダーに追加

前回の続きなのでいきなりですが、テンプレートのHTMLを書き換えます。</head> の直前辺りに、以下のコードをコピペします。

<script>
  // <![CDATA[
  function addIndexLink(url, id, linkText) {
    // 引数チェック
    if (url == null || id == null) return;
    // blogger のフィード URL の形式か否か
    if (url.search(/^[^:]*:\/\/[^\/\?#]*\/feeds\/posts\/[^\/\?#]*/) == -1) return;
    // urlの基礎(アドレス部分の抽出)
    var urlbase = url.match(/^[^:]*:\/\/[^\/\?#]*/)[0];
    // urlのパス(ラベル部分の抽出)
    var urloption = url.slice(urlbase.length + "/feeds/posts/default".length).match(/^[^#\?]*/);
    // リンク先 URL の部品
    var linkUrl = urlbase + "/search";
    var linkOpt = [];
    // ラベル取得の場合
    if (urloption != null && urloption.length > 0 && urloption[0].search(/^\/-\/.+$/) != -1) {
      var labels = urloption[0].slice(3).split("/");
      // 最初の一つはラベル検索
      if (labels.length > 0) {
        linkUrl += "/label/" + labels[0];
      }
      // 二つ目以降はキーワード検索&関連度順
      if (labels.length > 1) {
        var q = labels[1];
        for (var i = 2; i < labels.length; i++) {
          q += "+" + labels[i];
        }
        linkOpt.push({"name": "q", "data": q});
        linkOpt.push({"name": "by-date", "data": "false"});
      }
    }
    // 特殊ケース対策(PCでモバイル表示中、モバイルでPC表示中)
    var mobileFlag = window.location.search.match(/[\?\&]m=\d[\?\&$]/);
    if (mobileFlag != null && mobileFlag.length > 0) {
      linkOpt.push({"name": "m", "data": mobileFlag[0].match(/\d/)[0]});
    } 
    // リンク URL の生成
    if (linkOpt.length > 0) {
      linkUrl += "?" + linkOpt[0].name + "=" + linkOpt[0].data;
      for(var j = 1; j < linkOpt.length; j++) {
        linkUrl += "&" + linkOpt[j].name + "=" + linkOpt[j].data;
      }
    }
    // リンクをガジェットに追加
    var widget = document.getElementById(id);
    var widgetContent = widget.querySelector(".widget-content");
    var widgetFooter = document.createElement("div");
    var widgetAnchor = document.createElement("a");
    var widgetLinkText = document.createTextNode(linkText);
    widgetAnchor.setAttribute("href", linkUrl);
    widgetAnchor.appendChild(widgetLinkText);
    widgetFooter.setAttribute("class", "widget-footer");
    widgetFooter.appendChild(widgetAnchor);
    widget.insertBefore(widgetFooter, widgetContent.nextSibling);
    var widgetClass = widget.getAttribute("class");
    widget.setAttribute("class", widgetClass + " linkAdded");
  }
  // ]]>
</script>
今回はこれを考えるのが一番大変でした。正規表現とか久しぶりに使ったので、もう何日も試行錯誤の連続です。

このスクリプトでは、addIndexLink という関数を定義しています。この関数に適切な引数を与えてやると、 『一覧はこちら >』 ボタンをフィードガジェットに追加してくれます。

リンクを追加する方法はもっと短いものもあるかもしれません。というかあった気がします。昔仕事で XML の解析プログラムを書いたときのノリで XML でも使える関数をよく使う癖があるだけです。

一応コメントを付けていますが、妙にテクニカルなこともしているので分かりにくいかもしれません。ここの解説が欲しいよという方がいらっしゃいましたら、コメントを下されば解説します。

ガジェットのテンプレートを書き換える

ガジェットのテンプレートを以下のように変えます。
<b:widget id='Feed1' locked='false' title='最新の制作日誌' type='Feed' version='1' visible='true'>
  <b:includable id='main'>
    <h2>
      <data:title/>
    </h2>
    <div class='widget-content' expr:id='data:widget.instanceId + &quot;_feedItemListDisplay&quot;'>
      <span style='filter: alpha(25); opacity: 0.25;'>
        <a expr:href='data:feedUrl'>
          <data:loadingMsg/>
        </a>
      </span>
    </div>
    <script>
      var feedUrl = &quot;<data:feedUrl/>&quot;;
      var widgetId = &quot;<data:widget.instanceId/>&quot;;
      // <![CDATA[
      addIndexLink(feedUrl, widgetId, "一覧はこちら >");
      // ]]>
    </script>
    <b:include name='quickedit'/>
  </b:includable>
</b:widget>
widget-content クラスの id 属性のところにある、 data:widget.instanceId というのがガジェットの id タグの中身と同じようなので、それを利用してヘッダーの方で定義した addIndexLink 関数を呼び出しています。

やっているのはこれだけなんですが、 feedUrl を取得する方法がこのガジェットのテンプレート内でデータタグを使うしかないので、フィードガジェットを追加するたびにガジェットのテンプレートを逐一書き換える必要がどうしてもあるのです。

どうにかならないものか、調査を続けます。

ガジェットの外見を変更する

今回の修正に合わせてガジェットの外見に少しだけ手を加えています。
.information .Feed h2 {
display:inline-block;
background: orange;
border: 0;
padding: 0.3em 0.5em 0.2em 0.5em;
margin-bottom: 0;
font-size: 1.5em;
text-align: left;
width: auto;
border-radius: 10px 10px 0 0;
}
.information .Feed .widget-content {
background: white;
margin-top: 0;
border: 2px solid gray;
padding: 0.4em;
font-size: 1.5em;
}
.information .Feed.linkAdded .widget-content {
border-bottom: 0;
}
.information .Feed .widget-footer {
background: #f3f3f3;
margin-top: 0;
border: 2px solid gray;
border-top: 0;
padding: 0.2em 0.4em;
font-size: 1.5em;
text-align: right;
}
.linkAdded というのは、リンクを追加したフィードガジェットだよという印です。なんでこんなことをしているのかというと、二つの div 要素の枠を組み合わせて一つの枠に見せたいからです。widget-footer を widget-content の中に入れる方法は、とある理由により使えませんでした。

工夫したこととか、思ったこととか

このフィードガジェット、地味に色々な対策が施されています。

ここに苦戦! フィード URL が Blogger か否かの判定

フィードガジェットには、Blogger 以外のフィード URL も使用することができます。しかし、今回は Blogger の検索ページへのリンクを差し込むので、Blogger のフィード URL 以外では反応しないよう判定する必要がありました。

厳密にやるとしたら、フィードの中身の何かを使って判定するとか、実際にブログのトップページを受け取ってメタタグなどから Blogger かどうかを判定する必要があると思いますが、かなりのコード量になると思ったので断念しました。

Blogger のフィードURL は以下のようになっているのですが、この途中にある太字の部分があるかないかで 「きっと Blogger だ!」 という判定をしています。
ブログのトップページのURL/feeds/posts/default/-/ラベル名
ちなみに、default の部分は summary というのもあるので、何かの文字列であることを判定しています。posts の部分が comments だったり pages だったりすることもありますが、適切な検索ページが見つからなかったので今回は対応外としました。

最新のコメント一覧から 「もっと見る」 リンクとかあっても便利そうですよね。夢が膨らみます。

ここが自慢! モバイル表示とPC表示、 m=1 と m=0

たとえばモバイル端末からPC表示でブログを見ていた時、普通に自前で用意したリンクをクリックするとモバイル表示に戻ってしまいます。

Blogger では URL の ? 以降に m=数値 というパラメーターが入っていない場合、今見ている端末に適したモードでブログが表示されるという便利な仕組みがあるんですが、自前で用意したリンクには自動的に m=数値 を付けてくれないので、リンクをクリックするとPC表示などの設定が切れてしまうのです。

ということで、m=0 のPC表示でブログを見ているときと、m=1 でモバイル表示でブログを見ているとき、それと m=数値 がなく端末に適した表示でブログを見ているときのどれでもその表示のままリンク先に飛べるように、という配慮をして URL の生成を行っています。

ここに工夫! フィードガジェットの上書き範囲と、ボタンの配置場所

フィードガジェットに javascript で一覧はこちらボタンを追加した時、最初は一瞬しかボタンが追加されていませんでした。

理由は簡単で、その時はボタンを widget-content クラスの div 要素の中に入れていたのですが、 フィードガジェットが RSS や Atom のデータを受け取ると、 widget-content クラスの要素の中身が全て削除されて新しい要素に置き換えられるためです。

ですので、 widget-content の中にボタンを追加しても、フィードを取得できたタイミングで削除されてしまいます。しかし、widget-content の枠の中にボタンを配置したかったので、頭をひねる必要が出てきました。

結局、javascript 側でボタンを追加するフィードガジェットにだけ特別なクラスを付け、css でそのガジェットの widget-content から下側のボーダーを削除し、その下に上側のボーダーがない新しい要素をくっつけるという方法で、二つの要素を一つに見せる荒業を使っています。

作って使ってみた感想

フィードガジェットを使うバージョンでは、自分でフィードを取得して表示しようとしなくても勝手にやってくれるので、その点で言えば非常に楽でした。ただ、サムネイル画像があったら表示とか、そういう機能は存在しないのであくまでタイトルを表示するだけになりそうです。

前々回に考えていた他の方法との違いといえば、ラベルを使わない、すべての記事のフィードでも一覧はこちらボタンを追加できることですね。コメントのフィードは一覧がそもそもないのでリンクを用意することはできませんでしたが。

……でも、ガジェットを追加するたびにガジェットのテンプレートを書き換える必要があるので、ブログ製作者的には使いやすいんでしょうか。ちょっと悩みどころです。

実は、WidgetManager とかいう Blogger が用意したオブジェクトを発見しました。そこから遡るとフィード URL とかもデータタグを介さないで取得できるようになると思いますので、もしかしたらテンプレート利用者は特に意識することなく一覧ボタンを追加できるようになるかもしれません。

これも要調査ですね。

あと思ったのは、自動でやらなくても良ければフィードガジェットの下にHTMLガジェットかリンクリストガジェットを付けてリンクボタンにすれば、ここまで多機能ではないにしても似たようなことができるんですよね。

そんなことを考えていると今回頑張った甲斐がなくなってしまうので、作ってよかったんだと心に言い聞かせることにします。

よく頑張った。自分をほめてやりたいです。よーし頭を撫でてやろう。えへへ。

今後の予定

そろそろ、 Blogger テンプレートの制作日誌以外も書いてみたくなってきた今日この頃。まったりとテンプレートの手直しをして制作日誌を投稿しつつ、特に大きな変更も思いつかなければ、近いうちにベータ版としてテンプレートを公開してみます!

0 件のコメント :

コメントを投稿