【问题标题】:jquery plugin multiple instancesjquery插件多个实例
【发布时间】:2015-06-17 22:57:24
【问题描述】:

我正在尝试创建一个简单的 jQuery 插件,它允许“时间选择器”的多个实例。过去我没有做过太多 JavaScript OOP,所以我认为创建它对我来说将是一次极好的学习体验。话虽如此,我似乎无法弄清楚为什么当我更改时间时所有实例都会受到影响。这是我在 StackOverflow 上的第一篇文章,所以请多多包涵。 代码如下:

(function($) {

  //Helper functions 
  if (typeof String.prototype.endsWith != 'function') {
    String.prototype.endsWith = function(str) {
      return str.length > 0 && this.substring(this.length - str.length, this.length) === str;
    }
  }

  //Find if area is on the clickable list
  var findOne = function(haystack, arr) {
    return arr.some(function(v) {
      return haystack.indexOf(v) >= 0;
    });
  };

  var Timepicker = function(element, options) {

    this.defaults = {
      now: new Date()
    };
    this.element = $(element);
    this.createTimepicker();
    this.options = $.extend({}, this.defaults, options);
    this.timepicker = $('.wicked-picker'); //The outer portion of the picker
    this.up = $('.wicked-picker__controls__control-up'); //the up control(s)
    this.down = $('.wicked-picker__controls__control-down'); //the down control(s)
    this.hoursElem = $('.wicked-picker__controls__control--hours'); //the hours text
    this.minutesElem = $('.wicked-picker__controls__control--minutes'); //the minutes text
    this.meridiemElem = $('.wicked-picker__controls__control--meridiem'); //the am or pm text
    this.canClick = ['timepicker', this.timepicker.selector.substring(1), this.up.selector.substring(1), this.down.selector.substring(1), this.hoursElem.selector.substring(1), this.minutesElem.selector.substring(1), this.meridiemElem.selector.substring(1)]; //the clickable areas
    this.selectedHour = ((this.defaults.now.getHours() + 11) % 12) + 1; //the default hour
    this.selectedMin = ((this.defaults.now.getMinutes() < 10) ? '0' : '') + this.defaults.now.getMinutes(); //the default minute
    this.selectedMeridiem = (this.defaults.now.getHours > 12) ? 'PM' : 'AM'; //the defaut meridiem
    this.attach(element); //attach events to this element

  };

  $.extend(Timepicker.prototype = {

    showTimepicker: function(element) {
      var timepickerPos = this.element.offset();
      //set time to default time (now)
      this.setText(element);
      //if the timepicker's time differs from the input field's time change it
      if (this.getText(element) !== this.getTime()) {
        var inputTime = this.getText(element).replace(':', '').split(' ');
        var newTime = new Date();
        newTime.setHours(inputTime[0]);
        newTime.setMinutes(inputTime[2]);
        this.setTime(newTime);
      }
      //Positioning
      this.timepicker.css({
        'z-index': this.element.zIndex() + 1,
        position: 'absolute',
        left: timepickerPos.left,
        top: timepickerPos.top + element.target.offsetHeight + 5
      }).show();

      //Time up/down events
      //Most likely the area with issues
      //Needs to know which instance
      $(this.up).on('click', $.proxy(this.changeValue, this, '+', element));
      $(this.down).on('click', $.proxy(this.changeValue, this, '-', element));
    },

    hideTimepicker: function(element) {
      var targetClass = element.target.className.split(' ');
      //Check if area is clickable before hiding
      if (findOne(targetClass, this.canClick) === false) {
        this.timepicker.hide();
      }
    },

    //Create only one timepicker per page
    createTimepicker: function() {
      if ($('.wicked-picker').length === 0)
        $('body').append('<div class="wicked-picker"> <p class="wicked-picker__title">Timepicker</p> <ul class="wicked-picker__controls"> <li class="wicked-picker__controls__control"> <span class="wicked-picker__controls__control-up"></span><span class="wicked-picker__controls__control--hours">00</span><span class="wicked-picker__controls__control-down"></span> </li> <li class="wicked-picker__controls__control"> <span class="wicked-picker__controls__control-up"></span><span class="wicked-picker__controls__control--minutes">00</span><span class="wicked-picker__controls__control-down"></span> </li> <li class="wicked-picker__controls__control"> <span class="wicked-picker__controls__control-up"></span><span class="wicked-picker__controls__control--meridiem">AM</span><span class="wicked-picker__controls__control-down"></span> </li> </ul> </div>');
    },

    //Attach the show and hide picker events
    attach: function(element) {
      $(element).on('focus', $.proxy(this.showTimepicker, this));
      $('body').on('click', $.proxy(this.hideTimepicker, this));
    },

    //set the timepicker's time 
    setTime: function(time) {
      this.setHours(time.getHours());
      this.setMinutes(time.getMinutes());
      this.setMeridiem();
    },

    //get the timepicker's time in the form H : MM : AM || PM
    getTime: function() {
      return [this.getHours + ' : ' + this.getMinutes() + ' ' + this.getMeridiem()];
    },

    //set the timepicker's and input field's hours
    setHours: function(hours) {
      var hour = new Date();
      hour.setHours(hours);
      var hoursText = ((hour.getHours() + 11) % 12) + 1;
      this.hoursElem.text(hoursText);
      this.selectedHour = hoursText;
    },

    //set the timepicker's hours
    getHours: function() {
      var hours = new Date();
      hours.setHours(this.hoursElem.text());
      return hours.getHours();
    },

    //set the timepicker's and input field's minutes
    setMinutes: function(minutes) {
      var minute = new Date();
      minute.setMinutes(minutes);
      var minutesText = minute.getMinutes();
      var min = ((minutesText < 10) ? '0' : '') + minutesText;
      this.minutesElem.text(min);
      this.selectedMin = min;
    },

    //set the timepicker's minutes
    getMinutes: function() {
      var minutes = new Date();
      minutes.setMinutes(this.minutesElem.text());
      var minutesText = minutes.getMinutes();
      return ((minutesText < 10) ? '0' : '') + minutesText;
    },

    //set the timepicker's and input field's meridiem
    setMeridiem: function() {
      var meridiem = this.getMeridiem();
      var newMeridiem = (meridiem === 'PM') ? 'AM' : 'PM';
      this.meridiemElem.text(newMeridiem);
      this.selectedMeridiem = newMeridiem;
    },

    //set the timepicker's meridiem
    getMeridiem: function() {
      return this.meridiemElem.text();
    },

    //change the input field's time based on the arrow selected for each time unit
    //input is the input field to be changed
    //element is the up or down arrow clicked
    //operator is the '+' or '-' sign
    changeValue: function(operator, input, element) {
      var target = (operator === '+') ? element.target.nextSibling : element.target.previousSibling;
      var targetClass = $(target).attr('class');
      if (targetClass.endsWith('hours')) {
        this.setHours(eval(this.getHours() + operator + 1));
      } else if (targetClass.endsWith('minutes')) {
        this.setMinutes(eval(this.getMinutes() + operator + 1));
      } else {
        this.setMeridiem();
      }
      console.log('changed ' + $(input.target).attr('name'));
      this.setText(input);
    },


    //Set the input field's time
    setText: function(input) {
      console.log('set ' + $(input.target).attr('name') + ' to ' + this.selectedHour + ' : ' + this.selectedMin + ' ' + this.selectedMeridiem);
      $(input.target).val(this.selectedHour + ' : ' + this.selectedMin + ' ' + this.selectedMeridiem);
    },

    //Get the input field's time
    getText: function(input) {
      return $(input.target).val();
    }


  });

  //Create timepickers
  $.fn.timepicker = function(options) {
    return this.each(function() {
      new Timepicker(this, options);
    });
  };

}(jQuery));
<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <input type="text" name="event-start-time" id="event-start-time" class="form-input timepicker grid-5" />
  <input type="text" name="event-end-time" id="event-end-time" class="form-input timepicker grid-5" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script>
    $('.timepicker').timepicker({});
  </script>
