
今勤めている会社のサービス関連で、あったら便利だなーと思って、半年ほど前に作ったツールがありまして。
ツールって言っても、ラジオボタンをポチポチと選んでいくと動的にそのページで読み込んでいるCSSファイルが変わるっていういだけの単純なシロモノです。
要は
:大きい文字サイズ :標準の文字サイズ
みたいなものを細かく組み合わせた奴です。
要はサイトのテンプレートをお客さんに選んでもらう時に、掛け算すると100種類以上になるテンプレートの中から(100種類以上のCSSがあるわけじゃなくて、CSSの組み合わせで100種類以上になる)、サムネイルだけで一つ選んでもらうのは、なかなかしんどいかなと思ったので作ったプレビューツールです。
JavaScriptでちょこちょこと作っていたら、仕組みとしてはなんとかなったのですが、どうもブラウザで問題が。
最初はこのへんのコレクションを使ってやってました。
document.all.tags('LINK')
document.getElementsByTagName('LINK')
このhrefプロパティを変えて、やってたわけです。だけどこれが大体お客さんの10%くらいが落ちて使えなかったそうです。
FirefoxとかOperaは問題ないのですが、とにかくIEがダメ。うちのお客さんはほとんどIEなのでこれはマズイ。
一度
document.getElementsByTagName('LINK').item(i).href = "" ;
document.getElementsByTagName('LINK').item(i).href = "xxx.css"
っていう風に一旦空にしてから新しいCSSを読み込ませるようにしたら結構良くなったのですが、それでもまだ落ちまして。もうちょっとなんとかしたいな、と思いつつ忙しさにかまけてそのままにしていました。
で、最近そのJavaScriptを別のシステムに組み込まないといけなくなったのでブラッシュアップすることに。それでなんとなく、link要素じゃなくて、document.styleSheets経由でやったらどうかなと思ってやってみた。
document.styleSheets[n].href = "xxx.css"
そしたら、ビンゴでした。全然落ちなくなりました。なぜ?
しかも、こいつのhref属性ってReadOnlyなんです。でもなぜかIEでは書き込めてしまう。IE7でも書き込めてしまった。なぜ。
仕様書見ると
// Introduced in DOM Level 2:
interface StyleSheet {
readonly attribute DOMString type;
attribute boolean disabled;
readonly attribute Node ownerNode;
readonly attribute StyleSheet parentStyleSheet;
readonly attribute DOMString href;
readonly attribute DOMString title;
readonly attribute MediaList media;
};
http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet
…やっぱり readonly 。Firefoxだとこれは動かないので、Firefoxが正しい挙動ですね。
そもそも元々のdocument.getElementsByTagNameを使うやり方でFirefoxとかは落ちなかったので、ブラウザを振り分けてIEだけdocument.styleSheetsを使うことで、結果的には満足いく感じになったのですが、なんか後味が…。
JavaScriptは難しいですなぁ…。あ、FireBugって本当に便利ですね。びっくり。もっと早く使えばよかった…。
■Firebug | Firefox Add-ons | Mozilla Corporation
https://addons.mozilla.org/firefox/1843/
ちなみにこんなソースでやってました。振り分け部分は省略。
ラジオボタンのonChangeイベントをきっかけにぐるぐる回してました。the力技。
function changeCSS(cssColorName,cssMainName){
var targetObj = document.getElementsByTagName('LINK') ;
targetObj.item(1).href = "" ;
targetObj.item(1).href = cssColorName ;
targetObj.item(0).href = "" ;
targetObj.item(0).href = cssMainName ;
}
function changeCSS(cssColorName,cssMainName){
if( document.styleSheets ){
var css_list = document.styleSheets ;
css_list[0].href = cssMainName ;
css_list[1].href = cssColorName ;
}
}
ちょっと前に、CSSの角丸の話題が盛り上がり気味だったみたいで、うちの記事「CSSだけで、フレキシブルな角丸ボックスを作る方法」にも結構人が来ていてびっくり。
はてなの方をみると、「tikeda::Diary - 角丸ライブラリ」が人気です。やっている内容は、「Flexible box with custom corners and borders | 456 Berea Street」と同じなのですが、色々サンプルのバリエーションを用意していたりPSD込みだったりで、親切でよさげです。
さて、前のエントリーでも指摘されましたが、CSSだけだとどうしてもマークアップ的に汚くなってしまうという欠点があります。
マークアップを重視するなら、どうせ見た目の問題なんだし、JavaScriptとCSSでやってしまってもいいですよね。
その思想で作られたNifty Cornersというテクニックがあります。これについては、検索していただければ、相当数の解説記事が浮かび上がってくると思いますので割愛…。
今回は、なんていうかそれがバージョンアップしてNifty Corners Cube として公開されたという内容です。
とは言っても、前回のバージョンを使ってないので何がどうなったのかあまり分かってないのですが…。仕様やサンプルは以下の本家のページを見てもらえれば、一番良いと思います。
【本家はこちら】■Nifty Corners Cube - freedom to round
なんていうか、ぶっちゃけこちらの方の記事に刺激されて、もうちょっと掘り下げネタです(笑)
戯れ言日記 - 角丸ライブラリかぁ
とりあえず、このサイトのblockquote全てに、角丸を設定してみた。
ちょっと躓いたのですが、なんか、上手く行かなかった人は、niftycube.js内で、別のCSSをリンクさせてるところを、絶対パスで書くと上手く行くかも。
var niftyOk=(document.getElementById && document.createElement && Array.prototype.push);
二つのメソッドと、後Arrayオブジェクトがpushメソッドを使える環境なら"NiftyOK!"
var niftyCss=false;String.prototype.find=function(what){
return(this.indexOf(what)>=0 ? true : false);
}
Stringオブジェクトそのものに、findというメソッドを追加してます。
自分の中に、引数の文字列があったら、trueを返すという。
var oldonload=window.onload;
oldonloadというオブジェクトに、window.onloadイベントハンドラを複製
if(typeof(NiftyLoad)!='function') NiftyLoad=function(){};
NiftyLoad関数がなければ、作成。
if(typeof(oldonload)=='function')
window.onload=function(){oldonload();AddCss();NiftyLoad()};
else window.onload=function(){AddCss();NiftyLoad()};
oldonloadイベントハンドラに関数が
→セットがされていれば、window.onloadイベントハンドラに、oldonload、AddCss、NiftyLoad関数をセット。
→セットされてなければ、window.onloadイベントハンドラに、AddCss、NiftyLoad関数をセット。
function AddCss(){
niftyCss=true;
var l=CreateEl("link");
l.setAttribute("type","text/css");
l.setAttribute("rel","stylesheet");
l.setAttribute("href","niftyCorners.css");
l.setAttribute("media","screen");
document.getElementsByTagName("head")[0].appendChild(l);
}
■AddCss関数について。(外部CSSを読み込んでくる関数)
niftyCssにTrueをセット。多分、起動フラグ。
niftyCorners.cssというCssファイルをJavascript経由で読み込ませる。
※JSファイル置いてみたけど、上手く行かなかった方は、
ここのCSSのパスを絶対パスにするといいかもです。
function Nifty(selector,options){
if(niftyOk==false) return;
if(niftyCss==false) AddCss();
var i,v=selector.split(","),h=0;
if(options==null) options="";
if(options.find("fixed-height"))
h=getElementsBySelector(v[0])[0].offsetHeight;
for(i=0;i<v.length;i++)
Rounded(v[i],options);
if(options.find("height")) SameHeight(selector,h);
}
■Nifty関数(セレクタとオプションが引数)
基本的にNifty()→Rounded()→AddTop() , AddBottom() と関数が連携して、丸くするみたいです。
こいつがスタート地点です。HTMLでヘッダ部分に書いてあげる関数です。
冒頭でniftyOkじゃなければ、ここで終わり。非対応ブラウザへの配慮。
niftyCssがfalse、つまりAddCss関数が実行されてなければ、実行。
引数として与えられたセレクタ(selector)をカンマ区切りで配列vに入れて、ついでに変数iとhを作る。
optionsがnullなら、空にしておく。
optionsの中に"fixed-height"があったら、後で出てくる"getElementsBySelector"関数(これ、めっさ便利ですな)とoffsetHeightプロパティを使って、対象のエレメントに設定されてる高さを維持する。
そして、後述する"Rounded関数"に対象のエレメントとオプションを渡す。
もし、optionsの中に'height'という文字列が合ったら、対象のエレメントを全て同じ高さにする"SameHeight"関数を動かす。
function Rounded(selector,options){
var i,top="",bottom="",v=new Array();
if(options!=""){
options=options.replace("left","tl bl");
options=options.replace("right","tr br");
options=options.replace("top","tr tl");
options=options.replace("bottom","br bl");
options=options.replace("transparent","alias");
if(options.find("tl")){
top="both";
if(!options.find("tr")) top="left";
}
else if(options.find("tr")) top="right";
if(options.find("bl")){
bottom="both";
if(!options.find("br")) bottom="left";
}
else if(options.find("br")) bottom="right";
}
if(top=="" && bottom=="" && !options.find("none")){top="both";bottom="both";}
v=getElementsBySelector(selector);
for(i=0;i<v.length;i++){
FixIE(v[i]);
if(top!="") AddTop(v[i],top,options);
if(bottom!="") AddBottom(v[i],bottom,options);
} }
■Rounded関数(対象のエレメントと、オプションを引数に。)
まず、ここは元ドキュメントのoptionパラメータのとこを見てもらうと、すぐあれだと思うのですが、
| tl | 左上の角 |
|---|---|
| tr | 右上の角 |
| bl | 右下の角 |
| br | 左下の角 |
| top | 上二つの角 |
| bottom | 下二つの角 |
| left | 左側二つの角 |
| right | 右側二つの角 |
| all (default) | 全ての角(デフォルト) |
| none | なし |
となってます。
まず、最終的にはtlとtrとblとbrの組み合わせになるように、optionsの値を置換。
関数の中で宣言してるtopとbottomに、"left"とか"right"とかの値が入っていきます。
ここが丸くなるのね。
そして、対象のエレメントと、丸くする部分と、元のoptionsの値を、FixIE関数を通した後に、AddTop関数とAddBottom関数を動かして、実際に丸くさせていきます。
function AddTop(el,side,options){
var d=CreateEl("b"),lim=4,border="",p,i,btype="r",bk,color;
d.style.marginLeft="-"+getPadding(el,"Left")+"px";
d.style.marginRight="-"+getPadding(el,"Right")+"px";
if(options.find("alias") || (color=getBk(el))=="transparent"){
color="transparent";bk="transparent"; border=getParentBk(el);btype="t";
}
else{
bk=getParentBk(el); border=Mix(color,bk);
}
d.style.background=bk;
d.className="niftycorners";
p=getPadding(el,"Top");
if(options.find("small")){
d.style.marginBottom=(p-2)+"px";
btype+="s"; lim=2;
}
else if(options.find("big")){
d.style.marginBottom=(p-10)+"px";
btype+="b"; lim=8;
}
else d.style.marginBottom=(p-5)+"px";
for(i=1;i<=lim;i++)
d.appendChild(CreateStrip(i,side,color,border,btype));
el.style.paddingTop="0";
el.insertBefore(d,el.firstChild);
}
■AddTop関数(対象エレメント , 丸くする部分 , オプション が引数)
そろそろ、実際に丸くするところみたいですー。
一気に変数を宣言。CreateElっていうのは、後で出てきますが、CreateElementのことでした。
d,lim,border,p,i,btype,bk,colorと、たくさんの変数がっ。
それぞれ、後に出てくるgetParentBk関数とかMix関数で、対象のオブジェクトにあったプロパティを、CreateElで作ったbエレメントに与えていきます。getPaddingなんていう関数、最初からあったんですね、全然知らんかったです。使えるのかな。
なんか、おっかけていくのがだんだんしんどくなってきました。こまいです。
【参照サイト】G の索引 (共通 DOM API)
AddBottom関数はほぼ同じだから省略で…。
後、色々サブルーチン的なのも省略で…。
最後に一つだけ。
便利な getElementsBySelector関数が一番最後にあります。
ゴリゴリと、力技でセレクタを分解して、エレメントのリストにしています。
これ、他でも便利に使えそう…。
次期CSSとして現在WorkingDraftなCSS3ですが、その中に、新聞のような多段カラムを実現するモジュールが予定されています。
具体的にはCSS3 module: Multi-column layout仕様書原文というものです。昔あったNetscape独自タグのMulticolを思い出させます。
ちなみにCSS3の策定中の草案は色々なサイトで邦訳がされております。
・CSS3のモジュール:テキスト
・CSS3ハイパーリンクプレゼンテーションモジュール(邦訳)
・Web関連技術の仕様書邦訳
もちろん現在のブラウザには基本的には実装されていないのですが(Geckoエンジン系は-moz-つきのでテスト実装されてたんでしたっけ。)、一足先にこれをJavaScriptで使ってしまおうというものが、A List Apart(→A List Apartトップ)に記事としてあげられていました。
該当記事は
A List Apart: Articles: Introducing the CSS3 Multi-Column Moduleです。
最初の方では、多段コラムが再び光を浴びてきた事情や、実際にCSS3に加わった際にどういったプロパティになるか、などの解説になります。
そして、「An interim measure for experimentation」の部分から、JavaScriptで一時的に多段カラムを実現する方法の部分になります。
まず、サンプルページがCSS3 Multi-Colum Layout Demonstration and Documentation - CSScripting.comです。ここのTestCaseの部分から各サンプルページに行くことができます。
これを実現するには、一枚のjsファイルを外部から読み込むだけです。それだけで、後はCSS3のカラム周りのセレクタを使えるようになります。例えば
.article {
column-count: 2;
column-gap: 20px;
}
の様に。
実際にローカルでやってみましたが、ちゃんと再現されました(当たり前か)。
JavaScriptがDOM経由でCSSを取得して処理するので、JSファイルを読み出す前にCSSを外部から読み込んでおかないといけません。なので、header内での順番に注意です。
また、あまり複雑なセレクタだと上手くスタイルを取得できないようです。できるだけシンプルなセレクタにするべきだそうです。
それに気をつけていれば、ひとまずOKっぽいです。視線の動きにさえ注意して上手く使えば段組は確実に可読性を向上させると思いますし、デザインの幅も広がりますので、よさげではないでしょうか( ´-`)
JavaScriptのデバッグは、なかなか面倒です。
ひとまず動作させるだけを目指すなら、MozillaのJavaScript Consoleが、InternetExplorerのエラー表示と比べて、かなりしっかりエラーを出してはくれますので、便利です。
しかし、「ブラウザが適当に上手くやってくれているから動いているだけで、文法的には微妙」なところがチェックできないのが難点です。可能な範囲で、できるだけきちんとしたコードを書きたいですよね('A`)
そんな時に便利なものを作った方がいます。その名もストレートに"JavaScript Lint"。
OutOfHanwell.comの中の人が、 Douglas Crockford氏のJSLintを元に作ったものだそうです。
使い方は簡単です。JavaScriptLintのページの、「Download JavaScript Lint (jsl.zip)」をダウンロードし解凍すると、いくつかファイルが入っています。
1.そのフォルダの中に、チェックしたいファイルを入れます。
2.そして、「jsl.default.conf」をテキストエディタで開き、最後にある"+proces"の後に、チェックしたいJSファイルの名前を書きます。
3.jsl-sample.batを起動すると、DOSコンソールが開いて結果が出力されます。
とのことです。
いやはや手元のJSファイルで試してみたらでるわでるわ…('A`)。
JavaScriptでちょっとアプリケーションくさいものを作ろうとすると、途端にソースにJavaScriptが繁茂し始めます。HTMLの可読性も損なわれるし、コードも分散してしまってメンテも面倒くさい。
そんな人たちの為に、あらかじめHTMlの各要素に、イベントハンドラを仕込んでおけるスクリプトが公開されています。
#→Behaviour : Using CSS selectors to apply Javascript behaviours
要素の指定は、CSSのセレクタをそのまま使えます。
var myrules = {
'#example li' : function(el){
el.onclick = function(){
this.parentNode.removeChild(this);
}
}
};
Behaviour.register(myrules);
みたいな感じでいける模様。
うまく使えばかなりすっきりする予感。
TMT Validatorは、フォーム入力チェック用JavaScriptのライブラリです。
使い方は、外部JavaScriptファイルを一つ読み込み、後は各HTMLのタグ内にattribute等を書くだけです。ちなみにJSファイルはダウンロードしたZIPの中の"script_tmt_validator.js"という600行弱のファイルです。
サンプルはTMT Validator - Validation samplesにありますので、そちらで体験も出来ます。ちなみに、使ったときの挙動としては、普通のチェックスクリプトです。
使い方は詳しくはTMT Validator Syntax Referenceのページにあります。具体的には…
<form action="index.htm" tmt:validate="true" tmt:callback="displayError">
の様にformタグに書き加えます。tmt:validate="true"で使用する状態になります。CallBackは、独自に関数を書いて、好みの処理をしたい場合に使います。以下のページに各formオブジェクトを取ってくる方法など書いてあるので、必要な方はこちらに。
#TMT Validator - Customizing errors notification
それぞれのinputタグには、同様に tmt:pattern 等のattributeを追加してチェックのパターンを決めます。これは、submitをした時にアラートとして出るものです。例えば、tmt:pattern には、
といったパターンが用意されてます。他にもファイルアップロード用にもいくつかデフォルトで用意されてます。日本語には対応していませんが…。
また、tmt:filters というattributeにて、入力禁止文字を指定できます。デフォルトで以下のものが用意されています。
これは先ほどのtmt:pattern とどう違うかというと、サンプルをいじってもらうと分かりやすいかと思いますが、入力禁止文字を入れると、リアルタイムに消去されます。
数字しか入れてはいけないフィールドにアルファベットを連打すると、どんどん勝手に消えていきます。
注釈をつけておかないと、キーボードが壊れたんじゃないかと、などと思われる恐れがあるので、目立つ形で注釈をつける必要があると思いますが、これは有用な予感が。
できれば勝手に消す前にalertを一発出した方が、使用者にとっては分かりやすいと思います。
こんな感じで他にもcheckbox用のものやselect用のものや色々と用意されているので、フォームの入力チェックが面倒くさくなったら、これで済ませてしまうのも良いかと思います。DreamWeaverとかFrontPageとかでも簡単につけられると言えばそれまでですが('A`)
実は一番感銘を受けたのは、submit後の入力チェックではねられると、inValidな部分のフィールドの背景が黄色になることだったりします。これは間違った場所が分かりやすくてよさげ。