Skip to content

Commit

Permalink
Adjust the option focusVisible when calling HTMLElement.focus() and i…
Browse files Browse the repository at this point in the history
…mprove focusable detection (#259)
  • Loading branch information
david-tejada authored Dec 8, 2023
1 parent fe35780 commit 20ba96f
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 12 deletions.
12 changes: 3 additions & 9 deletions src/content/actions/focus.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { TalonAction } from "../../typings/RequestFromTalon";
import { notify } from "../notify/notify";
import { dispatchKeyDown, dispatchKeyUp } from "../utils/dispatchEvents";
import { editableElementSelector } from "../utils/domUtils";
import { editableElementSelector, getFocusable } from "../utils/domUtils";
import { ElementWrapper } from "../../typings/ElementWrapper";
import { getWrapperForElement } from "../wrappers/wrappers";

const focusableSelector =
"a, area[href], button, frame, iframe, input, object, select, textarea, summary, [tabindex]";

export function focus(wrappers: ElementWrapper[]): TalonAction[] | undefined {
window.focus();

Expand All @@ -16,13 +13,10 @@ export function focus(wrappers: ElementWrapper[]): TalonAction[] | undefined {
dispatchKeyDown(document.activeElement, "Tab");
}

const focusable = wrapper.element.matches(focusableSelector)
? wrapper.element
: wrapper.element.querySelector(focusableSelector) ??
wrapper.element.closest(focusableSelector);
const focusable = getFocusable(wrapper.element);

if (focusable instanceof HTMLElement) {
focusable.focus();
focusable.focus({ focusVisible: true });
dispatchKeyUp(focusable, "Tab");
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/content/utils/dispatchEvents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { setSelectionAtEdge } from "../actions/setSelection";
import { getElementCenter } from "./cssomUtils";
import { isEditable } from "./domUtils";
import { getFocusable, isEditable } from "./domUtils";

// At the moment this only works in Firefox but it seems it's going to be
// implemented in the other browsers.
Expand Down Expand Up @@ -51,8 +51,9 @@ export function dispatchClick(element: Element): boolean {
element.dispatchEvent(pointerEvent("pointerdown", clientX, clientY));
element.dispatchEvent(mouseEvent("mousedown", clientX, clientY));

if (element instanceof HTMLElement) {
element.focus({ focusVisible: false });
const focusable = getFocusable(element);
if (focusable instanceof HTMLElement) {
focusable.focus({ focusVisible: isEditable(focusable) });
}

if (element instanceof HTMLElement && isEditable(element)) {
Expand Down
17 changes: 17 additions & 0 deletions src/content/utils/domUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,20 @@ export function getActiveElement() {

return activeElement;
}

const focusableSelector =
":is(a[href], area[href], input, select, textarea, button, iframe, object, summary, [tabindex]):not(:is([disabled], [tabindex='-1'], [contenteditable='false']))";

/**
* Returns the element itself if it's focusable. If not, the closest focusable
* descendant or ascendant, in that order.
* The reason to check descendants is for instances where we mark as hintable
* the parent and not the child. For example, if the parent is a wrapper
* div[role="button"] and the child is a button.
*/
export function getFocusable(element: Element) {
return element.matches(focusableSelector)
? element
: element.querySelector(focusableSelector) ??
element.closest(focusableSelector);
}

0 comments on commit 20ba96f

Please sign in to comment.