コメントと更新履歴はゼロと無限の間のログ » Pryn.js & cssへどうぞ。
よく使う初期設定用のJavaScriptとCSSをまとめたもの。
主にユーザビリティの向上が目的。JavaScriptとCSSを読み込めば、あとは自動で設定される。
使いやすくするためにMITライセンスで公開した。
※サンプルで使っているのは最新のBeta版の場合あり
MITライセンスで。
内部的に使っているYUIのCSSはBSDライセンス。
Pryn.cssではGoogleによってホスティングされているYUIのCSSをimportする。これについてはGoogle AJAX Libraries APIの利用規約も確認のこと。
/** * Pryn.js * @see http://0-oo.net/sbox/javascript/pryn-js-css * @version 0.2.4 * @copyright 2007-2009 dgbadmin@gmail.com * @license http://0-oo.net/pryn/MIT_license.txt (The MIT license) * * 連携するCSSクラス * (a, form).open 別Windowで開く * (a, form).same-window 同じWindowで開く * (input, textarea).focused 入力状態になった要素 * (input, textarea).ready デフォルトでfocusを当てる要素 * (input, textarea).hint ヒント付テキストボックス * ・入力後のclassは ex-hint になる * ・ヒントはtitle属性にセットしておく * (input, textarea).copy コピー用テキスト * (input, img).clickable クリックできる要素 * table.stripes シマシマにしたい表 * tr.stripe シマシマにされた行 */ /** IE高速化 @see http://d.hatena.ne.jp/amachang/20071010/1192012056 */ /*@cc_on _d = document; eval("var document = _d")@*/ /***** 組み込みオブジェクトの拡張 *****/ /** * 文字列のtrim * @return trim後の文字列 */ String.prototype.trim = function() { return this.replace(/(^\s+|\s+$)/g, ""); }; /** * 1単語を表す正規表現を取得する * @return 正規表現オブジェクト */ String.prototype.word2regexp = function() { return new RegExp("(^|\\s)" + this + "(\\s|$)", "ig"); }; /** * 子要素全てに関数の処理を実行する * @param Object it 子要素をループ処理できるオブジェクト */ Function.prototype.foreach = function(it) { for (var i = 0, len = it.length; i < len; i++) { this(it[i]); } }; /***** グローバル関数/オブジェクト *****/ /** * Firebugが無い場合はconsole.log()を空振りさせる */ window.console = window.console || {log: function(s){}, dummy: true}; /** * html要素をidで取得する(既に$()があればそちらを使う) * @param String id * @retrun html要素 */ var $ = $ || function(id) { return document.getElementById(id); }; /** * html要素をタグ名で取得する * @param String tagName * @retrun NodeList */ function $T(tagName) { return document.getElementsByTagName(tagName); } /** * ページを遷移する * @param String url */ function go(url) { location.href = url; } /***** Pryn *****/ var Pryn = { /** ページ表示時のユーザビリティ向上用の設定 */ Init: { /** 別ドメインへの遷移を別Windowで開くかどうか */ keepThisSite: true, /** formの二重送信防止を解除するまでの時間(秒) */ frozenTime: 5, /** formの二重送信防止対象 */ frozen: [], /** label要素と対応させるidの番号 */ labelForIdNo: 0, /** label要素と対応させるidのプリフィクス */ labelForIdPrefix: "pryn-label-for-id-", /** imageボタンの配列 */ inputImages: [] } }; /** * イベントを追加する * @param HTMLElement elm * @param String eventName * @param Function fnc */ Pryn.addEvent = (function(){ if (window.attachEvent) { return function(elm, eventName, fnc){ //IEでもfunction内でthisで自分(elm)を参照できるようにする elm.attachEvent("on" + eventName, function(){ fnc.call(elm); }); }; } //Safari3.0.x以前はDOMContentLoadedに未対応 var webKit = navigator.userAgent.match(/AppleWebKit\/([0-9]+)/); var oldWebKit = (webKit && webKit[1] < 525); return function(elm, eventName, fnc){ if (elm == window && eventName == "load" && !oldWebKit) { document.addEventListener("DOMContentLoaded", fnc, false); } else { elm.addEventListener(eventName, fnc, false); } }; })(); /** * リンクのURLからドメイン部分を取り出す * @param HTMLElement link * @return メイン(ドメインを含んでいない場合はnull) */ Pryn.getDomain = function(link) { var href = link.href; if (href.match(/^http(|s):/i)) { return href.split("/")[2]; } else { return null; } }; /***** Pryn.ClassAccessor *****/ /** * html要素のclass属性へのアクセサクラス * @param HTMLElement elm */ Pryn.ClassAccessor = function(elm) { /** * html要素にclassがあるか確かめる * @param String cName */ this.hasClass = function(cName) { var cn = elm.className; return (cn && cn.match(cName.word2regexp())); }; /** * html要素にclassを追加する * @param String cName */ this.addClass = function(cName) { if (!this.hasClass(cName)) { elm.className += " " + cName; } }; /** * html要素からclassを削除する * @param String cName */ this.removeClass = function(cName) { elm.className = elm.className.replace(cName.word2regexp(), " ").trim(); }; /** * html要素のclassを変更する * @param String oldClass * @param String newClass */ this.convertClass = function(oldClass, newClass) { this.removeClass(oldClass); this.addClass(newClass); }; }; /***** Pryn.Init *****/ /** * 表をシマシマにする(奇数番目のtr要素のclassに stripe を追加する) * シマシマにしたくない行(見出し行など)はthead要素で囲っておくこと * @param HTMLElement table table要素 */ Pryn.Init.stripeTable = function(table) { if (!(new Pryn.ClassAccessor(table)).hasClass("stripes")) { return; } var rows = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr"); for (var i = 0, len = rows.length; i < len; i += 2) { (new Pryn.ClassAccessor(rows[i])).addClass("stripe"); } }; /** * formの子要素のstyle等の設定 * CSSにてclass "clickable" "focused" のstyleを定義すること * @param HTMLElement fElm formの子要素 */ Pryn.Init.setStyles = function(fElm) { var acs = new Pryn.ClassAccessor(fElm); if (acs.hasClass("ready")) { fElm.focus(); //デフォルトでfocusを当てる } var type = fElm.type; if (fElm.tagName == 'TEXTAREA' || type.match(/^(text|password)$/i)) { Pryn.Init.setTextStyle(fElm, acs); } else if (fElm.tagName == 'INPUT' && type != "hidden") { acs.addClass("clickable"); //ファイルアップロードはFirefoxは非対応 if (type == "image") { Pryn.Init.setSmartImage(fElm); Pryn.Init.inputImages.push(fElm); } } }; /** * テキスト入力系要素のstyle等の設定 * @param HTMLElement fElm formの子要素 * @param Pryn.ClassAccessor acs */ Pryn.Init.setTextStyle = function(fElm, acs) { Pryn.addEvent(fElm, "focus", function() { //フォーカスされた時 if (!fElm.readOnly) { acs.addClass("focused"); } if (acs.hasClass("hint")) { //ヒント付テキストボックス this.value = ""; acs.convertClass("hint", "ex-hint"); } else if (acs.hasClass("copy")) { //コピー用テキスト setTimeout(function() { fElm.select(); }, 100); //selectし損ねるのを防ぐ } }); Pryn.addEvent(fElm, "blur", function() { //フォーカスが外れた時 acs.removeClass("focused"); if (acs.hasClass("ex-hint") && !fElm.value) { //ヒント付テキストボックスで未入力 acs.convertClass("ex-hint", "hint"); fElm.value = fElm.title; } }); if (acs.hasClass("hint")) { //ヒント付テキストボックス if (fElm.value && fElm.value != fElm.title) { //既に値がある場合 acs.convertClass("hint", "ex-hint"); } else { fElm.value = fElm.title; //title属性の値を表示 } } else if (acs.hasClass("copy")) { //コピー用テキスト fElm.readOnly = true; } }; /** * 別Windowで開くリンク・formの設定 * @param HTMLElement link a要素 or form要素(自動判断にはhref属性が必要) */ Pryn.Init.setTarget = function(link) { var acs = new Pryn.ClassAccessor(link); var ext = false; if (Pryn.Init.keepThisSite && !acs.hasClass("same-window")) { var next = Pryn.getDomain(link); ext = (next && next != location.hostname); //外部へのリンクかどうか } if (ext || acs.hasClass("open")) { link.target = "_blank"; } }; /** * formの二重送信防止設定 * @param HTMLElement frm */ Pryn.Init.setSmartSubmit = function(frm) { Pryn.addEvent(frm, "submit", function() { Pryn.Init.setSubmitStyles.foreach(this); //imageボタンはformの子要素にならないので別途処理 Pryn.Init.setSubmitStyles.foreach(Pryn.Init.inputImages); }); frm.href = frm.action; //別Windowで開くための前処理 Pryn.Init.setTarget(frm); //必要に応じて別Windowで開く }; /** * formのsubmit時の処理 * @param HTMLElement formの子要素 */ Pryn.Init.setSubmitStyles = function(fElm) { if ((new Pryn.ClassAccessor(fElm)).hasClass("hint")) { //送信前にヒントをクリア fElm.value = ""; } //textareaはFirefoxで戻ったときに初期値が消えるので除外 if (fElm.disabled || fElm.tagName == "TEXTAREA") { return; } //操作不可にして、しばらくしたら戻す setTimeout(function() { fElm.disabled = true; }, 1); setTimeout(function() { fElm.disabled = false; }, Pryn.Init.frozenTime * 1000); Pryn.Init.frozen.push(fElm); }; /** * label要素のクロスブラウザ対応(Safari2以前は非対応) * @param HTMLElement label */ Pryn.Init.setSmartLabel = function(label) { if (!label.htmlFor) { var input = label.getElementsByTagName("INPUT")[0]; if (!input) { return; } else if (!input.id) { input.id = Pryn.Init.labelForIdPrefix + Pryn.Init.labelForIdNo++; } label.htmlFor = input.id; //IE6ではfor属性の省略ができない } //IEはlabel要素内のimg要素をクリックしてもチェックが付かない var images = label.getElementsByTagName("IMG"); var len = images.length; if (!len) { return; } var clickIt = function() { document.getElementById(label.htmlFor).click(); }; for (var i = 0; i < len; i++) { Pryn.addEvent(images[i], "click", clickIt); } }; /** * img要素のtitleとstyleの設定 * CSSにてclass "clickable" のstyleを定義すること * @param HTMLElement img */ Pryn.Init.setSmartImage = function(img) { //title属性をセット(IEの動きに合わせる) if (!img.title && img.alt) { img.title = img.alt; } if (img.onclick) { (new Pryn.ClassAccessor(img)).addClass("clickable"); } }; /** * 初期処理(見た目の影響のあるものを優先) */ Pryn.addEvent(window, "load", function() { Pryn.Init.stripeTable.foreach($T("table")); Pryn.Init.setStyles.foreach($T("input")); Pryn.Init.setStyles.foreach($T("textarea")); Pryn.Init.setStyles.foreach($T("button")); Pryn.Init.setTarget.foreach($T("a")); Pryn.Init.setSmartSubmit.foreach($T("form")); Pryn.Init.setSmartLabel.foreach($T("label")); Pryn.Init.setSmartImage.foreach($T("img")); }); /** * formの二重送信防止の凍結解除 */ Pryn.addEvent(window, "unload", function() { (function(fElm) { fElm.disabled = false; }).foreach(Pryn.Init.frozen); });
@charset "UTF-8"; /** * Pryn.css * @see http://0-oo.net/sbox/javascript/pryn-js-css * @version 0.2.4 * @copyright 2007-2009 dgbadmin@gmail.com * @license http://0-oo.net/pryn/MIT_license.txt (The MIT license) */ /***** YUIのimport *****/ /** * Googleによるホスティングを利用 * @see http://developer.yahoo.com/yui/articles/hosting/ * @see http://code.google.com/intl/ja/apis/ajaxlibs/documentation/#yui */ @import "//ajax.googleapis.com/ajax/libs/yui/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css"; @import "//ajax.googleapis.com/ajax/libs/yui/2.7.0/build/base/base-min.css"; /** * YUI適用後の調整 */ body{ margin: 0; } input, textarea{ margin-right: 1px; padding: 1px; } select{ margin-right: 1px; } /** * YUIをSticy Footer化 * @see http://0-oo.net/sbox/css-small-box/yahho-sticky-footer */ html, body{ height: 100%; } div#doc, div#doc2, div#doc3, div#doc4, div#custom-doc{ position: relative; min-height: 100%; _height: 100%; /* for IE6 */ } div#ft{ position: absolute; bottom: 0; width: 100%; } /* フッターの高さはサイトに合わせて変えること */ div#bd{ padding-bottom: 4em; /* div#ftの高さと同じか、それより大きくする */ } div#ft{ height: 3em; } /***** 汎用的なstyle *****/ html{ overflow-y: scroll; /* Firefox:常に縦のスクロールバーを表示 */ } select, label, button, input.copy, textarea.copy, .clickable{ cursor: pointer; /* マウスカーソルを手にする(IE7-:選択リスト非対応) */ } select[disabled], input[disabled], textarea[disabled], button[disabled]{ cursor: not-allowed; /* マウスカーソルを禁止マークに(IE6:非対応、IE7+:一部のみ) */ } textarea{ overflow: auto; /* IE:スクロールバー不要なら非表示 */ } input, textarea{ ime-mode: active; /* IME On */ } input.han, input.number, textarea.han, select{ ime-mode: inactive; /* IME Off */ } input[type="password"]{ ime-mode: auto; /* Firefox:パスワードでautoだとIME不可になる */ } input.number{ /* 数値 */ text-align: right; } .left{ float: left; } .right{ float: right; } .center{ text-align: center; } .clear{ /* floatのクリア */ clear: both; } /** * Googleカスタム404ページ対応 * @see http://www.google.com/support/webmasters/bin/answer.py?hl=jp&answer=100044 */ #goog-wm li.search-goog{ display: block; } /***** サイトごとのstyle(サイトに合わせて上書きする) *****/ div#bd{ font-size: 116%; /* @see http://developer.yahoo.com/yui/fonts/#using */ line-height: 1.7; /* 単位を付けない */ } div.error, span.error{ /* 入力エラーメッセージのフォント */ color: #f00; font-weight: bold; } input.error, textarea.error, select.error{ background-color: #f99; /* 入力エラーの要素の背景色(Firefox:check系非対応) */ } input.focused, textarea.focused{ background-color: #feb; /* 入力要素のfocus時の背景色 */ } input.hint, textarea.hint{ /* テキストボックス内の説明 */ color: #999; } input.copy, textarea.copy{ background-color: #ddd; /* コピー用テキストの背景色 */ } table.stripes tr.stripe{ background-color: #eee; /* シマシマにされる行 */ } optgroup{ color: #999; } option{ color: #000; /* optgroupから引き継ぐcolorを元に戻す */ }