Hatena::ブログ(Diary)

Cyokodog :: Diary

こちらのブログは更新を停止しております。最新の記事は以下ブログをご覧ください。
www.cyokodog.net

November 05, 2012

Markdown記法+クラウド環境でノウハウを整理したくて試したこと

パソコン崩壊

先日、雷の影響で約10年間連れ添ったパソコンがとうとう壊れてしまいました。

ここ数日は奥さんが衝動買いして埃をかぶってたVistaのノートPCをひっぱりだして、環境設定にいそしむ毎日です。電源系の故障なのでハードディスクは無事でしたが、3ヶ月前にもディスプレイが雷の影響で壊れたりとなにかと家電製品にトラブルの多い我が家、クラウドを活用したデータバックアップ環境などを真面目に考え始めようかなと思ってます。

Markdown記法Google Sitesっぽく管理したい

そんな訳で心機一転、安心して使用できるインフラ環境を整えるべく無料ストレージのサービス内容を調べてはGoogle SitesGoogle Driveなどにメモしてるんですが、メモの量が増えるにつれ前々から感じていたこれらサービスがWYGWYG変換出力するヘンテコHTMLに対する違和感が気になりはじめました。

もっとプレーンなテキストのままデータを取っておきたい、Google Sitesでmarkdownが使えればなぁ・・・と

できない事を嘆いてもしょうがないのでmarkdownが使えるサービスを調べてみました。

う〜ん・・・Google Sitesみたいにサイトマップを自由に構成できてmarkdownが使えるサービスって全然無いんですね・・・

DropBox + JavaScript製markdownパーサーでなんとできないか?

ノウハウの集積と整理が主目的なので、自宅/会社に関わらず手軽に書き込め、カテゴリ分けなどの整理整頓が容易にできるというのが理想なのですが、markdownでとなるとなかなかめぼしいサービスが見つかりません。仕方が無いので半分は自力で何とかする方向で考えたのが以下構成。

  • DropBoxのPublicフォルダにマークダウン記法でメモファイルを保存
  • JavaScript製markdownビューワーでドキュメントを参照

この構成だと自宅PCにDropBoxクライアントソフトを入れておけば、フォルダ/ファイルの変更も自動でWeb上に反映/公開されるんでアップロード忘れの心配も無く、会社からでも閲覧できます。ただDropBoxにはmarkdown表記のHTML変換表示機能がないので、JavaScript製のmarkdownパーサを見つけてきて自前でなんとかしようという考えです。(編集中のmarkdownをすぐにHTML形式で確認したい場合で、publicフォルダがwebに反映されるタイムラグが気になる時は、ローカルインストールしたApacheのドキュメントルート(http.confのDocumentRoot)をPublicフォルダにして、http://localhostより確認)

会社からの編集については、DropBoxの場合、Web上での直接編集ができず、かと言ってクライアントソフトを入れるわけにもいかないので、ZeroPCというクラウドサービスを使います。ZeroPCはDropBoxGoogle DriveEvernoteなどデータを統合管理することができるサービスで、DropBoxに置いてあるテキストファイルの直接編集や、フォルダ/ファイル構成の変更管理などが容易にできるようになっています。(変更内容もクライアントソフトと違い即座に反映されるので、編集についてはこちらの方が便利かもしれません。)ちなみにchromeであれば、DropBox上のテキストファイルを直接編集できる拡張機能ていうのもあります。

JavaScript製markdownパーサーを探す

ビューワー作りに必要なJavaScript製markdownパーサーを探したところいくつか見つかりました。

Itty Editor

Hostされてるファイル数も多く使い方を調べるのも面倒そうだったので、すぐあきらめました。

markdonw.js

これはリンク先に記載されてますが単純に以下のようにするだけ。

markdown.toHTML('# H1に変換される!');

これは簡単!と言うことでビューワーの実装は始めたのですが、いくつか致命的な問題にぶつかりました。

ネストしたリストがちゃんと変換されない

こういう記述が・・・

- aaa
	- bbb
- aaa
	- bbb

こんななっちゃいます><

・aaa
	・bbb
	・bbb
・aaa

HTMLがエスケープされてしまう

こういう記述が・・・

<a target="_blank">link</a>

こんななっちゃいます><

&lt;a target="_blank"&gt;link&lt;/a&gt;

dankogaiさんも指摘してます

ここでは Markdown -> HTML の変換にshowdown.jsを採用した。なぜmarkdown.jsでないかというと、markdown.jsは「HTMLをembedできるマークアップ言語」という、Markdownの最も重要な要件を満たしていなかったから。

http://blog.livedoor.jp/dankogai/archives/51818456.html

他にも「1タブもしくはスペース4つ」による「pre code」に対応してないなどの問題もあったので、採用を断念しました。

showdown.js

dankogaiさんも一押しのshowdown.js。使い方もこんな感じで至って簡単!

var converter = new Showdown.converter();
converter.makeHtml('# H1に変換される!');

markdownパーサーはshowdown.jsで決定です。(と思ったら、markdown記法のtableやdlには対応してないみたいです。すごく残念)

markdownビューワーの実装

Query Stringから表示するドキュメントを判定する実装

まず仕様をどうするかですが、最初に考えたのは以下のような感じ。

1) markdownで書かれた各ドキュメントに対するリンクをQuery String付きで記述する。

<a href="?url=doc/hoge.md">hoge.md</a>

2) リンクのクリックで自身のページがリロードされるので、ページロード時の処理でlocation.searchよりdoc/hoge.mdを取得する。

var url = location.search.replace(/^\?url=(.+)/,'$1'); → doc/hoge.md

3) ajaxでコンテンツを取得し、showdonw.jsでパースしたHTMLを画面に表示する。リンクの記述が相対パスで済むように、ajax通信前にダミーのリンクオブジェクトを使って絶対パスを取得しておく。

var dummy = $('<a/>').prop('href',url);
url = dummy.prop('href'); → http://dl.dropbox.com/u/xxxxx/doc/hoge.md
dummy.remove();
$.ajax({url:url ...

で、実装してみたのですが、DropBox側の機嫌によるとこが大きいと思うのですが、時々すごくもっさりする事が・・・

ハッシュフラグメントから表示するドキュメントを判定する実装

上記の実装だと、リンククリックでのページリロード、リロード後のajaxでコンテンツ取得と、2回分の通信が必要になりましたが、ハッシュフラグメントにドキュメントurlを記述する事で通信回数を1回で済むようにしてみます。

1) markdownで書かれた各ドキュメントに対するリンクをハッシュフラグメントで記述する。

<a href="#!doc/hoge.md">hoge.md</a>

2) jQuery Hashchage Event(Ajaxやタブ切替には必須かも?ブラウザの「戻る」「進む」を有効にするjQueryのhashchangeプラグイン - 5509) で、urlの変更を検出し、location.hashよりdoc/hoge.mdを取得する。

var url = location.hash.replace(/^#!(.+)$/ig,'$1'); → doc/hoge.md

3) Query Stringの場合と同様に、コンテンツを取得し画面に表示する。

若干早くなったような気がします。

サイトマップの生成

フォルダの構成やファイル名の変更などは、DropBoxクライアントツールで直接Web上に反映されますが、前述のリンクはマニュアルで修正する必要があります。面倒なのでVBScriptサイトマップファイルをmarkdown形式で生成して、これをビューワーからAjaxで取り込むようにしてみます。

サイトマップ生成VBScript

sitemapFile = "sitemap.md"
filePattern = "^.*\.md$"
docFolderName = "doc"

set shell = WScript.CreateObject("WScript.Shell")
set fs = CreateObject("Scripting.FileSystemObject")
set mapFile = fs.CreateTextFile(sitemapFile)

