JavaScriptでできたスライドショーアプリ「S5」すごすぎ

先輩にS5ってのを教えてもらった。
これ、すごすぎ。
JavaScriptからFlash経由で音までつけてる。
ただ、重いね。
やっぱり機能つけすぎるとこうなるよなぁ。
movabletypeとの連携ってアイデアいいね。

最初からS5とか使うのもいいけど、JavaScriptにびびらず自分で作ってみるのを進める。
今回作ったslide.jsくらいだったらすぐできるから
ちなみに俺はびびってたw
今では、いい勉強になったと思ってる。

prototype.js

1週間JavaScriptを実際に書いてみて、
だいたいJavaScriptってのがわかってきて、
prototype.jsのやってること、その有用性がわかってきた。
なるほどーって感じ。
いつかprototype.jsリファクタリングしようかな。
ソースが読みやすくなるだろうな。
エフェクトとかはObject.extendで拡張して機能追加しやすいようにできるだろうし。


document.getElementsByClassName
作ってるときに気づいたんだけど、
firefoxだとchildNodesの要素に改行文字(\n)まで入ってるね。
そんなのいらねー。

slide.js 7日目

まぁ目標としていたレベルは完成したかな。
自分的にはα版まではできた。
フェードインをつけたい。
あと目次の自動生成して、クリック移動できるモーダルウィンドウ・ナビゲーターつけたら終了かな。

今回のポイント

 背景色(background-color)と文字色(color)を同じ色にして、
 クリックしたら背景色をスライドの背景色と同じに変更するだけ。
 zoom-inに至っては、cssの:hoverを使っただけ。

  • グリッドレイアウトも一応それっぽくできた

 CSS難しい。

  • ページ指定ジャンプ機能実装
  • ブラウザ互換addEventListener実装(どこからかのぱくり)
  • クラス名でDOM Elementが取れるdocument#getElementsByClassNameを実装(どこからかのぱくり)

slide.js

/*
 * slide.js
 * @version:	2007/05/10
 * @author:		futa23
*/

// Slide class
function Slide() {
    this.initialize.apply(this, arguments);
}

Slide.prototype.initialize = function() {
	this.header;
	this.contents = new Array();	
	this.page = 0;
	//deafult: false(normal mode)
	this.mode = false;
};

Slide.prototype.start = function(){

	//neglect slide mode
	if (this.mode) return;

	//set style sheet
	this.setStyleSheet('./slide.js/css/slide.css');

	//change mode
	this.mode = true;
	
	//create header, footer into body
	document.getElementById('body').innerHTML
		= '<div id="header" class="header">header</div>'
			 + document.getElementById('body').innerHTML
			 + '<div id="footer" class="footer">'
			 + '<a href="./slide.js/core/help.html?height=200&amp;width=400&modal=true" class="thickbox" title="slide.js -help-">help</a>'
			 + '<a href="#top" id="start" onclick="javascript:slide.jump(0)">top</a>'
			 + '<a href="#prev" id="prev" onclick="javascript:slide.prev()" disable>prev</a>'
			 + '<input type="text" name="jump" id ="jump" width="30px" value="1"' 
			 + 'onkeypress="javascript:if(event.keyCode == 13) {slide.jump((this.value-1));}"/>'
			 + '<span id="lastPage">/0</span>'
			 + '<a href="#next" id="next" onclick="javascript:slide.next()">next</a>'
			 + '<a href="#quit" id="quit" onclick="javascript:slide.quit()">quit</a>'
			 + '&nbsp;&nbsp;</div>';

	//store slide base element on memory
	this.header = document.getElementById('header');
			
	if (document.getElementById('next')) {
		document.getElementById('next').disabled = false;
	}
	
	//load contents
	var divPages = document.getElementsByTagName('div');
	for (var i=0; i<divPages.length ; i++) {
		if (divPages[i].className == 'page') {
			divPages[i].id = this.contents.length;
			this.contents.push(divPages[i]);
			
			divPages[i].className = 'hide';
		}
	}
	
	//set lastPage
	document.getElementById('lastPage').innerHTML = '/' + this.contents.length;

	//load first page
	this.jump(0);
};

Slide.prototype.next = function() {
	this.jump(++this.page);
};

