もなかアイスの試食品

「とりあえずやってみたい」そんな気持ちが先走りすぎて挫折が多い私のメモ書きみたいなものです.

【Javascript】select要素の開いた/閉じたイベントを実装した話

はじめに

前回の記事の続き

monakaice88.hatenablog.com

前回の話は、セレクトボックスを選択した時(値が決まった時)にモーダルを表示するという機能を作成したけれども、

onchangeイベントを使っていたため、同じ値を選択したときにモーダルが表示されない問題点があった。

調べてみてもあまりコレだ!とくる内容のサイト・記事が見つからなかった

なので、セレクトボックスに「開かれた」、「閉じた」、「値が変わらなかった」という独自イベントを作成した話

実装

動作するイベントを調べた結果、「開くとき」に発生するイベントは「click」で「閉じるとき」に発生するイベントは「click blur keyup」ということがわかった(Chromeで調査)

今回はjQueryとAngularJSを使って実装

HTML

<div ng-app="App" ng-controller="Sample">
  <select ng-model="Sample.selectValue" ng-select-event expanded="Sample.onOpened()" unexpanded="Sample.onClosed()"  nonchanged="Sample.onNonChanged()" ng-change="Sample.onChanged()">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
  </select>
  {{Sample.selectValue}}
</div>

JavascriptjQuery、AngularJS)

(function(window, jQuery, angular) {
  'use strict';
  
  var module = angular.module('App', []);
  
  module.controller('Sample', [
    '$scope',
    function($scope) {
      $scope.Sample = this;
      
      this.selectValue = '1';
    
      this.onOpened = function() {
        window.console.log('おーぷん');
      };
      
      this.onClosed = function() {
        window.console.log('しまった');
      };
      
      this.onChanged = function() {
        window.console.log('変わった');
      };
      
      this.onNonChanged = function() {
        window.console.log('変わってない');
      };
    }
  ]);
  
  /**
   * 普通のselect要素では、「リストボックスが開かれた」、「リストボックスが閉じた」、
   * 「値が変わらなかった」等のイベントが無いため作成
   * 
   * 以下のイベントを発生させる
   *   ・リストボックスが開かれた
   *   ・リストボックスが閉じた
   *   ・値が変わらなかった
   */
  module.directive('ngSelectEvent', [
    '$timeout',
    function($timeout) {
      return {
        restrict: 'A',
        scope: {
          expanded: '&',
          unexpanded: '&',
          nonchanged: '&'
        },
        link: function(scope, element, atts, ctrl) {
          var jQueryElement = jQuery(element);
          
          var WATCH_EXPAND_EVENTS = 'click';
          var WATCH_UNEXPAND_EVENTS = 'click blur keyup';
          
          jQueryElement.data('expanded', false);

          var __expanding = function() {
            jQueryElement.off(WATCH_EXPAND_EVENTS, __expanding);
            jQueryElement.data('expanded', true);
            jQueryElement.data('oldvalue', jQueryElement.val());
            jQueryElement.on(WATCH_UNEXPAND_EVENTS, __unexpanding);

            (scope.expanded)();
          };

          var __unexpanding = function() {
            jQueryElement.off(WATCH_UNEXPAND_EVENTS, __unexpanding);
            jQueryElement.data('expanded', false);
            jQueryElement.on(WATCH_EXPAND_EVENTS, __expanding);

            (scope.unexpanded)();
            
            var oldValue = jQueryElement.data('oldvalue');
            $timeout(function() {
              var newValue = jQueryElement.val();
              if (oldValue == newValue) {
                (scope.nonchanged)();
              }
            }, 0);
          };

          jQueryElement.on(WATCH_EXPAND_EVENTS, __expanding);
        }
      };
    }
  ]);
  
})(window, window.jQuery, window.angular);

今後の自分のためにも、AngularJSを使用しないソースコードも作ろうと思ったけど面倒くさくなったでござる

問題点?

見直しでjsfiddleで動作確認したところ、「ESC」キーを押したときの挙動がイマイチかも

「ESC」キー押してもイベントが走るように、「keyup」イベントを監視するようにしているけれども、2回押さないと反応してくれない・・・

他のキー(数字の1とか)を押した後だと、1回でイベントが呼ばれる感じ

keyupイベントは別処理にして、押されたキーのチェックをしたほうが良いかもしれない