もっと便利に!jQueryでラクラクサイト制作(実践サンプル付き)

第17回「その場編集」実装する

「その場編集」を実装

前回はツールチップの実装方法をご紹介しました。

今回「Edit In Place」「その場編集」と呼ばれる、テキストをクリックすると、その場で編集できるようにする仕組みを実装してみます。

画像

今回の仕組みを考える

この「その場編集」の仕組みを考えてみましょう。今回の要件は以下の通りにします。

  1. 最初はただのテキスト
  2. クリックをすると、テキストがinput要素に置き換わり編集可能に。
  3. input要素からフォーカスが外れたら、input要素が編集後の文字に置き換わる。
  4. フォーカス外れた時、valueの値が空なら最初の値に戻す。

本来であれば、上記の仕組みの4の後に、PHPなどでデータベースに編集後の情報を送信し、保存するという仕組みになるのですが、今回はその前のところまでの実装方法のご紹介とします。

まずは完成サンプルとソースコードを見てみましょう。

完成ソースコード
jQuery(function($){
    $('dd').click(function(){
        //classでonを持っているかチェック
        if(!$(this).hasClass('on')){
            //編集可能時はclassでonをつける
            $(this).addClass('on');
            var txt = $(this).text();
            //テキストをinputのvalueに入れてで置き換え
            $(this).html('<input type="text" value="'+txt+'" />');
            //同時にinputにフォーカスをする
            $('dd > input').focus().blur(function(){
                var inputVal = $(this).val();
                //もし空欄だったら空欄にする前の内容に戻す
                if(inputVal===''){
                    inputVal = this.defaultValue;
                };
                //編集が終わったらtextで置き換える
                $(this).parent().removeClass('on').text(inputVal);
            });
        };
    });
});

今回の仕組みでは、どのタイミングで編集を終了するかの仕様は、フォーカスが外れた時にしてみました。今回の紹介した方法のの他にも「保存」などのボタンを作り、そのボタンを押したタイミングで編集が終了するという仕組みにしてみるのもいいと思います。保存前に、アラートを出して確認するというのでもいいと思います。その時々の仕様で仕様を実装してみて下さい。

使用するHTML

今回はdl、dt、ddを利用してマークアップしたものを使用してみます。

HTML
<dl>
    <dt>名前:</dt>
    <dd>技評太郎</dd>

    <dt>会社名:</dt>
    <dd>技術評論社</dd>

    <dt>役職:</dt>
    <dd>WEBディレクター</dd>

    <dt>得意:</dt>
    <dd>jQuery</dd>
</dl>

今回は実装を順をおいながら見ていくことにしましょう。

テキストをクリック後にinput要素へ

まずはテキストをクリックしたら、input要素を表示し、そのinputのvalueに最初のテキストの内容を入れる部分です。

その部分のみを実装したサンプルとソースコードを見てみましょう。

テキストをクリック後にinput要素を表示するソースコード
jQuery(function($){
    $('dd').click(function(){
        var txt = $(this).text();
        $(this).html('<input type="text" value="'+txt+'" />');
    });
});

DD要素のテキストを変数に入れ、.html()メソッドを利用してテキストとinput要素を置き換えます。.htmlメソッドを使うことで、DD要素の中身が置き換えられるため、最初のテキストを消したりなどの作業は不要になります。

問題点

しかし、このままでは問題があります。このサンプルを実際に試してみて下さい。input要素になるところまではうまくいくと思います。その後、編集をしようとinputをクリックすると、なんと最初に入っていた文字が消えてしまいます。

これはなぜか?

この原因は、DD要素のテキストをinputのvalueに入れている部分にあります。inputが表示されたあとでも、DD要素のテキストを見に行き、それをinput内のvalueに入れ表示させているからです。inputに置き換わった後は、テキストが無いので、変数にはテキストが何も入っていない状態が代入され、それがvalueになって再度inputごと再度置き換わってしまうのです。

これを回避するための策を次に実装してみます。

編集中のDD要素にclassを持たせる

先ほどの問題を回避するために、今回は編集中のDD要素、つまりinput要素に置き換わった後のDD要素に「on」というclassを持たせて、そのclassを持っている間は新たにinputで置き換わらないようにします。

編集中のDD要素にclassを持たせるソースコード
jQuery(function($){
    $('dd').click(function(){
        if(!$(this).hasClass('on')){
            $(this).addClass('on');
            var txt = $(this).text();
            $(this).html('<input type="text" value="'+txt+'" />');
        }
    });
});

classを持っているかどうかは.hasClassメソッドを使い、もし、それを持っていなかったら実行するようになっています。

//classでonを持っているかチェック
if(!$(this).hasClass('on')){
    //classでonを持っていなかったら実行
};

$(this).hasClass('on')の前に「!」をつけることで、否定表現にしているため、$(this).hasClass('on')がtrueではなかったら・・・というコードになります。

これで、inputの中身が消えなくなりました。