Slide.prototype.prev = function() {
	this.jump(--this.page);
};

Slide.prototype.jump = function(p) {

	if (!this.mode || typeof p == 'undefined' || isNaN(p)) return;

	if (0 <= p && p < this.contents.length) {
		for (var i=0; i<this.contents.length; i++) {
			this.contents[i].className = 'hide';
		}
		//for current page
		this.contents[p].className = 'main';

		this.setEffect();

		//get title
		for(var j=0; j<this.contents[p].childNodes.length; j++) {
			if (this.contents[p].childNodes[j].className
				 && this.contents[p].childNodes[j].className == 'title') {
//				alert(this.contents[p].childNodes[j].innerHTML);
				this.header.innerHTML = '<span class=title>' + this.contents[p].childNodes[j].innerHTML + '</span>';
				break;
			}
		}

		//set inputed page to the current page.
		this.page = p;
		document.getElementById('jump').value = (p+1);

		// change next button disable
		if (p == this.contents.length - 1){
			if (document.getElementById('next')){
				document.getElementById('next').disabled = true;
			}			
		}
		// change prev button disable
		if (document.getElementById('prev')){
			if (p == 0){
				document.getElementById('prev').disabled = true;
			}else {			
				document.getElementById('prev').disabled = false;
			}
		}
	}else{
		this.quit();
	}
};

Slide.prototype.quit = function() {
	if (!this.mode) return;

	for(var i=0; i<this.contents.length; i++) {	
		this.contents[i].className = 'page';
		this.contents[i].removeAttribute('style');
	}

	//remove header
	document.getElementById('body').removeChild(this.header);
	//remove footer
	document.getElementById('body').removeChild(document.getElementById('footer'));
	
	this.setStyleSheet();
	
	//change to normal-mode(not slideshow-mode)
	this.initialize();
	this.resetEffect();
};

Slide.prototype.setStyleSheet = function (sheet) {
	
	if(links = document.getElementsByTagName('link')){
		for(var i=0; i<links.length; i++) {
			if (links[i].rel == 'stylesheet' && links[i].type == 'text/css') {
				//set normal or another stylesheet enable if slideshow-mode
				if (this.mode) {
					if(links[i].title != 'slide'){
						links[i].disable = false;	
					}else {
						links[i].disable = true;
						document.getElementsByTagName('head')[0].removeChild(links[i]);	
					}; 
				}
			}
		}
	}
	
	if (sheet) {
		linkTag = document.createElement('link');
		linkTag.setAttribute('title', 'slide');
		linkTag.setAttribute('rel', 'stylesheet');
		linkTag.setAttribute('type', 'text/css');
		linkTag.setAttribute('href', sheet);
		
		document.getElementsByTagName('head')[0].appendChild(linkTag);
	}
};

Slide.prototype.setEffect = function() {
	//TODO
	//fade-in
	
	//monta
	var montas = document.getElementsByClassName('monta');
	for(var i=0; i<montas.length; i++) {
		montas[i].setAttribute('style', 'z-index:10');
		addEventListener(montas[i], 'click', 
						function() {
							this.className = 'monta-off';
						});
	}
	
};

Slide.prototype.resetEffect = function() {
	//monta-off
	var montas = document.getElementsByClassName('monta-off');
	for(var i=0; i<montas.length; i++) {
		montas[i].className = 'monta';
	}
};

/**
 * general eventListner
 * @param {Object} target
 * @param {Object} type
 * @param {Object} func
 * 
 * @usage:addEventListner(obj, 'click', func1);
 */
function addEventListener(target, type, func) {
  if(target.attachEvent) {
    target.attachEvent("on" + type, func);
  } else if(target.addEventListener) {
    target.addEventListener(type, func, true);
  } else {
    target["on" + type] = func;
  }
}

/**
 * 
 * @param {Object} className
 */
document.getElementsByClassName = function(className) {
	var elementsAll = document.getElementsByTagName('*') || document.all;
	var elements = new Array();
	for(var i = 0; i < elementsAll.length; i++) {
		var classNames = elementsAll[i].className.split(' ');
		for(var j = 0; j < classNames.length; j++) {
			if (classNames[j] == className) {
				elements[elements.length] = elementsAll[i];
				break;
			}
		}
	}
	return elements;
}

