Debug:
Array
(
[0] => 1282667147|開発日誌,JavaScript|FirefoxでのouterHTML,outerHTML-in-Firefox| innerHTML に比べると、outerHTML を使う頻度はぐっと少ない。現状、IE と Safari , Chrome , Opera が対応していて、未対応はFirefox だけ。使用頻度が低いメソッドに対して、Firefox向けに、長々しいスクリプトを書くのもどうかねぇ、と思ってググってみたところ、ナイスな投稿がありました 。``sOuterHTML = new XMLSerializer().serializeToString(oElement); `` 確かに、これは理にかなってますね。outerHTML自体は、値の取得だけでなくて、設定も出来るんだけど、そういう場面はまずないだろうから、このgetterだけで十分ですね。``<div id="Here">Hello, <strong>really strange</strong> World !</div> `対して、innerHTML なら`Hello, <strong>really strange</strong> World ! `が返されるし、outerHTML `なら、`<div id="Here">Hello, <strong>really strange</strong> World !</div> `が返される。実装例としては、``function getOuterHTML(e) {` if(e.outerHTML){ // IE, Webkit, Opera` return e.outerHTML;` } else { // Firefox (, Webkit, Opera)` return new XMLSerializer().serializeToString(e);` }`};` `あるいは、短く``function getOuterHTML(e) {` return e.outerHTML || new XMLSerializer().serializeToString(e);`};` `とか。`outerHTML
Hello, really strange World !
`
`[script]`function getOuterHTML(e) {` if(e.outerHTML){ // IE, Webkit, Opera` return e.outerHTML;` } else { // Firefox (, Webkit, Opera)` return new XMLSerializer().serializeToString(e);` }`};`[/script]`` 実際には、Firefoxだと xmlns="http://www.w3.org/1999/xhtml" という属性が付加されるので、気になるようなら適宜省けば良いでしょう。|martin|1|1|||
[1] => 1282574381|開発日誌,JavaScript|ひとつの記事に複数の拡大画像がある場合| 簡易画像ギャラリーのテスト。タイトルのような場合、ひとつの画像をクリックして、拡大表示にした際に、ポップアップした画面の右端に矢印が表示される。それをクリックすると、同じ記事に含まれる次の画像を読み込む仕様。`` 特に画像キャプションが付いている場合は、それも表示されるので、写真を主体に見せたい場合なんかには、好都合だと思います。`` ` ` ` |martin|1|1|||
[2] => 1281709863|開発日誌,JavaScript|表示オンオフ制御周りの改良| ppBlogのoParts.jsにtweenアニメーションみたいな動きを取り入れてみました。まだ試作段階ですが、テーマ:Basicであれば、左側のサイドバーの展開や、ソーシャルブックアイコンで、ちょっとした動きをみることが出来ます。`` 写真のポップアップにも。` |martin|1|1|||
[3] => 1281223799|開発日誌,JavaScript|メソッドチェーンで遅延処理をしたい,method-chain-with-delay| こんばんは、martinです。タイトル通りなんですが、要は以下のようなことを簡単に 実装したい。``foo.await (2000).say("Hello, ").await (1500).say("world !"); `` まぁfoo という関数オブジェクトがあるとして、2秒後に「Hello, 」 を出力して、そこで小休止、で1.5秒後に「world !」 を出力すると。それをメソッドチェーンに拘ってやりたい。簡単な例を挙げてみます(うまく行かないサンプルです )。``var foo = function(){}; // 関数オブジェクトを作成``foo.prototype = { // プロトタイプを設定` say : function(s){` alert(s);` return this ;` },` await : function(ms){ // ここで遅延処理をゴニョゴニョしたい` // var _this = this;` // setTimeout(function(){` // return _this; # まぁ、これはダメだけど、こんな感じでやれたらいい` // }, ms);` return this ;` }`}``var bar = new foo();`bar.await(2000).say("Hello"); // すぐに「Hello」が出力される `` メソッドチェーンを実装するとなると、return this; をかまして、用意した関数オブジェクト自身を返すようにする必要があります。上のawait関数内で、setTimeoutを用いて遅延処理をしようと思いましたが、どうもうまい方法が思いつかず、とあるサイトで質問してみました 。すると、数分後(!)に返答があって、「それ、jQueryで出来るよっ! 」と[zzz/](そのコメントは今は消されたようなんですが) まぁ確かにそうなんですが(なのであえて.delay という単語を選んだ)何かすごい技があって、それで解決!みたいなことを期待していたわけです(jQueryを使わずに)。`` 2つ目に付いたコメントで、簡単に済ませる方法はなくて、何か別のキューシステム(待ち行列システム?)を用意する必要があるよ、と。やっぱりそうか。で、沢山ヒントを頂いたんで、自分なりに作ってみました。次のようなキューシステムを用意しました。```Queue = {` entries : [], // 関数を登録する配列を用意` inprocess : null,` enqueue : function(entry){ // 関数を登録する` Queue.entries.push(entry);` },` flush : function(){ // 配列に登録された関数群を一気に実行` if(Queue.inprocess) return; // 「待ち中」なら実行しない` while (Queue.entries.length){` var entry = Queue.entries.shift(); // 配列の頭から取り出す(つまり登録順)` if(entry.toString().indexOf('await:') !== -1){ // 「待ち」のマークを見つけたら` var ms = Number(entry.split(':')[1]);` Queue.inprocess = setTimeout(function(){` Queue.inprocess = null;` Queue.flush(); // 指定時間後に処理を再開` }, ms);` return;` }` entry(); // 取り出された関数を実行` }` }`} ` こいつを最初のサンプルに適用すると以下のような感じになります。``var foo = function(){};``foo.prototype = {` say : function(s){` Queue.enqueue(function(){ // 配列に関数を登録` alert(s);` });` Queue.flush();` return this;` },` await : function(ms){` Queue.enqueue('await:' + ms); // 配列に「await:2000」みたいなマークを登録` return this;` }`} `` 自分のスキルではこの程度です[zzz/] キューシステムに頼らずに自己完結できる目から鱗な手法がないですかねぇ。``とりあえず動作サンプルを以下に。ボタンを押すと、``var bar = new foo();` bar.await(1500).say('Hello, ').await(2000).say('world!'); `ってのが実行されます。ここでは、ボタンを押して1.5秒後に「Hello, 」、更に2秒後に「world !」とボタンの右側に表示されます。```遅延実行 `
` 何か、「こうしたらいいよ」とかのアドバイスあればよろしくお願いします。`[script]`var foo = function(){};`foo.prototype = {` say : function(s){` Queue.enqueue(function(){` o("#H").html(s, 'add');` });` Queue.flush();` return this;` },` await : function(ms){` Queue.enqueue('await:' + ms);` return this;` }`}``Queue = {` entries : [],` inprocess : null,` enqueue : function(entry){` Queue.entries.push(entry);` },` flush : function(){` if(Queue.inprocess) return;` while (Queue.entries.length){` var entry = Queue.entries.shift();` if(entry.toString().indexOf('await:') !== -1){` var ms = Number(entry.split(':')[1]);` Queue.inprocess = setTimeout(function(){` Queue.inprocess = null;` Queue.flush();` }, ms);` return;` }` entry();` }` }`}``function sayHello(){` var bar = new foo();`bar.await(1500).say('Hello, ').await(2000).say('world!');`}`[/script]|martin|1|1|||
[4] => 1280546339|開発日誌,JavaScript|IE8でのHTML5要素有効化あれこれ,various-HTML5-enabling-scripts| もう7月も終わりですね、martinです。相変わらず時の流れは速いもので。`` このサイトのブログの基本テーマ(スキン)「Basic」では、試験的にHTML5 を導入しています。HTML5では新しい要素がいくつか追加 されていますが、IE9 未満のブラウザでは、article やheader , nav などのクールで新しい要素に対するスタイルシート指定が効かない、という事が昔から知られています。これに対するアプローチとして、 document.createElement を使うとスタイルシートでの指定が可能になるよ、というのがよく知られています。初出は Sjoerd Visscher さんあたりでしょうかねぇ。`document.createElement(”article”); // 未知の要素articleに対してIEでもCSS指定が可能になる `` このdocument.createElementを使うテクニックは、いろんなところで見かけることができて、有名どころでは、Remy sharp氏のhtml5.js あたりですね。`` もっとも、このテクニックを使っただけでは、IEでHTML5化を効かせたページを印刷したときまでは反映されないので、これに対しては、IE Print Protector がよく知られていると思います(個人的には、印刷のサポートまではあまり興味がありませんが )。`` で、このcreateElementを使うテクニックは、ベタに書けば以下のような感じになります。``// ここでは簡潔化のため、以下の6個の新要素に絞ってます。`<!--[if lt IE 9]>`var html5_elements = ["header", "nav", "article", "section", "aside", "footer"]; `for (var i = 0, len = html5_elements.length; i < len; i++){` document.createElement(html5_elements[i]); // 各要素に適用`}`<![endif]--> `` どこで最初に見かけたのか失念しましたが、これを一行(ワンライナー)で簡潔に済ませたスクリプトを見たときはいたく感心しました。Dean Edwards氏のブログ もこうなっています。`"header,nav,article,section,aside,footer".replace(/¥w+/g, function(a){document.createElement(a)}); ` 上のポイントは、replaceメソッドの引数に関数を指定できて、その関数内では、正規表現にマッチした要素を適宜適用していく点です。ループみたいなことを勝手にやってくれる点ですね。おそらくこれ以上短くは書けないのではないかと思うのですが、息抜きに自分なりにいくつか考えてみました。`"header,nav,article,section,aside,footer".split(',').sort(function(a){return document.createElement(a)*1}); ` これも、似たような発想からですね。自動でなんかやってくれるという。`with("header,nav,article,section,aside,footer".split(',')))while(length)document.createElement(pop()); ` ここではwith 構文を使ってみました。これもなかなかシンプルです。同じくwithと、Enumerator を使って、`with(new Enumerator("header,nav,article,section,aside,footer".split(',')))for(;!atEnd();moveNext())document.createElement(item()); ` これは、ちょっと長いし、Enumerator Object はマイナーですかね。`` 短さ命で、グローバル変数の汚染なんて気にしないぜっ、という向きには以下のようなものもありかと思います。``s="header,nav,article,section,aside,footer".split(',');while(s[0])document.createElement(s.pop()); `` for 文関連では、``for(i in s="header,nav,article,section,aside,footer".split(','))document.createElement(s[i]); `とか`for(i=0;n="header,nav,article,section,aside,footer".split(',')[i++];document.createElement(n)); `とかですかねぇ。`` ちなみに、配列を作るのに、文字列にsplit() をかませるというのは、よく見かける手法です。ここでの、6個ぐらいの要素数ではあまり差は出ませんが、要素数が増えてくると、逐一ダブルクォート(or シングルクォート)で括っていくやり方と大きく差が付いてきます。``var html5_elements = ["header", "nav", "article", "section", "aside", "footer"]; // これより`var html5_elements = "header,nav,article,section,aside,footer".split(','); // こっちが記述が短い `` 正規表現から配列も出来ますね。``var html5_elements = "header,nav,article,section,aside,footer".match(/¥w+/g); `` 以上、トリビアルなエントリーでした[署名/]|martin|1|1|||
[5] => 1280233709|開発日誌,JavaScript|HTML5 FileAPIの不満な点,inflexible-HTML5-File-API| こんにちは、martinです。なかなかまとまった時間が取れないので、ppBlogの最新版をリリースできずにいます[zzz/]`` 最新版のバージョンとしては、v1.9.0 を予定しています。新機能としては、コメント認証機能 だとか、HTML5を意識したppBlogエンジンでしょうか(ちなみにテーマBasicのソースはHTML5仕様ですよ)。`` で、このHTML5 なんですが。過去のエントリーでいくつかあるように、最新版では、記事投稿の際に複数ファイルアップロードが可能になっています。HTML5のFileAPI を積極的に採り入れたものにしようか、それとも「レガシーな」もので実現させるか迷いましたが、とりあえず「レガシー」なバージョンで行こうと思います。理由はいくつかありますが、一番の理由は、どうもHTML5のFileAPIの使い勝手が悪い点です。`` ブログでの写真投稿において、一度に複数枚の写真を選択してアップロードできる点は、HTML5を使う魅力のひとつですが、この「複数枚のファイルを選択 」というのが、どうも洗練されていない印象です。例えば、アップする写真を複数枚決めてそれをアップロードする場合を考えてみます。頭の中では、大まかに写真をのせる順番が決まっていて、ファイル選択画面から、コントールキー(CTRL )を押しながら、順序良く写真をいくつか選んだとします。最新のブラウザなら以下のように、multiple を指定するだけで、この複数枚選択という動作が可能になります。``<input type="file" name="src[]" multiple /> ``でも、残念なことに、この選んだ順序 というのは、FileList には反映されない のです。否応なしに、ファイル選択画面上で表示されている通りの順序になるようです。何か、選んだ順序を紐付けするような属性が欲しくなります。`` もうひとつ 。選んだ複数枚のファイルは、FileList配列に収められて、あたかも配列のようにDOM操作が可能ですが、この配列は、readonly 、つまり読み取り専用 のようです。たとえば、ちょっと余分に画像を選んじゃったよ、という場合に、その余分なファイルだけをリストから除きたいというケースは、間違いなく出てくると思いますが、そういう操作は出来ないようです。再度、すべてのファイルを選び直す必要があります。これは、ユーザーには使い勝手が悪いですね。`` というわけで、上記の理由により、ppBlogではW3C FileAPI の積極的な採用は見送りました。でも、HTML5に頼らずとも、一度に複数枚のファイルのアップロードは可能です。Stickman さんが、2005年に、すでにそういうギミックを見付けていました 。最初見たときは、目から鱗でした。やってることはすごくシンプルなんですが、まさにコロンブスの卵ですね。で、彼のスクリプトは、ppBlogにはちょっと冗長でしたので、参考にしつつ、ppBlog仕様にしました。実際のデモを見てみましょう。尚、デモでは「アップロード」ボタンは、文字通りただのボタンなので、実際にはファイルはアップロードされませんが、雰囲気は十分に伝わると思います。`` レガシーな複数ファイルアップロードのデモ →http://p2b.jp/demo/EasyFileUpload.html
`` また、Firefox最新版など、File APIに十分対応しているブラウザでは、選んだ画像をサムネイル表示するようにしています。Firefoxだと以下のようなスクリーンショットです。Firefox以外では、普通に選んだ画像のファイル名が選んだ順にリスト表示されます。`` ``この部分のスクリプトは以下の感じ。`` if(window.File && window.FileList && window.FileReader){ // FileAPIに対応しているなら` var file = el.files[0];` if(file.type.match(/image.*/)){` var reader = new FileReader(); // FileReader オブジェクト!` reader.onload = function(){` var span = d.createElement("span");` span.innerHTML = '<img class="thumb" src="' + this.result + '" alt="preview" /> (' + Math.round(file.size / 1000) + ' KB)';` li.insertBefore(span, li.lastChild);` };` reader.readAsDataURL(file); // 画像データの読み込み` }` } `` レガシーなインターフェイスですが、任意のリストを削除出来ますし、HTML5仕様より却って高機能(=使い勝手が良い)な気がします。`` ちなみに、デモのソースを見ていただけると分かりますが、INPUT[type=file] 要素に、レガシーにonchange イベントハンドラを仕込んでいます。document.addEventListerで監視しても良いのですが、IEでは、onchangeイベントがバブルしないようで[うーむ/]`` 余談ですが、「目から鱗」の語源は、新約聖書の「使徒行伝(しとぎょうでん)」中のエピソード、見えなくなっていたパウロ (サウロ)の目からうろこのようなもの(コンタクトレンズ?)が外れて再度見えるようになったという「パウロの回心」からですね。「豚に真珠」と同様、日本とか中国由来と思いきや、新約聖書からの諺です。
|martin|1|1|||
[6] => 1269996654|開発日誌,JavaScript|IE9ではDOMContentLoaded をサポートするようだ,IE9-supports-DOMContentLoaded| こんにちは、martinです。久しぶりの書き庫な気がします。`` IE9のプラットフォームプレビュー が出ていたんで、インストールしてみました。最初に試したことは、他の主要なブラウザでは実装済みのDOMContentLoaded がサポートされているかなぁ、ということでした。現時点では、まだでしたが、IEBlog によると今後のロードマップに、DOMContentLoadedのサポートがあるようです。DOMContentLoaded自体は、DOM Level 3 Eventsには定義されていないので、IE9が標準仕様に忠実であろうとするならば実装は微妙だなぁ、と思っていたんですが、HTML5には定義されています (注意: リンク先はかなりページが重い )ね。`` IE9でDOMContentLoadedがサポートされるのはまことに喜ばしいことですが、IE9が普及するのはまだまだずっと先の話でしょう。なので現状では、IE向けにはDOMContentLoaded相当の機能を自前で実装する必要があります。世の中には、jQueryを始めとした便利なJavaScriptライブラリーがいくつかありますが、猫も杓子もDiego Perini氏が発見したdoScrollメソッドを使う手法 に則っています。ppBlogでのoParts.js でも、doScrollの実行をもって、DOMパース完了とみなすやり方で実装しています。`` さて、このdoScrollメソッドですが、MSDNの説明にあるように、` ` The doScroll method is available on all objects, regardless of whether they support scrollbars.
` 引用元: doScroll Method (A, ABBR, ACRONYM, ...) ` `` ほぼすべての要素について適応可能です。多くのJavaScriptライブラリーでは、``document.documentElement.doScroll('left'); `` というふうに、document.documentElementに適用しているケースが殆どですが、何でも良いので、以下のようなやつもOKです。```(function(){` if(navigator.userAgent.match(/MSIE/)){ // IEなら` try {` new Image().doScroll() ; // new Imageオブジェクトについて適用` onReadyFunc(); // DOM構築後に最初に呼び出す関数` } catch(e){ setTimeout(arguments.callee, 1);}` } else { // IE以外` document.addEventListener("DOMContentLoaded", onReadyFunc, false); // DOM Level 3 Events相当` }`})(); `` 別にnew Imageオブジェクトをdocument.body にappendChild する必要もありませんし、メモリーリークも起こしません。以下のようにコメント文を生成するメソッドcreateComment()を用いても良いでしょう。これも、既存の文書に作ったコメントをappendする必要はなく、ただ宣言するだけで動きます。```(function(){` if(navigator.userAgent.match(/MSIE/)){ // IEなら` try {` document.createComment().doScroll() ; // document.createCommentについて適用` onReadyFunc(); // DOM構築後に最初に呼び出す関数` } catch(e){ setTimeout(arguments.callee, 1);}` } else { // IE以外` document.addEventListener("DOMContentLoaded", onReadyFunc, false); // DOM Level 3 Events相当` }`})(); `` doScroll()の引数は省略可能です。省略するとscrollbarDown が適用されるので、document.documentElement.doScroll()だと、実際に画面がスクロールしてしまうかもしれません(なのでたいてい”left” を指定している)。new ImageやcreateCommentだと、DOMツリーに追加するわけではないので引数は省略して構いません。`` new Image().doScroll()なんて、短い記述で済むんでいいなぁ、と思うけれど。[署名/]|martin|1|1|||
[7] => 1261627359|開発日誌,JavaScript|CSS3のtransformをIE8でも使いたい,CSS3-Transform-for-IE8| こんばんは、martinです。Firefox やWebKit系のSafari 、グーグルのChrome などは、CSS3 の魅力的な機能を色々取り入れています。中でも、CSS3のtransform やドロップシャドウ系は、ブログで写真を見せる場合などに凝った演出が出来るので、仕様が正式に決まるのが待ち遠しいですね(そう言えば、ボックスのドロップシャドウ box-shadow ですが、CSS3の勧告候補から消えた のかなぁ)。便利な仕様だとは思うのですが。`` ちなみに、transform とは「変形」のことで、CSSのみで画像やらボックスやらを自在に回転させたり、ゆがませたりできる優れものです。で、例によってIE系はCSS3の対応は遅れています。とは言っても(これまたいつものように)transformに関しては、IEは実にIE5.5 の時から似たようなものを実装していました ! なので、それらを駆使すれば、CSS3相当のtransformが実装できそうです。`` transformが使えると何がうれしいって、例えば以下のようなポラロイド写真みたいなギャラリーがCSSのみで簡単に出来ます。実は下のショットはIE8 でのものです。` `` 画像の回転に関して、FirefoxやWebKit系は、画像の中心を回転軸として回転させます(なので回転軸は移動しない)。これは直感的で分かりやすいです。これに対して、IEは回転軸が指定した回転角度によって刻々と移動します。なので、標準仕様に似せようと思うと、回転軸を動かさない様にするための補正計算が必要になります。この計算にちょっとてこずりましたが、何とか回転軸が動かないような補正をすることが出来ました。`` `` で、とりあえずモノになりそうなものが出来たのでデモサイトを挙げておきます。画像へのマウスオーバーで拡大なんてのもCSSのみで出来るのですが、それもIEで動くようにしています。`` →「CSS3 Transformを使ったギャラリー(IE互換) 」
` FirefoxやSafariでは、完全にCSS3のみで実現していて、IEでは、IE特有のフィルター機能をJavaScirptを通して操作しています。ボックス要素のドロップシャドウについては、以前、まぁまぁ凝ったスクリプトを書きました が、新たに要素を追加しないといけないなど、あまり納得のいくものではありませんでした。今回ドロップシャドウに関しては、あまり凝ることはせずに、IEのフィルター機能を素直にそのまま使っています。``progid:DXImageTransform.Microsoft.dropshadow(enable=true,OffX=3, OffY=7, Color='#11000000 ', Positive='true'); `` 実は最近知ったのですが、フィルターのColor には通常のRGB 形式だけでなく、透明度も指定できる#AARRGGBB の形式が使えるので、黒色の透明度を高くすればより影っぽくなります。CSS3レベルのぼやけた感じまでは出ませんが、まぁ、これで良しとしましょう。`` 使い方は、簡単で、形はHTCファイルですが、JavaScriptの外部ファイルとして動作するようにしてます。これは、以前のドロップシャドウのスクリプトと同じです。それをIEのみが解釈する条件コメントの中に書いています。`<!--[if IE]>` <script type="text/javascript" src="js/css3transform.htc"></script>`<![endif]--> `` これを書いておくだけで、スタイルシートに記された`` -moz-transform: rotate(-2deg);` -webkit-transform: rotate(-2deg);` transform: rotate(-2deg); `などを解釈して、IEのフィルター機能(Matrix Filter)で同等のことを表現します。デモページにあるように、IEでもCSS3のtransformはおよそ実現できるみたいです(まぁ、そもそもがIEのMatrix仕様を参考に実装したのでしょうけど)。`` このスクリプトを添付しておきます。`[file:1261627359_css3transform.htc:4.6/]|martin|1|1|||
[8] => 1260999974|開発日誌,JavaScript|localStorageでIE8がクラッシュする件 続き,localStorage-crashes-IE8-with-empty-value-part2| こんばんは。前回の書き庫 で、localStorageに空の値を入れるとIE8 がクラッシュすると書きました。なので、空の値が入らないようなチェックを入れれば良いのですが、それだけだとナンなので、症状をもう少し追ってみました。`` もう一度、(IE8が)クラッシュするコードを書いておきます。``function addItem(){` var key = document.getElementById("key").value;` var value = document.getElementById("value").value;` window.localStorage[key] = value; // valueが空白だとクラッシュ!`} `` 以下のように、直接空の値を入れる分には大丈夫でした。``function addItem(){` var key = document.getElementById("key").value;` var value = document.getElementById("value").value; // これは使用しない` window.localStorage[key] = ""; // 直接、空の値を入れる → これは大丈夫。`} `` ほうほう。で、クラッシュするデモページのHTMLソースをよく見てみると、input[type=text] でvalueの値を指定していないですね。もしかしてこのせいかなぁ、と``<input type="text" id="value" value="" /> ``value="" を追加しましたが、やっぱりクラッシュします。input[type=text] の値がないときでも、 document.getElementById("value").value はちゃんと文字列として解釈されます(typeof value == "string" )。デモページは、input[type=text] のvalue値を取得していますが、これはTEXTAREA 要素であっても、やはり内容が空っぽの場合、IE8ではクラッシュを引き起こしました。`` 何でですかねぇ。試しに、強引に文字列ではないオブジェクトを入れてもクラッシュしません 。``window.localStorage[key] = document.getElementById("value"); // これは [object] という文字列に変換される `` 確かに、文字列のstringだとブラウザが解釈しているのに、それが空っぽだとクラッシュするわけです。じゃ、以下はどうだろうか。```var empty = ""; // 空っぽの変数を用意する。`window.localStorage[key] = empty; // これはセーフ。クラッシュしない。 ``さすがにこれはオッケーです。じゃ以下はどうだろうか?`` var val = document.getElementById("val").value; // これが空っぽとする` var empty = ""; // val もempty も空っぽの場合` alert(empty === val); // 厳密等価演算子は true を返す` window.localStorage[key] = val; // なのに、val だとクラッシュ! `` とりあえず string と認識しているはずなのに、代入する段階以降で、別のクラッシュを引き起こす「何か」と解釈されるらしい。というわけで、とりあえず以下のようにすれば、IE8でのクラッシュを回避できることが分かった。でも、ナンだかすっきりしないですね[うーむ/]```function addItem(){` var _key = document.getElementById("key").value;` var _val = document.getElementById("val").value;` window.localStorage[_key] = new String(_val); // new String()で明示してあげる` alert("localStorage."+_key+" の値は「"+localStorage[_key]+"」です。");`} ``new String() でちゃんと文字列オブジェクトとして再評価 してあげると良いらしい。というわけで、IE8でもクラッシュしないlocalStorageのデモページをリンクしておきます。`` →http://p2b.jp/demo/localStorage2.html`` ちなみに、上のコードでalert(typeof new String(_val)) は何となるでしょうか? これは、 もはやstring ではなくobject となります。文字列オブジェクトです。|martin|1|1|||
[9] => 1260967011|開発日誌,JavaScript|localStorageで空の値を入れるとIE8がクラッシュする件| こんにちは。そろそろppBlogにも[g]localStorage[/g]に対応したスクリプトを組み込もうかと思っていますが、IE8で空(から)の値を入れるとブラウザがクラッシュして終了する現象に遭いました。MacBook Pro上のVMware Fusionに入れたWindows7 RC版IE8で発生します。正規版だとOKなのかなぁ。`` 簡単なデモを用意しました(IE8 ならクラッシュしても良い状態でアクセス )。→http://p2b.jp/demo/localStorage.html`` Firefox3.5では値が空でもエラーも何も起きませんが、IE8では即座にクラッシュです。そのスクリーンショットをば。適当なキーを設定して(例えば「foo 」)、値を何も設定せずに「追加」ボタンを押すと発生。`` ``上記ページのスクリプトはごくシンプルなもの。```if(typeof localStorage != "object"){` if(typeof globalStorage == "object"){ // Firefox2 possible` localStorage = globalStorage[location.hostname];` } else alert("お使いのブラウザはこのスクリプトに対応していません。");`}``function addItem(){` var key = document.getElementById("key").value;` var value = document.getElementById("value").value;` window.localStorage[key] = value;`} `` 空の値かどうかチェックすれば済む話ですが。`` 関連エントリー(解決編?) `` ■http://p2b.jp/200912-localStorage-crashes-IE8-with-empty-value-part2|martin|1|1|||
[10] => 1258656418|開発日誌,JavaScript|カラーキーワードをHEXやRGBに変換したい,ColorKeyword-to-Hex-or-RGB| こんばんは。久しぶりにDean Edwards のブログを覗いたら、MSIE でカラーキーワード(red とかtomato とか)やらをHEX 方式(#f01234 とか)に変換するやり方が載ってました。``function toHex(color) {` var body = createPopup().document.body,` range = body.createTextRange();` body.style.color = color;` var value = range.queryCommandValue("ForeColor");` value = ((value & 0x0000ff) << 16) | (value & 0x00ff00) | ((value & 0xff0000) >>> 16);` value = value.toString(16);` return "#000000".slice(0, 7 - value.length) + value;`}; `へぇ。これにも元ネタがあって、それはerik's weblogのコメント です。`` ppBlogには、cornerplay.js というJavaScriptがあって、指定ボックスの角を丸くしたり凸凹にしたりできる動作を担当しています。具体的には以下のような感じです。`` こんな感じで、コーナーをちょいと変えると、見た目のアクセントになります。
`` で、このボックスの背景色に、tomato とかindigo とかlavender などのいわゆるカラーキーワード でも指定できるように、これらカラーキーワードをHEX方式に直した対応表も含めているんですよね(147色ぐらいある )。今、考えるとほかにもやりようがある気もしますが、当時は仕方ないなぁとその対応表をJSON配列にして含めてました。`` このDean Edwardsのやり方なら、対応表なんていりませんね。記述が短くなってファイルサイズも小さくなるだろうし言うことないです。ただ、これはIE限定なので、Firefoxやその他のブラウザでも動くように改良してみましょう。Deanは、「ほかのブラウザは、getComputedStyle()が使えるからバッチリだぜ。」って述べています。`` 調べたところ、各ブラウザ(Firefox、Opera、Chrome・SafariなどのWebKit系)で結構動作が違っていて興味深かったです。ただ色の変換をしたいだけなのに、わざわざ既存のDOM構造に影響を与えるようなやり方(色を取得したいがために、document.createElement で新たに要素を作成して、それをdocument.body.appendChild してDOM構造を改変した後で、getComputedStyle() にて色を取得)は、reflowの問題 もあり出来れば避けたいところ。`` 大体以下のような感じでいけそうです。``function ColorToHEX(color){` var d = document, ecolor, element = d.createElement("button");` element.style.color = color;` ecolor = element.style.color; // ポイント!` if(ecolor.indexOf("#") !== -1) return ecolor; // Operaの可能性が高い` if(d.uniqueID){ // IE向け` color = element.createTextRange().queryCommandValue("ForeColor");` color = ((color & 0x0000ff) << 16) | (color & 0x00ff00) | ((color & 0xff0000) >>> 16);` return "#" + ("000000" + color.toString(16)).slice(-6);` } else { // IE以外のブラウザ` if(/webkit/i.test(navigator.userAgent)){ // Google Chrome, Safariで基本カラーなら` var basicColors = {` "aqua" : "#00ffff", "black" : "#000000", "blue" : "#0000ff", "fuchsia" : "#ff00ff", "gray" : "#808080", "green" : "#008000",` "grey" : "#808080", "lime" : "#00ff00", "maroon" : "#800000", "navy" : "#000080", "olive" : "#808000", "orange" : "#ffa500",` "purple" : "#800080", "red" : "#ff0000", "silver" : "#c0c0c0", "teal" : "#008080", "white" : "#ffffff", "yellow" : "#ffff00"` }` for (var col in basicColors){` if(color.toLowerCase() === col) return basicColors[col];` }` }` if(ecolor.indexOf("rgb") !== -1){ // RGB形式で色を返した!` color = ecolor;` } else color = getComputedStyle(element, null).getPropertyValue("color");` return "#" + color.replace(/rgb¥((¥d+?),¥s*(¥d+?),¥s*(¥d+?)¥)/g, function(a, r, g, b){` return ("000000" + ((+r) * 65536 + (+g) * 256 + (+b)).toString(16)).slice(-6);` });` }`}; `` 流れとしてはこうです。`` IEでcreateTextRange()を使うのでBUTTON要素を作成。 ` それに色を指定する。element.style.color = color ` 指定した色の取得。この時点で、OperaがHEX形式の値を返してくれる。Opera一抜け。 ` 同様に、この時点で、IEはqueryCommandValue経由でBGR値を取得できるので、それをHEXに変換。 ` IE以外でFirefoxは、ecolor は指定したままの値。getComputedStyleを使うとRGB値を返してくれる。 ` WebKit系は振る舞いがややこしい。 Webの基本17色、例えばred の場合、 ecolor = element.style.color にはredがそのまま入り、tomato などの拡張色ならRGB形式を返す。じゃ、getComputedStyleはどうかというと、まだDOMに組み入れていないので 空の値を返す。 ` なので、WebKit系で基本17色であれば、それはJSON対応させてHEX値を返すようにします。そうでなければ、RGB値を返してくれるのでHEX値に変換。 ` 以上。document.body.appendChildなどでのDOM改変は必要ない点がポイントです。 ` `` WebKit系の振る舞いが独特で、特別な処理が必要になりますが、いずれにせよ、DOMの改変を伴わなくて良いかなぁと(まぁcreateElementはしますが)。ちなみに、OperaでもgetComputedStyle は使えますが、FirefoxやWebKitがRGB値を返すのに対して、OperaはHEX値を返します。RGB値からのHEX値への変換は、いくつか方法があるでしょう。上では正規表現を用いています。逆にHEXからRGBへの変換も色々あると思います。```function HEXToRGB(hex){` return hex.replace(/#?(¥w{2})(¥w{2})(¥w{2})/, function(a, r, g, b){ ` return "rgb(" + parseInt(r, 16) + ", " + parseInt(g, 16) + ", " + parseInt(b, 16) + ")"}` );`} ``や``function HEXToRGB(hex){` var color = parseInt(hex.replace("#", ""), 16);` return "rgb(" + ((color >> 16) & 0xff) + ", " + ((color >> 8) & 0xff) + ", " + (color & 0xff) + ")";`} ``などですかね。`` ついでなので、カラーキーワードからRGB値に変換する関数も挙げておきます。考え方は基本的に同じです。DOMの改変は必要ないです。```function ColorToRGB(color){` var d = document, ecolor, element = d.createElement("button");` element.style.color = color;` element.style.display = "none";` ecolor = element.style.color;` if(ecolor.indexOf("rgb") !== -1) return ecolor; // WebKit系が一抜けする可能性あり。` if(ecolor.indexOf("#") !== -1){ // OperaはHEX値を返す` return ecolor.replace(/#?(¥w{2})(¥w{2})(¥w{2})/, function(a, r, g, b){ ` return "rgb(" + parseInt(r, 16) + ", " + parseInt(g, 16) + ", " + parseInt(b, 16) + ")"}` );` }` if(/webkit/i.test(navigator.userAgent)){ // WebKit系で、基本カラーの場合はRGB値を返さない` var basicColors = {` "aqua" : "rgb(0, 255, 255)", "black" : "rgb(0, 0, 0)", "blue" : "rgb(0, 0, 255)", "fuchsia" : "rgb(255, 0, 255)",` "gray" : "rgb(128, 128, 128)", "green" : "rgb(0, 128, 0)", "grey" : "rgb(128, 128, 128)", "lime" : "rgb(0, 255, 0)",` "maroon" : "rgb(128, 0, 0)", "navy" : "rgb(0, 0, 128)", "olive" : "rgb(128, 128, 0)", "orange" : "rgb(255, 165, 0)",` "purple" : "rgb(128, 0, 128)", "red" : "rgb(255, 0, 0)", "silver" : "rgb(192, 192, 192)",` "teal" : "rgb(0, 128, 128)", "white" : "rgb(255, 255, 255)", "yellow" : "rgb(255, 255, 0)"` }` for (var col in basicColors){` if(color.toLowerCase() === col) return basicColors[col];` }` }` if(d.uniqueID){ // IE用` color = element.createTextRange().queryCommandValue("ForeColor");` return "rgb(" + (color & 0xff) + ", " + ((color >> 8) & 0xff) + ", " + ((color >> 16) & 0xff) + ")";` } else { // FirefoxはこれでRGB値を返す` return getComputedStyle(element, null).getPropertyValue("color");` }`} ``` こんな感じですかねぇ。ファイルサイズが小さくなるなら、cornerplay.jsを書き換えても良いなぁ。`` ちなみに、圧縮したら920バイトになりました。```function ColorToHEX(b){var g=document,a,e=g.createElement("button");e.style.color=b;a=e.style.color;if(a.indexOf("#")!==-1){return a}if(g.uniqueID){b=e.createTextRange().queryCommandValue("ForeColor");b=((b&255)<<16)|(b&65280)|((b&16711680)>>>16);return"#"+("000000"+b.toString(16)).slice(-6)}else{if(/webkit/i.test(navigator.userAgent)){var f={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",grey:"#808080",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",orange:"#ffa500",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00"};for(var c in f){if(b.toLowerCase()===c){return f[c]}}}if(a.indexOf("rgb")!==-1){b=a}else{b=getComputedStyle(e,null).getPropertyValue("color")}return"#"+b.replace(/rgb¥((¥d+?),¥s*(¥d+?),¥s*(¥d+?)¥)/g,function(h,j,i,d){return("000000"+((+j)*65536+(+i)*256+(+d)).toString(16)).slice(-6)})}};`
|martin|1|1|||
[11] => 1253331309|開発日誌,JavaScript|IEでCSS3のドロップシャドウを使いたい,IE-CSS3-Dropshadow| こんばんは、martinです。CSS3+JavsScriptネタでも。目標は、IEでもCSS3のドロップシャドウ効果が得られるようになること。以下のような感じで、Firefoxとほぼ同等の効果を得ることが出来ます。左がIE8で右がFirefoxです。``[2010/08/04 01:02:25] プログラムアップデート→http://p2b.jp/201008-IE-compatible-CSS3-dropShadow
`` `` CSS3 では、テキストにドロップシャドウの効果を付けるtext-shadow が定義されていて (まだドラフト段階かな)、既にFirefox3.5やSafari, Chrome, Opera10などは対応しています。典型的には、以下のような感じで指定します。``text-shadow: 1px 2px 3px tomato; // 順にx-offset、y-offset、ぼかし半径、影の色。最初に色指定でもOK。` `` また、テキストに対する影だけでなく、DIV などのボックス要素に対するドロップシャドウとして、box-shadow というのもあり、これは、FirefoxとSafari、Chromeで先行実装されています(Opera10はまだかな)。指定の仕方は、text-shadowと同様です。ほんとは、spread-radius というオプションのパラメータもあるのですが、これはなくても良いんじゃないかと思ってます。``` -moz-box-shadow: 1px 2px 3px chocolate; // 順にx-offset、y-offset、ぼかし半径、影の色。`-webkit-box-shadow: 1px 2px 3px chocolate; // Safari, Chrome用の記述。` `` また、box-shadow固有の属性として、inset というのがあります。これは、ドロップシャドウをボックスの内側に見せるものです。``` -moz-box-shadow: inset 0 0 1em red;`-webkit-box-shadow: inset 0 0 1em red;` `` 今のところ、insetオプションを理解するのは、Firefox3.5だけのようです。`` あれっ、ドロップシャドウって、IEが10年ぐらい前から実装してなかったっけ?と思う方もいるでしょう。そう確かにそんなものがありました。IE独自のフィルター機能を使ってそれっぽいことが出来ます。``` これらが紹介された当初はクールだと思いましたが、使い勝手が今イチだったのと、あとは、玉に瑕というか白璧の微瑕(はくへきのびか)だったと思うのは(ここまでいうと誉めすぎですが)、影の「ぼかし 」が指定出来なかったことです。キリッとした輪郭の影では折角の魅力も半減です(個人的に)。おそらく、影のぼかしはそれなりにCPUのパワーを必要とするので、当時はまだ難しかったのかもしれません。なので、これらが広くWebサイトで使われるということはなかったです。AjaxのようにまたしてもIEの早すぎた実装だったわけです。`` 昨今、パソコンの性能も上がり、IE以外のモダンなブラウザがCSS3のtext-shadow をサポートするようになったせいか、この頃は色んなサイトでドロップシャドウ効果を見かけるようになりました。`` なので、まだまだシェアの大きいIEでも、CSS3に準拠した、それらしいドロップシャドウを付けたいということで考えてみました。基本的な考え方は、上で述べたマイクロソフト独自のFilter Blur を用いて、影のぼかしを演出するということです。このBlurは、要素自体をぶれた 表現にするので、それを別のレイヤーとして生成して、それを影を付けたい要素の背後に配置するというものです。別のレイヤー生成というと、大げさな気もしますが、ぼやけた感じを出すには、これ以外の方法はない気がしますし、Firefoxにしても、影は別レイヤーなんじゃないんでしょうか、知りませんが。`` とりあえず、それっぽいモノができたのでデモをお見せします。`` IEでもCSS3のドロップシャドウを実現するデモ
`` text-shadow もbox-shadow もカンマで区切った複数の指定が可能なので、これにも対応してみました。``.multi-text-shadow {` font: bold 3em Arial, sans-serif; color: white;` padding: 10px;` text-shadow: 1px 2px 2px #555, 0 0 1em blue, 0 0 0.2em blue; /* ここでは3つ指定している */`} `` 上の指定だと、以下のような感じになります。` ` また、現時点では、Firefox3.5のみが対応しているinsetオプションも解するようにしてみました。IE8でのスクリーンショットをば。`` `` で、実際の設置方法ですが、なるべく簡単に設置できるように、外部スクリプトとしました。CSSでの記述は、特別なものは必要ないです。普通に上に挙げたような感じで、text-shadowやbox-shadowの指定をしておけば良いです。後は、ドロップシャドウを効かせたいページで、外部スクリプトを次のように呼び出せばOKです。`<script type="text/javascript" src="path/to/css3shadow.htc "></script> ` ファイルの名前css3shadow.htc さえ変えなければOKで、それを置いた相対パスを、各自で記述すれば良いです。`` これ以外の指定方法としては、このスクリプト自体は、完全にIE向けなので、Firefoxその他のブラウザには不要なものです。なので、その分のトラフィックを減らしたいということであれば、IE向けの条件コメントを使って、``<!--[if lt IE9]>`<script type="text/javascript" src="path/to/css3shadow.htc"></script>`<![endif]--> ``でも良いと思います。これであれば、Firefoxなどではコメントとして解釈されるので、サーバーからcss3shadow.htcが呼び込まれることはないです。`` まだ、書き上げたばかりなんで、細かいチェックは出来ていませんが、外部スクリプトを呼び込むだけなので、気軽に試して頂けるかと思います。css3shadow.htc を添付しておきます。8KB未満の小さいファイルです。`` ちなみに、影の色指定ですが、透明度を設定できるrgba 方式にも対応しています。これは、通常のRGB形式に加えて、4つめに透明度を指定出来るものです。ゼロで透明、1で不透明となります。```rgba(123, 100, 125, 0.3); /* 透明度30%の指定 */ `` オフトピですが、RGBって、たまにRBGとタイプミスしたり、あれっ?どっちだっけと混乱するときがありますが、キーボードで、上から順にRGBとなっている 、と覚えると良いです。`` 以下、Mozillaの参考サイトを挙げておきます。````[file:1253331309_css3shadow.htc:7.7/]|martin|1|1|||
[12] => 1250867875|開発日誌,JavaScript|Google AJAX Feed APIを利用したスライドショー,Google-AJAX-Feed-API-Slideshow| こんばんは。前回の書き庫コメントでアップロードした画像のスライドショーの話が出ていたので。`` ブログのサイドバーでスライドショーを見せるブログパーツは結構あると思いますが、良く見かけるのはAdobeのFlashを用いて表示させる方法。これは純粋にJavaScriptを使うよりは、凝った演出が出来る傾向にあります。もっとも、最近はモダンなブラウザでは[g]Canvas要素[/g]をサポートしているので、JavaScriptだけでも凝った演出が可能になってますけど(ppBlogでは、写真を回転させたりして表示できますが、この部分でCanvasを使っています)。`` 単なる好みの問題ですが、サイドバーでスライドショーを見せる際に演出に凝りまくるのは、あまり好きでありません(特にFlash系)。なので、スライドショーを見せるにしてもJavaScriptのみで動くシンプルなものが良いなぁ、と思いました。スクリプトは自前で用意したいところですが、画像切り替えのアルゴリズムなどを考えるとはまりそうなので、ここは既知のJavaScriptライブラリーを使うことにします。`` で、Google AJAX API が良さそうでしたので、これを使ってスライドショーを付けてみました。Googleで、スライドショーそのもののスクリプトを公開している ので、それを利用するだけです。実際のデモは、http://p2b.jp/demo/slideshow.html あるいは http://martin.p2b.jp/index.phpで確認出来ます(3paneのテーマであれば右側に表示される)で確認できます。`` 早速ですが、簡単な設置手順を記しておきます。``` mediaRSSファイルが必要なので、それを作成するadmin.php とmodules/mrss2.inc.php をゲット。 ` 管理画面の「各種ツール」に「MRSSの作成 2」というメニューがあるので、それを選択。これでmediaRSSが作成される。 ` 表示させたいテーマのtemplate.phpに必要なJavaScript関連の記述をする。 ` `` 基本的にはこれで足ります。template.phpに記述するJavaScriptですが、先に挙げた小生のサイトでは、まず以下のようにして動作に必要なファイルをGoogleのサーバーから外部スクリプトとして呼び込んでいます。`` <script type="text/javascript" src="http://www.google.com/jsapi?key=(AJAX APIのキー) "></script>` <script src="http://www.google.com/uds/solutions/slideshow/gfslideshow.js" type="text/javascript"></script> ``AJAX APIのキー指定ですが、これはなくても動きますが、グーグルのサイトで簡単に入手出来る ので手に入れておくと良いでしょう。`` この2つのスクリプトを通じて、予め用意したmediaRSSファイルを読み込んで、画像を順次表示させていく訳ですが、mediaRSSファイルを手書きで用意するのは大変なので、こういうものはppBlogに作らせましょう(手順1, 2)。ppBlogで生成されるmediaRSSは、具体的にはhttp://martin.p2b.jp/feeds/mrss.xmlのような感じになります。`` このmediaRSSファイル読み込み時に起動する呼び出し関数は、以下のような記述です。グーグルのサイトにあるサンプルコードとほぼ同じです。```function load(){ // mediaRSSファイルを読み込んだ際のコールバック関数` var src = "http://martin.p2b.jp/feeds/mrss.xml"; // 読み込むRSSファイルを指定` var options = {` displayTime: 3000, // 一枚あたりの画像表示時間(ミリ秒)` transistionTime: 1000, // 画像切り替えのフェードアウト・フェードインの時間(ミリ秒)` scaleImages : true, // 指定したサイズに合わせて画像を縮小させるかどうかのオプション指定` linkTarget : google.feeds.LINK_TARGET_BLANK // 画像をクリックした際に新しいウィンドウ(タブ)でリンク先を開くか` };` new GFslideShow(src, "slideframe", options); // slideframeというID名を持つ要素に対して発動` }` google.load("feeds", "1");` google.setOnLoadCallback(load); ``これに対するHTMLの記述は以下のような感じです。これもtemplate.php内の記載になります。``` <div id="slideshow" style="margin: 60px auto 30px auto; width: 120px; height: 150px; background: url(theme/3pane/Images/slide-frame.jpg) no-repeat; padding-top: 30px;">` <div id="slideframe " style="width: 100px; height: 100px; margin: auto; position: relative;"></div>` </div><!--#slideshow--> ``ここでは、インライン形式のスタイルシート指定にしていますが、勿論スタイルシートのCSSファイルで指定しても構いません。設置の参考にするには、上に挙げたデモサイトが分かりやすいかと思います。`` 尚、このスライドショーは、マウスが画像に載ると、そこで一時停止します(これはデフォルトの動作)。で、そのままクリックするとその画像が含まれる記事にリンクするようになっています(これはmediaRSSの内容による)。`` ppBlogに組み込めるように、admin.phpとmodules/mrss2.inc.phpを添付しておきますね。`` 参考リンク: http://www.google.com/uds/solutions/slideshow/reference.html``[file:1250867875_admin.php:28.6/]``[file:1250867875_mrss2.inc.php:2.5/]|martin|1|1|||
[13] => 1238300138|開発日誌,JavaScript|VMLやSVGを用いた角丸コーナー,rounded-corner| おはようございます。時間があるときに、以下のようなサイトを作成しているのですが、この2枚のスクリーンショットは、違うところがあります。どこが違うのかというと、2枚目のやつは、ボックスのコーナーが丸っこくなっていて、いわゆる角丸コーナーと言われるものになっています。`` `` `` ちょっとお洒落っぽいサイトデザインにしようと思っているのですが、そのためには、角張ったデザインよりも、角が取れたデザインの方が良いかなと考えました。で、角丸コーナーは、FirefoxやWebKit系のSafariやChromeは、以下のような感じでCSSでの指定が可能です。``-moz-border-radius: 10px; // Firefox`-webkit-border-radius: 10px; // Webkit系 ``問題は、これらのCSSに対応していないOperaやIEです。Operaは、近い将来にサポートするでしょうけど、IEは、今後数年は期待できないかもです。なので、これら2つのブラウザに対しては、JavaScriptを通じてのアプローチを取ることになります。`` IEでは、VMLを利用するのがよさげです。しかも角丸コーナーのためにあるようなroundrect というのが予め実装されているので、これを使うのが簡単そうですが、例えば最初に挙げたサイトショットにあるように、ナビ―ゲーションタブの上側だけ丸っこくしたいとかいう場合の制御が難しいです。なので、VMLを使うにしても、もうちょっと、凝ってみることにします。そもそも角丸コーナーを実現するには、以下のようなアプローチを取るのが自然でしょう。`` `` 要は、4つの各コーナーに小さい円を描いて、それをクリッピングすれば良いわけです。これなら、ボーダー半径は、FirefoxやWebkit系で指定する値と同様に振る舞うことが期待出来ます。で、それを実際に書き下したのが以下。``<div style="background:#fffacd; position:absolute; right:-20px; bottom:-20px; width:50px; height:50px; clip:rect(25px 50px 50px 25px);">` <v:group style="width:50px; height:50px; position:absolute;" coordsize="50,50">` <v:oval fillcolor="#fff" strokeweight="20px" strokecolor="#ffd700" style="left:9px; top:9px; width:30px; height:30px;" />` </v:group>`</div> `` これをIEで見ると、以下のように見えます。なかなか良い感じですね。`` `` 残りの角についても同様にすればOKです。ただし、IEのVMLバグなのかどうか分かりませんが、作成したVML画像の位置が1ピクセルぐらい微妙にずれる場合があるようです。理論的には、OKのはずなんですが。。`` さて、Operaの場合はどうでしょうか。OperaはVMLはサポートしませんが、代わりにSVGをサポートします。なので、これを用いて、上で述べたようなアプローチを取ればOKですね。大体以下のようなスクリプトでOKでしょう。````function svg_corner(id, location, r, obg){` var d = document, e = d.getElementById(id);` if(e.currentStyle.position == 'static'){` e.style.setProperty('position', 'relative', null);` }` r = r || 10;` var svg = d.createElementNS('http://www.w3.org/2000/svg', 'svg');` svg.style.setProperty('position', 'absolute', null);` var ibg = e.currentStyle.backgroundColor;` ibg = ibg == 'transparent' ? '#fff' : ibg;` if(!obg){` var p = e.parentNode;` while(p && p.currentStyle.backgroundColor){` obg = p.currentStyle.backgroundColor;` if(p.nodeName == "HTML") break;` if(obg && obg != "transparent") break;` p = p.parentNode;` }` }` obg = (!obg || obg == "transparent") ? "#fff" : obg;` var bc = e.currentStyle.borderColor;` bc = (!bc || bc == "transparent") ? "#fff" : bc;` var bw = parseInt(e.currentStyle.borderWidth);` var rect = d.createElementNS('http://www.w3.org/2000/svg', 'rect');` var oval = d.createElementNS('http://www.w3.org/2000/svg', 'circle');` var attr = [];` switch (location){` case 'tl' : attr = [r + bw/2, r + bw/2, 'top', -bw, 'left', -bw]; break;` case 'tr' : attr = [0, r + bw/2, 'top', -bw, 'right', -bw]; break;` case 'bl' : attr = [r + bw/2, 0, 'bottom', -bw, 'left', -bw]; break;` case 'br' : attr = [0, 0, 'bottom', -bw, 'right', -bw]; break;` }` oval.setAttribute('fill', ibg);` oval.setAttribute('cx', attr[0]); oval.setAttribute('cy', attr[1]);` oval.setAttribute('r', r);` oval.setAttribute('stroke', bc); oval.setAttribute('stroke-width', bw + 'px');` rect.setAttribute('fill', obg);` rect.setAttribute('width', r * 2 + bw + 'px'); rect.setAttribute('height', r * 2 + bw + 'px');` svg.appendChild(rect);` svg.appendChild(oval);` svg.style.setProperty(attr[2], attr[3] + 'px', null); svg.style.setProperty(attr[4], attr[5] + 'px', null);` svg.style.setProperty('width', r + bw/2 + 'px', null); svg.style.setProperty('height', r + bw/2 + 'px', null);` e.insertBefore(svg, e.firstChild);`}` `` これらをまとめると、以下のようなデモンストレーションが出来ます。このデモでは、ID指定した要素に対して角丸を発動していますが、特定のクラス名を持ったものに対して、ということも簡単に出来るでしょう。``どのブラウザでもほぼ見た目が同じな角丸コーナーのデモ
`` 上のサンプルでは、ついでにFirefoxやWebkit系でも、スクリプトでボーダー属性を指定するようにしています。このふたつ、指定の仕方が微妙に違うんですよねぇ。ちょっとはまりました。以下の感じ。`document.getElementById(id).style.WebkitBorderTopLeftRadius = radius + "px"; // Webkit系`document.getElementById(id).style.MozBorderRadiusTopleft = radius + "px"; // Firefox。leftのLも小文字。 `` なお、OperaでのSVGの使い方は、「Operaで丸角を実現するCSSを試してみた (解決) 」や「角丸にするためにライブラリを作ってみる 」が参考になりました。|martin|1|1|||
[14] => 1237810287|開発日誌,JavaScript|正規表現を使わないCSS3対応の「属性限定」セレクタ関数| ppBlogでは、不十分ですけどCSS3対応のセレクター関数を実装していたんですが、これを大幅に書き換えました。従来は、XPathが使えるブラウザであれば、CSS3のセレクタをXPath用に変換して、意中の要素を取り出したりしていました(スピード面でなかり有利)。でも、document.querySelectorAll をサポートするブラウザが当たり前になってきたので、少なくともppBlogでは、XPathは不要という結論に達しました(今やFirefoxのためだけに用意してる感じで、しかも次期v3.5では間違いなくquerySelectorAllがサポートされるでしょうから)。`` ppBlogで使っている要素抽出のためのセレクターは、大体以下のような感じです。``` o("#ID") 系 // IDを持つ要素を取り出す ` o(".className") 系 // 特定のクラス名を持つ要素を取り出す ` o("a[rel=edit]") // 属性セレクター系 。A要素でrel="edit"のやつを取り出す ` o("textarea[id^=Page]") // 属性セレクター系 。テキストエリア要素でID名がPageで始まる要素を取り出す ` ``とまぁ、こんな感じで、ほぼ頻度順になってて、圧倒的にID名抽出が多いです。なので、別に高機能なセレクター関数は必要ないわけで。XPathへの変換にえらくファイル容量を割いていたんですが、それをばっさり削ったんで、だいぶシェイプアップされました(oParts.js)。`` ただ、属性セレクター(上のリストの下2つ)は、今後、色々使い道がありそうなので、これはスピードも考慮した関数を実装しました。以下のような関数です。正規表現を使っていないので、速度面で有利です。面白いことに、FirefoxよりIE6-8の方が高速に動きます 。別に詳しく計測した訳ではありませんが、属性セレクターに限定すれば最速の部類ではないかと思っています(XPathやquerySelectorAllを使わないレガシーな系で)。```// cache = {}; // キャッシュ使うなら`function queryByAttributeSelector(rule){` var apos = rule.indexOf('[');` var tag = rule.slice(0, apos); // タグ名を抽出。` var attr = rule.slice(apos + 1, -1); // 属性を抽出` attr = (attr.indexOf('][') > 0) ? attr.split('][') : [attr];` var s = '', e, k, ks, v, z, i = 0, l = attr.length;` // if(!cache[rule]){ // キャッシュ使うなら` for(; a = attr[i++];){` e = (i != l ? ' && ' : '');` z = a.split('=');` if(z[1]){` k = z[0], ks = k.slice(-1);` v = z[1].indexOf('"')===0 ? z[1].slice(1, -1) : z[1];` k = k.indexOf('class') !== -1 ? k.replace('class', 'className') : k;` switch (ks){` case '^' : k = k.slice(0, -1); s += 'n["'+k+'"].indexOf("'+v+'")===0' + e; break;` case '$' : k = k.slice(0, -1); s += 'n["'+k+'"].lastIndexOf("'+v+'")===(n["'+k+'"].length - "'+v+'".length)' + e; break;` case '~' : k = k.slice(0, -1); s += '(n["'+k+'"]=="'+v+'" || n["'+k+'"].indexOf("'+v+' ")===0 || n["'+k+'"].indexOf(" '+v+' ")!==-1 || ((z=n["'+k+'"].indexOf(" '+v+'"))>0 && !n["'+k+'"].charAt(z+"'+v+'".length+1)))' + e; break;` case '*' : k = k.slice(0, -1); s += 'n["'+k+'"] && n["'+k+'"].indexOf("'+v+'")!=-1' + e; break;` case '|' : k = k.slice(0, -1); s += '(n["'+k+'"]=="'+v+'" || n["'+k+'"].indexOf("'+v+'-")!= -1)' + e; break;` default : s += 'n["'+k+'"]=="'+ v +'"' + e;` }` } else {` s += 'n["'+(a==='class'?'className':a)+'"]' + e;` }` }` /*cache[rule] = s; // キャッシュ使うなら` } else {` s = cache[rule];` }*/` var r = [];` var items = document.getElementsByTagName(tag||'*'), j = 0, n;` var v = new Function('n', 'return ('+s+')');` for (;n = items[j++];){` if(v(n)){` r[r.length] = n;` }` }` return r;`} `` 工夫した点は、indexOf やらlastIndexOf の積極的活用です。split関数は、正規表現を用いない文字列での分割は、用いる場合より何倍も高速です。後、細かい速度の改善については、uupaaさん のエントリ [latest log]が非常に参考になりました。``CSS3の「属性 」セレクターをほぼ網羅していると思いますが、とりあえずhttp://mootools.net/slickspeed/にある属性セレクターはすべてパスします(ただし、div[class!=made_up] は、CSS3にはなさそうなので未対応)。`` 上のソースコメントをみれば分かりますが、キャッシュを有効にするとより速くなります。でも別になくても良いかなと思ってます。細かい点を言えば、A要素のREL属性なんてのは、大文字小文字を区別しないようですが、そこまでは追ってません。要は、ppBlogで動けば良いので。`` ppBlog1.8.0での実装は、上のソースをベースにしたものになっています。|martin|1|1|||
[15] => 1236976209|開発日誌,JavaScript|テキストエリアの自動リサイズ再び,autofit-textarea| 以前、テキストエリアのサイズを文章の長さに合わせて変えるスクリプトを考えて、それをppBlogのエディタにも採り入れているのですが、それは意図した様に動くには動くのですが、えらく力業なスクリプトでした。その恥ずかしいスクリプトは、ここ に置いてます[はてさて/]`` 記述も長くてスマートでないし、もっと簡単に出来ないかなぁと考えてみましたが、要は、自動調節というのは、テキストエリアのスクロールバーが表示されなくなるまでエリアを伸ばすということなので、その考えで行けば簡単でした。以下のような記述でいけますね。``function autofit(el){` if(el.scrollHeight > el.offsetHeight){` el.style.height = el.scrollHeight + 'px';` } `} ``element.scrollHeight で、目的のテキストエリアの、スクロール分も含めた高さ (+ボーダー幅)を取得できます。で、element.offsetHeight で、見た目の高さを取得できます。なので、スクロールバーが表示されている状態というのは、文章が長くて表示しきれない状態なので、じぁ、テキストエリアの高さをscrollHeight に合わせれば良い、ということになりますね。なんで、こんな簡単なことに気が付かなかったかなぁ。まず、このデモを見てみましょう。```AutoFit
`[script]`function autofit0313(el){` if(el.scrollHeight > el.offsetHeight){` el.style.height = el.scrollHeight + 'px';` }`}`function autofit03132(el){` if(el.scrollHeight > el.offsetHeight){` el.style.height = el.scrollHeight + 'px';` } else {` while (el.scrollHeight - 50 < parseInt(el.style.height)){` el.style.height = parseInt(el.style.height) - 50 + 'px';` }` arguments.callee(el);` }` el.focus();`}`[/script]`` とりあえずは、これでよさげですが、もうちょっといじってみましょう。例えば、この文章をコピペして、元の文章の下に貼り付けると、表示領域からはみ出るので、自動的にスクロールバーが表示されると思います。なので、ここでもう一度「AutoFit」ボタンを押すと、テキストエリアが広がります。次に、この貼り付けた文章を削除してみて下さい。テキストエリアが広がった分、下に大きな余白が出来ますね。今度は縮めようと再度ボタンを押してもテキストエリアのサイズは変わりません。これは、このautofit関数の定義からして当然なのですが、気持ちとしては、今度は逆に縮んで欲しいなと。では、どうするか?`` 強制的にスクロールバーを表示させる状態に持っていって、そこでこのautofit関数をかませば良いです。スクロールバーを表示させるには、意図的にテキストエリアの縦サイズを小さく取ればよいわけです。なので、極端な話、``element.style.height = '1em'; ``とでもして、つぎにautofit()を呼び出せばOKですが、これだと見た目にテキストエリアがスプラッシュみたくなるので、なるべく最小量のサイズ変更でいくには、次のような感じで良いかなと思います。````/* AutoFit関数その2 */`function autofit2(el){` if(el.scrollHeight > el.offsetHeight){` el.style.height = el.scrollHeight + 'px';` } else {` while (el.scrollHeight - 50 < parseInt(el.style.height)){` el.style.height = parseInt(el.style.height) - 50 + 'px'; // 適当に50pxずつ` }` arguments.callee(el); // 高さを縮めてスクロールバーが出てくるタイミングで呼び出す` }` el.focus();`} `` 以下に、これを適用したものを付けておきます。上の関数との動作の違いが分かると思います。```AutoFit 2
`` この関数をonkeyupなどのキーイベントと結びつけておけば、入力に合わせて自動的にサイズが変わるテキストエリアの出来上がりです。これのデモを以下に挙げておきます。`` →http://p2b.jp/demo/autofit-textarea.html|martin|1|1|||
[16] => 1236707125|開発日誌,JavaScript|コメント読み込み時にスクロール| ppBlogでは、コメントやトラックバックが付いている記事には、それを記事の下の方に表示するレスポンス展開ボタンが付いてます(複数記事モードのとき。単独表示では、最初からコメント類は表示されている)。ここで、ボタンをクリックすると、Ajax経由で動的にコメントを読み込むので、わざわざページをリロードさせる必要もなくスムーズにコメントなどを読むことができます。`` 現行では、このボタンをクリックしてデータを読み込むと、コメントエリアにフォーカスが移っていたと思いますが(ウィンドウがスクロールする)、これが、瞬時でピクッと移るために、何となく読み手の視点が混乱するというか、一瞬、何が起こったのか分からなくなるかもしれないと思い、このフォーカス移動を止めてみました。`` すると、特にウィンドウのスクロールを伴うことなく、展開されたコメント・トラックバックがボタンの下に表示されて良い感じです。でも、ひとつ問題があって、もし、このレスポンス展開ボタンをブラウザ画面の結構下の方でクリックすると、展開されたコメント類がブラウザ画面の領域下に表示され、これはこれで、何も展開されてないように見えるので、読み手を困惑させる可能性があります。`` なので、Ajaxで読み込んだデータを展開した際に、それらがウィンドウの枠外であるときに限って、そのエリアまでスクロールさせるようにしました。かといって、単なるフォーカスの瞬間移動では、最初に述べたように、これも読み手を惑わせるかもしれないので、瞬時に移動ではなくて、スクロール制御するようにしてみました。以下のようなスクリプトでいけます。``var wscY = oParts.metrics(3); // ウインドウのスクロール量を取得`var firstComment = o("div.comment-entry", commentsDiv).item(0); // 表示エリアの最初のコメント要素を取得`var diff = firstComment.offset(1) - Number(wscY + oParts.metrics(1)); // 上記コメントを表示させるためのスクロール量を算出``if(diff > 0){` (function(){` var a = 30, s = arguments[0] / a; // スクロール量を適当に分割。ここでは30分割してる ` var yy = []; // 縦移動させるスクロールポイントを入れる配列を用意` for (var i = 0; i < a; i++) yy[i] = wscY + s * i; // 適当に移動ポイントを作成` var i = 0, t = setInterval(function(){ window.scrollTo(0, yy[i++]); if(i >= a) clearInterval(t);}, 1);` }).await(300)(diff + firstComment.rect(1) + 140); // データ読み込み後、300ミリ秒後に発動` }`}); `` データを読み込んでからいきなりスクロールしだすと、それで面食らうかもしれないので、.await(300) と微妙に間を持たせています。`` ここのサイトで、実際にコメントの付いた記事で試せますので、興味のある方は、あえてコメント展開ボタンをブラウザ画面ぎりぎりにセットして試して下さい。|martin|1|1|||
[17] => 1236646850|開発日誌,JavaScript|ChromeでもJS簡易エディタが動くように,chrome-JS-editor| こんばんは。ppBlogでの記事作成は、テキストエリアに、タグ入力支援ツールバーの助けをかりて、(ベタに)ぐいぐい書いていくやつです。以前、IEとFirefoxがサポートしていた[g]WYSIWYG[/g](contentEditable/designMode)モードを採り入れようと思いましたが、IEの生成するHTMLソースがひどくて諦めたことを、どこかに書いたと思います。今は、Safariもそれをサポートしているようですが、まぁ現状の、テキストエリア方式でいいんじゃないかなと思っています。その場でプレビュー機能も付いてますし。`` で、このタグ入力支援の機能が、グーグルのChromeでうまく動かないなと前々から認識はしていたんですが、今回調べてみました。IEとの切り分けに、``if(document.getSelection){ // Firefox などIE以外のブラウザ ``としていたのが駄目だったようです。Chromeはこれを無視していたようで。なので、この部分はwindow.getSelection() を使えばOKでした。でも、今や、IE以外のブラウザはみんな同じ仕様に沿ってるようですし(2-3年前はOperaが遅れていた)、IEとそれ以外という大まかな切り分けで行けば良さそうです。なので、よくある、テキストエリアのマウスカーソルのある位置に文字列を挿入するというスクリプトは以下のような感じで行けるかなと思います。ここでは汎用性のあるサンプルを載せます。ppBlog用のlib.jsには、これを若干modifyして載せます。```/* テキストエリアでカーソルで指定したポイントあるいは文字列に新たな文字列を挿入するスクリプト */``var Caret = {` getArea : function(){ return document.getElementById("ta");}, // ID名ta(適当)というテキストエリアを用意` selection : '', // ここに選択したテキストを収納する` get : function(){ // テキスト選択メソッド` var area = Caret.getArea();` /*@cc_on@*/` /*@if(1)` if(!document.selection.createRange()) area.focus();` Caret.range = document.selection.createRange().duplicate();` return Caret.selection = Caret.range.text;` @else@*/` return Caret.selection = area.value.substring(area.selectionStart, area.selectionEnd);` /*@end@*/` },` set : function(string){ // 選択テキストを指定文字列に置換する` var area = Caret.getArea();` /*@if(1)` if(Caret.selection.length > 0){` Caret.range.text = string;` Caret.range.select();` } else {` area.focus();` Caret.range = document.selection.createRange().duplicate();` Caret.range.text = string;` }` @else@*/` if(Caret.selection.length >= 0 && area.selectionStart >= 0){` var s = area.selectionStart, scrollTop = area.scrollTop;` area.value = area.value.slice(0, s) + area.value.slice(s).replace(Caret.selection, string);` area.setSelectionRange(eval(s + string.length), eval(s + string.length));` area.scrollTop = scrollTop; // Firefoxでカーソルがトップに戻らないための処理` area.focus();` } else area.value += string;` /*@end@*/` }`} `` IEだけが違うと分かっているので、条件コンパイルを使って、動作の切り分けをしています。Firefox系で、指定位置に文字列を挿入した後、フォーカスが、テキストエリアのトップに戻ってしまうのを防ぐ処理を入れています。これはFirefox1.5で見られて、今はどうかなぁと思ったら相変わらずでした[zzz/]`` →参考リンク「 Firefoxでテキストエリア内のマウスカーソルが最初に戻ってしまう件 」`` 一応、簡単なデモページを挙げておきます。`` ` →上のスクリプトを使った、簡易JavaScriptエディタ。http://p2b.jp/demo/jseditor.html`` このデモにあるHTMLビューは悪くないなぁ。|martin|1|1|||
[18] => 1236556695|開発日誌,JavaScript|正規表現の挙動がブラウザ間で異なる件,regular-expression| ヘッドラインモードでの表示周りを整備していて、記事ごとのスタイルシート指定が効いていなかったので、効くようにスクリプトを書き換えましたが、正規表現周りの挙動がブラウザ間で異なっていて、ちょっとはまったのでメモ。簡単な例を示します。``<script type="text/javascript">`function doRex(){` var example = "J'adore Firefox!"; // I like Firefox!` var RE = /Fire/g;` var result = RE.exec(example); ` if(result) alert("マッチ: " + result + "¥nlastIndex: " + RE.lastIndex);` else alert("マッチしません!:" + "¥nlastIndex: " + RE.lastIndex);`}`</script>`<button onclick="doRex();"> 実 行 1 </button>` ` 実 行 1 `` 上のdoRex()関数を実行すると、最初ボタンを押したときはちゃんとマッチしますが、2回目にボタンを押したときの結果がブラウザで異なります。Firefox3.0.7, Chrome1.0, Opera9.64では、「マッチしない」、IE6-8とSafari4 public betaでは、マッチして1回目と同じ結果です。なぜ、こういうことになるかというと、lastIndex 絡みです。1回目の実行に対して、lastIndex の値が保持されているからです。2回目の実行で、マッチしないとなると、RE.lastIndexはまた0に戻るので、3回目の結果は初回と同じく「マッチ」です。`` でも、疑問なのが、doRex()という関数の中で定義したローカルな変数だから、doRex()を実行する度に、lastIndexの値はリセットされるのが正しい気もするんだけどなぁ。doRex()の中ではなく、グローバルな空間で同様のことをやれば、SafariもIEも2回目は「マッチしない」となり、これは納得できる挙動なんですが。`` では、このブラウザ間の差異をなくすにはどうするか?ひとつは、RE.exec(example) した後に、`RE.lastIndex = 0; // 値をリセット `という記述を加えて、明示的にlastIndexを元に戻す方法。もうひとつは、new演算子を使う方法です。これなら、どのブラウザでも同じ挙動、何度ボタンを押しても、「マッチ」となります。new RegExp("foo", "g") と /foo/g って同じと思っていたんだけど。``[script]`function doRex(){` var example = "J'adore Firefox!"; // I like Firefox!` var RE = /Fire/g;` var result = RE.exec(example);` if(result) alert("マッチ: " + result + "¥nlastIndex: " + RE.lastIndex);` else alert("マッチしません!:" + "¥nlastIndex: " + RE.lastIndex);`}``function doRex2(){` var example = "J'adore Firefox!"; // I like Firefox!` var RE2 = new RegExp("Fire", "g");` var result2 = RE2.exec(example);` if(result2) alert("マッチ: " + result2 + "¥nlastIndex: " + RE2.lastIndex);` else alert("マッチしません!:" + "¥nlastIndex: " + RE2.lastIndex);`}``[/script]``<script type="text/javascript">`function doRex2(){` var example = "J'adore Firefox!"; // I like Firefox!` var RE2 = new RegExp ("Fire", "g"); // new演算子を使用する` var result2 = RE2.exec(example);` if(result2) alert("マッチ: " + result2 + "¥nlastIndex: " + RE2.lastIndex);` else alert("マッチしません!:" + "¥nlastIndex: " + RE2.lastIndex);`}`</script>`<button onclick="doRex2();"> 実 行 2 </button>` `` 上のコードであれば、ブラウザ間の差異がない。`` 実 行 2 `` でも、Safari/IE と Firefox/Chrome/Opera、どっちが正しい挙動なんだろうな。今イチ、すっきりしないです。|martin|1|1|||
[19] => 1236256261|開発日誌,JavaScript|写真をお洒落に見せるスクリプトの更新| こんにちは。まだまだ寒い日が続きますね。さて、ppBlogには、以下のような感じで、普通のアップした写真をちょいとお洒落に演出するスクリプトが付いてますが、IE8でエラーが出ていたりしたんで、更新しました。`` ` `` IE8のdocument.querySelectorAll の実装に始まり、Safariもサポート、Firefoxは3.1でサポートするでしょうし、この手の用途には、staticなnodeListでこと足りるので、photo-effect をいうクラス名を付けたIMG要素の検索には、document.querySelectorAllを第一に試すようにしてます。`` 始めは、よくやるように、``var nodes = Array.prototype.slice.call(element.querySelectorAll("img.photo-effect")); `` としたのですが、何故かIE8では、JSオブジェクトがないと怒られて折角のquerySelectorAllが使えません。なので仕方なしに次のようにしました。``var nodes, items = element.querySelectorAll("img.photo-effect"), index = items.length;`while(index) nodes[--index] = items[index]; /* 地道に */ `` IE8でも、Array.prototype.slice.call(arguments) は問題なく作動するのに。。きっと、正式版前のバグなんだろうけど。`` てな訳で、最新版を添付しておきます。ついでに言っておくと、このスクリプトは単体で動作します。ただし、2種類の写真フレームの画像も用意して適切にパスを書かないといけませんが。デフォルトでは、Imagesディレクトリにあるphotoframe.png とstampframe.png の2つです。``[file:1236256261_photoeffect.js:5.2/]|martin|1|1|||
[20] => 1234925314|開発日誌,JavaScript|動的スタイルシートの作成| こんばんは。だいぶ日も長くなってきましたね。`` さて、小生のブログで、「エクセルデータをHTMLテーブルに変換」というエントリーを書いて、そこでppBlog用に見栄えの良いTABLEを作成してくれるページ を公開していました。早速、使って下さったユーザーの方から、ppBlogをリスト表示にしていると、テーブルのスタイルシートが有効になっていないとのご指摘がありました。確かに、リスト表示での個別スタイルシート指定はすっかり対応を忘れていました。`` というわけで、ppBlogでは記事毎にスタイルシートやJavaScriptを指定することが可能ですが、あまり知られていない気もするので簡単に使い方を書いておきます。スタイルシート(CSS)を指定したければ、記事のどこでも良いので、[style][/style] で囲った中の記述がCSSとして認識されます。記述はまんまCSSと同じです。具体例を挙げると以下のような感じ。``[style]`.article-content table { border-color: tomato; }`[/style] ``とか。.article-content は、記事内容要素に対するクラス名です。記事の個別表示のときは、記事ボックスはひとつしかないので問題ありませんが、複数の記事が表示されている場合、上の例だと、別の記事にテーブルが含まれていたら(クラス名が同じため)そこにも反映されてしまいます。なので、これを回避するには、指定したい記事の固有IDを利用すると良いです。ppBlogでは、すべての記事にUID1234567890 のようなIDが割り振られます。``[style]`#UID1234567890 .article-content table {` border-color: tomato; /* IDを利用して適応先の .article-content を絞り込む */`} `[/style] ``追記 2009/02/19 18:56:29 考えてみると、このUIDを付加する作業は、ppBlogの方で自動的にやるべきかなぁ。次バージョンではそうしようと思います。
`` 記事ごとのJavaScript指定も同じように、[script][/script] で囲った中に記述すればOKです。``[script]`alert("Bonjour, tout le monde!"); /* 普通にJavaScriptを記述 */`[/script] `` 前置きが長くなりましたが、本題。最初に挙げたリンク先 のソースを見れば分かりますが、動的にスタイル指定を適応させる処理を入れています。同じような処理は、ppBlogで使っているソースハイライト表示用のsyntax.js でも使っていますが、ここでは、上記エクセルコピペのテーブル変換の部分を抜粋してみます。```var sheet = d.styleSheets && d.styleSheets[0]; /* 最初のスタイルシートを取得 */``var rulesCSS = { /* JSON形式で要素毎のスタイルを指定 */` 'table' : 'border: solid 1px gray; margin: 2em auto; border-collapse: collapse;',` 'table td' : 'border: solid 1px gray; padding: 2px; text-align: right;',` 'table th' : 'border: solid 1px gray; padding: 2px; text-align: center;',` 'thead tr th' : 'background: #eeffff;',` 'tbody tr th' : 'background: #ffeeee;'`};``for (var el in rulesCSS){` if(sheet.addRule){ /* IE 向け */` sheet.addRule(el, rulesCSS[el]);` } else { /* Firefox, Safari, Opera 向け */` sheet.insertRule(el + '{' + rulesCSS[el] + '}', sheet.cssRules.length);` }`} `` このように、予めJSON配列などに適応させたいCSSを記述している時は、こんな感じで動的に変更させることが出来るのですが、「普通のCSSの記述」つまり``<style type="text/css">` table { border: solid 1px gray; margin: 2em auto; border-collapse: collapse; }` table td { border: solid 1px gray; padding: 2px; text-align: right; }` table th { border: solid 1px gray; padding: 2px; text-align: center; }` thead tr th { background: #eeffff; }` tbody tr th { background: #ffeeee; }`</style> ``を、このまま適応させたいときもあると思います。ppBlogでは、リスト表示モードというのがあり、記事の読み込みにはAjaxを活用しています。この際に、最初の方で紹介した、記事ごとのCSS記述があると、まさにこんな感じで、テキストとしてCSSの記述も読み込みます。で、それを処理するのを忘れていました、という最初の話に戻るのですが、その辺の処理は以下のようにしてみました。lib.jsの256行目あたりからの部分ですが、IEとMozilla系で処理が違うので条件付きコンパイルを使っています。``if(!o("#css4list")){ /* ID#css4list を持つオブジェクトがなければ */` oParts.create("style#css4list", null, o("head")); /* ID名css4listを持つSTYLE要素をHEAD要素の子要素として追加 */` newCSS = o("#css4list").$; /* HTMLStyleElementオブジェクトの取得 */` newCSS.type = "text/css";`} else newCSS = o("#css4list").$;` /*@cc_on @*//*@if(1) newCSS.styeSheet.cssText = css[1]; @else@*/` newCSS.textContent = ""; /* 前に適応させたCSSをクリア */` newCSS.appendChild(d.createTextNode(css[1])); /* 新たにCSSを設定 */`/*@end@*/ `` リストモードでは記事は常にひとつしか表示されないので、ひとつ動的スタイルシート用のHTMLStyleElementを用意しておけば十分です。IEとMozilla系では、そこから先の処理が違います。IEでは、スタイルシートオブジェクトから操作。``HTMLStyleElement.styleSheet.cssText = (先に挙げたような素のCSSテキスト); `` Mozilla系では、DOMオブジェクトとして操作。``HTMLStyleElement.appendChild(document.createTextNode(先に挙げたような素のCSSテキスト)); `` 複数行にわたるCSSテキストを一気に適応させたいときは、こんな感じでしょうか。``ちなみに、[object HTMLStyleElement] から[object CSSStyleSheet] にアクセス?する際もIEとMozilla系では違いますね。IE8では、だいぶ歩み寄りが見られたような気もするけど。```[object HTMLStyleElement].styleSheet = [object CSSStyleSheet] /* IE */`[object HTMLStyleElement].sheet = [object CSSStyleSheet] /* Mozilla */` ``なので上のスクリプトの例では、IEの処理部分は以下のように書いても同じです。```document.styleSheets[document.styleSheets.length - 1].cssText = css[1]; ``つまり``newCSS.styleSheet == document.styleSheets[document.styleSheets.length - 1]; ``まぁこんなとこですかねぇ。そろそろアップデートせねば。。|martin|1|1|||
[21] => 1232585022|開発日誌,JavaScript|IE8βでもVMLを使えるように,IE8-VML| こんばんは。Windows 7βを試用中ですが、写真をお洒落に見せるスクリプト (photoeffect.js)が、IE8βでエラーとなり意図した効果が得られないのを発見。調べてみると、IE8ベータがVMLをサポートしていないのが原因っぽいです。おそらく、正式版ではVMLはサポートされるとは思いますが、確証はないので(Silverlightとかあるし)、現時点での回避策を考えてみました。`` IE8には、3つのレンダリングモードがあり、(紆余曲折ありましたが)デフォルトではフル標準モードでレンダリングされます。じゃ、どんな時にQuirksモード(後方互換[1] の非標準モード)になるかというと、以下の場合です。```
` 文書中に DOCTYPE宣言がない場合 ` 文書型が HTML3.0以下の場合 ` HTML4.0 TransitionalまたはFramesetのDOCTYPE宣言にURLを含まない場合 ` `
``の3つです。詳しくは、SummerWind - IE8のレンダリングモードに関するまとめ あたりを参照。`` 今どきのブログシステムでは、これらに該当しない可能性が高く、IE8は標準モードで動きます。なので(少なくとも現時点では)VMLのスクリプトが動かないわけです。じゃ、どうするか? 簡単そうなのは、[g]METAスイッチ[/g]を使うこと。とりあえずVMLはIE7モードでは問題なく作動するので、以下のようなmetaタグをwebページに追加すれば良いです。``<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> `` 確かに、これは簡単だし現実的な解決法ですが、こうしちゃうと、JavaScriptもIE7モードになっちゃいます 。IE8では、querySelectorAll などのSelectors APIやらDOMオブジェクトに対するgetter/setter API など、面白そうなAPIがサポートされるのに、IE7モードで走らせると、それらが使えません。`` 出来れば、IE8モードのままでVMLも有効にしたいです。こんなときに使えるのが、インラインフレーム<iframe> 。最近、ちっとも更新されていないのが気がかりな Dean Edwardsのエントリー が参考になります。`` つまり、IE8なページの中で、sandbox化されたIE後方互換の環境を構築し、その中でVMLを走らせるわけです。実際のデモを見たほうが早いと思うので、IE8βでアクセスしてみて下さい。IE7でも動きます。`` → http://p2b.jp/demo/vml-IE8.html`` IE8やIE7では、どう見えるかイメージショットも載せておきます。`` `` ここでは、単に画像を出力させるVMLを、以下のように関数化しています。```function vmlImg(src, w, h, r){` /*` @src: 画像ファイルを指定 ` @ w: 表示させる横幅` @ h: 縦幅` @ r: 回転角度(オプション)、-360~360` */` var ifr = document.createElement("iframe");` ifr.marginWidth= "0"; ifr.marginHeight= "0";` ifr.scrolling = "no"; ifr.frameBorder = "0";` ifr.style.width = w + "px"; ifr.style.height = h + "px";` ` document.body.appendChild(ifr);` var idoc = ifr.contentWindow.document;` idoc.write(""); idoc.close(); // これがないと document.body は null になる` if(idoc.namespaces){` if(!idoc.namespaces.v){` idoc.namespaces.add("v", "urn:schemas-microsoft-com:vml", "#default#VML" );` }` }` idoc.body.style.backgroundColor = document.body.currentStyle.backgroundColor;` var img = idoc.createElement('<v:image src="'+ src +'" />');` var imgcss = img.style;` imgcss.width = w + "px"; imgcss.height = h + "px";` if(r){` imgcss.rotation = r;` ifr.style.width = 2 * w + "px"; ifr.style.height = 2 * h + "px";` }` idoc.body.appendChild(img);`} `` IE8版ではVMLが正式にサポートされるのを期待して、深追いはしません。```
` [1] 言葉の問題なので、本文とは関係ないですが、前方互換なのか後方互換なのか、上位互換なのか下位互換なのか訳わかめ。視点をどこに置くかの差ですし。IE8から見て、IE7以下は過去のものだから、それらに対する互換性ということで、後方互換みたいに使いましたが、何か違うような。まぁ、言いたいことが分かれば良いのかなって。[^] ` ` `
|martin|1|1|||
[22] => 1218749667|開発日誌,JavaScript|チェックボックスを一度に複数選択したい,shiftKey-event|[script]`o(d).on("click", function(){` var tar = oParts.evt.target;` if(tar.nodeName.toLowerCase() == "input" && tar.type.toLowerCase() == "checkbox"){` var tr = tar.parentNode.parentNode;` if(tar.checked == false){` tr.style.backgroundColor = (tr.rowIndex % 2 == 0) ? "#eff1f3" : "#fff";` } else tr.style.backgroundColor = "highlight";` if(oParts.evt.shiftKey){` var i = tr.rowIndex - 1, item, cb = o("td > input[type=checkbox]");` for(i; i > 0; i--){` item = cb.item(i).$;` if(item.checked == true) break;` item.checked = true; ` item.parentNode.parentNode.style.backgroundColor = "highlight";` }` }` }` })`function resetAll(){` o("td > input[type=checkbox]").each(function(e, i){` if(e.checked){` e.checked = false;` e.parentNode.parentNode.style.backgroundColor = (i % 2 == 0) ? "#eff1f3" : "#fff";` }` });`}`function setAll(){` o("td > input[type=checkbox]").each(function(e){` e.checked = true;` e.parentNode.parentNode.style.backgroundColor = "highlight";` });`}`[/script]` こんばんは。イベントに関するJavaScriptの話をば。`` ppBlogでは、管理画面で記事の削除やコメントの削除が出来ますが、現状、ひとつずつしか削除できません。削除したいものが沢山あるときは大変なので(あまりそういう機会はないと思うけれど)、次回アップデート版では、チェックボックスでチェックした複数の記事などを1度に削除出来るようにする予定です。実際のスクリーンショットは以下。`` `` チェックボックスをチェックする操作というのは、数が少ない内は良いけど、数が多くなるとマウスで逐一クリックしていくのが億劫になります。なので、シフトキー(SHIFT )を活用できるようにします。つまり、最初の方のチェックボックスをチェックしておいて、シフトキーを押した状態で、下の方のチェックボックスをクリックすると、その間のチェックボックスも自動的にチェックされた状態になるというものです。この手のインターフェイスはよく使われていて、例えば、Gメールでも、そういう操作が可能です。以下に実際のデモを載せておきます(下のデモはスクリーンショットじゃなくて、ちゃんと操作できますよ)。```` さて、この手のことを実装するには、シフトキー(SHIFT )が押された状態というのをJavaScriptで検知する必要があります。最初は、```var shift, e = oParts.evt, k = e.keyCode ? e.keyCode : e.which;`shift = e.shiftKey ? e.shiftKey : (k == 16 ? true : false);`if(shift){ // シフトキーが押されていれば` ``なんてのを思いつきましたが、キーボードイベントは、以前の知識しかないなぁ、と思って、新しい情報をwebで探してみました。したら、えらく新しいのがありました(今年七夕の記事です[にこっ/])。`` JavaScript Madness: Keyboard Events `` もはや、event.which なんてのは不要で、event.keyCode でこと足りるんですね。しかも、どのブラウザでも、event.shiftKey を認識してくれそうです。なので、最終的には、この部分は、```if(oParts.evt.shiftKey){ // シフトキーが押されていれば ``と非常に短く記述することが出来ますね。ppBlog向けの、上に上げたデモのソースコードを載せておきます。```o(document).on("click", function(){ // Event delegation` var tar = oParts.evt.target; ` if(tar.nodeName.toLowerCase() == "input" && tar.type.toLowerCase() == "checkbox"){` var tr = tar.parentNode.parentNode;` if(tar.checked == false){` tr.style.backgroundColor = (tr.rowIndex % 2 == 0) ? "#eff1f3" : "#fff";` } else tr.style.backgroundColor = "highlight";` if(oParts.evt.shiftKey){` var i = tr.rowIndex - 1, item, cb = o("td > input[type=checkbox]");` for(i; i > 0; i--){` item = cb.item(i).$;` if(item.checked == true) break;` item.checked = true; ` item.parentNode.parentNode.style.backgroundColor = "highlight";` }` }` }` });``function resetAll(){ // すべてのチェックボックスを解除する` o("td > input[type=checkbox]").each(function(e, i){` if(e.checked){` e.checked = false;` e.parentNode.parentNode.style.backgroundColor = (i % 2 == 0) ? "#eff1f3" : "#fff";` }` });`}``function setAll(){ // すべて選択する` o("td > input[type=checkbox]").each(function(e){` e.checked = true;` e.parentNode.parentNode.style.backgroundColor = "highlight";` });`}` `` ppBlogでは、多くのマウスイベント処理がありますが、ほとんどは[g]Event Delegation[/g]を 活用しています。上のデモでも、チェックボックス1つひとつにイベントを登録していくのではなくて、クリックされた要素がチェックボックスであれば・・・、という捉え方をしています。イベントは、バブリング(伝達)していくものなので、最上位要素でモニターして、そのターゲットさえ掴めれば良いというわけです。`` Event Delegation versus Event Handling ` |martin|1|1|||
[23] => 1217081338|開発日誌,JavaScript|Google Adsenseの表示タイミングを制御する| 日本を出る前後、昨年の9月頃から、このページでグーグルのAdsenseを利用しています。使いこなしていないせいか、広告収入が100$に達するのに、あと数年はかかりそうなペースです。さて、この広告表示は、JavaScriptのdocument.write を使ってインラインフレーム(IFRAME)で書き出すということをしています。前回のエントリーで使用しているBLOCKQUOTE のボーダー枠を演出するJavaScriptの実行タイミングが遅いなと感じていたんですが、原因は、このdocument.writeの存在でした。ちょうど2年前に、これに関するエントリー (document.write()の実行タイミングをずらす方法 )を書いていたのを思い出し、早速、このサイトにも適用させてみました。`` 効果はてきめんで、先にBLOCKQUOTE の装飾が終わり、その後、Google Adsenseが表示されるようになりました。ppBlogでは、DOMの構築が終わるタイミングで実行されるJavaScriptが多いので(これはドキュメントの読み込み完了前に実行される)、document.write()に起因する描画遅延対策は大切ですね。`` 具体的に行った対処法は、シンプルです。テーマのtemplate.php を弄ります。このサイトのベーシックなテーマでは、左側のカラム(サイドバー)にGoogle Adsense用のDIVタグ(IDはgoogle-ads)を用意してます。```<div id="google-ads">` /* この中にグーグルからの広告用javascriptを貼り付けている */`</div><!--#google_ads--> ``で、この中身を以下のようにします。既存のグーグルのコードをいじることはないです。``<div id="google-ads">` <script type="text/javascript">` (function (){` var alts = [];` d._write = d.write; /* オリジナルのdocument.writeをコピーしておく */` d.write = function(s){ alts.push(s);}; /* 新たにdocument.writeを定義。ここでは、単に配列に入れるだけ */` oParts.start(function(){ /* DOM構築完了のタイミングで実行されるように登録する */` o("#google-ads").html(alts.join("")); /* 配列に入れた本来のdocument.writeの中身を書き出す */` d.write = d._write; /* オリジナルのdocument.writeに戻す */` });` })();` </script>` /* 以下、既存のコード */` <script type="text/javascript"><!--` google_ad_client = "pub-XXXXXXXXXXXX";` google_ad_slot = "XXXXXXXX";` google_ad_width = 180; /* 広告の幅 */` google_ad_height = 150; /* 広告の高さ */` //--></script>` <script type="text/javascript"` src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>` </div><!--#google_ads--> `` 基本的には、これでOKです。後は、予め広告のサイズが分かっているので、見栄えとしてCSSファイルを少しいじるだけです。このサイトでは以下のように指定しています。``#google-ads { ` margin: 70px auto 0 10px;` padding: 0;` width: 180px; /* 広告の幅に合わせる */` height: 150px; /* 上と同様に */`} `` ちなみに、このサイトのwebRing は、「ページ作成機能」を使って作っていますが、表示スクリプトはBlogPeopleさん のを利用しています。ここでもdocument.writeが使われているので、それに対処した記述をしています。その中身をさらしておきます。```<div class="center" style="color:#2f4f4f;">`<h2>ppBlog's webRing</h2>`<p style="padding:1em;font-size:14px;width:300px;margin:auto;">`ppBlog使いの方々です。随時募集中。`<br />多分に見落としなどもあると思いますので,このサイトもお願いしますというのがあれば`気兼ねにメール下さい。自薦他薦問いません。</p>`<div id="_webRing" style="margin: 2em auto;width:300px;text-align:left;"></div>` /* ここから */`<script type="text/javascript">` (function (){` var alts = [];` d._write = d.write;` d.write = function(s){ alts.push(s);}` oParts.start(function(){` o("#_webRing").html(alts.join("")); /* 上で用意した DIV#_webRing に流し込む */` d.write = d._write;` });` })();`</script>` /* ここまでがポイント */`<script type="text/javascript" charset="utf-8"` src="http://www.blogpeople.net/display/usr/0f0d40535b5b4103.js"></script>`</div> |martin|1|1|||
[24] => 1209888535|開発日誌,JavaScript|window.onloadの代替スクリプトその2,events-order| 以前に、[g]window.onloadの代替スクリプト[/g]の記事を書いていたんですが、今はちょっと違ったスクリプトになっているので、この辺で、まとめもかねてメモを。`` 今ではどのJavaScrptライブラリーも、画像も含めたページ読み込み完了まで待って、つまりwindow.onloadのタイミングで初期化関数を走らせるなんてことはせずに、もっと早い段階、つまりブラウザがDOMの構造を把握してパースしたタイミングを見計らって、初期化関数を実行するようにしています。Firefox、Operaでは、ブラウザ側がDOMContentLoaded というイベントハンドラを用意してくれているので、これを使います。Safariがこれを採用するのも時間の問題だろな、と前々から思っていたんですが、昨日、Safari最新版(3.1.1)も対応しているっぽいことに気が付きました。こういうスクリプト を書いて、イベントの発火順序を調べていたんですが、何気にSafariで動いているんでちょっとビックリしました。`` イベントの発火順序を調べる→http://p2b.jp/demo/events-order.php`` IEに関しては、DOMContentLoaded というイベントハンドラはないので、以前から色々なアプローチが取られてましたが、今ではbase2 のDean Edwardsも、jquery のJohn ResigもDiego Periniが見つけたdoScroll を採用しています。確かに、これでも良いのですが、自分は天の邪鬼なので、HTCファイルでondocumentready を監視するというアプローチにしています。上に挙げたイベントの発火順序を調べるページの結果を見ると、IEに関しては、次のようなイベント順位で実行されてます。``` oncontentready /* この時点でDOMのパースが終了 */ ` deferred script /* defer指定された外部スクリプトが実行される */ ` ondocumentready /* 次にHTCでのこのイベント。oParts.jsではこの時点で初期化。*/ ` doScroll /* 次にdoScroll */ ` window.onload /* 最後にこれ */ ` `` このサイトのように、ソースをハイライト表示するスクリプトなどを使ってる場合は、数ミリ秒でも早くハイライト関数を起動させたいので、ondocumentreadyのタイミングで実行させてます。doScrollのタイミングでも良いんですが、十数ミリ秒程度遅れるんですよねぇ。oncontentreadyが一番早いですが、極々まれにこれが評価されないことがあるので、確実なondocumentreadyにしてます。`ondocumentready とかoncontentready ってBehavior絡みでしか使えないのが玉に瑕です。普通のイベントハンドラとして使えるようにしてくれれば良いのに、マイクロソフトさん。`` というわけで、ppBlogで使っているJSライブラリ oParts.js での初期化関数は以下のような感じになってます。```oParts.start = function(F){` if(client.Gecko || client.Opera || client.Safari){` document.addEventListener('DOMContentLoaded', F, false);` } else if(client.MSIE){` $IEHTC = document.documentElement.addBehavior('js/ie.htc');` } else o(window).on('load', F);`}; `` SafariでもDOMContentLoadedが使えるようになったので記述が少なくて良いですね。IE向けのie.htcファイルの中身は以下のような感じ。```<public:attach` event="ondocumentready"` onevent="oParts.callee[oParts.callee.length-1]();` document.documentElement.removeBehavior($IEHTC);` $IEHTC=null;" /> `` ちなみにイベント発火順位を調べるスクリプトですが、以下のように指定すると、このサイト以外のページも読み込めます。Yahoo!の例を挙げておきます。``http://p2b.jp/demo/events-order.php?site=yahoo.co.jp`` 余談ですが、jquery-1.2.3 のテストをしていて、気になった点がありました。以下のようなスクリプトを書くと、$()関数がwindow.onloadの後にしか実行されません(IE7の場合)。jqueryに関しては素人なので使い方が間違っているのかもしれませんが、非常に簡単な例なので気になります。```<script type="text/javascript">` window.onload = function(){` document.getElementById("test").innerHTML = '<p>window onloaded!</p>';` }` $(function(){` alert($("#test").html());` });`</script> ``` 実際の実行サンプル 。FirefoxとIE7で挙動が違います。Firefoxでは意図したように、window.onloadよりも先に$()が評価され、<p>original</p> とアラート表示されますが、IE7ではなぜか、window.onloadに割り当てた関数が先に実行されて、<p>window onloaded!</p> って表示されるんですよねぇ。何でかなぁ。doScroll絡み?|martin|1|1|||
[25] => 1207571829|開発日誌,JavaScript|pettieSyntaxの微調整とか| こんばんは、martinです。JavaScript単体で稼働するソースハイライト表示用のスクリプトpettieSyntax.js ですが、スタイルシートのクラス指定に対応したバージョン(pettieSyntax-class.js )も作成してみました。と、同時に正規表現の微調整をしています。一応、CSSソースのハイライトにも対応していますが、CSS3のセレクタ とかは何でもありの状態なので、まぁ抜けがあったりするかも知れませんが、そこまで完璧を求めていないので。ちなみに、CSS3のセレクタは、具体例で挙げると以下のような感じです。``a[rel="external"] { /* REL属性がexternalなら外部アイコンの背景を付ける */` padding-right: 5px;` background: url(Images/external-icon.png) no-repeat right center;` }`p[class~="foo"] { /* クラス名にfooが含まれていれば */` margin: 0;` padding: 0;`}`a[href$=".pdf"] { /* リンク名が.pdfで終わっていれば */` background: url(Images/pdf.png) no-repeat right center;`}`a[hreflang|="en"] { /* リンク要素のhreflang属性がenで始まっていれば(enとかen-USとか) */` background: url(Images/english.png) no-repeat right top;`}`div p:nth-child(2n+1) { /* DIVの子孫P要素で奇数番目のもの */` background: ivory;`} `` 別件ですが、JavaScriptライブラリのoParts.js は、このCSS3のセレクタにも対応していて、例えば、``o('div:nth-child(2n+1)') ``とすれば、奇数番目にあるDIV要素を一気に取得することが出来ます。お遊びですが、```<button onclick="o('ul li:nth-child(2n+1)').css('background:red');">`CLICK ME!`</button> ```というコードを記述してみます。期待するのは、「UL要素の子孫のLI要素で奇数番目のやつの背景を赤にする 」という動作です。````CLICK ME!`
``` syntax.jsの最新版とクラス対応版、およびそのCSSファイルを添付しておきます。ハイライト用のCSSファイルは、テーマのCSSファイルに追加しても良いでしょうし、以下のようにCSSファイルの中で呼び出すことも出来ます。``@import 'syntax.css'; `` メンテナンスしやすいのは、上記のように別ファイルで用意しておくインポート方式でしょうか。``[file:1207571829_syntax.js:2.9/]`[file:1207571829_syntax-class.js:2.8/]`[file:1207571829_syntax.css:0.3/]|martin|1|1|||
[26] => 1207470856|開発日誌,JavaScript|pettieSyntaxがCSSに対応| ソースをJavaScriptでハイライト表示するpettieSyntax ですが、CSSの表記にも対応しました。このせいでファイルサイズが3キロ弱になりましたが、まぁこの辺で落ち着くでしょう。`` 色の指定をソースの上の方で、指定できるようにしてます。ソースを見ると、CSSの表記に対応した色指定だけ3色ありますが、これは1.タグ 、2.CSSプロパティー (fontなど)、3.その実際の指定値 に対応しています。```var cssprops = ['indigo', 'slategray', 'indianred', /((?:[-#a-z0-9.,_* +:¥r¥n[¥]=]{2,}|[abipq*]))¥s*¥{¥s*[^}=]+?¥}/ig]; `` 実際のデモとして、このサイトのベーシックテーマに適用しているPREタグのCSS指定をば。```pre {` font: 500 1em/1.4 "Consolas","Bitstream Vera Sans Mono","Lucida Console","Courier New",Verdana,Meiryo,monospace;` background: #f6f6f9;` border: double 4px #808080;` border-width: 0 0 0 4px;` margin: 1em auto;` padding: 20px;` width: 89%;` height: 3em;` color: #333;` clear: both;` white-space: pre;` overflow-x: auto;` letter-spacing: 0.1px;`}`body[id=weblog] pre { /* IE6以外のモダンなブラウザ用 */` overflow: auto;` height: auto;`}` `` こんな感じです。仕様上、PHPやJavaScript用キーワードの中途半端な対応と違って、CSSのあらゆるプロパティーに対応してると思います。最新版を添付しておきます。`[file:1207470856_syntax.js:2.9/]|martin|1|1|||
[27] => 1207282359|開発日誌,JavaScript|ソースコード表示用の軽いスクリプト:pettieSyntax,pettie_syntax| こんにちは。たまにはアップデート以外の記事も書きたいので。`` 前々から思っていたのですが、このブログではPHPとかJavaScriptのソースを提示することがままあります。現状、モノクロの味気ないソースコードだったので、PHPマニュアルのユーザーノート にあるようなカラフルな色付けにしたいなと思っていたわけです。この手のことを実現するには大きく2通りのアプローチがあって、サーバー側でハイライト表示の処理をして読み込ませる方法と、もうひとつはクライアント側のJavaScriptに解析させてその場でハイライト表示させる方法です。前者の例だと、PHPライブラリではGeSHi - Generic Syntax Highlighter などがあり、後者だとグーグルのgoogle-code-prettify が有名でしょうか。`` でも、この手のやつって、これようのCSSファイルも用意しないといけないし、ファイルサイズはでかいし、設置が面倒そうだなと。なので書いてみました。基本的に、自分用なのでPHPとJavaScriptの一部の関数しかサポートしていませんが、まぁ、要は見栄えを良くすることが目的なので、すべてを網羅する必要はないと考えてます。PHPの組み込み関数なんて山ほどありますし。おかげで随分とサイズが小さいスクリプトになりましたが、それっぽく見えます。`` 特徴としては、このスクリプト単体で動作可能 ということです。他の見栄えのためのCSSファイルとかは必要ないです。記事の中のPREタグを見つけたら、その中身をハイライト表示します。初めは、PREタグに特定のクラス名を付けて、それを認識させようかと思っていましたが、そうすると過去の記事の書き換えをしないといけないし、どうせPREタグに書くのはソースコードぐらいだろうと割り切り、PREタグに絞りました。早速ですが、そのソースコードをこのスクリプトを使って表示してみます。``/*` Lightweight syntax-analyzing script` Copyright: modified BSD license 2008 martin` version: 20080803.204553`*/``function pettieSyntax(){` var cssprops = ['#c03', '#003366', '#636', /((?:[^{;_<]{2,}|[abipq*]))¥s*?¥{(¥s*[^}=_[¥]?<>]+?)¥}/ig];` var etcetera = ['#c33', /( > | < )/g];` var operants = ['green', /(¥/¥/¥-¥->|¥+¥+|¥-¥-|!?===?|<=|>=|=>|¥+=|¥-=|!=|&&|¥|¥|| = )/g];` var variants = ['#669', /(¥$¥w+?¥b|var¥b¥w+¥b|@)/g];` var htmltags = ['navy', /(<¥/¥w+?>|<¥w+|<¥?(?:xml|php)|(?:¥?|¥/)>|>)/g];` var keywords = ['blue', /(¥b(?:alert|[Aa]rray|break|case|catch|class|charset|continue|Date|default|define|delete|do|else|false|FALSE|for|function|global|i[fn]|instanceof|new|null|Object|return|script|src|type|switch|this|throw|document¥S|TRUE|true|echo|try|typeof|var|void|while|window¥S|with|Location|version|encoding)¥b)/g];` var decimals = ['#c69', /([¥s:¥/¥(,[+;."=>])(¥-?¥d+(?:¥.¥d+)?)(%|px|pt|em)?/g];` var equipped = ['#036', /([¥.¥s(@>])([^'"¥(¥n=¥s<>¥/]+?)([¥( ])/g];` var oneliner = ['#906', /('[^'¥r¥n]+?'|"[^"]*?"|<![^¥r¥n]+?¥-¥-.*?>)/g];` var regulars = ['tomato', /([¥s(])(¥/[^¥r¥n]+?¥/)([igmy¥s¥);.])/g];` var enoparts = ['indigo', /(¥b(?:o|oParts)¥b|¥.(?:away|moveTo|sizeTo|dimension|target)¥(¥)|¥.(?:loadScript|evt|metrics|start|css|view|each|sib))/g];` var comments = ['#093', /([^:]|)((?:¥/¥/|#)¥s+[^'¥r¥n]+(?:¥r?¥n?|$)|¥/¥*[¥s¥S]+?¥*¥/)/g];` var omitspan = function(m){ return m.replace(/<span[^>]*?>/ig, '').replace(/<¥/span>/ig, '');}` var targets = document.getElementsByTagName('PRE');` if(targets){` for(var t, i = 0; t = targets[i++];){` var c = t.innerHTML.replace(/<(?:span|code)[^>]*?>([^<]+?)<¥/(?:span|code)>/ig, '$1').replace(/¥t/g, '').replace(/'/g, ''').replace(/"/g, '"');` if(/<(?:a |img )/i.test(c)) continue;` c = c.replace(cssprops[3], function(A, B, C){ return /:/.test(C) ? '<span style=¥tcolor:'+cssprops[0]+'¥t>'+B+'</span>' + A.replace(B, '').replace(/([^:{]+?):([^;]+?);/g, '<span style=¥tcolor:'+cssprops[1]+'¥t>$1</span>:<span style=¥tcolor:'+cssprops[2]+'¥t>$2</span>;') : A;});` c = c.replace(etcetera[1], '<span style=¥tcolor:'+etcetera[0]+'¥t>$1</span>');` c = c.replace(operants[1], '<span style=¥tcolor:'+operants[0]+'¥t>$1</span>');` c = c.replace(variants[1], '<span style=¥tcolor:'+variants[0]+'¥t>$1</span>');` c = c.replace(htmltags[1], '<span style=¥tcolor:'+htmltags[0]+'¥t>$1</span>');` c = c.replace(keywords[1], '<span style=¥tcolor:'+keywords[0]+';font-weight:bold¥t>$1</span>');` c = c.replace(equipped[1], function (A, B, C, D){return B + '<span style=¥tcolor:'+equipped[0]+'¥t>' + C + '</span>' + D;});` c = c.replace(enoparts[1], '<span style=¥tcolor:'+enoparts[0]+';font-weight:bold;¥t>$1</span>');` c = c.replace(decimals[1], '$1<span style=¥tcolor:'+decimals[0]+'¥t>$2$3</span>');` c = c.replace(regulars[1], function (A, B, C, D){return B + '<span style=¥tcolor:'+regulars[0]+'¥t>' + omitspan(C) + '</span>' + D;});` c = c.replace(oneliner[1], function (A, B){return '<span style=¥tcolor:'+oneliner[0]+'¥t>' + omitspan(B) + '</span>';});` c = c.replace(comments[1], function (A, B, C){return B + '<span style="color:'+comments[0]+'">' + omitspan(C) + '</span>';});` c = c.replace(/¥t/g, '"');` if(/*@cc_on!@*/false){` t.outerHTML = '¥n<pre>' + c + '</pre>¥n'; /* IEの改行対策です */` } else t.innerHTML = c;` };` }`}; ``` ファイルサイズは2キロ前後と小さいです。ppBlogで使うのであれば、とりあえずこのファイルをjsディレクトリ にアップロードして、後は、lib.js の最初の方で、このスクリプトを以下のように呼び出します。``oParts.loadScript('js/syntax.js'); ``次に同じlib.jsの下の方にある、oParts.start 関数内で、このpettieSyntax() を呼び出せばOKです。``if(typeof pettieSyntax != UD) pettieSyntax(); `` コードを見れば分かりますが、ハイライトの色はSPANタグ内でスタイルシート指定してますので、この部分を好きな色に変えれば良いです。あと、強調したいキーワードを追加したいときも、ソース内の該当箇所に追加していけばよいです。`` ppBlog使用前提なら、上のような設置になりますが、純粋に単体で動かしたい場合は、外部スクリプトとして、``<script type="text/javascript" src="path/to/syntax.js"></script> ``みたく読み込ませて、ページ読み込み完了後にpettieSyntax() を呼べば良いです。`` まだろくに動作検証していないのですが、興味がある方はどうぞ。syntax.jsと、あと、ppBlogの設置例としてlib.jsを添付しておきます。```[file:1207282359_syntax.js:2.2/]`[file:1207282359_lib.js:20.7/]|martin|1|1|||
[28] => 1190046144|開発日誌,JavaScript|ppBlogでのgetElementsByClassName|こんばんは、martinです。ソーシャルブックマークへのリンクアイコンを各記事に付けるようにしました。近々配布予定の最新版に採用してます。またJavaScript関連の話をば。`` 愛用しているタブブラウザFirefoxの現行バージョンは2 ですが、バージョン3 からはネイティブでdocument.getElementsByClassName がサポートされる ようです。現時点でこのメソッドをサポートしている主要なブラウザは存在しないので、[g]document.getElementsByClassName[/g]でググれば分かるように、皆さん、色んなgetElementsByClassNameを書いています。ppBlogのJavaScriptのライブラリlib.js でも、この関数(getByClass)は多用しているのですが、この関数は、ページ内のオブジェクトを逐一調べていくループ作業を伴うので、一般的に言って動作に時間がかかります。`` JavaScript使いの間では、常識かと思いますが、この手の操作で一番スピードが速いのは、[g]XPath[/g]を利用するやり方です。[g]jQuery[/g]で有名なJohn Resig氏のブログに、この手の関数のスピード比較記事があります。 ` →http://ejohn.org/blog/getelementsbyclassname-speed-comparison/`` ネイティブサポートのFirefox3が無茶苦茶速いのは当然として、XPathもDOMを解析していく手法よりはずっと高速なのが分かります。IE6,7を除くモダンなブラウザは、このXPathをサポートしているので、ppBlogでもXPathを使うようにしました。以下のような感じです。``document.getElementsByClassName(className, pElement, tagName){` var d = document, nodes = [], item;` try { // XPathをサポートしているならこれを使う ` var xp = d.evaluate(` './/'+(tagName || '*')+'[contains(concat(" ", @class, " "), " '+className+' ")]',` (pElement || d), null, XPathResult.ANY_TYPE, null` );` for (item = xp.iterateNext(); item; item = xp.iterateNext()){` nodes.push(item);` }` } catch(e){ // そうでなければ地道にDOM解析 ` var cls, items = (pElement || d).getElementsByTagName((tagName || '*'));` for(var i = 0, l = items.length; i < l; i++){` item = items[i];` if(item.className){` cls = item.className.split(/¥s+/);` for(var j = 0, k = cls.length; j < k; j++){` if(cls[j]==className){` nodes[nodes.length] = item; break;` }` }` }` }` }` return nodes.length > 0 ? nodes : null;`} `` John Resig氏のブログにあるのと同じ実験 をしてみましたが(パクっただけ)、彼のサイトにある、Prototype.jsのXPathを使ったもの(xpath.html )と同等か少し速いくらいですね。`` 当然ながら、XPathをサポートしていないIE6,7は、通常通りDOM解析をするアプローチしかないです。こちらの計測では、XPathを利用するより5倍ほど遅いです。`` ちなみに、1つ前のエントリで紹介したphotoeffect.jsも、この関数を使用しているので更新しました。ついでに添付しておきますね。このphotoeffect.jsは、単体で動くので、ppBlog以外の各種ブログでも外部ファイルとして呼び込むだけで簡単に使えます。要望があれば、詳しいクラス指定などの解説書きますんで、その時はたずねて下さい。`[file:1190046144_photoeffect.js:4.9/]|martin|1|1|JavaScript||
[29] => 1188848148|開発日誌,JavaScript|アップした写真をお洒落に見せるJavaScript| 久しぶりのJavaScriptネタです。最近は、マック(MBP)で作業することも多いのですが、アップルの提供する.Mac というサービスを使うと簡単に見栄えのよいホームページ(HP)を作成することが出来ます。このサービスはマックを使ってこそ活きるものですが。`` で、何種類ものHP向けのテンプレートが予め用意してあるんですが、多くのテンプレには、デジカメで撮った写真を単に貼り付けるだけでなくて、それらをちょこっと斜めにしたり切手風に縁取りしたり出来て、お洒落に演出できるわけです。自分も以前からこういうことをppBlogでしたいなぁとは思っていて、現状、写真っぽく見せたりドロップシャドウを付けたりというのはスタイルシートを使って出来るようにしています。後は、切手風とか写真を少し傾けて貼り付けるとかですが、これらはサーバーサイドのPHPプログラムで画像を加工すれば可能なんだけど、写真をアップする際の手間が増えるのは好ましくないし、何よりプログラムを書かないといけない。`` 画像の回転(写真を傾けて貼り付けたいので)なんて、クライアント側のJavaScriptで出来れば楽チンだけど無理だよなぁと思っていたんだけど、実は<canvas> を使えば簡単に出来ちゃうんですね。しかも画像同士を合わせることも簡単に出来る。これなら自分がやりたいことはJavaScriptで出来るじゃないか、ということで書いてみました。`` `` これは通常の、ppBlogでデフォルトで出来ることです。つまり写真のように白枠を付けて、ドロップシャドウを付けて、ということですが、さすがに写真をちょいと傾けるとかは出来ません。ところが、これに、canvasを利用したJavaScriptをかますと以下のようになります。`` `` この写真は上の写真と同じ画像ファイルを指定していますが、見栄えが随分と違ってます。しかもこれをJavaScriptでやっているわけで、canvas恐るべしですね。反対向きの傾きにしたらこんな感じです。`` `` これは写真風のフレーム画像を合わせてますが、切手風のフレームを合わせたのが以下です。`` `` てな感じです。どちらに傾けるかとか(傾けないというのもあります)、写真風か切手風かというフレームの選択は、何れもIMGタグの中でクラス指定をすることで実現します。`` ほかにいくつかデモを並べてみます。`` ` ` `` ` `` ちなみに、IE(インターネットエクスプローラー)はcanvasをサポートしていないので、こちらは[g]VML[/g]を利用して同様の効果を見せるようにしています。こちらで動作が確認できたブラウザは、ウィンドウズではIE7, Firefox2.0でマックではSafariとFirefoxです。OperaやIE6以下は未確認です。`` 一応、このスプリプトは外部読み込み形式で作動するようにしています。ppBlogであれば、各テーマのテンプレートHTMLファイルに、外部スクリプトを呼び込む要領で、``<script type="text/javascript" src="js/photoeffect.js"></script> ``と記述して、あとはlib.js のDOM.onload の最後の行にでも、``if(typeof photoEffect != 'undefined'){` photoEffect.init();`} ``と書いておけば動きます。あ、あと2種類のフレーム用の画像はトップページのImagesディレクトリにアップしておく必要があります。クラス名の指定ですが、IMGタグでのクラス指定で photo-effect が指定されていれば、それを認識して、このスクリプトが発動します。このスクリプトと画像ファイルを添付しておきます。まだ作りたてなので細かいところは見ていませんが興味ある方はどうぞ。```[Update:2007/10/8 17:31:18] ` 傾きの角度は、自由に指定出来るようにしました。クラス名で photo-angle3 や photo-angle-3 みたいに指定します。数字は-360度から360度まで。マイナスの値なら、右肩上がり、正の値なら左肩上がりです。`` あ、ついでにppBlogユーザー向けも。これは、Firefoxに対するマウスオーバーチップ処理を追加しています。``[file:1188848148_photoframes.zip:31.7/]`[file:1188848148_photoeffect.js:4.2/]`[file:1188848148_photoeffect-ppblog.js:5.7/]|martin|1|1|JavaScript||
[30] => 1153728573|開発日誌,JavaScript|document.write()の実行タイミングをずらす方法|`` このサイトのppBlog's webRingのリストは、BlogPeople からdocument.write で書き出しています。というかBlogPeopleのスクリプトがそうなっているのですが。で、document.writeが、HTMLソースの最初の方にあると、document.writeによる書き出しが終わるまで、それ以降のページのレンダリング(HTMLの書き出し)はストップした状態となります。document.writeによって呼び出している外部サーバー(ここではBlogPeople)のレスポンスが速ければ、ページ描画で待たされることはありませんが、たまにはレスポンスが遅くて、ページの残りがなかなか表示されないという事態は起こりえます。このdocument.writeは、広告バナーなどでも当たり前のように使われていて、メインコンテンツの内容が知りたいのに、バナー広告がなかなか表示されず、イライラしたという経験もあるかと思います。確か、GoogleのADWordsなどでも使われているのではないでしょうか。`` こういう事態を避けるには、HTMLソースの最後の方で document.write を呼び出すというのがあり、これはこれで有効ですが、そう汎用性のあるやり方ではなく、また、スクリプト(JavaScript)がHTMLの中で、あちこちに散らばるのは美しくないです。`` ここでは、document.writeの内容をとりあえず配列などに一時的に退避させといて(この間にHTMLソースの描画は進んでいきます )、ページ読み込み完了と同時に、そのストックしておいたdocument.write内容を吐き出すという方法について書いておきます。`` まず、普通にdocument.writeを含んだ外部スクリプトを呼び出すやり方。これは、BlogPeopleがこうしましょう、と述べているものです。``<script type="text/javascript"` src="http://www.blogpeople.net/display/usr/0f0d40535b5b4103.js"` charset="euc-jp"></script> ``これの実際の中身は、次のようになっています。``document.write('<div class=¥'blogpeople-main¥'>');`document.write('登録されたサイト')` :` :`document.write('</div>');`document.write('<div class=¥'blogpeople-powered-by¥'>');`document.write('<br /><a href=¥'・・・¥' target=¥'_blank¥'><img src=¥'・・・¥' border=¥'0¥' alt=¥'Powered By BlogPeople¥' /></a>');`document.write('</div>'); ``ここに使われているdocument.write内容をとりあえず配列に入れておけば良いのですが、それには次のようにします。```(function(){` var alts = []; // document.writeの内容を入れておく配列を準備 ` d._write = d.write; // オリジナルはコピーしておく ` d.write = function(s){ alts.push(s);} // d.writeを新たに定義 ` var src = "http://www.blogpeople.net/display/usr/0f0d40535b5b4103.js";` var script = d.createElement('script');` script.setAttribute('type', 'text/javascript');` script.setAttribute('charset', 'euc-jp');` script.setAttribute('src', src);` d.getElementsByTagName('head')[0].appendChild(script);` addEvent(window, "load", function(){` o("_webRing").innerHTML = alts.join(""); // 指定した場所に流し込む ` d.write = d._write; // d.writeを元の定義に戻しておく ` });` }()); `` ポイントは、既存のdocument.writeを新たに、``document.write = function(s){ alts.push(s); } ``と再定義している点です。ここではalts という配列に退避させています。こうすることで、document.writeによる書き出しを待つことなく、残りのページレンダリングを進めていくことが出来ます。このスクリプトで、SafariでもIE6でもFirefoxでもきちんと動くことを確認しています。`` 小生のブログであるmartin.p2b.jpのwebRing と、このサイトにあるwebRing とは同じ外部スクリプトを呼び出しているのですが、比較のために、このサイトのwebRingは上記の手法で、martin'sの方は従来の方法で呼び出しています。BlogPeopleのサーバーレスポンスは素早いので、なかなか差が実感できにくいかと思いますが、このサイトのやつは、(サイドバーのHTMLソースはdocument.writeよりも後にもかかわらず)サイドバーが先に表示されて、最後にwebRingのリストが表示されるのに対して、martin'sの方は、HTMLソースの順序通り、webRingリスト→サイドバーという順序で表示されています。`` ちなみに、このサイトのwebRingは、ppBlogの「ページ作成機能」を使って作成しているのですが、具体的に、どのような内容になっているのか、理解の一助のために添付しておきますね。`` まぁ、こんなアプローチもありますよということで [署名/]``[file:1153728573_webRing.txt:0.9/]|martin|1|1|||
[31] => 1152214827|開発日誌,JavaScript|window.onloadの代替スクリプト|Updated Entry : http://p2b.jp/200805-events-order ``` 今日は[w]七夕[/w]ですね。小学生の頃は、学校総出で「♪笹の葉さーらさら〜」ってやって、七夕伝説に思いを馳せたものですが。いつになってもガキの頃の心を忘れない大人でいたいものです。`` さて、Ajax の隆盛によって、その根幹であるJavaScript はブログに欠かせないものとなっています。ppBlogも例外ではなく、至るところで活躍してます。一般にブログの(トップ)ページは、色々な情報を詰め込んでいるために、ファイルサイズは大きくなる傾向にあり、また、画像も至るところで使われるために、読み込むべきコンテンツサイズは更に大きくなります。ブロードバンドが当たり前の今日でもページの表示にちょっと待たされるのは珍しいことではありません。で、JavaScriptは(画像も含めた)ページの内容物が全部読み込まれた後に作動させるのが一般的です。なので、もし、画像の読み込みに時間がかかったりすると、JavaScriptがうまく動かずに、狙い通りの効果が得られなかったり、スクリプトエラーが出たりします。こういうのはたいてい、window.onloadやBODYタグでのonloadイベントで制御しているんですが、JavaScript使いなら、画像の読み込みを待たずに、ページの構成要素(DOMエレメント)がパース(読み込まれて解釈)された時点でプログラムを走らせたいと思うものです。`` その解決法として、JavaScriptマスターのDean Edwards 氏が、window.onload問題を解決した! と言ったのが昨年の9月頃で、このブログでも言及した ことがあります。彼のやり方は、Mozilla系ブラウザの隠し要素DOMContentLoaded を利用したもので、IEはこれに対応していないので、IEだけは、外部jsスクリプトを用意しないといけませんでした。その不完全さが好きになれなかったので、ppBlogではこれとは違うアプローチ(setTimeoutを利用するやり方)を取ってたんですが、このsetTimeoutがらみの手法は、PCの性能などにも大きく左右されるし、どうも不安定さがあるんですよね。角丸コーナースクリプトが上手く作動しなかったり。`` そんな折、先月にこれでほぼ100%解決と思われるやり方がEdwardsを含めたJavaScriptマスターたちによって「発見」されました。→http://dean.edwards.name/weblog/2006/06/again/`` この新しい方法では、IEでも外部スクリプトを別に用意する必要がなく、またSafariなどでも問題なく動くものです。いやぁ、みんな頭良いよなぁ。自分でも個人的に、IE向けにはdocument.readyState を利用した方法を試したりしたんですが、IEの返すdocument.readyStateが、またいい加減なシロモノでしてあえなく挫折。しかも、DOMContentLoaded に関しては、Operaの最新バージョンであるOpera9でサポートされたりしたんで、自分もこの潮流に乗っかることにしました。そのうちSafariでもDOMContentLoadedはサポートされそうな予感。そういえば、Opera9では、とうとうdocument.designModeがサポート されましたね。以前、[g]document.designMode[/g]を利用して、ppBlog組み込みの本格的なエディターを作ろうとしたことがあったのですが、IEの吐き出すHTMLソースがあまりに後進的だったので放置したままになっていますねぇ。IE7βでどうかはまだ試していないです(期待してないけど)。`` 少し逸れましたが、とにかくppBlogでもwindow.onloadに替わる新しいやり方を実装してみました。詳しい話はエドワーズのブロッグを読んでいただくとして、ppBlogでは現時点で、次のような感じにまとめています。``DOM = { /* window.onload alternative */` complete : function(){` if(arguments.callee.done) return;` arguments.callee.done = true;` if(typeof _timer != 'undefined'){` clearInterval(_timer);` _timer = null;` }` DOM.onload();` },` check : function(){` if(d.addEventListener){ // Mozilla/Opera9向け` d.addEventListener("DOMContentLoaded", DOM.complete, false);` }` if(navigator.userAgent.match(/webkit|safari|khtml/i)){// Safari向け` var _timer = setInterval(function(){` if(d.readyState.match(/loaded|complete/)){` DOM.complete();` }` }, 50);` }` /*@cc_on @*/` /*@if (@_win32)// IE向け` d.write('<script id="_decoy_" defer src="javascript:void 0"><¥/script>');` d.getElementById("_decoy_").onreadystatechange = function(){` if(this.readyState=='complete'){` DOM.complete();` }` };` /*@end @*/` addEvent(window, "load", DOM.complete);//その他` },` onload : function(){}`} `` ポイントは、DOMContentLoadedをサポートしているならそれを使い、Safari系ではdocument.readyStateを利用し(IEと違って信用できる値を返すようである)、IEではスクリプト属性のdefer を利用することです。このdeferは文字通り、(外部読み込み)スクリプトの実行を遅延させる属性でIEのみがサポートしています。このdeferは、document.readyStateよりずっと堅実です。ついでに言うと、ここで使われているdefer指定の外部スクリプト指定は、もはや単なる「飾り」になっています(それがIE7正式版でどういう扱いになるか一抹の不安が残りますが)。詳しくは、このトリッキーな手法を発見したMatthias Miller 氏のブログ(エントリー )をどうぞ。`` ちょっと話題がずれますが、IE7βは先日ベータの最新版かつベータ最終版が出ましたね。それに少し関連したErik氏の面白いエントリーがあったので紹介しておきます(まぁ、期待外れ感が漂う、ちょっとしたジョークなんでしょう)。`` →IE 7 DOM and JS Changelog `` Erik氏は、昨日のエントリーにも出てきたWebFXの主宰者の一人で、彼もまたJavaScriptマスターです。このエントリーは時期的にIE7ベータ1-2についてのものかな。思わず笑ってしまいました [署名/]|martin|1|1|||
[32] => 1152131691|開発日誌,JavaScript|Firefoxでのwindow.event| ワールドカップの準決勝ポルトガル-フランスのせいで寝むれないmartinです。``以前のエントリで「Firefoxでの擬似window.event 」として、``function windowEvent(){` if(window.event) return window.event;` var caller = arguments.callee.caller;` while(caller){` var ob = caller.arguments[0];` if(ob && ob.constructor == MouseEvent) return ob;` caller = caller.caller;` }` return null;`} ``というのを載せていましたが、どうせなら、Firefox(および、その他のモダンなブラウザ)にも、window.event を認識させたいです。window.eventは使い勝手が良いので。で、このwindow.eventを簡単に認識させる方法があったので、今はこれを使っています。もともとのソースは、JavaScript使いなら誰でも知っている、昔からの有名サイトWebFX 内にある「Classic Event Handlers - IE Emu 」で紹介してあった方法です。リンク先を見れば分かりますが、これ以上ないくらいシンプルでスマートなやり方ですねぇ。最初は、__defineGetter__ あたりを使ってエミュレートしようとしていたのですが。いやはや。`` js/lib.js では、次のようにしています。``if(w3c){ // IE以外のモダンなブラウザなら.` (function(){` for (var property in Event.prototype){` if(property.match(/MOUSE|CLICK/)){` window.addEventListener(property.toLowerCase(), function(e){` window.event = e;` }, true);` }` }` }());`}; `` イベントリスナーに逐一マウスイベントを列挙して登録しても良いのですが、Eventオブジェクトにある組み込み定数を利用してみました。マウスイベントを列挙するなら、次のような感じになると思います。``if(w3c){ // IE以外のモダンなブラウザなら.` (function(){` var events = ["mousedown", "mouseover", "mouseout", "mousemove",` "mousedrag", "click", "dblclick"]; ` for (var i = 0; i < events.length; i++){` window.addEventListener(events[i], function(e){` window.event = e;` }, true);` }` }());`}; `` まぁ、こちらの方が記述も短めでオーソドックスなやり方ですが、イベントを列挙するのがメンドかったので、別のアプローチを取ってみました。お好きな方でよいと思います。`` こうすると何が嬉しいかって、例えば、次のようなスクリプトにおいて、``document.onclick = function(e){` alert("Event X:"+e.clientX + ", Event Y:"+e.clientY);`} ``この引数のe は、Mozilla系のブラウザでは必須のもので省略は出来ません。こういうシンプルな例ならいいのですが、引数がいくつかあるような関数で、いちいちこのe を付けて回るのはメンドいものです。こういうときに、上記のエミュレーションを用いれば、``document.onclick = function(){ //e が不要` alert("Event X:"+event.clientX+", Event Y:"+event.clientY);`} ``みたいに書けるわけです [署名/]|martin|1|1|||
[33] => 1149276075|開発日誌,JavaScript|Firefoxでテキストエリア内のマウスカーソルが最初に戻ってしまう件| こんばんは。何だか、[g]地球特捜隊ダイバスター[/g]みたいなタイトルになってしまいました。近頃は、もっぱらFirefoxで記事を書いていますが、この際に、ひとつ「何じゃ」と思っていたことがあります。記事作成画面で記事をしこしこ書いていて、時々、文字を強調したり文の整形のために、ppBlogツールバーから選んでタグを反映させますが、その際にFirefoxだと、テキストエリアにフォーカスを合わせると、マウスカーソルがテキストエリアの先頭に移動しちゃうんですよね。なので、その都度、また修正を加えた場所までスクロールさせないといけません。文章が長くなってくると、スクロール量も増えて大変。で、ブロッグは書いてなんぼ、こういう所でストレスを感じさせるようではイケナイ。というわけで、webを検索したらちゃんと便利な属性がありました。ちなみにIE エンジンならマウスカーソルは、修正を加えた箇所にとどまってくれるので問題ないです。`` テキストエリア内で垂直方向にスクロールした距離は、次のプロパティーで読み書き 可能です。``object.scrollTop ``なので、予め、この値を取得しておき、内容に修正を加えた後で、再度この値に設定すれば良いです。具体的には、ppBlogのlib.jsで言えば、``var s = ed.selectionStart;`var scrollTop = ed.scrollTop; `ed.value = ed.value.slice(0,s)+ed.value.slice(s).replace(caretSelection, string);`ed.setSelectionRange(eval(s+string.length), eval(s+string.length));`ed.scrollTop = scrollTop; `ed.focus(); ``とすれば良い。ホットピンクの部分が変更した箇所です。これで、Firefox でもストレスなく記事が書けます。``` 余談ですが、Firefoxの最新版は、1.5.0.4 で6月2日にバージョンアップしています。最近のFirefoxは、アップデートもほぼ全自動ですし、良い感じですね。最近、見つけたTipsは、「スムーズスクロールを使用する 」です。自分がIEエンジンから、なかなかFirefoxに乗り換えることが出来なかった最大要因が、何か皮を一枚被ったような「もっさり」感でした。どことなくスムーズでない。で、よくよく注意してみると、どうもスクロール時のぎこちなさが原因じゃないかと思いました。そういえば、設定項目にスクロールの項目があったような、と、「ツール」→「オプション」→「詳細」→「一般」の「ブラウズ」という項目にありました。デフォルトではオフになってるんですね(自分が以前に弄ったのかも知れないけど)。で、これをオンにすると、マウスホイールでのスクローリングのスムーズなこと!おぉー、これこれ、って感じでした[にこっ/] 皆さんも一度、試されては■|martin|1|1|JavaScript||
[34] => 1149066600|開発日誌,JavaScript|Firefoxでの擬似window.event|`
Follow-Up 2006-7-6
` Firefoxでもwindow.eventを有効にするやり方のエントリーは
こちら `
`` Gecko系のブラウザでは、これはNetscape4.x時代からずっとそうなんだけど、イベントは、イベントハンドラーにargument として渡されるので、この手の記述には必ず引数(ひきすう)を書く必要がある。一方、IEでは(Operaもか?)、イベントモデルには、最後に生じたイベントをキャプチャーするwindow.event という属性があり、これはどこからでも自由にアクセスすることが出来る。以上のことをまとめると、イベントに関してクロスブラウザーな記述をしようと思えば、例えば以下のような感じになる。``function getEventType(e){` var e = e || window.event;` alert("Event Type is:" + e.type);`} `` で、ここで引数に使用している"e " であるが、これはGecko系のためだけに必須である。window.event であれば、この引数は不要だ。で、自分は常々、この"e "を追放できないかなと思っていた。何しろ、イベントを絡ませるような関数に必ずついて回るからだ。スクリプトの記述はシンプルなのが良いのに、この"e " のために余計に引数が増えてしまう。`` ppBlogでは数箇所に、この"e " が出てくるので、こいつを追放してやろうと決心した。そして色々弄った結果、これならOKかなというのを見つけたのでメモしておく。もちろん、ppBlogにも反映される。`` まず、最初にやったことは、ppBlogのjs/lib.jsに記述してある、ページ内でのマウスイベントX座標をゲットする以下の関数において、Firefoxでイベントがどんな風に呼び出されるのかだ。```function getEventPageX(e){` var e = e ||window.event;` if(e.pageX){` return e.pageX;` } else if(e.clientX){` if(d.documentElement && typeof d.documentElement.scrollLeft != "undefined"){` return d.documentElement.scrollLeft + e.clientX;` } else if(d.body && typeof d.body.scrollLeft != "undefined"){` return d.body.scrollLeft + e.clientX;` }` }` return 0;`} `` 実行している関数自身は、arguments.callee で参照出来る。そして、この関数が何によって呼び出されているかは、caller プロパティーを参照すればいい。これらを使って、上の関数に、`` alert(arguments.callee.caller); ``を付け足してみると、Firefoxでは、```function onclick(event){` getEventPageX(event);`} ```というのが返ってくる。→デモ1。 ほほう。ちなみにIEでは```function anonymous()`{` getEventPageX(event);`} ``という匿名関数が返ってくる。Firefoxでは、引数のevent がonclick 関数によってキャプチャーされていることが分かる。じゃ、このonclick関数のevent って何だろう。これは次のようにして調べることが出来る。`` alert(arguments.callee.caller.arguments[0]); ``すると次の答えが返ってくる。→デモ2 ``[object MouseEvent] ``ほうほう。イベントは、マウスイベントオブジェクトとしてonclick関数に組み込まれているらしい。ならば、これを呼び出して、"e " に渡してやれば、"e " の束縛から逃れられるのではと推察する。なので次のようにして、"e " なしのやつを走らせてみる。→デモ3 ``function getEventPageX(){` var e = arguments.callee.caller.arguments[0] || window.event;` if(e.pageX){` return e.pageX;` } else if(e.clientX){` if(d.documentElement && typeof d.documentElement.scrollLeft != "undefined"){` return d.documentElement.scrollLeft + e.clientX;` } else if(d.body && typeof d.body.scrollLeft != "undefined"){` return d.body.scrollLeft + e.clientX;` }` }` return 0;`} `` おおぉー、上手く行った[にこっ/] 3つ目のデモでは、もはや引数のe は必要なく、getEventPageX()`だけで座標を取得していることに着目。`` こりゃいいやと、js/lib.js やjs/editor.js に出てくる引数"e "をとりあえず、全部なしにしてブロッグを見てみると、意図したように動かない。何でかなと、デバッグダイアログを見てみると、これまでのデモでは、マウスクリックによって直接getEventPageX()を呼び出していたのに対して、実際のスクリプトでは、getEventPageX()などは、別の関数の中で呼び出されている。なので、``arguments.callee.caller.arguments[0] ``だと、getEventPageX()を含んだ関数の第一引数を返してしまうのである。初歩的ミス。。それならば、``arguments.callee.caller ``を次々に呼び出していけば、そのうち、マウスイベントオブジェクトに到達するはずである。イメージ的には、sourceに向かって遡っていくイベントバブリングですね。これは次のような記述でいい。``var caller = arguments.callee.caller;`while(caller){` var ob = caller.arguments[0];` if(ob == '[object MouseEvent]') return ob;` caller = caller.caller; // 次の呼び出し元にセット`} ``ただ、ob == "[object MouseEvent]" というのは何となく「カッコ悪い」。この部分はコンストラクタを用いて次のようにも書ける。``ob.constructor == MouseEvent `` 以上をまとめて、IEでも使えるようなクロスな記述にすると以下のような感じ。``function windowEvent(){` if(window.event) return window.event;` var caller = arguments.callee.caller;` while(caller){` var ob = caller.arguments[0];` if(ob && ob.constructor == MouseEvent) return ob;` caller = caller.caller;` }` return null;`} `` windowEvent()がそのままIEのwindow.eventに相当する。これを用いればイベント関連の記述に必須であったe からめでたく解放される訳である。最初に出てきたgetEventPageX()関数は、最終的には次のように記述できる。```function getEventPageX(){` var e = windowEvent();` if(e.pageX){` return e.pageX;` } else if(e.clientX){` if(d.documentElement && typeof d.documentElement.scrollLeft != "undefined"){` return d.documentElement.scrollLeft + e.clientX;` } else if(d.body && typeof d.body.scrollLeft != "undefined"){` return d.body.scrollLeft + e.clientX;` }` }` return 0;`} `` もはやイベントのキャプチャーのためだけに引数は必要ない。最終的なデモをあげておきます。` →http://p2b.jp/demos/windowEvent.html` なお、イベント座標取得関数とは別に、イベントが起こった要素の座標を返す、getElementPosition()という関数も新たに定義しています。これは、JavaScriptマスターであるppk氏 のサイトにある"Find Position " という記事に紹介されている関数をmodifyしたやつです。こんなやつ。```function getElementPosition(){` var _x = _y = 0;` var ob = windowEvent().target || windowEvent().srcElement;` if(ob.offsetParent){` while (ob.offsetParent){` _x += ob.offsetLeft; _y += ob.offsetTop;` ob = ob.offsetParent;` }` }` return [_x, _y];`} `` とりあえず [署名/]|martin|1|1|||
)
ppBlog official
[ カテゴリー » 開発日誌 » JavaScript ]
エントリー 35 件中 1-5 件を
ボックス表示 ⇔
リスト表示
2010/8/25
カテゴリー » 開発日誌 »
JavaScript
innerHTML に比べると、outerHTML を使う頻度はぐっと少ない。現状、IE と Safari , Chrome , Opera が対応していて、未対応はFirefox だけ。使用頻度が低いメソッドに対して、Firefox向けに、長々しいスクリプトを書くのもどうかねぇ、と思ってググってみたところ、ナイスな投稿がありました 。
sOuterHTML = new XMLSerializer().serializeToString(oElement);
確かに、これは理にかなってますね。outerHTML自体は、値の取得だけでなくて、設定も出来るんだけど、そういう場面はまずないだろうから、このgetterだけで十分ですね。
<div id="Here">Hello, <strong>really strange</strong> World !</div>
対して、
innerHTML なら
Hello, <strong>really strange</strong> World !
が返されるし、
outerHTML なら、
<div id="Here">Hello, <strong>really strange</strong> World !</div>
が返される。実装例としては、
function getOuterHTML(e) {
if(e.outerHTML){ // IE, Webkit, Opera
return e.outerHTML;
} else { // Firefox (, Webkit, Opera)
return new XMLSerializer().serializeToString(e);
}
};
あるいは、短く
function getOuterHTML(e) {
return e.outerHTML || new XMLSerializer().serializeToString(e);
};
とか。
outerHTML
Hello, really strange World !
実際には、Firefoxだと xmlns="http://www.w3.org/1999/xhtml" という属性が付加されるので、気になるようなら適宜省けば良いでしょう。
JavaScript
— posted by martin at 01:25 am
Comment [0]
TrackBack [0]
2010/8/23
カテゴリー » 開発日誌 »
JavaScript
簡易画像ギャラリーのテスト。タイトルのような場合、ひとつの画像をクリックして、拡大表示にした際に、ポップアップした画面の右端に矢印が表示される。それをクリックすると、同じ記事に含まれる次の画像を読み込む仕様。
特に画像キャプションが付いている場合は、それも表示されるので、写真を主体に見せたい場合なんかには、好都合だと思います。
JavaScript
— posted by martin at 11:39 pm
Comment [0]
TrackBack [0]
2010/8/13
カテゴリー » 開発日誌 »
JavaScript
ppBlogのoParts.jsにtweenアニメーションみたいな動きを取り入れてみました。まだ試作段階ですが、テーマ:Basicであれば、左側のサイドバーの展開や、ソーシャルブックアイコンで、ちょっとした動きをみることが出来ます。
写真のポップアップにも。
JavaScript
— posted by martin at 11:31 pm
Comment [0]
TrackBack [0]
2010/8/8
カテゴリー » 開発日誌 »
JavaScript
こんばんは、martinです。タイトル通りなんですが、要は以下のようなことを簡単に 実装したい。
foo.await (2000).say("Hello, ").await (1500).say("world !");
まぁfoo という関数オブジェクトがあるとして、2秒後に「Hello, 」 を出力して、そこで小休止、で1.5秒後に「world !」 を出力すると。それをメソッドチェーンに拘ってやりたい。簡単な例を挙げてみます(うまく行かないサンプルです )。
var foo = function(){}; // 関数オブジェクトを作成
foo.prototype = { // プロトタイプを設定
say : function(s){
alert(s);
return this ;
},
await : function(ms){ // ここで遅延処理をゴニョゴニョしたい
// var _this = this;
// setTimeout(function(){
// return _this; # まぁ、これはダメだけど、こんな感じでやれたらいい
// }, ms);
return this ;
}
}
var bar = new foo();
bar.await(2000).say("Hello"); // すぐに「Hello」が出力される
メソッドチェーンを実装するとなると、return this; をかまして、用意した関数オブジェクト自身を返すようにする必要があります。上のawait関数内で、setTimeoutを用いて遅延処理をしようと思いましたが、どうもうまい方法が思いつかず、とあるサイトで質問してみました 。すると、数分後(!)に返答があって、「それ、jQueryで出来るよっ! 」と (そのコメントは今は消されたようなんですが) まぁ確かにそうなんですが(なのであえて.delay という単語を選んだ)何かすごい技があって、それで解決!みたいなことを期待していたわけです(jQueryを使わずに)。
2つ目に付いたコメントで、簡単に済ませる方法はなくて、何か別のキューシステム(待ち行列システム?)を用意する必要があるよ、と。やっぱりそうか。で、沢山ヒントを頂いたんで、自分なりに作ってみました。次のようなキューシステムを用意しました。
Queue = {
entries : [], // 関数を登録する配列を用意
inprocess : null,
enqueue : function(entry){ // 関数を登録する
Queue.entries.push(entry);
},
flush : function(){ // 配列に登録された関数群を一気に実行
if(Queue.inprocess) return; // 「待ち中」なら実行しない
while (Queue.entries.length){
var entry = Queue.entries.shift(); // 配列の頭から取り出す(つまり登録順)
if(entry.toString().indexOf('await:') !== -1){ // 「待ち」のマークを見つけたら
var ms = Number(entry.split(':')[1]);
Queue.inprocess = setTimeout(function(){
Queue.inprocess = null;
Queue.flush(); // 指定時間後に処理を再開
}, ms);
return;
}
entry(); // 取り出された関数を実行
}
}
}
こいつを最初のサンプルに適用すると以下のような感じになります。
var foo = function(){};
foo.prototype = {
say : function(s){
Queue.enqueue(function(){ // 配列に関数を登録
alert(s);
});
Queue.flush();
return this;
},
await : function(ms){
Queue.enqueue('await:' + ms); // 配列に「await:2000」みたいなマークを登録
return this;
}
}
自分のスキルではこの程度です キューシステムに頼らずに自己完結できる目から鱗な手法がないですかねぇ。
とりあえず動作サンプルを以下に。ボタンを押すと、
var bar = new foo();
bar.await(1500).say('Hello, ').await(2000).say('world!');
ってのが実行されます。ここでは、ボタンを押して1.5秒後に「Hello, 」、更に2秒後に「world !」とボタンの右側に表示されます。
遅延実行
何か、「こうしたらいいよ」とかのアドバイスあればよろしくお願いします。
JavaScript
1件のレスポンス [+]
— posted by martin at 08:29 am
Comment [0]
TrackBack [1]
2010/7/31
カテゴリー » 開発日誌 »
JavaScript
もう7月も終わりですね、martinです。相変わらず時の流れは速いもので。
このサイトのブログの基本テーマ(スキン)「Basic」では、試験的に
HTML5 を導入しています。HTML5では新しい要素が
いくつか追加 されていますが、
IE9 未満のブラウザでは、
article や
header ,
nav などのクールで新しい要素に対するスタイルシート指定が効かない、という事が昔から知られています。これに対するアプローチとして、
document.createElement を使うとスタイルシートでの指定が可能になるよ、というのがよく知られています。初出は
Sjoerd Visscher さんあたりでしょうかねぇ。
document.createElement(”article”); // 未知の要素articleに対してIEでもCSS指定が可能になる
このdocument.createElementを使うテクニックは、いろんなところで見かけることができて、有名どころでは、Remy sharp氏のhtml5.js あたりですね。
もっとも、このテクニックを使っただけでは、IEでHTML5化を効かせたページを印刷したときまでは反映されないので、これに対しては、IE Print Protector がよく知られていると思います(個人的には、印刷のサポートまではあまり興味がありませんが )。
で、このcreateElementを使うテクニックは、ベタに書けば以下のような感じになります。
// ここでは簡潔化のため、以下の6個の新要素に絞ってます。
<!--[if lt IE 9]>
var html5_elements = ["header", "nav", "article", "section", "aside", "footer"];
for (var i = 0, len = html5_elements.length; i < len; i++){
document.createElement(html5_elements[i]); // 各要素に適用
}
<![endif]-->
どこで最初に見かけたのか失念しましたが、これを一行(ワンライナー)で簡潔に済ませたスクリプトを見たときはいたく感心しました。
Dean Edwards氏のブログ もこうなっています。
"header,nav,article,section,aside,footer".replace(/¥w+/g, function(a){document.createElement(a)});
上のポイントは、replaceメソッドの引数に関数を指定できて、その関数内では、正規表現にマッチした要素を適宜適用していく点です。ループみたいなことを勝手にやってくれる点ですね。おそらくこれ以上短くは書けないのではないかと思うのですが、息抜きに自分なりにいくつか考えてみました。
"header,nav,article,section,aside,footer".split(',').sort(function(a){return document.createElement(a)*1});
これも、似たような発想からですね。自動でなんかやってくれるという。
with("header,nav,article,section,aside,footer".split(',')))while(length)document.createElement(pop());
ここでは
with 構文を使ってみました。これもなかなかシンプルです。同じくwithと、
Enumerator を使って、
with(new Enumerator("header,nav,article,section,aside,footer".split(',')))for(;!atEnd();moveNext())document.createElement(item());
これは、ちょっと長いし、
Enumerator Object はマイナーですかね。
短さ命で、グローバル変数の汚染なんて気にしないぜっ、という向きには以下のようなものもありかと思います。
s="header,nav,article,section,aside,footer".split(',');while(s[0])document.createElement(s.pop());
for 文関連では、
for(i in s="header,nav,article,section,aside,footer".split(','))document.createElement(s[i]);
とか
for(i=0;n="header,nav,article,section,aside,footer".split(',')[i++];document.createElement(n));
とかですかねぇ。
ちなみに、配列を作るのに、文字列にsplit() をかませるというのは、よく見かける手法です。ここでの、6個ぐらいの要素数ではあまり差は出ませんが、要素数が増えてくると、逐一ダブルクォート(or シングルクォート)で括っていくやり方と大きく差が付いてきます。
var html5_elements = ["header", "nav", "article", "section", "aside", "footer"]; // これより
var html5_elements = "header,nav,article,section,aside,footer".split(','); // こっちが記述が短い
正規表現から配列も出来ますね。
var html5_elements = "header,nav,article,section,aside,footer".match(/¥w+/g);
以上、トリビアルなエントリーでした
HTML5 JavaScript
1件のレスポンス [+]
— posted by martin at 12:18 pm
Comment [0]
TrackBack [1]
エントリー 35 件中 1-5 件を
ボックス表示 ⇔
リスト表示
Created in 0.0251 sec.
日 月 火 水 木 金 土
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Comments