矢印付きボタンを作成するCSSジェネレーター「Button with Arrow.css」を公開しました。

Button with Arrow.css公開させて頂きました。
矢印付きのボタンのCSSを生成するジェネレーターです。

矢印付きのボタンをコーディングするのが面倒だったのでsassでmixinを作ったものの、それを使うことすら面倒だった(笑)のと、Vue.jsの勉強がしたかったのがこのツールを作ったきっかけです。

※現状、スマートフォンでの閲覧に対応しておりません。PCのブラウザでご利用ください。

使い方

EDITボタンをクリックするとスクリーンが編集モードに変形します。

編集エリアのフォームに任意の値を入力すると、画面右側のcodeエリアのCSSコードに動的に反映されます。

※codeエリアが閉じている場合は「get code」ボタンをクリックしてください。

入力が終わったら、codeエリアのcopyボタンをクリックしてコードをコピーし利用してください。

htmlとcssそれぞれ別になっています。

Vue.jsで作りました。

ここはアピールしておきたいところ(笑)。フレームワークとしてVue.jsを利用しています。

フォームに値を入力すると、codeエリアとボタンのプレビューに同期するようバインディングするコードを書いています。

cssで擬似要素を使っているので、これをVue.jsで扱うのに苦労しました。DOMではないので通常のバインディングができず、テキストノードとしてstyleタグを生成するなどしています。(何か他にいいやり方があるのかもしれませんね。)

ご質問やご意見はこの投稿にコメントいただくか、@stella_d_tweetにメンションなどお願いいたします。

最後に

非常に限られた需要しか無いと思われますが(汗)、よかったらお試しいただけると幸いです。

このツールを作るのに、@hrz31さんの以下の記事とサービスから非常に刺激を受けました。

自分のサービスを作ってみたいエンジニアな人は参考になると思います。おすすめです。

他にもtwitterでフォローしているフォロワーの方が続々とwebサービスをリリースをしており、こちらも大変気になっております。

話題のねこ本ももちろん買って読みました。ある程度JavaScriptの知識があることが前提ではありますが、Vue.jsの使い方のツボがいいかんじにまとめられていて、こういうときはどうすればいい?の答えにたどり着くスピードが格段に上がりました。みんな、読むといいよ!

さてこれから

次に作ってみたいwebサービスの構想がうすボンヤリ浮かんでいます。今度はより本格的なwebアプリを作ってみたいです。

オープンデータも活用したいし、firebaseを使えばよりスケールアップしたアプリが作れそう。micro:bitもまだ手付かずだし、blenderもやってみたい。(あぁ、もう全部やるのは無理だw)

時間の割り振りがなかなか難しいですがほそぼそやっていこうと思っています。

gulpでテンプレートエンジン「EJS」を使って静的なwebページ生成を試してみました。

近年の制作案件の多くはWordPressを利用していたのですが、久しぶりに静的なHTMLでサイト構築する仕事に携わりました。

写真ギャラリーのwebページを40以上。WordPressならばカスタムフィールドを使うところですが、静的サイトだとスタティックサイトジェネレーターやテンプレートエンジンを利用することになります。

知ってはいたものの、使ったことが無かったので調べて試してみました。納期間近の引き継ぎ案件だったこともあり実戦投入には間に合いませんでしたが、今後使用する可能性が非常に高いので記録して紹介します。

やりたかったこと

  • ヘッダーやフッターなどをインクルード。
  • 同一のテンプレートからページを大量生成。
  • gulpのプラグインとして動作させる。
  • なるべく学習コストを低く。

上記要件に合うものとして、テンプレートエンジン「EJS」がよさそうでした。有名なjekyllMiddlemanは高性能そう且つ実績がありますが、学習/導入コストが高そう。

ひとまず上記に挙げた機能があり、できればgulpで動作するものがよかったので、サンプルコードを見たかんじ簡単そうなEJSを選びました。

gulp-ejsをインストール

npmでプラグインが配布されています。

gulp-ejs

インストールは以下のコマンド。ページ生成のためにgulp-renameというプラグインも併用します。

npm install gulp-ejs
npm install gulp-rename

JSON形式でページデータを制作する

htmlを生成する際のページの内容は、JSONで記述してテンプレートに渡します。

