HTMLの最近のブログ記事

まあ、択一選択にしたければ、最初からラジオボタンかセレクトボックスでフォームパーツを作ればいいのだが、場合によってはチェックボックスで択一選択のフォームを作りたいこともある。(多分そうそう機会はないと思うが)

例えばパラメタの都合上、各inputのname属性の値を別々にしたいときとか。
ラジオボタンでも、name属性の値が違えば、どちらもチェックできてしまう。

以下のようなチェックボックスのフォームが合った場合、

----------------------------------------------------------------------------------
<div class='parts_list'>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_A' value='1'> 選択肢A
    </td>
   </tr>
  </table>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_B' value='1'> 選択肢B
    </td>
   </tr>
  </table>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_C' value='1> 選択肢C
    </td>
   </tr>
  </table>
</div>
----------------------------------------------------------------------------------

これだと、そのままでは全てのチェックボックスにチェックを入れることができてしまうので、これをjQeryで制御する。
簡単にいえば、チェックされた以外のチェックボックスのチェックを外す。

以下が改良版。

----------------------------------------------------------------------------------
<script type="text/javascript" src='jquery.js'></script>

<script type="text/javascript">
 $(function(){

  $("div.parts_list table").each(
   $("input:checkbox",this).click(
    // チェックボックスをチェックした時のイベント開始
    function(){
     // 親要素(table)のセレクタ取得
     var parent = $(this).closest("table");
     // これより前のtableに対する処理
     parent.prevAll().each(
      function(){
       // チェックを外す
       $("input:checkbox",this).attr("checked",false);
      }
     );
     // これより後ろのtableに対する処理
     parent.nextAll().each(
      function(){
       // チェックを外す
       $("input:checkbox",this).attr("checked",false);
      }
     );
     // チェックボックスをチェックした時のイベント終了
    }
   ) // click閉じ
  ) // each閉じ

 )); // function閉じ
</script>


<div class='parts_list'>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_A' value='1'> 選択肢A
    </td>
   </tr>
  </table>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_B' value='1'> 選択肢B
    </td>
   </tr>
  </table>
  <table>
   <tr>
    <td>
     <input type='checkbox' name='check_C' value='1> 選択肢C
    </td>
   </tr>
  </table>
</div>
----------------------------------------------------------------------------------


以上。

jqueryは便利だなーやっぱり。

スクリプト側の配列をテンプレートに反映させるとき、すべての値をそのまま出力するならいいのだが、値が1のときは「checked」、0の時は空白文字列を出力したい時などがある。

例えば、以下のようなテンプレートがあったとき。

■test.tpl
--------------------------------------------------------------------------------------

<TMPL_IF NAME="users">
 <ul>
  <TMPL_LOOP NAME="users">
   <li>
    名前:<TMPL_VAR NAME="user_name">
   </li>
   <li>
    <input type='checkbox' name='pub_name_on' value='1' <TMPL_VAR NAME="pub_name"> /> 名前を公開する
   </li>
  </TMPL_LOOP>
 </ul>
<TMPL_ELSE>
 <p>要素がありません</p>
</TMPL_IF>

--------------------------------------------------------------------------------------


スクリプト側をオーソドックスに書くとこんな感じ

■test.cgi
--------------------------------------------------------------------------------------

use strict;
use HTML::Template;

# オブジェクト生成
my $tpl = HTML::Template->new( filename => 'test.tpl');

#ユーザ情報を2次元配列にセット
my @users = (
 ['山田 太郎', 0], # 「0」は名前非公開
 ['鈴木 一郎', 1] # 「1」は名前公開
);

#ループ格納用配列作成
my @lood_data = ();

while (@users) {

 # 各行のハッシュ作成
 my %row_data = ();

 # 配列データを格納
 $row_data{'user_name'} = $$_[0];
 $row_data{'pub_name'} = $$_[1];

 # $row_data{'pub_name'} が1だったら「checked」に変更
 $row_data{'pub_name'} = $row_data{'pub_name'} == 1 ? 'checked' : '';

 # リファレンスでループ用配列に追加
 push(@loop_data, \%row_data);

}

