Skip to content

Commit 96c4c4d

Browse files
committed
base: disabled; input: focus
BaseElement fixes for disabled and tabindex, which is even funkier than I had thought. InputNum fixes for focus, also funkier than imagined.
1 parent 45c88c0 commit 96c4c4d

File tree

5 files changed

+120
-94
lines changed

5 files changed

+120
-94
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ If you bundle your templates into one file, then each `<template>` must have the
5757
<template id="input-num">...</template>
5858
```
5959

60-
NOTE: If you are using import options and getting an error about an element/tag already being registered, you might need to add the same import options to your JavaScript `import` statements, so that they match your HTML `<script>` imports. The browser can import these as two separate files. This happens sometimes for reasons unknown to me.
60+
NOTE: If you are using import options and getting an error about an element/tag already being registered, you might need to add the same import options to your JavaScript `import` statements, so that they match your HTML `<script>` imports. This happens sometimes because the browser imports them as two separate files, for reasons unknown to me.
6161

6262
### Managing Template Files
6363
There are built-in template files in the `templates/` directory. They serve two purposes:
@@ -103,7 +103,9 @@ A quick glossary entry of note: A [boolean attribute](https://developer.mozilla.
103103
These classes manage common attributes/properties and behaviors across elements. All the attributes/properties listed are inherited by the sub-classes.
104104
105105
### `class BaseElement extends HTMLElement`
106-
`BaseElement` is the top level class. It is the parent class for `InputNum` and `MultiState`. It manages two global DOM attributes, `disabled` and `tabindex`. See `base-element.js`.
106+
`BaseElement` is the top level class. It is the parent class for `InputNum` and `MultiState`. It manages two global DOM attributes, `disabled` and `tabindex`. See `base-element.js`.`
107+
108+
NOTE: `HTMLElement` does not have a `disabled` attribute/property. It only has `tabindex`/`tabIndex`. `BaseElement` observes the `disabled` and `tabindex` attributes and exposes the properties. To do so requires the `disabled` attribute to manage `tabindex` too, because setting `disabled` *should* set `tabindex="-1"`. This is complicated if you set `tabindex` instead of relying on the default tab order, but most of that can be handled. But if you are setting both `disabled` and `tabindex` in your HTML file, you *must* set `tabindex` before `disabled`. This because the DOM runs `attributeChangedCallback()` in a FIFO queue and when `disabled` sets the `tabindex` it goes to the end of the queue, after the `tabindex` setting in the HTML file. Though it's allowed, setting `tabindex` to anything other than `0` or `-1` is sternly [recommended against by MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) (*scroll down the page to the first Warning block*).
107109
108110
### `class MultiState`
109111
`MultiState` (`multi-state.js`) is the parent class for `<state-btn>` and `MultiCheck`.