data.json

{
  "pages": [
    {
      "id": "page1",
      "title": "ページ1",
      "photos": [
        {"photo":"1.jpg"},
        {"photo":"2.jpg"},
        {"photo":"3.jpg"}
      ]
    },
    {
      "id": "page2",
      "title": "ページ2",
      "photos": [
        {"photo":"4.jpg"},
        {"photo":"5.jpg"},
        {"photo":"6.jpg"}
      ]
    },
    {
      "id": "page3",
      "title": "ページ3",
      "photos": [
        {"photo":"7.jpg"},
        {"photo":"8.jpg"},
        {"photo":"9.jpg"}
      ]
    }
  ]
}

gulpfile.jsにタスクを記述

gulpfile.js

var fs           = require('fs');//JSONファイルをパースするのにnode.jsのfsモジュールが必要
var gulp         = require('gulp');
var ejs          = require("gulp-ejs");
var rename       = require('gulp-rename');


//ejsのサンプル jsonからファイル生成
gulp.task('ejs', function() {

	var jsonFile = '_src/_data/pages.json',
		tempFile = '_src/_ejs/_template.ejs',
		json     = JSON.parse(fs.readFileSync(jsonFile, 'utf8')),
		pages    = json.pages,
		id;
  
	for (var i = 0; i < pages.length; i++) {
	id = pages[i].id;
	gulp.src(tempFile)
		.pipe(ejs({
			jsonData: pages[i]
		}))
		.pipe(rename(id + '.html'))
		.pipe(gulp.dest('dist'));
	}

});

JSONをパース→テンプレートに渡してhtmlを生成→ファイル名をリネーム
という流れをfor文で回してページ生成します。

テンプレートを作成

_header.ejs

<% var data = jsonData; %><!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="UTF-8">
	<title><%= data.title %> | EJS DEMO PAGE</title>
</head>
<body>

<header class="header">
	<h1><%= data.title %></h1>
</header>

一行目の<% var data = jsonData; %>で、gulpfile.jsの処理でパースしたJSONデータのオブジェクトからデータを取得できますので、<%= data.title %>のようにするとタイトルが表示できます。

_template.ejs

<% include _header %>
    <% var data = jsonData; %>
    <div class="main">
        <% data.photos.forEach(function(p){ %>
	    <div><img src="images/<%= p.photo %> alt="" /></div>
        <% }) %>
    </div>
<% include _footer %>

<% include _header %><% include _footer %>でヘッダー、フッターをインクルードしています。

そしてforEachメソッドでページデータを取り出しています。最初はfor文を使ったのですが、条件式に.lengthを使うとdefinedエラーが出て実行できませんでした。forEachを使うのが良いようです。

※_footer.ejsは割愛します。

テンプレートを作成したら、タスクを実行

以下のコマンドでタスクを実行すると、JSONで設定したidをファイル名にしたhtmlが生成されます。今回はやってませんが、watchで監視して動的に生成することも可能です。

gulp ejs

まとめ

ひとまず簡単にEJSを利用する方法をまとめてみました。まだまだ使ってない機能があると思いますが、今回試したコードだけでも実案件に貢献できそうです。

また、

<h1><%= data.title %></h1>

のような記述はなんとなくPHPと似ていて親しみやすさを感じました。反面、rubyやpythonを使う人からすると可読性がよくないと感じる方もいらっしゃるようで、その場合はインデントの記法が使えるjade(今はpugになったらしい?)が良さげ。

ページネーションやアーカイブ、RSSなどを備えたブログを生成するとなるとjelyllとプラグインを組み合わせて使う方法があるようです。

StaticGenを見てみると本当にたくさんのスタティックサイトジェネレーターがあります。実行環境や使える機能が様々異なるようで奥が深いですね。

さて、ここまで紹介してきたサンプルコードはgithubにてひとまとめに公開しています。

kouichi-hoshi/project_ejs_sample

間違いのご指摘やご意見ございましたらコメント欄等でいただけると幸いです。よろしくお願いたします!

参考サイト

この記事を執筆するにあたって以下の記事を参考にさせていただきました。ありがとうございました!

