ラベル CSS の投稿を表示しています。 すべての投稿を表示
ラベル CSS の投稿を表示しています。 すべての投稿を表示

2014年5月6日火曜日

固定幅のサイトにおける viewport の設定。iPhone で見た場合、横の隙間 (空白) をなくすには。

1. 横の隙間 (空白) をなくしたい

iPhone の画面に最適化されていないサイトを見ると、コンテンツの両脇に隙間 (空白) ができることがある。

例えば、Open Source Web Design - Three Quarters > Three Quarters は、スマートフォンに最適な表示がされない。iPhone で表示すると、左下の画像のように表示される。コンテンツの両脇に不要な空白ができてしまう。

これを右下の画像のように、できるだけコンテンツを表示するように、両脇の隙間を取り除きたい。

IMG_0112 IMG_0117

 

2. このテンプレートにおける HTML の構造

最初にこのテンプレートにおける HTML の構造を把握しておく。

コンテンツを囲む全体の幅は、body タグに対する CSS の指定で 850px の固定幅が指定されている。body タグの背景画像として、コンテンツ部分の白色と、影を含めた両脇に対応した画像が設定されている。

body タグ直下の div タグは 700px の横幅。影を除いた白色の部分の幅を測ると約 750px。

SnapCrab_No-1001

両脇に影のあるデザインは古めかしく、簡単な変更で済ますために、予め、デザインをフラットなものに変更した。そのためには、CSS ファイル (screen.css) の body タグに対する背景の画像を削除。そして、背景の色を白に指定。幅を 750px に変更した。

  • body タグに対する CSS の指定
    • background-image を削除
    • width: 750px;
    • background-color: #fff;

これにより、以下のような表示となる。

SnapCrab_No-1002

 

3. Viewport とは表示される領域のこと

iPhone では横幅 980px として表示される

iPhone でサイトを見ると、横に隙間ができる理由は、iPhone に搭載されたブラウザの仕様による。

iPhone ではサイトに何も指定がなければ、PC のブラウザで横幅 980px の領域が表示できるウィンドウの大きさにしたときと同じように見える。iPhone では、PC のブラウザのようにウィンドウの大きさを変更するという操作がないため、横幅が固定されている。

iPhone用にサイトの幅と画面の幅を合わせるには によると、

iPhone4やiPhone4Sは、解像度が横980pxなので、… PC用に作られたPCサイトを表示させると、… 読みにくい表示になるだけでなく、右横に隙間が表示されてしまいます。

たとえば、… 横幅が900pxで作成しているため、80px分の隙間が出来てしまっています。

上例のサイトを PC 上のブラウザで 980px の横幅にして見てみると、以下のように表示される。

SnapCrab_No-1003

PC のブラウザと iPhone での表示を拡大縮小して重ね合わせると、表示されている領域が一致することが確認できる。

SnapCrab_No-1003

 

viewport

