~ モバイル版(スマートフォン)の操作 ~
左右にスワイプすると、前後の投稿へ移動します。
← 前の投稿 | 次の投稿 →
日々のコンピュータ情報の集積と整理

Dr.ウーパのコンピュータ備忘録

2014年5月14日水曜日

Blogger:「前の投稿」「次の投稿」に記事のタイトルを付与する! - JavaScript: ブログ アーカイブから取得編(前・次の投稿が月を跨ぐ場合にも対応)


スポンサーリンク

イントロダクション

記事「Blogger:「前の投稿」「次の投稿」に記事のタイトルを付与する! - JavaScript: ブログ アーカイブから取得編(前・次の投稿が月を跨ぐ場合は非対応)」では、ブログアーカイブより、前の投稿・次の投稿のタイトルを取得して、前の投稿・次の投稿のナビゲータにタイトルを設定しました。

この時は、現在の投稿の投稿月が、前の投稿・次の投稿と同じ投稿月である必要があり、異なる月の場合には投稿タイトルを取得できませんでした。


そこで、今回は前回のコードを改良し、現在の投稿の投稿月が、前の投稿・次の投稿と異なる月の場合にも投稿タイトルを取得できるようにしました。


2014/5/15 追記:

ハッシュが含まれた URL の場合に、記事タイトルを付与できない問題へ対処しました。

処理方式

現在の投稿の投稿月が、前の投稿・次の投稿と異なる月の場合には、疑似的にブログアーカイブの該当する月をクリックして、投稿タイトル一覧を読み込ませて、投稿タイトルが取得できるようにします。

ソースコード

以下のソースコードを<body>タグ内の最後(</body>タグの一つ上の行)に配置します。

<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <!-- 投稿ページにのみ 前の投稿・次の投稿のタイトル挿入 を適用する -->
  <!-- 前の投稿・次の投稿のタイトル挿入 START -->
  <script type='text/javascript'>
    //<![CDATA[
    <!--