function main(rPath,vPath)
	set folder = fs.GetFolder(rPath)
	cPath = vPath
	if cPath <> "" then
		cPath = cPath & "/"
	end if

	set reg = New RegExp
	reg.Pattern = filePattern

	qty = Ubound(Split(cPath,"/"))

	folderSpc = ""
	if qty > 1 then
		folderSpc = space((qty-1)*4)
	end if

	fileSpc = ""
	if qty > 0 then
		fileSpc = space(qty*4)
		mapFile.WriteLine(folderSpc & "- " & folder.name)
	end if

	found = false
	for each file in folder.files
		if reg.Test(file.name) and file.name <> sitemapFile then
			mapFile.WriteLine(fileSpc & "- [" & file.name & "](#!" & docFolderName & "/" & cPath & file.name &")")
			found = true
		end if
	next

	for each sFolder in folder.SubFolders
		call main(sFolder.path,cPath & sFolder.name)
	next

end function

call main(shell.CurrentDirectory & "\" & docFolderName,"")

mapFile.Close

msgbox "Finish!"

サイトマップ生成結果(sitemap.md)

- Database
    - [mysql.md](#!doc/Database/mysql.md)
    - [oracle.md](#!doc/Database/oracle.md)
- IDE
    - [eclipse.md](#!doc/IDE/eclipse.md)
    - [netbeans.md](#!doc/IDE/netbeans.md)
- library
    - Java
        - [seasar.md](#!doc/library/Java/seasar.md)
        - [struts.md](#!doc/library/Java/struts.md)
    - JavaScript
        - [dojo.md](#!doc/library/JavaScript/dojo.md)
        - [jquery.md](#!doc/library/JavaScript/jquery.md)
        - [prototype.md](#!doc/library/JavaScript/prototype.md)

フォルダ/ファイル構成がかわる都度、実行する必要があるのでスマートじゃありませんが、いちいちマニュアル修正するよりはマシなので、とりあえずこれで良しとします。

必要な機能

実装するにあたり必要な機能を整理してみます。

  • location.searchよりドキュメントのURLを判定し、Ajaxで取得し表示する
  • location.hashよりドキュメントのURLを判定し、Ajaxで取得し表示する
  • 指定したURLよりドキュメントをAjaxで取得し表示する(サイトマップの表示に必要)
  • location.hashが変更されたら2.の処理をコールする

jQueryプラグインとして実装してみます。実行方法は以下のような感じ。

$(ドキュメントを表示する場所).exMarkdown();

表示したいドキュメントのURLが決まってる場合は以下のように実行(今回の例ではサイトマップ)。あと、サイトマップドロップダウンメニューにしたいので、callbackも指定できるようにします。

$(ドキュメントを表示する場所).exMarkdown({
	url : 'sitemap.md',
	callback : function(api){
		api.getTarget().find('> ul')...
	}
});

ソースです。

(function($){
	$.ex = $.ex || {};
	$.ex.markdown = function(idx , targets , option){
		if (isNaN(idx)) {
			return $('body').exMarkdown(idx);
		}
		var o = this,
		c = o.config = $.extend({} , $.ex.markdown.defaults , option);
		c.targets = targets;
		c.target = c.targets.eq(idx);
		c.index = idx;
		c.singleton = $.ex.markdown.singleton;
		if(!c.singleton.converter){
			c.singleton.converter = new Showdown.converter();;
		}
		if(c.url){
			o.rendar(c.url);
		}
		else{
			if(!c.singleton.bindHashChange){
				$(window).hashchange(function(){
					o.rendar();
				});
				c.singleton.bindHashChange = true;
			}
			o.rendar();
		}
	}
	$.extend($.ex.markdown.prototype, {
		getTarget : function(){
			var o = this, c = o.config;
			return c.target;
		},
		getHashUrl : function(){
			var o = this, c = o.config;
			var url = location.hash.replace(/^#!(.+)$/ig,'$1') || location.search.replace(/^\?url=(.+)$/ig,'$1');
			if(!url || !(/^.+\.(md|txt)$/ig.test(url))) return '';
			return url;
		},
		rendar : function(url){
			var o = this, c = o.config;
			url = url || o.getHashUrl();
			if(!url) return;
			var dummy = $('<a/>').prop('href',url);
			url = dummy.prop('href');
			dummy.remove();
			$.ajax({
				url : url,
				cache : false,
				dataType : 'text',
				success : function(text){
					var html = c.singleton.converter.makeHtml(text);
					c.target.html(html);
					!c.callback || c.callback.apply(o,[o]);
				},
				error : function(){
					c.target.html('not found : ' + url);
				}
			});
		}
	});
	$.ex.markdown.defaults = {
		url : '',
		callback : ''
	}
	$.ex.markdown.singleton = {
		bindHashChange : false
	}
	$.fn.exMarkdown = function(option){
		var targets = this,api = [];
		targets.each(function(idx) {
			var target = targets.eq(idx);
			var obj = target.data('ex-markdown') || new $.ex.markdown( idx , targets , option);
			api.push(obj);
			target.data('ex-markdown',obj);
		});
		return targets;
	}
})(jQuery);

実行

$('#contents').exMarkdown();
$('#nav').exMarkdown({
	url : 'sitemap.md',
	callback : function(api){
		api.getTarget().find('> ul').exDropDown({
			horizonRootMenu : true,
			horizonListWidth : 100
		});
	}
});
Demo
ダウンロード

ご自由にどうぞ

独り言

  • OAuth認証DropBox API使って、リアルタイムプレビューでオンライン編集できるようなサービス作りたいなぁ
  • hatena diaryもmarkdown対応してくれないかなぁ。置換がすごいめんどい・・・
  • hatana blogに移行しようかな・・

October 12, 2012

可変幅+固定幅なマルチカラムレイアウトをレスポンシブにする jQuery Ex Responsive

なにやら意味の分かりづらいタイトルですが、「可変幅+固定幅なマルチカラムレイアウト」とは、ネガティブマージンを使って、メインコンテンツを可変幅、サイドバーを固定幅にするここのブログのようなレイアウトの事を指してます。詳しくは以下をご覧ください。

ただこのレイアウトの場合、いわゆるカラム落ちなどを利用した今はやりの(?)レスポンシブなレイアウトにすることができません・・・という訳で jQuery の力でこれを可能にするプラグインを作ってみました。

使い方

準備

jquery.exresponsive.cssjquery.jsjquery.exresponsive.js を読み込みます。

<link rel="stylesheet" type="text/css" media="screen" href="jquery.exresponsive.css">
<script type="text/javascript" src="../jquery.js"></script>
<script type="text/javascript" src="../jquery.exresponsive.js"></script>

サンプルとして以下のような構成の HTML を使用します。

<div id="header"/>
<div id="main-nav"/>
<div id="main-contents">
    <div id="image"/>
    <div id="image-info"/>
</div>
<div id="sub-nav"/>
<div id="footer"/>

Demo

可変幅+固定幅の2カラムレイアウトにする(#main-contents + #sub-nav)

Demo

可変幅にしたい要素に対し、exResponsive() メソッドを実行します。固定幅にする要素はパラメータにて指定します。

// #main-contents を可変幅カラムに
$('#main-contents').exResponsive({
    fixTarget : 'next',     // #sub-nav を固定幅カラムに
    fixAlign : 'right',     // 固定幅カラムを右側に配置
    fixWidth : 200,         // 固定幅カラムを200pxに
    clearWidth : 416        // 可変幅カラムの幅が416px未満になったらカラム落ちさせる
});

ここでは fixTarget パラメータに "next" を指定してるので、#main-contents の1つ後ろにいる #sub-nav を固定幅カラムにすることになります。(直感的じゃないんで、セレクタや要素自体の指定でも大丈夫なようにするかもしれません・・・)

fixTarget
"prev" 又は "next"(必須)
固定幅にする要素が、指定要素に対し前方(prev)にあるのか後方(next)にあるのかを指定します。
fixAlign
"left" 又は "right"(必須)
固定幅にする要素を、左(left)または右(right)に配置するかを指定します。
fixWidth
固定幅にする要素の幅を指定します。(必須)
clearWidth
可変幅の最少幅を指定します。ウィンドウをリサイズしこの値未満の幅なろうとすると float が解除されカラム落ちします。
可変幅カラム内の要素を横並びにする(#image + #image-info)

Demo

先の例同様、適用したい要素に exResponsive() メソッドを実行します。

// #image-info を可変幅カラムに
$('#image-info').exResponsive({
    fixTarget : 'prev',     // #image を固定幅カラムにし
    fixAlign: 'left',       // 左側に配置する
    fixWidth: 416,
    clearWidth : 230
});
固定幅+可変幅+固定幅の3カラムレイアウトにする(#main-nav + #main-contents + #sub-nav)

Demo

exResponsive() メソッドを実行すると、可変幅要素と固定幅要素を包むラッパー要素ができあがります。3カラムレイアウトにする場合は2回 exResponsive() メソッドを実行し、2回目の実行ではこのラッパー要素を指定します。

実行前
<div id="main-nav"/>
<div id="main-contents"/>
<div id="sub-nav"/>
↓
1回目の実行(#main-contents + #sub-nav)
<div id="main-nav"/>
<div class="ex-responsive-wrap">    //ラッパー要素1
    <div id="main-contents"/>
    <div id="sub-nav"/>
</div>
↓
2回目の実行(ラッパー要素1 + #main-nav)
<div class="ex-responsive-wrap">    //ラッパー要素2
    <div id="main-nav"/>
    <div class="ex-responsive-wrap">    //ラッパー要素1
        <div id="main-contents"/>
        <div id="sub-nav"/>
    </div>
</div>
// 1回目
// #main-contents を可変
$('#main-contents').exResponsive({
    fixTarget : 'next',     // #sub-nav を固定
    fixAlign : 'right',
    fixWidth : 200,
    clearWidth : 420,
    callback : function(api){

        // 2回目
        // ラッパー要素を可変
        api.getWrapper().exResponsive({
            fixTarget : 'prev',     //#main-nav を固定
            fixAlign: 'left',
            fixWidth: 150,
            clearWidth : 870
        });
    }
});

#main-contents への exResponsive() メソッドでできあがるラッパー要素が #main-nav の隣接要素となるので、これに対し exResponsive() メソッドを実行します。ラッパー要素は callback 処理にて渡される api オブジェクトの getWrapper() メソッドで取得できます。

api オブジェクトは callback を使用しなくても、api パラメータに true を指定することで取得することもできます。

var api = $('#main-contents').exResponsive({api : true, ... });
api.getWrapper().exResponsive({...

あるいは、data() メソッドでデータ名 "ex-responsive" を指定することでも取得できます。

$('#main-contents').exResponsive({...});
var api = $('#main-contents').data('ex-responsive');
api.getWrapper().exResponsive({...
CSS適用してみる

exResponsive() メソッド適用するとラッパー要素以外にも、レイアウト調整に必要となる要素が自動で生成されます。

適用

<div id="main-contents">
    ...
</div>
<div id="sub-nav">
    ...
</div>

適用

<div class="ex-responsive-wrap">    //追加要素A(ラッパー要素)
    <div id="main-contents">
        <div class="ex-responsive-auto-inner">  //追加要素B
            ...
        </div>
    </div>
    <div id="sub-nav">
        <div class="ex-responsive-fix-inner">   //追加要素C
            ...
        </div>
    </div>
</div>

autoClassName(可変幅のクラス名)、fixClassName(固定幅のクラス名) パラメータで、上記の追加要素B,Cに対しクラス名を割り当てることができます。

$('#main-contents').exResponsive({
    autoClassName : 'main-contents',	//#main-contents のクラス名
    fixClassName : 'sub-nav',			//#sub-nav のクラス名
    fixTarget : 'next',
    fixAlign : 'right',
    fixWidth : 200,
    clearWidth : 420,
    callback : function(api){

        api.getWrapper().exResponsive({
            fixClassName : 'main-nav',  //#main-nav のクラス名
            fixTarget : 'prev',
            fixAlign: 'left',
            fixWidth: 150,
            clearWidth : 870
        });

    }
});

また、追加要素B,Cについてはカラム落ちしてるか否かの判断が付くように、以下のようなクラス名が付加されます。

  • カラム落ちしてる場合、クラス名 + "-off"
  • カラム落ちしてない場合、クラス名 + "-on"

各カラムに背景色を付け、カラム落ちしてる場合のみリストを横並びにしてみます。

.main-contents{
    background:#e8f0ff;
}
.main-nav{
    background:#f0ffe8;
}
.sub-nav{
    background:#f0e8f0;
}
.main-nav-off li{
    float:left;
    width:100px;
}
.sub-nav-off li{
    float:left;
    width:50%;
}

Demo

コンテンツのラッパー要素を追加する

先のサンプルをみるとカラム内のコンテンツに余白(margin)が無いため見栄えがあまりよくありません。追加要素B、Cに margin を指定したいところですがこれらの要素は内部的にレイアウト調整で使用しており、へたに margin を設定してしまうと画面構成によってはレイアウトが崩れてしまいます。仕方がないのでカラム内コンテンツに対しさらにラッパー要素を追加し margin の調整をしてみます。

autoContentsClassName、fixContentsClassName パラメータを指定すると、指定した値をクラス名とするラッパー要素が追加要素B,C内に生成されます。

$('#main-contents').exResponsive({
    autoContentsClassName : 'conntents-wrapper',    //#main-contents のラッパー
    fixContentsClassName : 'conntents-wrapper',     //#sub-nav のラッパー
    autoClassName : 'main-contents',
    fixClassName : 'sub-nav',
    fixTarget : 'next',
    fixAlign : 'right',
    fixWidth : 200,
    clearWidth : 420,
    callback : function(api){

        api.getWrapper().exResponsive({
            fixContentsClassName : 'conntents-wrapper',     //#main-nav のラッパー
            fixClassName : 'main-nav',
            fixTarget : 'prev',
            fixAlign: 'left',
            fixWidth: 150,
            clearWidth : 870
        });

    }
});

以下 CSS でカラム内コンテンツに margin を与えます。

.conntents-wrapper{
    margin:16px;
}

Demo

ウィンドウサイズを変えて表示を確認してみる

ウィンドウサイズに応じ、以下のよう表示が切り替わります。

Demo

モバイル端末からの見た目を確認してみる

その他のパラメータ

debug
false(初期値) 又は true
true にすると可変幅のサイズが表示されます。clearWidth パラメータの設定値を決める際に便利です。

その他の API メソッド

on()
clearWidth パラメータの設定に関わらず、マルチカラムレイアウトを有効にします。
off()
clearWidth パラメータの設定に関わらず、マルチカラムレイアウトを無効にします。
adjust()
clearWidth パラメータの設定を考慮し、マルチカラムレイアウトの有効化、無効化を行います。

動作確認

下記環境で動作確認をしてます。

ダウンロード

こちらからどうぞ

April 27, 2012

一定範囲で position:fixed させる Ex Flex Fixed がかゆいとこだらけだったので作り直しました

かゆいとこだらけの Ex Flex Fixed (サイドメニューなどを一定の範囲でのみ position:fixed させる jQuery Ex Flex Fixed)を作り直しました。

改善内容

  • fixed 要素の幅を自動調整するようにした
  • リキッドレイアウト(可変幅)でちゃんと表示するようにした
  • fixed 要素がコンテナからはみ出ないようにした
  • fixed 要素がウィンドウ高さに収まらない状態でのスクロール時の表示を見やすくした
  • 動的にレイアウトサイズが変更されても、ちゃんと表示されるようにした
  • カラム高さを揃えるプラグインと併用できるようにした

という訳で半年も放置しておいてあれですが、ちょくちょく問い合わせもあるので、ご利用の際には新バージョンでどうぞ。

動作確認

以下のレイアウトのデモページで、新旧それぞれのバージョンの動作を確認してみます。

f:id:cyokodog:20120426201437p:image

<div class="header"></div>
<div class="contents-wrapper">
	<div class="sub-contents-wrapper">
		<div class="sub-contents">
			sub contents
		</div>
	</div>
	<div class="main-contents-wrapper">
		<div class="main-contents">
			main contents
		</div>
	</div>
	<div class="ext-contents-wrapper">
		<div class="ext-contents">
			ext contents
		</div>
	</div>
</div>
<div class="footer"></div>
/* 全体をセンタリング */
body{
	margin :0 auto;
	max-width : 1000px;
	min-width : 600px;
}

/* clearfixでwrapperに高さを出す */
div.contents-wrapper{
	/zoom : 1;
}
div.contents-wrapper:after{
	content : '';
	display : block;
	clear : both;
}

/* subを固定幅 */
div.sub-contents-wrapper{
	margin-right : -200px;
	float : left;
	width : 200px;
	position : relative;
	z-index:1;
}
/* extを%指定可変幅 */
div.ext-contents-wrapper{
	margin-left : -30%;
	float : left;
	width : 30%;
	position : relative;
	z-index:1;
}
/* margin,padding,borderをつける */
div.sub-contents,
div.ext-contents{
	margin:16px;
	padding:8px;
	border:solid 4px #ccc;
}

/* メインコンテンツを可変幅 */
div.main-contents-wrapper{
	float : left;
	width:100%;
	position : relative;
}
div.main-contents{
	margin:16px;
	margin-left : 216px;
	margin-right : 31.5%;
	width : auto;
	padding:8px;
	border:solid 4px #ccc;
}

/* wpapper はblue系 */
div.contents-wrapper{
	background:#c0e0ff;
}
div.main-contents-wrapper{
	background:#b0d0ff;
}
div.sub-contents-wrapper,
div.ext-contents-wrapper{
	background:#a0c0f0;
}
/* contents はgray系 */
div.main-contents{
	background:#e8e8e8;
}
div.sub-contents,
div.ext-contents{
	background:#f0f0f0;
}

デモでは div.sub-contents、div.ext-contents を固定表示にします。

$('div.sub-contents,div.ext-contents').exFlexFixed({
	container : 'div.contents-wrapper'
});

デモ画面では、「Ex Flex Fixedを適用する」ボタンをクリックすると実行されます。

fixed 要素の幅を自動調整するようにした

通常 position:fixed を適用すると、内包物に合わせて幅が縮小しますが、position:static のように親要素に合わせた幅調整がされます。

f:id:cyokodog:20120426201438p:image

Demo(旧) Demo(新)

任意の幅に固定したい場合は、width パラメータを指定します。

$('div.sub-contents,div.ext-contents').exFlexFixed({
	container : 'div.contents-wrapper',
	width : 80
});

リキッドレイアウト(可変幅)でちゃんと表示するようにした

旧バージョンでは、ウィンドウのリサイズで左カラムの表示位置がずれてしまいましたが、ずれないよう修正。

f:id:cyokodog:20120426201439p:image

Demo(旧) Demo(新)

fixed 要素がコンテナからはみ出ないようにした

メインコンテンツの内容が少ないと、コンテナの高さがでず fixed 要素がはみ出してしまいましたが、はみ出さないよう修正。

f:id:cyokodog:20120426201440p:image

Demo(旧) Demo(新)

fixed 要素がウィンドウ高さに収まらない状態でのスクロール時の表示を見やすくした

fixed 要素が大きくウィンドウ高さに収まらない状態でスクロールした場合、fixed 要素の全体を表示した後、位置固定をしますが、旧バージョンでは、上方向に向かってスクロールした場合、位置固定後のスクロールとなってしまい閲覧性がいまいちだったので修正。

f:id:cyokodog:20120426201441p:image

Demo(旧) Demo(新)

上下にスクロールし、右カラムの表示のされ方を確認してください。

動的にレイアウトサイズが変更されても、ちゃんと表示されるようにした

旧バージョンでも(固定レイアウトに限り) watchPosition パラメータを指定する事で、ある程度対応できてましたが不完全でしたので修正。

$('div.sub-contents,div.ext-contents').exFlexFixed({
	container : 'div.contents-wrapper',
	watchPosition : true
});

f:id:cyokodog:20120426201442p:image

Demo(旧) Demo(新)

ダミーテキストを、×で削除、+で追加することでレイアウトサイズを変更してみてください。

カラム高さを揃えるプラグインと併用できるようにした

Ex Equal Height という動的にレイアウトサイズが変更された場合でも、カラム高さを揃える事のできるプラグインを作りましたので、これを併用してみます。

$('div.contents-wrapper > div').exEqualHeight({
	watchHeight : true
});
$('div.sub-contents,div.ext-contents').exFlexFixed({
	container : 'div.contents-wrapper',
	watchPosition : true
});

f:id:cyokodog:20120426201436p:image

Demo

exEqualHeight() メソッド実行時に、watchHeight パラメータを指定することで、動的なレイアウトサイズ変更にも対応させることができます。ただこの場合、内部的に setTimeout による監視が、exEqualHeight、exFlexFixed それぞれで行われることになるので、リソースや処理タイミングが気になる場合は、以下のような記述で調整処理をまとめることができます。

var height_API = $('div.contents-wrapper > div').exEqualHeight({
	api : true,
	watchHeight : false,
	windowResizeAdjust : false
});
$('div.sub-contents,div.ext-contents').exFlexFixed({
	container : 'div.contents-wrapper',
	watchPosition : true,
	watchCallback : function(fixed_API){
		height_API.adjust();
	}
});

Demo

exEqualHeight を非監視状態で実行、API オブジェクトを取得し、exFlexFixed の監視処理のコールバックにて、リサイズ判定+高さ調整を実行してます。

パラメータ

下記パラメータがあります。

api
初期値:false
true にすると api オブジェクトを返します。
watchPosition
初期値:'auto'
watchPosition パラメータ、windowResizeAdjust パラメータが共に auto の場合 false になります。true の場合、レイアウトの監視とサイズ・位置調整を行います。
watchDelay
初期値:500
指定値の間隔(ms)で監視処理を行います。
watchCallback
初期値:function(api){}
監視処理のコールバック関数を指定できます。apiapi オブジェクトが渡されます。
container
初期値:null
必須パラメータ。position:fixed 適用範囲となるコンテナ要素を指定します。
width
初期値:'auto'
position:fixed の幅を指定します。auto の場合は自動調整されます。
windowResizeAdjust
初期値:'auto'
watchPosition パラメータが true で且つ windowResizeAdjust パラメータが auto の場合 false になります。ウィンドウリサイズ時にサイズ・位置調整を行います。
windowResizeDelay
初期値:100
ウィンドウのリサイズイベントの発生判定間隔(ms)を指定します。

API

下記メソッドがあります。

adjustPosition()
レイアウトサイズ変更時に行われるサイズ・位置調整処理を行います。
adjustScrollPosition()
スクロール時に行われる位置調整処理を行います。

ダウンロード

こちらからどうぞ

September 04, 2011

サイドメニューなどを一定の範囲でのみ position:fixed させる jQuery Ex Flex Fixed

更新履歴

2012-04-27
Ver 0.2.0
再実装し、もろもろ痒いとこを解消しました。詳しくは下記エントリをどうぞ。
一定範囲で position:fixed させる Ex Flex Fixed がかゆいとこだらけだったので作り直しました
2012-02-09
Ver 0.1.3.1
Firefox の場合、watchPosition パラメータ が true でスクロール状態で画面更新すると固定位置がずれる不具合を修正しました。
IE の場合、水平方向非固定処理が有効にならないケースがある不具合を修正しました。
2011-12-31
Ver 0.1.3
固定表示する要素の位置が変化するケースにも対応しました。 Demo
2011-11-05
Ver 0.1.2
IEで、固定表示する要素のmarginがautoの場合、正しく処理されない不具合を修正しました
2011-10-06
Ver 0.1.1
スクロール開始時に対象要素の表示位置が1pxずれる不具合を修正しました。
スクロールされた状態で、画面をリロードすると対象要素の表示位置がずれる不具合を修正しました。

サイドメニューの固定表示に position:fixed を適用するというケースがあると思いますが、表示位置が常に固定される故に、スクロール時にメニューがフッターに被らないようデザインしたり、ウィンドウの表示枠に収まるメニュー数に調整したりと、デザイン上の配慮がいろいろ必要になったりします。

jQuery Ex Flex Fixed を使用するとこういった事を極力気にせず、position:fixed を適用することができます。(IE6 には対応してません・・面倒だったので・・)

使い方

jquery.jsjquery.exflexfixed.js を読み込みます。

<script type="text/javascript" src="../jquery.js"></script>
<script type="text/javascript" src="../jquery.exflexfixed.js"></script>

以下のようなレイアウトで、サイドメニューを固定する場合、

<div class="header">header</div>
<div class="body">
	<div class="sub-contents">
		<ul class="sidemenu">
			<li>link</li>
			<li>link</li>
			<li>link</li>
		</ul>
	</div>
	<div class="main-contents">
		main
	</div>
</div>
<div class="footer">footer</div>

サイドメニュー要素である ul.sidemenu に対し、exFlexFixed() メソッド適用し、container パラメータに、fixed を有効にする範囲となる div.body 要素を指定します。

jQuery(function($){
	$('ul.sidemenu').exFlexFixed({
		container : 'div.body'
	});
});

Demo

特徴

ヘッダー領域の活用

通常の position:fixed では以下のように、垂直方向にスクロールした際、ヘッダーのあった領域にスペースができてしまいます。

f:id:cyokodog:20110903103308p:image

Ex Flex Fixed を適用すると、スクロール時にヘッダー領域のスペースがある状態では、position:fixed が適用されないので、スペースを無駄にしません。

f:id:cyokodog:20110903103309p:image

Demo

フッター領域に被らない

通常の position:fixed では、垂直方向のスクロールをしてくと以下のように position:fixed 要素がフッター領域に被ってしまいます。

f:id:cyokodog:20110903103310p:image

Ex Flex Fixed を適用するとフッター領域に被る直前で position:fixed の適用が解除され、フッターに被りません。

f:id:cyokodog:20110903103311p:image

Demo

ウィンドウ高さを超えるメニュー数でも表示可能

メニュー数が多く、ウィンドウの表示領域以上の高さがある状態でスクロールした場合、最下段のメニューが表示されるまで position:fixed が適用されません。

Demo

水平方向スクロールでは固定されない

水平方向にスクロールした場合は position:fixed は適用されないので、メニューがコンテンツに被ることはありません。

Demo

コンテンツ領域からの相対位置での固定表示

marign:0 auto などでコンテンツをセンタリング表示してるデザインでは、ウィンドウをリサイズするとコンテンツの X 座標が変化しますが、コンテンツの相対位置で固定表示されるので、ウィンドウをリサイズしても表示位置がずれません。

Demo

fixed させておきたい範囲を指定可能

fixed させておきたい範囲の高さを持つ親又は先祖要素を container パラメータにて指定します。

Demo

jQuery Ex DropDown との併用

Ex DropDown(リスト要素をドロップダウンメニューに変換する jQuery プラグインを作ってみた - Cyokodog::Diary)と併用するには、以下のように記述します。

jQuery(function($){
	$('ul.sidemenu').exDropDown().exFlexFixed({
		container : 'div.body'
	});
});

f:id:cyokodog:20110903233336p:image

Demo

固定表示要素の位置が変化する場合の対応

例えば、上方に配置された要素のサイズが変化し、固定表示要素の位置が変化するような場合は、以下のような記述で固定表示位置を補正できます。

jQuery(function($){
	$('ul.sidemenu').exDropDown().exFlexFixed({
		watchPosition : true,
		container : 'div.body'
	});
});

Demo

watchPosition には ms 単位の数値を指定することもできます。この場合は指定の数値間隔で表示位置の変化を検出します。true を指定した場合は、300ms で監視します。

ダウンロード

こちらからどうぞ

June 22, 2011

jQuery ColorBox を フォーム入力用にカスタマイズした Ex Colorbox Form

更新履歴

2011-10-20
Ver 0.1.3
Ver 0.1.2の fastIframe パラメータ関連の不具合を修正しました。fastIframe パラメータ未定義の旧 colorbox にも対応してます。
2011-10-13
Ver 0.1.2
ColorBox1.3.18に対応しました。
2011-10-08
Ver 0.1.1
jQuery1.6.4に対応しました。

LightBox 系プラグイン の中でも人気の高い jQuery ColorBox をフォームの入力画面用にカスタマイズしてみました。仕事柄、画像の表示を主目的とする LightBox 系のプラグインを使用することはあまりないのですが、iframe に対応してるものも多く、登録系の画面が主となる業務アプリでも使えそうだなぁと思い ColorBox をベースにちょっと作ってみました。(ちなみにオリジナル の colorbox のソースはいじってません)

POST で画面を遷移してくような画面でのブクマを抑止でき、登録完了後の親画面へのコールバック処理等もできるので、手軽な割には結構便利かとな思ってます。

機能概要

jQuery ColorBox をフォームの入力画面向きに使用することができます。

使い方

jquery.js、colorbox.cssjquery.colorbox.jsjquery.excolorboxform.js を読み込みます。

<link rel="stylesheet" type="text/css" href="colorbox.css"/>
<script type="text/javascript" src="../jquery.js"></script>
<script type="text/javascript" src="../jquery.colorbox.js"></script>
<script type="text/javascript" src="../jquery.excolorboxform.js"></script>

a、form、submit ボタンのいずれかに対し、exColorboxForm() メソッドを実行します。

jQuery(function($){
	$('a').exColorboxForm();

	//又は
	$('form').exColorboxForm();

	//又は
	$('input[type=submit]').exColorboxForm();
});

ColorBox では5種類のデザインが用意されており、Ex Colorbox Form でもそれぞれに対応してます。

f:id:cyokodog:20110622220803p:image

Demo : [A] [B] [C] [D] [E]

閉じるアイコンの位置

ColorBox の[A][D][E]デザインでは、閉じるアイコンがボックスの右下に配置されてますが、[A][D]についてのみ右上に配置されるよう調整されます。([E]はデザインが崩れてしまうのでそのままにしてます) これは Windows アプリケーションでは右上の閉じるアイコンが常識のため、右下に地味に配置されたアイコンが見つけずらいとの意見を受け、このようにしてます。

f:id:cyokodog:20110622220804p:image

オリジナルのように右下に配置したい場合は、closeIconTopPosition パラメータに false を指定して実行します。

$('a').exColorboxForm({closeIconTopPosition:false});
フォーム画面内での閉じるボタン

通常、ウィンドウを閉じるには、前述の閉じるアイコンをクリックする必要がありますが、登録処理の完了後にウィンドウの画面内に閉じるボタンを配置したい場合があるかと思います。以下のように colorbox-close クラスを割り当てたボタンを配置することで、ウィンドウを閉じることができるようになります。

<button class="colorbox-close">閉じる</button>

f:id:cyokodog:20110622220805p:image

closeButtonClass パラメータでクラス名を変更することができます。

ウィンドウ高さの伸縮

フォーム画面のコンテンツサイズに合わせウィンドウの高さが伸縮します。コンテンツサイズが親画面のウィンドウサイズを超える場合は、親画面のウィンドウと同じ高さになり、スクロールバーが表示されます。

fitContentsHeight パラメータに false を指定すると、サイズ調整は行われません。

閉じるアイコンクリック時の確認メッセージ

以下のように記述すると、閉じるアイコンクリックした際、実行確認メッセージを表示することができます。

$('a').exColorboxForm({
	onLoad : function(api){
		api.getCloseIcon().click(function(){
			return confirm('画面を閉じてよろしいですか?');
		});
	}
});

onLoad コールバックにて、API オブジェクトを受け取り、getCloseIcon() メソッドで取得した閉じるアイコンに対し、クリックイベントを割り当てています。クリックイベントの返却値を false にすることで、閉じる処理をキャンセルできます。

API オブジェクトは他のコールバック関数(onComplete や onClosed など)でも同様の方法で取得できます

f:id:cyokodog:20110622220806p:image

Demo : [A] [B] [C] [D] [E]

上記の記述により、colorbox-close クラスを割り当てた閉じるボタンをクリックした場合も、同様に確認メッセージが表示されます。これを抑止した場合は、closeButtonSyncCloseIcon パラメータに false を指定します。

確認メッセージに jQuery Alert Dialogs を使用する

正確には jQuery Alert Dialogs をカスタマイズした Ex Alerts Dialogs を使用します。

$('a').exColorboxForm({
	onLoad : function(api){
		api.getCloseIcon().exJConfirm('画面を閉じてよろしいですか?','終了確認');
	}
});

先程と同様に、getCloseIcon() メソッドで取得した閉じるアイコンに exJConfirm() メソッドを割り当てます。

f:id:cyokodog:20110622220807p:image

Demo : [A] [B] [C] [D] [E]

最後の画面では閉じる確認を表示しないようにする

入力途中で閉じようとした場合のみ確認メッセージを表示するようにしてみます。

var jconAPI;
$('a').exColorboxForm({
	onLoad : function(api){
		jconAPI = api.getCloseIcon().exJConfirm('登録は完了してません。\n画面を閉じてよろしいですか?','終了確認',{
			api : true
		});
	},
	onComplete : function(api){
		var isLastPage = !!api.getContents().find('.colorbox-close').size();
		jconAPI.disabled(isLastPage);
	}
});

exJConfirm() メソッド実行時、api パラメータを true にし Alert Dialogs の API オブジェクトを取得しておき、Colorbox のフォームの表示が完了後に起動される onComplete コールバックにて、最終画面か否かを判断し(ここでは閉じるボタンの有無で判定)、Alert Dialogs の disabled() メソッドにて確認メッセージの表示を抑止しています。

Demo : [A] [B] [C] [D] [E]

ちなみに onComplete 内の getContents() メソッドでは iframe 内のドキュメントが取得できます。iframe 関係のアクセスについてはこちらを参照ください。

登録開始時に確認メッセージを表示する

登録を開始したタイミングでDBの更新処理を行うようなケースでは、登録開始の確認メッセージが必要になるかと思います。そのような場合は以下のように記述します。

var jconAPI;
var option = {
	onLoad : function(api){
		jconAPI = api.getCloseIcon().exJConfirm('登録は完了してません。\n画面を閉じてよろしいですか?','終了確認',{
			api : true
		});
	},
	onComplete : function(api){
		var isLastPage = !!api.getContents().find('.colorbox-close').size();
		jconAPI.disabled(isLastPage);
	}
}

$('a').exJConfirm('登録を開始しますか?','開始確認')
	.exColorboxForm(option);

$($('input[type=submit]').exJConfirm('登録を開始しますか?','開始確認').attr('form'))
	.exColorboxForm(option);

$('form').exColorboxForm(option)
	.find('input[type=submit]').exJConfirm('登録を開始しますか?','開始確認');

f:id:cyokodog:20110622220808p:image

Demo : [A] [B] [C] [D] [E]

これまでと異なり、プラグイン適用する要素の種類によって書き方が異なります。(exColorboxForm のパラメータ指定は別定義にしてますが、先程までの例と同じ内容です)

まず適用対象が a 要素の場合ですが、単純に exJConfirm() と exColorboxForm() をメソッドチェーンでつなげて実行すればOKです。

それ以外の場合は、ColorBox の呼び出しのトリガーとなるボタンに対し exJConfirm() メソッド、form に対し exColorboxForm() メソッド適用する必要があります。

登録が完了したら親画面を更新する

登録が完了した事により、親画面に表示された情報を最新にするというケースはよくあると思います。単純な方法として location.reload() でページを更新しても良いですが、スクロールが発生する親画面などの場合は、Ajax で部分更新っていうのもありがちかと思います。ここでは単純に登録開始のボタンを登録完了メッセージに置換してみます。

var jconAPI,isLastPage = false;
var option = {
	onLoad : function(api){
		jconAPI = api.getCloseIcon().exJConfirm('登録は完了してません。\n画面を閉じてよろしいですか?','終了確認',{
			api : true
		});
	},
	onComplete : function(api){
		isLastPage = !!api.getContents().find('.colorbox-close').size();
		jconAPI.disabled(isLastPage);
	},
	onClosed : function(api){
		if (isLastPage) {
			api.getTarget().replaceWith('<p>登録完了</p>');
		}
	}
}

f:id:cyokodog:20110622220802p:image

Demo : [A] [B] [C] [D] [E]

プラグイン適用の記述は先程と同じなので省略しています。ColorBox が閉じた時に実行される onClosed コールバックにて最終ページに遷移した状態で画面を閉じたか判定します。この判定結果を保持するため isLastPage 変数の定義を外だしにしてます。

最終ページまで遷移されたと判定した場合は getTarget() メソッドにより exColorboxForm() メソッド適用した要素を取得し、replaceWidth() にて登録完了メッセージに置換してます。

パラメータ

基本、ColorBox のパラメータを指定できますが、一部初期値を上書きし、独自パラメータも追加されてます。

fitContentsHeight
初期値 : true
ColorBox 内のコンテンツがリロードされる度、ColorBox の高さを調整します。
adjustContentsBackgroundColor
初期値 : #fff
デザイン[B][C]を適用した際、呼び出し先のコンテンツの背景色が transparent の場合、黒で塗りつぶされてしまうのを指定色で抑止します。
contentsMargin
初期値 : 32
コンテンツに対する高さの margin を指定できます。
closeIconTopPosition
初期値 : true
デザイン[B][D]において、閉じるアイコンを ColorBox の右上に配置します。
closeIconTopMargin
初期値 : true
デザイン[B][D]において、closeIconTopPosition が true の場合、閉じるアイコンとコンテンツの間の margin を指定できます。
closeButtonClass
初期値 : ".colorbox-close"
コンテンツ内に指定クラス名を割り振ったボタン要素を配置すると、ColorBox の閉じるボタンにすることができます。
closeButtonSyncCloseIcon
初期値 : true
closeButtonClass パラメータで指定したクラス名を持つボタンをクリック時、閉じるアイコンのクリックイベントを起動します。
close
初期値 : "[X]"
デザイン[D]の閉じるアイコンのテキストを指定できます。
overlayClose
初期値 : false
true の場合、ColorBox 枠の外側をクリック時、ColorBox が閉じます。
escKey
初期値 : false
true の場合、ESC キー入力時、ColorBox が閉じます。
width
初期値 : "750px"
ColorBox の幅を指定できます。
height
初期値 : "80%"
ColorBox の高さを指定できます。fitContentsHeight が true の場合はコンテンツロード時に調整されます。
show
初期値 : false
プラグイン適用時に、ColorBox の表示を行います。

API

API オブジェクトを使用し、以下メソッドを使用することができます。API オブジェクトは onClosed 等のコールバック関数の第一引数に引き渡されます。

getTarget()
プラグイン適用した要素を取得できます。
getContents()
コンテンツを取得することができます。(iframe.contents())
getCloseIcon()
閉じるアイコンを取得することができます。
getFrame()
コンテンツを表示してる iframe 要素を取得することができます。
show()
ColorBox を表示することができます。

ダウンロード

こちらからどうぞ

ダウンロードしたサンプル HTMLローカル環境(file://)で実行すると FirefoxChrome の場合 Colorbox ウィンドウのリサイズ処理が正常に行われませんので、Webサーバ上(http://)でご確認ください。

June 20, 2011

jQuery Ex InPlace Editor に表示テキストの整形機能を追加しました

更新履歴

2011-07-05
Ver 0.1.5.1
リスト編集モードで値未入力時のラベル表示不具合等を修正しました
2011-06-20
Ver 0.1.5
表示テキストの整形処理を記述できる replaceLabel パラメータを追加しました。

前回のエントリで、編集データとは別に表示モードでのテキストを書き換えたいとのご要望がありましたので、機能追加しました。

replaceLabel パラメータに記述した関数が、編集確定処理のコールバック関数となり、置換したい値を返すことで表示テキストの書き換えができるようになります。以下の例では数字を3桁単位でカンマ編集し、¥マークを付けた値に編集してます。値が入力されてない場合は、”金額を入力してください”と表示してます。

var commify = function(text){
	var reverseText = function(str){
		return str.split('').reverse().join('');
	}
	return reverseText(reverseText(text).replace(/(\d{3})(?=\d)(?!\d*\.)/g,'$1,'))
}
$('input').exInPlaceEditor({
	replaceLabel : function(api){
		var ret = commify(api.getValue()); //編集内容を取得し整形
		return ret ? '¥' + ret : '金額を入力してください';
	}
});

f:id:cyokodog:20110621020319p:image

Demo(input に適用) Demo(a に適用)

ダウンロード

こちらからどうぞ

April 21, 2011

jQuery exInPlace Editor にリスト編集モード等の機能追加をしました

更新履歴

2011-07-05
Ver 0.1.5.1
リスト編集モードで値未入力時のラベル表示不具合等を修正しました
2011-04-29
Ver 0.1.4.1
編集値の変換処理の不具合を修正しました

久しぶりのエントリです。

仕事でちょっと必要になったので、以前紹介した In-Place-Editor の jQuery プラグインIn-Place-Editor (その場で編集する UI) の jQuery プラグインを書いてみた - Cyokodog::Diary)に機能追加をしました。

リスト形式での編集機能

テキストエリア内の箇条書きをリスト形式に変換します。

TEXTAREA 要素にプラグイン適用する場合

テキストエリアに対し、convertCR パラメータに li を指定して exInPlaceEditor() メソッド適用します。

<textarea>
jQuery()
jQuery.noConflict()
jQuery.sub()
jQuery.when()
</textarea>
$('textarea').exInPlaceEditor({
    convertCR : 'li'
});

テキストエリア内の改行が li 要素に変換されるようになります。

f:id:cyokodog:20110421103721p:image

Demo

UL 要素にプラグイン適用する場合

ul 要素に exInPlaceEditor() メソッド適用します。

<ul>
    <li>jQuery()</li>
    <li>jQuery.noConflict()</li>
    <li>jQuery.sub()</li>
    <li>jQuery.when()</li>
</ul>
$('ul').exInPlaceEditor();

テキストエリアによる編集が可能になります。改行が li 要素に変換されます。

f:id:cyokodog:20110421103813p:image

Demo

HTML 編集モードにおける自動改行

htmlEditor パラメータに true を指定すると HTML 編集モードになる機能があります。このモードの場合、純粋な HTML の入力モードのため p 要素や br 要素等の改行も意識して入力する必要がありました。

いざ使ってみるとこれが結構面倒だったので、改行コードを convertCR パラメータで指定した要素(br 又は p)に自動置換するようにしました。

自動置換したくない場合は、htmlEditorAutoConvertCR パラメータに false を指定します。

Demo

ダウンロード

こちらからどうぞ

February 17, 2011

Caps Lock や ime-mode を設定する jQuery exCapsLock

タイトルからも想像できますが、地味な jQuery プラグインです。毎度、似たような記述をするのが面倒だったのでプラグイン化してみました。

機能概要

INPUT もしくは TEXTAREA 要素のキー入力で、大文字のみに入力制限したり、キー入力を漢字モードにすることができます。

実装方法

実装は非常に単純で、対象要素に対し CSS の text-transform と ime-mode を当ててるだけです。ただ、text-transform については、見た目は大文字になりますが、内部的には実際入力した値を保持してしまうので、対象要素の blur イベント内で toUpperCase() して大文字変換しています。

ちなみに ime-mode は IE と Firefox3 以降でしか対応してないので、漢字入力モードはその他ブラウザでは適用されません。

使い方

jquery.jsjquery.excapslock.js を読み込みます。

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.excapslock.js"></script>

大文字入力にしたい場合は、以下のように記述します。

$('input:text').exCapsLock();

漢字入力モードにしたい場合は、以下のように記述します。

$('textarea').exCapsLock({
	capsLock : false,
	imeMode : 'active'
});

f:id:cyokodog:20110217091336p:image

Demo

パラメータ

以下パラメータがあります。

api
初期値 : false
true にすると API オブジェクトを返します。
capsLock
初期値 : true
大文字のみの入力に制限します。
imeMode
初期値 : auto
以下値が指定できます。
  • auto : 何もしない
  • active : 日本語入力モード
  • inactive : 英数字入力モード
  • disabled : 英数字入力モードで且つユーザによるモード変更不可

API

API オブジェクトを使用し、以下メソッドを使用することができます。

capsLockOn()
大文字のみの入力に制限します。
setImeMode(imeMode)
ime-mode のパラメータを指定できます。

ダウンロード

こちらからどうぞ

January 25, 2011

FORM の登録確認のダイアログ表示に jQuery Alert Dialogs を使う方法

jQuery Ex Alert Dialogs の更新履歴

2011-06-07
Ver 0.1.2
各種パラメータの指定、API オブジェクトの取得をできるようにしました。

毎度ながら地味なネタですが、FORM 要素で登録確認のダイアログを表示する際、jQuery Alert Dialogs で代替えする方法について書いてみたいと思います。ちなみに jQuery Alert Dialogs とは、独自デザインなダイアログを表示してくれる jQuery プラグインです。

confirm 関数で登録確認ダイアログを表示する

FORM の SUBMIT ボタンクリック時に、登録確認のダイアログを表示させる場合、通常以下のような書き方で confirm 関数を使用するかと思います。

$('input.entryBtn').click(function(){
    return confirm('登録しますか?');
});

confirm 関数により、サブミット前にユーザの OK、CANCEL の入力待ち状態となり、OK を選択すれば true が返りそのままサブミットされ、CANCEL なら false となりサブミットもキャンセルされるという処理になります。

f:id:cyokodog:20110125233413p:image

Demo

confirm 関数の代わりに jQuery Alert Dialogs を使用する

jQuery Alert Dialogs で、同じように SUBMIT 前に登録確認のダイアログを表示しようとした場合、以下のように少し工夫した書き方が必要になります。

var confirmOk = false;
$('input.send').click(function(){
    var button = $(this);
    if (confirmOk || jConfirm('登録しますか?', '登録確認',function(isOk){
        if (isOk) {
            confirmOk = true

            //再クリック
            button.click();
        }
    }));
    //1度目はfalse、ダイアログではいを選んだ場合は、再度、起動され true となる
    return confirmOk;
});

jQuery Alert Dialogs の場合では、confirm 関数のようにユーザの入力処理を待ってくれないので、一旦、SUBMIT 処理をキャンセルした後、jConfirm 関数でダイアログを表示し、OK を選択した時のみ、サブミット処理を起動するようにする必要があります。この時、サーバサイドで何のボタンによりサブミットされたかを判別できるよう、対象ボタンの CLICK イベントでサブミットさせる必要があります。

f:id:cyokodog:20110125233441p:image

Demo

プラグイン化してみる

単純な処理ですが、毎回書くのも面倒なのでプラグイン化してみました。オリジナルのソースは変更せず設定値やイベント等の上書きをしています。下記点がオリジナルと異なります。(jAlert、jPrompt 関数にも適用されます)

  • オーバーレイの背景色を #555、透過度を 0.5 にしています
  • OK、CANCEL ボタンの表示を はい、いいえ にしています
  • ダイアログボタンに exButton(CSS1 のみで質感のあるボタンを表現してみる - Cyokodog::Dialy)が適用されます
  • CANCEL ボタンにフォーカスした状態で、エンターキーを押した時、callback 関数の引数に ture が渡されてしまう不具合(?)っぽい動きを解消してます(falseが渡されます)
  • 入力フィールドにフォーカスした状態での、エンターキーによる自動サブミットを抑止します
使い方

jquery.jsjquery.alerts.cssjquery.alerts.jsjquery.exalertdialogs.js を読み込みます。(exButton を適用する場合は exbutton.css も読み込みます)

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.alerts.js"></script>
<script type="text/javascript" src="jquery.exalertdialogs.js"></script>

適用したいサブミットボタンに対し、exJConfirm() メソッドを実行します。

$('input.send').exJConfirm(
    '登録しますか?',   //メッセージ
    '登録確認'          //ダイアログの見出し
);

f:id:cyokodog:20110125233502p:image

Demo

登録確認ダイアログ表示前に入力チェックを行う

第3引数にコールバック関数を指定することで、preCallback パラメータに、ダイアログ表示前の処理を記述することができます。コールバック関数の返却値を false にすることで、登録確認ダイアログの表示を抑止できます。以下記述では登録処理前に各項目の必須入力チェック($.fn.requirdCheck)を行い、エラーのあった項目に error クラスを割り当てた後、カーソルをフォーカスさせてます。

//必須入力チェック
$.fn.requirdCheck = function(name){
    var target = this;
    if (!target.val().length) {
        target.addClass('error');
        jAlert(name + 'が入力されてません','入力エラー',function(){
            target.focus();
        });
        return false;
    }
    return true;
}
$('input.send').exJConfirm(
    '登録しますか?',
    '登録確認',{
        preCallback : function(){
            $($('input.send').attr('form')).find('.error').removeClass('error')
            return  $('input.name').requirdCheck('Name') &&
                    $('input.url').requirdCheck('URL') && 
                    $('textarea.comment').requirdCheck('Comment');
        }
    }
);

f:id:cyokodog:20110125233539p:image

Demo

(基本的に使用しないかと思いますが)第4引数にコールバック関数を指定した場合は、登録確認ダイアログで はい か いいえ を選択した後の処理として実行されます。ダイアログ表示前コールバック関数と同様に、返却値を false にすることでサブミットをキャンセルすることができます。

パラメータ

api
初期値:false
true にすると api オブジェクトを返します。
disabled
初期値:false
true にすると 確認ダイアログを表示しません。
preCallback
初期値:function(){}
ダイアログ表示前のコールバック関数を記述できます。
返却値を false(return false)とするとダイアログを非表示にできます。
callback
初期値:function(){}
ダイアログで「はい」を選択した場合のコールバック関数を記述できます。
返却値を false(return false)とすることで、後続のイベントをキャンセルできます。

API

getTarget()
プラグイン適用対象要素を返します。
disabled(true | false)
true でダイアログの表示、false で非表示となります。
setMessage( text )
ダイアログに表示するメッセージを変更できます。
setTitle( text )
ダイアログの見出しを変更できます。

ダウンロード

こちらからどうぞ

最後に

結婚前の自由なお金で購入した VAIO PCV-RX56 もそろそろ10年目・・・そんなわけで

MacBook Air 11インチ欲しい!

今日が締切ということで・・・早くスマートフォンが当たり前の時代がこないかな

December 13, 2010

テーブルのセル間をフォーカス移動できるようにする jQuery exTable Focus

先日、現物とドキュメントの乖離に嫌気がさし、テーブル定義書の和名や説明文を、DBのメタ情報として保存できるようにする Web アプリを作りました。(オラクルのデータディクショナリを利用してます)

テーブル定義書イメージな画面から Ajax + In-Place-Editor で直接編集できるようにしたのですが、編集量が多い時など、キー操作による上下左右のフォーカス移動ができないとどうにも不便だったので、これを可能にする jQuery プラグインを書いてみました。

機能概要

テーブル要素内の INPUT、TEXTAREA、SELECT、A のいづれかの要素にフォーカスした状態で、[Shift] + 移動したい方向の[矢印キー]の入力で、セルを跨いだフォーカス移動ができます。

Opera の空間ナビゲーション機能

Opera にはもともと テーブル要素に限定せず、同様のキー操作でフォーカス移動を行える「空間ナビゲーション」という機能があります。

なので Opera の場合のみ、キー操作部分は、空間ナビゲーション機能で代替する実装になってます。

使い方

jquery.jsjquery.extablefocus.js を読み込みます。(jQuery の delegate メソッドを使用してるので、Ver 1.4.2 以降の jQuery を使用してください)

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.extablefocus.js"></script>

適用したいテーブル要素に対し、exTableFocus() メソッドを実行します。

jQuery(function($){
	$('table.example').exTableFocus();
});
INPUT 要素のフォーカス移動

f:id:cyokodog:20101211212952p:image

Demo

INPUT 要素にフォーカスした状態で、Shift + 矢印キーを入力するとフォーカス移動できます。上下移動のみなら Opera でなければ Shift 入力なしでも移動できます。

デモではフォーカスの当たってる行の背景色が変わるようになってますが、これはカレント行の TR 要素に ex-table-current-row というクラスが割り当たる仕様になってるためで、デモでは以下のような CSS を当ててます。

tr.ex-table-current-row td{
	background:#ffdddd;
}
INPUT + TEXTAREA 要素のフォーカス移動

f:id:cyokodog:20101211213111p:image

Demo

Shift + 矢印キーでフォーカス移動できます。

INPUT + SELECT 要素のフォーカス移動

f:id:cyokodog:20101211213138p:image

Demo

Shift + 矢印キーでフォーカス移動できます。

A 要素のフォーカス移動

f:id:cyokodog:20101211213204p:image

Demo

Opera 以外であれば矢印キーのみでもフォーカス移動できます。

1つのセル内に複数のフォーカス可能要素がある場合

f:id:cyokodog:20101211213223p:image

Demo

移動方向に対し直近の要素からフォーカスしていきます。例えば右方向に移動し、移動先のセルにフォーカス可能要素が複数あった場合は、DOM 構成上、先に出現する要素を優先しフォーカスします。左に移動した場合はその逆で、DOM 構成上、後に出現する要素を優先してフォーカスします。

In-Place-Editor との併用例

f:id:cyokodog:20101211213303p:image

Demo

デモでは、テーブル内の A 要素に対し、以下のようにexInPlace Editor(In-Place-Editor (その場で編集する UI) の jQuery プラグインを書いてみた - Cyokodog::Diary)を適用しています。

jQuery(function($){
	$('table').exTableFocus().delegate('a','mouseover focus',function(){
		$(this).exInPlaceEditor({
			nowHover : true,
			saveLabel : false,
			cancelLabel : false,
			editorType : 'input'
		});
	});
});

delegate() メソッド経由で、ホバーもしくはフォーカスした要素に対し動的にプラグイン適用してるので、サイズの大きいテーブル要素でも、初期化処理におけるパフォーマンス低下の心配がありません。

改行キー入力でフォーカス移動させる

プラグイン実行時に、以下のようにパラメータ指定すると、改行キー入力で次要素にフォーカスさせることができます。

$('table.example').exTableFocus({
	overrideCrControl : true
});

Demo

改行キー入力で垂直方向にフォーカス移動させる

プラグイン実行時に、以下のようにパラメータ指定すると、改行キー入力で垂直方向にフォーカス移動させることができます。

$('table.example').exTableFocus({
	overrideCrControl : true,
	verticalCrControl : true
});

Demo

パラメータ

以下パラメータがあります。

api
初期値 : false
true にすると API オブジェクトを返します。
overrideCrControl
初期値 : false
true にすると verticalCrControl パラメータの設定が有効になります。
verticalCrControl
初期値 : false
overrideCrControl パラメータが true の場合のみ有効になります。true にするとエンターキー入力時に垂直方向にフォーカス移動します。
overrideTabControl
初期値 : false
true にすると verticalTabControl パラメータの設定が有効になります。
verticalTabControl
初期値 : false
overrideTabControl パラメータが true の場合のみ有効になります。true にすると TAB キー入力時に垂直方向にフォーカス移動します。
currentRowClass
初期値 : "ex-table-current-row"
フォーカスの当たった行(TR 要素)に振られるクラス名を指定できます。
selectValue
初期値 : true
false にすると INPUT や TEXTAREA にフォーカス時、値を選択状態にしません。
focusExpr
初期値 : "select,input,textarea,a"
フォーカス制御を適用する要素を指定できます。(Opera は不可)

API

API オブジェクトを使用し、以下メソッドを使用することができます。

focusDown()
フォーカスが下方向に移動します。
focusUp()
フォーカスが上方向に移動します。
focusLeft()
フォーカスが左方向に移動します。
focusRight()
フォーカスが右方向に移動します。

ダウンロード

こちらからどうぞ