入力したパスワードを確認できるVueディレクティブ(IEもOK)

さてさて、最近私の個人的なVue開発ではゲーム、独自コンポーネント、そして独自ディレクティブの3つがメインになっているのですが、やはりコンポーネントとディレクティブは使い回しがしやすいので、作れば作るほど後の作業が楽になることを実感しています。

そして、そんな関係から今回は開発するのは、次の機能をもったVueディレクティブです。

「パスワードの中身が確認できる入力ボックス」

みなさんは、これまでウェブサイトのユーザー登録で、次のような「入力内容を確認できるパスワード入力ボックス」を見たことがあるのではないでしょうか。

この入力ボックスがあれば確認するために2度同じパスワードを入力する必要性は少なくなりますので、ユーザビリティは向上するのではないでしょうか。

とうことで、今回はこの機能を独自Vueディレクティブで実装してみます。

ぜひ参考にしてみてくださいね。

※ 開発環境: Vue 2.5

ディレクティブの概要

まず使い方は簡単で、v-show-password-inputをつけるだけでパスワード確認機能が使えるようにします。

<input type="password" v-show-password-input>

※ 当初は、Vueコンポーネントで実現しようと考えましたが、そうなるとCSSを設定しにくくなってしまうのでディレクティブに落ち着きました。

そして、この機能を実装する手順は次のとおりです。

  • 通常の入力ボックスをつくる(これとパスワード入力が切り替わる)
  • 表示切り替えをするリンク(目のマーク➜👁)をつくる
  • リンクがクリックされたらノーマル入力とパスワード入力を切り替え、さらに目のマークも「👁」と「🕶」を切り替える

ちなみに目のマークとサングラスのマークは絵文字なので、HTMLエンティティコードだけで実現します。(つまり、画像やフォント・アイコンは必要ありません)

また、汎用的に使いたいのでIE 11edgeにも対応させます。

デモを用意しました

デモページを用意しました。
ぜひ体験してみてください。

ディレクティブをつくる

では実際にディレクティブを作っていきましょう。

ディレクティブの基本形をつくる

まずはv-show-password-inputが使えるようにディレクティブを定義します。

Vue.directive('show-password-input', {
    inserted: function(el) {

        // ここにコード

    }
});

今回は新しい要素を追加するのでinserted内にコードを書いていきます。

※ なおディレクティブの定義の場合はv-は不要なので気をつけてください。

これでv-show-password-inputディレクティブが有効になりました。

パスワード入力ボックスのクローンをつくる

では、確認用の入力ボックスを作成するために、<input type="password">をクローンして、すぐとなりに挿入します。

inserted: function(el) {

    var clone = el.cloneNode();
    clone.id = '';
    clone.type = 'text';
    clone.style.display = 'none';
    clone.addEventListener('input', function(e) {

        var inputEvent = document.createEvent('Event');
        inputEvent.initEvent('input', true, false);
        el.value = e.target.value;
        el.dispatchEvent(inputEvent);

    });
    el.parentNode.insertBefore(clone, el);

}

手順は次のとおりです。

  1. cloneNode() でパスワード入力ボックスと同じものを用意する
  2. idは不要なので空白にする
  3. 入力タイプを通常の text へ変更
  4. 切替したときだけ表示するので display は none を設定する
  5. 入力があったら、パスワード<input>と同期し、 input イベントを送出 ※
  6. 最後にクローンした確認用入力ボックスをHTMLに追加

※1 ・・・ v-modelに変更を伝えることを想定してます

表示切り替えするリンクをつくる

では、表示切り替えをするための次のような目マークのリンクを作っていきましょう。

inserted: function(el) {

    // 省略

    var icons = {
        show: '&#x1F441;',
        hide: '&#x1F576;'
    };
    var a = document.createElement('a');
    a.style.position = 'absolute';
    a.style.cursor = 'pointer';
    a.style.fontSize = clone.style.fontSize;
    a.style.color = clone.style.color;
    a.textDecoration = 'none';
    a.innerHTML = icons.show;
    a.addEventListener('click', function(e) {

        if(clone.style.display === 'none') {

            e.target.innerHTML = icons.hide;
            el.style.display = 'none';
            clone.style.display = '';
            clone.value = el.value;
            clone.focus();

        } else {

            e.target.innerHTML = icons.show;
            clone.style.display = 'none';
            el.style.display = '';
            el.value = clone.value;
            el.focus();

        }

        e.preventDefault();

    });
    document.body.appendChild(a);

}

手順は次のとおりです。

  1. 「👁」と「🕶」の絵文字をHTMLエンティティで定義する
  2. <a>タグを作成
  3. 位置は絶対指定するので absolute
  4. <a>に href をつけないとカーソルが切り替わらないのでカーソルを指定
  5. 色は入力ボックスと同じ色にし、アンダーバー等を非表示にする
  6. 初期状態は目のマークを表示
  7. クリックされたらパスワードとノーマル入力、そして目とサングラスの表示切り替えをし、フォーカスを入力ボックスへ移す
  8. 最後にHTMLにリンクを挿入する

表示切り替えするリンクの位置を修正する

表示切り替えリンクのpositionabsoluteにしていますが、実際の位置はまだ指定していないので、計算して移動させます。

inserted: function(el) {

    function alignElement(target) {

        var rect = el.getBoundingClientRect();
        const clientWidth = target.clientWidth;
        const clientHeight = target.clientHeight;
        const left = rect.right - Math.round(clientWidth) - 10;
        const top = rect.top + Math.round(rect.height * 0.5) - Math.round(clientHeight * 0.5);
        target.style.left = window.pageXOffset + left +'px';
        target.style.top = window.pageYOffset + top +'px';

    }
    
    // 省略

    alignElement(a);

    window.addEventListener('resize', function() {

        alignElement(a);

    })

まず、位置を移動させるalignElement()を関数として定義します。これは、コードを見ていただくと分かるとおり、作成時とウィンドウのリサイズ時の2ヶ所で利用するからです。

alignElement()の中身は次のとおりです。

  1. getBoundingClientRect() で入力ボックスの位置を取得
  2. リンクのサイズやスクロール位置使って top と left を計算
  3. 計算した位置へ移動

これで、もしスクロール位置がずれていてもきちんとした位置に移動させることができるようになりました。

ちなみに(IE対応するために)

ちなみに今回のプログラムはIE 11edgeでも動くことを確認していますが、実はこれらのブラウザには標準でパスワードを表示する機能がついているので、もしv-show-password-input.jsを使いたい場合は以下のCSSを追加してパスワード表示機能を無効にしておいてください。

<style>

    ::-ms-clear { display: none; }
    ::-ms-reveal { display: none; }

</style>

npmパッケージを用意しました

今回のパッケージをインストールできるようにnpmへ登録しておきました。
次のコマンドを実行してください。

npm i v-show-password-input --save

おわりに

ということで今回は独自Vueディレクティブでパスワードを確認できる入力ボックスをつくってみました。

少し今回のテーマとはずれますが、開発している中で「絵文字って便利!」を痛感しました。

というのも、一昔前までだとfont-awesomeなどのフォントアイコンを使うのが主流でしたが最近は少しデザインは違うとはいえ、Chrome、FireFox、Safari、IE、Edgeなど主要なブラウザでほぼ対応していますし、デザインもemojipediaなどで確認できてとても使い勝手がいいです。

しかも使い方は、9文字のHTMLエンティティを記述するだけ。

これからの開発でもぜひ活用していこうと思いました。
こちらもみなさんの参考になれば光栄です!

ではでは〜。