javascript:pryn-js-css
目次
読み込むだけでWebページの「おもてなし度」を向上 - Pryn.js & Pryn.css
コメントと更新履歴はゼロと無限の間のログ » Pryn.js & cssへどうぞ。
よく使う初期設定用のJavaScriptとCSSをまとめて“Pryn”と名付けた。
内容は主にユーザビリティの向上(つまり、おもてなし)のためのもの。
使い方は簡単。JavaScriptとCSSを1つずつ読み込むだけ。あとは勝手にやってくれる。
バージョン1.0.0からはIE6はサポート対象外にした。
Prynの主な効能
Pryn.js
- 入力時に、テキストボックス・テキストエリアの背景色が変わる
- formの二重送信を防止する
- かつ、戻ってきた場合のために備えてページ遷移時には二重送信防止を解除する
- また、エラー等の対処のために一定時間が経つと二重送信防止を解除する
- HTML5に未対応のブラウザでも、HTML5のplaceholder属性(テキストボックス・テキストエリアに入力のヒント等を表示し、入力時には消える)が使える
- HTML5のplaceholder属性を使うか、classを“hint”にしてtitle属性にヒント文をセットする
- HTML5に未対応のブラウザでも、HTML5のautofocus属性(ページ表示時に入力待機状態にする)が使える
- 。またはHTML5のautofocus属性を使うか、classを“ready”にする
- テキストボックス・テキストエリアをコピペ用のものにできる
- classを“copy”にする
- クロスブラウザなイベントリスナーの追加
- 簡単にclass属性の値を操作できる
- label要素にfor属性を書かなくても動作する
- 別ドメインへのリンクやformは別Windowで開く
- 画像にtitle属性が無い場合、alt属性をtitle属性にコピーする
- その他いろいろ
Pryn.css
- YUI CSS Componentsのインポートと、その微調整
- テキストボックスの背景色を変えても、テキストボックスの枠線のスタイルが変わらないようにする
- テキストボックス等でのIMEの制御 (classを“han”にするとIME無効、それ以外はIME有効)
- ブラウザごとに違うボタンの大きさを適度に調整
- リンク、ボタン、ラベル等にマウスカーソルを当てると、マウスカーソルが手(指差しマーク)になる
- ページ遷移時には、マウスカーソルがクルクルや砂時計等の待機状態になる
- 見やすいフォント(IEはMeiryo UI、Macはヒラギノ角ゴ、その他はブラウザのデフォルトのフォント)
- フォントサイズと行間を大き目にして見やすくする
- クロスブラウザで違う見た目の微調整
- Pryn.jsで見た目を操作している部分の具体的な表示内容の指定
- よく使うけどいざという時すぐに思い出せないTips的なCSSのメモ
- その他いろいろ
サンプル
※サンプルで使っているのは最新のBeta版の場合あり
ライセンス
MITライセンスで。
内部的に使っているYUIのCSSはBSDライセンス。
Pryn.cssではGoogleによってホスティングされているYUIのCSSをimportする。これについてはGoogle Libraries APIの利用規約も確認のこと。
ダウンロード
ソースコード
Pryn.js
/** * Pryn.js * @see http://0-oo.net/sbox/javascript/pryn-js-css * @version 1.0.0 * @copyright 2007-2011 dgbadmin@gmail.com * @license http://0-oo.net/pryn/MIT_license.txt (The MIT license) * * 連携するCSSクラス * (input, textarea).ready ページ表示時に入力待機状態にする要素。autofocus属性でも可 * (input, textarea).focused 入力中の要素 * (input, textarea).hint ヒント付テキストボックス。placeholder属性でも可 * placeholder属性を使わない場合、ヒントはtitle属性にセット * (input, textarea).copy コピー用テキストボックス * table.stripes シマシマにしたい表 * tr.stripe シマシマにされた行 * .wait 別のページへの遷移中に表示が変わる要素 */ /***** 組み込みオブジェクトの拡張 *****/ /** * 文字列の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.addEventListener) { return function(elm, eventName, fnc) { if (elm == window && eventName == "load") { document.addEventListener("DOMContentLoaded", fnc, false); } else { elm.addEventListener(eventName, fnc, false); } }; } return function(elm, eventName, fnc) { //IE8-でもfunction内でthisで自分(elm)を参照できるようにする elm.attachEvent("on" + eventName, function() { fnc.call(elm); }); }; })(); /** * 選択リストで選択された選択肢を取得する * @param String id 選択リストのid * @return HTMLElement 選択された選択肢(option要素) */ Pryn.getSelected = function(id) { var selector = document.getElementById(id); return selector.options[selector.selectedIndex]; }; /***** Pryn.ClassAccessor *****/ /** * html要素のclass属性へのアクセサクラス * @param HTMLElement elm */ Pryn.ClassAccessor = function(elm) { /** * html要素のclassを変更する * @param String oldClass * @param String newClass */ this.convertClass = function(oldClass, newClass) { this.removeClass(oldClass); this.addClass(newClass); }; //classListに対応していればclassListを使う (Firefox, Chrome) if (Pryn.Init._classListable) { this.hasClass = function(className) { return elm.classList.contains(className); }; this.addClass = function(className) { elm.classList.add(className); }; this.removeClass = function(className) { elm.classList.remove(className); }; return; } /** * html要素にclassがあるか確かめる * @param String className * @return Mixed あればArray、なければnullまたは"" */ this.hasClass = function(className) { var cn = elm.className; return (cn && cn.match(this._toRegExp(className))); }; /** * html要素にclassを追加する * @param String className */ this.addClass = function(className) { if (!this.hasClass(className)) { elm.className += " " + className; } }; /** * html要素からclassを削除する * @param String className */ this.removeClass = function(className) { elm.className = elm.className.replace(this._toRegExp(className), " "); }; /** * class名を表す正規表現を取得する * @param String className * @return RegExp */ this._toRegExp = function(className) { return new RegExp("(^|\\s)" + className + "(\\s|$)", "ig"); }; }; /***** Pryn.Init *****/ /** * ページ表示時の初期処理 */ Pryn.Init = { /** ページ遷移時のform凍結・表示変更を解除するまでの時間(秒) */ frozenTime: 10, /** ページ遷移時の表示変更の対象 */ _waitables: [], /** ブラウザがHTML5のautofocus属性に対応しているかどうか */ _autoFocusable: "autofocus" in document.createElement("textarea"), /** ブラウザがHTML5のplaceholder属性に対応しているかどうか */ _placeHoldable: "placeholder" in document.createElement("textarea"), /** ブラウザがHTML5のclassListに対応しているかどうか */ _classListable: "classList" in document.createElement("div"), /** IE7-かどうか */ _ltIE7: false }; /** * 表をシマシマにする(奇数番目の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 elm input要素 or textarea要素 */ Pryn.Init._setFormStyle = function(elm) { var acs = new Pryn.ClassAccessor(elm); if (elm.tagName == "TEXTAREA") { Pryn.Init._setTextStyle(elm, acs); } else if (elm.tagName == "INPUT" && !elm.getAttribute("type")) { elm.type = "text"; //FirefoxとOperaはセットしないとtextと認識しない } switch (elm.type) { case "text": case "password": Pryn.Init._setTextStyle(elm, acs); break; case "submit": elm.value = elm.value || "送信"; //IE:非対応 break; case "image": Pryn.Init._setSmartImage(elm); //imageボタンはformの子要素にならないのでここでセット Pryn.Init._waitables.push(elm); break; } //画面表示時にfocusを当てる if (acs.hasClass("ready")) { elm.focus(); } else if (!Pryn.Init._autoFocusable && elm.getAttribute("autofocus") !== null) { elm.focus(); } Pryn.Init._hideFocus(elm); }; /** * テキスト入力系要素のstyle等の設定 * @param HTMLElement elm formの子要素 * @param Pryn.ClassAccessor acs */ Pryn.Init._setTextStyle = function(elm, acs) { Pryn.Init._setPlaceHolder(elm, acs); Pryn.addEvent(elm, "focus", function() { if (!elm.readOnly) { acs.addClass("focused"); } }); Pryn.addEvent(elm, "blur", function() { acs.removeClass("focused"); }); if (acs.hasClass("hint")) { //ヒント付テキストボックス if (elm.value) { //初期状態で値がある場合 acs.removeClass("hint"); } else { elm.value = elm.getAttribute("placeholder"); } Pryn.addEvent(elm, "focus", function() { if (acs.hasClass("hint")) { //ヒント表示中の場合 acs.removeClass("hint"); elm.value = ""; } }); Pryn.addEvent(elm, "blur", function() { if (!elm.value) { //未入力の場合 acs.addClass("hint"); elm.value = elm.getAttribute("placeholder"); } }); } else if (acs.hasClass("copy")) { //コピー用テキストボックス elm.readOnly = true; Pryn.addEvent(elm, "focus", function() { setTimeout(function() { elm.select(); }, 100); //selectし損ねるのを防ぐ }); } }; /** * テキスト入力系要素のplaceholderの設定 * @param HTMLElement elm formの子要素 * @param Pryn.ClassAccessor acs */ Pryn.Init._setPlaceHolder = function(elm, acs) { var placeHolder = elm.getAttribute("placeholder"); if (placeHolder) { if (!Pryn.Init._placeHoldable) { acs.addClass("hint"); //擬似placeholder表示 } if (!elm.title) { elm.title = placeHolder; //title属性にもセット } } else if (acs.hasClass("hint")) { elm.setAttribute("placeholder", elm.title); } if (Pryn.Init._placeHoldable) { acs.removeClass("hint"); } }; /** * 別ページへの遷移時のマウスカーソル変更とformの凍結 * @param HTMLElement link a要素 */ Pryn.Init._setSmartLink = function(link) { if (Pryn.Init._setTarget(link)) { Pryn.Init._waitables.push(link); } Pryn.Init._hideFocus(link); }; /** * form送信時の処理 * @param HTMLElement frm form要素 */ Pryn.Init._setSmartSubmit = function(frm) { if (!Pryn.Init._placeHoldable) { Pryn.addEvent(frm, "submit", function() { (function(elm) { //form送信前にヒントをクリア if ((new Pryn.ClassAccessor(elm)).hasClass("hint")) { elm.value = ""; } }).foreach(frm); }); } if (Pryn.Init._setTarget(frm)) { (function(child) { Pryn.Init._waitables.push(child); }).foreach(frm); } }; /** * 別ドメインへの遷移は別Windowで開く * @param HTMLElement elm a要素 or form要素 * @return boolean 同一ドメインかどうか */ Pryn.Init._setTarget = function(elm) { var url = (elm.href || elm.action || ""); if (url.match(/^https?:/i) && url.split("/")[2] != location.hostname) { elm.target = "_blank"; return false; } else { return true; } }; /** * label要素のクロスブラウザ対応(Safari2以前は非対応) * @param HTMLElement label */ Pryn.Init._setSmartLabel = function(label) { var input = null; if (label.htmlFor) { input = document.getElementById(label.htmlFor); } else { input = label.getElementsByTagName("INPUT")[0]; } if (!input) { return; } if (input.title && !label.title) { label.title = input.title; //label要素にも同じtitle属性を付ける } //IEはlabel要素内のimg要素をクリックしてもチェックが付かない var images = label.getElementsByTagName("IMG"); var len = images.length; if (!len) { return; } var clickIt = function() { input.click(); }; for (var i = 0; i < len; i++) { Pryn.addEvent(images[i], "click", clickIt); } }; /** * 画像系要素のtitleの設定 * @param HTMLElement img */ Pryn.Init._setSmartImage = function(img) { if (!img.title && img.alt) { img.title = img.alt; //title属性をセット(IEの動きに合わせる) } }; /** * フォーカス時の破線を表示させない(IE7-のみ、IE8+はCSSで指定可能) * @param HTMLElement elm */ Pryn.Init._hideFocus = function(elm) { if (Pryn.Init._ltIE7) { elm.hideFocus = true; } }; /** * 別ページへの遷移時のマウスカーソル変更とformの二重送信防止の解除 */ Pryn.Init.unfreeze = function() { (function(elm) { var acs = new Pryn.ClassAccessor(elm); if (acs.hasClass("wait")) { elm.disabled = false; acs.removeClass("wait"); } }).foreach(Pryn.Init._waitables); }; /** * 初期処理(見た目の影響のあるものを優先) */ Pryn.addEvent(window, "load", function() { //IE7-かどうかの判定(@_jscript_versionと違い、IE7互換モードも判定可能) var div = document.createElement("div"); div.innerHTML = "<!--[if lt IE 8]>.<![endif]-->"; Pryn.Init._ltIE7 = !!div.innerText; Pryn.Init._stripeTable.foreach($T("table")); Pryn.Init._setFormStyle.foreach($T("input")); Pryn.Init._setFormStyle.foreach($T("textarea")); Pryn.Init._setSmartLink.foreach($T("a")); Pryn.Init._setSmartSubmit.foreach($T("form")); Pryn.Init._waitables.push(document.body); Pryn.Init._setSmartLabel.foreach($T("label")); Pryn.Init._setSmartImage.foreach($T("img")); Pryn.Init._setSmartImage.foreach($T("area")); document.body.spellcheck = false; //スペルチェックなし(Safari:非対応) }); /** * 別ページへの遷移時のマウスカーソル変更とformの二重送信防止 */ Pryn.addEvent(window, "beforeunload", function() { (function(elm) { if (!elm.disabled) { (new Pryn.ClassAccessor(elm)).addClass("wait"); if (elm.tagName != "A" && elm.tagName != "BODY") { elm.disabled = true; //formの子要素は使用不可にする } } }).foreach(Pryn.Init._waitables); //一定時間経ったら元に戻す setTimeout(Pryn.Init.unfreeze, Pryn.Init.frozenTime * 1000); }); /** * 別ページへの遷移時のマウスカーソル変更とformの二重送信防止の解除 */ Pryn.addEvent(window, "unload", Pryn.Init.unfreeze);
Pryn.css
@charset "UTF-8"; /** * Pryn.css * @see http://0-oo.net/sbox/javascript/pryn-js-css * @version 1.0.0 * @copyright 2007-2011 dgbadmin@gmail.com * @license http://0-oo.net/pryn/MIT_license.txt (The MIT license) */ /******************************************************************************* * YUI 3 CSS Componentsのimportと、YUI適用後の調整 * @see http://yuilibrary.com/yui/css/ * @see http://yuilibrary.com/yui/docs/cssfonts/#fontsize ******************************************************************************/ /** * GoogleのCDNから読み込む(httpsも使えてyahooapis.comより速い) * @see http://code.google.com/intl/ja/apis/libraries/devguide.html#yui */ @import "https://ajax.googleapis.com/ajax/libs/yui/3/build/cssreset/reset-min.css"; @import "https://ajax.googleapis.com/ajax/libs/yui/3/build/cssfonts/fonts-min.css"; @import "https://ajax.googleapis.com/ajax/libs/yui/3/build/cssgrids/grids-min.css"; @import "https://ajax.googleapis.com/ajax/libs/yui/3/build/cssbase/base-min.css"; /** * 下位互換のためYUI2のgrids.cssも読み込む * @see http://developer.yahoo.com/yui/grids/builder/ */ @import "https://ajax.googleapis.com/ajax/libs/yui/2/build/grids/grids-min.css"; /** * 微調整 */ body { margin: 0 auto; /* センタリング(真ん中寄せ) */ text-align: left; /* YUI2 grids.cssでのIE6用センタリングをリセット */ } ul, ol { margin-top: 0.2em; margin-bottom: 0.2em; } /******************************************************************************* * Font family * (画面) * ・Win IE : Meiryo UI or メイリオ * ・Mac : ヒラギノ角ゴ ProN W3 * ・その他 : 半角はVerdana、全角はブラウザのデフォルトのフォント * (印刷) * ・Win : MS P明朝 * ・Mac : ヒラギノ明朝 ProN W3 ******************************************************************************/ body, select, input, button, textarea { /* IE */ font-family: "Meiryo UI", "メイリオ", Verdana, sans-serif; } @-moz-document url-prefix() { /* Firefox */ body, select, input, button, textarea { font-family: "Hiragino Kaku Gothic ProN", Verdana, sans-serif; } } @media screen and (-webkit-min-device-pixel-ratio: 0) { /* Chrome, Safari */ body, select, input, button, textarea { font-family: "Hiragino Kaku Gothic ProN", Verdana, sans-serif; } } @media print { /* 印刷 */ body, select, input, button, textarea { font-family: "Hiragino Mincho ProN", "MS P明朝", serif; } } /******************************************************************************* * 汎用的なstyle ******************************************************************************/ html { overflow-y: scroll; /* Firefox:常に縦のスクロールバーを表示 */ } textarea { overflow: auto; /* IE:スクロールバー不要なら非表示 */ } img { border: none; /* リンク画像の枠線は非表示 */ -ms-interpolation-mode: bicubic; /* IE:サイズを変えた画像を綺麗にする */ } br { /* IE:letter-spacingを指定した要素内で偶数番目のbr要素が無視されるバグの回避 */ letter-spacing: 0; } hr { color: #eee; /* Firefox:色を他のブラウザに近付ける */ } optgroup { font: inherit; } input.number { /* 数値 */ text-align: right; } input.hint, input.placeholder, textarea.hint, textarea.placeholder { color: #728490; /* テキストボックス内の説明文 */ } input[readonly], input[disabled][type="text"], input[disabled][type="password"], textarea[readonly], textarea[disabled] { background-color: #eee; /* 使用不可系(IE6:非対応)*/ } .center { text-align: center; } /** * float */ .left { float: left; } .right { float: right; } .clear { clear: both; } /** * clear fix * @see http://nicolasgallagher.com/micro-clearfix-hack/ */ .clearfix:before, .clearfix:after { content: ""; display: table; } .clearfix:after { clear: both; } .clearfix { zoom: 1; /* for IE7- */ } /** * テキストボックスと選択リストは背景色を指定しても枠線が変わらないようにする * (IE6:非対応) */ select, input[type="text"], input[type="password"], textarea { border: solid 1px #7f9db9; } input[type="text"], input[type="password"], input[type="file"], textarea { padding: 2px; } /** * マウスカーソル制御 */ 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="radio"], input[type="file"] { cursor: pointer; /* 指差し(IE6:非対応、Firefox:file非対応)*/ } select[disabled], input[disabled], textarea[disabled], button[disabled] { cursor: not-allowed; /* 禁止マーク(IE6:非対応、IE7+:一部のみ) */ } body.wait, select.wait, input.wait, textarea.wait, a.wait { cursor: wait; /* 砂時計やクルクルにする(OSに依存) */ } /** * クリック時に破線を表示しない */ a, object { outline: none; /* IE7-:非対応 */ } input[type="submit"], input[type="button"], input[type="reset"], input[type="image"], input[type="checkbox"], input[type="radio"], input[type="file"], button { outline: none; /* IE7-:非対応、Firefox:imageのみ対応 */ } /** * 忘れやすいプロパティ */ table.collapse { /* cellspacing="0" cellpadding="0" */ border-collapse: collapse; } .nowrap { /* 改行防止 */ white-space: nowrap; } .text-more { /* テキストがはみ出す場合は途中まで表示して"..."を付ける */ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } table.text-more-table { /* .text-moreをtableに適用する場合 */ table-layout: fixed; width: 100%; /* 何らかの指定が必要。必要に応じて上書きする */ } .break-word { /* 半角英数字が続く文字列の強制改行 */ word-wrap: break-word; } table.break-word { /* table内で半角英数字が続く文字列の強制改行 */ word-break: break-all; /* Firefox:非対応 */ } .border-radius { /* 角丸(IE8-:非対応) */ border-radius: 10px; } .border-top-left-radius { /* 部分的に角丸(IE8-:非対応) */ border-top-left-radius: 10px; } /** * バリアフリーな色(必要に応じて追加する予定) * @see http://jfly.iam.u-tokyo.ac.jp/colorset/ * @see http://0-oo.net/sbox/javascript/universal-colors */ .bf-red { color: #f30; } .bf-green { color: #396; } .bf-gray { color: #728490; } .bf-pink { color: #f99; } /******************************************************************************* * サイトごとのstyle(サイトに合わせて上書きする) ******************************************************************************/ #bd { font-size: 116%; /* @see http://yuilibrary.com/yui/docs/cssfonts/#fontsize */ line-height: 1.4; /* 単位を付けない */ } select, input, textarea, button { margin-right: 1px; } optgroup { color: #728490; } option { color: #000; /* optgroupから引き継ぐ文字色を元に戻す */ } div.error, span.error { /* 入力エラーメッセージ */ color: #f30; font-weight: bold; } select.error, input.error, textarea.error { /* 入力エラー(Firefox:check系非対応)*/ background-color: #ffd3cc; border-color: #f30; } input.focused, textarea.focused { /* フォーカスされた入力要素 */ background-color: #ff9; border-color: #5f7d99; } label:hover { /* マウスカーソルを当てられたlabel */ background-color: #ff9; } pre { background-color: #ccc; } table.stripes tr.stripe { /* シマシマにされる行 */ background-color: #ccc; } /** * リンクの色 */ a { color: #03c; /* Bingの色 */ } a:visited { color: #639; /* Bingの色 */ } a:hover, a:active { color: #c11; /* Googleのactiveの色 */ } /** * ボタンの大きさ(IE6:非対応) */ input[type="submit"], input[type="button"], input[type="reset"], button { padding: 1px 1em; /* クリックしやすいように広げる */ line-height: 1.5; /* for IE */ overflow: visible; /* for IE7 */ } /** * IME */ input, textarea { ime-mode: active; /* デフォルトは IME On */ } select, input.han, input.number, textarea.han { ime-mode: inactive; /* 指定がある場合だけ IME Off */ } input[type="password"] { ime-mode: auto; /* Firefox:パスワードでautoだとIME不可になる */ }
javascript/pryn-js-css.txt · 最終更新: 2015/09/19 21:56 by dgbadmin