2つのフィードを結合してトップページに読み込む

投稿日:

弊社サイトでは、この「Notebook」とそれ以外のページで2つのフィード(RSS2.0とATOM1.0)を出力しています。

この2つのフィードをトップページの「Information」に表示するようにjQueryでスクリプトを作成しました。作成中にいくつかつまずいたところがあったので、手順を追って説明します。

 

ローカルで開発/検証する場合の問題点

AJAXでフィードなどのデータを取得する場合、下記のような問題があります。

IEの問題

WEBサーバーが送信するmime-typeが「text/xml」か「application/xml」以外の場合、XMLファイルを認識できずに「parsererror」になる。
Windows RSS Publisher’s Guide (work-in-progress) – Microsoft RSS Blog – Site Home – MSDN Blogs
このため、サーバーが返す「mime-type」が「application/rss+xml」などであった場合エラーになる。また、「mime-type」を返さないローカルファイル上のデータ(アドレスが「c:\\」など)でもエラーになる。ただし、jQueryの「jQuery.ajax()」では、ローカルファイルへのアクセスの問題は解決されている。

Firefoxなど(IE以外のモダンブラウザの場合)の問題

ローカルファイル上のHTMLファイル(アドレスが「c:\\」など)から、WEB上のファイルにアクセスできない。

ローカルWEBサーバーの準備

上記の問題があるため、フィードを取得するスクリプトを作成する際には、実際に公開する環境にあわせて開発したほうが効率が良くなります。そのため、ローカルWEBサーバーを準備します。

ローカルWEBサーバーについてはこちらをご覧ください。
xampp

ローカルWEBサーバー上にフィードを保存

ローカルWEBサーバーにあるファイルから、公開しているWEBサーバー上のXMLデータを取得することはできません(クロスドメインアクセス)。そのため検証する際には、ローカルWEBサーバー上にフィードを保存して検証します。

jQueryの準備

jQueryをダウンロードしてルートフォルダの[js]フォルダに保存します。

jQuery

[js]フォルダに「jquery.comb-atom.js」という名前のファイルを新規作成します。

index.htm(読み込むファイル)のhead要素内に以下の文を追加します。

<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jquery.comb-atom.js"></script>

ローカルWEBサーバー上のファイルは以下のようになります。

htdocs
├─[js]
│    ├jquery-min.js
│    └jquery.comb-atom.js
├─cms.atom
├─feed.atom
└─index.htm

ここからは新規作成した「jquery.comb-atom.js」を編集し、トップページでフィードを読み込むようにしていきます。

フィードの読み込み

jQueryの「jQuery.ajax」関数を使用してフィードを読み込み、フィードが読み込めていることを確認します。

jQuery.ajax(options) – jQuery 日本語リファレンス

「jquery.comb-atom.js」に以下の文を入力します。

$(function() {
    var url = ["feed.atom","cms.atom"];//フィードのファイル名
    $.each(url,function(){
        $.ajax({
            url: this,
            async: true,
            cache: false,
            success: function(data){
                alert($(data).text());//確認用 フィードのテキストデータをalertダイアログで表示する
            }
        });
    });
});

Firefoxでは問題なく実行されalertダイアログが表示されるのですが、IEで実行するとエラーもなくalertダイアログも表示されません。公開中のWEBサーバー(www.keyton-co.jp)でも同様のエラーが発生しました。

「jQuery.ajax」のオプションでerrorを表示してみると「parseerror」(パースエラー)が出ていました。このエラーを検索してみると、jQueryの公式サイトに記述がありました。

Specifying the Data Type for AJAX Requests

IEで「jQuery.ajax」を使用したときに、サーバーが正しいmime-typeを返さない場合やローカル上のファイルなどを読み込んだ場合にプレーンテキストとして受信してしまうとのこと。

上で書いた「準備 - IEの問題」がここにも影響しているようです。

ローカルWEBサーバーでは「.htaccess」に「AddType text/xml atom」と記述すれば「mime-type text/xml」を返すようになるのですが、公開中のWEBサーバーは「.htaccess」では設定が反映されませんでした。そのため、Javascript上でテキストデータをXMLとして格納できるように変更しました。

[修正後]

$(function() {
    $.each(url,function(){
        $.ajax({
            url: this,
            async: true,
            cache: false,
            dataType: ($.browser.msie) ? "text" : "xml",
            success: function(data){
                var xml;
                if (typeof data == "string") {
                    xml = new ActiveXObject("Microsoft.XMLDOM");
                    xml.async = true;
                    xml.loadXML(data);
                } else {
                    xml = data;
                }
                alert($(xml).text());//alertを表示 ←①
            }
        });
    });
});

