HTML dialog focusing button element when opened
Asked Answered
L

4

6

I currently have a <dialog> which I am opening via JS. For demonstration purposes, my dialog has a single <button> element within it.

My issue is, when I open the dialog using .showModal(), the button within the modal gets focused for some reason. See example of issue below:

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">&times;</button>
</dialog>

As you can see, when the dialog is opened, the background of the button within the dialog gets the :focus styles applied, showing that it is focused.

My question is: Why is this happening? My expected behaviour would be for the dialog to open and for the button to not be focused when opening the dialog. I'm aware that I can .blur() the close button programatically, but that feels like I'm just "hiding" the issue rather than addressing the thing that's actually causing it.

Note: This issue is present in the latest version of Google Chrome (Version 81.0.4044.138)

Lawabiding answered 14/5, 2020 at 12:55 Comment(0)
T
36

This is expected behavior for a modal dialog UI pattern and is important to not override for accessibility reasons. Calling blur() on the dialog's child button will dump focus on the HTML body element and likely cause problems for users of assistive technology such as those using screen reader software.

If you prefer not to have the dialog's child button have a visual focus style when the dialog is opened, style the button using the CSS :focus-visible pseudo class.

In the code snippet below you will see that when clicking the open and close buttons those buttons will not receive visual focus, but when using your keyboard by tabbing to the buttons and opening them using the Enter or Space keys, they will. Showing visual focus is important for usability for keyboard only users and for not violating the Web Content Accessibility Guidelines (reference 1, reference 2). You should always make sure to help keyboard users know where they are in the DOM by not removing focus indicators or doing things like calling blur() immediately after a focus event occurs.

const openButton = document.querySelector("button.open-dialog");
const closeButton = document.querySelector("button.close-dialog");
const dialog = document.querySelector("dialog");

openButton.addEventListener("click", () => {
  dialog.showModal();
});

closeButton.addEventListener("click", () => {
  dialog.close();
});
button:focus-visible {
  outline: 3px solid blue;
}
<button class="open-dialog">Open dialog</button>

<dialog>
  <h2>I am a dialog</h2>
  <button class="close-dialog">close</button>
</dialog>
Thailand answered 26/9, 2024 at 17:58 Comment(0)
I
-3

Outline property added by webkit

Above snapshot is captured from the style section of the close-btn after opening the dialog box (developer tools of the browser/simply press F12 key to open it).

Webkit of browser is adding outline property explicitly when the dialog box is opened.

The outline property provides visual feedback for links that have "focus" when navigating a web document using the TAB key (or equivalent). This is especially useful for folks who can't use a mouse or have a visual impairment. If you remove the outline you are making your site inaccessible for these people.

But if you still want to remove it, then use below code.

#close-btn {
  background: red;
  outline: none;
}

I hope this will help you

Intra answered 17/5, 2020 at 9:50 Comment(2)
thanks for the answer :). I'm mainly looking for a way of preventing the button from being focused in the first place. I'd also like to know why this would be default behaviour, it seems strange to me. Nonetheless, thanks again for your answerLawabiding
Do not remove focus indicator styles on interactive elements such as form controls. You will be violating the Web Content Accessibility Guidelines and making it difficult for many people to use your website or web app.Thailand
L
-4

This is seems default behavior defined in the standard to focus the modal subtree once its rendered, from the HTML live standard:

  1. Run the dialog focusing steps for subject.

If you want to keep it like this and prevent the button from focus just trigger blur() event on the button, I tried to use inert and autofocus attributes but they doesn't seems to work work, so I think you have to stick with .blur() implementation:

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

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="false" autofocus="false">&times;</button>
</dialog>
Livvi answered 17/5, 2020 at 17:9 Comment(2)
While I didn't want to resort to blur(), you explained the reasoning behind why this happens well enough for me to accept your answer. I didn't realize that this was intended default behaviour and seems like something I've just got to manage through JS. Thanks :)Lawabiding
Note that doing this is bad for accessibility. The reason the button in the dialog is focused is that the dialog handles focus management (moving focus from the button that opens it to its first focusable child element when opened and vice versa when closed) when used as a modal dialog pattern via dialog.showModal(). Blurring the button after it receives focus will dump focus on the body and likely confuse screen reader users. If you do not want the visual focus style to be applied try using :focus-visible instead of :focus in your CSS. Example: codepen.io/clhenrick/pen/vYoONGRThailand
D
-5

You can simply do a focus() followed by an immeridate blur() on some arbitrary element.

For example -

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

In the above example I chose to focus and blur on the dialog element itself - that's enough to counteract the default autofocus behavior. Works on Chromium-based browsers.

Deflocculate answered 3/8, 2023 at 10:39 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.