# paramメソッド実行
$tpl->param('users' => \@loop_data);

#出力
$tpl->output(print_to => \*STDOUT);

--------------------------------------------------------------------------------------


しかしこれだと、各値をいちいちハッシュにしたり面倒くさいので、下記のようにmapを用いてさらにその中で条件分岐も済ませると楽ちん。

■test2.cgi
--------------------------------------------------------------------------------------

use strict;
use HTML::Template;

# オブジェクト生成
my $tpl = HTML::Template->new( filename => 'test.tpl');

#ユーザ情報を2次元配列にセット
my @users = (
['山田 太郎', 0], # 「0」は名前非公開
['鈴木 一郎', 1] # 「1」は名前公開
);

# mapを使ってパラメータを一気に作成
my %params = ('users' => [
 map { +{
  user_name => $$_[0],
  pub_name => $$_[1] == 1 ? 'checked' : '' # ←ここで条件分岐
 }} @users
]);


# paramメソッド実行
$tpl->param(%params);

#出力
$tpl->output(print_to => \*STDOUT);

--------------------------------------------------------------------------------------

こんなかんじです。
MilkyStepのコミュニティでコアサーバを使ったとき、メルマガのデフォルト登録完了画面が文字化けする(日本語部分が???になる)との報告を受け、一時期かなり調査を行ったが解決せず。
それから約2カ月後、他のユーザから同様の現象が報告された。

開発環境では全くそのような現象が起こらないので、仕方なく、コアサーバーを実際に借りてMilkyStepを設置し徹底的に調査することにした。

表示させるデフォルト画面のHTMLソースは、ユーザが自由に編集できるようになっているのだが、そのソースはMySQLに保存される。

そこからデータを引っ張って、少し置換処理してからプログラムで出力する。

確かにコアサーバで実際に試してみると、5回中4回くらいの割合で日本語が「?」になる。
(成功する場合もある)

MySQLの文字セットはutf8、照合順序もutf8_general_ci、さらにSQL発行時に毎回「SET NAMES utf8」(MySQL4.1以降で有効)しているので、データ通信間で化けるとは考えにくい。
もちろんスクリプトのファイルもUTF8。

とりあえず、ブラウザの文字コードの誤判定臭いので、日本語を多めに入れたり、EUCでいうところの「美乳テーブル」である、郵便くんマーク(〠←これ)を冒頭に入れたりしてみたが、変わらず。

次に、Perlでutf8扱う時によくある、utf8フラグあるなしの問題かとも思い、「use utf8」したりEncode::encode/decodeしたりいろいろやったが、どうやら関係ないっぽい。

というか、なんかMySQLから参照した時点ですでに化けてるっぽい。

試しに、SELECT時に、以下のような感じで、明示的に照合順序を指定してみる。

まず、
--------------------------------------------------------------------------------
SELECT column_name COLLATE utf8_general_ci FROM ・・・・・
--------------------------------------------------------------------------------

・・・変化なし。

次に、
--------------------------------------------------------------------------------
SELECT COERCIBILITY(column_name COLLATE utf8_general_ci) FROM ・・・
--------------------------------------------------------------------------------

お、1回目はうまくいった。
もう1回。

--------------------------------------------------------------------------------
Software error:

COLLATION 'utf8' is not valid for CHARACTER SET 'latin1_swedish_ci' at ......
--------------------------------------------------------------------------------

あれあれ????

「utf8_general_ciでの照合はCHARACTER SET 'latin1'では有効ではありません」

latin1??なんで勝手に文字セット変わってんだよ!
utf8で固定だろーが!

なぜ、コアサーバだけ、しかも特定のカラムの文字列だけ参照するときにこうなるのか・・・

MySQL4から5に強引に移行した時の忘れものか?


とにかくこの、参照時に勝手にテーブルの文字コードをLatin1と判別するわけのわからない現象と格闘すること数時間。