このような表示に関する概念として覚えておく言葉は `Viewport’.

viewport とは、現在見ている矩形領域のこと。ブラウザで言えば、ウィンドウ内に表示されている領域に相当する。たとえるなら、覗き窓。

viewport – Wiktionary によると、

(computer graphics) A rectangular region representing the range or area currently being viewed.

iPhone では、画面の解像度と、表示される領域が異なることに注意すること。また、Android では事情が異なってくる。

スマートフォン時代のWebサイト制作 第2回 スマートフォン特有の表示環境を理解する | デベロッパーセンター

例えば、iPhoneではどうでしょうか。結論から言うと、iPhoneでは“何もしなければ”横幅980pxでウェブサイトが表示されます。iPhoneのスクリーンはデスクトップのスクリーンのようには大きくないのに、なぜそのようになるのか不思議に思うかもしれません。ここで理解しないといけないのが「Viewport(ビューポート)」です。…

Viewportとはブラウザーの画面表示領域のことです。もう少し分かりやすく言えば、デスクトップでいうところのブラウザーウィンドウ上の表示サイズです。スマートフォンではデスクトップのようなリサイズできるウィンドウの概念はないため、その機種のスクリーンサイズがブラウザーウィンドウのサイズとなります。この通り素直に考えれば、iPhoneの3.5インチスクリーンの横幅320pxに合わせてViewportも320pxとなると考えてしまうのですが、実際にはこれが980pxとなります。

なぜそのようになっているかというと、スマートフォンブラウザーはデスクトップ向けサイトを閲覧できるのが特徴です。そのため、既存のウェブサイトをちょうど画面に収まるようにするには、Viewportのサイズをデスクトップ相当にしておく必要があります。

ちなみにAndroidの場合は、その機種のスクリーン幅以上のコンテンツでも、スクリーンに収まるように表示されます。

 

4. meta タグの viewport で幅を指定

width=device-width

viewport を指定するには、meta タグを利用する。

これがスマートフォン向けサイトを作るときの viewport 設定3パターンだ - てっく煮ブログ によると、

「スマホ向けにデザインしているよ」と主張するには <head> タグの中に viewport を 1 行追加すればよい。

<meta name="viewport" content="width=device-width">

width=device-width というのは「デバイスの横幅で描画してください」という意味である。

一昔前は iPhone 前提で width=320px と指定すれば十分だったが、解像度がまちまちな Android やタブレット端末が登場したため、width=device-width と指定するのが新常識になった。

HTML5/ページ全般/meta要素 表示領域を設定する - TAG index Webサイト によると、

meta要素に name="viewport" を追加すると、文書の表示領域を設定することができます。…

content属性の値には、以下のプロパティを指定することができます。

width= 表示領域の幅 数値 ピクセル数 (200~10000 の範囲、初期値は 980)
device-width 端末画面の幅に合わせる

これより、viewport に

  • width=device-width

を指定した。その結果、iPhone でコンテンツが納まらなかった。横幅は、PC 上のブラウザで幅 640px を指定したときと同じようだ。

IMG_0119

 

width=[幅をピクセル数で指定]

これに対して、blog.鶯梭庵/links/たぶん、ほとんどの人は viewport meta タグの指定をまちがえてる によると、

先に結論を書いておく。適切な設定は、ページ幅が固定されているウェブページの場合、viewport meta タグを書かないか、

<meta name="viewport" content="width=(幅をピクセル数で指定)">

とする。ページ幅が可変(リキッドレイアウトとか RWD とか)のウェブページの場合、

<meta name="viewport" content="initial-scale=1.0">

とする。

そこで、iPhone に表示する領域として、body タグの幅を viewport の width に指定した。

<meta name="viewport" content="width=750">

これにより、iPhone でコンテンツが隙間なく納まるようになった。

IMG_0117

 

5. スマホサイトにおける viewport の設定に対する考え方

スマホサイトに対する設計には 2 つの考え方がある。

一つは全体を俯瞰し、必要に応じて拡大縮小するタイプ。もう一つは、ユーザによる拡大縮小操作を禁止し、スマホに特化した表示をすること。

それぞれ一長一短があるので、サイトの目的に合わせて最適化するのが良いようだ。

pxt | 考察:Appleはスマホサイトを作らない。 によると、

今まで気づかずにいたが、Appleはスマホ(スマートフォン)向けサイトを作っていない。それは何故か。…

Appleのウェブサイトの裏側をちょっとだけ覗いてみると、viewport に width=1024 が記述されている。これは、iPhoneの小さな画面でも1024px分の幅があるものと仮想してレンダリングされるようにする…

小さな画面でありながらページ上の構成要素の全体を一目で把握して、自分が必要な情報に、直感的なダブルタップの操作でフォーカスできる。興味の順に次々読み進められるし、斜め読みだってできてしまう。

これが、Appleが掲げたモバイルサイトのデザインであり、Mobile Safari のUI設計の本質なのではないだろうか。

スマートフォン時代のWebサイト制作 第2回 スマートフォン特有の表示環境を理解する | デベロッパーセンター によると、

またmeta viewportでは、他にもユーザによる拡大/縮小操作の倍率を指定したり、拡大/縮小操作自体を禁止することもできます。Yahoo! JAPANやlivedoorなどのサイトでは、下記のようにユーザによる拡大/縮小操作ができないようにしています。

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0">

ケースバイケースではありますが、これらはユーザによる任意の操作を禁止することになるので、筆者としてはあまり好ましい指定と考えてはいません。「ユーザにも親切である」という意図がなければ、この制限のある指定は避けるようにしましょう。

 

参考サイト

2011年5月2日月曜日

古いバージョンの Google ドキュメントに表示される「ドキュメントを最新バージョンの Google ドキュメントにアップグレードしてください。」を消す - Stylish で CSS を設定

1. 古いバージョンの Google ドキュメントを開くと、アップグレードを促される

古いバージョンで作成した Google ドキュメントを開くと、以下のメッセージが表示される。

ドキュメントを最新バージョンの Google ドキュメントにアップグレードしてください。

12-09-20111

以前は、もう少し穏やかな表現だった。

このドキュメントを最新バージョンのエディタで表示しますか?

CropperCapture[168]

もう、何が何でも、最新版を使わせたい意気込みを感じる。 ^^;

 

2. 最新のバージョンで、なくなってしまった機能

最新のバージョンは、他人と共有するときレスポンスが良いので便利。

しかし、以下の点が不満。

  • 段落スタイルに「引用文」がない
  • HTML, CSS の編集ができない

(cf. ドキュメントを Google ドキュメントの新バージョンにアップグレードする)

 

3. 古いバージョンを使う理由

以前の Google ドキュメントは、高機能な HTML エディタという感じだった。

自分が Docs を使う目的は、ブラウズしながら個人的なメモを取ること。毎日つける、「日誌」のためのテンプレートとして、

を、今でも利用している。

最新の Google ドキュメントは、一般的なエディタに進化した。MS Word, Libre Office のような、一般的な文書作成ソフトに近くなったことは、良いことだと思う。しかし、Web ページをクリップする目的として使うには、HTML に特化したエディタの方が使いやすい。なぜなら、得た情報を、後でブログとして出力する場合、HTML だとそのまま流用できるから。

 

4. Stylish でメッセージを消す

上記の理由のため、以前のバージョンを、これからも使い続けたい。

その際、表示されるメッセージは邪魔なので、

を利用して、メッセージを表示しないようにした。

Stylish をインストール後、以下のサイトより、CSS をインストール。

(更新日: 2011.12.9)

2010年8月21日土曜日

Firefox, Google Chrome で USTREAM のチャットをブロック

USTREAM のチャットは IRC を利用している

Ustream の配信を見ていると、映像の右横に Twitter の投稿とリアルタイムのチャットが表示されることがある。

このチャットの仕組みは「Ustreamチャット - UstreamまとめWiki」によると、

# 他の人のチャンネルを見ると(...)、FlashでIRCチャットに参加できます。

上記 IRC というのは、
正式名称はインターネット・リレー・チャット(Internet Relay Chat)です。世界中に張られたIRCサーバのネットワークを利用し、世界中の人々とリアルタイムで会話ができるようになっています。

... IRCサーバは互いにリンクされ、広大なIRCネットワークを形成しています。

ユーザーはこのサーバにつなぐためのソフト(...)を使用し て、IRCサーバに接続します。

(IRC普及委員会/IRCのしくみ より)

 

LimeChat の設定

サーバに接続するためのクライアントとして定番なのが LimeChat のようだ。

サーバの接続設定は「IRCクライアントでUstreamチャット - UstreamまとめWiki」を参考にした。

「サーバ」→「サーバを追加」 から、UstreamのIRCサーバを追加します。

  • 「ホスト名」は、chat1.ustream.tv
  • 接続用の「ポート番号」は、6667
  • 「ログイン名」と「パスワード」は、Ustreamに登録したログインID・パスを使用(コレで管理者権限がGETできます)
  • 私のように表示名を変えたい人は、「ニックネーム」を変えればOK

 

USTREAM のチャンネルと IRC のチャンネル

サーバに接続したら、次に Ustream の配信画面にあるチャットに接続してみる。

IRC ではサーバの中に「チャンネル」が存在している。このチャンネルと Ustream の各「チャンネル」が対応しているとのこと。

Ustreamでは、showsのページアドレス(http://www.ustream.tv/channel/*****)のうち最後の、*****の前に#を付ける事で、それがIRCの「チャンネル」に相当します。つまり#*****です。

(びるのブログ 今更ながら・・・ より)

上記の説明に従いチャンネルを追加。

 

ユーザ情報の表示

チャンネルに入ると、そこへ接続しているユーザ名が表示される。

ircの危険性 によると、

まず何が一番気になることかというと、IRCの特徴としてIPアドレスやホスト名が表示されるというところにあります。

確かに特定のユーザを右クリック > 「ユーザ情報」 を見るとホスト名 が表示された。これではブラウザで匿名接続したとしても、場合によっては半ば識別子としての価値を持ってしまう。

 

CSS でチャットを表示させない

これを避けるにはチャットに接続しないようにすれば良い。

ブラウザ上でチャット画面を表示しなければ IRC に接続されないようなので、この要素を CSS でブロックする。

Firefox, Google Chrome を使っている場合、アドオン Stylish をそれぞれ予めインストールしておく。

その後、以下の install with Stylish ボタンでインストール。

http://userstyles.org/styles/35330

USTREAM - Block IRC | userstyles.org via kwout

2010年8月12日木曜日

USTREAM のダッシュボードにおける配信動画のサイズを大きくする

USTREAM のダッシュボードに表示される動画のサイズは小さい。これをスタイルシートで以下のように大きくする。

image2826-2

Firefox, Google Chrome を使っている場合、アドオン Stylish をそれぞれ予めインストールしておく。

その後、以下の install with Stylish ボタンでインストール。

2010年6月28日月曜日

Firebug で要素をマウスオーバーときの CSS を調べる

1. 要素をマウスオーバーしたときのスタイルは :hover で指定

Web サイトにおいて、要素をマウスオーバーしたときに見かけを変化させたい場合、CSS の

:hover

を利用する。

hover については Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification の 5.11.3 The dynamic pseudo-classes: :hover, :active, and :focus を参照。

The :hover pseudo-class applies while the user designates an element (with some pointing device), but does not activate it. For example, a visual user agent could apply this pseudo-class when the cursor (mouse pointer) hovers over a box generated by the element.  

 

2. Firebug で :hover を指定する

:hover の指定をブラウザ上で Firebug を用いて微調整し、確認するにはどうすればいいのだろう?

意外と知られていない 機能が多い!?Firebugの使い方 | THE HAM MEDI によると、

「:hover」の擬似クラスをつけた要素を、実際にマウスが乗った状態にしてくれます。

要素をマウスオーバーして見かけが変化した後にマウスをさっと下に動かして、必死に :hover のスタイルを変えようとしたけど「できねぇ~」と諦めてた。 ^^;

 

3. :hover を指定する例

例えば アップル サイトにおけるトップのメニュー `iPhone’ の :hover を有効にするには、

  1. Firebug を起動する。
  2. `iPhone’ のメニュー要素に対応した li 要素内の a 要素を選択
  3. 右側のペインにおける スタイル で :hover を選択

これで :hover に設定されている指定を変更することができた。

SnapCrab_No-0287

 

4. その他

上記解説サイトに書かれていた Firebug 内にコメントを表示できるのも知らなかったなぁ。

2010年5月31日月曜日

CSS で画像と文字を揃える

1. リストのマーカーとして画像を設定する

HTML でリストを書くには、ul, li 要素を用いる。

<ul id="list0">
	<li>hoge</li>
	<li>piyo</li>
	<li>fuga</li>
</ul>

このリストの項目の前に、マーカーとして画像star(star.png) を付けるには、CSS で指定する。

#list0 { 
	list-style-image: url("star.png"); 
	list-style-position: inside;
} 

img05-30-2010[1]

これにより、右図のように表示される。

 

リスト項目の背景画像を設定

マーカーの中心に文字の位置を合わせるには、マーカーをリスト項目の背景画像として設定する。

#list0 li { 
	list-style: none;
	background: url("star.png") no-repeat top left;
	padding: 15px 0px 15px 60px;
} 

img05-30-2010[2]

文字が真ん中に位置するように padding で上下の間隔を調整する。

 

2. 画像と文字を揃える

a. 要素を垂直方向に対して、中央で揃える

画像と文字の位置を、垂直方向に真ん中で揃えたい場合は vertical-align を使う。

<img src="star.png" style="vertical-align:middle;" />
<span >hoge piyo fuga</span>

 img05-30-2010[4]

 

b. 要素を水平方向に対して、中央に配置

水平方向に対して、要素を中央に配置したい場合は text-align を用いる。

text-align が適用される対象は、

Applies to: block-level elements, table cells and inline blocks

(16.2 Alignment: the 'text-align' property より)

div 要素の子要素として画像を配置。

<div style="text-align:center;">
  <img src="star.png" />
</div>

または、display と 要素をセンタリング するための margin の設定を併用し、

<img src="star.png" style="display:block; text-align:center; margin:auto;" />

 

c. text-align と vertical-align

しかし、不思議なのは要素を中央に配置するの指定が

text-align

であって、なぜ horizontal-align ではないのだろう?

vertical-align

と意味的に対になるなら horizontal-align の方が忘れにくいのに。

CSS1

仕様を見ると Cascading Style Sheets, level 1 では、Text properties 節に各々含まれている。

CSS2.1

Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification では、別々の節に分かれて説明されている。

 

参考サイト

2009年12月21日月曜日

Firefox のアドオン Stylish の設定ファイルを Dropbox へ移動

1. Stylish の設定ファイルを Drpobox へ置く

よく見るサイトの表示をカスタマイズするために Stylish を使う。

Firefox のプロファイルが壊れたときや、PC が起動できなくなったときのために、Stylish の設定ファイルを Dropbox に設定ファイルを置くと良い。

 

2. 設定ファイルを about:config で指定する場合

Stylish の設定ファイルの位置は、

  • extensions.stylish.dbFile

で指定する。Stylish のバージョン履歴 :: Add-ons for Firefox によると、

… a new preference extensions.stylish.dbFile that lets you choose where your styles are stored. (太字は引用者による)

以前とは設定名の値が異なる。設定ファイルの位置を変更した場合に変更するパラメータも変更されている。

 

a. Stylish の設定ファイルを移動

予め Stylish の設定ファイルを確認しておく。ファイルがある位置は、

  • C:\Documents and Settings\ユーザ名\Application Data\Mozilla\Firefox\Profiles\ランダムな文字列.プロファイル名’ の `stylish.sqlite’

