読み込むだけでWebページの「おもてなし度」を向上 - Pryn.js & Pryn.css

コメントと更新履歴はゼロと無限の間のログ » Pryn.js & cssへどうぞ。

よく使う初期設定用のJavaScriptとCSSをまとめたもの。
主にユーザビリティの向上が目的。JavaScriptとCSSを読み込めば、あとは自動で設定される。
使いやすくするためにMITライセンスで公開した。

主な効能

Pryn.js

  • 別ドメインへのリンクは別Windowで開く
  • ボタン系要素にマウスカーソルを当てた時にマウスカーソルが手になる
  • 入力系要素にフォーカスを当てた時に背景色が変わる
  • テキストボックス・テキストエリアにヒント文を表示できる (classを”hint”にして、title属性にヒント文をセットする。または、HTML5のplaceholder属性を使う)
  • テキストボックス・テキストエリアをコピペ用のものにできる (classを”copy”にする)
  • ページ表示時にフォーカスを当てる要素を指定できる (classを”ready”にする。またはHTML5のautofocus属性を使う)
  • formの二重送信を防止する
  • label要素にfor属性を書かなくても動作する
  • 画像にtitle属性が無い場合、alt属性をtitle属性にコピーする

Pryn.css

  • YUI(Yahoo! User Interface Library)のCSSのインポートと、その微調整
  • テキストボックスの背景色を変えても、テキストボックスの枠線の見た目が変わらないようにする(IE6非対応)
  • テキストボックス等でのIMEの制御 (classを”han”にするとIME無効、それ以外はIME有効)
  • ブラウザごとに違うボタンの大きさを適度に調整
  • 見やすいフォント(IE7・IE8はメイリオ、Macはヒラギノ角ゴ、その他はブラウザのデフォルトのフォント)
  • フォントサイズと行間大き目にする
  • クロスブラウザで違う見た目の微調整
  • Pryn.jsで見た目を操作している部分の具体的な表示内容の指定

サンプル

Pryn.js & Pryn.cssのサンプル

※サンプルで使っているのは最新のBeta版の場合あり

ライセンス

MITライセンスで。
内部的に使っているYUIのCSSはBSDライセンス

Pryn.cssではGoogleによってホスティングされているYUIのCSSをimportする。これについてはGoogle AJAX Libraries APIの利用規約も確認のこと。

ダウンロード

pryn.js

pryn.css

※ダウンロードのリンク先が最新のBeta版の場合あり

ソースコード

Pryn.js

/**
 *  Pryn.js
 *  @see       http://0-oo.net/sbox/javascript/pryn-js-css
 *  @version   0.2.5
 *  @copyright 2007-2010 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).ready   ページ表示時に入力待機状態にする要素
 *                              autofocus属性にも対応
 *    (input, textarea).focused 入力中の要素
 *    (input, textarea).hint    ヒント付テキストボックス。placeholder属性にも対応
 *                              placeholder属性を使わない場合は、
 *                               ・入力後のclassは ex-hint になる
 *                               ・ヒントはtitle属性にセットしておく
 *    (input, textarea).copy    コピー用テキスト
 *    table.stripes             シマシマにしたい表
 *    tr.stripe                 シマシマにされた行
 *    .wait                     formをsubmit後のマウスカーソル設定用
 *    .round-inner              角丸の内側(必要に応じてpaddingを指定する)
 */
 
 
/** IE高速化 @see http://d.hatena.ne.jp/amachang/20071010/1192012056 */
/*@cc_on _d = document; eval("var document = _d")@*/
 
 
/***** 組み込みオブジェクトの拡張 *****/
 
/**
 *  文字列のtrim
 *  @return String  trim後の文字列
 */
String.prototype.trim = function() {
    return this.replace(/(^\s+|\s+$)/g, "");
};
/**
 *  子要素全てに関数の処理を実行する
 *  @param  Object  it  子要素をループ処理できるオブジェクト
 */
Function.prototype.foreach = function(it) {
    for (var i = 0, len = it.length; i < len; i++) {
        this(it[i]);
    }
};
Function.prototype.foreach.foreach = null;  //再帰を防ぐ
 
 
/***** グローバル関数/オブジェクト *****/
 
/**
 *  Firebugが無い場合はconsole.log()を空振りさせる
 */
if (!window.console) {
    window.console = { log: function(s){}, dummy: true };
}
/**
 *  html要素をidで取得する(既に$()があればそちらを使う)
 *  @param  String  id
 *  @retrun HTMLElement html要素
 */
if (!window.$) {
    window.$ = function(id) { return document.getElementById(id); };
}
/**
 *  html要素をタグ名で取得する
 *  @param  String  tagName
 *  @retrun NodeList
 */