そもそもこの処理って、forkした後の親プロセスの中でやっていたんだが、このforkを切ったらなぜか正常になった。
ということで、この処理はforkの中から外に出すことに決定。

いまだに原因はよくわからないが、fork時は内部的に変数を子プロセスにコピーするが、その時点で(コアサーバでは)なんか問題があるらしいってことはぼんやりわかった。

サーバサイドのアプリを広く対応するのはやっぱ一苦労だな・・・

例えば、長い文字列
「あいうえおかきくけこさしすせそ」を
「あいうえお...」みたいに、
指定の文字数以降は省略したい場合。

ただ単にバイト数で切ってしまうと、日本語の場合は余るバイトがでてくるので、そのへんをちゃんとしないとダメ。

以下、UTF8の場合の処理

----------------------------------------------------------------------------------

my #str = "あいうえおかきくけこさしすせそ"; # 元の文字列
my $view = 10; # 10文字まで表示し後は省略

print &main::round_utf8($str,$view);

sub round_utf8 {
my ($str,$view) = @_;

my $one = "[\x00-\x7F]"; # 1バイト文字
my $two = "[\xC2-\xDF][\x80-\xBF]"; # 2バイト文字
my $three = "[\xE0-\xEF][\x80-\xBF]{2}"; # 3バイト文字
my $bigger = "[\xF0-\xF7][\x80-\xBF]{3,}"; # 4バイト以上

$str =~ s/^(($one|$two|$three|$bigger){$view}).+/$1.../g;

return $str;
}

----------------------------------------------------------------------------------

EUCやSJISの場合も、サブルーチン中のバイト列の定義を変えれば同じ。
携帯3キャリア対応の絵文字・デコメが使えるメールシステム開発時にハマった点。
ハマり時間約10時間・・・

まず、デコメールの各キャリア向けのメールデータの組み立ては、すでに既知の方法で特に問題なく送信できた。
(2010/9/4現在)
【参考サイト】
http://pentan.info/mobile/send_deco.html など

しかし、ドコモ&ソフバンは上手くいくのに、auのデコメだけは添付画像(インラインHTML表示)がうまくいかない・・・というか、ばらつく。

サイズが1KB前後の画像ファイルは正常にインライン表示されるのだが、少しサイズが大きくなる(2KB前後)と、インラインに表示されず、別途添付ファイルのみダウンロードしようとしてもなぜかできない。
しかもインラインFLASH対応機種なはずなのに、swfファイルも再生されない。
(検証した機種はSH005)

これについて悩んでいたら、以下のサイトを発見。
http://www.plusmb.jp/2009/09/04/4481.html
てゆうか、よく見たら冒頭のサイトにもちゃんと書いてあるし・・・

これによると「auは、サイズが大きめの添付ファイルは、base64エンコードした後76byteごとに改行を入れる必要がある」らしい。

しかし、なんでいきなりこんな話になるのかと思って少し調べたら、どうやらRFCに準拠したBase64エンコードは、76byteごとに改行文字を入れなさいということらしい。
(ただしsubjectの64エンコードでこれをやるとダメ)

もともと実績のあるMIME.pmとかのbase64メソッドを使ってエンコードしていれば問題なかったのだが、私の場合、スクリプト配布時のことを考えて、自力で64エンコードしていたため、ハマったらしい。
(MIME.pm内を見てみると、たしかに76byteごとに改行を入れる処理があった・・・)

よって、対応策としては、
添付ファイルデータをくっつけるときに、

$buf = &base64encode($buf); #自力の64エンコードサブルーチン
のあと、
$buf =~ s/(.{1,76})/$1\n/g;

という処理を加えることで解決。

これを、メール内のtext/htmlパートにくっつけてやればよい。
(もちろんflashの場合は、flashのmimeタイプを指定)

しかし、docomoとsoftbankはこの処理をやらなくてもちゃんと送れるもんだから、最初は携帯の設定が悪いんだとばっかり思ってました・・・



