将小部件添加到现有的jQuery-UI库

时间:2019-06-28 08:51:47

标签: jquery-ui widget

我必须扩展一个使用1.10.4版本的现有项目,并且此jQuery-UI版本的构建没有任何小部件。 现在,我要使用“ Spinner”和“ Selectmenu”小部件。

对于“ Spinner”小部件:jQuery 1.10.4版可用。 可从versoin 1.11.4中获得“ Selectmenu”小工具。

不幸的是,我无法更新jQuery-UI的当前版本,所以我想知道如何/是否有可能使用这两个小部件来增强当前版本。

1 个答案:

答案 0 :(得分:0)

使用$.widget()可以扩展或创建自己的自定义窗口小部件。我从https://code.jquery.com/ui/1.11.4/jquery-ui.js收集了这段代码,由于$.ui.menu存在于1.10.4中,因此大多数情况下都有效。必须进行一些调整。

我从https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css

收集了样式

$(function() {
  $.widget("custom.selectmenu", {
    version: "1.11.4",
    defaultElement: "<select>",
    options: {
      appendTo: null,
      disabled: null,
      icons: {
        button: "ui-icon-triangle-1-s"
      },
      position: {
        my: "left top",
        at: "left bottom",
        collision: "none"
      },
      width: null,

      // callbacks
      change: null,
      close: null,
      focus: null,
      open: null,
      select: null
    },

    _create: function() {
      var selectmenuId = this.element.uniqueId().attr("id");
      this.ids = {
        element: selectmenuId,
        button: selectmenuId + "-button",
        menu: selectmenuId + "-menu"
      };

      this._drawButton();
      this._drawMenu();

      if (this.options.disabled) {
        this.disable();
      }
    },

    _drawButton: function() {
      var that = this;

      // Associate existing label with the new button
      this.label = $("label[for='" + this.ids.element + "']").attr("for", this.ids.button);
      this._on(this.label, {
        click: function(event) {
          this.button.focus();
          event.preventDefault();
        }
      });

      // Hide original select element
      this.element.hide();

      // Create button
      this.button = $("<span>", {
          "class": "ui-selectmenu-button ui-widget ui-state-default ui-corner-all",
          tabindex: this.options.disabled ? -1 : 0,
          id: this.ids.button,
          role: "combobox",
          "aria-expanded": "false",
          "aria-autocomplete": "list",
          "aria-owns": this.ids.menu,
          "aria-haspopup": "true"
        })
        .insertAfter(this.element);

      $("<span>", {
          "class": "ui-icon " + this.options.icons.button
        })
        .prependTo(this.button);

      this.buttonText = $("<span>", {
          "class": "ui-selectmenu-text"
        })
        .appendTo(this.button);

      this._setText(this.buttonText, this.element.find("option:selected").text());
      this._resizeButton();

      this._on(this.button, this._buttonEvents);
      this.button.one("focusin", function() {

        // Delay rendering the menu items until the button receives focus.
        // The menu may have already been rendered via a programmatic open.
        if (!that.menuItems) {
          that._refreshMenu();
        }
      });
      this._hoverable(this.button);
      this._focusable(this.button);
    },

    _drawMenu: function() {
      var that = this;

      // Create menu
      this.menu = $("<ul>", {
        "aria-hidden": "true",
        "aria-labelledby": this.ids.button,
        id: this.ids.menu
      });

      // Wrap menu
      this.menuWrap = $("<div>", {
          "class": "ui-selectmenu-menu ui-front"
        })
        .append(this.menu)
        .appendTo(this._appendTo());

      // Initialize menu widget
      this.menuInstance = this.menu
        .menu({
          role: "listbox",
          select: function(event, ui) {
            event.preventDefault();

            // support: IE8
            // If the item was selected via a click, the text selection
            // will be destroyed in IE
            that._setSelection();

            that._select(ui.item.data("ui-selectmenu-item"), event);
          },
          focus: function(event, ui) {
            var item = ui.item.data("ui-selectmenu-item");

            // Prevent inital focus from firing and check if its a newly focused item
            if (that.focusIndex != null && item.index !== that.focusIndex) {
              that._trigger("focus", event, {
                item: item
              });
              if (!that.isOpen) {
                that._select(item, event);
              }
            }
            that.focusIndex = item.index;

            that.button.attr("aria-activedescendant",
              that.menuItems.eq(item.index).attr("id"));
          }
        })
        .data("ui-menu");

      // Adjust menu styles to dropdown
      this.menu
        .addClass("ui-corner-bottom")
        .removeClass("ui-corner-all");

      // Don't close the menu on mouseleave
      this.menuInstance._off(this.menu, "mouseleave");

      // Cancel the menu's collapseAll on document click
      this.menuInstance._closeOnDocumentClick = function() {
        return false;
      };

      // Selects often contain empty items, but never contain dividers
      this.menuInstance._isDivider = function() {
        return false;
      };
    },

    refresh: function() {
      this._refreshMenu();
      this._setText(this.buttonText, this._getSelectedItem().text());
      if (!this.options.width) {
        this._resizeButton();
      }
    },

    _refreshMenu: function() {
      this.menu.empty();

      var item,
        options = this.element.find("option");

      if (!options.length) {
        return;
      }

      this._parseOptions(options);
      this._renderMenu(this.menu, this.items);

      this.menuInstance.refresh();
      this.menuItems = this.menu.find("li").not(".ui-selectmenu-optgroup");

      item = this._getSelectedItem();

      // Update the menu to have the correct item focused
      this.menuInstance.focus(null, item);
      this._setAria(item.data("ui-selectmenu-item"));

      // Set disabled state
      this._setOption("disabled", this.element.prop("disabled"));
    },

    open: function(event) {
      if (this.options.disabled) {
        return;
      }

      // If this is the first time the menu is being opened, render the items
      if (!this.menuItems) {
        this._refreshMenu();
      } else {

        // Menu clears focus on close, reset focus to selected item
        this.menu.find(".ui-state-focus").removeClass("ui-state-focus");
        this.menuInstance.focus(null, this._getSelectedItem());
      }

      this.isOpen = true;
      this._toggleAttr();
      this._resizeMenu();
      this._position();

      this._on(this.document, this._documentClick);

      this._trigger("open", event);
    },

    _position: function() {
      this.menuWrap.position($.extend({ of: this.button
      }, this.options.position));
    },

    close: function(event) {
      if (!this.isOpen) {
        return;
      }

      this.isOpen = false;
      this._toggleAttr();

      this.range = null;
      this._off(this.document);

      this._trigger("close", event);
    },

    widget: function() {
      return this.button;
    },

    menuWidget: function() {
      return this.menu;
    },

    _renderMenu: function(ul, items) {
      var that = this,
        currentOptgroup = "";

      $.each(items, function(index, item) {
        if (item.optgroup !== currentOptgroup) {
          $("<li>", {
              "class": "ui-selectmenu-optgroup ui-menu-divider" +
                (item.element.parent("optgroup").prop("disabled") ?
                  " ui-state-disabled" :
                  ""),
              text: item.optgroup
            })
            .appendTo(ul);

          currentOptgroup = item.optgroup;
        }

        that._renderItemData(ul, item);
      });
    },

    _renderItemData: function(ul, item) {
      return this._renderItem(ul, item).data("ui-selectmenu-item", item);
    },

    _renderItem: function(ul, item) {
      var li = $("<li>");

      if (item.disabled) {
        li.addClass("ui-state-disabled");
      }
      this._setText(li, item.label);

      return li.appendTo(ul);
    },

    _setText: function(element, value) {
      if (value) {
        element.text(value);
      } else {
        element.html("&#160;");
      }
    },

    _move: function(direction, event) {
      var item, next,
        filter = ".ui-menu-item";

      if (this.isOpen) {
        item = this.menuItems.eq(this.focusIndex);
      } else {
        item = this.menuItems.eq(this.element[0].selectedIndex);
        filter += ":not(.ui-state-disabled)";
      }

      if (direction === "first" || direction === "last") {
        next = item[direction === "first" ? "prevAll" : "nextAll"](filter).eq(-1);
      } else {
        next = item[direction + "All"](filter).eq(0);
      }

      if (next.length) {
        this.menuInstance.focus(event, next);
      }
    },

    _getSelectedItem: function() {
      return this.menuItems.eq(this.element[0].selectedIndex);
    },

    _toggle: function(event) {
      this[this.isOpen ? "close" : "open"](event);
    },

    _setSelection: function() {
      var selection;

      if (!this.range) {
        return;
      }

      if (window.getSelection) {
        selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(this.range);

        // support: IE8
      } else {
        this.range.select();
      }

      // support: IE
      // Setting the text selection kills the button focus in IE, but
      // restoring the focus doesn't kill the selection.
      this.button.focus();
    },

    _documentClick: {
      mousedown: function(event) {
        if (!this.isOpen) {
          return;
        }

        if (!$(event.target).closest(".ui-selectmenu-menu, #" + this.ids.button).length) {
          this.close(event);
        }
      }
    },

    _buttonEvents: {

      // Prevent text selection from being reset when interacting with the selectmenu (#10144)
      mousedown: function() {
        var selection;

        if (window.getSelection) {
          selection = window.getSelection();
          if (selection.rangeCount) {
            this.range = selection.getRangeAt(0);
          }

          // support: IE8
        } else {
          this.range = document.selection.createRange();
        }
      },

      click: function(event) {
        this._setSelection();
        this._toggle(event);
      },

      keydown: function(event) {
        var preventDefault = true;
        switch (event.keyCode) {
          case $.ui.keyCode.TAB:
          case $.ui.keyCode.ESCAPE:
            this.close(event);
            preventDefault = false;
            break;
          case $.ui.keyCode.ENTER:
            if (this.isOpen) {
              this._selectFocusedItem(event);
            }
            break;
          case $.ui.keyCode.UP:
            if (event.altKey) {
              this._toggle(event);
            } else {
              this._move("prev", event);
            }
            break;
          case $.ui.keyCode.DOWN:
            if (event.altKey) {
              this._toggle(event);
            } else {
              this._move("next", event);
            }
            break;
          case $.ui.keyCode.SPACE:
            if (this.isOpen) {
              this._selectFocusedItem(event);
            } else {
              this._toggle(event);
            }
            break;
          case $.ui.keyCode.LEFT:
            this._move("prev", event);
            break;
          case $.ui.keyCode.RIGHT:
            this._move("next", event);
            break;
          case $.ui.keyCode.HOME:
          case $.ui.keyCode.PAGE_UP:
            this._move("first", event);
            break;
          case $.ui.keyCode.END:
          case $.ui.keyCode.PAGE_DOWN:
            this._move("last", event);
            break;
          default:
            this.menu.trigger(event);
            preventDefault = false;
        }

        if (preventDefault) {
          event.preventDefault();
        }
      }
    },

    _selectFocusedItem: function(event) {
      var item = this.menuItems.eq(this.focusIndex);
      if (!item.hasClass("ui-state-disabled")) {
        this._select(item.data("ui-selectmenu-item"), event);
      }
    },

    _select: function(item, event) {
      var oldIndex = this.element[0].selectedIndex;

      // Change native select element
      this.element[0].selectedIndex = item.index;
      this._setText(this.buttonText, item.label);
      this._setAria(item);
      this._trigger("select", event, {
        item: item
      });

      if (item.index !== oldIndex) {
        this._trigger("change", event, {
          item: item
        });
      }

      this.close(event);
    },

    _setAria: function(item) {
      var id = this.menuItems.eq(item.index).attr("id");

      this.button.attr({
        "aria-labelledby": id,
        "aria-activedescendant": id
      });
      this.menu.attr("aria-activedescendant", id);
    },

    _setOption: function(key, value) {
      if (key === "icons") {
        this.button.find("span.ui-icon")
          .removeClass(this.options.icons.button)
          .addClass(value.button);
      }

      this._super(key, value);

      if (key === "appendTo") {
        this.menuWrap.appendTo(this._appendTo());
      }

      if (key === "disabled") {
        this.menuInstance.option("disabled", value);
        this.button
          .toggleClass("ui-state-disabled", value)
          .attr("aria-disabled", value);

        this.element.prop("disabled", value);
        if (value) {
          this.button.attr("tabindex", -1);
          this.close();
        } else {
          this.button.attr("tabindex", 0);
        }
      }

      if (key === "width") {
        this._resizeButton();
      }
    },

    _appendTo: function() {
      var element = this.options.appendTo;

      if (element) {
        element = element.jquery || element.nodeType ?
          $(element) :
          this.document.find(element).eq(0);
      }

      if (!element || !element[0]) {
        element = this.element.closest(".ui-front");
      }

      if (!element.length) {
        element = this.document[0].body;
      }

      return element;
    },

    _toggleAttr: function() {
      this.button
        .toggleClass("ui-corner-top", this.isOpen)
        .toggleClass("ui-corner-all", !this.isOpen)
        .attr("aria-expanded", this.isOpen);
      this.menuWrap.toggleClass("ui-selectmenu-open", this.isOpen);
      this.menu.attr("aria-hidden", !this.isOpen);
    },

    _resizeButton: function() {
      var width = this.options.width;

      if (!width) {
        width = this.element.show().outerWidth();
        this.element.hide();
      }

      this.button.outerWidth(width);
    },

    _resizeMenu: function() {
      this.menu.outerWidth(Math.max(
        this.button.outerWidth(),

        // support: IE10
        // IE10 wraps long text (possibly a rounding bug)
        // so we add 1px to avoid the wrapping
        this.menu.width("").outerWidth() + 1
      ));
    },

    _getCreateOptions: function() {
      return {
        disabled: this.element.prop("disabled")
      };
    },

    _parseOptions: function(options) {
      var data = [];
      options.each(function(index, item) {
        var option = $(item),
          optgroup = option.parent("optgroup");
        data.push({
          element: option,
          index: index,
          value: option.val(),
          label: option.text(),
          optgroup: optgroup.attr("label") || "",
          disabled: optgroup.prop("disabled") || option.prop("disabled")
        });
      });
      this.items = data;
    },

    _destroy: function() {
      this.menuWrap.remove();
      this.button.remove();
      this.element.show();
      this.element.removeUniqueId();
      this.label.attr("for", this.ids.element);
    }
  });
  $("#spinner").spinner();
  $("#speed").selectmenu();
});
.ui-selectmenu-menu {
  padding: 0;
  margin: 0;
  position: absolute;
  top: 0;
  left: 0;
  display: none;
}

