jQuery UI 自定义组件

使用 Widget Factory 创建 jQuery UI 组件

问题

如何使用 jQuery UI 的 Widget Factory 创建自定义组件?

解答

jQuery UI 提供了 $.widget() 方法来创建可复用的组件,它封装了状态管理、方法调用、事件触发等通用逻辑。

基本结构

$.widget("namespace.widgetName", {
  // 默认配置
  options: {
    option1: "default"
  },

  // 组件创建时调用
  _create: function() {},

  // 组件销毁时调用
  _destroy: function() {},

  // 配置项变更时调用
  _setOption: function(key, value) {}
});

完整示例:计数器组件

// 定义计数器组件
$.widget("custom.counter", {
  // 默认配置
  options: {
    value: 0,
    min: 0,
    max: 100,
    step: 1
  },

  // 组件初始化
  _create: function() {
    // 添加样式类
    this.element.addClass("ui-counter");

    // 创建 DOM 结构
    this._buildUI();

    // 绑定事件
    this._bindEvents();

    // 设置初始值
    this._updateDisplay();
  },

  // 构建 UI
  _buildUI: function() {
    this.decrementBtn = $("<button>")
      .text("-")
      .addClass("ui-counter-btn")
      .appendTo(this.element);

    this.display = $("<span>")
      .addClass("ui-counter-display")
      .appendTo(this.element);

    this.incrementBtn = $("<button>")
      .text("+")
      .addClass("ui-counter-btn")
      .appendTo(this.element);
  },

  // 绑定事件
  _bindEvents: function() {
    var self = this;

    this.decrementBtn.on("click", function() {
      self.decrement();
    });

    this.incrementBtn.on("click", function() {
      self.increment();
    });
  },

  // 更新显示
  _updateDisplay: function() {
    this.display.text(this.options.value);
  },

  // 公共方法:增加
  increment: function() {
    var newValue = this.options.value + this.options.step;
    if (newValue <= this.options.max) {
      this._setValue(newValue);
    }
  },

  // 公共方法:减少
  decrement: function() {
    var newValue = this.options.value - this.options.step;
    if (newValue >= this.options.min) {
      this._setValue(newValue);
    }
  },

  // 设置值并触发事件
  _setValue: function(value) {
    var oldValue = this.options.value;
    this.options.value = value;
    this._updateDisplay();

    // 触发自定义事件
    this._trigger("change", null, {
      oldValue: oldValue,
      newValue: value
    });
  },

  // 处理配置项变更
  _setOption: function(key, value) {
    // 调用父类方法
    this._super(key, value);

    if (key === "value") {
      this._updateDisplay();
    }
  },

  // 组件销毁
  _destroy: function() {
    this.element
      .removeClass("ui-counter")
      .empty();
  }
});

使用组件

// 初始化
$("#myCounter").counter({
  value: 10,
  max: 50,
  step: 5
});

// 调用方法
$("#myCounter").counter("increment");
$("#myCounter").counter("decrement");

// 获取/设置配置
var value = $("#myCounter").counter("option", "value");
$("#myCounter").counter("option", "value", 20);

// 监听事件
$("#myCounter").on("counterchange", function(event, data) {
  console.log("值从", data.oldValue, "变为", data.newValue);
});

// 销毁组件
$("#myCounter").counter("destroy");

组件继承

// 继承已有组件
$.widget("custom.superCounter", $.custom.counter, {
  options: {
    showReset: true
  },

  _buildUI: function() {
    // 调用父类方法
    this._super();

    // 添加重置按钮
    if (this.options.showReset) {
      this.resetBtn = $("<button>")
        .text("重置")
        .on("click", $.proxy(this.reset, this))
        .appendTo(this.element);
    }
  },

  reset: function() {
    this._setValue(0);
  }
});

关键点

  • $.widget() 第一个参数是 namespace.name 格式,命名空间避免冲突
  • _create 是初始化入口,_destroy 负责清理
  • _ 开头的方法是私有方法,不能通过 $(el).widget("_method") 调用
  • this.element 是 jQuery 对象,指向组件绑定的 DOM 元素
  • _trigger 触发的事件名会自动加上组件名前缀(如 counterchange
  • _setOption_setOptions 用于响应配置变更,记得调用 this._super()