------------------------------------------------------------------------------

以下は、2010/9/4現在の検証でわかったことのまとめ。

検証機種は、
ドコモ・・・P08A(松下)
au・・・SH005(シャープ)
Softbank・・・930CA(カシオ)

・KDDIの技術資料(http://www.au.kddi.com/ezfactory/tec/spec/decorations/emoji.html)には、デコメ絵文字は20×20ドット指定になっているが、120×25くらいのファイルでも送信できた。
 また、Dispositionは指定しなくても問題なかった。

・swfファイルのサイズ制限は特に書いていないが、50px×50px(約2KB)は3キャリアとも送信できた。ただし、機種により再生の滑らかさはだいぶ差がある模様。

・冒頭に紹介したサイトでは、HTMLソースはquoted-printableエンコードすると書いてあるが、特にこれを行わず、「Content-Transfer-Encodeing: 7bit」で送信しても、特に問題なかった。

また、Softbankにおいては、HTMLソースをUTF8で送信(quoted-printableエンコードなし)しても特に問題なかった。

また、text/plainとtext/htmlの両方のパートが無いとダメと書いてあるが、docomoとsoftbankにおいては、text/plainパートを記述しなくても問題なく送信できた。


以上、ご参考までに。
やりたいこと。

javascriptで画像ファイルの一覧を表示
    ↓
一覧から一つの画像を選択
    ↓
javascriptでその画像のサイズ指定画面を表示
    ↓
サイズを入力しOKを押すと画像名とサイズを表示
(実務的には、その情報をテキストエリアに入力させたりする)



【HTML】
<!-- JSファイル読み込み -->
<script type="text/javascript" src='./transition.js'></script>


<!-- 事前に遷移画面は用意しおき、隠しておく-->

<!-- 画像選択画面 -->
<div id="confirm1" style='visibility:hidden;position:absolute;'>
 <div>
  <div style="float:left;padding:3px;">
   <b>画像一覧</b>
  </div>
 <div style='text-align:right;padding:3px;'>
  <a href="javascript:void(0)" onclick="close_img_list();">閉じる</a>
 </div>
 <div style='overflow:auto;'>
  <table>
 <tr>
    <td style="padding:4px;">
     <!-- 画像のイメージと画像ファイル名 -->
     <div style="overflow:hidden;float:left;">
      <div style='clear:both;overflow:hidden;'>
       <img src='./hoge/hoge.gif' />
      </div>
      <div>
        <a href="javascript:void(0)" onclick="show_given_size('hoge.gif');">hoge.gif<a/>
      </div>
     </div>
        ・
        ・
        ・
    </td>
   </tr>
  </table>
 </div>
</div>

<!-- 画像サイズ指定画面 -->
<div id="confirm2" style='visibility:hidden;position:absolute;'>
 <div>
  <div style="padding:3px;">
   <b>画像サイズ指定</b>
  </div>
  <div style='padding:3px;'>
   <a href="javascript:void(0)" onclick="close_given_size();">閉じる</a>
  </div>
 </div>
 <div>
  <table>
   <tr>
    <td>
     画像の縦横サイズを指定してください
    </td>
   </tr>
   <tr>
    <td>
     <form name='given_size' action='#'>
      W <input type='text' name='img_w' value='' /> px × H <input type='text' name='img_h' value='' /> px
<input type='button' name='ok' value='O K' onclick="alert(get_img_info\(\),'');close_given_size();" />
      <input type='hidden' name='img_name' value='' />
     </form>
    </td>
   </tr>
  </table>
 </div>
</div>


<!-- 初期画面 -->
<div>
 <a href='javascript:void(0)' onclick="show_img_list()">画像一覧を表示</a>
</div>


【javascript(transition.js)】

// 画像一覧表示
function show_img_list() {
 document.getElementById("confirm1").style.visibility="visible";
}

// 画像サイズ指定画面表示
function show_given_size(name) {
document.getElementById("confirm2").style.visibility="visible";
document.given_size.img_name.value = name;
}

// 画像一覧を非表示
function close_img_list() {
document.getElementById("confirm1").style.visibility="hidden";
}

// 画像サイズ指定画面を非表示
function close_given_size() {
document.getElementById("confirm2").style.visibility="hidden";
}

// 画像名と画像サイズを組み合わせて返却
function get_img_info(){
var img_name = document.given_size.img_name.value;
var img_w = parseFloat(document.given_size.img_w.value);
var img_h = parseFloat(document.given_size.img_h.value);
if((!img_h) || (!img_w) || (img_h < 1) || (img_w < 1) ){
alert("サイズは1以上の半角数字で指定してください");
}
return '画像ファイル名は' + img_name + 'で、サイズは横が' + img_w + 'PX、縦が' + img_h + 'PXです。';
}


------------------------------------------------------------------------

※ポイントは、hidennフィールドに後から値を付け足すということ。
テキストエリア挿入支援処理などを行うjavascriptの処理時に発見したこと。

Vista+IE8の環境で「getElementById("moji")」とかやるとなぜかエラーになる。

内容は「オブジェクトがありません」とのこと。


無いわけない。

ちゃんとid属性もname属性も指定して、値も一緒にしている。


ためしに、

getElementById(moji)

とやったら、「'moji'は定義されていません」というエラー。
まあ、当たり前だわな。

しかし、

getElementById('moji')

とやると、うまくいく。


IE7の時はこのエラーが出なかったような記憶がアルのだが・・・

協調性を無視し我が道をいくMicrosoftに感謝。


しかし何回も何回もソースを見直してたのがバカみたいだ。


最近はどの言語でも"moji"と書いたら文字列で処理するだろが。普通は。
----------------------------------------------------------------------------------------------

#!/usr/bin/perl -w

use strict;
use LWP::UserAgent;
use HTTP::Request::Common;

# オブジェクト作成
my $ua = LWP::UserAgent->new();
my $url = 'http://yahoo.co.jp';
my $req = &HTTP::Request::Common::GET($url);

# レスポンスを得る
my $res = $ua->request($req);

# フィールド名を指定してヘッダを取得
my $con_type = $res->header('Content-Type');

#処理
if($con_type =~ /shift_jis/i){
  # (sjis用エンコーディング処理)
}
elsif($con_type =~ /euc-jp/i){
  # (ujis用エンコーディング処理)
}
elsif($con_type =~ /utf-8/i){
  # (utf8用エンコーディング処理)
}
  ・
  ・
  ・

----------------------------------------------------------------------------------------------


※charsetをダイレクトに返してくれるメソッドはどうやら無いらしい・・・
あったら教えてください。
今更基本的なことだが、form部品の中に、

例えば

<input type='checkbox' name='cb' value='1' checked disabled>

とすると、「checked」にしていても、送信先でvalue値が空になってしまうらしい。
(firefoxでしか試してないけど)


チェックボックスを空のまま固定するんならいいけど、チェックを入れた状態で固定したいときに保存処理を行うとチェックされてない状態と同じ状態で保存されてしまうので困る。

そしたら「readonly」というのがあったらしく、disabledの代わりにこれを使ったらよかったらしい。

<input type='checkbox' name='cb' value='1' checked readonly>

(XHTMLの場合は、readonly='readonly'とする)


だったら「disabled」なんていらねーんじゃねーか?
と思ったんだけど、頭いい人は何かしらのときに使うんだべな。


ちなみにIEじゃサポートされて無いって噂なんだが今はダイジョブなのかな?



2010/2/14追記:

readonlyにすると、テキストフォームなら入力できないが、チェックボックスの場合はチェックができてしまい、値も送信されちゃうので、そういうときは
"checked disabled"にしといて、別にhiddenフィールドでチェックボックスのvalueを送信します。

<form action='' method='post'>
(中略)
<input type='checkbox' name='aaa' value='1' checked disabled>
(中略)
<input type='hidden' name='aaa' value='1'>
</form>