自社サイト用の静的サイトジェネレータ選定と導入の記録 - Casual Startup - MBA/プログラマの起業日記
Gulp+EJS+JSONからHTMLファイルを生成する - kinalog
テンプレートエンジン「EJS」とタスクランナー「Gulp.js」で爆速HTMLコーディング | 株式会社LIG
テンプレートエンジンEJSで使える便利な構文まとめ - Qiita
Node.jsで静的にサイトを生成する時の現時点でのおすすめ - Qiita

[Sass]for文とif文を使って見出し要素のCSSを一括で設定する。

見出し要素は出現頻度が高く、無計画にcssを記述していると乱雑になりやすいのでひとまとめに管理する記述する方法を考えてみました。タイトル通りSassのfor文とif文を使います。

Sass


body{

	$reduce : 0.2; //フォントサイズを0.2ずつ源算するための値
	$fontSize : 3 + $reduce; //フォントサイズの初期値

	//$valueに1ずつ加算して代入し、6になるまで繰り返す  
	@for $value from 1 through 6 {

		h#{$value}{ //#{}を使用して見出しセレクタを作成

			//フォントサイズを再設定
			$fontSize: $fontSize - $reduce; //0.2ずつ源算
			font-size: $fontSize + rem;

			//h2要素のcssを設定
			@if $value == 2 {
				color: red;
			}

			//h3要素のcssを設定
			@if $value == 3 {
				color: blue;
			}

			//class付きh4要素にcssを設定
			@if $value == 4 {
				&.h4-custom {
					border-bottom: 1px solid #CCCCCC;
				}
			}

		}

	}

}

@forでh1からh6までのフォントサイズを調整し、@ifで条件分岐して見出しごとのスタイルを設定しています。より複雑なスタイルを施したい場合は別途@mixinを作って@includeすればさらに管理しやすくなりますね。

[JavaScript]ES2015のclass構文のお作法まとめ。

脱初心者、目指せ中級者!
このところJavaScriptのclass構文を集中して学習しています。そこから得た知識のメモです。

言葉の定義を整理する

  • クラス
  • インスタンス
  • コンストラクタ
  • プロパティ(メンバ変数)
  • メソッド(メンバ関数)
  • 継承

まずは簡単なclassの構文サンプルをご紹介。

class name{
	constructor(myoji,namae){
		this.myoji = myoji;
		this.namae = namae;
	}
	getfullName(){
		return this.myoji + this.namae;
	}
}

var N = new name('やまだ','たろう');
console.log(N.getfullName());

クラス

class name{
    // コンストラクタで初期化
    // メソッドを記述
}

まずはclassキーワードを使ってclassを宣言します。(変数に代入して定義する方法もあるようです。)

後述するコンストラクタやメソッドを定義し、newキーワードでインスタンス化して使用します。メソッドを実行するにはインスタンス化した後に呼び出す必要があります。

コンストラクタ

コンストラクタ は、new式でクラスから定義されるオブジェクトの生成時に(クラスをインスタンス化するときに)、自動的に呼び出されるメソッドです。プロパティ(メンバ変数)の初期化を行います。

class name{
    //コンストラクタを定義
    constructor(myoji,namae){
        this.myoji = myoji; //プロパティを初期化
        this.namae = namae; //プロパティを初期化
    }
}

メソッド

class宣言直下に記述した関数のことをメソッドと呼びます。通常の関数とは振る舞いが異なります。

メソッドでプロパティを使うには、this.をつけて呼び出します。

JavaScriptのclassでは、class宣言の直下ではコンストラクタとメソッドしか定義できません。通常の関数のようなfunction宣言は不要です。(というか、構文エラー)

class name{
    constructor(myoji,namae){
        this.myoji = myoji; //プロパティを初期化
        this.namae = namae; //プロパティを初期化
    }
    //メソッドを定義
    getfullName(){
        return this.myoji + this.namae;
    }
}

インスタンス

クラスからインスタンスを生成します。メソッドも呼び出します。

class name{
    constructor(myoji,namae){
        this.myoji = myoji; //プロパティを初期化
        this.namae = namae; //プロパティを初期化
    }
    //メソッドを定義
    getfullName(){
        return this.myoji + this.namae;
    }
}

var N = new name('やまだ','たろう'); //インスタンスを生成
console.log(N.getfullName()); //メソッドを実行 -> やまだたろう

継承