フィードのデータを配列に格納

配列feedにxmlから読み込んだデータを格納する式を①の行に入力します。

var feed = [];
$('entry',xml).each(function(){
    feed.push({
        t: $('title',this).text(),//タイトル
        l: $('link',this).attr('href'),//リンク
        d: dateParse($('published',this).text())//日時(タイムスタンプ)←②
    });
});

dateParse」(②の行)は、日時(yyyy-mm-ddThh:mm:ss+09:00)からタイムスタンプを返します。

function dateParse(str){// str==yyyy-mm-ddThh:mm:ss+09:00
    var strdate = str.split('\+')[0].replace('T',' ').replace('-','\/').replace('-','\/');//strdate==YYYY/mm/dd hh:mm:ss
    return Date.parse(strdate);
}

フィードの並び替え

配列feedをjavascriptのsortメソッドでタイムスタンプの値の大きい順に並び替えます。sortメソッドでは、引数にソート順を定義する関数を指定できます。関数を「a.d < b.d」の様にすると文字列として比較されるので、数値として比較されるように数値の計算式にします。

feed.sort(function(a,b){return b.d - a.d});//タイムスタンプの降順で並び替える

フィードデータをhtmlに変換

並び替え後のフィードデータをhtml形式に変換します。

var source = buildNews(feed);
var max_info = 4;//最大表示件数
function buildNews(news){
    news = news.slice(0,max_info);//max_info件数に絞り込む
    var newsStr = '';//戻り値
    $.each(news,function(){//newsの件数分繰り返す
        if(this.l.length){//リンクがある時
            newsStr += '<dt>'+expDate(this.d)+'</dt><dd><a href="'+this.l+'">'+this.t+'</a></dd>';
        }else{//リンクがない時
            newsStr += '<dt>'+expDate(this.d)+'</dt><dd>'+this.t+'</dd>';
        }
    });
    return newsStr;
}

上記「expDate」はタイムスタンプから日付(YYYY.M.D)を返します。

function expDate(tStamp){
    var pDate = new Date(tStamp);
    var pYear = pDate.getFullYear();
    var pMonth = pDate.getMonth()+1;// getMonthは0から11の値が返ってくる
    var pDay = pDate.getDate();
    return pYear+'.'+pMonth+'.'+pDay;
}

フィードを「Information」に挿入

「class=info」の要素の下のDL要素の中を、上記のhtml形式にしたフィードデータと置き換えます。

$('.info dl').html(source);

最終的に「jquery.comb-atom.js」は以下の通りになりました。

$(function(){
	var url = ["feed.atom","cms.atom"];//RSSファイル名
	var feed=[];
	var max_info = 4;//最大表示件数
	$.each(url,function(){
		$.ajax({
			url: this,
			async: true,
			cache: false,
			dataType: ($.browser.msie) ? "text" : "xml",
			success: function(data){
				var xml;
				if (typeof data == "string") {
					xml = new ActiveXObject("Microsoft.XMLDOM");
					xml.async = true;
					xml.loadXML(data);
				} else {
					xml = data;
				}
				if(xml.length==0) return;
				$('entry',xml).each(function(){
					feed.push({t: $('title',this).text(),l: $('link',this).attr('href'),d: dateParse($('published',this).text())});
				});
				feed.sort(function(a,b){return b.d-a.d});
				$('.info dl').html(buildNews(feed));
			}
		});
	});
	function dateParse(str){
		var strdate = str.split('\+')[0].replace('T',' ').replace('-','\/').replace('-','\/');
		return Date.parse(strdate);
	}
	function buildNews(news){
		news = news.slice(0,max_info);
		var newsStr = '';
		$.each(news,function(){
			if(this.l.length){
				newsStr += expDate(this.d)+'<dd><a href="'+this.l+'">'+this.t+'</a></dd>';
			}else{
				newsStr += expDate(this.d)+'<dd>'+this.t+'</dd>';
			}
		});
		return newsStr;
	}
	function expDate(tStamp){
		var pDate = new Date(tStamp);
		var pYear = pDate.getFullYear();
		var pMonth = pDate.getMonth()+1;
		var pDay = pDate.getDate();
		return '<dt>'+pYear+'.'+pMonth+'.'+pDay+'</dt>';
	}
});