apps/input-num/index.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,20 +207,21 @@ function change(evt) {
207207
case elms.min:
208208
minDigits();
209209
case elms.max: // validate
210-
setInfo(elms.max, inNum.max <= inNum.min, "Max/Min overlap!");
211-
disable(elms.autoWidth, inNum.max == Infinity
212-
|| inNum.min == -Infinity);
210+
setInfo(elms.max, inNum.max <= inNum.min, "Max/Min overlap!");
211+
elms.autoWidth.disabled = inNum.max == Infinity
212+
|| inNum.min == -Infinity;
213213
break;
214214
case elms.locale:
215-
disable(elms.currency, !val);
215+
elms.currency.disabled = !val;
216+
elms.currency.labels[0].classList.toggle("disabled", !val);
216217
case elms.currency:
217-
disable(elms.accounting, !val || !inNum.currency
218-
|| !inNum.useLocale);
218+
elms.accounting.disabled = !val || !inNum.currency
219+
|| !inNum.useLocale;
219220
case elms.units: // display info
220221
setInfo(tar, val, g[id][val]);
221222
break;
222223
case elms.spins: // disable step, delay, interval
223-
spinner.forEach(elm => disable(elm, !val));
224+
spinner.forEach(elm => elm.disabled = !val);
224225
case elms.keyboards:
225226
setInfo(elms.accounting,
226227
!elms.spins.checked && !elms.keyboards.checked,
@@ -239,11 +240,6 @@ function change(evt) {
239240
function setInfo(elm, b, text) { // sets info/warning text to the right of elm
240241
elm.nextElementSibling.innerHTML = b ? text : "";
241242
}
242-
function disable(elm, b) { // disables an element and its label
243-
elm.disabled = b;
244-
for (const lbl of [elm.previousElementSibling, elm.nextElementSibling])
245-
lbl?.classList.toggle("disabled", b);
246-
}
247243
function minDigits() { // one alert set in two places
248244
setInfo(elms.min,
249245
inNum.min && Math.abs(inNum.min) < 1 / Math.pow(base, inNum.digits),

base-element.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,21 @@ VALUE = "value"; // exported: defined here but not handled here
66
// =============================================================================
77
// The custom base class, direct sub-class of HTMLElement
88
class BaseElement extends HTMLElement {
9-
#id; #meta; #tabIndex;
9+
#connected; #disabled; #disabling; #id; #meta; #ptrEvents; #tabIndex;
1010
static searchParams; // set by html-elements.js, if it's used
1111
static observedAttributes = [DISABLED, TAB_INDEX];
1212
static promises = new Map; // for noAwait, see https://github.com/sidewayss/html-elements/issues/8
1313
constructor(meta, template, noAwait) {
1414
super();
15-
if (noAwait) { // passes subclass as template arg
15+
if (noAwait) { // passes subclass as template arg
1616
this.#meta = meta;
1717
this.#id = classToTag(template);
1818
BaseElement.promises.set(this, promise());
1919
}
2020
else
21-
this.#attach(template); // the <template> as DocumentFragment
21+
this.#attach(template); // <template> as DocumentFragment
2222

23-
if (this.tabIndex < 0)
24-
this.tabIndex = 0;
25-
this.#tabIndex = this.tabIndex;
23+
this.setAttribute(TAB_INDEX, "0"); // default value emulates <input>
2624
}
2725
#attach(template) {
2826
this._dom = this.attachShadow({mode:"open"});
@@ -42,28 +40,43 @@ class BaseElement extends HTMLElement {
4240
}
4341
else
4442
this._connected?.();
43+
44+
// Creating the disabled attribute from scratch is complicated
45+
this.#disabled = this.getAttribute(DISABLED);
46+
this.#connected = true;
4547
}
4648
// attributeChangedCallback() handles changes to the observed attributes
47-
attributeChangedCallback(name, _, path) {
49+
attributeChangedCallback(name, _, val) {
4850
switch (name) {
4951
case DISABLED:
50-
if (path !== null) { // null == removeAttribute()
51-
this.tabIndex = -1;
52+
if (this.#connected) {
53+
if (val === this.#disabled)
54+
return;
55+
else //-------------------
56+
this.#disabled = val;
57+
}
58+
this.#disabling = true;
59+
if (val !== null) { // null == removeAttribute()
60+
this.tabIndex = -1; // indirectly recursive
61+
this.#ptrEvents = this.style.pointerEvents;
5262
this.style.pointerEvents = "none";
5363
}
5464
else {
5565
this.tabIndex = this.#tabIndex;
56-
this.style.pointerEvents = "";
66+
this.style.pointerEvents = this.#ptrEvents;
5767
}
58-
break;
68+
return;
5969
case TAB_INDEX:
60-
this.#tabIndex = path; // to restore post-disable
70+
if (this.#disabling) // easier done here, not case DISABLED
71+
this.#disabling = false;
72+
else // to restore post-disable
73+
this.#tabIndex = val;
6174
default:
6275
}
6376
}
6477
// getters/setters reflect the HTML attributes, see attributeChangedCallback()
6578
get disabled() { return this.hasAttribute(DISABLED); }
66-
set disabled(path) { this._setBool(DISABLED, path); }
79+
set disabled(val) { this._setBool(DISABLED, val); }
6780

6881
// define wraps customElements.define for consistency of class and tag names
6982
static define(cls) {

html-elements.xlsx

4.71 KB
Binary file not shown.

0 commit comments

Comments
 (0)