in_place_editorのハイライトを制御する。

些細なことだが、以前から気になっていたのだ...。in_place_editorの上にマウスを乗せた時、ハイライトして、消え行く最後の色を。もう少し詳しく説明すると、in_place_editorでは、以下のような状態変化を確認出来る。

  1. マウスカーソルがin_place_editorの上に乗ると、その文字列の背景色がピカッと黄色く光る。
  2. マウスカーソルがin_place_editorの上に滞在している間は、背景色の黄色はその状態で維持される。
  3. マウスカーソルがin_place_editorから離れると、文字列の背景色が次第に薄れて行く...。

3番目の「背景色が次第に薄れて行く」ところが問題で、実は「薄れて行く」のではなく、「白(#ffffff)に変化して行く」というのが正しい表現だ。今まで背景が白い時は気にならなかった。(同じ白に変化して行くので、薄れて行くように見えたのだ。)
ところが、今やCSVサーバープロジェクトでは、in_place_editorの背景色は6色に変化する可能性がある。(通常の縞模様の2色、並べ替えのキー列として強調した縞模様2色、新規アップロードした時の濃いめの水色、削除対象を選択した時の灰色)
背景が白以外のin_place_editorの上をマウスが通過すると...、黄色から白に向かって変化して、最後に一気に元の背景色に戻る。背景色が白の時と違って、この不自然な変化が気になりだしたのだ、些細なことだが...。

ハイライトを制御するオプション

Railsのヘルパメソッドからは指定出来ないが、InPlaceEditorにはハイライトを制御するオプションが二つある。highlightendcolorを指定すれば、次第に薄れて行くような変化を表現できるかもしれない。

オプション 意味 デフォルト値
highlightcolor 最初に光った時の色 #ffff99
highlightendcolor 最終的に変化する色 #ffffff

Railsのin_place_editorを拡張

ハイライトオプションを制御出来るように、in_place_editorを拡張した。

app/helpers/aplication_helper.rb
ヘルパー
...(途中省略)...
  def in_place_editor(field_id, options = {})
        function =  "new Ajax.InPlaceEditor("
        function << "'#{field_id}', "
        function << "'#{url_for(options[:url])}'"

        js_options = {}
        js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
        js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
        js_options['emptyText'] = %('#{options[:empty_text]}') if options[:empty_text]
        js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
        js_options['rows'] = options[:rows] if options[:rows]
        js_options['cols'] = options[:cols] if options[:cols]
        js_options['size'] = options[:size] if options[:size]
        js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
        js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]        
        js_options['ajaxOptions'] = options[:options] if options[:options]
        js_options['evalScripts'] = options[:script] if options[:script]
        js_options['callback']   = "function(form) { return #{options[:with]} }" if options[:with]
        js_options['highlightcolor'] = %('#{options[:highlightcolor]}') if options[:highlightcolor]        
        js_options['highlightendcolor'] = %('#{options[:highlightendcolor]}') if options[:highlightendcolor]        
        function << (', ' + options_for_javascript(js_options)) unless js_options.empty?

        function << ')'

        javascript_tag(function)
  end
...(途中省略)...

highlightendcolorオプションを利用してみる

app/views/csvs/_listd.rhtml
ビュー(リストデータの1行を描画する部分)
  • bgclassは背景色を表現するクラス属性を保存する変数。そのクラス属性には、light、darkの他に、アップロードで追加された時のupload、削除候補として選択された時のcheckedが設定される可能性がある。
<% bgclass = cycle('light', 'dark', :name=>'row_class') %>
<tr class="<%= bgclass %>" id="csv_<%= listd.id %>" name="csv_row">
...(途中省略)...
        <%= in_place_editor_field :csv, :file_comment, {}, :rows => 2, 
                                  :save_text=>"保存", :cancel_text=>"戻る", 
                                  :empty_text=>"編集>>", 
                                  :highlightendcolor=>current_bgcolor(bgclass, "file_comment") %>
