swk's log 最新ページ

mform - from swk's log

2006-12-23 Sat

* form の内容がデフォルト値から変わったら色を変える JavaScript コード [mform] 103 users

これが便利だといえる状況はだいぶ限られてはいるのだけど.文字を入力したり,チェックボックスを押したりしてデフォルトの状態から変化すると,そこの色を変えるような JavaScript のコードを書いてみた.

といってもわかりにくいと思うのでまずデモ:

適用したいページで,こんな JavaScript コードをロードします.

(function(){

var color_modified = '#ffff88';
var reset_when_reloaded = 1;

init();

function init() {
    var fs = document.forms;

    for (var i = 0; i < fs.length; i++) {
        if (reset_when_reloaded) {
            fs[i].reset();
        }
        var es = fs[i].elements;
        add_event(fs[i], ['reset'],
                  mkhandler(fs[i].elements,
                            function(e) { set_color(e, false); }));
        for (var j = 0; j < es.length; j++) {
            var prop = get_property_watched(es[j]);
            es[j].saved_default = es[j][prop];
            es[j].orig_color = es[j].style.backgroundColor;

            if (es[j].type == 'radio') { /* XXX: inefficient */
                add_event(es[j], ['keyup', 'change', 'click'],
                          mkhandler(es, watch_property));
            } else {
                add_event(es[j], ['keyup', 'change', 'click'],
                          mkhandler([es[j]], watch_property));
            }
        }
    }
    
    var ls = document.getElementsByTagName('label');
    for (var i = 0; i < ls.length; i++) {
        var labeled_elm;
        if (ls[i].htmlFor
            && (labeled_elm = document.getElementById(ls[i].htmlFor))) {
            if (!labeled_elm.assoc_labels) {
                labeled_elm.assoc_labels = [];
            }
            labeled_elm.assoc_labels.push(ls[i]);
            /* XXX: implicitly labeled controls not considered */
            ls[i].orig_color = ls[i].style.backgroundColor;
        }
    }
    function mkhandler(es, func) {
        return function() {
            for (var i = 0; i < es.length; i++) {
                func(es[i]);
            }
        };
    }
}

function add_event(elm, evtypes, func) {
    for (var i = 0; i < evtypes.length; i++) {
        if (elm.attachEvent) {
            elm.attachEvent('on' + evtypes[i], func);
        } else if (elm.addEventListener) {
            elm.addEventListener(evtypes[i], func, false);
        } else {
            elm['on' + evtypes[i]] = func;
        }
    }
}

function get_property_watched(elm) {
    var property_watched = { checkbox: 'checked', radio: 'checked',
                             _default: 'value' };
    return property_watched[elm.type] || property_watched['_default'];
}

function watch_property(elm) {
    var prop = get_property_watched(elm);
    set_color(elm, elm[prop] != elm.saved_default);
}

function set_color(elm, hilight) {
    elm.style.backgroundColor = hilight? color_modified: elm.orig_color;
    if (!elm.assoc_labels) {
        return;
    }
    for (var i = 0; i < elm.assoc_labels.length; i++) {
        set_color(elm.assoc_labels[i], hilight);
    }
}

})();

どんなときに使うのかというと,サーバ側に保存してあるデータを編集するようなフォームに適用することを想定してます.編集すると色が変わるから submit しなくてはいけないとわかる.色が変わっていないならば Ctrl-W でウィンドウごと閉じちゃってよいとわかる.

で,submit 後に表示するページを,編集ページと同じものにしておけば, submit が「save」みたいになる.感覚的にはエディタっぽいインタフェースになります.私の場合は,自分用のウェブクリッピング CGI に使ってます.というかそれ用に書いた.

HTML に手を入れる必要はないので,bookmarklet とか Greasemonkey とかにもできるはず.ただし,新しい情報を提出するようなフォームに適用しちゃうとわけわからんと思うので,いまいち使いどころがないのがアレだ.

現状の問題点としては,

  • reload すると,その時点のフォームの内容がデフォルト値になっちゃって困るので,強制的に reset() するようにしている.これは変数 reset_when_reloaded で制御できる.
  • 大きな入力ページだと,画面外にあるフォームの色が変わっているかどうかがわからない.document.body の色を変えるとか,title に細工するとかの方がわかりやすいんだろうか.
  • これは問題と考えるべきかどうか分からないけど,Firefox だとチェックボックスやラジオボタンは style.backgroundColor を変えても何も変化しない.とりあえず label で対応するようにしてみた.
  • ただし implicit な label には未対応.for 属性で明示的に指定してやらないと連動しません.

(追記) 最後の「})();」をうっかり消しちゃってたので修正.

最終更新時間: 2009-01-04 15:31


Shingo W. Kagami - swk(at)kagami.org