上記の設定ファイルを Dropbox 上の適当なフォルダに移動する。

 

b. Firefox による設定
  1. Firefox のロケーションバーに about:config と入力。
  2. フィルタに extensions.stylish.dbFile と入力。
  3. 抽出された行をダブルクリックし、Dropbox 上の stylish.sqlite へのパスを文字列で入力する。
  4. Firefox を再起動する。

パスの指定は、Windows のパス形式で入力すればよい。 以前のバージョンでは fileURL で入力する必要があった。

 

3. シンボリックリンクで指定する場合

追記(2014/12/25): シンボリックリンクを使った方がより簡単に設定できる。

  1. 「Stylish の設定ファイルを移動」
  2. `stylish.sqlite’ があった元の位置から、移動先のファイルへシンボリックリンクを張る

 

関連記事

2009年12月14日月曜日

Windows Live Writer で編集中のブログテーマ (テンプレート) を CSS で カスタマイズ

1. ブログを書くとき、実際のデザインと同じ状態を表示させたい

ブログを書くときは、できるだけ実際に表示される状態を見ながら書きたい。

Windows Live Writer は、編集モードにおいて、

  • メニューより「表示 > 使用中のテーマを編集」

にチェックを入れると、実際のブログで使用している CSS に基いて表示される。

(ショートカートキー: Ctrl + F11 により、チェック状態をトグルできる。)

この機能を利用するには、予めブログのテーマをダウンロードする必要がある。また、ブログのテーマを変更するごとに再設定が必要。

ブログ テーマを更新

メニューより「ブログ > ブログ設定の編集」を選択。

091214-014.png

「編集」のブログテーマにおいて、「テーマの更新」ボタンを押す。

091214-013.png

 

2. ブログテーマの表示をカスタマイズ

ブログのデザインによっては、ブログのテーマで表示させると見にくくなる。

自分のブログは、左にサイドバーを配置し、メインコンテンツは右という構成。

091214-016.png

この場合、Windows Live Write でブログのテーマの更新をすると、サイドバーが表示される。ブログを書くとき、この領域は不必要。サイドバーが表示されないようにしたい。

091214-015.png

ブログ テーマの保存場所

ブログのテーマは、以下のディレクトリに保存される。

Windows XP の場合

  • C:\Documents and Settings\ユーザ名\Application Data\Windows Live Writer\blogtemplates

Windows Vista の場合

  • C:\Users\ユーザ名\AppData\Roaming\Windows Live Writer\blogtemplates\ランダムな文字列\blogtemplates

Windows 7 の場合

  • C:\Users\ユーザ名\AppData\Roaming\Windows Live Writer\blogtemplates\ランダムな文字列

blogtemplates 以下にあるフォルダの名称は、ランダムな文字列。

何度かブログテーマの更新を行った場合、複数のフォルダが保存される。フォルダの更新日時を見て、最新のものを選ぶ必要がある。フォルダ中から、表示に対応している CSS の記述を探し、カスタマイズして保存する。

 

CSS をカスタマイズ

予め、実際のブログを見ながら、Firebug を使い、サイドバーの辺りを

  • 右クリック > 「要素を調査」

して、CSS を確認しておく。

自分の場合、index.html ファイルにおける CSS の記述中の

  • div#main

の以下の属性を削除することによって、サイドバーを消すことができた。

float:right;
width:66%;

2009年11月11日水曜日

Google Docs で 段組

追記 (2010.6.16) : 段組のドキュメントをテンプレートギャラリーに追加した。

訂正 (2010.6.18) : 上記の段組は文字を入力している途中で段組が崩れてしまうことがあるので、表による段組も作成。

 

 

HTML の編集

メニューより 「編集 > HTML を編集」

091111-009.png

以下を貼り付けて、「アップデート」ボタンを押す。

<div id="menu">
メニュー
</div>

<div id="content">
内容
</div>
091111-006.png

 

CSS の編集

メニューより 「編集 > CSS を編集」

091111-010.png

以下を貼り付けて、「OK」ボタンを押す。

#menu {
  float: left;
  width: 25%;
}
#content {
  float: left;
  width: 75%;
}

width で幅を指定。上記は比率で指定場合。

091111-011.png

 

これで以下のように表示される。

091111-016.png

 

改行について

Google Chrome

Shift + Enter で、段組の中で改行がされるようだ。Enter キーだけでは、段組が崩れる。

 

Firefox

Enter で改行される。

 

IE8

意外なことに、段組の操作に関しては IE8 が扱いやすい。

適当に入力した文字の辺りをクリックすると、ハンドルが表示される。これで枠の大きさを調整もできる。

091111-013.png

 

ダブルクリックすると文字の入力ができるようになる。枠が表示されたままなので、入力に戸惑うことがない。

091111-015.png

 

参考

2009年9月26日土曜日

JavaScript で半透明な固定した領域を生成する

1. CSS で要素を固定し、半透明にする

JavaScript で半透明な固定した領域を生成したい。

要素の位置を固定するには、HTML において、CSS の position プロパティを fixed にする。

絶対的な位置に固定配置する によると、

「position:fixed」と距離を指定するプロパティは、指定された要素を絶対的に移動させて配置しますが、その要素はウィンドウ上のその位置に固定されて、スクロールしても位置が変わらなくなります

要素を半透明にするには、CSS で filter プロパティを設定する。

IE・Firefox・Opera・Netscape・Safariで表示可能な半透明CSS/Opacityテクニック - WEBデザイン BLOG によると、

以下のコードでIE・Firefox・Opera・Netscape・Safariで同等表示がえられます。

  filter: alpha(opacity=25);
  -moz-opacity:0.25;
  opacity:0.25;

 

2. JavaScript で要素を生成し、CSS のプロパティを設定する

JavaScript で DOM Element を生成し、CSS を設定する。

DOM Element を生成する方法は、以下を参照。

CSS を設定するには、element.style を利用する。

var newElem = document.createElement("div");
var p = document.body.firstChild;    // この要素の前に newElem を挿入 
newElem.innerHTML = "挿入";
p.parentNode.insertBefore(newElem, p);

with(newElem.style){
    // 大きさ
    width = "100px";
    height = "100px";
    // 位置
    position = "fixed";
    top = "30px";
    left = "30px";
    // 背景の色
    backgroundColor = "red";
    // 透過度
    opacity = "0.7";
}

 

関連記事

JavaScript で CSS の float 属性を設定する

1. DOM Element を右に寄せたい

DOM Element を生成し、右に寄せたい。

そのために、element.style.float を設定したが、要素を右に寄せることができなかった。 (+_+)

var newElem = document.createElement("div");
var p = document.body.firstChild;    // この要素の前に newElem を挿入 
newElem.innerHTML = "挿入";
p.parentNode.insertBefore(newElem, p);

newElem.style.float = "right";

 

2. float ではなく cssFloat 属性を設定する

右寄せをするには、element.sytle.cssFloat 属性を利用する。

UchのX40記: [JavaScript]DOM Elementのstyle属性のfloat (via floatが動かない - くれすの開発日記〔JavaScript編〕 - g:javascript) によると、

IEやと

(DOM Element).style.styleFloat = "left";

Firefoxやと
(DOM Element).style.cssFloat = "left";


OperaやとどっちでもOK。

先ほどの右寄せを設定しているコードを、以下のように変更。

newElem.style.cssFloat = "right";

なぜ Float の前に css と接頭辞があるのだろう?

 

3. DOM の仕様

DOM の仕様を確認してみる。

Document Object Model – Wikipedia によると、

  • Document Object Model (DOM) Level 2 Style Specification
  • Document Object Model Style Sheets の Index で `float’ と検索したら、確かに cssFloat となっていた。

    cssFloat of type DOMString
    See the float property definition in CSS2.

    2008年11月23日日曜日

    Stylish で特定の文字列を含む「属性」を持つ要素を消す - CSS における属性を利用した要素の特定

    「Stylish で特定の要素を消す」の続き。

    1. 要素の属性の値により指定したい

    前回 Stylish で作成したスタイルに、新しくスタイルを追加する。

    スタイルを新たに追加する方法は、

    1. Stylish のアイコンをクリックして、
    2. 前回作成したスタイルを右クリック。

    (cf. Greasemonkey, Stylish で既存のスクリプトを素早く編集)

    今回は、

    a 要素の href 属性に `new/main’ が含まれる a 要素を消したい。

    とする。

     

    2. *=, ^=, $= によって属性を指定

    広告をブロックするための、Stylish のスタイル 

    を見ると、以下の記述がされている。

    [alt*="Advertising"]

    この書き方は Selectors によると、

    E[foo*="bar"]
    an E element whose "foo" attribute value contains the substring "bar"

    要素 E の内、属性 foo の値として、bar を含むものを対象とすることができる。

    つまり、上例の意味は、

    alt 属性に Advertising という文字列が含まれている要素

    を指定していることになる。

     

    要素の属性に対する基本な指定方法

    基本的な CSS による、要素の特定の仕方は、以下のものがある。

    E[foo]
    an E element with a "foo" attribute

    要素 E の内、foo 属性を持つもの。

    E[foo="bar"]
    an E element whose "foo" attribute value is exactly equal to "bar"

    要素 E の内、属性 foo の値が、bar であるもの。

     

    要素の属性の値の一部に一致するか検査する

    基本的な書き方以外に、正規表現の書き方に似た方法もある。

    E[foo^="bar"]
    an E element whose "foo" attribute value begins exactly with the string "bar"

    要素 E の内、foo 属性の値の先頭が bar ではじまるもの。

    E[foo$="bar"]
    an E element whose "foo" attribute value ends exactly with the string "bar"

    要素 E の内、foo 属性が bar で終わるもの。

    E[foo*="bar"]
    an E element whose "foo" attribute value contains the substring "bar"

    要素E の内、foo 属性の値に bar を含むもの。

    よって、今回は以下のようにして指定することができる。

     a[href*="news/main"] {display: none !important;}

     

    3. 参考

    4. 関連記事

    Stylish で特定の要素を消す - CSS による要素の指定

    1. Stylish で特定の要素を消したい

    Firefox のアドオン Stylish を利用すると、ウェブサイトの表示をカスタマイズできる。

    Stylish を使い、ウェブサイトの特定の要素を消したい。

    例えば、NIKKEI NET に表示される中の「注目のニュース・特集」の部分を消したいとする。

     

    2. Firebug で HTML の要素を特定する

    最初に、アドオン Firebug を使い、消したい部分を特定する。

    方法は、

    1. 消したい部分で右クリック
    2. 「要素を調査」を選択。

    これにより、Firebug の HTML要素を調査するためのペインが、ウィンドウ下部に表示される。

    消したい範囲の要素を特定するために、Firebug の中で HTML 要素を選択。選択すると、HTML の対応するウェブサイトの要素が青色で示される。

    081123-001

    例の場合、「注目のニュース」というタイトルの部分と、その「一覧」に対して、要素と属性が指定されている。

    • div --- id : news-P_M
      • div --- class : main-ctn cfix ---------- タイトル
      • table --- id : pickupNews-Box ---------- 一覧

    つまり、この要素に対しては、class と id によって要素を指定することができる。

    CSS による、要素の指定方法について、詳しくは以下を参照。

     

    3. Stylish に CSS を記述

    Firebug で消したい対象が特定できたので、Stylish の記述を行う。

    1. Firebug で特定した、要素の属性 id または class を控えておく。
    2. Stylish のアイコンを右クリック > 「スタイルを書く > www.nikkei.co.jp 用」を選択。
    3. 「スタイルを追加」ウィンドウが表示されるので、以下を記述して保存。
    #news-P_M > .main-ctn, #pickupNews-Box {display: none !important;}

     

    display で要素を消す

    CSS で、特定の要素を消すためには、

    {display: none !important;}

    none という指定は、The 'display' property によると、

    none
    This value causes an element to generate no boxes in the formatting structure (i.e., the element has no effect on layout).

    ( cf. 6.4.2 !important rules )

     

    CSS の要素を組み合わせて指定する

    id と class については「クラス、idによる定義の局所化」を参照。

    例において、「一覧」の方は id で指定されていた。よって、同一ページの中で pickupNews-Box と id が付けられている要素はない。id に対しては、接頭辞に `#’ をつけて指定する。

    「注目のニュース」というタイトルの部分は、class で指定されていた。よって、同一ページの中で、他にも main-ctn という class を付けることができる。

    main-ctn の親要素を調べたら、id 属性が付けられていた。よって、要素の親子関係を利用して、CSS を指定する。

    Selectors W3C Working Draft 15 December 2005 の 2. Selectors を参考にした。

    E F
    an F element descendant of an E element

    E > F
    an F element child of an E element

    E + F
    an F element immediately preceded by an E element

    E ~ F
    an F element preceded by an E element

    (Selectors より)

     

    関連記事

    2008年11月16日日曜日

    Stylish で特定の URL ではじまるサイトのスタイルを書く

    1. 特定の URL で始まるサイトを対象とする

    Stylish では、特定の URL, ドメインを対象としたスタイルを書くことができる。では、特定の URL で始まるサイトを対象にしたスタイルを定義したい場合、どのように指定すればいいのだろう?

     

    サイト規則で指定

    ステータスバーの  Stylish のアイコン 081116-001 を

    • 右クリック > 「スタイルを書く > 空のスタイル」

    を選択。

    「スタイルを追加」ダイアログが表示されるので、「挿入」セレクトボックスから「サイト規則」を選択。

    081116-003

    「サイト指定」ダイアログが表示されるので、ラジオボタンの「前に付く」を選択し、フィールドに URL を記入する。

    例えば、Google-Maps-API-Japan | Google グループ のスタイルを変更したいなら、

    をフィールドに入力し、追加ボタンを押す。

    081116-004

    「スタイルを追加」ダイアログに文字列が挿入されるので、スタイルを { } の中に書く。

    081116-005

     

    2. @-moz-document

    `@-moz-ducument’ の `-moz’ は拡張仕様を表す。

    Mozilla 拡張仕様 – MDC によると、

    Mozilla は '-moz' ではじまる多くの CSS の拡張仕様をサポートしています。

    @-moz-document の意味は、@-moz-document – MDC によると、

    @-moz-document 規則は、ドキュメントの URL 上にあるスタイルのスタイル規則を制限する、Gecko 仕様の @ 規則です。これはユーザスタイルシートで最初に記述されます。

    例 によると、Stylish が挿入した文字列

    `url-prefix’

    が、指定した URL で始まるページに対してスタイルを適用することを表す。

    @-moz-document url(http://www.w3.org/),
                   url-prefix(http://www.w3.org/Style/),
                   domain(mozilla.org)

    2008年9月6日土曜日

    Google App Engine でダイエット表の作成 (4) - 完成 o(^^)o

    追記(2008.9.6) : ダイエット表を作成するには → http://4diet.appspot.com/


    「Google App Engine でダイエット表の作成 (3)」の続き。前回は、実際にダイエット表を作成するクラスである DietTable を作成した。今回は、これをテンプレートから呼出し、ダイエット表を生成する部分、そして最初の設定画面、画面遷移を扱うコントローラ  ( webapp.RequestHandler のサブクラス ) を作成して完成。実際には、この部分から作りはじめているのだけれど ^^;

     

    ダイエット表を生成するテンプレート

    まずは、DietTable クラスをテンプレートで呼出している部分。シンプルに、

    {{dietTable.toHtml}}

    としているだけ。そして、このテンプレートにおいて、 DietTable クラスで設定した CSS のクラス指定に対して、CSS を設定している。 CSS における背景の色をプリンタで印刷する場合、デフォルトでは印刷されないのでブラウザの設定が必要。 (cf. Firefox, IE で背景色も印刷する )

     

    diet_table.html

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional/EN">
    <html>
      <head>
        <title>ダイエット表</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
        <!-- Diet.py の Diet.DietTable#_addCssToHtmlTable() で設定したものに対応 -->
        <style type="text/css">
        <!--
        table {
            border-collapse: collapse;
            width: 175mm;
            height: 240mm;
            border-color: #000;
        }
        table, td {
            border: solid 1px;
            text-align: center;
        }
        td {
            height: 4mm;
            font-size: x-small;
        }
        
        .sunday{ background-color: #FFEEFD; }
        .saturday{ background-color: #EEF2FF; }
    
        .x_axis{ border-top: solid 3px; width:2em;}
        .y_axis{ border-right: solid 3px; }
    
        .x_axis_05 { border-top: dotted 2px; width:2em;}
        .x_axis_1 { border-top: solid 2px; width:2em;}
        .y_axis_10 { border-right: solid 2px; }
        .y_axis_05 { border-right: dotted 2px; }
        
        .target , .weightNow { background-color: #ffc; }
    
        h1 {
            color: #000;
            text-align: center;
        }
        
        .forPrint {
            text-align: center;
        }
    
        @media print {
            .forPrint {
                display: none;
            }
        }
        -->
        </style>
      </head>
      <body>
    
        <p class="forPrint">表の中の色を印刷するには
            <a href="http://jutememo.blogspot.com/2008/08/firefox_23.html#ie">
            こちら</a>の説明を参考にしてください。
            (Firefox の場合は
            <a href="http://jutememo.blogspot.com/2008/08/firefox_23.html#firefox">
            こちら</a>。)
        </p>
    
        <h1>{{dietTable.year}} / {{dietTable.month}}</h1>
        
        {{dietTable.toHtml}}
        
      </body>
    </html>

     

    最初に表示される設定画面

    上記のダイエット表を出力するページや設定画面は、当然ながら最初にデザインを無視して作っている。ここでやっと設定画面をデザインすることにした。デザインするのに使ったのは Inkscape 。絵心はないので適当にシンプルなものを作った。 ^^;  以前に少しだけ使ったときのことを参考に。

    とりあえず、何かスリムな感じがするようなイメージを… (@_@;)

    080906-002

    絵が描けたら、今度はフォームを送信する部分を、表示内容に応じて長さが変化するように設定しなければならない。入力に不備があった場合、エラーメッセージを表示するためだ。作成ボタンを含んでいる影のある四角の部分を切取り、 Gimp で縁を拡大して、CSS で指定して伸縮できるように画像を抜き出した。 (cf. CSS と JavaScript で伸縮する領域を作成 )

    HTML のファイルは、フォームの部分だけ抽出して別ファイルとした。

     

    ソースコード

     

    setting_page.html

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional/EN">
    <html>
      <head>
        <title>表の設定ページ | 計るだけダイエット</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
       
        <script type="text/javascript">
        <!--
        /* このページが読み込まれたときに呼出される関数 */
        function setup(){
            resizeContent();
        }
        /* ウィンドウをリサイズしたときに呼出される関数
         * id が topLeft の要素の幅を広げる
         */
        function resizeContent(){
            var topRightDiv = document.getElementById("topRightCorner");
            contentDiv  = document.getElementById("topLeft");
            contentDiv.style.width = topRightDiv.offsetWidth  + 11 + "px";
        }
        /* 入力された「現在の体重」に応じて「目標体重」を設定する */
        function setTargetValue(){
            var weight = parseFloat(document.forms["createTable"].weight.value)
            if (!isNaN(weight)){
                document.forms["createTable"].target.value = weight - getTargetValue();
            }
        }
        /* 設定できる目標体重の上限の 3/4 の値を返す */
        function getTargetValue(){
            return parseFloat(
                document.getElementById(
                    "intervalLimit").firstChild.nodeValue) * 3 / 4
        }
        //-->
        </script>
    
        <style type="text/css">
        <!--
        body{
            background: #ffffff url("/img/title.png") top center no-repeat;
        }
        h1{ display: none; }
        .error{
            color: red;
            margin-bottom: 10px;
        }
        #content {
            margin: 300px auto 0px;
            text-align: center;
        }
        #input {
            margin: 0px auto;
            width: 450px;
        }
        #input table{
            text-align: left;
            margin: 0px auto;
        }
        .small { font-size: small }
    
        #createBtn{
            background: url('/img/createBtn.png');
            width: 338px;
            height: 114px;
            border: none;
            cursor: pointer;
        }
        #createBtn:hover{
            background: url('/img/createBtnActivated.png');
            border: none;
        }
    
        /*---------------------------------------------------------------
         * 影
         */
        /* 右側の縦の影のライン */
        #rightLine{
            position: relative;
            background: transparent url("/img/rightLine.png") right repeat-y;
        }
        /* 右上の角の影 */
        #topRightCorner{
            position: relative;
            background: transparent url("/img/topRightCorner.png") top right no-repeat;
        }
        /* 右下の角の影 */
        #bottomRightCorner{
            position: relative;
            top: 6px;
            background: transparent url("/img/bottomRightCorner.png") bottom right no-repeat;
        }
        /* 下の横の影のライン */
        #bottomLine{
            position: relative;
            left: -11px;
            background: transparent url("/img/bottomLine.png") bottom repeat-x;
        }
        /* 左下の角の影 */
        #bottomLeftCorner{
            position: relative;
            left: -11px;
            background: transparent url("/img/bottomLeftCorner.png") bottom left no-repeat;
        }
        /* 背景となる色を指定する領域。
         * リサイズにより、JavaScript でこの領域の幅を変更する。
         */
        #topLeft{
            position: relative;
            background-color: #fff;
            top: -6px;
        }
        /* 文字を表示する領域 */
        #content2{
            position: relative;
            padding: 10px 0px;
        }
        
        /* --------------------------- */
        #bottom_ad{
            text-align: center;
            margin: 30px auto 0px;
        }
        
        -->
        </style>
    
      </head>
      <body onresize="resizeContent()" onload="setup()">
        
        <h1>計るだけダイエットのための表を作成</h1>
        
        <div id="content">
            <p>以下の項目を半角数字で入力してください。</p>
            <!-- ダイエットの情報を設定したときのエラー表示 -->
    
            <div id="input">
            <div id="rightLine">
                <div id="topRightCorner">
                    <div id="bottomRightCorner">
                        <div id="bottomLine">
                            <div id="bottomLeftCorner">
                                <div id="topLeft">
                                    <div id="content2">
    
                                        {%include "setting_form.htm" %}
                                    
                                    </div><!-- end content -->
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            </div><!-- end input -->
    
        </div><!-- end content -->
        
      </body>
    </html>

    現在の体重を入力すると、設定できる上限の 3/4 の値が「目標体重」に自動的に入力されるように JavaScript で設定した。 (cf. JavaScript でキー入力に応じて HTML の要素の値とスタイルを変化させる )

     

    setting_form.html

    <div class="error">
    {% if errors %}
        {% for error in errors %}
            {{error}}<br />
        {% endfor %}
    {% endif %}
    </div><!-- end error -->
    
    <form name="createTable" action="/createTable" method="post">
        <table>
            <tr>
                <td>年 / 月</td>
                <td>
                    <input type="text" name="year"
                        value="{{diet.year}}" maxlength="4" />
                     /
                    <input type="text" name="month"
                        value="{{diet.month}}" maxlength="2" />
                </td>
            </tr>
            <tr>
                <td>現在の体重</td>
                <td><input type="text" name="weight"
                    value="{{diet.weight}}" onkeyup="setTargetValue()" /></td>
            </tr>
            <tr>
                <td>目標体重</td>
                <td>
                    <input type="text" name="target"
                        value="{{diet.target}}" />
    
                    {% if targetLimit %}
                        <span class="error">
                            {{targetLimit}} より小さい値は設定できません。
                        </span>
                    {% endif %}
                </td>
            </tr>
        </table>
    
        <p><input id="createBtn" type="submit" value=""></p>
    
    </form>
    
    <p class="small">
        ※「目標体重」は「現在の体重」より
        -<span id="intervalLimit">{{ dt.intervalLimit }}</span> kg
        以内に設定してください。
    </p>

     

    設定ファイル

    背景となる画像を Google App Engine で参照できるように設定が必要となる。 (cf. Google App Engine で画像を扱うには )

     

    app.yaml

    application: 4diet
    version: 1
    runtime: python
    api_version: 1
      
    handlers:
    - url: /img
      static_dir: img
    - url: /.*
      script: dietsetting.py

     

    コントローラ

    最後になってしまったけれど、コントローラの部分。webapp.RequestHandler のサブクラス。

     

    dietsetting.py

    import os
    import wsgiref.handlers
    from google.appengine.ext.webapp import template
    from google.appengine.ext import webapp
    import Diet
    import logging
    
    class SettingPage(webapp.RequestHandler):
        """ 設定画面を表示する """
        def get(self):
           moveTo(self.response, 'setting_page.html', {
                                        "dt"    : Diet.DietTable(),
                                        "diet"  : Diet.Diet().setDefaultSetting()
                                        })
    
    class TablePage(webapp.RequestHandler):
        """ ダイエット表を作成する """
        def post(self):
            diet = Diet.Diet(self.request.get("weight"),
                                self.request.get("target"),
                                self.request.get("year"),
                                self.request.get("month"))
            dt = Diet.DietTable()
            dt.setDiet(diet)
            try:
                # 入力された値を検証する
                diet.validate()
                # ダイエット表を作成する
                dt.createHtml()
                moveTo(self.response, 'diet_table.html', {'dietTable' : dt})
            except Diet.DietValErr, e:
                template_values = {
                    'errors'        : e.errorMessages,      # エラーメッセージ
                    'targetLimit'   : e.targetLowerLimit,   # 目標体重の下限
                    'diet'          : diet,                 # ダイエット情報
                    'dt'            : dt }                  # ダイエット表
                moveTo(self.response, 'setting_page.html', template_values)
    
    def moveTo(response, tmpl, templateValues={}):
        u""" 画面遷移 """
        path = os.path.join(os.path.dirname(__file__), tmpl)
        response.out.write(template.render(path, templateValues))
    
    def main():
        # ログの設定
        logging.getLogger().setLevel(logging.DEBUG)
        # URL と対応するクラス
        application = webapp.WSGIApplication(
                        [('/', SettingPage),            # 設定
                         ('/createTable', TablePage)],  # ダイエット表
                        debug=True)
        wsgiref.handlers.CGIHandler().run(application)
    
    if __name__ == "__main__":
        main()

    うーん、エラー処理が何か今一 … (+_+)

     

    アップロード

    アプリケーションを登録後、作成したファイルをアップロードした。diet フォルダの中に作成していたので、コマンドラインより、

    appcfg.py update diet/

     

    完成!

    http://4diet.appspot.com/

    2008年9月3日水曜日

    Google App Engine でダイエット表の作成 (2)

    追記(2008.9.6) : ダイエット表を作成するには → http://4diet.appspot.com/


    「Google App Engine でダイエット表の作成 (1)」の続き。

    前回、Django のテンプレートタグを使って table 要素を生成するのを諦め、代わりに DietTable クラスで table 要素を生成するように変更した。しかし、実装しているうちにコードが複雑になり、実装を一時中断することに。 (+_+)

     

    table 要素を生成することに専念した HtmlTable クラス

    DietTable クラスが複雑になってしまった理由を考えてみると、

    1. ダイエットのための情報から「表」を作成するための「ルール」を管理している
    2. HTML の table 要素を生成する

    という二つの異なることを担当しているからだと気がついた。ここで言う「ルール」というのは、例えば、作成するダイエット表において

    • 「土日の列は色を変える」
    • 「表の横線の一目盛は 0.1 kg 」
    • 「目標として設定できる体重には制限がある」

    などのこと。これらは HTML の table 要素を生成することとは直接関係がない。関係がないので、HTMLを生成する責務を DietTable クラスから独立させ、 DietTable クラスはそのクラスを使うように変更してみた。 HTML の table 要素を生成するクラスなので HtmlTable と名付けた。これにより HtmlTable クラスは、保持する内容を HTMLとして書き出すことのみに専念し、 DietTable クラスはフォームに入力された Diet 情報から表として生成可能か、ルールに照らしてその妥当性を検証し、また、表のどこに何を書くかを決めることに専念できる。これにより役割分担がはっきりするようになり、誰に何を頼めばいいのか理解しやすくなった。

    g5922

     

    構造

    HtmlTable クラスの構造は上図に示した通りである。クラスの内部は、シンプルに「表」の構造を表現。 HtmlTable クラスの直下にある Rows クラスは、表の各列を管理する Row クラスの集合を管理。そして、一つの Row オブジェクトは、表の列数だけ Cell を管理。 Cell クラスは、セルに設定された内容と、そこにおける複数のスタイルを管理するようにした。

    HtmlTable クラスにおいては、

    • 表の大きさ (行・列数) を設定する
    • 特定のセルの内容とスタイルを設定する
    • 特定の行・列のスタイルを設定する
    • HTML の table 要素に変換する

    の操作を持つ。

     

    余談

    当初は「列」とその集合を管理する Cols, Col クラスも作り、Cell へのリンクを持つようにしてた。しかし、結局必要がなかったので削除。 Cols, Col クラスの実装は、Rows, Row クラスとほぼ同じだった。もし、Cell に対して列方向からもリンクが必要であるならば、次元を表わす Dimention クラスを作成し、そのインスタンスとして「行・列」を表現するのがいいのかもしれない。

     

    実装

    Cell オブジェクトは、「内容やスタイル」が設定されたりする方が、何も設定されないものより圧倒的に少ない予定。だから、最初に HtmlTable オブジェクトを作成したときに、Cell オブジェクトを「行 × 列」の数だけ生成するのではなく、内容やスタイルが設定されたときに、はじめて Cell オブジェクトを生成するようにした。それは、Row オブジェクトについても同様にした。まぁ、どうせ表なんて大きくないのだから、最初から全て Cell オブジェクトを生成しておいてもいいような気もしたし、その方が実装がシンプルになったような気もするが、とりあえずよしとしておくか。 ^^;

     

    ソースコード

    以下にコードを示す。

    HtmlTable.py

    class HtmlTable:
        u""" HTML のtable 要素を作成するためのクラス
    
        構造: HtmlTable - Rows -* Row -* Cell
        """
        def __init__(self, row, col):
            self.rows = Rows()      # 行を管理するオブジェクト
            self.row = row          # 行数
            self.col = col          # 列数
            
        def setCell(self, row, col, content=""):
            u""" 指定された行・列番号のセルの内容を設定する
    
            セルを設定した場合、設定した Cell オブジェクトのみが存在する。
            """
            # 指定された行・列番号が生成したテーブルの範囲を超えてないことを確認する
            if self.row < row or self.col < col:
                raise OutOfRangeErr
            # セルを作成して、行方向からリンクする
            self._setLink(Cell(row, col, content))
    
        def _setLink(self, cell):
            u""" セルを行方向からリンクする """
            self.rows.setCellRow(cell)
            
        def addCssToCell(self, row, col, css):
            u""" セルに CSS を追加する"""
            # 指定された行・列番号のセルの存否を確認する
            c = self.findCell(row, col)
            if c: c.addCss(css)
            else:
                newCell = Cell(row, col, "")
                newCell.addCss(css)
                self._setLink(newCell)
        
        def addCssToCol(self, col, css):
            u""" 列に CSS を設定する """
            for i in range(0, self.row):
                self.addCssToCell(i, col, css)
                
        def addCssToRow(self, row, css):
            u""" 行に CSS を設定する """
            for i in range(0, self.col):
                self.addCssToCell(row, i, css)
            
        def findCell(self, row, col):
            u""" 行番号 row, 列番号 col のセルを取得する """
            return self.rows.findCell(row, col)
            
        def toHtml(self):
            u""" HTML に変換する """
            return "<table align='center'>\n" + \
                        self.rows.toHtml(self.row, self.col) +\
                    "</table>"
    
    class OutOfRangeErr(Exception):
        pass
    
    class Rows:
        u""" 行の集合を管理するクラス """
        def __init__(self):
            self.rows = []      # 行のリスト
            
        def setCellRow(self, cell):
            u""" 指定されたセルを行に設定する """
            row = self.findRow(cell.row)
            if row:
                # 新しくセルが設定された場合、古いセルは上書き。
                row.setCell(cell)
            else:
                newRow = Row(cell.row)
                newRow.setCell(cell)
                self.add(newRow)
    
        def findRow(self, num):
            u""" 指定された番号の行が存在するか検索する """
            for row in self.rows:
                if row.num == num:
                    return row
            return None
    
        def findCell(self, row, col):
            u""" セル(row,col) を取得する """
            r = self.findRow(row)
            if r: return r.findCell(col)
            else: return None
    
        def add(self, row):
            u""" 指定された行を追加する """
            if not self.rows:
                self.rows.append(row)
            else:
                for i,r in enumerate(self.rows):
                    if r.num > row.num:
                        self.rows.insert(i-1, row)
                        return
                self.rows.append(row)
        
        def toHtml(self, row, col):
            u""" HTML に変換する
    
            HtmlTable#setCell() において設定されてないセルは、Cell オブジェクトが
            空である。その際、このオブジェクトが空の tr 要素を出力する責務がある。
            """
            result = ""
            for i in range(0,row):
                r = self.findRow(i)
                if r:
                    result += r.toHtml(i, col)
                else:
                    # 行の要素がない場合
                    result += "\t<tr>\n\t\t" + "<td></td>"*col +"\n\t</tr>\n"
            return result
    
    class Row:
        u""" 行を表わすクラス """
        def __init__(self, num):
            self.cells = []     # セルのリスト
            self.num = num      # 行番号
    
        def setCell(self, cell):
            u""" 列番号の順でセルのリストに追加する。 """
            for i,c in enumerate(self.cells):
                if c.col == cell.col:
                    # 同じ列番号のセルが見つかった場合は上書きする。
                    self.cells[i] = cell; return
                if c.col > cell.col:
                    self.cells.insert(i-1, cell); return
            self.cells.append(cell)
    
        def findCell(self, col):
            u""" 指定された行番号のセルを検索する """
            for c in self.cells:
                if c.col == col: return c
            return None
    
        def toHtml(self, row, col):
            u""" HTML に変換する
    
            HtmlTable#setCell() において設定されてないセルは、Cell オブジェクトが
            空である。その際、このオブジェクトが空の td 要素を出力する責務がある。
            """
            result = "\t<tr>\n"
            for i in range(0,col):
                result += "\t\t"
                c = self.findCell(i)
                if c:
                    result += c.toHtml()
                else:
                    result += "<td></td>"
                result += "\n"
            result += "\t</tr>\n"
            return result
    
    class Cell:
        u""" セルを表わすクラス """
        def __init__(self, row, col, content=""):
            self.row = row              # 行番号
            self.col = col              # 列番号
            self.content = content      # 内容
            self.csss = []              # CSS のリスト
    
        def addCss(self, css):
            u""" セルに CSS を追加 """
            self.csss.append(css)
    
        def toHtml(self):
            return "<td class='" + " ".join(str(x) for x in self.csss) + "'>" \
                        + str(self.content) + "</td>"

    「セルの内容を削除する」など、今回必要のないメソッドは実装していない。

    あ~、これでやっと各々のメソッドがシンプルになった。 ^^

     

    ユニットテスト

    ついでに ユニットテストもちょっとだけ書いておこう。 (cf. PyScripter で UnitTest を自動で生成)

    import unittest
    import HtmlTable
    
    class TestHtmlTable(unittest.TestCase):
    
        def setUp(self): 
            #  2行3列のテーブルを作成
            self.tb = HtmlTable.HtmlTable(2,3)
            # セルの設定
            self.tb.setCell(0,0,"hoge")
            self.tb.setCell(0,1,"piyo")
            self.tb.setCell(1,0,"fuga")
            # セルにCSS を追加
            self.tb.addCssToCol(0, "sunday")
            self.tb.addCssToCol(2, "saturday")
            self.tb.addCssToCell(1,1,"sunday")
            # 行・列に CSS を追加
            self.tb.addCssToRow(1, "x_axis")
            self.tb.addCssToCol(1, "y_axis")
    
        def tearDown(self): 
            pass
    
        def testsetCell(self):
            tb = HtmlTable.HtmlTable(3,4)
            # 例外が投げられることを確認する
            self.assertRaises(HtmlTable.OutOfRangeErr, tb.setCell, 3, 5, "hoge")
            self.assertRaises(HtmlTable.OutOfRangeErr, tb.setCell, 5, 3, "hoge")
            self.assertRaises(HtmlTable.OutOfRangeErr, tb.setCell, 5, 5, "hoge")
            # 例外が投げられないことを確認する
            try:
                tb.setCell(3,3,"hoge")
            except OutOfRangeErr:
                fail("expected a OutOfRangeErr")
        
        def testtoHtml(self):
            self.assertEqual(self.tb.toHtml(), """\
    <table align='center'>
     <tr>
      <td class='sunday'>hoge</td>
      <td class='y_axis'>piyo</td>
      <td class='saturday'></td>
     </tr>
     <tr>
      <td class='sunday x_axis'>fuga</td>
      <td class='sunday x_axis y_axis'></td>
      <td class='saturday x_axis'></td>
     </tr>
    </table>\
    """, self.tb.toHtml())
    
    if __name__ == '__main__':
        unittest.main()

     

    テストについて

    久しぶりにちょっと本をひもといて、

    私は、クラスがなすべきことをすべて調べてから、それらについて 1 つずつ、不具合を起こしそうな条件でテストするようにしています。プログラマによっては「すべての公開メソッドをテストする」よう勧めていますが、これとは違います。テストはリスク主導であるべきです。 (…)

    テストをたくさん書こうとする余り、必要なテストを書き漏らしてしまうからです。(…)

    大事なことは、一番怪しいと思う部分をテストすることです。

    (リファクタリング , p97 より。太字は引用者による。)

    なるほど。

    あ、決してテストをあまりしてない言訳に使っているわけではありません…。 ^^;


    関連記事

    Google App Engine でダイエット表の作成 (1)

    追記(2008.9.6) : ダイエット表を作成するには → http://4diet.appspot.com/


    「Google App Engine でシンプルなアプリを作成する計画」 の続き。

     

    実装方法について考える

    早速実装について考える。「設定画面」については、ただのフォームなので問題なし。では、「表」の方は何で作ればいいのか?表と言えば、 HTML の table 要素。 table 要素でマス目を細かくして、CSS で枠の太さや背景となる色を設定してやればいいのかな? JavaScritp で簡単にサクっとカスタマイズしたテーブルを作成するライブラリがあればいいけれど、 よく知らないのでとりあえず JavaScript は横に置いておく。

    必要なメモを探して、実装開始。

     

    画面遷移

    まずは画面に対応した HTML ファイルを二つ作成し、「作成」ボタンを押したら、表が表示される HTML へと遷移するようにした。同時に、フォームの設定内容を受ける Diet クラスを作成し、遷移先のテンプレートでその情報にアクセスできるようにした。

    path3258

     

    表の作成

    「表」のページでは、まずマス目を作成しなくてはいけない。 A4 の用紙で印刷した場合に、 1 ページで納まるマス目を「印刷プレビュー」を見ながら探る。

    マス目を描くには、テンプレートファイルにおいて for タグ を使う。

    {% for 変数 in リスト %}
        {{ 変数.プロパティ}}
    {% endfor %}

    これは上手くいった。 ^^

    次に、表の枠・背景の色をカスタマイズするために td 要素において各々 class 指定をし、 CSS で設定を行う。土・日曜日の列を「青・赤」に設定するには、Diet オブジェクトから「年月」情報を得て、列番号とその月の日付の曜日に合わせて class 情報を設定しなくてはいけない。(cf. Python である月の日曜日の日付のリストを取得)

    for タグの中で、 if タグを使って、

    {% for 変数 in リスト %}
        {% if 変数.プロパティ == 値 %}
            処理 A
        {% else %}
            処理 B
        {% endif %}
    {% endfor %}

    としたら、あれ?(@_@;) エラーが…。 for タグで取り出した要素オブジェクトを使って、 if タグで値の検査をすることができないようだ。うーん、使い方が悪いのかなぁ。

    少し記事が古いが、Pythoneer » Django’s template syntax, enhanced ifchanged にループの中で if タグを使う場合、

    {% if forloop.counter%2==0 %}
        row1
    {% else %}
        row2
    {% endif %}

    But that doesn’t work in Django templates :-(.

    とあったので、やはりダメなのかもしれない…。

     

    table 要素を作成するクラス

    表を作成するために、テンプレートファイルにおいてテンプレートタグを使うのは諦めた。 (+_+) もしかすると、カスタムタグを使えばいいのかもしれないけれど、 Django については Google App Engine での説明でしか読んだことがないのでわからない。 ^^; また今度ちゃんと読んでから考えよう。

    仕方がないので Diet オブジェクトからフォームに入力された情報をもらって、HTML の table 要素を文字列として出力する DietTable クラスを作成することにした。

    g5240

    入力されたフォームの値を検証するメソッド validate()と、table 要素を出力する html_trs() を実装。「土日」の背景色を設定するだけでなく、表の「枠の太さ」も条件によって変えるようにしたら、こんなことに…。 (@_@;)

    (一つの要素に複数のスタイル情報をつけるには、「CSS で複数の class を指定する」を参照。)

    class DietTable:
        COLS_NUM = 32
        START_COL_NUM = 1
        
        def __init__(self, year, month, weight, target):
            self.year, self.month, self.weight, self.target = year, month, weight, target
            self.rows = range(0,38)
            self.cols = range(0,32)
    
        # 入力された値を検証する
        def validate(self):
            # TODO
            error = []
            
            # 「年」が数字に変換できることを確認する
            if self.year.isdigit(): self.year = int(self.year)
            else: error.append("「年」を数字で入力してください。")
            # 「月が数字に変換できることを確認する
            if self.month.isdigit(): self.month = int(self.month)
            else: error.append("「月」を数字で入力してください。")
            
            # 目標体重が適切か?
            if error: raise DietValErr, error
    
        # HTML で TR 要素を出力
        def html_trs(self):
            result = ""
            for i in self.rows:
                result += ""
                if i == len(self.rows) - 1:
                    # 最後の行は横軸方向を太くする
                    result += self.html_tds(x_axis=True)
                else:
                    result += self.html_tds()
                result += ""
            return result
        
        # HTML で TD 要素を出力
        def html_tds(self, x_axis=False):
            result = ""
            # 日曜日、土曜日の列に色を付ける
            # 色はこのオブジェクトを使用するテンプレートの CSS で指定
            for i in range(0, DietTable.COLS_NUM):
                if i in [self._dayColPosition(x)
                            for x in self._dayslist(calendar.SUNDAY)]:
                    # 日曜日の場合
                    if i == 0: result += self._html_td("", x_axis, y_axis=True)
                    else: result += self._html_td("sunday", x_axis)
                elif i in [self._dayColPosition(x)
                                for x in self._dayslist(calendar.SATURDAY)]:
                    # 土曜日の場合
                    if i == 0: result += self._html_td("", x_axis, y_axis=True)
                    else: result += self._html_td("saturday", x_axis)
                else:
                    if i == 0: result += self._html_td("", x_axis, y_axis=True)
                    else: result += self._html_td("", x_axis)
            return result
    
        # このオブジェクトの年月における指定された曜日の日付のリストを返す
        def _dayslist(self, dayOftheWeek):
            return [x[dayOftheWeek]
                for x in calendar.monthcalendar(self.year, self.month)]
    
        # td 要素を指定した曜日を入れて文字列として返す
        def _html_td(self, dayOftheWeek="", x_axis=False, y_axis=False):
            if   x_axis and y_axis:
                return " "
            elif x_axis and not y_axis:
                return " "
            elif not x_axis and y_axis:
                return " "
            else:
                return " "
    
        # 一日の列を基準にして、指定した日付の列の相対的な位置を返す
        def _dayColPosition(self, x):
            return x + self.START_COL_NUM - 1

     

    もうだめだ。翌日になって見直したら、何が書いてあるのかよくわからなくなった。このまま実装を続けるのは放棄。 ^^; だいたいこんな if if して見にくいコードなんて読みたくもない。 (+_+)


    関連記事

    2008年9月2日火曜日

    CSS と JavaScript で伸縮する領域を作成

    CSS で要素の相対的な位置指定 (2) のつづき

    1. 伸縮する領域を作成したい

    ウェブページにおいて、

    縦横に伸縮する区画

    を作成したい。

    例えば、以下のような影のある領域を作成し、領域内にある文字の量と、ウィンドウのリサイズに応じて、「高さ・幅」が伸縮するようにしたい。

    box

     

    2. 方法

    a. 画像の分割

    上記のデザインを Gimp を使って作成。できたものを各パーツに分割する。分割する対象は、影になっている角の部分 3 つと、影の縦・横線を最小限の長さで取り出す。

    2815750125_fc2aa781bc

    背景となる色については、Web ページでは CSS により指定をするので、 ColorPickerTool Color Picker Tool を使って色を確認しておく。 Foreground Color をダブルクリックして、開かられるダイアログに HTML notation が表示されるので控えておく。

     

    b. 構成要素の位置関係の概略

    さて、上記のように分割した画像を使って領域を作成していく。以下の図では、影の部分を緑色で示している。また、角がわかりやすくなるように、角の大きさを拡大して示した。

    kaisetu2

    上図は 7 つの層から構成される。

    kaisetu

    下の層から、

    1. 右側の影
    2. 右上の角 (ぴったり重ねる)
    3. 右下の角 (下にずらす)
    4. 下側の影 (左にずらす)
    5. 左下の影 (左にずらす)
    6. 背景の色の部分 ( 上にずらす。 JavaScript で幅を動的に設定)
    7. 文字を書く部分 (ぴったり重ねる)

    DSC01623

    上記の位置を考えるときは、同じ大きさの小さな紙を用意し、それぞれにパーツに必要な画像に相当するものを書き込み、いろいろと重ね方を試行錯誤する方法が理解しやすかった。 実際には、 CSS において background の transparent を指定するので、透明な紙であると仮定して考える必要がある。

    これを頭の中、または絵を描くだけでは、わけがわからなくなってしまった。 ^^;

     

    c. JavaScript によるリサイズ

    ウィンドウのリサイズによる JavaScript の操作については、「JavaScript でブラウザのウィンドウのリサイズに合わせて HTML の要素の値を更新」を参照。

    HTML の要素の幅を取得するには、element.offsetWidth – MDC によると、offsetWidth を使うようだ。

    Typically, an element's offsetWidth is a measurement which includes the element borders, the element horizontal padding, the element vertical scrollbar (if present, if rendered) and the element CSS width.

     

    3. ソースコード

    HTML のソースを以下に示す。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional/EN">
    <html>
      <head>
        <title>伸縮する領域</title>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
        <script type="text/javascript">
        <!--
        /* このページが読み込まれたときに呼出される関数 */
        function setup(){
            resizeContent();
        }
        /* ウィンドウをリサイズしたときに呼出される関数
         * id が topLeft の要素の幅を広げる
         */
        function resizeContent(){
            topRightDiv = document.getElementById("topRightCorner");
            contentDiv  = document.getElementById("topLeft");
            contentDiv.style.width = topRightDiv.offsetWidth  + 11 + "px";
        }
        -->
        </script>
        
        <style type="text/css">
        <!--
        /* 右側の縦の影のライン */
        #rightLine{
            position: relative;
            background: transparent url("rightLine.png") right repeat-y;
            
            margin: 15%;
        }
        /* 右上の角の影 */
        #topRightCorner{
            position: relative;
            background: transparent url("topRightCorner.png") top right no-repeat;
        }
        /* 右下の角の影 */
        #bottomRightCorner{
            position: relative;
            top: 10px;
            background: transparent url("bottomRightCorner.png") bottom right no-repeat;
        }
        /* 下の横の影のライン */
        #bottomLine{
            position: relative;
            left: -10px;
            background: transparent url("bottomLine.png") bottom repeat-x;
        }
        /* 左下の角の影 */
        #bottomLeftCorner{
            position: relative;
            left: -10px;
            background: transparent url("bottomLeftCorner.png") bottom left no-repeat;
        }
        /* 背景となる色を指定する領域。
         * リサイズにより、JavaScript でこの領域の幅を変更する。
         */
        #topLeft{
            position: relative;
            background-color: #dddbff;
            top: -9px;
        }
        /* 文字を表示する領域 */
        #content{
            position: relative;
            padding: 20px;
        }
        -->
        </style>
        
        </head>
        <body onresize="resizeContent()" onload="setup()">
    
            <div id="rightLine">
                <div id="topRightCorner">
                    <div id="bottomRightCorner">
                        <div id="bottomLine">
                            <div id="bottomLeftCorner">
                                <div id="topLeft">
                                    <div id="content">
                                        ウィンドウのリサイズに合わせて幅と高さが変化する領域。
                                        JavaScript でoffsetWidth により要素の幅を取得し、
                                        width に設定する。
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
    
        </body>
    </html>

    これにより、ウィンドウのリサイズによって、以下のように伸縮する。

    080902-019080902-020

    Windows 上の、Firefox 3, IE 7, Opera 9.5, Safari 3.1 において動作した。ただし、 Safari の動作がちょっと微妙… (+_+) 昔のブラウザだとどうなのかなぁ~

    それにしても、もっと簡単な方法はないかな? (@_@;)

     

    4. その後

    後で気がついたが、「左下の影」を左にずらすことなく、「下側の影」にぴったりと重ねた場合、 JavaScript を利用しなくてもよいはず。

    betukkai2

    重ねたものを上から見ると、ちょうど大きさが納まっていることがわかる。

    betukkai

    しかし、この方法では、文字のある要素において、 CSS の padding を指定すると、Firefox, Opera, Safari では問題なく表示されるが、 IE で表示が崩れてしまった…。なぜだろう。

    パタッ(o_ _)o~†

    それにしても、CSS って指定の方法はややこしいしい、ブラウザによって微妙に動作が違うのでやだなぁ~。統一してくれ~ (+_+)

     

    画像ファイル

    参考サイト