あらかじめ作成した別のクラスを継承して新しいクラスを作ることもできます。構文としてはextendsキーワードを使います。

継承元からプロパティやメソッドを呼び出すにはsuper.キーワードを使います。

継承元にあるメソッドと同じ名前でメソッドを作るとオーバーライドすることができます。

class person extends name{
	constructor(myoji,namae,age,blood,address){
		super(myoji,namae); //継承元のコンストラクタを呼び出す
		this.age = age;
		this.blood = blood;
		this.address = address;
	}

	createPerson(){
		let fullName = super.getfullName(); //継承元のメソッドを呼び出す
		return fullName + 'の血液型は' + this.blood + '型で、年齢は' + this.age + '歳' + 'です。' + this.address + 'に住んでいます。';
	}
}

var p = new person('さとう','たろう','1','A','北海道');
console.log(p.createPerson()); //さとうたろうの血液型はA型で、年齢は1歳です。北海道に住んでいます。

classに関する機能や構文、仕様はまだまだたくさんあるようですが、ひとまず自分が現状で分かった範囲はこのような感じです。

もっともっと学習と実践を重ねてよりよいコードが書けるようがんばります!

[WordPress]タクソノミーのタームが増えたらリンクが動的に新しいタームへ切り替わるナビゲーションの作り方。

2016年、2017年、2018年というような「年度」をカスタムタクソノミータームとした、毎年タームが増えていくWordPressサイトを作った時のメモです。

グローバルメニューの中に「最新年度のカスタムタクソノミーアーカイブページヘのリンク」が必要で、年度が変わってタームが増えたら自動的に最新年度ページへのリンクを出力する仕組みが必要になりました。

今年が2016年だったとして、来年になったら自動的に2017年のカスタムタクソノミータームアーカイブページへリンクを切り替えたいわけです。

仕様として、カスタムタクソノミーは「year」、タームは前述のように「年度」、タームスラッグは「西暦」とします。

term-sample

コードを以下のように作りました。

<ul>
<?php
/**
 * カスタムタクソノミー「year」のタームアカーイブページヘのリンクを動的に表示させる
 * 仕様としてカスタムタクソノミーのスラッグは西暦を使っていて、西暦を降順で取得し、一つ目のデータからリンクを生成する
 */

//ソートの仕方を指定
$args = array(
    'hide_empty' => 0, // デフォルトでは投稿を持っていない(空の)タームを返さない。 0=falseを指定すると返す。
    'orderby' => 'slug',
    'order' => 'DESC'
);

//ソートしたタームの配列を取得
$terms = get_terms( 'year', $args);

//最新の西暦のタームへのリンクを出力
echo '<li class="menu-item"><a href="';
echo home_url() . '?taxonomy=year&term=' . $terms[0]->slug . '/'; //ソートしたタームの一つ目からurlを生成
echo '">最新の西暦</a></li>';

?>
</ul>

タクソノミー「year」のターム「西暦」の最新を取得するために、get_terms()でターム情報が入ったオブジェクトを取得し、「最新の西暦」であるタームを取得するためにタームスラッグを降順でソート。タームスラッグを西暦にしてあるので、新しい年が常にオブジェクトの一番目に来ます。

取得したターム「最新の西暦」の名前とスラッグを組み合わせてリンクを生成します。

※get_terms()で以下のようなオブジェクトが取得できます。
term-array

get_terms()の二番目の引数のオプションに「hide_empty」というのがあるのですが、これでハマりました。デフォルトではtrueになっているのですが、そのままだと「タームは作ったけど投稿はまだ無い」という場合に、オブジェクトの中に入ってきません。場合によっては$terms[0]->slugで取りたくても[0]が無い!という状況になってしまいます。

うまくいかない時はこのオプションに注意したほうが良さそうです。

JavaScriptの習作。Yahooの住所ディレクトリAPIと気象情報APIを利用して一時間後の予想降水量を調べるWEBアプリのサンプルを作ってみました。

Yahoo デベロッパーネットワーク というYahooが開発/提供しているツール群があります。その中のに全国の市町村名を取得することができるAPIと、予想降水量を取得できるAPIがあるのですが、それを利用して指定した地域の一時間後の予想降水量を取得できるWEBアプリのサンプルを練習がてら作ってみました。