</body>

</html>

【问题讨论】:

  • 改成$('.timepicker').timepicker();还能用吗?您传入的是一个空数组而不是一个对象。不知道这是否重要,但仍然很奇怪。
  • 对不起,在我的实际代码中它有一个空对象。它仅代表未来选项的可能列表。换句话说,是的,它仍然可以。
  • "//Create only one timepicker per page" 并不完全像允许多个实例。您的所有 Timepicker 实例都引用相同的 DOM 元素。这是预期的吗?
  • @Bergi 当我说单个时间选择器时,我的意思是我只想要一个生成多个输入可以使用的单个选择器。有点像如何在多个输入上使用相同的 jQuery UI 日期选择器日历
  • @EricGagnon:是的,但是您的TimePickers 都将它们的事件处理程序附加到同一个 DOM 元素上,因此一个事件会影响所有输入。您需要有一个静态变量来告诉时间选择器当前是否以及在哪个输入上处于活动状态。

标签: javascript jquery oop


【解决方案1】:

我能够通过删除以前的 up 和 down 事件 click 事件处理程序,然后重新应用新的 click 事件处理程序来解决问题。这是通过改变来实现的

$(this.up).on('click', $.proxy(this.changeValue, this, '+', element));
$(this.down).on('click', $.proxy(this.changeValue, this, '-', element));

$(this.up).off('click').on('click', $.proxy(this.changeValue, this, '+', element));

$(this.down).off('click').on('click', $.proxy(this.changeValue, this, '-', element));

感谢所有建议!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多