// 遅延して前の投稿・次の投稿のタイトルを取得し設定する ---
setTimeout(function () {

    // --- Util ---

    /*
    タグ名から子要素のみを選択して取得する
    */
    function getChildElementsByTagName(obj, tagName) {
        var items = new Array();

        for (var i = 0; i < obj.childNodes.length; i++) {
            if (obj.childNodes[i].tagName !== void 0)          // undefined でなければ処理
            {
                if (obj.childNodes[i].tagName.toLowerCase() == tagName.toLowerCase()) {
                    items.push(obj.childNodes[i]);
                }
            }
        }

        return items;
    }

    /*
    クラス名から子要素のみを選択して取得する
    */
    function getChildElementsByClassName(obj, className) {
        var items = new Array();

        for (var i = 0; i < obj.childNodes.length; i++) {
            if (obj.childNodes[i].className !== void 0)          // undefined でなければ処理
            {
                if (obj.childNodes[i].className.toLowerCase() == className.toLowerCase()) {
                    items.push(obj.childNodes[i]);
                }
            }
        }

        return items;
    }


    /* 
    clickをエミュレート
    参考:http://d.hatena.ne.jp/language_and_engineering/20090907/p1
    */
    function Click(elem) {
        if (/*@cc_on!@*/false) {
            // IEの場合
            elem.fireEvent("onclick");
        }
        else {
            // Firefoxの場合
            var evt = document.createEvent("MouseEvents"); // マウスイベントを作成
            evt.initEvent("click", false, true); // イベントの詳細を設定
            elem.dispatchEvent(evt); // イベントを強制的に発生させる
        }
    }

    // --- Main ---

    /*
    ブログアーカイブから前の投稿・次の投稿のタイトルを取得し、
    前の投稿・次の投稿ナビゲータに設定する
    */
    function setPrevNextTitle() {

        var obj_archive = document.getElementById("BlogArchive1_ArchiveList");                  // ブログアーカイブ取得
        var obj_years = getChildElementsByTagName(obj_archive, "ul");                           // 年毎のアーカイブ

        for (var y = 0; y < obj_years.length; y++) {
            var obj_year_childs = getChildElementsByTagName(obj_years[y], "li");

            var obj_months = getChildElementsByTagName(obj_year_childs[0], "ul");               // 月ごとのアーカイブ
            for (var m = 0; m < obj_months.length; m++) {
                var obj_month_childs = getChildElementsByTagName(obj_months[m], "li");

                var obj_posts_tops = getChildElementsByTagName(obj_month_childs[0], "ul");

                // 投稿データが読み込まれていなかったらスキップする
                if (obj_posts_tops.length == 0) {
                    continue;
                }

                var obj_posts = getChildElementsByTagName(obj_posts_tops[0], "li");             // 月ごとの投稿
                for (var p = 0; p < obj_posts.length; p++) {

                    // 要素の a タグから href を取得し、現在のページの要素かどうかチェック
                    var obj_a = obj_posts[p].getElementsByTagName("a");
                    var now_href = obj_a[0].getAttribute("href");

                    // 現在のページの要素を発見
                    if (now_href == location.href.replace(/#.*$/, "")) {

                        // 次の投稿が同じ月だったら
                        if (p > 0) {
                            setNextTitleFromArchiveElement(obj_posts[p - 1]);
                        } else {

                            // 次の月があったら
                            if (m > 0) {

                                setTitle_FromMonth(obj_months[m - 1], getNextURL(), setNextTitleFromArchiveElement);

                            } else {

                                // 次の年があったら
                                if (y > 0) {

                                    setNextTitle_diffYear(obj_years[y - 1], getNextURL());

                                } else {
                                    // 次のページなし
                                }
                            }
                        }

                        // 前の投稿が同じ月だったら
                        if (p < obj_posts.length - 1) {
                            setPrevTitleFromArchiveElement(obj_posts[p + 1]);
                        } else {

                            // 前の月があったら
                            if (m < obj_months.length - 1) {

                                setTitle_FromMonth(obj_months[m + 1], getPrevURL(), setPrevTitleFromArchiveElement);

                            } else {

                                // 前の年があったら
                                if (y < obj_years.length - 1) {

                                    setPrevTitle_diffYear(obj_years[y + 1], getPrevURL());

                                } else {
                                    // 前のページなし
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /*
    異なる年・前の月のブログアーカイブから投稿タイトルを取得し、ナビゲータに設定する
    */
    function setPrevTitle_diffYear(obj_year, url) {

        var obj_year_childs = getChildElementsByTagName(obj_year, "li");
        var obj_months = getChildElementsByTagName(obj_year_childs[0], "ul");               // 月ごとのアーカイブ
        setTitle_FromMonth(obj_months[0], url, setPrevTitleFromArchiveElement);

    }

    /*
    異なる年・次の月のブログアーカイブから投稿タイトルを取得し、ナビゲータに設定する
    */
    function setNextTitle_diffYear(obj_year, url) {

        var obj_year_childs = getChildElementsByTagName(obj_year, "li");
        var obj_months = getChildElementsByTagName(obj_year_childs[0], "ul");               // 月ごとのアーカイブ
        setTitle_FromMonth(obj_months[obj_months.length - 1], url, setNextTitleFromArchiveElement);

    }


    /*
    特定の月のブログアーカイブから投稿タイトルを取得し、ナビゲータに設定する
    */
    function setTitle_FromMonth(obj_month, url, callBack) {

        var obj_month_childs = getChildElementsByTagName(obj_month, "li");
        var obj_toggles = getChildElementsByClassName(obj_month_childs[0], "toggle");        // 投稿を読み込むための要素取得

        // クリックイベントを発生させ、投稿データの読み込みを行う
        Click(obj_toggles[0]);


        // 投稿一覧の読み込みが終わった後に投稿タイトルの取得を実行
        setTimeout(delayCheck, 1000);


        function delayCheck() {
            var obj_posts_tops = getChildElementsByTagName(obj_month_childs[0], "ul");

            // 投稿データが読み込まれていなかったら再チェックする
            if (obj_posts_tops.length == 0) {
                setTimeout(delayCheck, 1000);
                return;
            }

            var obj_posts = getChildElementsByTagName(obj_posts_tops[0], "li");             // 月ごとの投稿
            for (var p = 0; p < obj_posts.length; p++) {

                // 要素の a タグから href を取得し、現在のページの要素かどうかチェック
                var obj_a = obj_posts[p].getElementsByTagName("a");
                var now_href = obj_a[0].getAttribute("href");

                // 現在のページの要素を発見
                if (now_href == url) {

                    callBack(obj_posts[p]);
                }
            }
        }
    }


    /*
    次の投稿のタイトルをブログアーカイブの要素より設定する
    */
    function setNextTitleFromArchiveElement(obj_archive_element) {

        setTitleFromArchiveElement(obj_archive_element, "Blog1_blog-pager-newer-link", "next-title");
    }

    /*
    前の投稿のタイトルをブログアーカイブの要素より設定する
    */
    function setPrevTitleFromArchiveElement(obj_archive_element) {

        setTitleFromArchiveElement(obj_archive_element, "Blog1_blog-pager-older-link", "prev-title");
    }

    /*
    投稿のタイトルをブログアーカイブの要素より設定する
    */
    function setTitleFromArchiveElement(obj_archive_element, id_navi, id_title) {

        // 投稿のナビゲータ取得
        var obj = document.getElementById(id_navi);
        if (!obj) return;


        var obj_archive_a = obj_archive_element.getElementsByTagName("a");

        // 投稿の URL と、ブログアーカイブから取得した URL が異なっていたら対象外
        if (obj.getAttribute("href") != obj_archive_a[0].getAttribute("href")) {
            return;
        }

        obj.innerHTML = obj.innerHTML + "<br /><div id=\"" + id_title + "\">" + obj_archive_a[0].innerHTML + "</div>";
    }

    /*
    前の投稿の URL を取得
    */
    function getPrevURL() {

        return getHref("Blog1_blog-pager-older-link");
    }

    /*
    次の投稿の URL を取得
    */
    function getNextURL() {

        return getHref("Blog1_blog-pager-newer-link");
    }

    /*
    要素の href を取得
    */
    function getHref(id) {

        var obj = document.getElementById(id);
        if (!obj) return undefined;

        return obj.getAttribute("href");
    }

    // --- 実行 ---
    setPrevNextTitle();

}, 1000);
    //-->
    //]]>
  </script>
  <!-- 前の投稿・次の投稿のタイトル挿入 END -->
</b:if>

最適化したソースコード

上記ソースコードを最適化しました。

上記ソースコードを変更せず、そのまま使用する場合にはこのコードを使用することで、効率的に処理を実行できます。

<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <!-- 投稿ページにのみ 前の投稿・次の投稿のタイトル挿入 を適用する -->
  <!-- 前の投稿・次の投稿のタイトル挿入 START -->
  <script type='text/javascript'>
    //<![CDATA[
    <!--
setTimeout(function(){function e(a,b){for(var d=[],c=0;c<a.childNodes.length;c++)void 0!==a.childNodes[c].tagName&&a.childNodes[c].tagName.toLowerCase()==b.toLowerCase()&&d.push(a.childNodes[c]);return d}function t(a,b){for(var d=[],c=0;c<a.childNodes.length;c++)void 0!==a.childNodes[c].className&&a.childNodes[c].className.toLowerCase()==b.toLowerCase()&&d.push(a.childNodes[c]);return d}function u(a){if(/*@cc_on!@*/false){a.fireEvent("onclick");}else{var b=document.createEvent("MouseEvents");b.initEvent("click",!1,!0);a.dispatchEvent(b)}}function l(a,
b,d){function c(){var a=e(f[0],"ul");if(0==a.length)setTimeout(c,1E3);else for(var a=e(a[0],"li"),g=0;g<a.length;g++)a[g].getElementsByTagName("a")[0].getAttribute("href")==b&&d(a[g])}var f=e(a,"li");a=t(f[0],"toggle");u(a[0]);setTimeout(c,1E3)}function m(a){p(a,"Blog1_blog-pager-newer-link","next-title")}function n(a){p(a,"Blog1_blog-pager-older-link","prev-title")}function p(a,b,d){if(b=document.getElementById(b))a=a.getElementsByTagName("a"),b.getAttribute("href")==a[0].getAttribute("href")&&(b.innerHTML=
b.innerHTML+'<br /><div id="'+d+'">'+a[0].innerHTML+"</div>")}function q(){return r("Blog1_blog-pager-older-link")}function s(){return r("Blog1_blog-pager-newer-link")}function r(a){return(a=document.getElementById(a))?a.getAttribute("href"):void 0}(function(){for(var a=document.getElementById("BlogArchive1_ArchiveList"),a=e(a,"ul"),b=0;b<a.length;b++)for(var d=e(a[b],"li"),d=e(d[0],"ul"),c=0;c<d.length;c++){var f=e(d[c],"li"),f=e(f[0],"ul");if(0!=f.length)for(var f=e(f[0],"li"),k=0;k<f.length;k++)if(f[k].getElementsByTagName("a")[0].getAttribute("href")==
location.href.replace(/#.*$/, "")){if(0<k)m(f[k-1]);else if(0<c)l(d[c-1],s(),m);else if(0<b){var g=s(),h=e(a[b-1],"li"),h=e(h[0],"ul");l(h[h.length-1],g,m)}k<f.length-1?n(f[k+1]):c<d.length-1?l(d[c+1],q(),n):b<a.length-1&&(g=q(),h=e(a[b+1],"li"),h=e(h[0],"ul"),l(h[0],g,n))}}})()},1E3);
    //-->
    //]]>
  </script>
  <!-- 前の投稿・次の投稿のタイトル挿入 END -->
</b:if>


結果

以下のように、現在のページの投稿月と、前の投稿・次の投稿の投稿月が異なる場合でも、前の投稿・次の投稿に対して投稿タイトルを設定することが出来ました。

現在のページの投稿月と、前の投稿・次の投稿の投稿月が異なる場合でも、
前の投稿・次の投稿に対して投稿タイトルを設定できた。

まとめ

Bloggerの「前の投稿」「次の投稿」の投稿タイトルを、JavaScriptにてブログ アーカイブから取得することで、付与することが出来ました。

前の投稿・次の投稿が、現在の投稿とは異なる月に投稿された場合にも対応することが出来ました。


これで、Bloggerの「前の投稿」「次の投稿」へ投稿タイトルを設定する機能は完成です。


スポンサーリンク

コメントを投稿

コメント投稿機能について