Five years ago a question like what I will be asking today was asked but apparently no answers were posted that would solve the problem. Now I am having the same problem. Here is what I’ve done, so far.
#cellHandlers = {
lvCell: null,
outerDiv: null,
td: null,
span: null,
input: null,
commitChanges: undefined,
beginEdit: (td, cell) => {
this.#cellHandlers.lvCell = cell;
this.#cellHandlers.td = td;
this.#cellHandlers.outerDiv = td.firstChild;
this.#cellHandlers.span = td.querySelector('span');
if (this.#cellHandlers.input == null) {
this.#cellHandlers.input = document.createElement('input');
this.#cellHandlers.input.type = 'text';
//this.#cellHandlers.input.setAttribute('autofocus', 'true');
}
this.#cellHandlers.outerDiv.remove();
this.#cellHandlers.td.appendChild(this.#cellHandlers.input);
this.#cellHandlers.td.focus();
this.#cellHandlers.input.select();
// using setTimeout is a work around. The original code was
//this.#cellHandlers.hookEvents(true);
//this.#cellHandlers.input.focus();
window.setTimeout(() => {
this.#cellHandlers.hookEvents(true);
this.#cellHandlers.input.focus();
}, 10);
//this.#cellHandlers.input.focus();
},
filterKey: e => {
e.stopPropagation();
const result = typeof this.#cellKeyFilter == 'function' &&
this.#cellKeyFilter({
cell: this.#cellHandlers.cell,
key: e.key || e.data,
value: this.#cellHandlers.input.value,
pos: this.#cellHandlers.input.selectionStart
});
if (result)
return true;
e.preventDefault();
return false;
},
hookEvents: a => {
for (let h in this.#cellHandlers) {
if (h[0] != '_')
continue;
if (a)
this.#cellHandlers.input.addEventListener(h.substr(1), this.#cellHandlers[h])
else
this.#cellHandlers.input.removeEventListener(h.substr(1), this.#cellHandlers[h])
}
},
endEdit: e => {
if (e !== undefined) {
e.stopPropagation();
}
if (this.#cellHandlers.outerDiv) {
this.#cellHandlers.hookEvents(false);
}
if (this.#cellHandlers.commitChanges) {
this.#cellHandlers.commitChanges = false;
if (this.#cellHandlers.span.textContent != this.#cellHandlers.input.value) {
this.#cellHandlers.span.textContent = this.#cellHandlers.input.value;
this.dispatchEvent(ListView.#evCellValueChanged);
}
}
try {
this.#cellHandlers.input.remove();
}
catch {
}
this.#cellHandlers.td.appendChild(this.#cellHandlers.outerDiv);
this.#cellHandlers.outerDiv = null;
this.focus();
},
_blur: e => {
this.#cellHandlers.endEdit(e);
},
_input: e => {
return this.#cellHandlers.filterKey(e);
},
_keydown: e => {
e.stopPropagation();
switch (e.key) {
case 'Escape':
this.#cellHandlers.commitChanges = false;
this.#cellHandlers.endEdit();
break;
case 'Tab':
case 'Enter':
this.#cellHandlers.input.removeEventListener('blur', this.#cellHandlers._blur);
if (e.key == 'Tab') {
let index = this.#cellHandlers.lvCell.columnIndex + 1;
const cells = this.#cellHandlers.td.parentElement.cells;
if (index == cells.length)
index = 0;
this.#setActiveCell(cells[index]);
}
this.#cellHandlers.commitChanges = true;
this.#cellHandlers.endEdit();
break;
}
}
}
#cellHandlers is a property of a class that subclasses HTMLElement. It acts/mimics some capabilities of c#’s DataGridView. When a cell is clicked in this class and the cell’s editable property is true, #cellHandlers.beginEdit is invoked where input element is created when it has not been created yet and added to the cell. The cell is a table cell (td) stored in #cellHandlers.td. Upon hooking up the events and setting focus on the input, blur event fires immediately and the handler of this event calls #cellHandlers.endEdit which, obviously end the editing state of the cell. Why such a behavior and how can I prevent it?
BTW, I hope no one suggests jQuery-ish solution although it could be a last resort.