七転び八起きブログ

JavaScriptで読み込むCSSファイルをまるっと動的に入れ替えるには

2007年01月26日   :: JavaScript

今勤めている会社のサービス関連で、あったら便利だなーと思って、半年ほど前に作ったツールがありまして。

ツールって言っても、ラジオボタンをポチポチと選んでいくと動的にそのページで読み込んでいる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 ;
   }
}

JavaScriptだけで角丸Box。Nifty Corners(TM)がバージョンアップ。

2006年05月20日   :: JavaScript

ちょっと前に、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関数が一番最後にあります。

ゴリゴリと、力技でセレクタを分解して、エレメントのリストにしています。
これ、他でも便利に使えそう…。

CSS3のMulti-columnレイアウトを先取りする方法

2005年09月30日   :: JavaScript

次期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 Lint"でJavaScriptの文法を細かくチェックする

2005年08月19日   :: JavaScript

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コンソールが開いて結果が出力されます。

チェックしてくれる項目

  • 足りないセミコロン
  • ifやfor、while等のないところにあるブレース("{}")など
  • セミコロンではなくコンマで区切られてしまっている文
  • returnやthrow、continueやbreakによって、実行されることが無くなってしまっているコード
  • breakの無いcase文
  • おかしな場所でのインクリメントやデクリメント
  • voidが使われている時
  • "x+++y"の様に連続している"+"や"-"
  • ループ部分の中でlabeledステートメントが使われいる部分
  • ブレースが使われていないif, for, while
  • 数値の前や後についている小数点
  • A leading zero that turns a number into octal (base 8).(?)
  • コメント内にあるコメント
  • バーレンやアサインメント、コロンやカンマが前に来ていない正規表現
  • 一つの文なのかそうじゃないのか分からない、繋がった文
  • 何もしていない文

とのことです。
いやはや手元のJSファイルで試してみたらでるわでるわ…('A`)。


JavaScriptのイベントハンドラ周りでソースが汚くなっている人の為に

2005年06月29日   :: JavaScript

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);

みたいな感じでいける模様。
うまく使えばかなりすっきりする予感。

フォーム入力チェックJavaScriptライブラリ

2005年05月17日   :: JavaScript

tmt form ValidatorTMT 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 には、

  • email
  • lettersonly
  • alphanumeric
  • integer
  • positiveinteger
  • number

といったパターンが用意されてます。他にもファイルアップロード用にもいくつかデフォルトで用意されてます。日本語には対応していませんが…。

また、tmt:filters というattributeにて、入力禁止文字を指定できます。デフォルトで以下のものが用意されています。

  • ltrim
  • rtrim
  • nospaces
  • nocommas
  • nodots
  • noquotes
  • nodoublequotes
  • nohtml
  • alphanumericonly
  • numbersonly
  • lettersonly
  • commastodots
  • dotstocommas
  • numberscommas
  • numbersdots

これは先ほどのtmt:pattern とどう違うかというと、サンプルをいじってもらうと分かりやすいかと思いますが、入力禁止文字を入れると、リアルタイムに消去されます。

数字しか入れてはいけないフィールドにアルファベットを連打すると、どんどん勝手に消えていきます。

注釈をつけておかないと、キーボードが壊れたんじゃないかと、などと思われる恐れがあるので、目立つ形で注釈をつける必要があると思いますが、これは有用な予感が。

できれば勝手に消す前にalertを一発出した方が、使用者にとっては分かりやすいと思います。

こんな感じで他にもcheckbox用のものやselect用のものや色々と用意されているので、フォームの入力チェックが面倒くさくなったら、これで済ませてしまうのも良いかと思います。DreamWeaverとかFrontPageとかでも簡単につけられると言えばそれまでですが('A`)

実は一番感銘を受けたのは、submit後の入力チェックではねられると、inValidな部分のフィールドの背景が黄色になることだったりします。これは間違った場所が分かりやすくてよさげ。

mail TOP