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

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

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

主な効能

Pryn.js

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

Pryn.css

  • YUI CSSのインポートと、その微調整
  • Sticky Footer(フッターの高さの調整が必要。参照:Yahho Sticky Footer
  • IEでのMS-IMEの制御 (classを”han”にするとIME無効、それ以外はIME有効)
  • 大き目のフォントサイズと行間をセット
  • IEとFirefoxで違う見た目の微調整
  • Pryn.jsで見た目を操作している部分の具体的な表示内容の指定

サンプル

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

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

ライセンス

MITライセンスで。
YUIのYahoo!のサーバからのインポートについてはServing YUI Files from Yahoo! Serversを参照。

ダウンロード

Pryn.js

Pryn.css

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

ソースコード

Pryn.js

/**
 *  Pryn.js
 *  @see       http://0-oo.net/sbox/javascript/pryn-js-css
 *  @version   0.2.2
 *  @copyright 2007-2008 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      ヒント付テキストボックス
 *                                 ・入力後のクラスは ex-hint になる
 *                                 ・ヒントはtitle属性にセットしておく
 *    (input, textarea).copy      コピー用テキスト
 *    (input, textarea).clickable クリックできる要素
 */
 
 
/** 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  arr 子要素をループ処理できるオブジェクト
 */
Function.prototype.foreach = function(arr) {
    for (var i = 0, len = arr.length; i < len; i++) {
        this(arr[i]);
    }
};
 
 
/***** グローバル関数/オブジェクト *****/
 
/**
 *  Firebugが無い場合はconsole.log()を空振りさせる
 */
if (!console) {
    var 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,
 
        /** input要素のtype属性をclass属性にコピーするかどうか */
        copyInputType: false,
        /** input要素のtype属性をclass属性にコピーする場合のプリフィクス */
        inputTypePrefix: "input-",
 
        /** formの二重送信防止を解除するまでの時間(秒) */
        frozenTime: 3,
 
        /** imageボタンの配列 */
        inputImages: []
    }
};
 
/**
 *  イベントを追加する。IEでもfunction内でthisで自分(elm)を参照できるようにしている
 *  @param  HTMLElement elm
 *  @param  String      eventName
 *  @param  Function    fnc
 */