//app
var slide = new Slide();

呼び出すHTML

?<html>
<head>
<title>slide.js</title>

<link rel="stylesheet" type="text/css" href="./slide.js/css/normal.css"/>

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

<!--help-->
<script type="text/javascript" src="./slide.js/plugins/FishEye.js"></script>

<script type="text/javascript" language="JavaScript" charset="utf-8" src="./slide.js/plugins/shortcuts.js"></script>
<script type="text/javascript" language="JavaScript" charset="utf-8" src="./slide.js/core/slide.js"></script>

<script type="text/javascript">
<!--
	//add keybind
	shortcut('s', function(){slide.start();});
	shortcut('left', function(){slide.prev();});
	shortcut('right', function(){slide.next();});
	shortcut('space', function(){slide.next();});
	shortcut('q', function(){slide.quit();});
-->
</script>

</head>

<body id="body" bgcolor="#FFFFFF" text="#003366" link="#3399FF" vlink="#99CCFF" alink="#FF3333">

<div class="page">
	<h3 class="title">自己紹介</h3>
	<ul>
		<li>名前:futa23
		<li>職業:SEでもプログラマーでもないIT系
		<li>装備:Java(普通に), PHP(並以下), Perl(並以下), Ruby(初心者)
	</ul>
</div>

<div class="page">
	<h3 class="title">slide.jsを作るきっかけ</h3>
	<ul>
		<li>oopなJavaScriptを書いてみたかった。
		<li>作るなら実用的なもの。
		<li>他のライブラリも使ってエフェクトも掛けたい。
		<li>ショートカット厨なので、ショートカット機能は必須実装機能。
		<li>HTML表示モードとプレゼンモードの切り替えができるように。
	</ul>
</div>

<div class="page">
	<h3 class="title">slide.jsのショートカット一覧</h3>
	<ul>
		<li><strong>s</strong> --- start slide show
		<li><strong>right(→) or space</strong> --- go to next page
		<li><strong>left(←)</strong> --- go to previous page
		<li><strong>q</strong> --- quit slide show
		<li><strong>h</strong> --- show this help page
		<li><strong>esc</strong> --- close this help page
	</ul>
</div>

<div class="page">
	<h3 class="title">class指定でできるエフェクト</h3>
	<ul>
		<li class="fishEye">li.fishEye --- FishEyeメニュー
		<li>monta --- <span class="monta">もんたメソッド</span>
		<li>zoom-in --- <span class="zoom-in">拡大</span>
	</ul>
</div>

</body>
</html>

slide.css

z-indexっていうオーバーレイ表現でよく使う属性を初めて使った。
これでモーダルウィンドウ実装するのね。なるほど。

*{
	margin:0px;
	border:0px;
	font-family:MeiryoKe_Pgothic, メイリオ, Osaka, Arial, Helvetica, sans-serif;
}

#body{
	height:100%;
	width:100%;
	background-color:#c4daa3;
}

/* header */
#header{
	height:2em;
	color:#003366;
	background-color:#c4daa3;	
	margin:0px;
	padding:1em 0.7em 0em 1em;/*top right bottom left */
	font-weight:bolder;
	font-size:2.5em;
	vertical-align:text-bottom;
}

#header .title{
	border-bottom:0.2em solid;
	width:80%;
	border:solid #50682b;
 	border-width:0 0 1px 12px;
 	padding:0.4em 0.4em;	
}

/* current page */
.main{
	font-size:1.5em;
	font-weight:bold;
	color:#003366;
	background-color:#c4daa3;
	border:0px;
	border-top:#FFFFFF 0px solid;
	margin:0px;
	padding:0.2em 0.5em 0em 1em;/*top right bottom left */
	line-height:40px;
}

/* current page's title */
.main .title{
	display:none;
}

.main ul, .main ol{
	padding-left:2em;
}

/* footer */
#footer {
	position:absolute;
	bottom:0px;
	z-index:10; 
	width:100%;
	background-color:#314051;
	border-top:#FFFFFF 0px solid;
	padding:0.1em 0em 0.1em 0em;/*top right bottom left */
	text-align:right;
	vertical-align:middle;;
	color:#ffffff;
	font-size:1em;
}