アプリにアクセスしたりクリックイベントが発火するとJavaScriptのgetJSONからproxy_rest.phpにデータが渡され、そのデータに基づいて各種APIからデータを取り出し、htmlに表示する、という流れで動作します。

上記のデモページにアクセスすると、select要素の中に都道府県が入っています。loadイベント実行時に、getJSONを使ってAPIから都道府県のデータを取得して表示します。

select要素から任意の選択肢を選ぶと自動的にchangeイベントが発火し、市町村名を五十音順でフィルタリングしたボタンが表示されます。このボタンはjsファイルに記述してある連想配列と、前段階で取得しているAPIから得たデータを組み合わせて生成しています。

同時に戻るボタンも出ます。文字通りひとつ前の段階に戻る機能を持っていますが、やっていることはhtml要素にclassをつけたり外したりしてCSS3のtransitionを利用してウィンドウの外に追い出したり引き入れたりして画面表示を変更しているだけです。

五十音ボタンをクリックすると、属する市町村名ボタンが出ます。こちらもAPIからデータを取得して生成しています。次の段階で利用する緯度経度の値をdata属性として持たせています。

最後に市町村名ボタンをクリックすると、一時間後の予想降水量が表示されます。ボタンの中に入れてある緯度経度の値をAPIに渡してデータを取得することにより実現してます。

と、文章で書いてもなかなか伝わりませよね。コードを掲載してみます。

JavaScript

JavaScript初学者が作った学習用のコードのため、間違いや非効率なコードが含まれている可能性があります。ご了承ください。ご指摘、ご助言いただけますと非常にありがたいです!コメント欄をご利用ください。
//script.js