...(途中省略)...
app/helpers/application_helper.rb
ヘルパー
  • current_bgcolorは自分で設定したヘルパメソッド。クラス属性と列タイトルから、現在の背景色を返すようにした。
  def current_bgcolor(bgclass, column_name)
    case
    when bgclass == 'checked'
      "#808080" # チェックあり
    when bgclass == 'upload'
      "#e8e8fa" # 新規アップロード
    when !arranged_class(column_name).empty? && bgclass == 'light'
      "#f2f2f2" # 並び替えキー列、明るい縞模様
    when !arranged_class(column_name).empty? && bgclass == 'dark'
      "#d8d8ed" # 並び替えキー列、暗い縞模様
    when bgclass == 'light'
      "#ffffff" # 明るい縞模様
    when bgclass == 'dark'
      "#f0f0fa" # 暗い縞模様
    end
  end

クラス属性checkedは検出できない...。

ここまで実装して、クラス属性がchecked以外の時は、ハイライトした色が背景色に消え行くように変化するようになった。しかし、一括削除のためにチェックマークを付けた行では、チェック前の背景色が変化の最終カラーになってしまう不自然な状態だ。何故か?
今回、チェックボックスの操作では、再描画せずにJavaScriptでclass属性とchecked属性を追加しているだけ。だから、bgclass変数も変化しない。それで、最初に描画した時の背景色のままになってしまうのだ。残念...。
このままRailsの世界でクラス属性checkedを検出するためには、チェックされた時に属性の追加だけでなく、行そのものを再描画する必要がある。(と思う。)でも、そのために、せっかく覚えたJavaScriptによる効率的な処理を利用しないのは悔しい。削除対象にしたまま、編集作業をするという無意味なことは、あまりしないだろうと思うが、マウスカーソルが上に乗ってしまえば、不自然なハイライトの変化が気になる。(気にしているのは開発者である自分だけかもしれないが...。)

InPlaceEditorのソースを覗いてみる

そもそも、InPlaceEditorが自分が存在する場所の背景色を読み取って、最終的に変化する色を勝手に設定してくれれば良いのだ。ソースを確認すると以下の部分を発見した。ビジュアルエフェクトのハイライトで実現していたのだ。

public/javascripts/control.js
//740行目前後
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: this.options.highlightendcolor,
      restorecolor: this.originalBackground
    });

endcolorは変化の最終色、restorecolorは元の状態の背景色と解釈して、単純にendcolorthis.originalBackgroundに変更してみたが...。話はそんなに単純ではなかった。ますます不自然な変化になってしまった...。

this.originalBackgroundが返す値

alert('this.originalBackground');と追記してthis.originalBackgroundの値を確認してみると、常にtransparent(透明色)が返ってくる。ソースを検索してみるとthis.originalBackgroundは以下のように定義されていた。

  • Element.getStyle(this.element, 'background-color')で、InPlaceEditor自体の背景色を取得しているのだが、設定されていない時はtransparentを設定するようになっている。InPlaceEditorの背景色はスタイルシートに設定してないので、それで常にtransparentになってしまうようだ。
//521行目前後
    this.originalBackground = Element.getStyle(this.element, 'background-color');
    if (!this.originalBackground) {
      this.originalBackground = "transparent";
    }

親要素の背景色を取得する

透明ということは、それより下の色が透けて見えるということだ。それなら親要素の背景色が検出できれば問題は解決するかと思い、以下のようにやってみた。

  • this.elementの後ろに、parentNodeを追記した。
//740行目に追記
alert(Element.getStyle(this.element.parentNode, 'background-color'));

おっと、マウスカーソルを乗せた時の背景色が返ってきた!alertの内容は以下。

RGB(240, 240, 250)

      • つまり、#f0f0faが背景色として取得できた!

これで、うまく行きそうな予感がして、endcolorに設定してみた。

//740行目前後
    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: Element.getStyle(this.element.parentNode, 'background-color'),
      restorecolor: this.originalBackground
    });

しかし、思い通りの結果にならない...。どうもendcolorに黒が設定されているようだ。何故か?調べてみると、endcolorの書式は#f0f0faのような表現でないとダメらしい。試しに、'#f0f0fa'と変更してみると、変化の最終色はその色になった!