function $T(tagName) {
    return document.getElementsByTagName(tagName);
}
 
 
/***** Pryn *****/
 
var Pryn = {};
 
/**
 *  イベントを追加する
 *  @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);
        }
    };
})();
/**
 *  選択リストで選択された選択肢を取得する
 *  @param  String  id  選択リストのid
 *  @return HTMLElement 選択された選択肢(option要素)
 */
Pryn.getSelected = function(id){
    var selector = document.getElementById(id);
    return selector.options[selector.selectedIndex];
};
/**
 *  角丸っぽく見せる
 *  @param  HTMLElement elm
 */
Pryn.round = function(elm){
    elm.style.padding = "1px 0";
    elm.innerHTML = '<div class="round-inner">' + elm.innerHTML + "</div>";
};
 
 
/***** Pryn.ClassAccessor *****/
 
/**
 *  html要素のclass属性へのアクセサクラス
 *  @param  HTMLElement elm
 */
Pryn.ClassAccessor = function(elm) {
    /**
     *  html要素にclassがあるか確かめる
     *  @param  String  cName
     *  @return Mixed   あればArray、なければnullまたは""
     */
    this.hasClass = function(cName) {
        var cn = elm.className;
        return (cn && cn.match(this._toRegExp(cName)));
    };
    /**
     *  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(this._toRegExp(cName), " ");
    };
    /**
     *  html要素のclassを変更する
     *  @param  String  oldClass
     *  @param  String  newClass
     */
    this.convertClass = function(oldClass, newClass) {
        this.removeClass(oldClass);
        this.addClass(newClass);
    };
    /**
     *  class名を表す正規表現を取得する
     *  @param  String  cName
     *  @return RegExp
     */
    this._toRegExp = function(cName) {
        return new RegExp("(^|\\s)" + cName + "(\\s|$)", "ig");
    };
};
 
 
/***** Pryn.Init *****/
 
/**
 *  ページ表示時の初期処理
 */
Pryn.Init = {
    /** 別ドメインへの遷移を別Windowで開くかどうか */
    keepThisSite: true,
 
    /** formの二重送信防止を解除するまでの時間(秒) */
    frozenTime: 10,
    /** formの二重送信防止対象 */
    frozen: [],
 
    /** label要素と対応させるidの番号 */
    labelForIdNo: 0,
    /** label要素と対応させるidのプリフィクス */
    labelForIdPrefix: "pryn-label-for-id-",
 
    /** imageボタンの配列 */
    _inputImages: [],
 
    /** HTML5 placeholder属性対応可否 */
    _placeHoldable: navigator.userAgent.match(/AppleWebKit/)
};
 
/**
 *  表をシマシマにする(奇数番目の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等の設定
 *  @param  HTMLElement fElm    formの子要素
 */
Pryn.Init.setStyles = function(fElm) {
    var acs = new Pryn.ClassAccessor(fElm);
    if (fElm.tagName == "TEXTAREA") {
        Pryn.Init._setTextStyle(fElm, acs);
    } else if (fElm.tagName == "INPUT" && !fElm.getAttribute("type")) {
        fElm.type = "text"; //FirefoxとOperaはセットしないとtextと認識しない
    }
    switch (fElm.type) {
        case "text":
        case "password":
            Pryn.Init._setTextStyle(fElm, acs);
            break;
        case "submit":
            fElm.value = fElm.value || "クエリ送信";    //IEに合わせる
            break;
        case "image":
            Pryn.Init.setSmartImage(fElm);
            Pryn.Init._inputImages.push(fElm);
            break;
    }
    if (acs.hasClass("ready") || fElm.getAttribute("autofocus") != undefined) {
        fElm.focus();   //デフォルトでfocusを当てる
    }
};
/**
 *  テキスト入力系要素のstyle等の設定
 *  @param  HTMLElement         fElm    formの子要素
 *  @param  Pryn.ClassAccessor  acs
 */
