Disable showModal auto-focusing using HTML attributes
Asked Answered
T

2

7

As I am aware, the showModal() method runs the following steps which end up focusing elements within an HTML dialog (emphasis mine) :

  1. Let subject be the dialog element on which the method was invoked.

  2. If subject already has an open attribute, then throw an "InvalidStateError" DOMException.

  3. If subject is not connected, then throw an "InvalidStateError" DOMException`.

  4. Add an open attribute to subject, whose value is the empty string.

  5. Set the dialog to the centered alignment mode.

  6. Let subject's node document be blocked by the modal dialog subject.

  7. If subject's node document's top layer does not already contain subject, then add subject to subject's node document's top layer.

  8. Run the dialog focusing steps for subject.

So the last step, 8, will run the following dialog focusing steps on the dialog. From my understanding (which could be completely wrong), these three steps from the dialog focusing-steps section of the spec specify that the element should only be focused if the element is not inert and is auto-focusable:

  1. If subject is inert, return.

  2. Let control be the first descendant element of the subject, in tree order, that is not inert and has the autofocus attribute specified.

    If there isn't one, then let control be the first non-inert descendant element of subject, in tree order.

    If there isn't one of those either, then let control be subject.

  3. Run the focusing steps for control.

    ...

So, to me, it seems as though if my button below (see snippet) has the inert attribute or is not auto-focusable then it shouldn't get focused when the dialog opens. However, when I try and apply both attributes, it still ends up being focused.


Attempt with the inert boolean attribute (which I thought would've made the dialog focusing steps return above, hence performing no focusing):

const dialog = document.querySelector("#dialog");
document.querySelector("#open-btn").addEventListener('click', () => {
  dialog.showModal();
});

document.querySelector("#close-btn").addEventListener('click', () => {
  dialog.close();
});
#close-btn:focus {
  background: red;
}
<button id="open-btn">Open</button>
<dialog id="dialog">
  <button id="close-btn" inert="inert">&times;</button>
</dialog>

Attempt with the autofocus boolean attribute set to false (I believe this is how you set it to false, I also tried autofocus="false" which didn't work either):

update 2023: It appears the below hidden snippet using inert now does stop the button from being autofocused (when I asked this question it didn't, which I suspect was a chrome bug), however it stops the button from being clickable, so this isn't a valid option.

const dialog = document.querySelector("#dialog");
document.querySelector("#open-btn").addEventListener('click', () => {
  dialog.showModal();
});

document.querySelector("#close-btn").addEventListener('click', () => {
  dialog.close();
});
#close-btn:focus {
  background: red;
}
<button id="open-btn">Open</button>
<dialog id="dialog">
  <button id="close-btn" autofocus="">&times;</button>
</dialog>

With both of these failing to work, I searched SO and found this answer which suggested that I might also be able to use tabindex="-1", which didn't work either.

I'm aware that I can blur the button once it is focused using .blur(), but my question specifically is:

  1. Why don't the two fiddles above disable the button from being automatically focused?
  2. Is there an HTML attribute of some sort that I can use to stop my button from being focused?
Tractable answered 5/8, 2020 at 14:40 Comment(1)
Related GitHub issue: github.com/whatwg/html/issues/1929Veil
V
0

Disabled elements cannot be focused on. You could add the disabled attribute to close-btn.

But, disabled elements cannot be clicked. Add the onclick attribute to open-btn. Set the onclick to this: setTimeout(function(){document.getElementById('close-btn').disabled = false}). This just enables the button 1 millisecond after the button is clicked. The timeout is required so that it does not enable close-btn before the dialog is opened.

If the dialog is re-opened, The button is automatically focused. We could add another onclick attribute to close-btn. Set onclick on close.btn to this: this.disabled = true. This disables close-btn when it is clicked.

Final result:

const dialog = document.querySelector("#dialog");
document.querySelector("#open-btn").addEventListener('click', () => {
  dialog.showModal();
});

document.querySelector("#close-btn").addEventListener('click', () => {
  dialog.close();
});
#close-btn:focus {
  background: red;
}
<button id="open-btn" onclick='setTimeout(function(){document.getElementById("close-btn").disabled = false})'>Open</button>
<dialog id="dialog">
  <button disabled id="close-btn" inert="inert" onclick='this.disabled = true'>&times;</button>
</dialog>
Veil answered 11/8, 2020 at 18:53 Comment(0)
W
0

maybe this would work, it did to me

Being selectorElement the actual element we want to dont show the outline when is focused

selectorElement:focus-visible {
    outline: none;
}
Walkerwalkietalkie answered 15/9, 2023 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.