#footer input{
	margin-left:10px;
	width:30px;
}

img{
	border:none;
}

A{
	margin-left:10px;
}

A:link{
	color:#0ac0ff;
	text-decoration:none;
	}
A:visited{
	color:#5080e0;
	text-decoration:none;
}
A:hover{
	color:#2ae0ff;
	text-decoration:underline;
}

/* effect */
.hide{
	display: none;
} 

.main li.zoom-in:hover{
	font-size:2em;
}

.main .monta {
	background-color:yellow;
	color:yellow;
}

.main .monta-off{
	
}

normal.css

normal.css(スライドモードじゃないとき)はてきとーに書いてOK

body{
	background-color:#ffffff;
	color:#003366; 	
}

div.page{
	padding-left:3em;
	padding-top:2em;
}

div.page ul, div.page ol{
	padding-left:3em;
}

div.page.h1, div.page.h2, div.page.h3, div.page.h4, div.page.h5{
	padding-left:0em;
}

slide.js 6日目

今回のポイント

  • #next()と#prev()を汎化させて#loadPage()を作った

 #next()と#prev()ではページ番号のインクリメント・デクリメント?だけして
 loadPageでそのページ番号に対応する配列インデックスをロード(style切り替え)

  • titleを取得してid=headerなdivに埋め込み
  • nextなどのボタンを下部につけた
  • cssのグリッドレイアウトに苦しむ(;´Д`)

 でもスライドショーっぽくなってきた!

  • エフェクト系を調べ始め、FishEyeエフェクトを改変してみた(コードの整理してないからHTMLに直書)
  • script.aculo.usのeffectにあるHighLightもつけてみたが、つけてみただけorz

slide.js

/*
 * slide.js
 * @version:	2007/05/09
 * @author:		futa23
*/

// Slide class
function Slide() {
    this.initialize.apply(this, arguments);
}

Slide.prototype.initialize = function() {
	this.header;
	this.contents = new Array();	
	this.page = 0;
	//deafult: false(normal mode)
	this.mode = false;
};

Slide.prototype.start = function(){

	//neglect slide mode
	if (this.mode) {
		return;
	}
	
	//change mode
	this.mode = true;
		
	//create header, footer into body
	document.getElementById('body').innerHTML
		= '<div id=\"header\" class=\"header\">header</div>'
			 + document.getElementById('body').innerHTML
			 + '<div id=\"footer\" class=\"footer\">'
			 + '<a href=\"#\" onClick=\"javascript:Modalbox.show(\'slide.js -help-\'\, \'slide/help.html\'\, {width: 460\, height:400})\;return false\;\">(h)elp</a>'
			 + '<input type=\"button\" value=\"start!\" id=\"startBtn\" onclick=\"slide.start()\" disable>'
			 + '<input type=\"button\" value=\"prev!\" id=\"prevBtn\" onclick=\"slide.prev()\" disable>'
			 + '<input type=\"button\" value=\"next!\" id=\"nextBtn\" onclick=\"slide.next()\"></div>';
	
	//store slide base element on memory
	this.header = document.getElementById('header');
	
	//change startBtn button disable
	if (document.getElementById('startBtn')) {
//		document.getElementById('startBtn').value = 'quit!';
//		document.getElementById('startBtn').addEventListener('onClick', slde.quit(), false);
		document.getElementById('startBtn').disabled = true;
	}
	if (document.getElementById('nextBtn')) {
		document.getElementById('nextBtn').disabled = false;
	}
	
	//load contents
	var divPages = document.getElementsByTagName('div');
	for (var i=0; i<divPages.length ; i++) {
		if (divPages[i].className == 'page') {
			divPages[i].id = this.contents.length;
			this.contents.push(divPages[i]);
			
			divPages[i].className = 'hide';
		}
	}
	
	//load first page
	this.loadPage(0);
};

Slide.prototype.next = function() {
	this.loadPage(++this.page);
};

Slide.prototype.prev = function() {
	this.loadPage(--this.page);
};

Slide.prototype.loadPage = function(p) {

	if (!this.mode) {
		return;
	}

	if (0 <= p && p < this.contents.length) {
		for (var i=0; i<this.contents.length; i++) {
			this.contents[i].className = 'hide';
		}
		//for current page
		this.contents[p].className = 'main';

		//TODO
		if (typeof Effect != 'undefined') {
			new Effect.Highlight(document.getElementById(p), {duration: 2});
		}

		//get title
		for(var j=0; j<this.contents[p].childNodes.length; j++) {
			if (this.contents[p].childNodes[j].className
				 && this.contents[p].childNodes[j].className == 'title') {
//				alert(this.contents[p].childNodes[j].innerHTML);
				this.header.innerHTML = this.contents[p].childNodes[j].innerHTML;
				break;
			}
		}

		//set inputed page to the current page.
		this.page = p;

		// change next button disable
		if (p == this.contents.length - 1){
			if (document.getElementById('nextBtn')){
				document.getElementById('nextBtn').disabled = true;
			}			
		}
		// change prev button disable
		if (p == 0){
			if (document.getElementById('prevBtn')){
				document.getElementById('prevBtn').disabled = true;
			}			
		}else {
			if (document.getElementById('prevBtn')){
				document.getElementById('prevBtn').disabled = false;
			}
		}
	}else{
		this.quit();
	}
};

Slide.prototype.quit = function() {
	if (!this.mode) {
		return;
	}

	for(var i=0; i<this.contents.length; i++) {	
		this.contents[i].className = 'page';
		this.contents[i].removeAttribute('style');
	}

	//remove header
	document.getElementById('body').removeChild(this.header);
	//remove footer
	document.getElementById('body').removeChild(document.getElementById('footer'));
	
	this.initialize();

	if (document.getElementById('startBtn')){
			document.getElementById('startBtn').disabled = false;
	}
	if (document.getElementById('nextBtn')){
			document.getElementById('nextBtn').disabled = false;
	}
};

//app
var slide = new Slide();

呼び出すhtml側

<html>
<head>
<title>slide.js</title>

<link rel="stylesheet" type="text/css" href="includes/modalbox.css" media="screen" />
<link rel="stylesheet" type="text/css" href="slide/slide.css"/>

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

<!--help-->
<script type="text/javascript" src="includes/prototype.js"></script>
<script type="text/javascript" src="includes/scriptaculous.js"></script>
<script type="text/javascript" src="includes/modalbox.js"></script>
<script type="text/javascript" language="JavaScript" charset="utf-8" src="FishEye.js"></script>

<script type="text/javascript" language="JavaScript" charset="utf-8" src="shortcuts.js"></script>
<script type="text/javascript" language="JavaScript" charset="utf-8" src="slide.js"></script>

<script type="text/javascript">
<!--
	//add keybind
	shortcut('s', function(){slide.start();});
	shortcut('left', function(){slide.prev();});
	shortcut('right', function(){slide.next();});
	shortcut('space', function(){slide.next();});
	shortcut('q', function(){slide.quit();});
	shortcut('h', function(){
						Modalbox.show(
							'slide.js -help-', 
							'slide/help.html', 
							{width: 460, height:400}
							);
						return false;
					}
			);
-->
</script>

</head>

<body id="body" bgcolor="#FFFFFF" text="#003366" link="#3399FF" vlink="#99CCFF" alink="#FF3333">

<div class="page">
	<h3 class="title">自己紹介</h3>
	<ul>
		<li >名前:futa23
		<li>職業:SEでもプログラマーでもないIT系
		<li>装備:Java(普通に), PHP(並以下), Perl(並以下), Ruby(初心者)
	</ul>
</div>

<div class="page">
	<h3 class="title">slide.jsを作るきっかけ</h3>
	<ul>
		<li>oopなJavaScriptを書いてみたかった。
		<li>作るなら実用的なもの。
		<li>他のライブラリも使ってエフェクトも掛けたい。
		<li>ショートカット厨なので、ショートカット機能は必須実装機能。
		<li>HTML表示モードとプレゼンモードの切り替えができるように。
	</ul>
</div>

<div class="page">
	<h3 class="title">slide.jsのショートカット一覧</h3>
	<ul>
		<li><strong>s</strong> --- start slide show
		<li><strong>right(→) or space</strong> --- go to next page
		<li><strong>left(←)</strong> --- go to previous page
		<li><strong>q</strong> --- quit slide show
		<li><strong>h</strong> --- show this help page
		<li><strong>esc</strong> --- close this help page
	</ul>
</div>

<script type="text/javascript">
<!--
	//FishEye
	var fishEye = new FishEye(
					{'large':'3em', 'medium':'2em', 'regular':'1em'}
					, {'target': 'li'}
				);
	targets = document.getElementsByTagName('li');
	for(var i=0; i<targets.length; i++) {
//		alert(targets.length);
		targets[i].addEventListener('onmouseover', this.bigger ,true);
		targets[i].addEventListener('onmouseout', this.normal ,true);
	}
-->
</script>
</body>
</html>

css

/* header */
#header{
	height:15%;
	width:100%;
	background-color:#4a617b;
	margin:0px;
	padding:0.5em 0.7em 0em 1em;/*top right bottom left */
	color:#ffffff;
	font-size:2em;
	font-weight:bold;
	vertical-align:middle;
}

/* current page */
.main{
	height:77%;
	width:100%;
	font-size:1.5em;
	background-color:#eeeeee;
	border:0px;
	border-top:#FFFFFF 0px solid;
	margin:0px;
	padding:1.5em 1em 0em 1em;/*top right bottom left */
	/*行間隔*/
}

/* current page's title */
.main .title{
	display:none;
}

/* footer */
#footer{
	height:8%;
	width:100%;
	background-color:#314051;
	border-top:#FFFFFF 0px solid;
	margin:0px;
	padding:0.1em 0em 0em 0em;/*top right bottom left */
	text-align:right;
	vertical-align:middle;
}

img{
	border:none;
}


A:link{
	color:#0ac0ff;
	text-decoration:none;
	}
A:visited{
	color:#5080e0;
	text-decoration:none;
}
A:hover{
	color:#2ae0ff;
	text-decoration:underline;
}

.hide{
	display: none;
} 

FishEye.js

おまけ。
DOMのpreviousSibling、nextSiblingを知った。
こんなシンプルに実装できるのね。感動。
FishEyeのオリジナルにオプションで引数渡せられるようにしたのと、クラス化しただけ。

/**
 * FishEye
 * by http://dannyayers.com/2003/10/fisheye.html
 * 
 * revised by futa23
 */

// FishEye class
function FishEye() {
    this.initialize.apply(this, arguments);
}

FishEye.prototype.initialize = function() {
	//default
	this.large = '3em';
	this.medium = '2em';
	this.regular = '1em';
	this.targetTagName = null;

	if(fontSize = arguments[0]) {
		if(fontSize['large']) this.large = fontSize['large'];
		if(fontSize['medium']) this.medium = fontSize['medium'];
		if(fontSize['regular']) this.regular = fontSize['regular'];
	}
//	if(option = arguments[1]) {
//		alert(option['target']);
//		if(option['target']) {
//			this.targetTagName = option['target'];
//			targets = document.getElementsByName(this.targetTagName);
//			for(var i=0; i<targets.length; i++) {
//				alert(targets.length);
//				targets[i].addEventListener('onmouseover', this.bigger ,true);
//				targets[i].addEventListener('onmouseout', this.normal ,true);
//			}
//		}
//	}
};

//function convertPreBlocks() {}

FishEye.prototype.bigger = function (thing){
	thing.style.fontSize = this.large;
	
	siblingElement = thing.previousSibling;

	while(siblingElement) {
		if(siblingElement.tagName == thing.tagName) {
			siblingElement.style.fontSize = this.medium;
			break;
		}
		siblingElement = siblingElement.previousSibling;
	}

	siblingElement = thing.nextSibling;

	while(siblingElement) {
		if(siblingElement.tagName == thing.tagName) {
			siblingElement.style.fontSize = this.medium;
			break;
		}

		siblingElement = siblingElement.nextSibling;
	}
}

FishEye.prototype.normal = function(thing){
	thing.style.fontSize = this.regular;

	siblingElement = thing.previousSibling;

	while(siblingElement) {
		if(siblingElement.tagName == thing.tagName) {
			siblingElement.style.fontSize = this.regular;
			break;
		}
		siblingElement = siblingElement.previousSibling;
	}

	siblingElement = thing.nextSibling;

	while(siblingElement) {
		if(siblingElement.tagName == thing.tagName) {
			siblingElement.style.fontSize = this.regular;
			break;
		}

		siblingElement = siblingElement.nextSibling;
	}
}

参考

JavaScriptのコード自体は初めて書いたに等しいけど、ちょこちょこ本は読んでた。
オライリーのサイ本。
レビューにもあるとおり、書かれてるJavaScriptのバージョンは古いけどためになる。
prototypeベースなオブジェクト指向の意味がわかった本。
あと、JavaScriptにおいて
連想配列JavaでいうHashMapクラスだと思っていいforJavaしか知らない人)はオブジェクト、
というかオブジェクトは連想配列なこととか、
documentクラスやwindowsクラス、for inなど基礎がたくさん載ってる名著。

slide.js 5日目

ちょこちょこ続けてたんだけど、日記更新する余裕がなかった(;´Д`)