Pryn.Init._setTextStyle = function(fElm, acs) {
    //HTML5 placeholder対応済みブラウザはplaceholderを使う
    if (Pryn.Init._placeHoldable) {
        if (acs.hasClass("hint")) {
            fElm.placeholder = fElm.title;
            acs.removeClass("hint");
        }
    } else {    //placeholder未対応ブラウザはhintクラスにより対応
        var placeHolder = fElm.getAttribute("placeholder");
        if (placeHolder) {
            fElm.title = placeHolder;
            acs.addClass("hint");
        }
    }
 
    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 obj a要素 or form要素(自動判断にはhref属性が必要)
 */
Pryn.Init.setTarget = function(obj) {
    var url = (obj.href || obj.action);
    if (!url) {
        return;
    }
    var acs = new Pryn.ClassAccessor(obj);
    var ext = false;
    if (Pryn.Init.keepThisSite && !acs.hasClass("same-window")) {
        //外部へのリンクかどうか
        ext = (url.match(/^http(|s):/i) && url.split("/")[2] != location.hostname);
    }
    if (ext || acs.hasClass("open")) {
        obj.target = "_blank";
    }
};
/**
 *  formの二重送信防止と別Windowで開く設定
 *  @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);
        //ページ全体のマウスカーソルも変える
        Pryn.Init._setSubmitStyles(document.body);
    });
    Pryn.Init.setTarget(frm);       //必要に応じて別Windowで開く
};
/**
 *  formのsubmit時の処理
 *  @param  HTMLElement formの子要素等
 */
Pryn.Init._setSubmitStyles = function(fElm) {
    var acs = new Pryn.ClassAccessor(fElm);
    if (acs.hasClass("hint")) {  //送信前にヒントをクリア
        fElm.value = "";
    }
    if (fElm.disabled) {
        return;
    }
    //操作不可にして、しばらくしたら戻す
    setTimeout(function() {
        fElm.disabled = true;
        acs.addClass("wait");
    }, 1);
    setTimeout(function() {
        fElm.disabled = false;
        acs.removeClass("wait");
    }, 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);
    }
};
/**
 *  画像系要素のtitleの設定
 *  @param  HTMLElement img
 */
Pryn.Init.setSmartImage = function(img) {
    //title属性をセット(IEの動きに合わせる)
    if (!img.title && img.alt) {
        img.title = img.alt;
    }
};
 
/**
 *  初期処理(Pryn.Initの実行。見た目の影響のあるものを優先)
 */
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.setTarget.foreach($T("a"));
 
    Pryn.Init.setSmartSubmit.foreach($T("form"));
 
    Pryn.Init.setSmartLabel.foreach($T("label"));
 
    Pryn.Init.setSmartImage.foreach($T("img"));
    Pryn.Init.setSmartImage.foreach($T("area"));
});
/**
 *  formの二重送信防止の凍結解除
 */
Pryn.addEvent(window, "unload", function() {
    (function(fElm) {
        fElm.disabled = false;
        (new Pryn.ClassAccessor(fElm)).removeClass("wait");
    }).foreach(Pryn.Init.frozen);
});

Pryn.css

@charset "UTF-8";
/**
 *  Pryn.css
 *  @see       http://0-oo.net/sbox/javascript/pryn-js-css
 *  @version   0.2.5
 *  @copyright 2007-2010 dgbadmin@gmail.com
 *  @license   http://0-oo.net/pryn/MIT_license.txt (The MIT license)
 */
 
/*******************************************************************************
 *  YUI 2 CSS Toolsのimport
 *  @see http://developer.yahoo.com/yui/2/
 *  Grids.cssはbuilderが便利
 *  @see http://developer.yahoo.com/yui/grids/builder/
 ******************************************************************************/
 
/**
 *  GoogleのCDNから読み込む(httpsも使えてyahooapis.comより速い)
 *  @see http://code.google.com/intl/en/apis/ajaxlibs/documentation/#yui
 */
@import "https://ajax.googleapis.com/ajax/libs/yui/2/build/reset-fonts-grids/reset-fonts-grids.css";
@import "https://ajax.googleapis.com/ajax/libs/yui/2/build/base/base-min.css";
 
/** YUI 2 CSS Tools適用後の調整 */
body{
    margin: 0;
}
select, input, textarea, button{
    margin-right: 1px;
}
select, input[type="text"], input[type="password"], textarea{
    border: solid 1px #7f9db9;  /* 背景色を指定しても枠線が変わらないようにする */
}
input[type="text"], input[type="password"], textarea{
    padding: 2px;
}
input[type="submit"], input[type="button"], input[type="reset"], button{
    padding: 0 1em; /* クリックしやすいように広げる */
    /* IE8:余白調整 */
    padding-top: 2px\9;
    line-height: 1.3;
    /* IE7:ボタンが大きいので小さめにする */
    *padding: 3px 0.4em 0;
    *line-height: 1;
}
/**
 *  フォント指定
 *  ・IE7+  : メイリオ(IE7+はClearTypeが適用されるのでギザギザにならない)
 *  ・Mac   : ヒラギノ
 *  ・その他: 半角はVerdana、全角はブラウザのデフォルトフォント
 */