focusが外れたらテキストに戻る

先ほどの問題を回避するために、今回は編集中のDD要素、つまりinput要素に置き換わった後のDD要素に「on」というclassを持たせて、そのclassを持っている間は新たにinputで置き換わらないようにします。

focusが外れたらテキストに戻るソースコード
jQuery(function($){
    $('dd').click(function(){
        if(!$(this).hasClass('on')){
            $(this).addClass('on');
            var txt = $(this).text();
            $(this).html('<input type="text" value="'+txt+'" />');
            $('dd > input').focus().blur(function(){
                var inputVal = $(this).val();
                $(this).parent().removeClass('on').text(inputVal);
            });
        }
    });
});

inputに対して、最初に.focus()メソッドが入っているのですが、これはinputが表示されたらすぐにその編集部分にフォーカスするようにしているためです。

そしてフォーカスが外れたら、DD要素から「on」のclassを外し、編集後の値を.text()メソッドを使い、改めてDD要素の中身を置き換えます。

ここまででほぼ完成なのですが、全部消した場合(inputを空白にした場合⁠⁠、元に戻す機能も加えます。

空白なら元の内容に戻すソースコード
if(inputVal===''){
    inputVal = this.defaultValue;
};

「this.defaultValue」はinputに元々入っていたvalue値になります。今回の場合、inputが表示された時点のvalue値が再度表示されます。

これでとりあえず、予定していた仕組みは全て実装できました。

おまけ1:初期の値に戻す

ここからは今回の仕組みにちょっとした機能を盛り込むおまけです。

inputで値を変化させた後でも、元の内容に復元したいなんてことも想定されると思います。その場合、あらかじめ最初のテキスト内容をどこかに保持しておき、リセットを押した時に、その保持していた内容でもとに戻すということを組み込んでみます。

リセット機能を組み込んだソースコード
jQuery(function($){
    //一つ一つの内容を保持するためeachを利用
    $('dd').each(function(){
        var backup = $(this).text();
        //.data()を利用して最初の内容を残しておく
        $(this).data('backup',backup)
            .click(function(){
            if(!$(this).hasClass('on')){
                $(this).addClass('on');
                var txt = $(this).text();
                $(this).html('<input type="text" value="'+txt+'" />');
                $('dd > input').focus().blur(function(){
                    var inputVal = $(this).val();
                    if(inputVal===''){
                        inputVal = this.defaultValue;
                    };
                    $(this).parent().removeClass('on').text(inputVal);
                });
            };
        });
    });
    //リセットボタンをクリックで最初の状態に戻す機能
    $('button').click(function(){;
        $('dd').each(function(){
            var backup = $(this).data('backup');
            $(this).text(backup);
        });
    });
});

個々のテキストを.data()で保持させておくため、.each()を利用します。また、最初に.data()で最初のテキスト内容をそれぞれのDD要素に保持させ、リセットを押した時に、その保持した内容で元に戻す機能にしました。

これで、入力内容を間違えたりしたときに、元に戻せることができるようになります。このような仕組みを入れておくことで、編集する人もミスを心配することなく、編集をすることができるようになります。

おまけ2:内容に変更があった時だけリセット

先ほどのおまけ1で実装したリセット機能は、最初の状態から変化したときにだけクリックできるようにしておくこともできます。今回の最後に、それを実装してみましょう。

変更があった時だけリセットできるソースコード
jQuery(function($){
    $('dd').each(function(){
        var backup = $(this).text();
        $(this).data('backup',backup)
            .click(function(){
            if(!$(this).hasClass('on')){
                $(this).addClass('on');
                var txt = $(this).text();
                $(this).html('<input type="text" value="'+txt+'" />');
                $('dd > input').focus().blur(function(){
                    var inputVal = $(this).val();
                    var backup = $(this).parent().data('backup');
                    if(inputVal===''){
                        inputVal = this.defaultValue;
                    };
                    $(this).parent().removeClass('on').text(inputVal);
                    //.data()の内容と比較し変化したていたらクリックできるように
                    if(backup !== inputVal){
                        $('button').removeAttr('disabled');
                    };
                });
            };
        });
    });
    //リセットボタンは最初はクリックできないようにdisabled状態に
    $('button').attr('disabled','disabled')
        .click(function(){;
        $('dd').each(function(){
            var backup = $(this).data('backup');
            $(this).text(backup);
        });
        //リセットした際は再びクリックできないように変更
        $(this).attr('disabled','disabled');
    });
});

変更内容を、最初に保持させた.data()と比較し、相違があればリセットボタンがクリックできるように.removeAttr()でdisabledの属性を削除します。また、一度リセットした後は再びクリックできないようにdisabled属性をつけます。

これで、リセットも必要な時だけ機能するようにできました。

おまけも以上です。

今回ご紹介した以外にも、つけておくといい機能もありますので、実装前にきちんとユーザーの事を考えてUIを実装するようにして下さい。

おすすめ記事

記事・ニュース一覧