jQuery(function() {
/**
 * API設定
 */

//name space
var gVar = gVar || {};

//JSON取得用URLを切り替えるワード
gVar.changingWord = {
	country : 'country',
	city : 'city',
	geometry: 'geometry'
};

//市区町村 絞込 リクエストパラメーター 頭文字コード
gVar.cityFiltering = [
	{
		code: 'a',
		codeVal: 'あ行',
	} , {
		code : 'k',
		codeVal : 'か行、が行'
	} , {
		code: 's',
		codeVal: 'さ行、ざ行',
	} , {
		code: 't',
		codeVal: 'た行、だ行',
	} , {
		code: 'n',
		codeVal: 'な行',
	} , {
		code: 'h',
		codeVal: 'は行、ば行、ぱ行',
	} , {
		code: 's',
		codeVal: 'ま行',
	} , {
		code: 'y',
		codeVal: 'や行',
	} , {
		code: 'r',
		codeVal: 'ら行',
	} , {
		code: 'w',
		codeVal: 'わ行',
	}
];

/**
 * 都道府県名表示
 */
 (function(){

	jQuery(window).one('load',function(){

		jQuery.getJSON('proxy_rest.php',{changingWord:gVar.changingWord.country})
		.done(function(data){

			jQuery('.country select').append('');

			for(var i = 0; i < data.ResultInfo.Count; i++ ){
				var val = {
					name : data.Feature[0].Property.AddressDirectory[i].Name,
					AreaCode : data.Feature[0].Property.AddressDirectory[i].AreaCode
				}
				jQuery('.country select').append('');
			}
		})
		.fail(function(){
			window.alert('失敗');
		});

	});

 }());


/**
 * 市区町村名50音 表示
 */
(function(){

	jQuery(document).on('change','.country select', function(){
		jQuery('.city_filtering_body').empty();
		var $cityName = jQuery('.country select option:selected').text();
		var $areaCode = jQuery(this).val();
		for(var i = 0; i <= gVar.cityFiltering.length -1; ++i){
			jQuery('.city_filtering_body').append('');
		}
		removeOut('.city_filtering');
		addOut('.country');
		setName('.city_filtering_body',$cityName);
		removeNone('.controller_city_filtering');
	});

}());


/**
 * 市区町村名/座標情報(緯度経度) 表示
 */
 (function(){

 	jQuery(document).on('click','.city_filtering_body button',function(){

 		var cityfiltering_code = jQuery(this).attr('data-cityfiltering_code');

		jQuery.getJSON('proxy_rest.php',{
			changingWord:gVar.changingWord.city,
			cityFilteringCode:cityfiltering_code
		})
		.done(function(data){
			jQuery('.city_body').empty();
			for(var i = 0; i < data.ResultInfo.Count; i++ ){
				var val = {
					name : data.Feature[0].Property.AddressDirectory[i].Name,
					coordinates : data.Feature[0].Property.AddressDirectory[i].Geometry.Coordinates
				}
				jQuery('.city_body').append('');
			}
			removeOut('.city');
			addOut('.city_filtering');
			removeNone('.controller_city');
			removeNone('.city_body');
			addNone('.controller_city_filtering');
		})
		.fail(function(){
			window.alert('失敗');
		});

	});

}());


/**
 * 天気情報 表示
 */
(function(){
	jQuery(document).on('click','.city_body button',function(){

		var dataCoordinates = jQuery(this).attr('data-coordinates');
		addNone('.city_body');

		jQuery.getJSON('proxy_rest.php',{
			changingWord:gVar.changingWord.geometry,
			coordinates:dataCoordinates
		})
		.done(function(data){
			jQuery('.weather_body').empty();
			var val = {
				weather :data.Feature[0].Property.WeatherList.Weather[6].Rainfall
			}
			jQuery('.weather_body').append('

1時間後の予想降水量
' + val.weather + '
mm/h

'); removeOut('.weather'); addOut('.city'); removeNone('.controller_weather'); addNone('.controller_city'); }) .fail(function(){ window.alert('失敗'); }); }); }()); /** * リターン/クローズボタン */ jQuery('.controller_city_filtering button[name=return]').on('click',function(){ selectClear('.country select'); addOut('.city_filtering'); removeOut('.country'); addNone('.controller_city_filtering'); }) jQuery('.controller_city button[name=return]').on('click',function(){ addOut('.city'); removeOut('.city_filtering'); addNone('.controller_city'); removeNone('.controller_city_filtering'); jQuery('.city_body').empty(); }) jQuery('.controller_weather button[name=return]').on('click',function(){ removeNone('.city_body'); addOut('.weather'); removeOut('.city'); addNone('.controller_weather'); removeNone('.controller_city'); }) jQuery('.controller button[name=close]').on('click',function(){ removeOut('.country'); addOut('.city'); addOut('.weather'); addNone('.controller_city_filtering'); addNone('.controller_city'); addNone('.controller_weather'); selectClear('.country select'); jQuery('.city_body').empty(); }) /** functions */ function setName($name,$elm){ jQuery($name).prepend('

' + $elm + '

'); } function removeOut($elm){ jQuery($elm).removeClass('out'); } function addOut($elm){ jQuery($elm).addClass('out'); } function removeNone($elm){ jQuery($elm).removeClass('none'); } function addNone($elm){ jQuery($elm).addClass('none'); } function selectClear($elm){ jQuery($elm).each(function() { this.selectedIndex = 0; }); } });

PHP

<?php

//proxy_rest.php

mb_http_output('utf-8');
mb_internal_encoding('utf-8');
header('Content=Type: text/xml;charset=UTF-8');

/**
 * 受信したデータを判定し、JSONを取得するURL生成メソッドを切り替えて呼び出す
 * changingWordはURLを切り替えるために使う
 */
if (!isset($_GET["changingWord"]) || $_GET["changingWord"] === ''){

	echo 'no data';

} else {

	$json = new createJson();
	$changingWord = $_GET["changingWord"];

	if ($_GET["changingWord"] === 'country') {

		//都道府県名をリクエスト
		$json->responseData($changingWord);

	} else if ($_GET["changingWord"] === 'city') {

		//市区町村名(五十音順)をリクエスト
		if (isset($_GET["cityFilteringCode"])){
			$json->responseData($changingWord,$_GET["cityFilteringCode"]);
		}

	} else if ($_GET["changingWord"] === 'geometry') {

		//天気をリクエスト
		if (isset($_GET["coordinates"])){
			$json->responseData($changingWord,$_GET["coordinates"]);
		}

	}

}

/**
 * APIからJSON形式のデータを取得/表示する
 */
class createJson{

	//API URL 住所ディリクトリAPI
	private $url_dir     = 'http://search.olp.yahooapis.jp/OpenLocalPlatform/V1/addressDirectory';

	//API URL 気象情報API
	private $url_weather = 'http://weather.olp.yahooapis.jp/v1/place';
	
	//アプリケーションID
	private $appid       = ***; //Yahooより取得したアプリケーションIDを入力する

	//出力形式
	private $output      = 'json';

	//住所コード 地域名を表現するリテラルを格納する
	private $ac;

	//緯度経度を表す数値を格納する
	private $cd;

	//url生成/返却データ表示
	function responseData($word,$setVal = false){

		//条件に応じたリクエストを呼び出す
		if ($word == 'country') {

			//都道府県名をリクエスト
			$url = $this->requestUrl_country();

		} else if ($word == 'city') {

			//住所コードをセット
			$this->ac = $setVal;

			//市区町村名(五十音順)をリクエスト
 			$url = $this->requestUrl_city();

		} else if ($word == 'geometry') {

			//緯度経度をセット
			$this->cd = $setVal;

			//天気をリクエスト
			$url = $this->requestUrl_weather();

		}

		//返却されたjsonデータを表示
		echo file_get_contents($url);
		
	}

	function requestUrl_country(){
		return $this->url_dir."?appid=".$this->appid."&output=".$this->output."&ac=JP&callBack=?";
	}

	function requestUrl_city(){
		return $this->url_dir."?appid=".$this->appid."&output=".$this->output."&ac=".$this->ac."&mode=1"."&callBack=?";
	}

	function requestUrl_weather(){
		//"http://weather.olp.yahooapis.jp/v1/place?coordinates=139.732293,35.663613&output=json&appid=dj0zaiZpPUpUR2x2UG04azBPbiZzPWNvbnN1bWVyc2VjcmV0Jng9ZmE-&callBack"
		return $this->url_weather."?appid=".$this->appid."&output=".$this->output."&coordinates=".$this->cd."&callBack";
	}

} //createJson end

Html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link href="style.css" rel="stylesheet" type="text/css" media="all" />
<script src="http://code.jquery.com/jquery-2.2.3.min.js"></script>
<script src="js/custom.js"></script>
</head>

<body>

    <div class="controller">
        <div class="controller_city_filtering none">
            <button name="return" class="btn">戻る</button>
        </div>
        <div class="controller_city none">
            <button name="return" class="btn">戻る</button>
            <button name="close" class="btn">閉じる</button>
        </div>
        <div class="controller_weather none">
            <button name="return" class="btn">戻る</button>
            <button name="close" class="btn">閉じる</button>
        </div>        
    </div>

    <div class="country">
        <div  class="country_inner">
            <h1 class="title">一時間後の予想降水量</h1>
            <div class="country_body"><select></select></div>        
        </div>
    </div>

    <div class="city_filtering out">
        <div class="city_filtering_body"></div>
    </div>

    <div class="city out">
        <div class="city_body"></div>
    </div>

    <div class="weather out">
        <div class="weather_body"></div>
    </div>


</body>
</html>

まとめ

一部例外処理を省略してはいますが、概ね期待した動作をしています。コードは割りとシンプルに書けたかな、とは思いますが、いかんせん経験不足なためコードの良し悪しを自己評価するのが難しいです。良くないところとか改善できるところがたくさんあるのだろうとは思うのですが、もうこれは経験を積み重ねた後に解ることなので精進あるのみですね。

もっと機能を追加する場合は、APIから取得したデータをhtmlに埋め込むのではなく、配列を作ってキャッシュし、必要に応じてデータを再利用しやすくするような感じがいいのでしょうかね。

たとえば、複数の地域の降水量情報を並列で表示して、一方のデータのみ切り替えて比較するような動作とか、降水量は一時間おきだけじゃなく、2時間後、3時間後とかもたしかとれるはずなので平均値を求めたり、まぁいろいろあると思います。

そうなるとAngularJSとかVue.jsとかMVCフレームワークの出番があるのかなーとか思います。想像していると楽しくなってきますね。そういったフレームワークも機会があれば勉強してみたいと思います。

今後の予定は、さしあたってアニメーションに特化したjsライブラリをやってみようかなと検討しています。候補にあるのがTweenMaxというやつ。なんか便利そう。p5.jsもいいらしい。いろいろ試してみようと思います。