如何为SlickGrid下拉单元格编辑器动态添值并复刻jQuery UI Combobox?
Alright, let's build that jQuery UI Combobox with dynamic option addition—exactly matching Excel's handy Alt+Down Arrow behavior for spreadsheet columns! You mentioned you already have a selection cell editor set up, so we'll extend that to support adding new values that show up in subsequent cells of the same column. Here's how to do it step by step:
First, we need to move away from static comma-separated strings to a column-specific array. This lets us easily add new values and share them across all cells in the same column:
// Initialize a map where keys are your column IDs, values are option arrays const columnOptions = { "target-column-id": ["Option1", "Option2", "Option3"] };
Replace "target-column-id" with the actual identifier you use for your editable column (like a data-column-id attribute on table cells).
We'll modify the core combobox widget to support inputting new values and updating our shared option array. This is where the magic happens:
$.widget("custom.dynamicCombobox", { _create: function() { this.wrapper = $("<span>") .addClass("dynamic-combobox") .insertAfter(this.element); this.element.hide(); this._createAutocomplete(); this._createDropdownButton(); }, _createAutocomplete: function() { const self = this; const select = this.element; const columnId = select.closest("td").data("column-id"); // Grab current column ID this.input = $("<input>") .appendTo(this.wrapper) .attr("title", "") .addClass("dynamic-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left") .autocomplete({ delay: 0, minLength: 0, source: function(request, response) { const matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); // Match existing options, plus the new input if it's not already in the list const matchedOptions = $.grep(columnOptions[columnId], item => matcher.test(item)); const newOptionCandidate = request.term.trim() && !matchedOptions.some(opt => opt.toLowerCase() === request.term.toLowerCase()) ? [request.term] : []; response(matchedOptions.concat(newOptionCandidate)); }, select: function(event, ui) { // Add the selected item to the select if it's a new value if (!select.find(`option[value="${ui.item.value}"]`).length) { select.append($("<option>").val(ui.item.value).text(ui.item.label)); columnOptions[columnId].push(ui.item.value); } select.val(ui.item.value); self._trigger("select", event, { item: ui.item.option }); return false; }, change: function(event, ui) { // Handle cases where user enters a value without selecting from autocomplete const newVal = $(this).val().trim(); if (!ui.item && newVal) { if (!columnOptions[columnId].includes(newVal)) { columnOptions[columnId].push(newVal); select.append($("<option>").val(newVal).text(newVal)); } select.val(newVal); } else if (!newVal) { select.val(""); } } }); // Highlight new values in the autocomplete dropdown this.input.data("ui-autocomplete")._renderItem = function(ul, item) { const isNew = !columnOptions[columnId].includes(item.label); return $("<li>") .append(`<div>${item.label}${isNew ? " (new)" : ""}</div>`) .appendTo(ul); }; }, _createDropdownButton: function() { const self = this; const input = this.input; let wasOpen = false; $("<a>") .attr("tabIndex", -1) .attr("title", "Show all options") .tooltip() .appendTo(this.wrapper) .button({ icons: { primary: "ui-icon-triangle-1-s" }, text: false }) .removeClass("ui-corner-all") .addClass("dynamic-combobox-toggle ui-corner-right") .on("mousedown", () => wasOpen = input.autocomplete("widget").is(":visible")) .on("click", () => { input.trigger("focus"); wasOpen ? input.autocomplete("close") : input.autocomplete("search", ""); }); }, _destroy: function() { this.wrapper.remove(); this.element.show(); } });
Update your cell initialization code to use our new dynamic combobox, and ensure each cell pulls the latest options from the columnOptions array:
// Example: Initialize editor for a table cell (adjust to match your existing code) function initEditableCell(td) { const columnId = td.data("column-id"); // Build select with current column options const select = $("<select>") .appendTo(td.empty()) .append(columnOptions[columnId].map(opt => `<option value="${opt}">${opt}</option>`).join("")); // Initialize our dynamic combobox select.dynamicCombobox(); // Handle editor cleanup when done (e.g., on cell blur) td.on("blur", () => { td.text(select.val()); select.dynamicCombobox("destroy"); }); }
Add this CSS to make the combobox look more familiar to Excel users:
.dynamic-combobox { position: relative; display: inline-block; width: 100%; } .dynamic-combobox-toggle { position: absolute; top: 0; right: 0; bottom: 0; margin: 0; padding: 0 8px; } .dynamic-combobox-input { width: calc(100% - 30px); margin: 0; padding: 4px 8px; box-sizing: border-box; }
- Shared Dynamic Options: New values added in one cell automatically appear in all subsequent cells of the same column
- Excel-like Workflow: Click the dropdown button to see all options (just like Alt+Down Arrow)
- Smart Autocomplete: Suggests existing options and highlights new values you're about to add
- Seamless Integration: Works with your existing cell editor setup
内容的提问来源于stack exchange,提问作者Chapo