RGB(240,240,250)を#f0f0faに変換する

あとは、うまく#f0f0fa書式に変換できれば良いのだが、自分で一から書くなると、ちょっと骨が折れそうだ。検索してみると、p4lifeさんのメモ - 色表現を変換するJavaScriptライブラリ - rgbcolor.jsを発見。自分が悩んでいることは、既に世界のどこかで同じように悩んでいて、素晴らしいライブラリとして公開されていることを知った!

早速、rgbcolor.jsをダウンロードして、public/javascript/フォルダに取り込み、以下のように活用してみた。素晴らしいです!#f0f0faに変換されて、背景色に反映されるようになりました!

//740行目前後
    visiblecolor = Element.getStyle(this.element.parentNode, 'background-color')

    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: new RGBColor(visiblecolor).toHex(),
      restorecolor: this.originalBackground
    });

親要素を遡って背景色を取得する

現状では、InPlaceEditorの親要素、つまりtd要素の背景色を取得しているが、さらに上のtr要素まで遡らないと、背景色を取得できない状況もあった。(スタイルシートで、アップロードした時の背景色はtr要素で設定しているからだと思う。)そこで、もう少し一般的に使えるように、以下のように変更してみた。

//740行目前後
    element = this.element;
    i = 0;
    do {
      element = element.parentNode;
      i++;
      visiblecolor = Element.getStyle(element, 'background-color');
    } while (visiblecolor=='transparent' && i<10)

    this.effect = new Effect.Highlight(this.element, {
      startcolor: this.options.highlightcolor,
      endcolor: new RGBColor(visiblecolor).toHex(),
      restorecolor: this.originalBackground
    });


もうin_place_editorの:highlightendcolorオプションは不要である。current_bgcolorも、無用なヘルパメソッドになった。削除しておこう。これでInPlaceEditor自身が、勝手に背景色を調整してくれる!(でも、うまく調整できない状況もあるかも...。)

忘れたくない自分メモ

  • in_place_editorを使う時は、くれぐれも<%= javascript_include_tag "rgbcolor" %>も忘れずに!

スタイルシート

/* th,tdのセルレベルまで指定する。(safariで、in_place_editorのハイライトを自然に減光するため) */
/* テーブル基本設定 */
table.list{
  border-width: 1px;
  border-style: solid;
  border-color: #999;
  
  border-collapse: collapse;/* collapse separate inherit */
  border-spacing: 0px;
  
  empty-cells: show;
  table-layout: fixed;
  /* マージンを0以上に設定する。(safariで、テーブル外枠の描き残しを防止するため) */
  margin: 1px;
  padding: 0px;
  background-color:	#999;
}

table.list tr th { 
  border-style: solid;
  background-color: #e6e6e6;
  text-align: left;
}
table.list tr td { 
  border-style: none solid;
} 


/* 縞模様リストの設定 */
table.list tr.light td { 
  background-color: #FFFFFF;/* 明るい縞模様 */
}
table.list tr.dark td { 
  background-color: #f0f0fa;/* 暗い縞模様 */
}


/* 並べ替えキー列の設定 */
table.list tr th:hover, 
table.list tr th.arranged, 
table.list tr th.asc, 
table.list tr th.desc { 
  background-color: #d9d9d9;/* キー列のタイトル */
}
table.list tr th:active,
table.list tr th.asc:active, 
table.list tr th.desc:active { 
  background-color: #cccccc;/* キー列のタイトル クリック */
}
table.list tr.light td.arranged { 
  background-color: #f2f2f2;/* キー列の明るい縞模様 */
}
table.list tr.dark td.arranged { 
  background-color: #d8d8ed;/* キー列の暗い縞模様 */
}


/* アップロードとチェックあり */
table.list tr.upload td.arranged,
table.list tr.upload td { 
  background-color: #e8e8fa;/* 新規アップロード項目、キー列含む */
}
table.list tr.checked td.arranged,
table.list tr.checked td { 
  background-color: #808080;/* チェックあり選択項目、キー列含む */
}