で、過去形だけど進捗を。
過去のことなので、日付詐称。
JavaScript勉強してる人の参考になれば。
ならなかったらただのゴミでけっこう(`・ω・´)
しかし、ソースが汚い。
でも、この辺から徐々にJavaScriptがわかってきた。
まぁ、ほぼ動く状態になったし。

今回のポイント

cssにdispaly:none(非表示するやつ)があるのを思い出したので
(元々やろうとしてた)「JavaScriptのメモリ上でpageタグ内の情報を持っておいて、
表示したいやつだけHTMLに入れてやる」って方法をやめた。
ってか、こういうのはcssでstyleを動的に変えて方法を使うのが普通なのね。
エフェクトするJavaScriptのライブラリを見て気づいた。
ってか、Webに勉強材料はたくさんあるんだから「最初から実装方法ちゃんと調べておけよ」って話だよなぁ。
firebugの使い方がやっとわかった。
たしかにこれは便利だわ(・∀・)イイー

slide.js

あと、modalbox.jsってのでヘルプをつけて遊んでみた。
特に意味はない。やってみたかっただけ。
操作ボタンは放置。
1つ前のページに戻る#prev()を足したけど、バグあり。
あとモード情報(mode)を追加。
あとpage番号を持つDivのid属性を自動で採番。
これでページを移動させるの楽になった。

/*
 * slide.js
 * @version:	2007/05/07
 * @author:		futa23
*/

// Slide class
function Slide() {
    this.initialize.apply(this, arguments);
}

Slide.prototype.initialize = function() {
	this.main;
	this.contents = new Array();	
	this.page = 0;
	//deafult: false(normal mode)
	this.mode = false;
};

Slide.prototype.start = function(){

	//neglect slide mode
	if (this.mode) {
		return;
	}
	
	//change mode
	this.mode = true;
	
	//load slide base element
	this.main = document.getElementById('main');
	
	if (document.getElementById('startBtn')) {
		document.getElementById('startBtn').disabled = true;
	}
	
	var divPages = document.getElementsByTagName('div');
	for (var i=0; i<divPages.length ; i++) {
		if (divPages[i].className == 'page') {
			divPages[i].setAttribute('id', this.contents.length);
			this.contents.push(divPages[i]);
			
			//dispaly: none will be replaced
//			divPages[i].innerHTML = '';
			divPages[i].className = 'hide';
		}
	}
	
	this.loadPage(this.contents[0]);
};

Slide.prototype.next = function() {
	
	if (!this.mode) {
		return;
	}
	
	// count up page
	this.page++;
	
	if (this.page < this.contents.length) {
		
//		alert(this.contents[this.page++]);

		//initialize main pane
		this.initPane();

		//load next page
		this.loadPage(this.contents[this.page]);
		
	}
	//last action
	else if(this.page == this.contents.length) {
		//quit slide show		
		this.quit();
		
		// TODO
		if (document.getElementById('nextBtn')){
			document.getElementById('nextBtn').disabled = true;
		}
	}else {
		// TODO(under 0 and over max)
		this.quit();
	}

}

Slide.prototype.prev = function() {
	
	if (!this.mode) {
		return;
	}
	
	this.page--;

	if (this.page < this.contents.length) {
		
		//initialize main pane
		this.initPane();

		//load next page
		this.loadPage(this.contents[this.page]);		
	}
	//last action
	else if(this.page == this.contents.length) {
		//quit slide show		
		this.quit();
		
		// TODO
		if (document.getElementById('nextBtn')){
			document.getElementById('nextBtn').disabled = true;
		}
	}else {
		// TODO(under 0 and over max)
		this.quit();
	}
}

Slide.prototype.quit = function() {

	if (!this.mode) {
		return;
	}

	this.initPane();
	
	if (typeof(this.main) != 'undefined') {
		for(var i=0; i<this.contents.length; i++) {
			
			this.contents[i].className = 'page';
//			this.loadPage(this.contents[i]);
		}
	}else {
		alert("main undefiened");
	}
	
	this.initialize();		
}

Slide.prototype.initPane = function() {
	// initialize main pane
//	alert("initialize");
	while(this.main.firstChild){
//		alert("remove some tags from main");
		this.main.removeChild(this.main.firstChild);
	}
}

Slide.prototype.loadPage = function(el) {

	if (!this.mode) {
		return;
	}

//	alert("loaded-id:" + el.getAttribute('id'));
	for (var i=0; i<this.contents.length; i++) {
		this.contents[i].className = 'hide';
	}
	if (0 <= this.page && this.page < this.contents.length) {
		el.className = 'current';
	}

//	var tempDiv = document.createElement('div');
//	tempDiv.className = 'page';
//	tempDiv.setAttribute('id', el.getAttribute('id'));
//	
//	while(el.firstChild) {
//		tempDiv.appendChild(el.removeChild(el.firstChild));
//	}
//	this.main.appendChild(tempDiv);
}

//app
var slide = new Slide();

呼び出すHTML側

cssのdispaly:noneがポイント。

<html>
<head>
<title>プレゼンツール</title>

<link rel="stylesheet" href="includes/modalbox.css" type="text/css" media="screen" />
<STYLE TYPE="text/css">
<!--
A:link
  { color:#0ac0ff; text-decoration:none; }
A:visited
  { color:#5080e0; text-decoration:none; }
A:hover
  { color:#2ae0ff; text-decoration:underline;}
.hide { display: none; } 
-->
</STYLE>

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">

<!--help-->
<script type="text/javascript" src="includes/prototype.js"></script>
<script type="text/javascript" src="includes/scriptaculous.js"></script>
<script type="text/javascript" src="includes/modalbox.js"></script>

<script type="text/javascript" language="JavaScript" charset="utf-8" src="shortcuts.js"></script>
<script type="text/javascript" language="JavaScript" charset="utf-8" src="slide.js"></script>

<script type="text/javascript">
<!--
	//add keybind
	shortcut('s', function(){slide.start();});
	shortcut('left', function(){slide.prev();});
	shortcut('right', function(){slide.next();});
	shortcut('space', function(){slide.next();});
	shortcut('q', function(){slide.quit();});
	shortcut('h', function(){
						Modalbox.show(
							'slide.js -help-', 
							'slide/help.html', 
							{width: 460, height:400}
							);
						return false;
					}
			);
-->
</script>

</head>

<body id="body" bgcolor="#FFFFFF" text="#003366" link="#3399FF" vlink="#99CCFF" alink="#FF3333">

<input type="button" value="start!" id="startBtn" onclick="slide.start()">
<input type="button" value="next!" id="nextBtn" onclick="slide.next()">

<div id="main" class="main"></div>

<div class="page">
<h3>自己紹介</h3>
<ul>
	<li>名前:futa23
	<li>職業:SEでもプログラマーでもないIT系
	<li>装備:Java(普通に), PHP(並以下), Perl(並以下), Ruby(初心者)
</ul>
</div>

<div class="page">
<h3>slide.js</h3>
スライドショーアプリ。
<ul>
	<li>oopなJavaScriptを書いてみたかった。
	<li>作るなら実用的なもの。
	<li>他のライブラリも使ってエフェクトも掛けたい。
	<li>ショートカット厨なので、ショートカット機能は必須実装機能。
	<li>HTML表示モードとプレゼンモードの切り替えができるように。
</ul>
</div>

<div id="footer" class="footer">
	<a href="#" onClick="javascript:Modalbox.show('slide.js -help-', 'slide/help.html', {width: 460, height:400});return false;">(h)elp</a>
</div>
</body>
</html>