.ui-selectmenu-menu .ui-menu {
  overflow: auto;
  /* Support: IE7 */
  overflow-x: hidden;
  padding-bottom: 1px;
}

.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
  font-size: 1em;
  font-weight: bold;
  line-height: 1.5;
  padding: 2px 0.4em;
  margin: 0.5em 0 0 0;
  height: auto;
  border: 0;
}

.ui-selectmenu-open {
  display: block;
}

.ui-selectmenu-button {
  display: inline-block;
  overflow: hidden;
  position: relative;
  text-decoration: none;
  cursor: pointer;
}

.ui-selectmenu-button span.ui-icon {
  right: 0.5em;
  left: auto;
  margin-top: -8px;
  position: absolute;
  top: 50%;
}

.ui-selectmenu-button span.ui-selectmenu-text {
  text-align: left;
  padding: 0.4em 2.1em 0.4em 1em;
  display: block;
  line-height: 1.4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.js" integrity="sha256-tp8VZ4Y9dg702r7D6ynzSavKSwB9zjariSZ4Snurvmw=" crossorigin="anonymous"></script>

<p>
  <label for="spinner">Select a value:</label>
  <input id="spinner" name="value">
</p>

<label for="speed">Select a speed</label>
<select name="speed" id="speed">
  <option>Slower</option>
  <option>Slow</option>
  <option selected="selected">Medium</option>
  <option>Fast</option>
  <option>Faster</option>
</select>

第一个问题1.11.4使用.menu('instance'),而1.10.4对此不可用。在上面的代码中,该代码用在第145行上,可以恢复为.data("ui-menu");进行补偿。

第二,选择项目时,由于未定义item,因此触发了错误。这也与上述问题有关。需要做更多的工作。进行中。