Pryn.addEvent = function(){
    if (window.attachEvent) {
        return function(elm, eventName, fnc){
            elm.attachEvent("on" + eventName, function(){ fnc.call(elm); });
        };
    } else {
        return function(elm, eventName, fnc){
            if (elm == window && eventName == "load") {
                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 *****/
 
/**
 *  別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 != Pryn.getDomain(location));   //外部へのリンクかどうか
    }
    if (ext || acs.hasClass("open")) {                      //classがopenの場合も
        link.target = "_blank";
    }
};
/**
 *  formの子要素のstyle等の設定
 *  CSSにてclass "clickable" "focused" のstyleを定義すること
 *  @param  HTMLElement fElm    formの子要素
 */
Pryn.Init.setStyles = function(fElm) {
    var acs = new Pryn.ClassAccessor(fElm);
    if (Pryn.Init.copyInputType && fElm.tagName == "INPUT") {   //有効にされている場合は
        acs.addClass(Pryn.Init.inputTypePrefix + fElm.type);    //inputのtypeをclassにコピー
    }
    if (acs.hasClass("ready")) {    //デフォルトでfocusを当てる
        fElm.focus();
    }
    //ボタン系
    if (fElm.type.match(/^(submit|button|reset|image)$/i|>)) {
        if (fElm.type == "image") {
            Pryn.Init.setImageTitle(fElm);
            Pryn.Init.inputImages.push(fElm);
        }
        acs.addClass("clickable");
        return;
    }
    //ボタン系以外
    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 (fElm.type == "file") {                  //ファイルアップロード(IEのみ適用される)
        acs.addClass("clickable");
    } else 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;
        acs.addClass("clickable");
    }
};
/**
 *  formの二重送信防止設定
 *  @param  HTMLElement frm
 */
Pryn.Init.setSmartSubmit = function(frm) {
    Pryn.addEvent(frm, "submit", function() {
        Pryn.Init.setSubmitStyles.foreach(this);
        Pryn.Init.setSubmitStyles.foreach(Pryn.Init.inputImages);   //imageボタンはformの子要素にならない
    });
    frm.href = frm.action;          //別Windowで開くための前処理
    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 = "";
    }
    //操作不可にして、しばらくしたら戻す
    switch (fElm.type) {            //戻ってきた時にFirefoxでdisabledを解除できないtypeを峻別
        case "text":
        case "file":                //fileはFirefoxではreadOnlyは効かないがIE用に
            if (!fElm.readOnly) {
                setTimeout(function() { fElm.readOnly = true; }, 1);
                setTimeout(function() { fElm.readOnly = false; }, Pryn.Init.frozenTime * 1000);
            }
            break;
        case "checkbox":
        case "radio":
        case "hidden":
            break;
        default:
            if (!fElm.disabled) {
                setTimeout(function() { fElm.disabled = true; }, 1);
                setTimeout(function() { fElm.disabled = false; }, Pryn.Init.frozenTime * 1000);
            }
    }
};
/**
 *  label要素のクロスブラウザ対応
 *  @param  HTMLElement label
 */
Pryn.Init.setSmartLabel = function(label) {
    var input;
    var forId = label.htmlFor;
    if (forId) {
        input = document.getElementById(forId);
    } else {    //for属性省略ならlabel内のinputを対象にする
        input = label.getElementsByTagName("INPUT")[0];
    }
    if (input) {
        label.onclick = function() { input.click(); };
    }
};
/**
 *  img要素にtitle属性をセットする(IEの動きに合わせる)
 *  @param  HTMLElement img
 */
Pryn.Init.setImageTitle = function(img) {
    if (!img.title && img.alt) {
        img.title = img.alt;
    }
};
 
/**
 *  初期処理
 */
Pryn.addEvent(window, "load", function() {
    Pryn.Init.setTarget.foreach($T("a"));
 
    Pryn.Init.setStyles.foreach($T("input"));
    Pryn.Init.setStyles.foreach($T("textarea"));
    Pryn.Init.setStyles.foreach($T("button"));
 
    Pryn.Init.setSmartSubmit.foreach($T("form"));
 
    Pryn.Init.setSmartLabel.foreach($T("label"));
 
    Pryn.Init.setImageTitle.foreach($T("img"));
});

Pryn.css

@charset "UTF-8";
/**
 *  Pryn.css
 *  @see       http://0-oo.net/sbox/javascript/pryn-js-css
 *  @version   0.2.2
 *  @copyright 2007-2008 dgbadmin@gmail.com
 *  @license   http://0-oo.net/pryn/MIT_license.txt (The MIT license)
 */
 
 
/***** YUIのimport *****/
 
/*
 *  Yahoo!によるホスティングを利用
 *  @see http://developer.yahoo.com/yui/articles/hosting/
 */
@import "http://yui.yahooapis.com/2.5.2/build/reset-fonts-grids/reset-fonts-grids.css";
@import "http://yui.yahooapis.com/2.5.2/build/base/base-min.css";
 
/* httpsのページではYUIを自サーバに置いて使う(httpのCSSをimportすると警告が出るため)
@import "/yui/reset-fonts-grids/reset-fonts-grids.css";
@import "/yui/base/base-min.css";
*/
 
/**
 *  YUI適用後の調整
 */
input, textarea{
    margin-right:2px;
    padding: 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{
    position: relative;
    min-height: 100%;
    _height: 100%;          /* for IE6 */
}
div#ft{
    position: absolute;
    bottom: 0;
    width: 100%;
}
 
/* フッターの高さはサイトに合わせて変えること */
div#bd{
    padding-bottom: 4em;    /* フッターの高さと同じか、それより大きくする */
}
div#ft{
    height: 3em;
}
 
 
 
/***** 汎用的なstyle *****/
 
html{
    overflow: -moz-scrollbars-vertical; /* Firefox:常にスクロールバーを表示 */
}
select, label, button, .clickable{
    cursor: pointer;        /* マウスカーソルを手にする(IEはselectに非対応) */
}
textarea{  
    overflow: auto;         /* IE:スクロールバー不要なら非表示 */
}  
input, textarea{
    ime-mode: active;       /* IE, Firefox3:IME有効 */
}
input.han, input.number, textarea.han{
    ime-mode: inactive;     /* IE, Firefox3:IME無効 */
}
input.number{               /* 数値 */
    text-align: right;
}
.left{
    float: left;
}
.right{
    float: right;
}
.center{
    text-align: center;
}
.clear{                     /* floatのクリア */
    clear: both;
}
hr.mobile{                  /* 携帯専用区切りライン(PCでは見せない) */
    display: none;
}
 
 
/***** サイトごとのstyle(サイトに合わせて 変更 or 上書き する) *****/
 
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; /* 入力エラーの要素の背景色 */
}
input.focused, textarea.focused{
    background-color: #feb; /* 入力要素のfocus時の背景色 */
}
input.hint, textarea.hint{  /* テキストボックス内の説明 */
    color: #999;
}
input.copy, textarea.copy{
    background-color: #ddd; /* コピー用テキストの背景色 */
}
optgroup{
    color: #999;
}
option{
    color: #000;            /* optgroupから引き継ぐcolorを元に戻す */
}
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0