body, select{
    font-family: "メイリオ", Verdana, sans-serif;   /* IE7+ */
    _font-family: Verdana, sans-serif;              /* IE6 */
}
html:not(:target) body, html:not(:target) select{   /* その他 */
    font-family: "Hiragino Kaku Gothic Pro", Verdana, sans-serif;
}
input, textarea, button{    /* 巨大化するのでメイリオにしない */
    font-family: "Hiragino Kaku Gothic Pro", Verdana, sans-serif;
}
 
 
/*******************************************************************************
 *  汎用的なstyle
 ******************************************************************************/
 
html{                   /* Firefox:常に縦のスクロールバーを表示 */
    overflow-y: scroll;
}
textarea{               /* IE:スクロールバー不要なら非表示 */
    overflow: auto;
}  
a, object{              /* クリックされた時に破線を表示しない */
    outline: none;
}
img{                    /* IE:サイズを変えた画像を綺麗にする */
    -ms-interpolation-mode: bicubic;
}
select, label, button, input.copy, textarea.copy, .clickable{
    cursor: pointer;    /* マウスカーソルを手にする(IE7-:select非対応)*/
}
input[type="submit"], input[type="button"], input[type="reset"], input[type="image"],
input[type="checkbox"], input[type="file"], input[type="radio"]{
    cursor: pointer;    /* マウスカーソルを手にする(Firefox:file非対応)*/
}
select[disabled], input[disabled], textarea[disabled], button[disabled]{
    cursor: not-allowed;/* マウスカーソルを禁止マークに(IE6:非対応、IE7+:一部のみ) */
}
body.wait, select.wait, input.wait, textarea.wait{
    cursor: wait;       /* マウスカーソルを砂時計やクルクルにする */
}
input, textarea{        /* IME On */
    ime-mode: active;
}
select, input.han, input.number, textarea.han{  /* IME Off */
    ime-mode: inactive;
}
input[type="password"]{ /* Firefox:パスワードでautoだとIME不可になる */
    ime-mode: auto;
}
input.number{           /* 数値 */
    text-align: right;
}
optgroup{
    color: #728490;
}
option{                 /* optgroupから引き継ぐ文字色を元に戻す */
    color: #000;
}
table.collapse{         /* cellspacing="0" cellpadding="0" */
    border-collapse: collapse;
}
.left{
    float: left;
}
.right{
    float: right;
}
.center{
    text-align: center;
}
.clear{                 /* floatのクリア */
    clear: both;
}
.nowrap{                /* 改行防止 */
    white-space: nowrap;
}
/**
 *  角丸っぽく見えるボックス
 */
.round-outer{           /* こちらに背景色(あればmarginも)を指定する */
    padding: 1px 0;
}
.round-inner{           /* こちらに(あれば)paddingを指定する */
    display: block;
    margin: 0 -1px;
    background-color: inherit;
}
/**
 *  バリアフリーな色
 *  @see http://jfly.iam.u-tokyo.ac.jp/colorset/
 */
.bf-gray{
    color: #728490;
}
 
 
/*******************************************************************************
 *  サイトごとのstyle(サイトに合わせて上書きする)
 ******************************************************************************/
 
#bd{
    font-size: 116%;        /* @see http://developer.yahoo.com/yui/fonts/#using */
    line-height: 1.4;       /* 単位を付けない */
}
div.error, span.error{      /* 入力エラーメッセージ */
    color: #f30;
    font-weight: bold;
}
a{
    color: #03c;            /* Bingの色 */
}
a:visited{
    color: #639;            /* Bingの色 */
}
a:hover, a:active{
    color: #c11;            /* Googleのactiveの色 */
}
select.error, input.error, textarea.error{
    background-color: #ffd3cc;  /* 入力エラーの要素(Firefox:check系非対応) */
}
input.focused, textarea.focused{/* フォーカスされた入力要素 */
    background-color: #ff9;
}
input.hint, textarea.hint{  /* テキストボックス内の説明文 */
    color: #728490;
}
input[readonly], input[disabled][type="text"], input[disabled][type="password"],
textarea[readonly], textarea[disabled]{ /* 使用不可系 */
    background-color: #eee;
}
pre{
    background-color: #ccc;
}
table.stripes tr.stripe{    /* シマシマにされる行 */
    background-color: #ccc;
}
.border-radius{             /* 角丸(IE:非対応) */
    border-radius: 10px;        /* Chrome, Opera */
    -moz-border-radius: 10px;   /* Firefox */
    -webkit-border-radius: 10px;/* Safari, (Chrome) */
}
 
/**
 *  Googleカスタム404ページ対応
 *  @see http://www.google.com/support/webmasters/bin/answer.py?hl=jp&answer=100044
 */
#goog-wm li{
    padding-bottom: 1em;
}
Wiki内アクセスランキング
www.chimeric.de Driven by DokuWiki Recent changes RSS feed